diff --git a/Helpers/Picker.qml b/Helpers/Picker.qml index 386702a..e0b2884 100644 --- a/Helpers/Picker.qml +++ b/Helpers/Picker.qml @@ -66,7 +66,7 @@ MouseArea { function save(): void { const tmpfile = Qt.resolvedUrl(`/tmp/zshell-picker-${Quickshell.processId}-${Date.now()}.png`); - const cmd = Config.screenshot.enable_pp ? [Quickshell.shellDir + "/scripts/rs-pictures"] : ["swappy", "-f"]; + const cmd = Config.screenshot.enable_pp ? ["iwaku", "--image"] : ["swappy", "-f"]; ZShellIo.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached([...cmd, path])); closeAnim.start(); } diff --git a/Plugins/iwaku/Cargo.lock b/Plugins/iwaku/Cargo.lock new file mode 100644 index 0000000..c5e7fe8 --- /dev/null +++ b/Plugins/iwaku/Cargo.lock @@ -0,0 +1,1100 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + +[[package]] +name = "built" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fax" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png 0.18.1", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "iwaku" +version = "0.1.0" +dependencies = [ + "anyhow", + "image", + "serde", + "serde_json", + "tiny-skia", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "no_std_io2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png 0.17.16", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/Plugins/iwaku/Cargo.toml b/Plugins/iwaku/Cargo.toml new file mode 100644 index 0000000..d74c3eb --- /dev/null +++ b/Plugins/iwaku/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "iwaku" +version = "0.1.0" +edition = "2024" + +[[bin]] +name = "iwaku" +path = "src/main.rs" + +[dependencies] +image = { version = "0.25", features = ["png"] } +tiny-skia = "0.11" +serde = { version = "1", features = ["derive"] } +anyhow = "1" +serde_json = "1.0.149" + +[profile.release] +opt-level = 3 +lto = "thin" +codegen-units = 1 +strip = true + +[profile.dev] +opt-level = 0 + +[profile.dev.package."*"] +opt-level = 3 diff --git a/Plugins/iwaku/README.md b/Plugins/iwaku/README.md new file mode 100644 index 0000000..bbe4940 --- /dev/null +++ b/Plugins/iwaku/README.md @@ -0,0 +1,6 @@ +# What_That_Claude_DO? + +What That Claude Do? (WTCD) +A repository of random things I ask Claude to do for me. + +In this case it is creating a screenshot tool diff --git a/Plugins/iwaku/src/config.rs b/Plugins/iwaku/src/config.rs new file mode 100644 index 0000000..3b502f1 --- /dev/null +++ b/Plugins/iwaku/src/config.rs @@ -0,0 +1,45 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Config { + #[serde(rename = "screenshot")] + pub screenshot: EffectsConfig, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EffectsConfig { + pub mode: String, + pub rounded_corners: bool, + pub corner_radius: f32, + pub drop_shadow: bool, + pub shadow_blur_radius: f32, + pub shadow_offset_x: f32, + pub shadow_offset_y: f32, + pub shadow_color: [u8; 4], +} + +impl Config { + pub fn config_path() -> Option { + let home = std::env::var("HOME").ok()?; + Some( + PathBuf::from(home) + .join(".config") + .join("zshell") + .join("config.json"), + ) + } + + pub fn load() -> Result { + let path = Self::config_path().context("Could not determine HOME directory")?; + Self::load_from(&path) + } + + pub fn load_from(path: &PathBuf) -> Result { + let raw = std::fs::read_to_string(path) + .with_context(|| format!("Failed to read config at {}", path.display()))?; + serde_json::from_str(&raw) + .with_context(|| format!("Failed to parse JSON config at {}", path.display())) + } +} diff --git a/Plugins/iwaku/src/effects.rs b/Plugins/iwaku/src/effects.rs new file mode 100644 index 0000000..e6d41f6 --- /dev/null +++ b/Plugins/iwaku/src/effects.rs @@ -0,0 +1,288 @@ +use crate::config::EffectsConfig; +use image::RgbaImage; +use tiny_skia::{ + BlendMode, Color, FillRule, Paint, Path, PathBuilder, Pixmap, PixmapPaint, Transform, +}; + +pub fn apply_effects(img: RgbaImage, cfg: &EffectsConfig) -> RgbaImage { + let img = if cfg.rounded_corners { + apply_rounded_corners(img, cfg.corner_radius) + } else { + img + }; + if cfg.drop_shadow { + apply_drop_shadow( + img, + cfg.shadow_blur_radius, + cfg.shadow_offset_x, + cfg.shadow_offset_y, + cfg.shadow_color, + ) + } else { + img + } +} + +pub fn apply_rounded_corners(img: RgbaImage, radius: f32) -> RgbaImage { + let (w, h) = img.dimensions(); + let mut mask = Pixmap::new(w, h).expect("mask pixmap"); + let path = rounded_rect_path(0.0, 0.0, w as f32, h as f32, radius); + let mut paint = Paint::default(); + paint.set_color(Color::WHITE); + paint.anti_alias = true; + mask.fill_path( + &path, + &paint, + FillRule::Winding, + Transform::identity(), + None, + ); + + let mut pixmap = rgba_image_to_pixmap(&img); + let mut dst_paint = PixmapPaint::default(); + dst_paint.blend_mode = BlendMode::DestinationIn; + pixmap.draw_pixmap(0, 0, mask.as_ref(), &dst_paint, Transform::identity(), None); + pixmap_to_rgba_image(pixmap) +} + +pub fn apply_drop_shadow( + img: RgbaImage, + blur_radius: f32, + offset_x: f32, + offset_y: f32, + shadow_color: [u8; 4], +) -> RgbaImage { + let (iw, ih) = img.dimensions(); + let br = blur_radius.ceil() as u32; + + let extra_left = br.saturating_sub((-offset_x).max(0.0) as u32); + let extra_top = br.saturating_sub((-offset_y).max(0.0) as u32); + let extra_right = br + offset_x.max(0.0) as u32; + let extra_bottom = br + offset_y.max(0.0) as u32; + + let canvas_w = iw + extra_left + extra_right; + let canvas_h = ih + extra_top + extra_bottom; + + let mut shadow_pixmap = Pixmap::new(canvas_w, canvas_h).expect("shadow pixmap"); + let img_pixmap = rgba_image_to_pixmap(&img); + let shadow_x = (extra_left as f32 + offset_x) as i32; + let shadow_y = (extra_top as f32 + offset_y) as i32; + + let mut sp = PixmapPaint::default(); + sp.blend_mode = BlendMode::Source; + shadow_pixmap.draw_pixmap( + shadow_x, + shadow_y, + img_pixmap.as_ref(), + &sp, + Transform::identity(), + None, + ); + + tint_pixmap_as_shadow(&mut shadow_pixmap, shadow_color); + + let shadow_img = pixmap_to_rgba_image(shadow_pixmap); + let blurred = box_blur_rgba(&shadow_img, br); + let blurred_pixmap = rgba_image_to_pixmap(&blurred); + + let mut canvas = Pixmap::new(canvas_w, canvas_h).expect("canvas pixmap"); + let mut p = PixmapPaint::default(); + p.blend_mode = BlendMode::Source; + canvas.draw_pixmap( + 0, + 0, + blurred_pixmap.as_ref(), + &p, + Transform::identity(), + None, + ); + + let mut p2 = PixmapPaint::default(); + p2.blend_mode = BlendMode::SourceOver; + canvas.draw_pixmap( + extra_left as i32, + extra_top as i32, + img_pixmap.as_ref(), + &p2, + Transform::identity(), + None, + ); + + pixmap_to_rgba_image(canvas) +} + +fn rounded_rect_path(x: f32, y: f32, w: f32, h: f32, r: f32) -> Path { + let r = r.min(w / 2.0).min(h / 2.0); + let mut pb = PathBuilder::new(); + pb.move_to(x + r, y); + pb.line_to(x + w - r, y); + pb.quad_to(x + w, y, x + w, y + r); + pb.line_to(x + w, y + h - r); + pb.quad_to(x + w, y + h, x + w - r, y + h); + pb.line_to(x + r, y + h); + pb.quad_to(x, y + h, x, y + h - r); + pb.line_to(x, y + r); + pb.quad_to(x, y, x + r, y); + pb.close(); + pb.finish().expect("rounded rect path") +} + +fn rgba_image_to_pixmap(img: &RgbaImage) -> Pixmap { + let (w, h) = img.dimensions(); + let mut pixmap = Pixmap::new(w, h).expect("pixmap alloc"); + let pixels = pixmap.pixels_mut(); + for (i, px) in img.pixels().enumerate() { + let [r, g, b, a] = px.0; + let af = a as f32 / 255.0; + pixels[i] = tiny_skia::PremultipliedColorU8::from_rgba( + (r as f32 * af) as u8, + (g as f32 * af) as u8, + (b as f32 * af) as u8, + a, + ) + .unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT); + } + pixmap +} + +fn pixmap_to_rgba_image(pixmap: Pixmap) -> RgbaImage { + let (w, h) = (pixmap.width(), pixmap.height()); + let mut out = RgbaImage::new(w, h); + for (i, px) in pixmap.pixels().iter().enumerate() { + let x = (i as u32) % w; + let y = (i as u32) / w; + let a = px.alpha(); + let (r, g, b) = if a == 0 { + (0, 0, 0) + } else { + let af = a as f32 / 255.0; + ( + (px.red() as f32 / af).round().min(255.0) as u8, + (px.green() as f32 / af).round().min(255.0) as u8, + (px.blue() as f32 / af).round().min(255.0) as u8, + ) + }; + out.put_pixel(x, y, image::Rgba([r, g, b, a])); + } + out +} + +fn tint_pixmap_as_shadow(pixmap: &mut Pixmap, color: [u8; 4]) { + let [sr, sg, sb, _] = color; + for px in pixmap.pixels_mut() { + let a = px.alpha(); + if a > 0 { + let af = a as f32 / 255.0; + *px = tiny_skia::PremultipliedColorU8::from_rgba( + (sr as f32 * af) as u8, + (sg as f32 * af) as u8, + (sb as f32 * af) as u8, + a, + ) + .unwrap_or(tiny_skia::PremultipliedColorU8::TRANSPARENT); + } + } +} + +fn box_blur_rgba(img: &RgbaImage, radius: u32) -> RgbaImage { + if radius == 0 { + return img.clone(); + } + let mut buf = sliding_horizontal(img, radius); + buf = sliding_vertical(&buf, radius); + buf = sliding_horizontal(&buf, radius); + buf = sliding_vertical(&buf, radius); + buf +} + +fn sliding_horizontal(img: &RgbaImage, radius: u32) -> RgbaImage { + let (w, h) = img.dimensions(); + let r = radius as i32; + let diam = (2 * r + 1) as u32; + let mut out = RgbaImage::new(w, h); + + for y in 0..h { + let mut sr = 0u32; + let mut sg = 0u32; + let mut sb = 0u32; + let mut sa = 0u32; + + for dx in -r..=r { + let sx = dx.clamp(0, w as i32 - 1) as u32; + let p = img.get_pixel(sx, y).0; + sr += p[0] as u32; + sg += p[1] as u32; + sb += p[2] as u32; + sa += p[3] as u32; + } + + for x in 0..w { + out.put_pixel( + x, + y, + image::Rgba([ + (sr / diam) as u8, + (sg / diam) as u8, + (sb / diam) as u8, + (sa / diam) as u8, + ]), + ); + + let remove_x = (x as i32 - r).clamp(0, w as i32 - 1) as u32; + let add_x = (x as i32 + r + 1).clamp(0, w as i32 - 1) as u32; + let rp = img.get_pixel(remove_x, y).0; + let ap = img.get_pixel(add_x, y).0; + sr = sr.saturating_sub(rp[0] as u32) + ap[0] as u32; + sg = sg.saturating_sub(rp[1] as u32) + ap[1] as u32; + sb = sb.saturating_sub(rp[2] as u32) + ap[2] as u32; + sa = sa.saturating_sub(rp[3] as u32) + ap[3] as u32; + } + } + out +} + +fn sliding_vertical(img: &RgbaImage, radius: u32) -> RgbaImage { + let (w, h) = img.dimensions(); + let r = radius as i32; + let diam = (2 * r + 1) as u32; + let mut out = RgbaImage::new(w, h); + + for x in 0..w { + let mut sr = 0u32; + let mut sg = 0u32; + let mut sb = 0u32; + let mut sa = 0u32; + + for dy in -r..=r { + let sy = dy.clamp(0, h as i32 - 1) as u32; + let p = img.get_pixel(x, sy).0; + sr += p[0] as u32; + sg += p[1] as u32; + sb += p[2] as u32; + sa += p[3] as u32; + } + + for y in 0..h { + out.put_pixel( + x, + y, + image::Rgba([ + (sr / diam) as u8, + (sg / diam) as u8, + (sb / diam) as u8, + (sa / diam) as u8, + ]), + ); + + let remove_y = (y as i32 - r).clamp(0, h as i32 - 1) as u32; + let add_y = (y as i32 + r + 1).clamp(0, h as i32 - 1) as u32; + let rp = img.get_pixel(x, remove_y).0; + let ap = img.get_pixel(x, add_y).0; + sr = sr.saturating_sub(rp[0] as u32) + ap[0] as u32; + sg = sg.saturating_sub(rp[1] as u32) + ap[1] as u32; + sb = sb.saturating_sub(rp[2] as u32) + ap[2] as u32; + sa = sa.saturating_sub(rp[3] as u32) + ap[3] as u32; + } + } + out +} diff --git a/Plugins/iwaku/src/main.rs b/Plugins/iwaku/src/main.rs new file mode 100644 index 0000000..b142628 --- /dev/null +++ b/Plugins/iwaku/src/main.rs @@ -0,0 +1,211 @@ +mod config; +mod effects; + +use anyhow::{bail, Context, Result}; +use std::io::Write as _; +use std::process::{Command, Stdio}; + +/// CLI overrides that map 1:1 to `EffectsConfig` fields. +/// All fields are `Option` so we can tell "not supplied" from any concrete value. +#[derive(Default)] +struct CliOverrides { + rounded_corners: Option, + corner_radius: Option, + drop_shadow: Option, + shadow_blur_radius: Option, + shadow_offset_x: Option, + shadow_offset_y: Option, + /// Accepted as four comma-separated u8 values, e.g. `255,0,0,200` + shadow_color: Option<[u8; 4]>, +} + +fn parse_bool(s: &str) -> Result { + match s.to_lowercase().as_str() { + "true" | "1" | "yes" => Ok(true), + "false" | "0" | "no" => Ok(false), + other => bail!("Expected a boolean (true/false), got '{other}'"), + } +} + +fn parse_shadow_color(s: &str) -> Result<[u8; 4]> { + let parts: Vec<&str> = s.split(',').collect(); + if parts.len() != 4 { + bail!("--shadow_color expects four comma-separated u8 values, e.g. 255,0,0,200"); + } + let r = parts[0] + .trim() + .parse::() + .context("shadow_color red channel")?; + let g = parts[1] + .trim() + .parse::() + .context("shadow_color green channel")?; + let b = parts[2] + .trim() + .parse::() + .context("shadow_color blue channel")?; + let a = parts[3] + .trim() + .parse::() + .context("shadow_color alpha channel")?; + Ok([r, g, b, a]) +} + +fn main() -> Result<()> { + let args: Vec = std::env::args().skip(1).collect(); + + let mut image_path: Option = None; + let mut overrides = CliOverrides::default(); + + let mut i = 0; + while i < args.len() { + match args[i].as_str() { + "--image" => { + i += 1; + image_path = Some( + args.get(i) + .cloned() + .context("Expected a path after --image")?, + ); + } + "--rounded_corners" => { + i += 1; + let val = args + .get(i) + .context("Expected true/false after --rounded_corners")?; + overrides.rounded_corners = Some(parse_bool(val)?); + } + "--corner_radius" => { + i += 1; + let val = args + .get(i) + .context("Expected a number after --corner_radius")?; + overrides.corner_radius = Some( + val.parse::() + .context("--corner_radius must be a number")?, + ); + } + "--drop_shadow" => { + i += 1; + let val = args + .get(i) + .context("Expected true/false after --drop_shadow")?; + overrides.drop_shadow = Some(parse_bool(val)?); + } + "--shadow_blur_radius" => { + i += 1; + let val = args + .get(i) + .context("Expected a number after --shadow_blur_radius")?; + overrides.shadow_blur_radius = Some( + val.parse::() + .context("--shadow_blur_radius must be a number")?, + ); + } + "--shadow_offset_x" => { + i += 1; + let val = args + .get(i) + .context("Expected a number after --shadow_offset_x")?; + overrides.shadow_offset_x = Some( + val.parse::() + .context("--shadow_offset_x must be a number")?, + ); + } + "--shadow_offset_y" => { + i += 1; + let val = args + .get(i) + .context("Expected a number after --shadow_offset_y")?; + overrides.shadow_offset_y = Some( + val.parse::() + .context("--shadow_offset_y must be a number")?, + ); + } + "--shadow_color" => { + i += 1; + let val = args + .get(i) + .context("Expected r,g,b,a after --shadow_color")?; + overrides.shadow_color = Some(parse_shadow_color(val)?); + } + unknown => bail!("Unknown argument: {unknown}"), + } + i += 1; + } + + let image_path = image_path.context("Missing --image ")?; + + let config = config::Config::load().context("Failed to load config")?; + + let mut effects = config.screenshot; + if effects.mode == "auto" { + if let Some(v) = overrides.rounded_corners { + effects.rounded_corners = v; + } + if let Some(v) = overrides.corner_radius { + effects.corner_radius = v; + } + if let Some(v) = overrides.drop_shadow { + effects.drop_shadow = v; + } + if let Some(v) = overrides.shadow_blur_radius { + effects.shadow_blur_radius = v; + } + if let Some(v) = overrides.shadow_offset_x { + effects.shadow_offset_x = v; + } + if let Some(v) = overrides.shadow_offset_y { + effects.shadow_offset_y = v; + } + if let Some(v) = overrides.shadow_color { + effects.shadow_color = v; + } + } + + if let Err(e) = process_image(&image_path, &effects) { + eprintln!("Error processing '{}': {e:#}", image_path); + } + + Ok(()) +} + +fn process_image(path: &str, effects: &config::EffectsConfig) -> Result<()> { + let img = image::open(path) + .with_context(|| format!("Failed to open image '{path}'"))? + .into_rgba8(); + + let processed = effects::apply_effects(img, effects); + + let mut png_bytes: Vec = Vec::new(); + image::DynamicImage::ImageRgba8(processed) + .write_to( + &mut std::io::Cursor::new(&mut png_bytes), + image::ImageFormat::Png, + ) + .context("Failed to encode processed image as PNG")?; + + let mut child = Command::new("swappy") + .args(["-f", "-"]) + .stdin(Stdio::piped()) + .spawn() + .context("Failed to spawn swappy. Is it installed and in PATH?")?; + + child + .stdin + .take() + .context("Failed to get swappy stdin")? + .write_all(&png_bytes) + .context("Failed to write image data to swappy")?; + + let status = child.wait().context("Failed to wait for swappy")?; + + if !status.success() { + eprintln!( + "swappy exited with non-zero status for '{}': {}", + path, status + ); + } + + Ok(()) +} diff --git a/scripts/iwaku b/scripts/iwaku new file mode 100755 index 0000000..d5b6a6c Binary files /dev/null and b/scripts/iwaku differ