generated from nhcarrigan/template
feat: add native clipboard support for screenshot paste
- Add Tauri clipboard-manager plugin for native clipboard access - Fall back to native clipboard when WebView clipboard API returns empty - Convert RGBA image data to PNG via canvas for saving - Allow sending messages with attachments only (no text required) - Log attached files to output with 📎 emoji - Update send button to enable when attachments exist Co-Authored-By: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
|
||||
"@tauri-apps/plugin-dialog": "^2",
|
||||
"@tauri-apps/plugin-notification": "^2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
|
||||
Generated
+10
@@ -11,6 +11,9 @@ importers:
|
||||
'@tauri-apps/api':
|
||||
specifier: ^2
|
||||
version: 2.9.1
|
||||
'@tauri-apps/plugin-clipboard-manager':
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2
|
||||
'@tauri-apps/plugin-dialog':
|
||||
specifier: ^2
|
||||
version: 2.6.0
|
||||
@@ -735,6 +738,9 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
hasBin: true
|
||||
|
||||
'@tauri-apps/plugin-clipboard-manager@2.3.2':
|
||||
resolution: {integrity: sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==}
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.6.0':
|
||||
resolution: {integrity: sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==}
|
||||
|
||||
@@ -2286,6 +2292,10 @@ snapshots:
|
||||
'@tauri-apps/cli-win32-ia32-msvc': 2.9.6
|
||||
'@tauri-apps/cli-win32-x64-msvc': 2.9.6
|
||||
|
||||
'@tauri-apps/plugin-clipboard-manager@2.3.2':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.9.1
|
||||
|
||||
'@tauri-apps/plugin-dialog@2.6.0':
|
||||
dependencies:
|
||||
'@tauri-apps/api': 2.9.1
|
||||
|
||||
Generated
+350
-5
@@ -47,6 +47,27 @@ version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arboard"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
|
||||
dependencies = [
|
||||
"clipboard-win",
|
||||
"image",
|
||||
"log",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.60.2",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
@@ -310,6 +331,12 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.0"
|
||||
@@ -449,6 +476,15 @@ dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "5.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
|
||||
dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
@@ -604,6 +640,12 @@ 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 = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -807,6 +849,12 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[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.2"
|
||||
@@ -926,6 +974,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-code"
|
||||
version = "3.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.1"
|
||||
@@ -953,6 +1007,26 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fax"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
|
||||
dependencies = [
|
||||
"fax_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fax_derive"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
@@ -978,6 +1052,12 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.8"
|
||||
@@ -994,6 +1074,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.5.0"
|
||||
@@ -1458,12 +1544,32 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[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 = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
@@ -1496,7 +1602,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hikari-desktop"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"parking_lot",
|
||||
@@ -1505,6 +1611,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-clipboard-manager",
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-notification",
|
||||
@@ -1665,7 +1772,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1776,6 +1883,20 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png 0.18.0",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -2135,6 +2256,16 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moxcms"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "muda"
|
||||
version = "0.17.1"
|
||||
@@ -2150,7 +2281,7 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation",
|
||||
"once_cell",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"serde",
|
||||
"thiserror 2.0.17",
|
||||
"windows-sys 0.60.2",
|
||||
@@ -2210,6 +2341,15 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.11.7"
|
||||
@@ -2628,6 +2768,17 @@ version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap 2.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
@@ -2817,6 +2968,19 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.11.0"
|
||||
@@ -2945,6 +3109,21 @@ dependencies = [
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -4068,7 +4247,7 @@ dependencies = [
|
||||
"ico",
|
||||
"json-patch",
|
||||
"plist",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"semver",
|
||||
@@ -4115,6 +4294,21 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-clipboard-manager"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.6.0"
|
||||
@@ -4452,6 +4646,20 @@ dependencies = [
|
||||
"syn 2.0.114",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
|
||||
dependencies = [
|
||||
"fax",
|
||||
"flate2",
|
||||
"half",
|
||||
"quick-error",
|
||||
"weezl",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.45"
|
||||
@@ -4747,12 +4955,23 @@ dependencies = [
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation",
|
||||
"once_cell",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"serde",
|
||||
"thiserror 2.0.17",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree_magic_mini"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"nom",
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
@@ -5034,6 +5253,76 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"downcast-rs",
|
||||
"rustix",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.31.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"rustix",
|
||||
"wayland-backend",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.32.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols-wlr"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.31.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quick-xml 0.38.4",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.31.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
@@ -5143,6 +5432,12 @@ dependencies = [
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@@ -5673,6 +5968,24 @@ version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "wl-clipboard-rs"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"os_pipe",
|
||||
"rustix",
|
||||
"thiserror 2.0.17",
|
||||
"tree_magic_mini",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-wlr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
@@ -5745,6 +6058,23 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
|
||||
dependencies = [
|
||||
"gethostname",
|
||||
"rustix",
|
||||
"x11rb-protocol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11rb-protocol"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
@@ -5915,6 +6245,21 @@ version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea"
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.9.1"
|
||||
|
||||
@@ -26,6 +26,7 @@ tauri-plugin-store = "2.4.2"
|
||||
tauri-plugin-notification = "2"
|
||||
tauri-plugin-os = "2"
|
||||
tauri-plugin-http = "2"
|
||||
tauri-plugin-clipboard-manager = "2"
|
||||
tempfile = "3"
|
||||
semver = "1"
|
||||
chrono = { version = "0.4.43", features = ["serde"] }
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
"notification:default",
|
||||
"notification:allow-is-permission-granted",
|
||||
"notification:allow-request-permission",
|
||||
"notification:allow-notify"
|
||||
"notification:allow-notify",
|
||||
"clipboard-manager:default",
|
||||
"clipboard-manager:allow-read-image"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_os::init())
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.manage(bridge_manager.clone())
|
||||
.manage(temp_manager.clone())
|
||||
.setup(move |app| {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { readImage } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { get } from "svelte/store";
|
||||
import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
|
||||
import { characterState } from "$lib/stores/character";
|
||||
@@ -165,10 +166,13 @@
|
||||
event.preventDefault();
|
||||
|
||||
const message = inputValue.trim();
|
||||
if (!message || isSubmitting) return;
|
||||
const hasAttachments = attachments.length > 0;
|
||||
|
||||
// Need either a message or attachments to submit
|
||||
if ((!message && !hasAttachments) || isSubmitting) return;
|
||||
|
||||
// Check for slash commands first (these work even when disconnected)
|
||||
if (isSlashCommand(message)) {
|
||||
if (message && isSlashCommand(message)) {
|
||||
// Add slash commands to history too
|
||||
addToHistory(message);
|
||||
historyIndex = -1;
|
||||
@@ -189,8 +193,10 @@
|
||||
// Regular messages require connection
|
||||
if (!isConnected) return;
|
||||
|
||||
// Add to history before clearing
|
||||
addToHistory(message);
|
||||
// Add to history before clearing (only if there's text)
|
||||
if (message) {
|
||||
addToHistory(message);
|
||||
}
|
||||
historyIndex = -1;
|
||||
tempInput = "";
|
||||
userHasTyped = false;
|
||||
@@ -198,12 +204,33 @@
|
||||
isSubmitting = true;
|
||||
inputValue = "";
|
||||
|
||||
// Apply mode prefix if needed
|
||||
// Capture attachments before clearing
|
||||
const currentAttachments = [...attachments];
|
||||
|
||||
// Apply mode prefix if needed (only if there's a message)
|
||||
const currentMode = getCurrentMode();
|
||||
const formattedMessage = formatMessageWithMode(message, currentMode);
|
||||
const formattedMessage = message ? formatMessageWithMode(message, currentMode) : "";
|
||||
|
||||
// Build message with attachments
|
||||
let messageWithAttachments = formattedMessage;
|
||||
if (currentAttachments.length > 0) {
|
||||
const attachmentPaths = currentAttachments.map((a) => a.path).join("\n");
|
||||
const attachmentPrefix = formattedMessage
|
||||
? `${formattedMessage}\n\n`
|
||||
: "";
|
||||
messageWithAttachments = `${attachmentPrefix}[Attached files - please read these files to see their contents:]\n${attachmentPaths}`;
|
||||
|
||||
// Log attached files to the output
|
||||
for (const attachment of currentAttachments) {
|
||||
claudeStore.addLine("system", `📎 Attached: ${attachment.filename} (${attachment.path})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear attachments after capturing them
|
||||
claudeStore.clearAttachments();
|
||||
|
||||
// Check if we need to restore conversation history
|
||||
let messageToSend = formattedMessage;
|
||||
let messageToSend = messageWithAttachments;
|
||||
if (getShouldRestoreHistory()) {
|
||||
const savedHistory = getSavedHistory();
|
||||
|
||||
@@ -341,7 +368,7 @@ User: ${formattedMessage}`;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrop(event: DragEvent) {
|
||||
async function handleDrop(event: DragEvent) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
isDragging = false;
|
||||
@@ -349,6 +376,8 @@ User: ${formattedMessage}`;
|
||||
const files = event.dataTransfer?.files;
|
||||
if (!files || files.length === 0) return;
|
||||
|
||||
const conversationId = get(claudeStore.activeConversationId);
|
||||
|
||||
for (const file of files) {
|
||||
const filename = file.name;
|
||||
const extension = filename.split(".").pop()?.toLowerCase() || "";
|
||||
@@ -361,10 +390,29 @@ User: ${formattedMessage}`;
|
||||
previewUrl = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
// Check if the file has a native path (from Tauri's file drop)
|
||||
// If not, we need to save it to a temp file
|
||||
let savedPath = (file as File & { path?: string }).path;
|
||||
|
||||
if (!savedPath && conversationId) {
|
||||
try {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const bytes = Array.from(new Uint8Array(arrayBuffer));
|
||||
savedPath = await invoke<string>("save_temp_file", {
|
||||
conversationId,
|
||||
filename,
|
||||
data: bytes,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to save dropped file to temp:", error);
|
||||
savedPath = file.name;
|
||||
}
|
||||
}
|
||||
|
||||
const attachment: Attachment = {
|
||||
id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
filename,
|
||||
path: (file as File & { path?: string }).path || file.name,
|
||||
path: savedPath || file.name,
|
||||
size: file.size,
|
||||
type: fileType,
|
||||
mimeType: file.type,
|
||||
@@ -441,40 +489,124 @@ User: ${formattedMessage}`;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePaste(event: ClipboardEvent) {
|
||||
async function handlePaste(event: ClipboardEvent) {
|
||||
// First, try the web clipboard API for files
|
||||
const items = event.clipboardData?.items;
|
||||
if (!items) return;
|
||||
let handledFile = false;
|
||||
|
||||
for (const item of items) {
|
||||
// Check if the item is a file (image or other)
|
||||
if (item.kind === "file") {
|
||||
const file = item.getAsFile();
|
||||
if (!file) continue;
|
||||
if (items && items.length > 0) {
|
||||
for (const item of items) {
|
||||
if (item.kind === "file") {
|
||||
const file = item.getAsFile();
|
||||
if (!file) continue;
|
||||
|
||||
// Prevent default for file pastes so we handle it
|
||||
event.preventDefault();
|
||||
event.preventDefault();
|
||||
handledFile = true;
|
||||
|
||||
const filename = file.name || `pasted-${Date.now()}.${file.type.split("/")[1] || "png"}`;
|
||||
const extension = filename.split(".").pop()?.toLowerCase() || "";
|
||||
const fileType = getFileTypeFromExtension(extension);
|
||||
const filename = file.name || `pasted-${Date.now()}.${file.type.split("/")[1] || "png"}`;
|
||||
const extension = filename.split(".").pop()?.toLowerCase() || "";
|
||||
const fileType = getFileTypeFromExtension(extension);
|
||||
|
||||
// Create preview URL for images
|
||||
let previewUrl: string | undefined;
|
||||
if (fileType === "image" || file.type.startsWith("image/")) {
|
||||
previewUrl = URL.createObjectURL(file);
|
||||
let previewUrl: string | undefined;
|
||||
if (fileType === "image" || file.type.startsWith("image/")) {
|
||||
previewUrl = URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
const conversationId = get(claudeStore.activeConversationId);
|
||||
let savedPath = filename;
|
||||
|
||||
if (conversationId) {
|
||||
try {
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
const bytes = Array.from(new Uint8Array(arrayBuffer));
|
||||
savedPath = await invoke<string>("save_temp_file", {
|
||||
conversationId,
|
||||
filename,
|
||||
data: bytes,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to save pasted file to temp:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const attachment: Attachment = {
|
||||
id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
filename,
|
||||
path: savedPath,
|
||||
size: file.size,
|
||||
type: file.type.startsWith("image/") ? "image" : fileType,
|
||||
mimeType: file.type,
|
||||
previewUrl,
|
||||
};
|
||||
|
||||
claudeStore.addAttachment(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const attachment: Attachment = {
|
||||
id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
filename,
|
||||
path: filename,
|
||||
size: file.size,
|
||||
type: file.type.startsWith("image/") ? "image" : fileType,
|
||||
mimeType: file.type,
|
||||
previewUrl,
|
||||
};
|
||||
// If web clipboard didn't have files, try Tauri's native clipboard for images
|
||||
if (!handledFile) {
|
||||
try {
|
||||
const image = await readImage();
|
||||
const rgba = await image.rgba();
|
||||
const size = await image.size();
|
||||
|
||||
claudeStore.addAttachment(attachment);
|
||||
if (rgba && rgba.length > 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const conversationId = get(claudeStore.activeConversationId);
|
||||
const filename = `screenshot-${Date.now()}.png`;
|
||||
|
||||
// Convert RGBA to PNG using canvas
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = size.width;
|
||||
canvas.height = size.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
if (ctx) {
|
||||
const imgData = ctx.createImageData(size.width, size.height);
|
||||
imgData.data.set(new Uint8ClampedArray(rgba));
|
||||
ctx.putImageData(imgData, 0, 0);
|
||||
|
||||
// Create preview URL from canvas
|
||||
const previewUrl = canvas.toDataURL("image/png");
|
||||
|
||||
// Convert to blob for saving
|
||||
const blob = await new Promise<Blob | null>((resolve) =>
|
||||
canvas.toBlob(resolve, "image/png")
|
||||
);
|
||||
|
||||
let savedPath = filename;
|
||||
if (blob && conversationId) {
|
||||
try {
|
||||
const arrayBuffer = await blob.arrayBuffer();
|
||||
const bytes = Array.from(new Uint8Array(arrayBuffer));
|
||||
savedPath = await invoke<string>("save_temp_file", {
|
||||
conversationId,
|
||||
filename,
|
||||
data: bytes,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed to save clipboard image to temp:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const attachment: Attachment = {
|
||||
id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
filename,
|
||||
path: savedPath,
|
||||
size: blob?.size || 0,
|
||||
type: "image",
|
||||
mimeType: "image/png",
|
||||
previewUrl,
|
||||
};
|
||||
|
||||
claudeStore.addAttachment(attachment);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// No image in clipboard or clipboard read failed - that's fine, just ignore
|
||||
console.log("No image in native clipboard:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,7 +751,7 @@ User: ${formattedMessage}`;
|
||||
{:else}
|
||||
<button
|
||||
type="submit"
|
||||
disabled={!isConnected || isSubmitting || !inputValue.trim()}
|
||||
disabled={!isConnected || isSubmitting || (!inputValue.trim() && attachments.length === 0)}
|
||||
class="send-button bg-[var(--accent-primary)] hover:bg-[var(--accent-secondary)]
|
||||
disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user