diff --git a/Cargo.lock b/Cargo.lock index 5186b44..12b0ce5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,7 +241,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -256,6 +256,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "ashpd" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "ast_node" version = "0.9.9" @@ -265,9 +286,117 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.81", + "syn 2.0.90", +] + +[[package]] +name = "async-broadcast" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.83" @@ -276,7 +405,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -302,6 +431,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -527,6 +662,19 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "blowfish" version = "0.9.1" @@ -557,7 +705,7 @@ dependencies = [ "proc-macro-crate 2.0.2", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", "syn_derive", ] @@ -668,7 +816,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -711,7 +859,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -878,6 +1026,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1069,7 +1226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1085,7 +1242,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1151,7 +1308,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1162,7 +1319,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1215,7 +1372,7 @@ dependencies = [ "swc_ecma_parser", "swc_eq_ignore_macros", "text_lines", - "thiserror", + "thiserror 1.0.65", "unicode-width", "url", ] @@ -1261,6 +1418,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -1271,7 +1439,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1313,6 +1481,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + [[package]] name = "dlopen2" version = "0.7.0" @@ -1333,9 +1510,15 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" version = "0.1.1" @@ -1517,6 +1700,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "env_filter" version = "0.1.2" @@ -1559,6 +1769,27 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "exr" version = "1.72.0" @@ -1683,7 +1914,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1709,7 +1940,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1760,6 +1991,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -1768,7 +2012,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -1993,7 +2237,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -2026,7 +2270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaeb9672a55e9e32cb6d3ef781e7526b25ab97d499fae71615649340b143424" dependencies = [ "serde", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -2038,7 +2282,7 @@ dependencies = [ "git-ref-format-core", "proc-macro-error", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -2074,7 +2318,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -2088,7 +2332,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -2178,7 +2422,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -2234,6 +2478,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2508,7 +2758,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -2535,7 +2785,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -2603,7 +2853,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.65", "walkdir", "windows-sys 0.45.0", ] @@ -2647,7 +2897,7 @@ dependencies = [ "jsonptr", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -2952,7 +3202,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", @@ -2974,7 +3224,7 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror", + "thiserror 1.0.65", "windows-sys 0.59.0", ] @@ -3001,7 +3251,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -3025,6 +3275,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "libc", + "memoffset", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -3098,7 +3360,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -3160,7 +3422,7 @@ dependencies = [ "proc-macro-crate 2.0.2", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -3290,6 +3552,7 @@ checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", "block2", + "dispatch", "libc", "objc2", ] @@ -3437,6 +3700,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "os_pipe" version = "1.2.1" @@ -3510,6 +3783,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -3673,7 +3952,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -3715,6 +3994,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -3750,7 +4040,7 @@ checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", "indexmap 2.6.0", - "quick-xml", + "quick-xml 0.32.0", "serde", "time", ] @@ -3768,6 +4058,21 @@ dependencies = [ "miniz_oxide 0.8.0", ] +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -3873,9 +4178,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3896,7 +4201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" dependencies = [ "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -3961,6 +4266,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.37" @@ -3998,7 +4312,7 @@ dependencies = [ "siphasher 1.0.1", "sqlite", "tempfile", - "thiserror", + "thiserror 1.0.65", "unicode-normalization", ] @@ -4018,7 +4332,7 @@ dependencies = [ "radicle-git-ext", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -4038,7 +4352,7 @@ dependencies = [ "serde", "sqlite", "ssh-key", - "thiserror", + "thiserror 1.0.65", "zeroize", ] @@ -4062,7 +4376,7 @@ dependencies = [ "percent-encoding", "radicle-std-ext", "serde", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -4073,7 +4387,7 @@ checksum = "fbee758010fb64482be4b18591fbeb3cbc15b16450d143edf4edb5484c7366c6" dependencies = [ "byteorder", "log", - "thiserror", + "thiserror 1.0.65", "zeroize", ] @@ -4099,7 +4413,7 @@ dependencies = [ "radicle-std-ext", "serde", "tar", - "thiserror", + "thiserror 1.0.65", "url", ] @@ -4118,10 +4432,11 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-clipboard-manager", + "tauri-plugin-dialog", "tauri-plugin-log", "tauri-plugin-shell", "tauri-plugin-window-state", - "thiserror", + "thiserror 1.0.65", "tokio", "ts-rs", ] @@ -4139,7 +4454,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "thiserror", + "thiserror 1.0.65", "tree-sitter-bash", "tree-sitter-c", "tree-sitter-css", @@ -4275,7 +4590,7 @@ dependencies = [ "rand_chacha 0.3.1", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.65", "v_frame", "wasm-bindgen", ] @@ -4337,7 +4652,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -4425,6 +4740,29 @@ dependencies = [ "subtle", ] +[[package]] +name = "rfd" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +dependencies = [ + "ashpd", + "block2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rgb" version = "0.8.50" @@ -4576,7 +4914,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -4668,7 +5006,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -4679,7 +5017,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -4713,7 +5051,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -4764,7 +5102,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -4799,6 +5137,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -4836,6 +5185,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.6.4" @@ -5143,7 +5501,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -5243,7 +5601,7 @@ checksum = "695a1d8b461033d32429b5befbf0ad4d7a2c4d6ba9cd5ba4e0645c615839e8e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -5254,7 +5612,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -5277,7 +5635,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -5304,9 +5662,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.81" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198514704ca887dd5a1e408c6c6cdcba43672f9b4062e1b24aa34e74e6d7faae" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -5322,7 +5680,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -5400,7 +5758,7 @@ checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -5465,7 +5823,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror", + "thiserror 1.0.65", "tokio", "tray-icon", "url", @@ -5518,9 +5876,9 @@ dependencies = [ "serde", "serde_json", "sha2", - "syn 2.0.81", + "syn 2.0.90", "tauri-utils", - "thiserror", + "thiserror 1.0.65", "time", "url", "uuid", @@ -5536,7 +5894,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", "tauri-codegen", "tauri-utils", ] @@ -5571,7 +5929,48 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror", + "thiserror 1.0.65", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b59fd750551b1066744ab956a1cd6b1ea3e1b3763b0b9153ac27a044d596426" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.6", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1edf18000f02903a7c2e5997fb89aca455ecbc0acc15c6535afbb883be223" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.6", + "toml 0.8.2", + "url", + "uuid", ] [[package]] @@ -5592,7 +5991,7 @@ dependencies = [ "swift-rs", "tauri", "tauri-plugin", - "thiserror", + "thiserror 1.0.65", "time", ] @@ -5613,7 +6012,7 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror", + "thiserror 1.0.65", "tokio", ] @@ -5629,7 +6028,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -5646,7 +6045,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror", + "thiserror 1.0.65", "url", "windows", ] @@ -5708,7 +6107,7 @@ dependencies = [ "serde_with", "serialize-to-javascript", "swift-rs", - "thiserror", + "thiserror 1.0.65", "toml 0.8.2", "url", "urlpattern", @@ -5772,7 +6171,7 @@ dependencies = [ "radicle-types", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", "tokio", "tower-http", ] @@ -5798,7 +6197,16 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.65", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -5809,7 +6217,18 @@ checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -5882,8 +6301,10 @@ dependencies = [ "libc", "mio", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] @@ -5895,7 +6316,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -6032,7 +6453,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -6061,7 +6482,7 @@ dependencies = [ "once_cell", "png", "serde", - "thiserror", + "thiserror 1.0.65", "windows-sys 0.59.0", ] @@ -6127,7 +6548,7 @@ dependencies = [ "lazy_static", "regex", "streaming-iterator", - "thiserror", + "thiserror 1.0.65", "tree-sitter", ] @@ -6262,7 +6683,7 @@ dependencies = [ "dprint-plugin-typescript", "lazy_static", "serde_json", - "thiserror", + "thiserror 1.0.65", "ts-rs-macros", ] @@ -6274,7 +6695,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", "termcolor", ] @@ -6296,6 +6717,17 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -6540,7 +6972,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -6574,7 +7006,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6598,6 +7030,66 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +dependencies = [ + "bitflags 2.6.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml 0.36.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.72" @@ -6674,7 +7166,7 @@ checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -6683,7 +7175,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ - "thiserror", + "thiserror 1.0.65", "windows", "windows-core 0.58.0", ] @@ -6779,7 +7271,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -6790,7 +7282,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -7096,7 +7588,7 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror", + "thiserror 1.0.65", "webkit2gtk", "webkit2gtk-sys", "webview2-com", @@ -7164,6 +7656,75 @@ dependencies = [ "rustix", ] +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "zbus" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8e3d6ae3342792a6cc2340e4394334c7402f3d793b390d2c5494a4032b3030" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "derivative", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand 0.8.5", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3e850ff1e7217a3b7a07eba90d37fe9bb9e89a310f718afcde5885ca9b6d7" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -7182,7 +7743,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.81", + "syn 2.0.90", ] [[package]] @@ -7214,3 +7775,41 @@ checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" dependencies = [ "zune-core", ] + +[[package]] +name = "zvariant" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e09e8be97d44eeab994d752f341e67b3b0d80512a8b315a0671d47232ef1b65" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "url", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a5857e2856435331636a9fbb415b09243df4521a267c5bedcd5289b4d5799e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00bedb16a193cc12451873fee2a1bc6550225acece0e36f333e68326c73c8172" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/crates/radicle-tauri/Cargo.toml b/crates/radicle-tauri/Cargo.toml index 356ac07..8f67275 100644 --- a/crates/radicle-tauri/Cargo.toml +++ b/crates/radicle-tauri/Cargo.toml @@ -31,6 +31,7 @@ tauri-plugin-window-state = { version = "2.0.1" } thiserror = { version = "1.0.64" } ts-rs = { version = "10.0.0", features = ["serde-json-impl", "no-serde-warnings"] } tokio = { version = "1.40.0", features = ["time"] } +tauri-plugin-dialog = { version = "2.2.0" } [features] # by default Tauri runs in production mode diff --git a/crates/radicle-tauri/capabilities/default.json b/crates/radicle-tauri/capabilities/default.json index 164ebf9..e7b13eb 100644 --- a/crates/radicle-tauri/capabilities/default.json +++ b/crates/radicle-tauri/capabilities/default.json @@ -16,6 +16,7 @@ "shell:allow-open", "clipboard-manager:default", "clipboard-manager:allow-write-text", - "log:default" + "log:default", + "dialog:default" ] } diff --git a/crates/radicle-tauri/src/commands/cob.rs b/crates/radicle-tauri/src/commands/cob.rs index 608d93b..29a4acf 100644 --- a/crates/radicle-tauri/src/commands/cob.rs +++ b/crates/radicle-tauri/src/commands/cob.rs @@ -1,3 +1,7 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result}; + use radicle::cob; use radicle::git; use radicle::identity; @@ -5,6 +9,8 @@ use radicle_types as types; use radicle_types::error::Error; use radicle_types::traits::cobs::Cobs; use radicle_types::traits::thread::Thread; +use tauri_plugin_clipboard_manager::ClipboardExt; +use tauri_plugin_dialog::DialogExt; use crate::AppState; @@ -13,22 +19,69 @@ pub mod issue; pub mod patch; #[tauri::command] -pub async fn get_file_by_oid( +pub async fn get_embed( ctx: tauri::State<'_, AppState>, rid: identity::RepoId, oid: git::Oid, -) -> Result { +) -> Result, Error> { ctx.get_embed(rid, oid) } #[tauri::command] -pub async fn save_embed( +pub async fn save_embed_by_path( + ctx: tauri::State<'_, AppState>, + rid: identity::RepoId, + path: PathBuf, +) -> Result { + ctx.save_embed_by_path(rid, path) +} + +#[tauri::command] +pub async fn save_embed_by_clipboard( + app_handle: tauri::AppHandle, ctx: tauri::State<'_, AppState>, rid: identity::RepoId, - name: &str, - bytes: &[u8], + name: String, ) -> Result { - ctx.save_embed(rid, name, bytes) + let content = app_handle + .clipboard() + .read_image() + .map(|i| i.rgba().to_vec()) + .context("Not able to read the image from the clipboard")?; + + ctx.save_embed_by_bytes(rid, name, content) +} + +#[tauri::command] +pub async fn save_embed_by_bytes( + ctx: tauri::State<'_, AppState>, + rid: identity::RepoId, + name: String, + bytes: Vec, +) -> Result { + ctx.save_embed_by_bytes(rid, name, bytes) +} + +#[tauri::command] +pub async fn save_embed_to_disk( + app_handle: tauri::AppHandle, + ctx: tauri::State<'_, AppState>, + rid: identity::RepoId, + oid: git::Oid, + name: String, +) -> Result<(), Error> { + let path = app_handle + .dialog() + .file() + .set_file_name(name) + .blocking_save_file() + .context("no path defined")?; + + let path = path + .into_path() + .context("Not able to convert into PathBuf")?; + + ctx.save_embed_to_disk(rid, oid, path) } #[tauri::command] diff --git a/crates/radicle-tauri/src/lib.rs b/crates/radicle-tauri/src/lib.rs index d4c8261..13b67c3 100644 --- a/crates/radicle-tauri/src/lib.rs +++ b/crates/radicle-tauri/src/lib.rs @@ -13,7 +13,9 @@ use commands::{auth, cob, diff, profile, repo, thread}; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { #[cfg(debug_assertions)] - let builder = tauri::Builder::default().plugin(tauri_plugin_log::Builder::new().build()); + let builder = tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_log::Builder::new().build()); #[cfg(not(debug_assertions))] let builder = tauri::Builder::default(); @@ -74,9 +76,12 @@ pub fn run() { repo::diff_stats, repo::list_commits, diff::get_diff, - cob::get_file_by_oid, + cob::get_embed, + cob::save_embed_to_disk, cob::activity_by_id, - cob::save_embed, + cob::save_embed_by_path, + cob::save_embed_by_clipboard, + cob::save_embed_by_bytes, cob::issue::list_issues, cob::issue::issue_by_id, cob::issue::comment_threads_by_issue_id, diff --git a/crates/radicle-types/src/error.rs b/crates/radicle-types/src/error.rs index 20df15a..54740b8 100644 --- a/crates/radicle-types/src/error.rs +++ b/crates/radicle-types/src/error.rs @@ -9,6 +9,14 @@ pub enum Error { #[error(transparent)] Profile(#[from] radicle::profile::Error), + /// Anyhow error. + #[error(transparent)] + Anyhow(#[from] anyhow::Error), + + /// Io error. + #[error(transparent)] + Io(#[from] std::io::Error), + /// Crypto error. #[error(transparent)] Crypto(#[from] radicle::crypto::ssh::keystore::Error), diff --git a/crates/radicle-types/src/traits/repo.rs b/crates/radicle-types/src/traits/repo.rs index ac41053..32d6230 100644 --- a/crates/radicle-types/src/traits/repo.rs +++ b/crates/radicle-types/src/traits/repo.rs @@ -202,6 +202,7 @@ pub trait Repo: Profile { #[allow(clippy::unwrap_used)] mod test { use std::str::FromStr; + use std::vec; use radicle::crypto::test::signer::MockSigner; use radicle::{git, test}; diff --git a/crates/radicle-types/src/traits/thread.rs b/crates/radicle-types/src/traits/thread.rs index 2ed718e..5c5eeda 100644 --- a/crates/radicle-types/src/traits/thread.rs +++ b/crates/radicle-types/src/traits/thread.rs @@ -1,4 +1,5 @@ -use base64::{engine::general_purpose::STANDARD, Engine as _}; +use std::fs; + use localtime::LocalTime; use radicle::cob; @@ -14,23 +15,51 @@ use crate::error::Error; use crate::traits::Profile; pub trait Thread: Profile { - fn get_embed(&self, rid: identity::RepoId, oid: git::Oid) -> Result { + fn get_embed(&self, rid: identity::RepoId, oid: git::Oid) -> Result, Error> { + let profile = self.profile(); + let repo = profile.storage.repository(rid)?; + let blob = repo.blob(oid)?; + + Ok::<_, Error>(blob.content().to_vec()) + } + + fn save_embed_to_disk( + &self, + rid: identity::RepoId, + oid: git::Oid, + path: std::path::PathBuf, + ) -> Result<(), Error> { let profile = self.profile(); let repo = profile.storage.repository(rid)?; let blob = repo.blob(oid)?; + fs::write(path, blob.content())?; - Ok::<_, Error>(STANDARD.encode(blob.content())) + Ok::<_, Error>(()) + } + + fn save_embed_by_path( + &self, + rid: identity::RepoId, + path: std::path::PathBuf, + ) -> Result { + let profile = self.profile(); + let repo = profile.storage.repository(rid)?; + let bytes = fs::read(path.clone())?; + let name = path.file_name().and_then(|s| s.to_str()).unwrap_or("embed"); + let embed = radicle::cob::Embed::::store(name, &bytes, &repo.backend)?; + + Ok(embed.oid()) } - fn save_embed( + fn save_embed_by_bytes( &self, rid: identity::RepoId, - name: &str, - bytes: &[u8], + name: String, + bytes: Vec, ) -> Result { let profile = self.profile(); let repo = profile.storage.repository(rid)?; - let embed = radicle::cob::Embed::::store(name, bytes, &repo.backend)?; + let embed = radicle::cob::Embed::::store(&name, &bytes, &repo.backend)?; Ok(embed.oid()) } diff --git a/crates/test-http-api/src/api.rs b/crates/test-http-api/src/api.rs index 6ce96ab..c42e2e5 100644 --- a/crates/test-http-api/src/api.rs +++ b/crates/test-http-api/src/api.rs @@ -1,4 +1,5 @@ use std::ops::Deref; +use std::path::PathBuf; use std::sync::Arc; use axum::extract::State; @@ -67,8 +68,11 @@ pub fn router(ctx: Context) -> Router { .route("/list_patches", post(patches_handler)) .route("/patch_by_id", post(patch_handler)) .route("/revisions_by_patch", post(revision_handler)) - .route("/get_file_by_oid", post(get_embeds_handler)) - .route("/save_embed", post(save_embed_handler)) + .route("/get_embed", post(get_embeds_handler)) + .route("/save_embed_by_path", post(save_embed_handler)) + .route("/save_embed_by_clipboard", post(save_embed_handler)) + .route("/save_embed_by_bytes", post(save_embed_handler)) + .route("/save_embed_to_disk", post(save_embed_handler)) .layer( CorsLayer::new() .allow_origin(cors::Any) @@ -255,15 +259,14 @@ async fn get_embeds_handler( #[derive(Serialize, Deserialize)] struct CreateEmbedBody { pub rid: identity::RepoId, - pub name: String, - pub content: Vec, + pub path: PathBuf, } async fn save_embed_handler( State(ctx): State, - Json(CreateEmbedBody { rid, name, content }): Json, + Json(CreateEmbedBody { rid, path }): Json, ) -> impl IntoResponse { - let embed = ctx.save_embed(rid, &name, &content)?; + let embed = ctx.save_embed_by_path(rid, path)?; Ok::<_, Error>(Json(embed)) } diff --git a/package-lock.json b/package-lock.json index 2c38951..9196cfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^2.1.1", - "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-clipboard-manager": "^2.2.0", + "@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-log": "^2.0.0", "@tauri-apps/plugin-shell": "^2.0.0", "@tauri-apps/plugin-window-state": "^2.0.0" @@ -1341,10 +1342,17 @@ } }, "node_modules/@tauri-apps/plugin-clipboard-manager": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0.tgz", - "integrity": "sha512-V1sXmbjnwfXt/r48RJMwfUmDMSaP/8/YbH4CLNxt+/sf1eHlIP8PRFdFDQwLN0cNQKu2rqQVbG/Wc/Ps6cDUhw==", - "license": "MIT OR Apache-2.0", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.2.0.tgz", + "integrity": "sha512-sIBrW/HioKq2vqomwwcU/Y8ygAv3DlS32yKPBX5XijCc0IyQKiDxYpGqmvE9DC5Y0lNJ/G53dfS961B31wjJ1g==", + "dependencies": { + "@tauri-apps/api": "^2.0.0" + } + }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.2.0.tgz", + "integrity": "sha512-6bLkYK68zyK31418AK5fNccCdVuRnNpbxquCl8IqgFByOgWFivbiIlvb79wpSXi0O+8k8RCSsIpOquebusRVSg==", "dependencies": { "@tauri-apps/api": "^2.0.0" } diff --git a/package.json b/package.json index 18683a8..ae6e881 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "license": "MIT", "dependencies": { "@tauri-apps/api": "^2.1.1", - "@tauri-apps/plugin-clipboard-manager": "^2.0.0", + "@tauri-apps/plugin-clipboard-manager": "^2.2.0", + "@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-log": "^2.0.0", "@tauri-apps/plugin-shell": "^2.0.0", "@tauri-apps/plugin-window-state": "^2.0.0" diff --git a/public/index.css b/public/index.css index ccd3f31..ac2e928 100644 --- a/public/index.css +++ b/public/index.css @@ -64,6 +64,12 @@ body { font-weight: var(--font-weight-regular); } +@media (max-width: 1600px) { + .global-hide-on-small-desktop-down { + display: none !important; + } +} + :root { --elevation-low: 0 0 48px 0 #000000ee; } diff --git a/src/components/Border.svelte b/src/components/Border.svelte index 7a48abe..a52c934 100644 --- a/src/components/Border.svelte +++ b/src/components/Border.svelte @@ -6,6 +6,7 @@ variant: "primary" | "secondary" | "ghost" | "float" | "danger"; hoverable?: boolean; onclick?: () => void; + stylePosition?: string; stylePadding?: string; styleHeight?: string; styleMinHeight?: string; @@ -27,6 +28,7 @@ stylePadding, styleHeight, styleMinHeight, + stylePosition, styleWidth, styleCursor = "default", styleGap = "0.5rem", @@ -222,6 +224,7 @@
+ import type { UnlistenFn } from "@tauri-apps/api/event"; import type { ComponentProps } from "svelte"; - import type { Embed } from "@bindings/cob/thread/Embed"; import * as utils from "@app/lib/utils"; + import type { Embed } from "@bindings/cob/thread/Embed"; + import { invoke } from "@app/lib/invoke"; + import { listen } from "@tauri-apps/api/event"; + import { onDestroy, onMount } from "svelte"; + import { open } from "@tauri-apps/plugin-dialog"; + import Button from "./Button.svelte"; import Icon from "./Icon.svelte"; import Markdown from "./Markdown.svelte"; @@ -11,6 +17,7 @@ import OutlineButton from "./OutlineButton.svelte"; interface Props { + styleMinHeight?: string; rid: string; placeholder?: string; submitCaption?: string; @@ -21,6 +28,7 @@ submitInProgress?: boolean; disallowEmptyBody?: boolean; isValid?: () => boolean; + preview?: boolean; stylePadding?: string; borderVariant?: ComponentProps["borderVariant"]; submit: (opts: { @@ -32,6 +40,8 @@ /* eslint-disable prefer-const */ let { + preview = $bindable(false), + styleMinHeight, rid, placeholder = "Leave your comment", submitCaption = "Comment", @@ -49,12 +59,124 @@ }: Props = $props(); /* eslint-enable prefer-const */ - let preview: boolean = $state(false); - let selectionStart = $state(0); - let selectionEnd = $state(0); - let inputFiles: FileList | undefined = $state(undefined); + let selectionStart = $state(body.length); + let selectionEnd = $state(body.length); + let draggingOver = $state(false); + let dragEnterUnlistenFn: UnlistenFn | undefined = undefined; + let dragLeaveUnlistenFn: UnlistenFn | undefined = undefined; + let dragDropUnlistenFn: UnlistenFn | undefined = undefined; + + function updateBodyAndSelection(input: string[], pre: string, after: string) { + const allEmbeds = input.join(""); + body = pre.concat(allEmbeds, after); + selectionStart = pre.length + allEmbeds.length; + selectionEnd = pre.length + allEmbeds.length; + } + + function splitBody() { + return [body.substring(0, selectionStart), body.substring(selectionStart)]; + } + + onMount(async () => { + if (window.__TAURI_INTERNALS__) { + dragEnterUnlistenFn = await listen("tauri://drag-enter", () => { + draggingOver = true; + }); + + dragLeaveUnlistenFn = await listen("tauri://drag-leave", () => { + draggingOver = false; + }); + + dragDropUnlistenFn = await listen<{ + paths: string[]; + position: { x: number; y: number }; + }>("tauri://drag-drop", async event => { + draggingOver = false; + const [preBody, afterBody] = splitBody(); + + return Promise.all( + event.payload.paths.map(async path => { + const name = path.split("/").at(-1); + const uploadLabel = `[Uploading ${name}...]()\n`; + + body = preBody.concat(uploadLabel, afterBody); + const oid = await invoke("save_embed_by_path", { + rid, + path, + }).catch(console.error); + return `[${name}](${oid})\n`; + }), + ).then(texts => updateBodyAndSelection(texts, preBody, afterBody)); + }); + } + }); + + onDestroy(() => { + if (dragEnterUnlistenFn) dragEnterUnlistenFn(); + if (dragLeaveUnlistenFn) dragLeaveUnlistenFn(); + if (dragDropUnlistenFn) dragDropUnlistenFn(); + }); + + async function attachEmbedsByPaths(paths: string[]) { + const [preBody, afterBody] = splitBody(); + + return Promise.all( + paths.map(async path => { + const name = path.split("/").at(-1); + const uploadLabel = `[Uploading ${name}...]()\n`; + body = preBody.concat(uploadLabel, afterBody); + const oid = await invoke("save_embed_by_path", { + rid, + path, + }).catch(console.error); + return `[${name}](${oid})\n`; + }), + ).then(texts => updateBodyAndSelection(texts, preBody, afterBody)); + } + + async function handlePaste(e: ClipboardEvent) { + if (e.clipboardData?.files && e.clipboardData.files.length > 0) { + e.preventDefault(); + const [preBody, afterBody] = splitBody(); + // We read the buffer on the backend, if it's a image buffer. + if (e.clipboardData.items.length === 1) { + const file = e.clipboardData.files[0]; + const uploadLabel = `[Uploading...]()\n`; + body = preBody.concat(uploadLabel, afterBody); + const oid = await invoke("save_embed_by_clipboard", { + name: file.name, + rid, + }).catch(console.error); + body = preBody.concat(`[${file.name}](${oid})\n`, afterBody); + } else { + return Promise.all( + Array.from(e.clipboardData.files).map(async file => { + const arrayBuffer = await file.arrayBuffer(); + const bytes = new Uint8Array(arrayBuffer); + const uploadLabel = `[Uploading ${file.name}...]()\n`; + body = preBody.concat(uploadLabel, afterBody); + const oid = await invoke("save_embed_by_bytes", { + rid, + name: file.name, + bytes, + }).catch(console.error); + return `[${file.name}](${oid})\n`; + }), + ).then(texts => updateBodyAndSelection(texts, preBody, afterBody)); + } + } else { + // In case that the clipboard data isn't an array of files, + // we want to make use of the default behavior and insert the clipboard content. + } + } - const inputId = `input-label-${crypto.randomUUID()}`; + function selectFiles() { + void open({ multiple: true }).then(paths => { + if (paths) { + void attachEmbedsByPaths(paths); + } + }); + } function submitFn() { void submit({ comment: body, embeds }) @@ -87,16 +209,19 @@ .buttons { display: flex; margin-left: auto; - gap: 1rem; + gap: 0.5rem; } + .caption { font-size: var(--font-size-small); color: var(--color-fill-gray); display: flex; - align-items: center; + flex-wrap: wrap; + align-items: flex-start; gap: 0.25rem; } .preview { + width: 100%; font-size: var(--font-size-small); min-height: 109px; padding: 0.75rem; @@ -111,17 +236,14 @@
{:else} - + {#if draggingOver} +
Drop files to add them as embeds.
+ {/if} diff --git a/src/lib/file.ts b/src/lib/file.ts deleted file mode 100644 index 94b0b92..0000000 --- a/src/lib/file.ts +++ /dev/null @@ -1,38 +0,0 @@ -async function parseGitOid(bytes: Uint8Array): Promise { - // Create the header - const header = new TextEncoder().encode(`blob ${bytes.length}\0`); - - // Concatenate the header and the original file content - const combined = new Uint8Array(header.length + bytes.length); - combined.set(header); - combined.set(bytes, header.length); - - // Compute the SHA-1 hash - const hashBuffer = await crypto.subtle.digest("SHA-1", combined); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - - // Convert the hash to a hexadecimal string - return hashArray.map(b => b.toString(16).padStart(2, "0")).join(""); -} - -function base64String(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = (event: ProgressEvent) => { - if (event.target?.result && typeof event.target.result === "string") { - resolve(event.target.result); - } else { - reject(new Error("Failed to generate base64 string")); - } - }; - - reader.readAsDataURL(file); - }); -} - -export async function embed(file: File) { - const bytes = new Uint8Array(await file.arrayBuffer()); - const oid = await parseGitOid(bytes); - const content = await base64String(file); - return { oid, name: file.name, content }; -} diff --git a/src/views/repo/CreateIssue.svelte b/src/views/repo/CreateIssue.svelte index 1f1c281..cc94328 100644 --- a/src/views/repo/CreateIssue.svelte +++ b/src/views/repo/CreateIssue.svelte @@ -4,6 +4,7 @@ import type { Issue } from "@bindings/cob/issue/Issue"; import type { RepoInfo } from "@bindings/repo/RepoInfo"; import type { IssueStatus } from "./router"; + import type { Embed } from "@bindings/cob/thread/Embed"; import { invoke } from "@app/lib/invoke"; @@ -14,18 +15,14 @@ import AssigneeInput from "@app/components/AssigneeInput.svelte"; import Border from "@app/components/Border.svelte"; - import Button from "@app/components/Button.svelte"; - import Icon from "@app/components/Icon.svelte"; import InlineTitle from "@app/components/InlineTitle.svelte"; import IssueSecondColumn from "@app/components/IssueSecondColumn.svelte"; import LabelInput from "@app/components/LabelInput.svelte"; - import Markdown from "@app/components/Markdown.svelte"; - import OutlineButton from "@app/components/OutlineButton.svelte"; import Sidebar from "@app/components/Sidebar.svelte"; import TextInput from "@app/components/TextInput.svelte"; - import Textarea from "@app/components/Textarea.svelte"; import Layout from "./Layout.svelte"; + import ExtendedTextarea from "@app/components/ExtendedTextarea.svelte"; interface Props { repo: RepoInfo; @@ -36,17 +33,17 @@ const { repo, issues, config, status }: Props = $props(); - let description: string = $state(""); let preview: boolean = $state(false); let title: string = $state(""); - const embeds: { name: string; content: string }[] = []; - let assignees: Author[] = $state([]); let labels: string[] = $state([]); - async function createIssue() { - const response: Issue = await invoke("create_issue", { + async function createIssue( + description: string, + embeds: Embed[], + ): Promise { + return invoke("create_issue", { rid: repo.rid, new: { title, @@ -57,12 +54,6 @@ }, opts: { announce: $nodeRunning && $announce }, }); - void router.push({ - resource: "repo.issue", - rid: repo.rid, - issue: response.id, - status, - }); } @@ -79,12 +70,6 @@ padding: 0 1rem 1rem 1rem; height: calc(100% - 14rem); } - .body { - background-color: var(--color-background-float); - padding: 1rem; - min-height: calc(100% + 2px); - clip-path: var(--2px-corner-fill); - } .metadata-divider { width: 2px; background-color: var(--color-fill-ghost); @@ -148,55 +133,29 @@ - {#if preview} -
- {#if description.trim() === ""} - - No description. - - {:else} - - {/if} -
- {:else} -