diff --git a/Cargo.lock b/Cargo.lock index 3400dbbe228..3634bd9fb7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ "bytestring", "derive_more 2.0.1", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "http 0.2.12", "httparse", @@ -95,7 +95,7 @@ dependencies = [ "actix-utils", "futures-core", "futures-util", - "mio 1.0.4", + "mio 1.2.0", "socket2 0.5.10", "tokio", "tracing", @@ -139,7 +139,7 @@ dependencies = [ "cfg-if", "derive_more 2.0.1", "encoding_rs", - "foldhash", + "foldhash 0.1.5", "futures-core", "futures-util", "impl-more", @@ -321,9 +321,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" @@ -343,11 +343,11 @@ dependencies = [ "clipboard-win", "image 0.25.8", "log", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", "windows-sys 0.60.2", @@ -905,7 +905,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.2", + "objc2 0.6.4", ] [[package]] @@ -1224,7 +1224,7 @@ name = "cap-cursor-info" version = "0.0.0" dependencies = [ "hex", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "serde", "sha2", @@ -1283,9 +1283,9 @@ dependencies = [ "md5", "nix 0.29.0", "objc", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "opentelemetry", "opentelemetry-otlp", "opentelemetry_sdk", @@ -1650,7 +1650,7 @@ dependencies = [ "libc", "libproc", "objc", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "parking_lot", "relative-path", @@ -1703,7 +1703,7 @@ dependencies = [ "image 0.25.8", "log", "metal 0.31.0", - "objc2 0.6.2", + "objc2 0.6.4", "pretty_assertions", "rayon", "reactive_graph", @@ -2034,9 +2034,9 @@ checksum = "afede46921767868c5c7f8f55202bdd8bec0bab6bc9605174200f45924f93c62" dependencies = [ "clipboard-win", "image 0.25.8", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "windows 0.59.0", "x11rb", ] @@ -2573,6 +2573,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -2585,14 +2598,20 @@ dependencies = [ [[package]] name = "ctor" -version = "0.2.9" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" dependencies = [ - "quote", - "syn 2.0.106", + "ctor-proc-macro", + "dtor", ] +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + [[package]] name = "darling" version = "0.20.11" @@ -2655,6 +2674,17 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" +[[package]] +name = "dbus" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.0", +] + [[package]] name = "debugid" version = "0.8.0" @@ -2882,7 +2912,7 @@ dependencies = [ "bitflags 2.9.4", "block2 0.6.1", "libc", - "objc2 0.6.2", + "objc2 0.6.4", ] [[package]] @@ -2946,6 +2976,21 @@ dependencies = [ "litrs", ] +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser 0.36.0", + "foldhash 0.2.0", + "html5ever 0.38.0", + "precomputed-hash", + "selectors 0.36.1", + "tendril 0.5.0", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -3000,6 +3045,21 @@ dependencies = [ "dtoa", ] +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "dunce" version = "1.0.5" @@ -3359,6 +3419,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "font-types" version = "0.9.0" @@ -3480,9 +3546,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -3495,9 +3561,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -3505,15 +3571,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -3533,9 +3599,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -3552,9 +3618,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -3563,21 +3629,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -3587,7 +3653,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -3925,7 +3990,7 @@ checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" dependencies = [ "crossbeam-channel", "keyboard-types", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "once_cell", "serde", @@ -4169,7 +4234,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -4242,10 +4307,20 @@ checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.14.1", "match_token", ] +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever 0.38.0", +] + [[package]] name = "http" version = "0.2.12" @@ -4432,7 +4507,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.3", "system-configuration 0.6.1", "tokio", "tower-service", @@ -4466,9 +4541,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" dependencies = [ "byteorder", "png 0.17.16", @@ -4770,17 +4845,6 @@ dependencies = [ "leaky-cow", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -4921,10 +4985,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.80" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -5023,10 +5089,10 @@ version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ - "cssparser", - "html5ever", + "cssparser 0.29.6", + "html5ever 0.29.1", "indexmap 2.11.4", - "selectors", + "selectors 0.24.0", ] [[package]] @@ -5116,9 +5182,18 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.175" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] [[package]] name = "libfuzzer-sys" @@ -5318,8 +5393,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119c8490084af61b44c9eda9d626475847a186737c0378c85e32d77c33a01cd4" dependencies = [ "cc", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", "time", ] @@ -5376,9 +5451,20 @@ dependencies = [ "log", "phf 0.11.3", "phf_codegen 0.11.3", - "string_cache", - "string_cache_codegen", - "tendril", + "string_cache 0.8.9", + "string_cache_codegen 0.5.4", + "tendril 0.4.3", +] + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril 0.5.0", + "web_atoms", ] [[package]] @@ -5620,14 +5706,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -5678,23 +5764,23 @@ dependencies = [ [[package]] name = "muda" -version = "0.17.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" +checksum = "0ae8844f63b5b118e334e205585b8c5c17b984121dbdb179d44aeb087ffad3cb" dependencies = [ "crossbeam-channel", "dpi", "gtk", "keyboard-types", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "once_cell", - "png 0.17.16", + "png 0.18.0", "serde", "thiserror 2.0.16", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -6087,9 +6173,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", "objc2-exception-helper", @@ -6097,21 +6183,23 @@ dependencies = [ [[package]] name = "objc2-app-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ "bitflags 2.9.4", "block2 0.6.1", "libc", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-cloud-kit", "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-image", - "objc2-foundation 0.3.1", - "objc2-quartz-core 0.3.1", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", ] [[package]] @@ -6123,15 +6211,15 @@ dependencies = [ "bitflags 2.9.4", "block2 0.6.1", "dispatch2", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-avf-audio", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-image", "objc2-core-media", "objc2-core-video", - "objc2-foundation 0.3.1", - "objc2-quartz-core 0.3.1", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", ] [[package]] @@ -6140,19 +6228,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfc1d11521c211a7ebe17739fc806719da41f56c6b3f949d9861b459188ce910" dependencies = [ - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] name = "objc2-cloud-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] @@ -6162,7 +6250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82" dependencies = [ "dispatch2", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-core-audio-types", "objc2-core-foundation", ] @@ -6174,52 +6262,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", + "objc2 0.6.4", ] [[package]] name = "objc2-core-data" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] name = "objc2-core-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags 2.9.4", "dispatch2", - "objc2 0.6.2", + "objc2 0.6.4", ] [[package]] name = "objc2-core-graphics" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ "bitflags 2.9.4", "dispatch2", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", "objc2-io-surface", ] [[package]] name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-location" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e" +checksum = "ac0f75792558aa9d618443bbb5db7426a7a0b6fddf96903f86ef9ad02e135740" dependencies = [ - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] @@ -6230,21 +6328,33 @@ checksum = "f0b7afa6822e2fa20dfc88d10186b2432bf8560b5ed73ec9d31efd78277bc878" dependencies = [ "bitflags 2.9.4", "dispatch2", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-core-audio", "objc2-core-audio-types", "objc2-core-foundation", "objc2-core-video", ] +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.9.4", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-core-graphics", +] + [[package]] name = "objc2-core-video" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1989c3e76c7e978cab0ba9e6f4961cd00ed14ca21121444cc26877403bfb6303" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", "objc2-io-surface", @@ -6279,14 +6389,14 @@ dependencies = [ [[package]] name = "objc2-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.9.4", "block2 0.6.1", "libc", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -6302,22 +6412,12 @@ dependencies = [ [[package]] name = "objc2-io-surface" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-javascript-core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a" -dependencies = [ - "objc2 0.6.2", + "objc2 0.6.4", "objc2-core-foundation", ] @@ -6340,9 +6440,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26bb88504b5a050dbba515d2414607bf5e57dd56b107bc5f0351197a3e7bdc5d" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", ] [[package]] @@ -6360,36 +6460,44 @@ dependencies = [ [[package]] name = "objc2-quartz-core" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", ] [[package]] -name = "objc2-security" +name = "objc2-ui-kit" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44" +checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" dependencies = [ "bitflags 2.9.4", - "objc2 0.6.2", + "block2 0.6.1", + "objc2 0.6.4", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "objc2-user-notifications", ] [[package]] -name = "objc2-ui-kit" +name = "objc2-user-notifications" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed" +checksum = "2a3f5ec77a81d9e0c5a0b32159b0cb143d7086165e79708351e02bf37dfc65cd" dependencies = [ - "bitflags 2.9.4", - "objc2 0.6.2", - "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", ] [[package]] @@ -6400,12 +6508,10 @@ checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad" dependencies = [ "bitflags 2.9.4", "block2 0.6.1", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", - "objc2-javascript-core", - "objc2-security", + "objc2-foundation 0.3.2", ] [[package]] @@ -6734,8 +6840,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" dependencies = [ - "objc2 0.6.2", - "objc2-foundation 0.3.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", "objc2-osa-kit", "serde", "serde_json", @@ -6886,10 +6992,20 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", "phf_shared 0.11.3", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", + "serde", +] + [[package]] name = "phf_codegen" version = "0.8.0" @@ -6910,6 +7026,16 @@ dependencies = [ "phf_shared 0.11.3", ] +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", +] + [[package]] name = "phf_generator" version = "0.8.0" @@ -6940,6 +7066,16 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.10.0" @@ -6956,12 +7092,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator 0.13.1", + "phf_shared 0.13.1", "proc-macro2", "quote", "syn 2.0.106", @@ -6994,6 +7130,15 @@ dependencies = [ "siphasher 1.0.1", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -7399,7 +7544,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls 0.23.31", - "socket2 0.6.0", + "socket2 0.6.3", "thiserror 2.0.16", "tokio", "tracing", @@ -7436,7 +7581,7 @@ dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -7923,11 +8068,45 @@ dependencies = [ "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", + "wasm-streams 0.4.2", "web-sys", "webpki-roots 1.0.2", ] +[[package]] +name = "reqwest" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.7.0", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper 1.0.2", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams 0.5.0", + "web-sys", +] + [[package]] name = "resize" version = "0.8.8" @@ -7978,10 +8157,10 @@ dependencies = [ "gtk-sys", "js-sys", "log", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", @@ -8307,9 +8486,9 @@ dependencies = [ "clap", "futures", "inquire", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "scap-targets", "tokio", "tracing", @@ -8482,14 +8661,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", - "cssparser", + "cssparser 0.29.6", "derive_more 0.99.20", "fxhash", "log", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.9.4", + "cssparser 0.36.0", + "derive_more 2.0.1", + "log", + "new_debug_unreachable", + "phf 0.13.1", + "phf_codegen 0.13.1", + "precomputed-hash", + "rustc-hash 2.1.1", + "servo_arc 0.4.3", "smallvec", ] @@ -8844,6 +9042,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -9057,12 +9264,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -9241,6 +9448,18 @@ dependencies = [ "serde", ] +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.13.1", + "precomputed-hash", +] + [[package]] name = "string_cache_codegen" version = "0.5.4" @@ -9253,6 +9472,18 @@ dependencies = [ "quote", ] +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -9537,33 +9768,35 @@ dependencies = [ [[package]] name = "tao" -version = "0.34.3" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33f7f9e486ade65fcf1e45c440f9236c904f5c1002cdc7fc6ae582777345ce4" dependencies = [ "bitflags 2.9.4", "block2 0.6.1", "core-foundation 0.10.1", - "core-graphics 0.24.0", + "core-graphics 0.25.0", "crossbeam-channel", - "dispatch", + "dbus", + "dispatch2", "dlopen2", "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", "jni", - "lazy_static", "libc", "log", "ndk 0.9.0", - "ndk-context", "ndk-sys 0.6.0+11769913", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", + "objc2-ui-kit", "once_cell", "parking_lot", + "percent-encoding", "raw-window-handle", - "scopeguard", "tao-macros", "unicode-segmentation", "url", @@ -9603,9 +9836,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.8.5" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d1d3b3dc4c101ac989fd7db77e045cc6d91a25349cd410455cb5c57d510c1c" +checksum = "d059f2527558d9dba6f186dec4772610e1aecfd3f94002397613e7e648752b66" dependencies = [ "anyhow", "bytes", @@ -9625,15 +9858,15 @@ dependencies = [ "log", "mime", "muda", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "objc2-ui-kit", "objc2-web-kit", "percent-encoding", "plist", "raw-window-handle", - "reqwest 0.12.24", + "reqwest 0.13.3", "serde", "serde_json", "serde_repr", @@ -9649,7 +9882,6 @@ dependencies = [ "tokio", "tray-icon", "url", - "urlpattern", "webkit2gtk", "webview2-com", "window-vibrancy", @@ -9658,9 +9890,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.4.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c432ccc9ff661803dab74c6cd78de11026a578a9307610bbc39d3c55be7943f" +checksum = "3a318b234cc2dea65f575467bafcfb76286bce228ebc3778e337d61d03213007" dependencies = [ "anyhow", "cargo_toml", @@ -9674,15 +9906,14 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.9.7", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab3a62cf2e6253936a8b267c2e95839674e7439f104fa96ad0025e149d54d8a" +checksum = "d3e4e8230d565106aa19dfbaa01a7ed01abf78047fe0577a83377224bd1bf20e" dependencies = [ "base64 0.22.1", "brotli", @@ -9707,9 +9938,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4368ea8094e7045217edb690f493b55b30caf9f3e61f79b4c24b6db91f07995e" +checksum = "bc8de2cddbbc33dbdf4c84f170121886595efdbcc9cb4b3d76342b79d082cedc" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -9909,7 +10140,7 @@ dependencies = [ "dunce", "glob", "objc2-app-kit", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "open", "schemars 0.8.22", "serde", @@ -10083,16 +10314,16 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.8.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4cfc9ad45b487d3fded5a4731a567872a4812e9552e3964161b08edabf93846" +checksum = "1e42bbcb76237351fbaa02f08d808c537dc12eb5a6eabbf3e517b50056334d95" dependencies = [ "cookie", "dpi", "gtk", "http 1.3.1", "jni", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-ui-kit", "objc2-web-kit", "raw-window-handle", @@ -10108,17 +10339,16 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.8.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fe9d48bd122ff002064e88cfcd7027090d789c4302714e68fcccba0f4b7807" +checksum = "2cadb13dad0c681e1e0a2c49ae488f0e2906ded3d57e7a0017f4aaf46e387117" dependencies = [ "gtk", "http 1.3.1", "jni", "log", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", - "objc2-foundation 0.3.1", "once_cell", "percent-encoding", "raw-window-handle", @@ -10163,24 +10393,26 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.7.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a3852fdf9a4f8fbeaa63dc3e9a85284dd6ef7200751f0bd66ceee30c93f212" +checksum = "d57200389a2f82b4b0a40ae29ca19b6978116e8f4d4e974c3234ce40c0ffbdec" dependencies = [ "anyhow", "brotli", "cargo_metadata", "ctor", + "dom_query", "dunce", "glob", - "html5ever", + "html5ever 0.29.1", "http 1.3.1", "infer", "json-patch", "kuchikiki", "log", "memchr", - "phf 0.11.3", + "phf 0.13.1", + "plist", "proc-macro2", "quote", "regex", @@ -10245,6 +10477,16 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -10459,30 +10701,27 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.52.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", - "mio 1.0.4", + "mio 1.2.0", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.3", "tokio-macros", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -10702,9 +10941,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "bitflags 2.9.4", "bytes", @@ -10732,9 +10971,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -10756,9 +10995,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -10767,9 +11006,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -10847,24 +11086,24 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.21.1" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d92153331e7d02ec09137538996a7786fe679c629c279e82a6be762b7e6fe2" +checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" dependencies = [ "crossbeam-channel", "dirs 6.0.0", "libappindicator", "muda", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", "objc2-core-graphics", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "once_cell", - "png 0.17.16", + "png 0.18.0", "serde", "thiserror 2.0.16", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] @@ -11353,9 +11592,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.103" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -11364,38 +11603,21 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.106", - "wasm-bindgen-shared", -] - [[package]] name = "wasm-bindgen-futures" -version = "0.4.53" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ - "cfg-if", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.103" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -11403,22 +11625,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.103" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.106", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.103" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -11436,6 +11658,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wayland-backend" version = "0.3.11" @@ -11511,9 +11746,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.80" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -11529,11 +11764,23 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +dependencies = [ + "phf 0.13.1", + "phf_codegen 0.13.1", + "string_cache 0.9.0", + "string_cache_codegen 0.6.1", +] + [[package]] name = "webkit2gtk" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -11555,9 +11802,9 @@ dependencies = [ [[package]] name = "webkit2gtk-sys" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ "bitflags 1.3.2", "cairo-sys-rs", @@ -11863,10 +12110,10 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "raw-window-handle", "windows-sys 0.59.0", "windows-version", @@ -12686,30 +12933,29 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "wry" -version = "0.53.3" +version = "0.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f0e9642a0d061f6236c54ccae64c2722a7879ad4ec7dff59bd376d446d8e90" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" dependencies = [ "base64 0.22.1", "block2 0.6.1", "cookie", "crossbeam-channel", "dirs 6.0.0", + "dom_query", "dpi", "dunce", "gdkx11", "gtk", - "html5ever", "http 1.3.1", "javascriptcore-rs", "jni", - "kuchikiki", "libc", "ndk 0.9.0", - "objc2 0.6.2", + "objc2 0.6.4", "objc2-app-kit", "objc2-core-foundation", - "objc2-foundation 0.3.1", + "objc2-foundation 0.3.2", "objc2-ui-kit", "objc2-web-kit", "once_cell", @@ -13076,3 +13322,7 @@ dependencies = [ "syn 2.0.106", "winnow 0.7.13", ] + +[[patch.unused]] +name = "tao" +version = "0.34.3" diff --git a/Cargo.toml b/Cargo.toml index 6d101defef8..52a47a92162 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,12 +8,12 @@ members = [ ] [workspace.dependencies] -anyhow = { version = "1.0.86" } +anyhow = { version = "1.0.102" } # This includes a currently-unreleased fix that ensures the audio stream is actually # stopped and released on drop on macOS cpal = { git = "https://github.com/CapSoftware/cpal", rev = "3cc779a7b4ca" } ffmpeg = { package = "ffmpeg-next", git = "https://github.com/CapSoftware/rust-ffmpeg", rev = "49db1fede112" } -tokio = { version = "1.39.3", features = [ +tokio = { version = "1.52.2", features = [ "macros", "process", "fs", @@ -22,7 +22,7 @@ tokio = { version = "1.39.3", features = [ "rt-multi-thread", "time", ] } -tauri = { version = "2.5.0", features = ["specta"] } +tauri = { version = "2.11.0", features = ["specta"] } specta = { version = "=2.0.0-rc.20", features = [ "derive", "serde_json", @@ -37,18 +37,18 @@ nokhwa = { git = "https://github.com/CapSoftware/nokhwa", rev = "b9c8079e82e2", "serialize", ] } nokhwa-bindings-macos = { git = "https://github.com/CapSoftware/nokhwa", rev = "b9c8079e82e2" } -wgpu = { version = "25.0.0", features = ["wgpu-core"] } -wgpu-hal = "25.0.0" -wgpu-core = "25.0.0" -flume = "0.11.0" +wgpu = { version = "25.0.2", features = ["wgpu-core"] } +wgpu-hal = "25.0.2" +wgpu-core = "25.0.2" +flume = "0.11.1" thiserror = "1.0" sentry = { version = "0.42.0", features = [ "anyhow", "backtrace", "debug-images", ] } -tracing = "0.1.41" -futures = "0.3.31" +tracing = "0.1.44" +futures = "0.3.32" aho-corasick = "1.1.4" cidre = { git = "https://github.com/CapSoftware/cidre", rev = "bf84b67079a8", features = [ @@ -72,7 +72,7 @@ windows = "0.60.0" windows-core = "0.60" windows-sys = "0.59.0" windows-capture = "1.5.0" -percent-encoding = "2.3.1" +percent-encoding = "2.3.2" sysinfo = "0.32" # TODO: Reenable these: https://github.com/CapSoftware/Cap/issues/859 diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 3e32c06fb03..4312db3e5b5 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -41,7 +41,7 @@ "@solidjs/router": "^0.14.2", "@solidjs/start": "^1.1.3", "@tanstack/solid-query": "^5.51.21", - "@tauri-apps/api": "2.8.0", + "@tauri-apps/api": "2.11.0", "@tauri-apps/plugin-clipboard-manager": "^2.3.0", "@tauri-apps/plugin-deep-link": "^2.4.1", "@tauri-apps/plugin-dialog": "^2.4.0", @@ -77,7 +77,7 @@ "@fontsource/geist-sans": "^5.0.3", "@webgpu/types": "^0.1.44", "@iconify/json": "^2.2.239", - "@tauri-apps/cli": ">=2.1.0", + "@tauri-apps/cli": ">=2.11.0", "@total-typescript/ts-reset": "^0.6.1", "@types/dom-webcodecs": "^0.1.11", "@types/uuid": "^9.0.8", diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 1d8271a5492..142a2e5d4ca 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -13,7 +13,7 @@ name = "cap_desktop_lib" crate-type = ["lib", "cdylib", "staticlib"] [build-dependencies] -tauri-build = { version = "2.1.0", features = [] } +tauri-build = { version = "2.6.1", features = [] } [target.'cfg(target_os = "macos")'.build-dependencies] swift-rs = { version = "1.0.6", features = ["build"] } @@ -126,7 +126,7 @@ aho-corasick.workspace = true [target.'cfg(target_os = "macos")'.dependencies] core-graphics = "0.24.0" core-foundation = "0.10.0" -objc2-app-kit = { version = "0.3.0", features = [ +objc2-app-kit = { version = "0.3.2", features = [ "NSWindow", "NSResponder", "NSHapticFeedback", diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index 745254f6eb4..944246dc097 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -51,6 +51,7 @@ "core:webview:default", "core:webview:allow-create-webview-window", "core:app:allow-version", + "core:app:allow-set-app-theme", "shell:default", "core:image:default", "dialog:default", diff --git a/apps/desktop/src-tauri/src/camera.rs b/apps/desktop/src-tauri/src/camera.rs index 257c60ae52f..16a3de14a97 100644 --- a/apps/desktop/src-tauri/src/camera.rs +++ b/apps/desktop/src-tauri/src/camera.rs @@ -316,7 +316,7 @@ fn wait_for_shutdown_signal(runtime: &Runtime, receiver: oneshot::Receiver<()>, // crashes after ~4-5 toggle cycles. // // Pause/Resume hide/show the window while keeping GPU resources alive. -// See the comment in windows.rs ShowCapWindow::Camera for the critical detail +// See the comment in windows.rs CapWindow::Camera for the critical detail // about avoiding order_front_regardless(). #[derive(Clone)] enum ReconfigureEvent { diff --git a/apps/desktop/src-tauri/src/deeplink_actions.rs b/apps/desktop/src-tauri/src/deeplink_actions.rs index a1170284877..6908138b6b0 100644 --- a/apps/desktop/src-tauri/src/deeplink_actions.rs +++ b/apps/desktop/src-tauri/src/deeplink_actions.rs @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf}; use tauri::{AppHandle, Manager, Url}; use tracing::trace; -use crate::{App, ArcLock, recording::StartRecordingInputs, windows::ShowCapWindow}; +use crate::{App, ArcLock, recording::StartRecordingInputs, windows::CapWindow}; #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] @@ -151,7 +151,7 @@ impl DeepLinkAction { crate::open_project_from_path(Path::new(&project_path), app.clone()) } DeepLinkAction::OpenSettings { page } => { - crate::show_window(app.clone(), ShowCapWindow::Settings { page }).await + crate::show_window(app.clone(), CapWindow::Settings { page }).await } } } diff --git a/apps/desktop/src-tauri/src/display_utils.rs b/apps/desktop/src-tauri/src/display_utils.rs new file mode 100644 index 00000000000..9613f860b02 --- /dev/null +++ b/apps/desktop/src-tauri/src/display_utils.rs @@ -0,0 +1,168 @@ +use scap_targets::Display; +use tauri::{PhysicalPosition, PhysicalSize}; + +// Credits: tauri-plugin-window-state +pub trait MonitorExt { + fn intersects( + &self, + position: PhysicalPosition, + size: PhysicalSize, + scale: f64, + ) -> bool; + + fn intersects_window(&self, window: tauri::Window) -> tauri::Result; +} + +impl MonitorExt for Display { + fn intersects_window(&self, window: tauri::Window) -> tauri::Result { + Ok(self.intersects( + window.outer_position()?, + window.outer_size()?, + window.scale_factor()?, + )) + } + + fn intersects( + &self, + position: PhysicalPosition, + size: PhysicalSize, + _scale: f64, + ) -> bool { + #[cfg(target_os = "macos")] + { + let Some(bounds) = self.raw_handle().logical_bounds() else { + return false; + }; + + let left = (bounds.position().x() * _scale) as i32; + let right = left + (bounds.size().width() * _scale) as i32; + let top = (bounds.position().y() * _scale) as i32; + let bottom = top + (bounds.size().height() * _scale) as i32; + + [ + (position.x, position.y), + (position.x + size.width as i32, position.y), + (position.x, position.y + size.height as i32), + ( + position.x + size.width as i32, + position.y + size.height as i32, + ), + ] + .into_iter() + .any(|(x, y)| x >= left && x < right && y >= top && y < bottom) + } + + #[cfg(windows)] + { + let Some(bounds) = self.raw_handle().physical_bounds() else { + return false; + }; + + let left = bounds.position().x() as i32; + let right = left + bounds.size().width() as i32; + let top = bounds.position().y() as i32; + let bottom = top + bounds.size().height() as i32; + + [ + (position.x, position.y), + (position.x + size.width as i32, position.y), + (position.x, position.y + size.height as i32), + ( + position.x + size.width as i32, + position.y + size.height as i32, + ), + ] + .into_iter() + .any(|(x, y)| x >= left && x < right && y >= top && y < bottom) + } + } +} + +const DEFAULT_FALLBACK_DISPLAY_WIDTH: f64 = 1920.0; +const DEFAULT_FALLBACK_DISPLAY_HEIGHT: f64 = 1080.0; +pub struct CursorMonitorInfo { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +impl CursorMonitorInfo { + pub fn get() -> Self { + let display = Display::get_containing_cursor().unwrap_or_else(Display::primary); + let bounds = display.raw_handle().logical_bounds(); + let (x, y, width, height) = bounds + .map(|b| { + ( + b.position().x(), + b.position().y(), + b.size().width(), + b.size().height(), + ) + }) + .unwrap_or(( + 0.0, + 0.0, + DEFAULT_FALLBACK_DISPLAY_WIDTH, + DEFAULT_FALLBACK_DISPLAY_HEIGHT, + )); + + Self { + x, + y, + width, + height, + } + } + + pub fn center_position(&self, window_width: f64, window_height: f64) -> (f64, f64) { + let pos_x = self.x + (self.width - window_width) / 2.0; + let pos_y = self.y + (self.height - window_height) / 2.0; + (pos_x, pos_y) + } + + pub fn bottom_center_position( + &self, + window_width: f64, + window_height: f64, + offset_y: f64, + ) -> (f64, f64) { + let pos_x = self.x + (self.width - window_width) / 2.0; + let pos_y = self.y + self.height - window_height - offset_y; + (pos_x, pos_y) + } + + pub fn from_window(window: &tauri::WebviewWindow) -> Self { + let window_pos = window + .outer_position() + .ok() + .map(|p| (p.x as f64, p.y as f64)) + .unwrap_or((0.0, 0.0)); + + for display in Display::list() { + if let Some(bounds) = display.raw_handle().logical_bounds() { + let (x, y, width, height) = ( + bounds.position().x(), + bounds.position().y(), + bounds.size().width(), + bounds.size().height(), + ); + + if window_pos.0 >= x + && window_pos.0 < x + width + && window_pos.1 >= y + && window_pos.1 < y + height + { + return Self { + x, + y, + width, + height, + }; + } + } + } + + Self::get() + } +} diff --git a/apps/desktop/src-tauri/src/general_settings.rs b/apps/desktop/src-tauri/src/general_settings.rs index ff6cadffe65..726304745db 100644 --- a/apps/desktop/src-tauri/src/general_settings.rs +++ b/apps/desktop/src-tauri/src/general_settings.rs @@ -136,8 +136,8 @@ pub struct GeneralSettingsStore { pub disable_auto_open_links: bool, #[serde(default = "default_true")] pub has_completed_startup: bool, - #[serde(default)] - pub theme: AppTheme, + #[serde(default, alias = "theme")] + pub appearance: Appearance, #[serde(default)] pub commercial_license: Option, #[serde(default)] @@ -184,8 +184,6 @@ pub struct GeneralSettingsStore { #[serde(default)] pub studio_recording_quality: StudioRecordingQuality, #[serde(default)] - pub main_window_position: Option, - #[serde(default)] pub camera_window_position: Option, #[serde(default)] pub camera_window_positions_by_monitor_name: BTreeMap, @@ -251,7 +249,7 @@ impl Default for GeneralSettingsStore { enable_notifications: true, disable_auto_open_links: false, has_completed_startup: false, - theme: AppTheme::System, + appearance: Appearance::System, commercial_license: None, last_version: None, window_transparency: false, @@ -273,7 +271,6 @@ impl Default for GeneralSettingsStore { transcription_hints: default_transcription_hints(), editor_preview_quality: EditorPreviewQuality::Half, studio_recording_quality: default_studio_recording_quality(), - main_window_position: None, camera_window_position: None, camera_window_positions_by_monitor_name: BTreeMap::new(), has_completed_onboarding: false, @@ -285,13 +282,23 @@ impl Default for GeneralSettingsStore { #[derive(Default, Debug, Copy, Clone, Serialize, Deserialize, Type)] #[serde(rename_all = "camelCase")] -pub enum AppTheme { +pub enum Appearance { #[default] System, Light, Dark, } +impl From for Option { + fn from(appearance: Appearance) -> Self { + match appearance { + Appearance::Light => Some(tauri::Theme::Light), + Appearance::Dark => Some(tauri::Theme::Dark), + Appearance::System => None, + } + } +} + impl GeneralSettingsStore { pub fn get(app: &AppHandle) -> Result, String> { match app.store("store").map(|s| s.get("general_settings")) { diff --git a/apps/desktop/src-tauri/src/hotkeys.rs b/apps/desktop/src-tauri/src/hotkeys.rs index eccd9e700bf..b459e77a8ad 100644 --- a/apps/desktop/src-tauri/src/hotkeys.rs +++ b/apps/desktop/src-tauri/src/hotkeys.rs @@ -2,7 +2,7 @@ use crate::{ RequestOpenRecordingPicker, RequestStartRecording, recording, recording_settings::{RecordingSettingsStore, RecordingTargetMode}, tray, - windows::ShowCapWindow, + windows::CapWindow, }; use cap_recording::screen_capture::ScreenCaptureTarget; use global_hotkey::HotKeyState; @@ -102,7 +102,7 @@ pub fn init(app: &AppHandle) { if shortcut.key == Code::Comma && shortcut.mods == Modifiers::META { let app = app.clone(); tokio::spawn(async move { - let _ = ShowCapWindow::Settings { page: None }.show(&app).await; + let _ = CapWindow::Settings { page: None }.show(&app).await; }); } @@ -212,7 +212,7 @@ async fn handle_hotkey(app: AppHandle, action: HotkeyAction) -> Result<(), Strin match recording::take_screenshot(app.clone(), target).await { Ok(path) => { - let _ = ShowCapWindow::ScreenshotEditor { path }.show(&app).await; + let _ = CapWindow::ScreenshotEditor { path }.show(&app).await; Ok(()) } Err(e) => Err(format!("Failed to take screenshot: {e}")), @@ -229,7 +229,7 @@ async fn handle_hotkey(app: AppHandle, action: HotkeyAction) -> Result<(), Strin match recording::take_screenshot(app.clone(), target).await { Ok(path) => { - let _ = ShowCapWindow::ScreenshotEditor { path }.show(&app).await; + let _ = CapWindow::ScreenshotEditor { path }.show(&app).await; Ok(()) } Err(e) => Err(format!("Failed to take screenshot: {e}")), diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index cbf29ca469c..00b80a6f62e 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -8,6 +8,7 @@ mod camera; mod camera_legacy; mod captions; mod deeplink_actions; +mod display_utils; mod editor_window; mod exit_shutdown; mod export; @@ -108,7 +109,7 @@ use upload::{create_or_get_video, upload_image, upload_video}; use web_api::AuthedApiError; use web_api::ManagerExt as WebManagerExt; use windows::{ - CapWindowId, EditorWindowIds, ScreenshotEditorWindowIds, ShowCapWindow, hide_overlay, + CapWindow, CapWindowId, EditorWindowIds, ScreenshotEditorWindowIds, hide_overlay, set_window_transparent, show_overlay, }; @@ -837,7 +838,7 @@ async fn set_camera_input( let show_result = if camera_window_is_visible { Ok(()) } else { - ShowCapWindow::Camera { centered: false } + CapWindow::Camera { centered: false } .show(&app_handle) .await .map(|_| ()) @@ -934,7 +935,7 @@ async fn set_camera_input( if !showed_camera_window { showed_camera_window = true; - let show_result = ShowCapWindow::Camera { centered: false } + let show_result = CapWindow::Camera { centered: false } .show(&app_handle) .await; show_result @@ -3036,7 +3037,7 @@ async fn upload_screenshot( }; if !auth.is_upgraded() { - ShowCapWindow::Upgrade.show(&app).await.ok(); + CapWindow::Upgrade.show(&app).await.ok(); return Ok(UploadResult::UpgradeRequired); } @@ -3620,8 +3621,8 @@ async fn editor_delete_project( #[tauri::command] #[specta::specta] #[instrument(skip(app))] -async fn show_window(app: AppHandle, window: ShowCapWindow) -> Result<(), String> { - if matches!(window, ShowCapWindow::Camera { .. }) { +async fn show_window(app: AppHandle, window: CapWindow) -> Result<(), String> { + if matches!(window, CapWindow::Camera { .. }) { let operation_lock = app.state::(); let _operation_guard = operation_lock.lock().await; window.show(&app).await.map_err(|e| e.to_string())?; @@ -3893,6 +3894,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { let specta_builder = tauri_specta::Builder::new() .commands(tauri_specta::collect_commands![ + log, set_mic_input, set_camera_input, set_native_camera_preview_enabled, @@ -3974,8 +3976,6 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { is_camera_window_open, seek_to, get_display_frame_for_cropping, - windows::position_traffic_lights, - windows::set_theme, global_message_dialog, show_window, write_clipboard_string, @@ -4106,7 +4106,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { else { let app = app.clone(); tokio::spawn(async move { - ShowCapWindow::Main { + CapWindow::Main { init_target_mode: None, } .show(&app) @@ -4142,14 +4142,10 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { tauri_plugin_window_state::Builder::new() .with_state_flags({ use tauri_plugin_window_state::StateFlags; - let mut flags = StateFlags::all(); - flags.remove(StateFlags::VISIBLE); - flags + StateFlags::all() - StateFlags::VISIBLE - StateFlags::DECORATIONS }) .with_denylist(&[ CapWindowId::Onboarding.label().as_str(), - CapWindowId::Main.label().as_str(), - CapWindowId::Settings.label().as_str(), "window-capture-occluder", "target-select-overlay", CapWindowId::CaptureArea.label().as_str(), @@ -4358,10 +4354,10 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { async move { if should_show_onboarding(&app) { println!("Showing onboarding"); - let _ = ShowCapWindow::Onboarding.show(&app).await; + let _ = CapWindow::Onboarding.show(&app).await; } else { println!("Showing main window"); - let _ = ShowCapWindow::Main { + let _ = CapWindow::Main { init_target_mode: None, } .show(&app) @@ -4404,7 +4400,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { if let Some(target_mode) = event.target_mode { open_target_picker(&app, target_mode).await; } else { - let _ = ShowCapWindow::Main { + let _ = CapWindow::Main { init_target_mode: None, } .show(&app) @@ -4413,7 +4409,7 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { }); RequestOpenSettings::listen_any_spawn(&app, async |payload, app| { - let _ = ShowCapWindow::Settings { + let _ = CapWindow::Settings { page: Some(payload.page), } .show(&app) @@ -4640,14 +4636,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { CapWindowId::Settings => { for (label, window) in app.webview_windows() { if let Ok(id) = CapWindowId::from_str(&label) { - match id { - CapWindowId::TargetSelectOverlay { .. } => { - show_overlay(&window); - } - CapWindowId::Main => { - let _ = window.show(); - } - _ => {} + if let CapWindowId::Main = id { + let _ = window.show(); } } } @@ -4665,14 +4655,8 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { CapWindowId::Upgrade | CapWindowId::ModeSelect => { for (label, window) in app.webview_windows() { if let Ok(id) = CapWindowId::from_str(&label) { - match id { - CapWindowId::TargetSelectOverlay { .. } => { - show_overlay(&window); - } - CapWindowId::Main => { - let _ = window.show(); - } - _ => {} + if let CapWindowId::Main = id { + let _ = window.show(); } } } @@ -4764,33 +4748,18 @@ pub async fn run(recording_logging_handle: LoggingHandle, logs_dir: PathBuf) { if let Ok(window_id) = CapWindowId::from_str(label) { let scale_factor = window.scale_factor().unwrap_or(1.0); let logical_pos = position.to_logical::(scale_factor); - match window_id { - CapWindowId::Main => { - let display_id = - display_id_for_position(logical_pos.x, logical_pos.y); - window_position_persistence::queue_main_position( - app, - general_settings::WindowPosition { - x: logical_pos.x, - y: logical_pos.y, - display_id, - }, - ); - } - CapWindowId::Camera => { - if app - .try_state::() - .is_some_and(|guard| guard.should_ignore()) - { - return; - } - window_position_persistence::queue_camera_position( - app, - logical_pos.x, - logical_pos.y, - ); + if let CapWindowId::Camera = window_id { + if app + .try_state::() + .is_some_and(|guard| guard.should_ignore()) + { + return; } - _ => {} + window_position_persistence::queue_camera_position( + app, + logical_pos.x, + logical_pos.y, + ); } } } @@ -4890,7 +4859,7 @@ fn handle_run_event(_handle: &AppHandle, event: tauri::RunEvent) { } else { let handle = _handle.clone(); spawn_on_runtime(async move { - let _ = ShowCapWindow::Main { + let _ = CapWindow::Main { init_target_mode: None, } .show(&handle) @@ -4958,7 +4927,7 @@ where fn show_camera_window_unlocked(app: &AppHandle) { let app = app.clone(); spawn_on_runtime(async move { - let _ = ShowCapWindow::Camera { centered: false }.show(&app).await; + let _ = CapWindow::Camera { centered: false }.show(&app).await; }); } @@ -5009,7 +4978,7 @@ fn restore_camera_window(app: &AppHandle) { return; }; let _operation_guard = operation_lock.lock().await; - let _ = ShowCapWindow::Camera { centered: false }.show(&app).await; + let _ = CapWindow::Camera { centered: false }.show(&app).await; }); } } @@ -5041,7 +5010,7 @@ fn reopen_main_window(app: &AppHandle) { } else { let handle = app.clone(); tokio::spawn(async move { - let _ = ShowCapWindow::Main { + let _ = CapWindow::Main { init_target_mode: None, } .show(&handle) @@ -5470,6 +5439,12 @@ fn format_project_name( ) } +#[tauri::command(async)] +#[specta::specta] +fn log(window: tauri::WebviewWindow, string: String) { + println!("[{}] {}", window.label(), string); +} + trait EventExt: tauri_specta::Event { fn listen_any_spawn( app: &AppHandle, @@ -5512,7 +5487,7 @@ fn open_importable_from_path(path: &Path, app: AppHandle) -> Result<(), String> tokio::spawn(async move { match import::start_video_import(app.clone(), source_path).await { Ok(project_path) => { - if let Err(err) = (ShowCapWindow::Editor { project_path }).show(&app).await { + if let Err(err) = (CapWindow::Editor { project_path }).show(&app).await { error!("Failed to show imported video editor: {err}"); show_import_error_dialog( &app, @@ -5537,7 +5512,7 @@ fn open_importable_from_path(path: &Path, app: AppHandle) -> Result<(), String> tokio::spawn(async move { match import::start_image_import(app.clone(), source_path).await { Ok(path) => { - if let Err(err) = (ShowCapWindow::ScreenshotEditor { path }).show(&app).await { + if let Err(err) = (CapWindow::ScreenshotEditor { path }).show(&app).await { error!("Failed to show imported image editor: {err}"); show_import_error_dialog( &app, @@ -5573,7 +5548,7 @@ fn open_project_from_path(path: &Path, app: AppHandle) -> Result<(), String> { } let project_path = path.to_path_buf(); - tokio::spawn(async move { ShowCapWindow::Editor { project_path }.show(&app).await }); + tokio::spawn(async move { CapWindow::Editor { project_path }.show(&app).await }); } RecordingMetaInner::Instant(_) => { let mp4_path = path.join("content/output.mp4"); diff --git a/apps/desktop/src-tauri/src/platform/macos/delegates.rs b/apps/desktop/src-tauri/src/platform/macos/delegates.rs deleted file mode 100644 index bdc4583d4f2..00000000000 --- a/apps/desktop/src-tauri/src/platform/macos/delegates.rs +++ /dev/null @@ -1,376 +0,0 @@ -// TODO(Ilya): Re-write all macos code to use `objc2` crates n -/// -/// Credit to @haasal, @charrondev, Hoppscotch app, Electron, Zed Editor -/// -/// https://github.com/haasal -/// https://gist.github.com/charrondev -/// https://github.com/hoppscotch/hoppscotch -/// https://github.com/clearlysid/tauri-plugin-decorum/ -/// (Issue) https://github.com/tauri-apps/tauri/issues/4789 -/// (Gist) https://gist.github.com/charrondev/43150e940bd2771b1ea88256d491c7a9 -/// (Hoppscotch) https://github.com/hoppscotch/hoppscotch/blob/286fcd2bb08a84f027b10308d1e18da368f95ebf/packages/hoppscotch-selfhost-desktop/src-tauri/src/mac/window.rs -/// (Electron) https://github.com/electron/electron/blob/38512efd25a159ddc64a54c22ef9eb6dd60064ec/shell/browser/native_window_mac.mm#L1454 -/// -use objc::{msg_send, sel, sel_impl}; -use rand::{Rng, distributions::Alphanumeric}; -use tauri::{Emitter, LogicalPosition, Runtime, Window}; - -pub struct UnsafeWindowHandle(pub *mut std::ffi::c_void); -unsafe impl Send for UnsafeWindowHandle {} -unsafe impl Sync for UnsafeWindowHandle {} - -#[derive(Debug)] -struct WindowState { - window: Window, - controls_inset: LogicalPosition, -} - -// TODO: Respect RTL display language -// TODO: Update Height, consider supporting the scenario where the buttons are hidden by the system due to screen sharing of the window -// https://developer.apple.com/documentation/appkit/nsapplication/1428556-userinterfacelayoutdirection?language=objc -pub fn position_window_controls( - ns_window_handle: UnsafeWindowHandle, - inset: &LogicalPosition, -) { - use cocoa::{ - appkit::{NSView, NSWindow, NSWindowButton}, - base::id, - foundation::NSRect, - }; - - let ns_window = ns_window_handle.0 as id; - unsafe { - let close = ns_window.standardWindowButton_(NSWindowButton::NSWindowCloseButton); - let minimize = ns_window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton); - let zoom = ns_window.standardWindowButton_(NSWindowButton::NSWindowZoomButton); - - let title_bar_container_view = close.superview().superview(); - - let close_rect: NSRect = msg_send![close, frame]; - let button_height = close_rect.size.height; - - let title_bar_frame_height = button_height + inset.y; - let mut title_bar_rect = NSView::frame(title_bar_container_view); - title_bar_rect.size.height = title_bar_frame_height; - title_bar_rect.origin.y = NSView::frame(ns_window).size.height - title_bar_frame_height; - let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect]; - - let window_buttons = vec![close, minimize, zoom]; - let space_between = NSView::frame(minimize).origin.x - NSView::frame(close).origin.x; - let vertical_offset = 4.0; // Adjust this value to push buttons down - - for (i, button) in window_buttons.into_iter().enumerate() { - let mut rect: NSRect = NSView::frame(button); - rect.origin.x = inset.x + (i as f64 * space_between); - rect.origin.y = ((title_bar_frame_height - button_height) / 2.0) - vertical_offset; - button.setFrameOrigin(rect.origin); - } - } -} - -pub fn setup(window: Window, controls_inset: LogicalPosition) { - use cocoa::appkit::NSWindow; - use cocoa::base::{BOOL, id}; - use cocoa::foundation::NSUInteger; - use objc::runtime::{Object, Sel}; - use std::ffi::c_void; - - let Ok(ns_win) = window.ns_window() else { - tracing::warn!("Failed to get window handle for delegate setup"); - return; - }; - - // Do the initial positioning - position_window_controls(UnsafeWindowHandle(ns_win), &controls_inset); - - // Ensure they stay in place while resizing the window. - fn with_window_state) -> T, T>( - this: &Object, - func: F, - ) { - let ptr = unsafe { - let x: *mut c_void = *this.get_ivar("app_box"); - &mut *(x as *mut WindowState) - }; - func(ptr); - } - - fn suppress_delegate_panic(selector: &'static str, fallback: T, operation: F) -> T - where - F: FnOnce() -> T, - { - match std::panic::catch_unwind(std::panic::AssertUnwindSafe(operation)) { - Ok(value) => value, - Err(_) => { - tracing::error!(selector, "Suppressed panic in macOS window delegate"); - fallback - } - } - } - - unsafe { - let ns_win_id = ns_win as id; - let current_delegate: id = ns_win_id.delegate(); - - extern "C" fn on_window_should_close(this: &Object, _cmd: Sel, sender: id) -> BOOL { - suppress_delegate_panic("windowShouldClose:", cocoa::base::NO, || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - msg_send![super_del, windowShouldClose: sender] - }) - } - extern "C" fn on_window_will_close(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("windowWillClose:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowWillClose: notification]; - }); - } - extern "C" fn on_window_did_resize(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("windowDidResize:", (), || unsafe { - with_window_state(this, |state: &mut WindowState| { - if let Ok(window_handle) = state.window.ns_window() { - position_window_controls( - UnsafeWindowHandle(window_handle), - &state.controls_inset, - ); - } else { - tracing::warn!("Failed to get handle to NSWindow during resize"); - } - }); - - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidResize: notification]; - }); - } - extern "C" fn on_window_did_move(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("windowDidMove:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidMove: notification]; - }); - } - extern "C" fn on_window_did_change_backing_properties( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic("windowDidChangeBackingProperties:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidChangeBackingProperties: notification]; - }); - } - extern "C" fn on_window_did_become_key(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("windowDidBecomeKey:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidBecomeKey: notification]; - }); - } - extern "C" fn on_window_did_resign_key(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("windowDidResignKey:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidResignKey: notification]; - }); - } - extern "C" fn on_dragging_entered(this: &Object, _cmd: Sel, notification: id) -> BOOL { - suppress_delegate_panic("draggingEntered:", cocoa::base::NO, || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - msg_send![super_del, draggingEntered: notification] - }) - } - extern "C" fn on_prepare_for_drag_operation( - this: &Object, - _cmd: Sel, - notification: id, - ) -> BOOL { - suppress_delegate_panic("prepareForDragOperation:", cocoa::base::NO, || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - msg_send![super_del, prepareForDragOperation: notification] - }) - } - extern "C" fn on_perform_drag_operation(this: &Object, _cmd: Sel, sender: id) -> BOOL { - suppress_delegate_panic("performDragOperation:", cocoa::base::NO, || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - msg_send![super_del, performDragOperation: sender] - }) - } - extern "C" fn on_conclude_drag_operation(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("concludeDragOperation:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, concludeDragOperation: notification]; - }); - } - extern "C" fn on_dragging_exited(this: &Object, _cmd: Sel, notification: id) { - suppress_delegate_panic("draggingExited:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, draggingExited: notification]; - }); - } - extern "C" fn on_window_will_use_full_screen_presentation_options( - this: &Object, - _cmd: Sel, - window: id, - proposed_options: NSUInteger, - ) -> NSUInteger { - suppress_delegate_panic( - "window:willUseFullScreenPresentationOptions:", - proposed_options, - || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - msg_send![super_del, window: window willUseFullScreenPresentationOptions: proposed_options] - }, - ) - } - extern "C" fn on_window_did_enter_full_screen( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic("windowDidEnterFullScreen:", (), || unsafe { - with_window_state(this, |state: &mut WindowState| { - if let Err(err) = state.window.emit("did-enter-fullscreen", ()) { - tracing::warn!("Failed to emit did-enter-fullscreen: {err}"); - } - }); - - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidEnterFullScreen: notification]; - }); - } - extern "C" fn on_window_will_enter_full_screen( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic("windowWillEnterFullScreen:", (), || unsafe { - with_window_state(this, |state: &mut WindowState| { - if let Err(err) = state.window.emit("will-enter-fullscreen", ()) { - tracing::warn!("Failed to emit will-enter-fullscreen: {err}"); - } - }); - - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowWillEnterFullScreen: notification]; - }); - } - extern "C" fn on_window_did_exit_full_screen( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic("windowDidExitFullScreen:", (), || unsafe { - with_window_state(this, |state: &mut WindowState| { - if let Err(err) = state.window.emit("did-exit-fullscreen", ()) { - tracing::warn!("Failed to emit did-exit-fullscreen: {err}"); - } - - if let Ok(window_handle) = state.window.ns_window() { - position_window_controls( - UnsafeWindowHandle(window_handle), - &state.controls_inset, - ); - } else { - tracing::warn!("Failed to get handle to NSWindow after exiting fullscreen"); - } - }); - - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidExitFullScreen: notification]; - }); - } - extern "C" fn on_window_will_exit_full_screen( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic("windowWillExitFullScreen:", (), || unsafe { - with_window_state(this, |state: &mut WindowState| { - if let Err(err) = state.window.emit("will-exit-fullscreen", ()) { - tracing::warn!("Failed to emit will-exit-fullscreen: {err}"); - } - }); - - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowWillExitFullScreen: notification]; - }); - } - extern "C" fn on_window_did_fail_to_enter_full_screen( - this: &Object, - _cmd: Sel, - window: id, - ) { - suppress_delegate_panic("windowDidFailToEnterFullScreen:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, windowDidFailToEnterFullScreen: window]; - }); - } - extern "C" fn on_effective_appearance_did_change( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic("effectiveAppearanceDidChange:", (), || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![super_del, effectiveAppearanceDidChange: notification]; - }); - } - extern "C" fn on_effective_appearance_did_changed_on_main_thread( - this: &Object, - _cmd: Sel, - notification: id, - ) { - suppress_delegate_panic( - "effectiveAppearanceDidChangedOnMainThread:", - (), - || unsafe { - let super_del: id = *this.get_ivar("super_delegate"); - let _: () = msg_send![ - super_del, - effectiveAppearanceDidChangedOnMainThread: notification - ]; - }, - ); - } - - let window_label = window.label().to_string(); - - let app_state = WindowState { - window, - controls_inset, - }; - let app_box = Box::into_raw(Box::new(app_state)) as *mut c_void; - let random_str: String = rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(20) - .map(char::from) - .collect(); - - // We need to ensure we have a unique delegate name, otherwise we will panic while trying to create a duplicate - // delegate with the same name. - let delegate_name = format!("windowDelegate_cap_{window_label}_{random_str}"); - - ns_win_id.setDelegate_(cocoa::delegate!(&delegate_name, { - window: id = ns_win_id, - app_box: *mut c_void = app_box, - toolbar: id = cocoa::base::nil, - super_delegate: id = current_delegate, - (windowShouldClose:) => on_window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL, - (windowWillClose:) => on_window_will_close as extern "C" fn(&Object, Sel, id), - (windowDidResize:) => on_window_did_resize:: as extern "C" fn(&Object, Sel, id), - (windowDidMove:) => on_window_did_move as extern "C" fn(&Object, Sel, id), - (windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern "C" fn(&Object, Sel, id), - (windowDidBecomeKey:) => on_window_did_become_key as extern "C" fn(&Object, Sel, id), - (windowDidResignKey:) => on_window_did_resign_key as extern "C" fn(&Object, Sel, id), - (draggingEntered:) => on_dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL, - (prepareForDragOperation:) => on_prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, - (performDragOperation:) => on_perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL, - (concludeDragOperation:) => on_conclude_drag_operation as extern "C" fn(&Object, Sel, id), - (draggingExited:) => on_dragging_exited as extern "C" fn(&Object, Sel, id), - (window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger, - (windowDidEnterFullScreen:) => on_window_did_enter_full_screen:: as extern "C" fn(&Object, Sel, id), - (windowWillEnterFullScreen:) => on_window_will_enter_full_screen:: as extern "C" fn(&Object, Sel, id), - (windowDidExitFullScreen:) => on_window_did_exit_full_screen:: as extern "C" fn(&Object, Sel, id), - (windowWillExitFullScreen:) => on_window_will_exit_full_screen:: as extern "C" fn(&Object, Sel, id), - (windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern "C" fn(&Object, Sel, id), - (effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern "C" fn(&Object, Sel, id), - (effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id) - })) - } -} diff --git a/apps/desktop/src-tauri/src/platform/macos/mod.rs b/apps/desktop/src-tauri/src/platform/macos/mod.rs index 0fb75385083..41aa96f7a9c 100644 --- a/apps/desktop/src-tauri/src/platform/macos/mod.rs +++ b/apps/desktop/src-tauri/src/platform/macos/mod.rs @@ -1,19 +1,8 @@ -// use std::ffi::c_void; - -// use cocoa::{ -// base::{id, nil}, -// foundation::NSString, -// }; -// use core_graphics::{ -// base::boolean_t, -// display::{CFDictionaryRef, CGRect}, -// }; -// use objc::{class, msg_send, sel, sel_impl}; - -pub mod delegates; mod sc_shareable_content; +use objc2_app_kit::NSWindow; pub use sc_shareable_content::*; +use tauri::WebviewWindow; pub fn set_window_level(window: tauri::Window, level: objc2_app_kit::NSWindowLevel) { let c_window = window.clone(); @@ -26,56 +15,14 @@ pub fn set_window_level(window: tauri::Window, level: objc2_app_kit::NSWindowLev }); } -pub fn apply_squircle_corners(window: &tauri::WebviewWindow, radius: f64) { - use cocoa::base::{id, nil}; - use cocoa::foundation::NSString; - use objc::{msg_send, sel, sel_impl}; - - let Ok(ns_win) = window.ns_window() else { - return; - }; - - unsafe { - let ns_win = ns_win as id; - let content_view: id = msg_send![ns_win, contentView]; - - if content_view != nil { - let _: () = msg_send![content_view, setWantsLayer: true]; - - let layer: id = msg_send![content_view, layer]; - if layer != nil { - let _: () = msg_send![layer, setCornerRadius: radius]; - let _: () = msg_send![layer, setMasksToBounds: true]; +pub trait WebviewWindowExt { + fn objc2_nswindow(&self) -> &NSWindow; +} - let continuous = NSString::alloc(nil).init_str("continuous"); - let _: () = msg_send![layer, setCornerCurve: continuous]; - } - } +impl WebviewWindowExt for WebviewWindow { + #[inline] + fn objc2_nswindow(&self) -> &NSWindow { + // SAFETY: This cast is safe as long as we get a NSWindow from Tauri. + unsafe { &*self.ns_window().expect("NSWindow not ready").cast() } } } - -// pub fn get_ns_window_number(ns_window: *mut c_void) -> isize { -// let ns_window = ns_window as *const objc2_app_kit::NSWindow; - -// unsafe { (*ns_window).windowNumber() } -// } - -// #[link(name = "CoreGraphics", kind = "framework")] -// unsafe extern "C" { -// pub fn CGRectMakeWithDictionaryRepresentation( -// dict: CFDictionaryRef, -// rect: *mut CGRect, -// ) -> boolean_t; -// } - -// /// Makes the background of the WKWebView layer transparent. -// /// This differs from Tauri's implementation as it does not change the window background which causes performance performance issues and artifacts when shadows are enabled on the window. -// /// Use Tauri's implementation to make the window itself transparent. -// pub fn make_webview_transparent(target: &tauri::WebviewWindow) -> tauri::Result<()> { -// target.with_webview(|webview| unsafe { -// let wkwebview = webview.inner() as id; -// let no: id = msg_send![class!(NSNumber), numberWithBool:0]; -// // [https://developer.apple.com/documentation/webkit/webview/1408486-drawsbackground] -// let _: id = msg_send![wkwebview, setValue:no forKey: NSString::alloc(nil).init_str("drawsBackground")]; -// }) -// } diff --git a/apps/desktop/src-tauri/src/recording.rs b/apps/desktop/src-tauri/src/recording.rs index b16c008fa5e..be43293bd65 100644 --- a/apps/desktop/src-tauri/src/recording.rs +++ b/apps/desktop/src-tauri/src/recording.rs @@ -69,7 +69,7 @@ use crate::{ thumbnails::*, upload::{InstantMultipartUpload, SegmentUploader, compress_image}, web_api::ManagerExt, - windows::{CapWindowId, ShowCapWindow, hide_overlay}, + windows::{CapWindowId, CapWindow, hide_overlay}, }; #[derive(Clone)] @@ -680,7 +680,7 @@ pub async fn start_recording( let operation_lock = app.state::(); let _operation_guard = operation_lock.lock().await; - ShowCapWindow::Camera { centered: true } + CapWindow::Camera { centered: true } .show(&app) .await .map_err(|err| format!("Failed to show centered camera window: {err}"))?; @@ -844,13 +844,13 @@ pub async fn start_recording( if let Some(show) = inputs .capture_target .display() - .map(|d| ShowCapWindow::WindowCaptureOccluder { screen_id: d.id() }) + .map(|d| CapWindow::WindowCaptureOccluder { screen_id: d.id() }) { let _ = show.show(&app).await; } } ScreenCaptureTarget::Area { screen, .. } => { - let _ = ShowCapWindow::WindowCaptureOccluder { + let _ = CapWindow::WindowCaptureOccluder { screen_id: screen.clone(), } .show(&app) @@ -875,7 +875,7 @@ pub async fn start_recording( hide_overlay(win); } } - let _ = ShowCapWindow::InProgressRecording { + let _ = CapWindow::InProgressRecording { countdown, capture_target: Some(inputs.capture_target.clone()), } @@ -1783,7 +1783,7 @@ pub async fn delete_recording(app: AppHandle, state: MutableState<'_, App>) -> R match settings.post_deletion_behaviour { PostDeletionBehaviour::DoNothing => {} PostDeletionBehaviour::ReopenRecordingWindow => { - let _ = ShowCapWindow::Main { + let _ = CapWindow::Main { init_target_mode: None, } .show(&app) @@ -2186,14 +2186,14 @@ async fn handle_recording_finish( match post_behaviour { PostStudioRecordingBehaviour::OpenEditor => { - let _ = ShowCapWindow::Editor { + let _ = CapWindow::Editor { project_path: recording_dir.clone(), } .show(app) .await; } PostStudioRecordingBehaviour::ShowOverlay => { - let _ = ShowCapWindow::RecordingsOverlay.show(app).await; + let _ = CapWindow::RecordingsOverlay.show(app).await; let app_clone = AppHandle::clone(app); let recording_dir_clone = recording_dir.clone(); @@ -2422,14 +2422,14 @@ async fn handle_recording_finish( .unwrap_or(PostStudioRecordingBehaviour::OpenEditor) { PostStudioRecordingBehaviour::OpenEditor => { - let _ = ShowCapWindow::Editor { + let _ = CapWindow::Editor { project_path: recording_dir, } .show(app) .await; } PostStudioRecordingBehaviour::ShowOverlay => { - let _ = ShowCapWindow::RecordingsOverlay.show(app).await; + let _ = CapWindow::RecordingsOverlay.show(app).await; let app = AppHandle::clone(app); tokio::spawn(async move { diff --git a/apps/desktop/src-tauri/src/target_select_overlay.rs b/apps/desktop/src-tauri/src/target_select_overlay.rs index 91bb67df584..368caaff60b 100644 --- a/apps/desktop/src-tauri/src/target_select_overlay.rs +++ b/apps/desktop/src-tauri/src/target_select_overlay.rs @@ -13,7 +13,7 @@ use crate::{ App, ArcLock, general_settings, recording_settings::RecordingTargetMode, window_exclusion::WindowExclusion, - windows::{CapWindowId, ShowCapWindow, hide_overlay, show_overlay}, + windows::{CapWindowId, CapWindow, hide_overlay, show_overlay}, }; use scap_targets::{ Display, DisplayId, Window, WindowId, @@ -120,7 +120,7 @@ pub async fn open_target_select_overlays( state.spawn(display_id, window.clone()); } else if start.elapsed() < Duration::from_secs(1) { - if let Ok(window) = (ShowCapWindow::TargetSelectOverlay { + if let Ok(window) = (CapWindow::TargetSelectOverlay { display_id: display_id.clone(), target_mode, }) @@ -136,7 +136,7 @@ pub async fn open_target_select_overlays( let app_clone = app.clone(); let display_id_clone = display_id.clone(); tokio::spawn(async move { - if let Ok(window) = (ShowCapWindow::TargetSelectOverlay { + if let Ok(window) = (CapWindow::TargetSelectOverlay { display_id: display_id_clone, target_mode, }) diff --git a/apps/desktop/src-tauri/src/tray.rs b/apps/desktop/src-tauri/src/tray.rs index 348145a2519..0982f9f916a 100644 --- a/apps/desktop/src-tauri/src/tray.rs +++ b/apps/desktop/src-tauri/src/tray.rs @@ -2,7 +2,7 @@ use crate::{ NewScreenshotAdded, NewStudioRecordingAdded, RecordingStarted, RecordingStopped, RequestOpenSettings, recording, recording_settings::{RecordingSettingsStore, RecordingTargetMode}, - windows::ShowCapWindow, + windows::CapWindow, }; use cap_recording::RecordingMode; @@ -575,7 +575,7 @@ fn handle_previous_item_click(app: &AppHandle, path_str: &str) { let app = app.clone(); let screenshot_path = path; tokio::spawn(async move { - let _ = ShowCapWindow::ScreenshotEditor { + let _ = CapWindow::ScreenshotEditor { path: screenshot_path, } .show(&app) @@ -597,7 +597,7 @@ fn handle_previous_item_click(app: &AppHandle, path_str: &str) { let app = app.clone(); let project_path = path.clone(); tokio::spawn(async move { - let _ = ShowCapWindow::Editor { project_path }.show(&app).await; + let _ = CapWindow::Editor { project_path }.show(&app).await; }); } RecordingMetaInner::Instant(_) => { @@ -689,7 +689,7 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> { Ok(TrayItem::OpenCap) => { let app = app.clone(); tokio::spawn(async move { - let _ = ShowCapWindow::Main { + let _ = CapWindow::Main { init_target_mode: None, } .show(&app) @@ -726,7 +726,7 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> { match recording::take_screenshot(app.clone(), target).await { Ok(path) => { - let _ = ShowCapWindow::ScreenshotEditor { path }.show(&app).await; + let _ = CapWindow::ScreenshotEditor { path }.show(&app).await; } Err(e) => { tracing::error!("Failed to take screenshot: {e}"); @@ -757,7 +757,7 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> { match crate::import::start_video_import(app.clone(), path).await { Ok(project_path) => { - let _ = ShowCapWindow::Editor { project_path }.show(&app).await; + let _ = CapWindow::Editor { project_path }.show(&app).await; } Err(e) => { tracing::error!("Failed to import video: {e}"); @@ -786,7 +786,7 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> { Ok(TrayItem::OpenSettings) => { let app = app.clone(); tokio::spawn( - async move { ShowCapWindow::Settings { page: None }.show(&app).await }, + async move { CapWindow::Settings { page: None }.show(&app).await }, ); } Ok(TrayItem::UploadLogs) => { @@ -827,7 +827,7 @@ pub fn create_tray(app: &AppHandle) -> tauri::Result<()> { Ok(TrayItem::RequestPermissions) => { let app = app.clone(); tokio::spawn(async move { - let _ = ShowCapWindow::Onboarding.show(&app).await; + let _ = CapWindow::Onboarding.show(&app).await; }); } _ => {} diff --git a/apps/desktop/src-tauri/src/window_position_persistence.rs b/apps/desktop/src-tauri/src/window_position_persistence.rs index 55b4b802fbf..46244b2892f 100644 --- a/apps/desktop/src-tauri/src/window_position_persistence.rs +++ b/apps/desktop/src-tauri/src/window_position_persistence.rs @@ -91,9 +91,6 @@ pub fn install(app: &AppHandle) { let write_app = app_handle.clone(); let write_result = tokio::task::spawn_blocking(move || { GeneralSettingsStore::update(&write_app, |settings| { - if let Some(main) = pending.main { - settings.main_window_position = Some(main); - } if let Some((x, y)) = pending.camera_position { crate::update_camera_window_position_settings(settings, x, y); } @@ -112,12 +109,6 @@ pub fn install(app: &AppHandle) { }); } -pub fn queue_main_position(app: &AppHandle, position: WindowPosition) { - if let Some(persistence) = app.try_state::>() { - persistence.queue_main(position); - } -} - pub fn queue_camera_position(app: &AppHandle, x: f64, y: f64) { if let Some(persistence) = app.try_state::>() { persistence.queue_camera(x, y); diff --git a/apps/desktop/src-tauri/src/windows.rs b/apps/desktop/src-tauri/src/windows.rs index fb916f767f4..0e1b9fdef50 100644 --- a/apps/desktop/src-tauri/src/windows.rs +++ b/apps/desktop/src-tauri/src/windows.rs @@ -17,8 +17,8 @@ use std::{ time::Duration, }; use tauri::{ - AppHandle, LogicalPosition, LogicalSize, Manager, Monitor, PhysicalPosition, PhysicalSize, - WebviewUrl, WebviewWindow, WebviewWindowBuilder, Wry, + AppHandle, Listener, LogicalPosition, LogicalSize, Manager, Monitor, PhysicalPosition, + PhysicalSize, WebviewUrl, WebviewWindow, WebviewWindowBuilder, Wry, }; use tauri_specta::Event; use tokio::sync::RwLock; @@ -31,10 +31,12 @@ use crate::{ App, ArcLock, CameraWindowCloseGate, CameraWindowPositionGuard, MainWindowReadyState, NewNotification, RequestScreenCapturePrewarm, RequestSetTargetMode, camera_preview_error_message, + display_utils::{CursorMonitorInfo, MonitorExt}, editor_window::PendingEditorInstances, emit_camera_preview_clear, emit_camera_preview_error, fake_window, - general_settings::{self, AppTheme, GeneralSettingsStore}, + general_settings::{self, Appearance, GeneralSettingsStore}, permissions, + platform::WebviewWindowExt, recording::{RecordingEvent, RecordingInputKind}, recording_settings::RecordingTargetMode, screenshot_editor::PendingScreenshotEditorInstances, @@ -44,49 +46,7 @@ use crate::{ use cap_recording::{feeds, sources::screen_capture::ScreenCaptureTarget}; #[cfg(target_os = "macos")] -const DEFAULT_TRAFFIC_LIGHTS_INSET: LogicalPosition = LogicalPosition::new(12.0, 12.0); - -const DEFAULT_FALLBACK_DISPLAY_WIDTH: f64 = 1920.0; -const DEFAULT_FALLBACK_DISPLAY_HEIGHT: f64 = 1080.0; - -#[cfg(target_os = "macos")] -fn is_system_dark_mode() -> bool { - use cocoa::base::{id, nil}; - use cocoa::foundation::NSString; - use objc::{class, msg_send, sel, sel_impl}; - - unsafe { - let app: id = msg_send![class!(NSApplication), sharedApplication]; - let appearance: id = msg_send![app, effectiveAppearance]; - if appearance == nil { - return false; - } - let name: id = msg_send![appearance, name]; - if name == nil { - return false; - } - let dark_appearance = NSString::alloc(nil).init_str("NSAppearanceNameDarkAqua"); - let vibrant_dark = NSString::alloc(nil).init_str("NSAppearanceNameVibrantDark"); - let is_dark: bool = msg_send![name, isEqualToString: dark_appearance]; - let is_vibrant_dark: bool = msg_send![name, isEqualToString: vibrant_dark]; - is_dark || is_vibrant_dark - } -} - -#[cfg(target_os = "windows")] -fn is_system_dark_mode() -> bool { - use winreg::RegKey; - use winreg::enums::HKEY_CURRENT_USER; - - let hkcu = RegKey::predef(HKEY_CURRENT_USER); - if let Ok(key) = - hkcu.open_subkey("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize") - && let Ok(value) = key.get_value::("AppsUseLightTheme") - { - return value == 0; - } - false -} +const DEFAULT_TRAFFIC_LIGHTS_POS: LogicalPosition = LogicalPosition::new(13.0, 16.0); pub fn hide_overlay(window: &WebviewWindow) { let _ = window.set_ignore_cursor_events(true); @@ -501,93 +461,6 @@ pub(crate) async fn cleanup_camera_window( !still_exists } -struct CursorMonitorInfo { - x: f64, - y: f64, - width: f64, - height: f64, -} - -impl CursorMonitorInfo { - fn get() -> Self { - let display = Display::get_containing_cursor().unwrap_or_else(Display::primary); - let bounds = display.raw_handle().logical_bounds(); - let (x, y, width, height) = bounds - .map(|b| { - ( - b.position().x(), - b.position().y(), - b.size().width(), - b.size().height(), - ) - }) - .unwrap_or(( - 0.0, - 0.0, - DEFAULT_FALLBACK_DISPLAY_WIDTH, - DEFAULT_FALLBACK_DISPLAY_HEIGHT, - )); - - Self { - x, - y, - width, - height, - } - } - - fn center_position(&self, window_width: f64, window_height: f64) -> (f64, f64) { - let pos_x = self.x + (self.width - window_width) / 2.0; - let pos_y = self.y + (self.height - window_height) / 2.0; - (pos_x, pos_y) - } - - fn bottom_center_position( - &self, - window_width: f64, - window_height: f64, - offset_y: f64, - ) -> (f64, f64) { - let pos_x = self.x + (self.width - window_width) / 2.0; - let pos_y = self.y + self.height - window_height - offset_y; - (pos_x, pos_y) - } - - fn from_window(window: &tauri::WebviewWindow) -> Self { - let window_pos = window - .outer_position() - .ok() - .map(|p| (p.x as f64, p.y as f64)) - .unwrap_or((0.0, 0.0)); - - for display in Display::list() { - if let Some(bounds) = display.raw_handle().logical_bounds() { - let (x, y, width, height) = ( - bounds.position().x(), - bounds.position().y(), - bounds.size().width(), - bounds.size().height(), - ); - - if window_pos.0 >= x - && window_pos.0 < x + width - && window_pos.1 >= y - && window_pos.1 < y + height - { - return Self { - x, - y, - width, - height, - }; - } - } - } - - Self::get() - } -} - fn center_camera_window(app: &AppHandle, window: &WebviewWindow) { let camera_state = match app.try_state::>() { Some(state) => state @@ -699,19 +572,6 @@ fn is_position_on_any_screen(pos_x: f64, pos_y: f64) -> bool { false } -fn ensure_settings_window_bounds(window: &WebviewWindow) { - const MIN_W: f64 = 800.0; - const MIN_H: f64 = 580.0; - let _ = window.set_min_size(Some(LogicalSize::new(MIN_W, MIN_H))); - if let (Ok(physical), Ok(scale)) = (window.inner_size(), window.scale_factor()) { - let width = physical.width as f64 / scale; - let height = physical.height as f64 / scale; - if width < MIN_W || height < MIN_H { - let _ = window.set_size(LogicalSize::new(width.max(MIN_W), height.max(MIN_H))); - } - } -} - #[derive(Clone, Deserialize, Type)] pub enum CapWindowId { Main, @@ -836,20 +696,6 @@ impl CapWindowId { ) } - pub fn is_transparent(&self) -> bool { - matches!( - self, - Self::Main - | Self::Onboarding - | Self::Camera - | Self::WindowCaptureOccluder { .. } - | Self::CaptureArea - | Self::RecordingControls - | Self::RecordingsOverlay - | Self::TargetSelectOverlay { .. } - ) - } - pub fn get(&self, app: &AppHandle) -> Option { if matches!(self, Self::Camera) { return current_camera_window(app); @@ -863,11 +709,9 @@ impl CapWindowId { pub fn traffic_lights_position(&self) -> Option>> { match self { Self::Editor { .. } | Self::ScreenshotEditor { .. } => { - Some(Some(LogicalPosition::new(20.0, 32.0))) + Some(Some(LogicalPosition::new(20.0, 24.0))) } Self::Camera - | Self::Main - | Self::Onboarding | Self::WindowCaptureOccluder { .. } | Self::CaptureArea | Self::RecordingsOverlay @@ -893,7 +737,7 @@ impl CapWindowId { } #[derive(Debug, Clone, Type, Deserialize)] -pub enum ShowCapWindow { +pub enum CapWindow { Main { init_target_mode: Option, }, @@ -930,7 +774,7 @@ pub enum ShowCapWindow { Onboarding, } -impl ShowCapWindow { +impl CapWindow { pub async fn show(&self, app: &AppHandle) -> tauri::Result { if let Self::Editor { project_path } = &self { let state = app.state::(); @@ -1332,10 +1176,6 @@ impl ShowCapWindow { window.unminimize().ok(); window.set_focus().ok(); - if let Self::Settings { .. } = self { - ensure_settings_window_bounds(&window); - } - if let Self::Main { init_target_mode } = self { emit_app_event( app, @@ -1380,7 +1220,6 @@ impl ShowCapWindow { .visible_on_all_workspaces(true) .content_protected(should_protect) .transparent(true) - .visible(false) .initialization_script(format!( " window.__CAP__ = window.__CAP__ ?? {{}}; @@ -1391,63 +1230,9 @@ impl ShowCapWindow { )) .build()?; - let saved_position = GeneralSettingsStore::get(app) - .ok() - .flatten() - .and_then(|s| s.main_window_position) - .filter(|pos| is_position_on_any_screen(pos.x, pos.y)); - - let (pos_x, pos_y) = if let Some(pos) = saved_position { - (pos.x, pos.y) - } else { - cursor_monitor.center_position(330.0, 395.0) - }; - #[cfg(target_os = "macos")] { - app.run_on_main_thread({ - let window = window.clone(); - let app = app.clone(); - move || { - use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior; - use tauri_nspanel::panel_delegate; - use crate::panel_manager::try_to_panel; - - const MAIN_PANEL_LEVEL: i32 = 100; - - let delegate = panel_delegate!(MainPanelDelegate { - window_did_become_key, - window_did_resign_key - }); - - delegate.set_listener(Box::new(|_delegate_name: String| {})); - - let panel = match try_to_panel(&window) { - Ok(p) => p, - Err(e) => { - tracing::error!("Failed to convert main window to panel: {}", e); - crate::permissions::sync_macos_dock_visibility(&app); - return; - } - }; - - panel.set_collection_behaviour( - NSWindowCollectionBehavior::NSWindowCollectionBehaviorCanJoinAllSpaces - | NSWindowCollectionBehavior::NSWindowCollectionBehaviorFullScreenPrimary, - ); - - panel.set_delegate(delegate); - - panel.set_level(MAIN_PANEL_LEVEL); - - let _ = window.set_position(tauri::LogicalPosition::new(pos_x, pos_y)); - - crate::platform::apply_squircle_corners(&window, 16.0); - - crate::permissions::schedule_macos_dock_visibility_sync(&app); - } - }) - .ok(); + crate::permissions::schedule_macos_dock_visibility_sync(&app); let app_handle = app.clone(); tauri::async_runtime::spawn(async move { @@ -1524,7 +1309,6 @@ impl ShowCapWindow { .visible_on_all_workspaces(true) .skip_taskbar(true) .transparent(true) - .visible(false) .initialization_script(format!( "window.__CAP__ = window.__CAP__ ?? {{}}; window.__CAP__.cameraWsPort = {camera_ws_port};" )); @@ -1614,6 +1398,7 @@ impl ShowCapWindow { let max_level = unsafe { CGWindowLevelForKey(kCGMaximumWindowLevelKey) }; panel.set_level(max_level - 1); + panel.set_style_mask(objc2_app_kit::NSWindowStyleMask::NonactivatingPanel.0 as i32); panel.order_front_regardless(); panel.show(); @@ -1644,9 +1429,6 @@ impl ShowCapWindow { .focused(true) .build()?; - let (pos_x, pos_y) = cursor_monitor.center_position(800.0, 580.0); - let _ = window.set_position(tauri::LogicalPosition::new(pos_x, pos_y)); - #[cfg(windows)] { if let Err(e) = window.set_size(LogicalSize::new(800.0, 580.0)) { @@ -1657,10 +1439,6 @@ impl ShowCapWindow { } } - window.show().ok(); - window.set_focus().ok(); - ensure_settings_window_bounds(&window); - window } Self::Editor { .. } => { @@ -1674,9 +1452,6 @@ impl ShowCapWindow { .focused(true) .build()?; - let (pos_x, pos_y) = cursor_monitor.center_position(1275.0, 800.0); - let _ = window.set_position(tauri::LogicalPosition::new(pos_x, pos_y)); - #[cfg(windows)] { use tauri::LogicalSize; @@ -1717,9 +1492,6 @@ impl ShowCapWindow { } }; - let (pos_x, pos_y) = cursor_monitor.center_position(1240.0, 800.0); - let _ = window.set_position(tauri::LogicalPosition::new(pos_x, pos_y)); - #[cfg(windows)] { use tauri::LogicalSize; @@ -1737,9 +1509,6 @@ impl ShowCapWindow { } } - window.show().ok(); - window.set_focus().ok(); - window } Self::Upgrade => { @@ -1772,9 +1541,6 @@ impl ShowCapWindow { } } - window.show().ok(); - window.set_focus().ok(); - window } Self::ModeSelect => { @@ -1807,9 +1573,6 @@ impl ShowCapWindow { } } - window.show().ok(); - window.set_focus().ok(); - window } Self::Onboarding => { @@ -1847,9 +1610,6 @@ impl ShowCapWindow { } } - window.show().ok(); - window.set_focus().ok(); - window } Self::Camera { centered } => { @@ -1944,8 +1704,7 @@ impl ShowCapWindow { state.camera_ws_port, centered, enable_native_camera_preview )) .content_protected(should_protect) - .transparent(true) - .visible(false); + .transparent(true); let window = match window_builder.build() { Ok(w) => w, @@ -2229,13 +1988,10 @@ impl ShowCapWindow { // Hide the main window if the target monitor is the same if let Some(main_window) = CapWindowId::Main.get(app) - && let (Ok(outer_pos), Ok(outer_size)) = - (main_window.outer_position(), main_window.outer_size()) - && let Ok(scale_factor) = main_window.scale_factor() - && display.intersects(outer_pos, outer_size, scale_factor) + && display.intersects_window(window.as_ref().window())? { let _ = main_window.minimize(); - }; + } window } @@ -2265,7 +2021,6 @@ impl ShowCapWindow { .content_protected(should_protect) .inner_size(width, height) .skip_taskbar(true) - .visible(false) .initialization_script(format!( "window.COUNTDOWN = {};", countdown.unwrap_or_default() @@ -2324,7 +2079,7 @@ impl ShowCapWindow { let window = window.clone(); let app = app.clone(); move || { - use tauri_nspanel::cocoa::appkit::NSWindowCollectionBehavior; + use tauri_nspanel::cocoa::appkit::{NSWindowCollectionBehavior, NSWindowStyleMask}; use tauri_nspanel::panel_delegate; use tauri_nspanel::WebviewWindowExt as NSPanelWebviewWindowExt; @@ -2358,6 +2113,7 @@ impl ShowCapWindow { ); panel.set_delegate(delegate); + panel.set_style_mask(objc2_app_kit::NSWindowStyleMask::NonactivatingPanel.0 as i32); let max_level = unsafe { CGWindowLevelForKey(kCGMaximumWindowLevelKey) }; panel.set_level(max_level); @@ -2428,6 +2184,7 @@ impl ShowCapWindow { }; panel.set_level(cocoa::appkit::NSMainMenuWindowLevel); + panel.set_style_mask(objc2_app_kit::NSWindowStyleMask::NonactivatingPanel.0 as i32); panel.set_collection_behaviour( NSWindowCollectionBehavior::NSWindowCollectionBehaviorTransient @@ -2437,8 +2194,7 @@ impl ShowCapWindow { ); #[allow(non_upper_case_globals)] - const NSWindowStyleMaskNonActivatingPanel: i32 = 1 << 7; - panel.set_style_mask(NSWindowStyleMaskNonActivatingPanel); + panel.set_style_mask(objc2_app_kit::NSWindowStyleMask::NonactivatingPanel.0 as i32); } }) .ok(); @@ -2450,14 +2206,6 @@ impl ShowCapWindow { } }; - // removing this for now as it causes windows to just stay hidden sometimes -_- - // window.hide().ok(); - - #[cfg(target_os = "macos")] - if let Some(position) = _id.traffic_lights_position() { - add_traffic_lights(&window, position); - } - #[cfg(target_os = "macos")] if _id.activates_dock() { crate::permissions::sync_macos_dock_visibility(app); @@ -2483,15 +2231,10 @@ impl ShowCapWindow { ) -> WebviewWindowBuilder<'a, Wry, AppHandle> { let id = self.id(app); - let theme = GeneralSettingsStore::get(app) + let theme: Option = GeneralSettingsStore::get(app) .ok() .flatten() - .map(|s| match s.theme { - AppTheme::System => None, - AppTheme::Light => Some(tauri::Theme::Light), - AppTheme::Dark => Some(tauri::Theme::Dark), - }) - .unwrap_or(None); + .and_then(|s| s.appearance.into()); let mut builder = WebviewWindow::builder(app, label, WebviewUrl::App(url.into())) .title(id.title()) @@ -2501,20 +2244,6 @@ impl ShowCapWindow { .theme(theme) .devtools(cfg!(debug_assertions)); - if !id.is_transparent() { - let is_dark = match theme { - Some(tauri::Theme::Dark) => true, - Some(tauri::Theme::Light) => false, - None | Some(_) => is_system_dark_mode(), - }; - - let bg_color = if is_dark { "#141414" } else { "#ffffff" }; - let init_script = format!( - r#"(function(){{var s=document.createElement('style');s.textContent='html,body{{background-color:{bg_color}}}';document.documentElement.appendChild(s);}})();"# - ); - builder = builder.initialization_script(&init_script); - } - if let Some(min) = id.min_size() { builder = builder .inner_size(min.0, min.1) @@ -2523,10 +2252,11 @@ impl ShowCapWindow { #[cfg(target_os = "macos")] { - if id.traffic_lights_position().is_some() { + if let Some(pos) = id.traffic_lights_position() { builder = builder .hidden_title(true) - .title_bar_style(tauri::TitleBarStyle::Overlay); + .title_bar_style(tauri::TitleBarStyle::Overlay) + .traffic_light_position(pos.unwrap_or(DEFAULT_TRAFFIC_LIGHTS_POS)); } else { builder = builder.decorations(false) } @@ -2542,32 +2272,28 @@ impl ShowCapWindow { pub fn id(&self, app: &AppHandle) -> CapWindowId { match self { - ShowCapWindow::Main { .. } => CapWindowId::Main, - ShowCapWindow::Settings { .. } => CapWindowId::Settings, - ShowCapWindow::Editor { project_path } => { + CapWindow::Main { .. } => CapWindowId::Main, + CapWindow::Settings { .. } => CapWindowId::Settings, + CapWindow::Editor { project_path } => { let state = app.state::(); let s = state.ids.lock().unwrap(); let id = s.iter().find(|(path, _)| path == project_path).unwrap().1; CapWindowId::Editor { id } } - ShowCapWindow::RecordingsOverlay => CapWindowId::RecordingsOverlay, - ShowCapWindow::TargetSelectOverlay { display_id, .. } => { - CapWindowId::TargetSelectOverlay { - display_id: display_id.clone(), - } - } - ShowCapWindow::WindowCaptureOccluder { screen_id } => { - CapWindowId::WindowCaptureOccluder { - screen_id: screen_id.clone(), - } - } - ShowCapWindow::CaptureArea { .. } => CapWindowId::CaptureArea, - ShowCapWindow::Camera { .. } => CapWindowId::Camera, - ShowCapWindow::InProgressRecording { .. } => CapWindowId::RecordingControls, - ShowCapWindow::Upgrade => CapWindowId::Upgrade, - ShowCapWindow::ModeSelect => CapWindowId::ModeSelect, - ShowCapWindow::Onboarding => CapWindowId::Onboarding, - ShowCapWindow::ScreenshotEditor { path } => { + CapWindow::RecordingsOverlay => CapWindowId::RecordingsOverlay, + CapWindow::TargetSelectOverlay { display_id, .. } => CapWindowId::TargetSelectOverlay { + display_id: display_id.clone(), + }, + CapWindow::WindowCaptureOccluder { screen_id } => CapWindowId::WindowCaptureOccluder { + screen_id: screen_id.clone(), + }, + CapWindow::CaptureArea { .. } => CapWindowId::CaptureArea, + CapWindow::Camera { .. } => CapWindowId::Camera, + CapWindow::InProgressRecording { .. } => CapWindowId::RecordingControls, + CapWindow::Upgrade => CapWindowId::Upgrade, + CapWindow::ModeSelect => CapWindowId::ModeSelect, + CapWindow::Onboarding => CapWindowId::Onboarding, + CapWindow::ScreenshotEditor { path } => { let state = app.state::(); let s = state.ids.lock().unwrap(); let id = s.iter().find(|(p, _)| p == path).unwrap().1; @@ -2577,83 +2303,6 @@ impl ShowCapWindow { } } -#[cfg(target_os = "macos")] -fn add_traffic_lights(window: &WebviewWindow, controls_inset: Option>) { - use crate::platform::delegates; - - let target_window = window.clone(); - window - .run_on_main_thread(move || { - delegates::setup( - target_window.as_ref().window(), - controls_inset.unwrap_or(DEFAULT_TRAFFIC_LIGHTS_INSET), - ); - - let c_win = target_window.clone(); - target_window.on_window_event(move |event| match event { - tauri::WindowEvent::ThemeChanged(..) | tauri::WindowEvent::Focused(..) => { - position_traffic_lights_impl(&c_win.as_ref().window(), controls_inset); - } - _ => {} - }); - }) - .ok(); -} - -#[tauri::command] -#[specta::specta] -#[instrument(skip(window))] -pub fn set_theme(window: tauri::Window, theme: AppTheme) { - let _ = window.set_theme(match theme { - AppTheme::System => None, - AppTheme::Light => Some(tauri::Theme::Light), - AppTheme::Dark => Some(tauri::Theme::Dark), - }); - - #[cfg(target_os = "macos")] - match CapWindowId::from_str(window.label()) { - Ok(win) if win.traffic_lights_position().is_some() => position_traffic_lights(window, None), - Ok(_) | Err(_) => {} - } -} - -#[tauri::command] -#[specta::specta] -#[instrument(skip(_window))] -pub fn position_traffic_lights(_window: tauri::Window, _controls_inset: Option<(f64, f64)>) { - #[cfg(target_os = "macos")] - position_traffic_lights_impl( - &_window, - _controls_inset.map(LogicalPosition::from).or_else(|| { - // Attempt to get the default inset from the window's traffic lights position - CapWindowId::from_str(_window.label()) - .ok() - .and_then(|id| id.traffic_lights_position().flatten()) - }), - ); -} - -#[cfg(target_os = "macos")] -fn position_traffic_lights_impl( - window: &tauri::Window, - controls_inset: Option>, -) { - use crate::platform::delegates::{UnsafeWindowHandle, position_window_controls}; - let c_win = window.clone(); - window - .run_on_main_thread(move || { - let ns_window = match c_win.ns_window() { - Ok(handle) => handle, - Err(_) => return, - }; - position_window_controls( - UnsafeWindowHandle(ns_window), - &controls_inset.unwrap_or(DEFAULT_TRAFFIC_LIGHTS_INSET), - ); - }) - .ok(); -} - fn should_protect_window(app: &AppHandle, window_title: &str) -> bool { let matches = |list: &[WindowExclusion]| { list.iter() @@ -2778,73 +2427,6 @@ pub fn refresh_window_content_protection(app: AppHandle) -> Result<(), Stri Ok(()) } -// Credits: tauri-plugin-window-state -trait MonitorExt { - fn intersects( - &self, - position: PhysicalPosition, - size: PhysicalSize, - scale: f64, - ) -> bool; -} - -impl MonitorExt for Display { - fn intersects( - &self, - position: PhysicalPosition, - size: PhysicalSize, - _scale: f64, - ) -> bool { - #[cfg(target_os = "macos")] - { - let Some(bounds) = self.raw_handle().logical_bounds() else { - return false; - }; - - let left = (bounds.position().x() * _scale) as i32; - let right = left + (bounds.size().width() * _scale) as i32; - let top = (bounds.position().y() * _scale) as i32; - let bottom = top + (bounds.size().height() * _scale) as i32; - - [ - (position.x, position.y), - (position.x + size.width as i32, position.y), - (position.x, position.y + size.height as i32), - ( - position.x + size.width as i32, - position.y + size.height as i32, - ), - ] - .into_iter() - .any(|(x, y)| x >= left && x < right && y >= top && y < bottom) - } - - #[cfg(windows)] - { - let Some(bounds) = self.raw_handle().physical_bounds() else { - return false; - }; - - let left = bounds.position().x() as i32; - let right = left + bounds.size().width() as i32; - let top = bounds.position().y() as i32; - let bottom = top + bounds.size().height() as i32; - - [ - (position.x, position.y), - (position.x + size.width as i32, position.y), - (position.x, position.y + size.height as i32), - ( - position.x + size.width as i32, - position.y + size.height as i32, - ), - ] - .into_iter() - .any(|(x, y)| x >= left && x < right && y >= top && y < bottom) - } - } -} - #[specta::specta] #[tauri::command(async)] #[instrument(skip(_window))] diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 4d8febbeb97..a27fb0553e7 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -86,7 +86,13 @@ "ext": ["cap"], "name": "Cap Project", "mimeType": "application/x-cap-project", - "role": "Editor" + "role": "Editor", + "rank": "Owner", + "description": "Cap Project", + "exportedType": { + "identifier": "so.cap.desktop.project", + "conformsTo": ["com.apple.package"] + } } ] } diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index 7bb17821215..821c5787d8d 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -1,21 +1,36 @@ -import { Route, Router, useCurrentMatches } from "@solidjs/router"; -import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"; import { - getCurrentWebviewWindow, - type WebviewWindow, -} from "@tauri-apps/api/webviewWindow"; + Route, + Router, + useCurrentMatches, + useIsRouting, +} from "@solidjs/router"; +import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"; import { message } from "@tauri-apps/plugin-dialog"; -import { createEffect, lazy, onCleanup, onMount, Suspense } from "solid-js"; +import { + children, + createEffect, + lazy, + onCleanup, + onMount, + type ParentProps, + Suspense, +} from "solid-js"; import { Toaster } from "solid-toast"; import "@cap/ui-solid/main.css"; import "unfonts.css"; import "./styles/theme.css"; +import { createEventListener } from "@solid-primitives/event-listener"; +import { setTheme } from "@tauri-apps/api/app"; +import { + getCurrentWindow, + type Window as TauriWindow, +} from "@tauri-apps/api/window"; import { CapErrorBoundary } from "./components/CapErrorBoundary"; import { generalSettingsStore } from "./store"; import { initAnonymousUser } from "./utils/analytics"; -import { type AppTheme, commands } from "./utils/tauri"; +import type { Appearance } from "./utils/tauri"; import titlebar from "./utils/titlebar-state"; const WindowChromeLayout = lazy(() => import("./routes/(window-chrome)")); @@ -108,8 +123,7 @@ export default function App() { } function Inner() { - const currentWindow = getCurrentWebviewWindow(); - createThemeListener(currentWindow); + createThemeListener(); onMount(() => { initAnonymousUser(); @@ -137,24 +151,20 @@ function Inner() { /> { - const matches = useCurrentMatches(); - - onMount(() => { - for (const match of matches()) { - if (match.route.info?.AUTO_SHOW_WINDOW === false) return; - } - - if (location.pathname !== "/" && location.pathname !== "/camera") - currentWindow.show(); - }); - - return {props.children}; - }} + root={(props) => ( + + {props.children} + + + )} > - + @@ -188,7 +198,11 @@ function Inner() { - + @@ -203,6 +217,7 @@ function Inner() { { - update(generalSettings.data?.theme ?? null); - }); + createEffect(() => apply(settings.data?.appearance)); - onMount(async () => { - const unlisten = await currentWindow.onThemeChanged((_) => - update(generalSettings.data?.theme), - ); - onCleanup(() => unlisten?.()); + createEventListener(prefersDark, "change", () => { + if (settings.data?.appearance === "system") apply("system"); }); - function update(appTheme: AppTheme | null | undefined) { + function apply(appearance: Appearance | null | undefined) { if (location.pathname === "/camera") return; - - if (appTheme === undefined || appTheme === null) return; - - const isDark = - appTheme === "dark" || - (appTheme === "system" && - window.matchMedia("(prefers-color-scheme: dark)").matches); + if (appearance === undefined || appearance === null) return; try { - if (appTheme === "system") { - localStorage.removeItem("cap-theme"); - } else { - localStorage.setItem("cap-theme", appTheme); - } + if (appearance === "system") localStorage.removeItem("cap-theme"); + else localStorage.setItem("cap-theme", appearance); } catch {} - commands.setTheme(appTheme).then(() => { - document.documentElement.classList.toggle("dark", isDark); - }); + setTheme(appearance === "system" ? null : appearance).then(() => + document.documentElement.classList.toggle( + "dark", + appearance === "dark" || + (appearance === "system" && prefersDark.matches), + ), + ); } } + +let windowShown = false; + +function AutoRevealWindowOnReady() { + const matches = useCurrentMatches(); + const isRouting = useIsRouting(); + + createEffect(() => { + if (isRouting() || windowShown) return; + const shouldDefer = matches().some( + (match) => match.route.info?.autoShow === false, + ); + if (shouldDefer) return; + windowShown = true; + getCurrentWindow().show(); + }); + + return null; +} + +export function RevealWindowWithSuspense(props: ParentProps) { + const resolved = children(() => props.children); + const isRouting = useIsRouting(); + + createEffect(() => { + if (windowShown || !resolved() || isRouting()) return; + windowShown = true; + getCurrentWindow().show(); + }); + + return {resolved()}; +} diff --git a/apps/desktop/src/components/titlebar/Titlebar.tsx b/apps/desktop/src/components/titlebar/Titlebar.tsx index 4e89ac0883a..7f34d11ccfe 100644 --- a/apps/desktop/src/components/titlebar/Titlebar.tsx +++ b/apps/desktop/src/components/titlebar/Titlebar.tsx @@ -25,7 +25,6 @@ export default function Titlebar() { style={{ height: titlebarState.height, }} - data-tauri-drag-region > {left() ? ( <> @@ -55,7 +54,7 @@ export function WindowControls(props: ComponentProps<"div">) { /> -
+
); diff --git a/apps/desktop/src/entry-client.tsx b/apps/desktop/src/entry-client.tsx index a44f61bdc54..ab6d66eebb9 100644 --- a/apps/desktop/src/entry-client.tsx +++ b/apps/desktop/src/entry-client.tsx @@ -9,7 +9,6 @@ async function initApp() { } catch (error) { console.error("Failed to get OS type:", error); } - const app = document.getElementById("app"); if (!app) throw new Error("App root element not found"); diff --git a/apps/desktop/src/routes/(window-chrome).tsx b/apps/desktop/src/routes/(window-chrome).tsx index 43cb16a88dd..ec4445ee42c 100644 --- a/apps/desktop/src/routes/(window-chrome).tsx +++ b/apps/desktop/src/routes/(window-chrome).tsx @@ -45,16 +45,9 @@ export default function (props: RouteSectionProps) { window.removeEventListener("keydown", handleKeyDown); }); - const isMacOS = ostype() === "macos"; - return ( -
+
{/* breaks sometimes */} @@ -92,31 +85,18 @@ function Header() { "flex items-center min-w-0 w-full h-9 select-none shrink-0 bg-gray-2", isWindows ? "flex-row" : "flex-row-reverse", )} - data-tauri-drag-region + data-tauri-drag-region="deep" > {ctx.state()?.items} {isWindows && } - {isMacOS && ( - - )} + {isMacOS &&
}
); } function Inner(props: ParentProps) { - onMount(() => { - if (location.pathname !== "/") getCurrentWindow().show(); - }); - return ( -
+
{props.children}
); diff --git a/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx b/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx index 82ce03f14f0..d6f32ea01db 100644 --- a/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx +++ b/apps/desktop/src/routes/(window-chrome)/new-main/index.tsx @@ -100,6 +100,7 @@ import TargetDropdownButton from "./TargetDropdownButton"; import TargetMenuGrid from "./TargetMenuGrid"; import TargetTypeButton from "./TargetTypeButton"; import useRequestPermission from "./useRequestPermission"; +import { showCapDebugWindow } from "~/routes/debug"; const WINDOW_SIZE = { width: 330, height: 395 } as const; @@ -2420,11 +2421,11 @@ function Page() {
-
-
+
+
Settings}>
diff --git a/apps/desktop/src/routes/(window-chrome)/settings/general.tsx b/apps/desktop/src/routes/(window-chrome)/settings/general.tsx index d0345a90793..5eebb2c7660 100644 --- a/apps/desktop/src/routes/(window-chrome)/settings/general.tsx +++ b/apps/desktop/src/routes/(window-chrome)/settings/general.tsx @@ -33,7 +33,7 @@ import { type GeneralSettingsStore, } from "~/utils/general-settings"; import { - type AppTheme, + type Appearance, type CaptureWindow, commands, events, @@ -95,14 +95,14 @@ export default function GeneralSettings() { } function AppearanceSection(props: { - currentTheme: AppTheme; - onThemeChange: (theme: AppTheme) => void; + currentTheme: Appearance; + onThemeChange: (theme: Appearance) => void; }) { const options = [ { id: "system", name: "System" }, { id: "light", name: "Light" }, { id: "dark", name: "Dark" }, - ] satisfies { id: AppTheme; name: string }[]; + ] satisfies { id: Appearance; name: string }[]; const previews = { system: themePreviewAuto, @@ -399,10 +399,10 @@ function Inner(props: { initialStore: GeneralSettingsStore | null }) {
{ - setSettings("theme", newTheme); - generalSettingsStore.set({ theme: newTheme }); + setSettings("appearance", newTheme); + generalSettingsStore.set({ appearance: newTheme }); }} /> diff --git a/apps/desktop/src/routes/capture-area.tsx b/apps/desktop/src/routes/capture-area.tsx index 9f9cb76cd63..f0d8025ff58 100644 --- a/apps/desktop/src/routes/capture-area.tsx +++ b/apps/desktop/src/routes/capture-area.tsx @@ -222,6 +222,7 @@ export default function CaptureArea() { title="Aspect Ratio" class="group flex items-center justify-center size-10 text-gray-11 hover:bg-gray-5 active:bg-gray-6 rounded-full transition-colors duration-200 cursor-default" type="button" + onMouseDown={showCropOptionsMenu} onClick={showCropOptionsMenu} >
diff --git a/apps/desktop/src/routes/debug.tsx b/apps/desktop/src/routes/debug.tsx index 9598426a3f6..7cc4ed7f78b 100644 --- a/apps/desktop/src/routes/debug.tsx +++ b/apps/desktop/src/routes/debug.tsx @@ -1,6 +1,8 @@ import { useNavigate } from "@solidjs/router"; import { createQuery } from "@tanstack/solid-query"; import { getVersion } from "@tauri-apps/api/app"; +import { WebviewWindow } from "@tauri-apps/api/webviewWindow"; +import { Effect, getCurrentWindow, LogicalSize } from "@tauri-apps/api/window"; import * as dialog from "@tauri-apps/plugin-dialog"; import { check } from "@tauri-apps/plugin-updater"; import { createSignal, createUniqueId, For, onMount } from "solid-js"; @@ -11,10 +13,18 @@ export default function Debug() { const [version, setVersion] = createSignal(""); const [updateStatus, setUpdateStatus] = createSignal(""); const [isChecking, setIsChecking] = createSignal(false); + let mainContent: HTMLElement | undefined; onMount(async () => { const v = await getVersion(); setVersion(v); + + if (mainContent) { + const win = getCurrentWindow(); + const rect = mainContent.getBoundingClientRect(); + const size = new LogicalSize(rect.width, rect.height); + win.setSize(size); + } }); const checkForUpdates = async () => { @@ -57,17 +67,21 @@ export default function Debug() { const orderedFails = () => Object.entries(fails.data ?? {}); return ( -
-

Debug Windows

-
+
+

Debug Windows

+
-

Updates

-
+

Updates

+

Current version: v{version()}

-

Fail Points

+

Fail Points

    {(fail) => { @@ -136,3 +150,22 @@ export default function Debug() {
); } + +export async function showCapDebugWindow() { + const previous = await WebviewWindow.getByLabel("debug"); + if (previous) { + previous.setFocus(); + return; + } + + new WebviewWindow("debug", { + url: "/debug", + title: "Cap Debug", + visible: false, + titleBarStyle: "overlay", + transparent: true, + windowEffects: { + effects: [Effect.UnderWindowBackground, Effect.Mica], + }, + }); +} diff --git a/apps/desktop/src/routes/editor/Editor.tsx b/apps/desktop/src/routes/editor/Editor.tsx index 59927c4456f..14dc12d8f51 100644 --- a/apps/desktop/src/routes/editor/Editor.tsx +++ b/apps/desktop/src/routes/editor/Editor.tsx @@ -518,10 +518,7 @@ function Inner() { }>
-
+
diff --git a/apps/desktop/src/routes/editor/EditorErrorScreen.tsx b/apps/desktop/src/routes/editor/EditorErrorScreen.tsx index 04505a8b27b..cce3bfd1fa3 100644 --- a/apps/desktop/src/routes/editor/EditorErrorScreen.tsx +++ b/apps/desktop/src/routes/editor/EditorErrorScreen.tsx @@ -41,11 +41,11 @@ export function EditorErrorScreen(props: { return (
{isMac() &&
} -
+
{ostype() === "windows" && }
diff --git a/apps/desktop/src/routes/editor/ExportPage.tsx b/apps/desktop/src/routes/editor/ExportPage.tsx index 8c20a5f57b5..6938500d90b 100644 --- a/apps/desktop/src/routes/editor/ExportPage.tsx +++ b/apps/desktop/src/routes/editor/ExportPage.tsx @@ -727,14 +727,13 @@ export function ExportPage() { return (

Export

Back to Editor -
+
{ostype() === "windows" && }
diff --git a/apps/desktop/src/routes/editor/Header.tsx b/apps/desktop/src/routes/editor/Header.tsx index aedc91b03e6..918f3e87d2c 100644 --- a/apps/desktop/src/routes/editor/Header.tsx +++ b/apps/desktop/src/routes/editor/Header.tsx @@ -135,13 +135,10 @@ export function Header() { return (
-
+
{ostype() === "macos" &&
} { @@ -170,7 +167,7 @@ export function Header() { .cap
-
+
{ if (clearTimelineSelection()) return; @@ -189,16 +186,12 @@ export function Header() { />
-
+
} /> -
+
diff --git a/apps/desktop/src/routes/editor/editor-skeleton.tsx b/apps/desktop/src/routes/editor/editor-skeleton.tsx index a6b2c67fbd9..ac346315da4 100644 --- a/apps/desktop/src/routes/editor/editor-skeleton.tsx +++ b/apps/desktop/src/routes/editor/editor-skeleton.tsx @@ -25,32 +25,25 @@ function SkeletonButton(props: { class?: string; width?: string }) { function HeaderSkeleton() { return (
-
+
{ostype() === "macos" &&
} -
+
-
+
-
+
{ostype() === "windows" && }
@@ -221,7 +214,7 @@ export function EditorSkeleton() {
diff --git a/apps/desktop/src/routes/in-progress-recording.tsx b/apps/desktop/src/routes/in-progress-recording.tsx index 4fa431574c2..0180d361a6a 100644 --- a/apps/desktop/src/routes/in-progress-recording.tsx +++ b/apps/desktop/src/routes/in-progress-recording.tsx @@ -647,7 +647,10 @@ function InProgressRecordingInner() { }; return ( -
+
@@ -786,10 +789,7 @@ function InProgressRecordingInner() { ) ) : ( - + )}
@@ -896,10 +896,7 @@ function InProgressRecordingInner() {
-
+
diff --git a/apps/desktop/src/routes/mode-select.tsx b/apps/desktop/src/routes/mode-select.tsx index 669cb414dba..240cef1beb9 100644 --- a/apps/desktop/src/routes/mode-select.tsx +++ b/apps/desktop/src/routes/mode-select.tsx @@ -38,7 +38,7 @@ const ModeSelectWindow = () => { return (
{isWindows && ( @@ -57,7 +57,7 @@ const ModeSelectWindow = () => {

-
+
diff --git a/apps/desktop/src/routes/screenshot-editor/Editor.tsx b/apps/desktop/src/routes/screenshot-editor/Editor.tsx index 543d6e453e3..02368236e3d 100644 --- a/apps/desktop/src/routes/screenshot-editor/Editor.tsx +++ b/apps/desktop/src/routes/screenshot-editor/Editor.tsx @@ -370,6 +370,7 @@ function Dialogs() { variant="white" size="xs" class="flex items-center justify-center text-center rounded-full h-[2rem] w-[2rem] border focus:border-blue-9" + onMouseDown={showCropOptionsMenu} onClick={showCropOptionsMenu} >
diff --git a/apps/desktop/src/routes/screenshot-editor/Header.tsx b/apps/desktop/src/routes/screenshot-editor/Header.tsx index ea589cb9364..1dec8345255 100644 --- a/apps/desktop/src/routes/screenshot-editor/Header.tsx +++ b/apps/desktop/src/routes/screenshot-editor/Header.tsx @@ -94,8 +94,8 @@ export function Header() { return (
{ostype() === "macos" &&
} diff --git a/apps/desktop/src/routes/screenshot-editor/screenshot-editor-skeleton.tsx b/apps/desktop/src/routes/screenshot-editor/screenshot-editor-skeleton.tsx index 4682750f851..58d72ea29c0 100644 --- a/apps/desktop/src/routes/screenshot-editor/screenshot-editor-skeleton.tsx +++ b/apps/desktop/src/routes/screenshot-editor/screenshot-editor-skeleton.tsx @@ -21,8 +21,8 @@ function SkeletonButton(props: { class?: string; width?: string }) { function HeaderSkeleton() { return (
{ostype() === "macos" &&
} diff --git a/apps/desktop/src/utils/tauri.ts b/apps/desktop/src/utils/tauri.ts index b57a5709925..a753ac12082 100644 --- a/apps/desktop/src/utils/tauri.ts +++ b/apps/desktop/src/utils/tauri.ts @@ -5,6 +5,9 @@ export const commands = { +async log(string: string) : Promise { + await TAURI_INVOKE("log", { string }); +}, async setMicInput(label: string | null) : Promise { return await TAURI_INVOKE("set_mic_input", { label }); }, @@ -248,16 +251,10 @@ async seekTo(frameNumber: number) : Promise { async getDisplayFrameForCropping(fps: number) : Promise { return await TAURI_INVOKE("get_display_frame_for_cropping", { fps }); }, -async positionTrafficLights(controlsInset: [number, number] | null) : Promise { - await TAURI_INVOKE("position_traffic_lights", { controlsInset }); -}, -async setTheme(theme: AppTheme) : Promise { - await TAURI_INVOKE("set_theme", { theme }); -}, async globalMessageDialog(message: string) : Promise { await TAURI_INVOKE("global_message_dialog", { message }); }, -async showWindow(window: ShowCapWindow) : Promise { +async showWindow(window: CapWindow) : Promise { return await TAURI_INVOKE("show_window", { window }); }, async writeClipboardString(text: string) : Promise { @@ -450,10 +447,9 @@ videoImportProgress: "video-import-progress" /** user-defined types **/ -export type AllGpusInfo = { gpus: GpuInfoDiag[]; primaryGpuIndex: number | null; isMultiGpuSystem: boolean; hasDiscreteGpu: boolean } export type Annotation = { id: string; type: AnnotationType; x: number; y: number; width: number; height: number; strokeColor: string; strokeWidth: number; fillColor: string; opacity: number; rotation: number; text: string | null; maskType?: MaskType | null; maskLevel?: number | null } export type AnnotationType = "arrow" | "circle" | "rectangle" | "text" | "mask" -export type AppTheme = "system" | "light" | "dark" +export type Appearance = "system" | "light" | "dark" export type AspectRatio = "wide" | "vertical" | "square" | "classic" | "tall" export type Audio = { duration: number; sample_rate: number; channels: number; start_time: number } export type AudioConfiguration = { mute: boolean; improve: boolean; micVolumeDb: number; micStereoMode: StereoMode; systemVolumeDb: number } @@ -477,6 +473,7 @@ export type CameraShape = "square" | "source" export type CameraWithFormats = { deviceId: string; displayName: string; modelId: string | null; formats: CameraFormatInfo[]; bestFormat: CameraFormatInfo | null } export type CameraXPosition = "left" | "center" | "right" export type CameraYPosition = "top" | "bottom" +export type CapWindow = { Main: { init_target_mode: RecordingTargetMode | null } } | { Settings: { page: string | null } } | { Editor: { project_path: string } } | "RecordingsOverlay" | { WindowCaptureOccluder: { screen_id: DisplayId } } | { TargetSelectOverlay: { display_id: DisplayId; target_mode: RecordingTargetMode | null } } | { CaptureArea: { screen_id: DisplayId } } | { Camera: { centered: boolean } } | { InProgressRecording: { countdown: number | null; capture_target?: ScreenCaptureTarget | null } } | "Upgrade" | "ModeSelect" | { ScreenshotEditor: { path: string } } | "Onboarding" export type CaptionData = { segments: CaptionSegment[]; settings: CaptionSettings | null } export type CaptionSegment = { id: string; start: number; end: number; text: string; words?: CaptionWord[] } export type CaptionSettings = { enabled: boolean; font: string; size: number; color: string; backgroundColor: string; backgroundOpacity: number; position: string; italic: boolean; fontWeight: number; outline: boolean; outlineColor: string; exportWithSubtitles: boolean; highlightColor: string; fadeDuration: number; lingerDuration: number; wordTransitionDuration: number; activeWordHighlight: boolean } @@ -516,7 +513,7 @@ export type ExportSettings = ({ format: "Mp4" } & Mp4ExportSettings) | ({ format export type FileType = "recording" | "screenshot" export type Flags = { captions: boolean } export type FramesRendered = { renderedCount: number; totalFrames: number; type: "FramesRendered" } -export type GeneralSettingsStore = { instanceId?: string; uploadIndividualFiles?: boolean; hideDockIcon?: boolean; autoCreateShareableLink?: boolean; enableNotifications?: boolean; disableAutoOpenLinks?: boolean; hasCompletedStartup?: boolean; theme?: AppTheme; commercialLicense?: CommercialLicense | null; lastVersion?: string | null; windowTransparency?: boolean; postStudioRecordingBehaviour?: PostStudioRecordingBehaviour; mainWindowRecordingStartBehaviour?: MainWindowRecordingStartBehaviour; custom_cursor_capture2?: boolean; serverUrl?: string; recordingCountdown?: number | null; enableNativeCameraPreview: boolean; autoZoomOnClicks?: boolean; captureKeyboardEvents?: boolean; postDeletionBehaviour?: PostDeletionBehaviour; excludedWindows?: WindowExclusion[]; deleteInstantRecordingsAfterUpload?: boolean; instantModeMaxResolution?: number; defaultProjectNameTemplate?: string | null; crashRecoveryRecording?: boolean; maxFps?: number; transcriptionHints?: string[]; editorPreviewQuality?: EditorPreviewQuality; studioRecordingQuality?: StudioRecordingQuality; mainWindowPosition?: WindowPosition | null; cameraWindowPosition?: WindowPosition | null; cameraWindowPositionsByMonitorName?: { [key in string]: WindowPosition }; hasCompletedOnboarding?: boolean; enableTelemetry?: boolean; outOfProcessMuxer?: boolean } +export type GeneralSettingsStore = { instanceId?: string; uploadIndividualFiles?: boolean; hideDockIcon?: boolean; autoCreateShareableLink?: boolean; enableNotifications?: boolean; disableAutoOpenLinks?: boolean; hasCompletedStartup?: boolean; appearance?: Appearance; commercialLicense?: CommercialLicense | null; lastVersion?: string | null; windowTransparency?: boolean; postStudioRecordingBehaviour?: PostStudioRecordingBehaviour; mainWindowRecordingStartBehaviour?: MainWindowRecordingStartBehaviour; custom_cursor_capture2?: boolean; serverUrl?: string; recordingCountdown?: number | null; enableNativeCameraPreview: boolean; autoZoomOnClicks?: boolean; captureKeyboardEvents?: boolean; postDeletionBehaviour?: PostDeletionBehaviour; excludedWindows?: WindowExclusion[]; deleteInstantRecordingsAfterUpload?: boolean; instantModeMaxResolution?: number; defaultProjectNameTemplate?: string | null; crashRecoveryRecording?: boolean; maxFps?: number; transcriptionHints?: string[]; editorPreviewQuality?: EditorPreviewQuality; studioRecordingQuality?: StudioRecordingQuality; cameraWindowPosition?: WindowPosition | null; cameraWindowPositionsByMonitorName?: { [key in string]: WindowPosition }; hasCompletedOnboarding?: boolean; enableTelemetry?: boolean; outOfProcessMuxer?: boolean } export type GifExportSettings = { fps: number; resolution_base: XY; quality: GifQuality | null } export type GifQuality = { /** @@ -528,7 +525,6 @@ quality: number | null; */ fast: boolean | null } export type GlideDirection = "none" | "left" | "right" | "up" | "down" -export type GpuInfoDiag = { vendor: string; description: string; dedicatedVideoMemoryMb: number; adapterIndex: number; isSoftwareAdapter: boolean; isBasicRenderDriver: boolean; supportsHardwareEncoding: boolean } export type HapticPattern = "alignment" | "levelChange" | "generic" export type HapticPerformanceTime = "default" | "now" | "drawCompleted" export type Hotkey = { code: string; meta: boolean; ctrl: boolean; alt: boolean; shift: boolean } @@ -546,6 +542,7 @@ export type KeyboardTrackSegment = { id: string; start: number; end: number; dis export type LogicalBounds = { position: LogicalPosition; size: LogicalSize } export type LogicalPosition = { x: number; y: number } export type LogicalSize = { width: number; height: number } +export type MacOSVersionInfo = { major: number; minor: number; patch: number; displayName: string; buildNumber: string; isAppleSilicon: boolean } export type MainWindowRecordingStartBehaviour = "close" | "minimise" export type MaskKeyframes = { position?: MaskVectorKeyframe[]; size?: MaskVectorKeyframe[]; intensity?: MaskScalarKeyframe[] } export type MaskKind = "sensitive" | "highlight" @@ -593,7 +590,6 @@ export type RecordingStatus = "pending" | "recording" export type RecordingStopped = null export type RecordingTargetMode = "display" | "window" | "area" | "camera" export type RenderFrameEvent = { frame_number: number; fps: number; resolution_base: XY } -export type RenderingStatus = { isUsingSoftwareRendering: boolean; isUsingBasicRenderDriver: boolean; hardwareEncodingAvailable: boolean; warningMessage: string | null } export type RequestOpenRecordingPicker = { target_mode: RecordingTargetMode | null } export type RequestOpenSettings = { page: string } export type RequestScreenCapturePrewarm = { force?: boolean } @@ -614,14 +610,13 @@ export type SerializedScreenshotEditorInstance = { framesSocketUrl: string; path export type SetCaptureAreaPending = boolean export type ShadowConfiguration = { size: number; opacity: number; blur: number } export type SharingMeta = { id: string; link: string } -export type ShowCapWindow = { Main: { init_target_mode: RecordingTargetMode | null } } | { Settings: { page: string | null } } | { Editor: { project_path: string } } | "RecordingsOverlay" | { WindowCaptureOccluder: { screen_id: DisplayId } } | { TargetSelectOverlay: { display_id: DisplayId; target_mode: RecordingTargetMode | null } } | { CaptureArea: { screen_id: DisplayId } } | { Camera: { centered: boolean } } | { InProgressRecording: { countdown: number | null; capture_target?: ScreenCaptureTarget | null } } | "Upgrade" | "ModeSelect" | { ScreenshotEditor: { path: string } } | "Onboarding" export type SingleSegment = { display: VideoMeta; camera?: VideoMeta | null; audio?: AudioMeta | null; cursor?: string | null } export type StartRecordingInputs = { capture_target: ScreenCaptureTarget; capture_system_audio?: boolean; mode: RecordingMode; organization_id?: string | null } export type StereoMode = "stereo" | "monoL" | "monoR" export type StudioRecordingMeta = { segment: SingleSegment } | { inner: MultipleSegments } export type StudioRecordingQuality = "compatibility" | "balanced" | "ultra" export type StudioRecordingStatus = { status: "InProgress" } | { status: "NeedsRemux" } | { status: "Failed"; error: string } | { status: "Complete" } -export type SystemDiagnostics = { windowsVersion: WindowsVersionInfo | null; gpuInfo: GpuInfoDiag | null; allGpus: AllGpusInfo | null; renderingStatus: RenderingStatus; availableEncoders: string[]; graphicsCaptureSupported: boolean; d3D11VideoProcessorAvailable: boolean } +export type SystemDiagnostics = { macosVersion: MacOSVersionInfo | null; availableEncoders: string[]; screenCaptureSupported: boolean; metalSupported: boolean; gpuName: string | null } export type TargetUnderCursor = { display_id: DisplayId | null; window: WindowUnderCursor | null } export type TextSegment = { start: number; end: number; track?: number; enabled?: boolean; content?: string; center?: XY; size?: XY; fontFamily?: string; fontSize?: number; fontWeight?: number; italic?: boolean; color?: string; fadeDuration?: number } export type TimelineConfiguration = { segments: TimelineSegment[]; zoomSegments: ZoomSegment[]; sceneSegments?: SceneSegment[]; maskSegments?: MaskSegment[]; textSegments?: TextSegment[]; captionSegments?: CaptionTrackSegment[]; keyboardSegments?: KeyboardTrackSegment[] } @@ -641,7 +636,6 @@ export type WindowExclusion = { bundleIdentifier?: string | null; ownerName?: st export type WindowId = string export type WindowPosition = { x: number; y: number; displayId?: DisplayId | null } export type WindowUnderCursor = { id: WindowId; app_name: string; bounds: LogicalBounds } -export type WindowsVersionInfo = { major: number; minor: number; build: number; displayName: string; meetsRequirements: boolean; isWindows11: boolean } export type XY = { x: T; y: T } export type ZoomMode = "auto" | { manual: { x: number; y: number } } export type ZoomSegment = { start: number; end: number; amount: number; mode: ZoomMode; glideDirection?: GlideDirection; glideSpeed?: number; instantAnimation?: boolean; edgeSnapRatio?: number } diff --git a/apps/src/utils/tauri.ts b/apps/src/utils/tauri.ts index e5c2cbb60fd..2e64807d808 100644 --- a/apps/src/utils/tauri.ts +++ b/apps/src/utils/tauri.ts @@ -287,7 +287,7 @@ export const commands = { async globalMessageDialog(message: string): Promise { await TAURI_INVOKE("global_message_dialog", { message }); }, - async showWindow(window: ShowCapWindow): Promise { + async showWindow(window: CapWindow): Promise { return await TAURI_INVOKE("show_window", { window }); }, async writeClipboardString(text: string): Promise { @@ -1089,7 +1089,7 @@ export type ShadowConfiguration = { blur: number; }; export type SharingMeta = { id: string; link: string }; -export type ShowCapWindow = +export type CapWindow = | "Setup" | { Main: { init_target_mode: RecordingTargetMode | null } } | { Settings: { page: string | null } } diff --git a/crates/cursor-info/Cargo.toml b/crates/cursor-info/Cargo.toml index e86871e9ecb..c5524c06707 100644 --- a/crates/cursor-info/Cargo.toml +++ b/crates/cursor-info/Cargo.toml @@ -22,7 +22,7 @@ sha2 = "0.10" [target.'cfg(target_os = "macos")'.dev-dependencies] objc2 = "0.6" -objc2-app-kit = { version = "0.3.0", features = ["NSCursor", "NSApplication"] } +objc2-app-kit = { version = "0.3.2", features = ["NSCursor", "NSApplication"] } [lints] diff --git a/crates/recording/Cargo.toml b/crates/recording/Cargo.toml index f3808bfec48..d54fc2c3486 100644 --- a/crates/recording/Cargo.toml +++ b/crates/recording/Cargo.toml @@ -69,7 +69,7 @@ screencapturekit = "0.3.5" cocoa = "0.26.0" objc = "0.2.7" objc2 = "0.6" -objc2-app-kit = "0.3.1" +objc2-app-kit = "0.3.2" core-graphics = "0.24.0" core-foundation = "0.10.0" foreign-types-shared = "0.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce77b06b4f8..1a787384a24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -120,8 +120,8 @@ importers: specifier: ^5.51.21 version: 5.75.4(solid-js@1.9.6) '@tauri-apps/api': - specifier: 2.8.0 - version: 2.8.0 + specifier: 2.11.0 + version: 2.11.0 '@tauri-apps/plugin-clipboard-manager': specifier: ^2.3.0 version: 2.3.0 @@ -220,8 +220,8 @@ importers: specifier: ^2.2.239 version: 2.2.336 '@tauri-apps/cli': - specifier: '>=2.1.0' - version: 2.5.0 + specifier: '>=2.11.0' + version: 2.11.0 '@total-typescript/ts-reset': specifier: ^0.6.1 version: 0.6.1 @@ -665,7 +665,7 @@ importers: version: 4.24.11(next@16.2.1(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(nodemailer@6.10.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next-mdx-remote: specifier: ^6.0.0 - version: 6.0.0(@types/react@19.2.14)(acorn@8.15.0)(react@19.2.4) + version: 6.0.0(@types/react@19.2.14)(acorn@8.16.0)(react@19.2.4) posthog-js: specifier: ^1.215.3 version: 1.240.0 @@ -7467,8 +7467,8 @@ packages: resolution: {integrity: sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==} engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} - '@tauri-apps/api@2.8.0': - resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} + '@tauri-apps/api@2.11.0': + resolution: {integrity: sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==} '@tauri-apps/cli-darwin-arm64@1.6.3': resolution: {integrity: sha512-fQN6IYSL8bG4NvkdKE4sAGF4dF/QqqQq4hOAU+t8ksOzHJr0hUlJYfncFeJYutr/MMkdF7hYKadSb0j5EE9r0A==} @@ -7476,8 +7476,8 @@ packages: cpu: [arm64] os: [darwin] - '@tauri-apps/cli-darwin-arm64@2.5.0': - resolution: {integrity: sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ==} + '@tauri-apps/cli-darwin-arm64@2.11.0': + resolution: {integrity: sha512-UfMeDNlgIP252rm/KSTuu8yHatPua5TjtUEUf+jyIzVwBNcIl7Ywkdpfj+e5jVVg3EfCTp+4gwuL1dNpgF8clg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] @@ -7488,8 +7488,8 @@ packages: cpu: [x64] os: [darwin] - '@tauri-apps/cli-darwin-x64@2.5.0': - resolution: {integrity: sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw==} + '@tauri-apps/cli-darwin-x64@2.11.0': + resolution: {integrity: sha512-lY1+aPlgyMN7vgjtCdQ3+WODfZkebAcxnrCrO0HjqDpKSXieDkrJbimqeaoM4RwhTSrCLRHfVYiYrfE5E131tg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] @@ -7500,8 +7500,8 @@ packages: cpu: [arm] os: [linux] - '@tauri-apps/cli-linux-arm-gnueabihf@2.5.0': - resolution: {integrity: sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg==} + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.0': + resolution: {integrity: sha512-5uCP0AusgN3NrKC8EpkuJwjek1k8pEffBdugJSpXPey/QGbPEb8vZ542n/giJ2mZPjMSllDkdhG2QIDpBY4PpQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] @@ -7512,8 +7512,8 @@ packages: cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-gnu@2.5.0': - resolution: {integrity: sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ==} + '@tauri-apps/cli-linux-arm64-gnu@2.11.0': + resolution: {integrity: sha512-loDPqtRHMSbIcrH2VBd4GgHoQlF7jJnrZj7MxA2lj1cixS/jEgMAPFqj83U6Wvjete4HfYplbE/gCpSFifA9jw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -7524,14 +7524,14 @@ packages: cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-arm64-musl@2.5.0': - resolution: {integrity: sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q==} + '@tauri-apps/cli-linux-arm64-musl@2.11.0': + resolution: {integrity: sha512-DtSE8ZBlB9H+L+eHkfZ3myt00EVEyAB3e41juEHoE2qT88fgVlJvyrwa9SZYc/xTwCS9TnmK+R84tpg+ZsAg7Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tauri-apps/cli-linux-riscv64-gnu@2.5.0': - resolution: {integrity: sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA==} + '@tauri-apps/cli-linux-riscv64-gnu@2.11.0': + resolution: {integrity: sha512-5QdgS4LD+kntClI1aj2JmwjW38LosNXxwCe8viIHEwqYIWuMPdNEIau6/cLogI38Yzx9DnfCPRfEWLyI+5li8Q==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] @@ -7542,8 +7542,8 @@ packages: cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-gnu@2.5.0': - resolution: {integrity: sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w==} + '@tauri-apps/cli-linux-x64-gnu@2.11.0': + resolution: {integrity: sha512-5UynPXo3Zq9khjVdAbD+YogeLltdVUeOah2ioSIM3tu6H7wY9vMy6rgGJhv9r5R8ZXmk9GttMippdqYJWrnLnA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -7554,8 +7554,8 @@ packages: cpu: [x64] os: [linux] - '@tauri-apps/cli-linux-x64-musl@2.5.0': - resolution: {integrity: sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ==} + '@tauri-apps/cli-linux-x64-musl@2.11.0': + resolution: {integrity: sha512-CNz7fHbApz1Zyhhq73jtGn9JqgNEV/lIWnTnUo6h6ujw+mHsTmkLszvJSM8W6JBaDjNpTTFr/RSNoVL5FMwcTg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -7566,8 +7566,8 @@ packages: cpu: [arm64] os: [win32] - '@tauri-apps/cli-win32-arm64-msvc@2.5.0': - resolution: {integrity: sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q==} + '@tauri-apps/cli-win32-arm64-msvc@2.11.0': + resolution: {integrity: sha512-K+br+VXZ+Xx0n/9FdWohpW5Ugq+2FQUpJScqcPl1hTxXfh3fgjYgt4qA2NgrjlJo+zZPNrmUMl+NLvm0ufEqBQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] @@ -7578,8 +7578,8 @@ packages: cpu: [ia32] os: [win32] - '@tauri-apps/cli-win32-ia32-msvc@2.5.0': - resolution: {integrity: sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg==} + '@tauri-apps/cli-win32-ia32-msvc@2.11.0': + resolution: {integrity: sha512-OFV+s3MLZnd75zl0ZAFU5riMpGK4waUEA8ZDuijDsnkU0btz/gHhqh5jVlOn8thyvgdtT3Xyoxqo099MMifH3g==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] @@ -7590,8 +7590,8 @@ packages: cpu: [x64] os: [win32] - '@tauri-apps/cli-win32-x64-msvc@2.5.0': - resolution: {integrity: sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ==} + '@tauri-apps/cli-win32-x64-msvc@2.11.0': + resolution: {integrity: sha512-AeDTWBd2cOZ6TX133BWsoo+LutG9o0JRcgjMsIfLE13ZugpgCMv/2dJbUiBGeRvbPOGin5A3aYmsArPVV6ZSHQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -7601,8 +7601,8 @@ packages: engines: {node: '>= 10'} hasBin: true - '@tauri-apps/cli@2.5.0': - resolution: {integrity: sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg==} + '@tauri-apps/cli@2.11.0': + resolution: {integrity: sha512-W5Wbuqsb2pHFPTj4TaRNKTj5rwXhDShPiLSY9T18y4ouSR/NNCptAEFxFsBtyNRgL6Vs1a/q9LzfqqYzEwC+Jw==} engines: {node: '>= 10'} hasBin: true @@ -19114,7 +19114,7 @@ snapshots: '@planetscale/database': 1.19.0 mysql2: 3.15.2 - '@mdx-js/mdx@3.1.0(acorn@8.15.0)': + '@mdx-js/mdx@3.1.0(acorn@8.16.0)': dependencies: '@types/estree': 1.0.8 '@types/estree-jsx': 1.0.5 @@ -19128,7 +19128,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 markdown-extensions: 2.0.0 recma-build-jsx: 1.0.0 - recma-jsx: 1.0.0(acorn@8.15.0) + recma-jsx: 1.0.0(acorn@8.16.0) recma-stringify: 1.0.0 rehype-recma: 1.0.0 remark-mdx: 3.1.0 @@ -23128,69 +23128,69 @@ snapshots: '@tauri-apps/api@1.6.0': {} - '@tauri-apps/api@2.8.0': {} + '@tauri-apps/api@2.11.0': {} '@tauri-apps/cli-darwin-arm64@1.6.3': optional: true - '@tauri-apps/cli-darwin-arm64@2.5.0': + '@tauri-apps/cli-darwin-arm64@2.11.0': optional: true '@tauri-apps/cli-darwin-x64@1.6.3': optional: true - '@tauri-apps/cli-darwin-x64@2.5.0': + '@tauri-apps/cli-darwin-x64@2.11.0': optional: true '@tauri-apps/cli-linux-arm-gnueabihf@1.6.3': optional: true - '@tauri-apps/cli-linux-arm-gnueabihf@2.5.0': + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.0': optional: true '@tauri-apps/cli-linux-arm64-gnu@1.6.3': optional: true - '@tauri-apps/cli-linux-arm64-gnu@2.5.0': + '@tauri-apps/cli-linux-arm64-gnu@2.11.0': optional: true '@tauri-apps/cli-linux-arm64-musl@1.6.3': optional: true - '@tauri-apps/cli-linux-arm64-musl@2.5.0': + '@tauri-apps/cli-linux-arm64-musl@2.11.0': optional: true - '@tauri-apps/cli-linux-riscv64-gnu@2.5.0': + '@tauri-apps/cli-linux-riscv64-gnu@2.11.0': optional: true '@tauri-apps/cli-linux-x64-gnu@1.6.3': optional: true - '@tauri-apps/cli-linux-x64-gnu@2.5.0': + '@tauri-apps/cli-linux-x64-gnu@2.11.0': optional: true '@tauri-apps/cli-linux-x64-musl@1.6.3': optional: true - '@tauri-apps/cli-linux-x64-musl@2.5.0': + '@tauri-apps/cli-linux-x64-musl@2.11.0': optional: true '@tauri-apps/cli-win32-arm64-msvc@1.6.3': optional: true - '@tauri-apps/cli-win32-arm64-msvc@2.5.0': + '@tauri-apps/cli-win32-arm64-msvc@2.11.0': optional: true '@tauri-apps/cli-win32-ia32-msvc@1.6.3': optional: true - '@tauri-apps/cli-win32-ia32-msvc@2.5.0': + '@tauri-apps/cli-win32-ia32-msvc@2.11.0': optional: true '@tauri-apps/cli-win32-x64-msvc@1.6.3': optional: true - '@tauri-apps/cli-win32-x64-msvc@2.5.0': + '@tauri-apps/cli-win32-x64-msvc@2.11.0': optional: true '@tauri-apps/cli@1.6.3': @@ -23208,67 +23208,67 @@ snapshots: '@tauri-apps/cli-win32-ia32-msvc': 1.6.3 '@tauri-apps/cli-win32-x64-msvc': 1.6.3 - '@tauri-apps/cli@2.5.0': + '@tauri-apps/cli@2.11.0': optionalDependencies: - '@tauri-apps/cli-darwin-arm64': 2.5.0 - '@tauri-apps/cli-darwin-x64': 2.5.0 - '@tauri-apps/cli-linux-arm-gnueabihf': 2.5.0 - '@tauri-apps/cli-linux-arm64-gnu': 2.5.0 - '@tauri-apps/cli-linux-arm64-musl': 2.5.0 - '@tauri-apps/cli-linux-riscv64-gnu': 2.5.0 - '@tauri-apps/cli-linux-x64-gnu': 2.5.0 - '@tauri-apps/cli-linux-x64-musl': 2.5.0 - '@tauri-apps/cli-win32-arm64-msvc': 2.5.0 - '@tauri-apps/cli-win32-ia32-msvc': 2.5.0 - '@tauri-apps/cli-win32-x64-msvc': 2.5.0 + '@tauri-apps/cli-darwin-arm64': 2.11.0 + '@tauri-apps/cli-darwin-x64': 2.11.0 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.11.0 + '@tauri-apps/cli-linux-arm64-gnu': 2.11.0 + '@tauri-apps/cli-linux-arm64-musl': 2.11.0 + '@tauri-apps/cli-linux-riscv64-gnu': 2.11.0 + '@tauri-apps/cli-linux-x64-gnu': 2.11.0 + '@tauri-apps/cli-linux-x64-musl': 2.11.0 + '@tauri-apps/cli-win32-arm64-msvc': 2.11.0 + '@tauri-apps/cli-win32-ia32-msvc': 2.11.0 + '@tauri-apps/cli-win32-x64-msvc': 2.11.0 '@tauri-apps/plugin-clipboard-manager@2.3.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-deep-link@2.4.1': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-dialog@2.4.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-fs@2.4.1': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-http@2.5.1': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-notification@2.3.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-opener@2.5.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-os@2.3.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-process@2.3.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-shell@2.3.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-store@2.4.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@tauri-apps/plugin-updater@2.9.0': dependencies: - '@tauri-apps/api': 2.8.0 + '@tauri-apps/api': 2.11.0 '@testing-library/dom@10.4.0': dependencies: @@ -23950,8 +23950,8 @@ snapshots: dependencies: '@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13) '@rollup/pluginutils': 5.1.4(rollup@4.40.2) - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) async-sema: 3.1.1 bindings: 1.5.0 estree-walker: 2.0.2 @@ -24707,6 +24707,10 @@ snapshots: dependencies: acorn: 8.15.0 + acorn-import-attributes@1.9.5(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-import-phases@1.0.4(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -24724,6 +24728,10 @@ snapshots: dependencies: acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-loose@8.5.0: dependencies: acorn: 8.15.0 @@ -26437,7 +26445,7 @@ snapshots: esast-util-from-js@2.0.1: dependencies: '@types/estree-jsx': 1.0.5 - acorn: 8.15.0 + acorn: 8.16.0 esast-util-from-estree: 2.0.0 vfile-message: 4.0.2 @@ -29290,8 +29298,8 @@ snapshots: micromark-extension-mdxjs@3.0.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) micromark-extension-mdx-expression: 3.0.1 micromark-extension-mdx-jsx: 3.0.2 micromark-extension-mdx-md: 2.0.0 @@ -29745,10 +29753,10 @@ snapshots: optionalDependencies: nodemailer: 6.10.1 - next-mdx-remote@6.0.0(@types/react@19.2.14)(acorn@8.15.0)(react@19.2.4): + next-mdx-remote@6.0.0(@types/react@19.2.14)(acorn@8.16.0)(react@19.2.4): dependencies: '@babel/code-frame': 7.27.1 - '@mdx-js/mdx': 3.1.0(acorn@8.15.0) + '@mdx-js/mdx': 3.1.0(acorn@8.16.0) '@mdx-js/react': 3.1.0(@types/react@19.2.14)(react@19.2.4) react: 19.2.4 unist-util-remove: 4.0.0 @@ -31097,9 +31105,9 @@ snapshots: estree-util-build-jsx: 3.0.1 vfile: 6.0.3 - recma-jsx@1.0.0(acorn@8.15.0): + recma-jsx@1.0.0(acorn@8.16.0): dependencies: - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn-jsx: 5.3.2(acorn@8.16.0) estree-util-to-js: 2.0.0 recma-parse: 1.0.0 recma-stringify: 1.0.0 @@ -33111,7 +33119,7 @@ snapshots: unplugin@2.3.10: dependencies: '@jridgewell/remapping': 2.3.5 - acorn: 8.15.0 + acorn: 8.16.0 picomatch: 4.0.3 webpack-virtual-modules: 0.6.2