diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 58d1325..5d0e04e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -40,6 +40,16 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + [[package]] name = "aes" version = "0.8.4" @@ -51,6 +61,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -60,6 +84,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -126,6 +159,23 @@ dependencies = [ "x11rb", ] +[[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 2.0.103", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-broadcast" version = "0.7.2" @@ -294,6 +344,29 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 7.1.3", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +dependencies = [ + "arrayvec", +] + [[package]] name = "backtrace" version = "0.3.75" @@ -341,6 +414,12 @@ dependencies = [ "virtue", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" @@ -356,6 +435,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + [[package]] name = "block" version = "0.1.6" @@ -442,6 +527,12 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + [[package]] name = "bumpalo" version = "3.18.1" @@ -625,8 +716,10 @@ checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link", ] @@ -679,6 +772,12 @@ dependencies = [ "objc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -905,6 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] @@ -945,6 +1045,15 @@ dependencies = [ "syn 2.0.103", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "darling" version = "0.20.11" @@ -1291,6 +1400,26 @@ dependencies = [ "syn 2.0.103", ] +[[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 2.0.103", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1344,6 +1473,33 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastrand" version = "2.3.0" @@ -1392,6 +1548,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1726,6 +1888,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1917,6 +2099,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1934,6 +2126,18 @@ name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.4", +] [[package]] name = "heck" @@ -2271,11 +2475,37 @@ checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", "num-traits", "png", + "qoi", + "ravif", + "rayon", + "rgb", "tiff", + "zune-core", + "zune-jpeg", ] +[[package]] +name = "image-webp" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + [[package]] name = "indexmap" version = "1.9.3" @@ -2325,6 +2555,17 @@ dependencies = [ "generic-array", ] +[[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 2.0.103", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2360,6 +2601,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -2476,6 +2726,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "keyring" +version = "3.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1961983669d57bdfe6c0f3ef8e4c229b5ef751afcc7d87e4271d2f71f6ccfa8b" +dependencies = [ + "log", +] + [[package]] name = "kuchikiki" version = "0.8.2" @@ -2495,6 +2754,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libappindicator" version = "0.9.0" @@ -2525,6 +2790,16 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.7.4" @@ -2565,6 +2840,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-rs-sys" version = "0.5.1" @@ -2608,6 +2894,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "mac" version = "0.1.1" @@ -2643,6 +2938,16 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[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.7.5" @@ -2833,12 +3138,59 @@ 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-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[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 2.0.103", +] + +[[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" @@ -3107,6 +3459,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "open" version = "5.3.2" @@ -3253,6 +3611,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.3" @@ -3489,6 +3853,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -3586,6 +3962,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn 2.0.103", +] + +[[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 = "quick-xml" version = "0.37.5" @@ -3720,6 +4130,56 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "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", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3730,17 +4190,27 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" name = "raycast-linux" version = "0.1.0" dependencies = [ + "aes-gcm", "arboard", "bincode", "bytes", + "chrono", "enigo 0.5.0", "freedesktop-file-parser", "futures-util", + "hex", + "image", + "keyring", + "once_cell", + "rand 0.9.1", "rayon", + "regex", "reqwest", + "rusqlite", "selection", "serde", "serde_json", + "sha2", "tauri", "tauri-build", "tauri-plugin-clipboard-manager", @@ -3900,6 +4370,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + [[package]] name = "ring" version = "0.17.14" @@ -3914,6 +4390,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rusqlite" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" +dependencies = [ + "bitflags 2.9.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rust-ini" version = "0.21.1" @@ -4367,6 +4857,15 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -5463,6 +5962,16 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "untrusted" version = "0.9.0" @@ -5523,6 +6032,17 @@ dependencies = [ "wasm-bindgen", ] +[[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 = "vcpkg" version = "0.2.15" @@ -6857,6 +7377,30 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[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.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6fe2e33d02a98ee64423802e16df3de99c43e5cf5ff983767e1128b394c8ac" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "5.5.3" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 2dde22b..c7747ff 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -40,6 +40,16 @@ futures-util = "^0.3.31" tokio = { version = "^1.45.1", features = ["full"] } uuid = { version = "^1.17.0", features = ["v4", "serde"] } enigo = "0.5.0" +rusqlite = { version = "0.36.0", features = ["bundled"] } +keyring = "3.6.2" +aes-gcm = "0.10.3" +sha2 = "0.10.9" +hex = "0.4.3" +chrono = { version = "0.4.41", features = ["serde"] } +once_cell = "1.21.3" +image = "0.25.6" +regex = "1.11.1" +rand = "0.9.1" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-global-shortcut = "2" diff --git a/src-tauri/src/clipboard_history.rs b/src-tauri/src/clipboard_history.rs new file mode 100644 index 0000000..fba304e --- /dev/null +++ b/src-tauri/src/clipboard_history.rs @@ -0,0 +1,387 @@ +use crate::error::AppError; +use aes_gcm::aead::{Aead, KeyInit}; +use aes_gcm::{Aes256Gcm, Nonce}; +use chrono::{DateTime, NaiveDateTime, Utc}; +use once_cell::sync::Lazy; +use regex::Regex; +use rusqlite::{params, Connection, Result as RusqliteResult}; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::path::PathBuf; +use std::sync::Mutex; +use std::time::Duration; +use tauri::{AppHandle, Manager}; + +const KEYRING_SERVICE: &str = "dev.byteatatime.raycast"; +const KEYRING_USERNAME: &str = "clipboard_history_key"; + +static COLOR_REGEX: Lazy = + Lazy::new(|| Regex::new(r"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$").unwrap()); +static URL_REGEX: Lazy = Lazy::new(|| Regex::new(r"^(https?|ftp)://[^\s/$.?#].[^\s]*$").unwrap()); + +#[derive(Serialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ClipboardItem { + id: i64, + hash: String, + content_type: ContentType, + content_value: String, + source_app_name: Option, + first_copied_at: DateTime, + last_copied_at: DateTime, + times_copied: i32, + is_pinned: bool, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum ContentType { + Text, + Image, + Color, + Link, + File, +} + +impl ContentType { + fn from_str(s: &str) -> Result { + match s { + "text" => Ok(ContentType::Text), + "image" => Ok(ContentType::Image), + "color" => Ok(ContentType::Color), + "link" => Ok(ContentType::Link), + "file" => Ok(ContentType::File), + _ => Err(AppError::ClipboardHistory("Invalid content type".into())), + } + } + + fn as_str(&self) -> &'static str { + match self { + ContentType::Text => "text", + ContentType::Image => "image", + ContentType::Color => "color", + ContentType::Link => "link", + ContentType::File => "file", + } + } +} + +fn get_encryption_key() -> Result<[u8; 32], AppError> { + let entry = keyring::Entry::new(KEYRING_SERVICE, KEYRING_USERNAME)?; + match entry.get_password() { + Ok(hex_key) => { + let key_bytes = + hex::decode(hex_key).map_err(|e| AppError::ClipboardHistory(e.to_string()))?; + Ok(key_bytes.try_into().unwrap()) + } + Err(keyring::Error::NoEntry) => { + let new_key: [u8; 32] = rand::random(); + let hex_key = hex::encode(new_key); + entry.set_password(&hex_key)?; + Ok(new_key) + } + Err(e) => Err(e.into()), + } +} + +fn encrypt(data: &str, key: &[u8; 32]) -> Result { + let cipher = Aes256Gcm::new(key.into()); + let nonce_bytes: [u8; 12] = rand::random(); + let nonce = Nonce::from_slice(&nonce_bytes); + let ciphertext = cipher + .encrypt(nonce, data.as_bytes()) + .map_err(|e| AppError::ClipboardHistory(e.to_string()))?; + + let mut result = nonce_bytes.to_vec(); + result.extend_from_slice(&ciphertext); + Ok(hex::encode(result)) +} + +fn decrypt(hex_data: &str, key: &[u8; 32]) -> Result { + let data = hex::decode(hex_data).map_err(|e| AppError::ClipboardHistory(e.to_string()))?; + if data.len() < 12 { + return Err(AppError::ClipboardHistory("Invalid encrypted data".into())); + } + let (nonce_bytes, ciphertext) = data.split_at(12); + let nonce = Nonce::from_slice(nonce_bytes); + let cipher = Aes256Gcm::new(key.into()); + let decrypted_bytes = cipher + .decrypt(nonce, ciphertext) + .map_err(|e| AppError::ClipboardHistory(e.to_string()))?; + String::from_utf8(decrypted_bytes).map_err(|e| AppError::ClipboardHistory(e.to_string())) +} + +pub struct ClipboardHistoryManager { + db: Mutex, + key: [u8; 32], + image_dir: PathBuf, +} + +impl ClipboardHistoryManager { + fn new(app_handle: AppHandle) -> Result { + let data_dir = app_handle + .path() + .app_local_data_dir() + .map_err(|_| AppError::DirectoryNotFound)?; + let image_dir = data_dir.join("clipboard_images"); + std::fs::create_dir_all(&image_dir)?; + + let db_path = data_dir.join("clipboard_history.sqlite"); + let db = Connection::open(db_path)?; + + let key = get_encryption_key()?; + + Ok(Self { + db: Mutex::new(db), + key, + image_dir, + }) + } + + fn init_db(&self) -> RusqliteResult<()> { + let db = self.db.lock().unwrap(); + db.execute( + "CREATE TABLE IF NOT EXISTS clipboard_history ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + hash TEXT UNIQUE NOT NULL, + content_type TEXT NOT NULL, + encrypted_content TEXT NOT NULL, + source_app_name TEXT, + first_copied_at INTEGER NOT NULL, + last_copied_at INTEGER NOT NULL, + times_copied INTEGER NOT NULL DEFAULT 1, + is_pinned INTEGER NOT NULL DEFAULT 0 + )", + [], + )?; + Ok(()) + } + + fn add_item( + &self, + hash: String, + content_type: ContentType, + content_value: String, + source_app_name: Option, + ) -> Result<(), AppError> { + let db = self.db.lock().unwrap(); + let now = Utc::now(); + + let existing_item: RusqliteResult = db.query_row( + "SELECT id FROM clipboard_history WHERE hash = ?", + params![&hash], + |row| row.get(0), + ); + + if let Ok(_id) = existing_item { + db.execute( + "UPDATE clipboard_history SET last_copied_at = ?, times_copied = times_copied + 1 WHERE hash = ?", + params![now.timestamp(), &hash], + )?; + } else { + let encrypted_content = encrypt(&content_value, &self.key)?; + db.execute( + "INSERT INTO clipboard_history (hash, content_type, encrypted_content, source_app_name, first_copied_at, last_copied_at) + VALUES (?, ?, ?, ?, ?, ?)", + params![hash, content_type.as_str(), encrypted_content, source_app_name, now.timestamp(), now.timestamp()], + )?; + } + Ok(()) + } + + fn get_items(&self, filter: String, limit: u32) -> Result, AppError> { + let db = self.db.lock().unwrap(); + let mut query = "SELECT id, hash, content_type, encrypted_content, source_app_name, first_copied_at, last_copied_at, times_copied, is_pinned FROM clipboard_history".to_string(); + + match filter.as_str() { + "all" => {} + "pinned" => query.push_str(" WHERE is_pinned = 1"), + "text" => query.push_str(" WHERE content_type = 'text'"), + "image" => query.push_str(" WHERE content_type = 'image'"), + "link" => query.push_str(" WHERE content_type = 'link'"), + "color" => query.push_str(" WHERE content_type = 'color'"), + _ => {} + } + + query.push_str(" ORDER BY last_copied_at DESC LIMIT ?"); + + let mut stmt = db.prepare(&query)?; + let items = stmt.query_map(params![limit], |row| { + let encrypted_content: String = row.get(3)?; + let first_ts: i64 = row.get(5)?; + let last_ts: i64 = row.get(6)?; + + Ok(ClipboardItem { + id: row.get(0)?, + hash: row.get(1)?, + content_type: ContentType::from_str(&row.get::<_, String>(2)?) + .unwrap_or(ContentType::Text), + content_value: decrypt(&encrypted_content, &self.key).unwrap_or_default(), + source_app_name: row.get(4)?, + first_copied_at: DateTime::from_naive_utc_and_offset( + NaiveDateTime::from_timestamp_opt(first_ts, 0).unwrap_or_default(), + Utc, + ), + last_copied_at: DateTime::from_naive_utc_and_offset( + NaiveDateTime::from_timestamp_opt(last_ts, 0).unwrap_or_default(), + Utc, + ), + times_copied: row.get(7)?, + is_pinned: row.get::<_, i32>(8)? == 1, + }) + })?; + + items.collect::, _>>().map_err(|e| e.into()) + } + + fn delete_item(&self, id: i64) -> RusqliteResult { + self.db + .lock() + .unwrap() + .execute("DELETE FROM clipboard_history WHERE id = ?", params![id]) + } + + fn toggle_pin(&self, id: i64) -> RusqliteResult { + self.db.lock().unwrap().execute( + "UPDATE clipboard_history SET is_pinned = 1 - is_pinned WHERE id = ?", + params![id], + ) + } + + fn clear_all(&self) -> RusqliteResult { + self.db + .lock() + .unwrap() + .execute("DELETE FROM clipboard_history WHERE is_pinned = 0", []) + } +} + +static MANAGER: Lazy>> = Lazy::new(|| Mutex::new(None)); + +pub fn init(app_handle: AppHandle) { + let mut manager_guard = MANAGER.lock().unwrap(); + if manager_guard.is_none() { + match ClipboardHistoryManager::new(app_handle.clone()) { + Ok(manager) => { + if let Err(e) = manager.init_db() { + eprintln!("Failed to initialize clipboard history database: {:?}", e); + return; + } + *manager_guard = Some(manager); + drop(manager_guard); + start_monitoring(app_handle); + } + Err(e) => eprintln!("Failed to create ClipboardHistoryManager: {:?}", e), + } + } +} + +fn start_monitoring(_app_handle: AppHandle) { + std::thread::spawn(move || { + let mut last_text_hash = String::new(); + let mut last_image_hash = String::new(); + let mut clipboard = arboard::Clipboard::new().unwrap(); + + loop { + if let Ok(text) = clipboard.get_text() { + let text = text.trim(); + if !text.is_empty() { + let current_hash = hex::encode(Sha256::digest(text.as_bytes())); + if current_hash != last_text_hash { + let (content_type, content_value) = if COLOR_REGEX.is_match(text) { + (ContentType::Color, text.to_string()) + } else if URL_REGEX.is_match(text) { + (ContentType::Link, text.to_string()) + } else { + (ContentType::Text, text.to_string()) + }; + + if let Some(manager) = MANAGER.lock().unwrap().as_ref() { + if let Err(e) = manager.add_item( + current_hash.clone(), + content_type, + content_value, + None, + ) { + eprintln!("Error adding clipboard text item: {:?}", e); + } + } + last_text_hash = current_hash; + last_image_hash.clear(); + } + } + } + + if let Ok(image_data) = clipboard.get_image() { + let current_hash = hex::encode(Sha256::digest(&image_data.bytes)); + if current_hash != last_image_hash { + if let Some(manager) = MANAGER.lock().unwrap().as_ref() { + let image_path = manager.image_dir.join(format!("{}.png", ¤t_hash)); + match image::save_buffer( + &image_path, + &image_data.bytes, + image_data.width as u32, + image_data.height as u32, + image::ColorType::Rgba8, + ) { + Ok(_) => { + let content_value = image_path.to_string_lossy().to_string(); + if let Err(e) = manager.add_item( + current_hash.clone(), + ContentType::Image, + content_value, + None, + ) { + eprintln!("Error adding clipboard image item: {:?}", e); + } + } + Err(e) => eprintln!("Failed to save image: {:?}", e), + } + } + last_image_hash = current_hash; + last_text_hash.clear(); + } + } + std::thread::sleep(Duration::from_millis(500)); + } + }); +} + +#[tauri::command] +pub fn history_get_items(filter: String, limit: u32) -> Result, String> { + if let Some(manager) = MANAGER.lock().unwrap().as_ref() { + manager.get_items(filter, limit).map_err(|e| e.to_string()) + } else { + Err("Clipboard history manager not initialized".to_string()) + } +} + +#[tauri::command] +pub fn history_delete_item(id: i64) -> Result<(), String> { + if let Some(manager) = MANAGER.lock().unwrap().as_ref() { + manager.delete_item(id).map_err(|e| e.to_string())?; + Ok(()) + } else { + Err("Clipboard history manager not initialized".to_string()) + } +} + +#[tauri::command] +pub fn history_toggle_pin(id: i64) -> Result<(), String> { + if let Some(manager) = MANAGER.lock().unwrap().as_ref() { + manager.toggle_pin(id).map_err(|e| e.to_string())?; + Ok(()) + } else { + Err("Clipboard history manager not initialized".to_string()) + } +} + +#[tauri::command] +pub fn history_clear_all() -> Result<(), String> { + if let Some(manager) = MANAGER.lock().unwrap().as_ref() { + manager.clear_all().map_err(|e| e.to_string())?; + Ok(()) + } else { + Err("Clipboard history manager not initialized".to_string()) + } +} \ No newline at end of file diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs index 2393bd5..7fa1bab 100644 --- a/src-tauri/src/error.rs +++ b/src-tauri/src/error.rs @@ -6,6 +6,9 @@ pub enum AppError { Serialization(String), DirectoryNotFound, CacheError(String), + Rusqlite(rusqlite::Error), + Keyring(keyring::Error), + ClipboardHistory(String), } impl From for AppError { @@ -14,6 +17,18 @@ impl From for AppError { } } +impl From for AppError { + fn from(error: rusqlite::Error) -> Self { + AppError::Rusqlite(error) + } +} + +impl From for AppError { + fn from(error: keyring::Error) -> Self { + AppError::Keyring(error) + } +} + impl From for AppError { fn from(error: bincode::error::DecodeError) -> Self { AppError::Serialization(format!("Decode error: {}", error)) @@ -33,8 +48,11 @@ impl std::fmt::Display for AppError { AppError::Serialization(msg) => write!(f, "Serialization error: {}", msg), AppError::DirectoryNotFound => write!(f, "Directory not found"), AppError::CacheError(msg) => write!(f, "Cache error: {}", msg), + AppError::Rusqlite(err) => write!(f, "Database error: {}", err), + AppError::Keyring(err) => write!(f, "Keychain error: {}", err), + AppError::ClipboardHistory(msg) => write!(f, "Clipboard history error: {}", msg), } } } -impl std::error::Error for AppError {} +impl std::error::Error for AppError {} \ No newline at end of file diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index bef2af3..3a48c16 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,6 +2,7 @@ mod app; mod browser_extension; mod cache; mod clipboard; +mod clipboard_history; mod desktop; mod error; mod extensions; @@ -15,7 +16,6 @@ use std::process::Command; use std::thread; use std::time::Duration; use tauri::{Emitter, Manager}; -use tauri_plugin_deep_link::DeepLinkExt; #[tauri::command] fn get_installed_apps() -> Vec { @@ -139,12 +139,19 @@ pub fn run() { clipboard::clipboard_clear, oauth::oauth_set_tokens, oauth::oauth_get_tokens, - oauth::oauth_remove_tokens + oauth::oauth_remove_tokens, + clipboard_history::history_get_items, + clipboard_history::history_delete_item, + clipboard_history::history_toggle_pin, + clipboard_history::history_clear_all ]) .setup(|app| { let app_handle = app.handle().clone(); tauri::async_runtime::spawn(browser_extension::run_server(app_handle)); + let app_handle_for_history = app.handle().clone(); + clipboard_history::init(app_handle_for_history); + setup_background_refresh(); setup_global_shortcut(app)?; @@ -152,4 +159,4 @@ pub fn run() { }) .run(tauri::generate_context!()) .expect("error while running tauri application"); -} +} \ No newline at end of file