diff --git a/package.json b/package.json index 6f6136d5..7db37c95 100644 --- a/package.json +++ b/package.json @@ -11,16 +11,17 @@ }, "dependencies": { "@headlessui/react": "^2.1.10", - "@react-oauth/google": "^0.12.1", "@tauri-apps/api": "^2.2.0", "@tauri-apps/plugin-autostart": "~2", "@tauri-apps/plugin-deep-link": "^2.2.0", + "@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-global-shortcut": "~2.0.0", "@tauri-apps/plugin-http": "~2.0.1", "@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-shell": ">=2.0.0", "@tauri-apps/plugin-websocket": "~2", "@tauri-apps/plugin-window": "2.0.0-alpha.1", + "@tauri-apps/plugin-updater": "^2.3.0", "ahooks": "^3.8.4", "axios": "^1.7.7", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f689742..2a7b02e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,6 @@ importers: '@headlessui/react': specifier: ^2.1.10 version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-oauth/google': - specifier: ^0.12.1 - version: 0.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tauri-apps/api': specifier: ^2.2.0 version: 2.2.0 @@ -23,6 +20,9 @@ importers: '@tauri-apps/plugin-deep-link': specifier: ^2.2.0 version: 2.2.0 + '@tauri-apps/plugin-dialog': + specifier: ^2.2.0 + version: 2.2.0 '@tauri-apps/plugin-global-shortcut': specifier: ~2.0.0 version: 2.0.0 @@ -35,6 +35,9 @@ importers: '@tauri-apps/plugin-shell': specifier: '>=2.0.0' version: 2.0.0 + '@tauri-apps/plugin-updater': + specifier: ^2.3.0 + version: 2.5.0 '@tauri-apps/plugin-websocket': specifier: ~2 version: 2.0.0 @@ -513,12 +516,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 - '@react-oauth/google@0.12.1': - resolution: {integrity: sha512-qagsy22t+7UdkYAiT5ZhfM4StXi9PPNvw0zuwNmabrWyMKddczMtBIOARflbaIj+wHiQjnMAsZmzsUYuXeyoSg==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@react-stately/utils@3.10.4': resolution: {integrity: sha512-gBEQEIMRh5f60KCm7QKQ2WfvhB2gLUr9b72sqUdIZ2EG+xuPgaIlCBeSicvjmjBvYZwOjoOEnmIkcx2GHp/HWw==} peerDependencies: @@ -557,55 +554,46 @@ packages: resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.24.0': resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.24.0': resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.24.0': resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.24.0': resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.24.0': resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.24.0': resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.24.0': resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.24.0': resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} @@ -664,28 +652,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@tauri-apps/cli-linux-arm64-musl@2.2.7': resolution: {integrity: sha512-+8HZ+txff/Y3YjAh80XcLXcX8kpGXVdr1P8AfjLHxHdS6QD4Md+acSxGTTNbplmHuBaSHJvuTvZf9tU1eDCTDg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@tauri-apps/cli-linux-x64-gnu@2.2.7': resolution: {integrity: sha512-ahlSnuCnUntblp9dG7/w5ZWZOdzRFi3zl0oScgt7GF4KNAOEa7duADsxPA4/FT2hLRa0SvpqtD4IYFvCxoVv3Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@tauri-apps/cli-linux-x64-musl@2.2.7': resolution: {integrity: sha512-+qKAWnJRSX+pjjRbKAQgTdFY8ecdcu8UdJ69i7wn3ZcRn2nMMzOO2LOMOTQV42B7/Q64D1pIpmZj9yblTMvadA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@tauri-apps/cli-win32-arm64-msvc@2.2.7': resolution: {integrity: sha512-aa86nRnrwT04u9D9fhf5JVssuAZlUCCc8AjqQjqODQjMd4BMA2+d4K9qBMpEG/1kVh95vZaNsLogjEaqSTTw4A==} @@ -716,6 +700,9 @@ packages: '@tauri-apps/plugin-deep-link@2.2.0': resolution: {integrity: sha512-H6mkxr2KZ3XJcKL44tiq6cOjCw9DL8OgU1xjn3j26Qsn+H/roPFiyhR7CHuB8Ar+sQFj4YVlfmJwtBajK2FETQ==} + '@tauri-apps/plugin-dialog@2.2.0': + resolution: {integrity: sha512-6bLkYK68zyK31418AK5fNccCdVuRnNpbxquCl8IqgFByOgWFivbiIlvb79wpSXi0O+8k8RCSsIpOquebusRVSg==} + '@tauri-apps/plugin-global-shortcut@2.0.0': resolution: {integrity: sha512-pnB4CUwFVjg4twtBSxoLJ4uLFTYxsvOdC1zIbG581pYzhYatOl6mjB+ijD5SSXgiS/jNoqMcfkOF9PWAisurew==} @@ -728,6 +715,9 @@ packages: '@tauri-apps/plugin-shell@2.0.0': resolution: {integrity: sha512-OpW2+ycgJLrEoZityWeWYk+6ZWP9VyiAfbO+N/O8VfLkqyOym8kXh7odKDfINx9RAotkSGBtQM4abyKfJDkcUg==} + '@tauri-apps/plugin-updater@2.5.0': + resolution: {integrity: sha512-CWpwrkgpMESDPgJ0EuXgQTI/U9zeZQ9NLUvMyuWVrsuRez7tJ/3c7y73LAkvkI6+ekUxVaRJrxYrSfd8W+DRvQ==} + '@tauri-apps/plugin-websocket@2.0.0': resolution: {integrity: sha512-O2qRxZCljd4g+ceJhW7LfgQr+fg0fBBiAaLiMopoKL6TXKMnhBHOenp4nZ5/MoVTr77OQIDNO6Jp/c1YwiRVtQ==} @@ -2649,11 +2639,6 @@ snapshots: clsx: 2.1.1 react: 18.3.1 - '@react-oauth/google@0.12.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - '@react-stately/utils@3.10.4(react@18.3.1)': dependencies: '@swc/helpers': 0.5.13 @@ -2780,6 +2765,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.2.0 + '@tauri-apps/plugin-dialog@2.2.0': + dependencies: + '@tauri-apps/api': 2.2.0 + '@tauri-apps/plugin-global-shortcut@2.0.0': dependencies: '@tauri-apps/api': 2.2.0 @@ -2796,6 +2785,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.2.0 + '@tauri-apps/plugin-updater@2.5.0': + dependencies: + '@tauri-apps/api': 2.2.0 + '@tauri-apps/plugin-websocket@2.0.0': dependencies: '@tauri-apps/api': 2.2.0 diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 5ed6950b..45bfa92c 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -80,6 +80,36 @@ version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ashpd" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d43c03d9e36dd40cab48435be0b09646da362c278223ca535493877b2c1dee9" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.8.5", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus 4.4.0", +] + [[package]] name = "async-broadcast" version = "0.5.1" @@ -183,7 +213,7 @@ dependencies = [ "futures-lite 2.5.0", "parking", "polling 3.7.4", - "rustix 0.38.37", + "rustix 0.38.44", "slab", "tracing", "windows-sys 0.59.0", @@ -222,7 +252,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.37", + "rustix 0.38.44", "windows-sys 0.48.0", ] @@ -241,7 +271,7 @@ dependencies = [ "cfg-if", "event-listener 5.3.1", "futures-lite 2.5.0", - "rustix 0.38.37", + "rustix 0.38.44", "tracing", ] @@ -268,7 +298,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.37", + "rustix 0.38.44", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -400,6 +430,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f" +dependencies = [ + "block-sys", + "objc2", +] + [[package]] name = "block2" version = "0.5.1" @@ -521,16 +570,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.18.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 2.0.6", ] [[package]] @@ -628,12 +677,18 @@ dependencies = [ "tauri-nspanel", "tauri-plugin-autostart", "tauri-plugin-deep-link", + "tauri-plugin-dialog", + "tauri-plugin-drag", + "tauri-plugin-fs", "tauri-plugin-global-shortcut", "tauri-plugin-http", + "tauri-plugin-os", + "tauri-plugin-process", "tauri-plugin-shell", "tauri-plugin-single-instance", "tauri-plugin-store", "tauri-plugin-theme", + "tauri-plugin-updater", "tauri-plugin-websocket", "thiserror 1.0.64", "tokio", @@ -946,6 +1001,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "derive_more" version = "0.99.18" @@ -987,6 +1053,15 @@ dependencies = [ "dirs-sys 0.4.1", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1004,7 +1079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -1016,10 +1091,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1027,7 +1114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -1037,6 +1124,26 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + [[package]] name = "dlopen2" version = "0.7.0" @@ -1069,6 +1176,12 @@ dependencies = [ "const-random", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" version = "0.1.1" @@ -1078,6 +1191,26 @@ dependencies = [ "serde", ] +[[package]] +name = "drag" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64223816f66ef644c52b12ff838eda3ff3f0713670ee8598a59864b61bed77ba" +dependencies = [ + "cocoa", + "core-graphics", + "dunce", + "gdk", + "gdkx11", + "gtk", + "objc", + "raw-window-handle", + "serde", + "thiserror 1.0.64", + "windows 0.52.0", + "windows-core 0.58.0", +] + [[package]] name = "dtoa" version = "1.0.9" @@ -1592,6 +1725,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" +dependencies = [ + "rustix 0.38.44", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1917,6 +2060,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + [[package]] name = "httparse" version = "1.9.5" @@ -2029,6 +2178,16 @@ dependencies = [ "png", ] +[[package]] +name = "icrate" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" +dependencies = [ + "block2 0.4.0", + "objc2", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2327,9 +2486,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -2451,6 +2610,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6367d84fb54d4242af283086402907277715b8fe46976963af5ebf173f8efba3" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -2689,7 +2854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "libc", "objc2", "objc2-core-data", @@ -2705,7 +2870,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-core-location", "objc2-foundation", @@ -2717,7 +2882,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -2729,7 +2894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -2740,7 +2905,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", "objc2-metal", @@ -2752,7 +2917,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-contacts", "objc2-foundation", @@ -2771,7 +2936,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", + "dispatch", "libc", "objc2", ] @@ -2782,7 +2948,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-app-kit", "objc2-foundation", @@ -2795,11 +2961,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6788b04a18ea31e3dc3ab256b8546639e5bbae07c1a0dc4ea8615252bc6aee9a" +dependencies = [ + "bitflags 2.6.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -2807,7 +2985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", "objc2-metal", @@ -2830,7 +3008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-cloud-kit", "objc2-core-data", @@ -2850,7 +3028,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -2862,7 +3040,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-core-location", "objc2-foundation", @@ -2875,7 +3053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" dependencies = [ "bitflags 2.6.0", - "block2", + "block2 0.5.1", "objc2", "objc2-app-kit", "objc2-foundation", @@ -2995,6 +3173,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_pipe" version = "1.2.1" @@ -3005,6 +3194,20 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "osakit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35366a452fce3f8947eb2f33226a133aaf0cacedef2af67ade348d58be7f85d0" +dependencies = [ + "icrate", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 1.0.64", +] + [[package]] name = "pango" version = "0.18.3" @@ -3260,7 +3463,7 @@ checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", "indexmap 2.6.0", - "quick-xml", + "quick-xml 0.32.0", "serde", "time", ] @@ -3304,7 +3507,7 @@ dependencies = [ "concurrent-queue", "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.37", + "rustix 0.38.44", "tracing", "windows-sys 0.59.0", ] @@ -3422,6 +3625,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.5" @@ -3586,6 +3798,17 @@ dependencies = [ "thiserror 1.0.64", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.6", +] + [[package]] name = "regex" version = "1.11.0" @@ -3668,6 +3891,29 @@ dependencies = [ "windows-registry 0.2.0", ] +[[package]] +name = "rfd" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" +dependencies = [ + "ashpd", + "block2 0.5.1", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ring" version = "0.17.8" @@ -3731,15 +3977,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3833,6 +4079,12 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -4279,6 +4531,15 @@ dependencies = [ "futures-core", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -4346,7 +4607,7 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", + "windows 0.58.0", "windows-core 0.58.0", "windows-version", "x11-dl", @@ -4363,6 +4624,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "tar" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -4386,6 +4658,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", + "http-range", "image", "jni", "libc", @@ -4417,7 +4690,7 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.58.0", ] [[package]] @@ -4501,9 +4774,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e6660a409963e4d57b9bfab4addd141eeff41bd3a7fb14e13004a832cf7ef6" +checksum = "5841b9a0200e954ef7457f8d327091424328891e267a97b641dc246cc54d0dec" dependencies = [ "anyhow", "glob", @@ -4551,10 +4824,43 @@ dependencies = [ ] [[package]] -name = "tauri-plugin-fs" -version = "2.0.3" +name = "tauri-plugin-dialog" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba7d46e86db8c830d143ef90ab5a453328365b0cc834c24edea4267b16aba0" +checksum = "8b59fd750551b1066744ab956a1cd6b1ea3e1b3763b0b9153ac27a044d596426" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.6", + "url", +] + +[[package]] +name = "tauri-plugin-drag" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25f35672f11127f90f96fbe787ed93ca3eecd2e2f23bd198351d3991afa9079" +dependencies = [ + "base64 0.21.7", + "drag", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 1.0.64", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1edf18000f02903a7c2e5997fb89aca455ecbc0acc15c6535afbb883be223" dependencies = [ "anyhow", "dunce", @@ -4566,7 +4872,9 @@ dependencies = [ "serde_repr", "tauri", "tauri-plugin", - "thiserror 1.0.64", + "tauri-utils", + "thiserror 2.0.6", + "toml 0.8.2", "url", "uuid", ] @@ -4608,6 +4916,34 @@ dependencies = [ "urlpattern", ] +[[package]] +name = "tauri-plugin-os" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dda2d571a9baf0664c1f2088db227e3072f9028602fafa885deade7547c3b738" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.6", +] + +[[package]] +name = "tauri-plugin-process" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40cc553ab29581c8c43dfa5fb0c9d5aee8ba962ad3b42908eea26c79610441b7" +dependencies = [ + "tauri", + "tauri-plugin", +] + [[package]] name = "tauri-plugin-shell" version = "2.0.1" @@ -4676,11 +5012,42 @@ dependencies = [ "tintanum", "tokio", "webview2-com", - "windows", + "windows 0.58.0", "windows-core 0.58.0", "windows-version", ] +[[package]] +name = "tauri-plugin-updater" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf3da08c36fb03c98c76e5563d4e74d9a590df0f40978cbe07f39cb52833f7c" +dependencies = [ + "base64 0.22.1", + "dirs 6.0.0", + "flate2", + "futures-util", + "http", + "infer", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.6", + "time", + "tokio", + "url", + "windows-sys 0.59.0", + "zip", +] + [[package]] name = "tauri-plugin-websocket" version = "2.2.0" @@ -4716,7 +5083,7 @@ dependencies = [ "tauri-utils", "thiserror 2.0.6", "url", - "windows", + "windows 0.58.0", ] [[package]] @@ -4741,15 +5108,15 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.58.0", "wry", ] [[package]] name = "tauri-utils" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9271a88f99b4adea0dc71d0baca4505475a0bbd139fb135f62958721aaa8fe54" +checksum = "96fb10e7cc97456b2d5b9c03e335b5de5da982039a303a20d10006885e4523a0" dependencies = [ "brotli", "cargo_metadata", @@ -4801,7 +5168,7 @@ dependencies = [ "cfg-if", "fastrand 2.3.0", "once_cell", - "rustix 0.38.37", + "rustix 0.38.44", "windows-sys 0.59.0", ] @@ -4938,8 +5305,10 @@ dependencies = [ "libc", "mio 1.0.2", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.7", "tokio-macros", + "tracing", "windows-sys 0.52.0", ] @@ -5477,6 +5846,66 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +dependencies = [ + "cc", + "downcast-rs", + "rustix 0.38.44", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +dependencies = [ + "bitflags 2.6.0", + "rustix 0.38.44", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +dependencies = [ + "bitflags 2.6.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml 0.37.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.72" @@ -5548,10 +5977,10 @@ checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.58.0", "windows-core 0.58.0", - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", ] [[package]] @@ -5572,7 +6001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ "thiserror 1.0.64", - "windows", + "windows 0.58.0", "windows-core 0.58.0", ] @@ -5621,6 +6050,18 @@ dependencies = [ "windows-version", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement 0.52.0", + "windows-interface 0.52.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" @@ -5646,13 +6087,24 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", + "windows-implement 0.58.0", + "windows-interface 0.58.0", "windows-result", "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-implement" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -5664,6 +6116,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "windows-interface" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -5992,7 +6455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61ce51277d65170f6379d8cda935c80e3c2d1f0ff712a123c8bddb11b31a4b73" dependencies = [ "base64 0.22.1", - "block2", + "block2 0.5.1", "cookie", "crossbeam-channel", "dpi", @@ -6022,7 +6485,7 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.58.0", "windows-core 0.58.0", "windows-version", "x11-dl", @@ -6049,6 +6512,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "xattr" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" +dependencies = [ + "libc", + "linux-raw-sys 0.4.14", + "rustix 0.38.44", +] + [[package]] name = "xdg-home" version = "1.3.0" @@ -6129,6 +6603,7 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", + "tokio", "tracing", "uds_windows", "windows-sys 0.52.0", @@ -6214,6 +6689,21 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zip" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "indexmap 2.6.0", + "memchr", + "thiserror 2.0.6", +] + [[package]] name = "zvariant" version = "3.15.2" @@ -6238,6 +6728,7 @@ dependencies = [ "enumflags2", "serde", "static_assertions", + "url", "zvariant_derive 4.2.0", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 97400e4b..44e937d2 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -14,23 +14,31 @@ name = "coco_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2.0.0", features = [] } +tauri-build = { version = "2", features = [] } [dependencies] pizza-common = { git = "https://github.com/infinilabs/pizza-common", branch = "main" } -tauri = { version = "2.0.6", features = ["macos-private-api", "tray-icon", "image-png", "unstable"] } -tauri-plugin-shell = "2.0.0" +tauri = { version = "2", features = ["protocol-asset", "macos-private-api", "tray-icon", "image-ico", "image-png", "unstable"] } +tauri-plugin-shell = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-plugin-http = "2" - tauri-plugin-websocket = "2" tauri-plugin-theme = "2.1.2" -tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } tauri-plugin-deep-link = "2.0.0" tauri-plugin-single-instance = "2.0.0" tauri-plugin-store = "2.2.0" +tauri-plugin-os = "2" +tauri-plugin-dialog = "2" +tauri-plugin-fs = "2" +tauri-plugin-updater = "2" +tauri-plugin-process = "2" +tauri-plugin-drag = "2" + +[target."cfg(target_os = \"macos\")".dependencies] +tauri-nspanel = { git = "https://github.com/ahkohd/tauri-nspanel", branch = "v2" } + reqwest = "0.12.12" futures = "0.3.31" ordered-float = { version = "4.6.0", default-features = false } diff --git a/src-tauri/Coco.desktop b/src-tauri/Coco.desktop new file mode 100644 index 00000000..b18d83ac --- /dev/null +++ b/src-tauri/Coco.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name={{{name}}} +Exec={{{exec}}} +Icon={{{icon}}} +Categories={{{categories}}} +Comment={{{comment}}} +Terminal=false diff --git a/src-tauri/assets/drag-icon.png b/src-tauri/assets/drag-icon.png new file mode 100644 index 00000000..bed288d1 Binary files /dev/null and b/src-tauri/assets/drag-icon.png differ diff --git a/src-tauri/assets/logo-mac.png b/src-tauri/assets/logo-mac.png new file mode 100644 index 00000000..fef05b9a Binary files /dev/null and b/src-tauri/assets/logo-mac.png differ diff --git a/src-tauri/assets/logo.png b/src-tauri/assets/logo.png new file mode 100644 index 00000000..4076306b Binary files /dev/null and b/src-tauri/assets/logo.png differ diff --git a/src-tauri/assets/tray-mac.ico b/src-tauri/assets/tray-mac.ico new file mode 100644 index 00000000..c3f96cd5 Binary files /dev/null and b/src-tauri/assets/tray-mac.ico differ diff --git a/src-tauri/assets/tray.ico b/src-tauri/assets/tray.ico new file mode 100644 index 00000000..5675d5e8 Binary files /dev/null and b/src-tauri/assets/tray.ico differ diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index e7b6d287..1ee7adc9 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -55,9 +55,6 @@ { "identifier": "http:default", "allow": [ - { - "url": "http://localhost:9000" - }, { "url": "https://coco.infini.cloud" } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 7c982047..507e9511 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -18,7 +18,7 @@ use reqwest::Client; use std::path::PathBuf; #[cfg(target_os = "macos")] use tauri::ActivationPolicy; -use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow}; +use tauri::{AppHandle, Emitter, Listener, Manager, Runtime, WebviewWindow, WindowEvent}; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_deep_link::DeepLinkExt; use tokio::runtime::Runtime as RT; @@ -73,7 +73,7 @@ struct Payload { pub fn run() { let mut ctx = tauri::generate_context!(); - tauri::Builder::default() + let app = tauri::Builder::default() .plugin(tauri_nspanel::init()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_shell::init()) @@ -96,7 +96,7 @@ pub fn run() { shortcut::get_current_shortcut, change_autostart, hide_coco, - switch_tray_icon, + // switch_tray_icon, server::servers::add_coco_server, server::servers::remove_coco_server, server::servers::list_coco_servers, @@ -158,9 +158,31 @@ pub fn run() { setup::default(app, main_window.clone(), settings_window.clone()); Ok(()) - }) - .run(ctx) + }).on_window_event(|window, event| match event { + WindowEvent::CloseRequested { api, .. } => { + dbg!("Close requested event received"); + window.hide().unwrap(); + api.prevent_close(); + } + _ => {} + }).build(ctx) .expect("error while running tauri application"); + + app.run(|app_handle, event| match event { + #[cfg(target_os = "macos")] + tauri::RunEvent::Reopen { + has_visible_windows, + .. + } => { + dbg!("Reopen event received: has_visible_windows = {}", has_visible_windows); + if has_visible_windows { + return; + } + } + _ => { + let _ = app_handle; + } + }); } pub async fn init(app_handle: &AppHandle) { @@ -253,7 +275,7 @@ fn hide_coco(app: tauri::AppHandle) { } fn handle_open_coco(app: &AppHandle) { - // println!("Open Coco menu clicked!"); + println!("Open Coco menu clicked!"); if let Some(window) = app.get_window(MAIN_WINDOW_LABEL) { window.show().unwrap(); @@ -279,36 +301,36 @@ fn handle_hide_coco(app: &AppHandle) { } } -#[tauri::command] -fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) { - let app_handle = app.app_handle(); - - // println!("is_dark_mode: {}", is_dark_mode); - - const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png"); - const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png"); - - let icon_path: &[u8] = if is_dark_mode { - DARK_ICON_PATH - } else { - LIGHT_ICON_PATH - }; - - let tray = match app_handle.tray_by_id("tray") { - Some(tray) => tray, - None => { - eprintln!("Tray with ID 'tray' not found"); - return; - } - }; - - if let Err(e) = tray.set_icon(Some( - tauri::image::Image::from_bytes(icon_path) - .unwrap_or_else(|e| panic!("Failed to load icon from bytes: {}", e)), - )) { - eprintln!("Failed to set tray icon: {}", e); - } -} +// #[tauri::command] +// fn switch_tray_icon(app: tauri::AppHandle, is_dark_mode: bool) { +// let app_handle = app.app_handle(); +// +// // println!("is_dark_mode: {}", is_dark_mode); +// +// const DARK_ICON_PATH: &[u8] = include_bytes!("../icons/dark@2x.png"); +// const LIGHT_ICON_PATH: &[u8] = include_bytes!("../icons/light@2x.png"); +// +// let icon_path: &[u8] = if is_dark_mode { +// DARK_ICON_PATH +// } else { +// LIGHT_ICON_PATH +// }; +// +// let tray = match app_handle.tray_by_id("tray") { +// Some(tray) => tray, +// None => { +// eprintln!("Tray with ID 'tray' not found"); +// return; +// } +// }; +// +// if let Err(e) = tray.set_icon(Some( +// tauri::image::Image::from_bytes(icon_path) +// .unwrap_or_else(|e| panic!("Failed to load icon from bytes: {}", e)), +// )) { +// eprintln!("Failed to set tray icon: {}", e); +// } +// } fn enable_tray(app: &mut tauri::App) { use tauri::{ @@ -335,8 +357,9 @@ fn enable_tray(app: &mut tauri::App) { .unwrap(); let _tray = TrayIconBuilder::with_id("tray") + .icon_as_template(true) // .icon(app.default_window_icon().unwrap().clone()) - .icon(Image::from_bytes(include_bytes!("../icons/light@2x.png")).expect("REASON")) + .icon(Image::from_bytes(include_bytes!("../assets/tray-mac.ico")).expect("Failed to load icon")) .menu(&menu) .on_menu_event(|app, event| match event.id.as_ref() { "open" => { @@ -351,7 +374,7 @@ fn enable_tray(app: &mut tauri::App) { "settings" => { // windows failed to open second window, issue: https://github.com/tauri-apps/tauri/issues/11144 https://github.com/tauri-apps/tauri/issues/8196 //#[cfg(windows)] - let _ = app.emit("open_settings", ""); + let _ = app.emit("open_settings", "settings"); // #[cfg(not(windows))] // open_settings(&app); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 41f57098..e3016ffe 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2.0.0", "productName": "Coco AI", - "version": "0.1.0", + "version": "../package.json", "identifier": "rs.coco.app", "build": { "beforeDevCommand": "pnpm dev", @@ -23,12 +23,13 @@ "maximizable": false, "skipTaskbar": true, "resizable": false, - "alwaysOnTop": false, + "alwaysOnTop": true, "acceptFirstMouse": true, "shadow": true, "transparent": true, "fullscreen": false, "center": false, + "visible": false, "windowEffects": { "effects": [], "radius": 12 @@ -43,7 +44,8 @@ "transparent": true, "maximizable": false, "skipTaskbar": false, - "dragDropEnabled": true, + "dragDropEnabled": false, + "hiddenTitle": true, "visible": false, "windowEffects": { "effects": [ @@ -54,17 +56,30 @@ } ], "security": { - "csp": null + "csp": null, + "dangerousDisableAssetCspModification": true, + "assetProtocol": { + "enable": true, + "scope": { + "allow": [ + "**/*" + ], + "requireLiteralLeadingDot": false + } + } } }, "bundle": { "active": true, - "targets": "all", - "windows": { - "certificateThumbprint": null, - "digestAlgorithm": "sha256", - "timestampUrl": "" - }, + "targets": [ + "nsis", + "dmg", + "app", + "appimage", + "deb", + "rpm" + ], + "shortDescription": "Coco AI", "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -108,6 +123,12 @@ ] }, "window": {}, + "updater": { + "pubkey": "", + "endpoints": [ + "https://api.coco.rs/update" + ] + }, "websocket": {}, "shell": {}, "globalShortcut": {}, diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json new file mode 100644 index 00000000..a53a8c6c --- /dev/null +++ b/src-tauri/tauri.linux.conf.json @@ -0,0 +1,15 @@ +{ + "identifier": "rs.coco.app", + "bundle": { + "linux": { + "deb": { + "depends": [], + "desktopTemplate": "./Coco.desktop" + }, + "rpm": { + "depends": [], + "desktopTemplate": "./Coco.desktop" + } + } + } +} diff --git a/src-tauri/tauri.macos.conf.json b/src-tauri/tauri.macos.conf.json new file mode 100644 index 00000000..1491f939 --- /dev/null +++ b/src-tauri/tauri.macos.conf.json @@ -0,0 +1,11 @@ +{ + "identifier": "rs.coco.app", + "bundle": { + "externalBin": [ + ], + "resources": [ + "assets/tray-mac.ico", + "assets/drag-icon.png" + ] + } +} diff --git a/src-tauri/tauri.windows.conf.json b/src-tauri/tauri.windows.conf.json new file mode 100644 index 00000000..a412e944 --- /dev/null +++ b/src-tauri/tauri.windows.conf.json @@ -0,0 +1,15 @@ +{ + "identifier": "rs.coco.app", + "bundle": { + "externalBin": [ + ], + "windows": { + "digestAlgorithm": "sha256", + "nsis": { + "languages": [ + ], + "installMode": "both" + } + } + } +} diff --git a/src/components/Search/InputBox.tsx b/src/components/Search/InputBox.tsx index c3154fbe..4517a422 100644 --- a/src/components/Search/InputBox.tsx +++ b/src/components/Search/InputBox.tsx @@ -1,406 +1,419 @@ -import { - Library, - Mic, - Send, - Plus, - AudioLines, - Image, - ArrowBigLeft, - Search, -} from "lucide-react"; -import { useRef, useState, useEffect, useCallback } from "react"; -import { listen } from "@tauri-apps/api/event"; -import { isTauri } from "@tauri-apps/api/core"; +import {ArrowBigLeft, AudioLines, Image, Library, Mic, Plus, Search, Send,} from "lucide-react"; +import {useCallback, useEffect, useRef, useState} from "react"; +import {listen} from "@tauri-apps/api/event"; +import {invoke, isTauri} from "@tauri-apps/api/core"; import ChatSwitch from "@/components/Common/ChatSwitch"; import AutoResizeTextarea from "./AutoResizeTextarea"; -import { useChatStore } from "@/stores/chatStore"; +import {useChatStore} from "@/stores/chatStore"; import StopIcon from "@/icons/Stop"; -import { useAppStore } from "@/stores/appStore"; -import { useSearchStore } from "@/stores/searchStore"; -import { metaOrCtrlKey } from "@/utils/keyboardUtils"; +import {useAppStore} from "@/stores/appStore"; +import {useSearchStore} from "@/stores/searchStore"; +import {metaOrCtrlKey} from "@/utils/keyboardUtils"; + interface ChatInputProps { - onSend: (message: string) => void; - disabled: boolean; - disabledChange: () => void; - changeMode: (isChatMode: boolean) => void; - isChatMode: boolean; - inputValue: string; - changeInput: (val: string) => void; - reconnect: () => void; + onSend: (message: string) => void; + disabled: boolean; + disabledChange: () => void; + changeMode: (isChatMode: boolean) => void; + isChatMode: boolean; + inputValue: string; + changeInput: (val: string) => void; + reconnect: () => void; } export default function ChatInput({ - onSend, - disabled, - changeMode, - isChatMode, - inputValue, - changeInput, - disabledChange, - reconnect, -}: ChatInputProps) { - const showTooltip = useAppStore((state: { showTooltip: boolean }) => state.showTooltip); + onSend, + disabled, + changeMode, + isChatMode, + inputValue, + changeInput, + disabledChange, + reconnect, + }: ChatInputProps) { + const showTooltip = useAppStore((state: { showTooltip: boolean }) => state.showTooltip); - const sourceData = useSearchStore((state: { sourceData: any; }) => state.sourceData); - const setSourceData = useSearchStore((state: { setSourceData: any; }) => state.setSourceData); + const sourceData = useSearchStore((state: { sourceData: any; }) => state.sourceData); + const setSourceData = useSearchStore((state: { setSourceData: any; }) => state.setSourceData); - useEffect(() => { - setSourceData(undefined); - }, []); + useEffect(() => { + setSourceData(undefined); + }, []); - const inputRef = useRef(null); - const textareaRef = useRef<{ reset: () => void; focus: () => void }>(null); + const inputRef = useRef(null); + const textareaRef = useRef<{ reset: () => void; focus: () => void }>(null); - const { curChatEnd, connected } = useChatStore(); + const {curChatEnd, connected} = useChatStore(); - const [isCommandPressed, setIsCommandPressed] = useState(false); + const [isCommandPressed, setIsCommandPressed] = useState(false); - const handleToggleFocus = useCallback(() => { - if (isChatMode) { - textareaRef.current?.focus(); - } else { - inputRef.current?.focus(); - } - }, [isChatMode, textareaRef, inputRef]); - - const handleSubmit = useCallback(() => { - const trimmedValue = inputValue.trim(); - if (trimmedValue && !disabled) { - onSend(trimmedValue); - } - }, [inputValue, disabled, onSend]); - - const pressedKeys = new Set(); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - console.log("handleKeyDown", e.code, e.key); - pressedKeys.add(e.key); - - if (e.key === metaOrCtrlKey()) { - setIsCommandPressed(true); - } - - if (pressedKeys.has(metaOrCtrlKey())) { - // e.preventDefault(); - switch (e.code) { - case "Comma": - setIsCommandPressed(false); - break; - case "KeyI": - handleToggleFocus(); - break; - case "ArrowLeft": - setSourceData(undefined); - break; - case "KeyM": - console.log("KeyM"); - break; - case "Enter": - isChatMode && (curChatEnd ? handleSubmit() : disabledChange?.()); - break; - case "KeyO": - console.log("KeyO"); - break; - case "KeyU": - console.log("KeyU"); - break; - case "KeyN": - console.log("KeyN"); - break; - case "KeyG": - console.log("KeyG"); - break; - default: - break; - } - } - }, - [ - handleToggleFocus, - isChatMode, - handleSubmit, - setSourceData, - setIsCommandPressed, - disabledChange, - curChatEnd, - ] - ); - - const handleKeyUp = useCallback((e: KeyboardEvent) => { - pressedKeys.delete(e.key); - if (e.key === metaOrCtrlKey()) { - setIsCommandPressed(false); - } - }, []); - - useEffect(() => { - window.addEventListener("keydown", handleKeyDown); - window.addEventListener("keyup", handleKeyUp); - - return () => { - window.removeEventListener("keydown", handleKeyDown); - window.removeEventListener("keyup", handleKeyUp); - }; - }, [handleKeyDown, handleKeyUp]); - - useEffect(() => { - if (!isTauri()) return; - const setupListener = async () => { - const unlisten = await listen("tauri://focus", () => { - // console.log("Window focused!"); + const handleToggleFocus = useCallback(() => { if (isChatMode) { - textareaRef.current?.focus(); + textareaRef.current?.focus(); } else { - inputRef.current?.focus(); + inputRef.current?.focus(); } - }); + }, [isChatMode, textareaRef, inputRef]); - return unlisten; - }; + const handleSubmit = useCallback(() => { + const trimmedValue = inputValue.trim(); + if (trimmedValue && !disabled) { + onSend(trimmedValue); + } + }, [inputValue, disabled, onSend]); - let unlisten: (() => void) | undefined; + const pressedKeys = new Set(); - setupListener().then((unlistener) => { - unlisten = unlistener; - }); + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + console.log("handleKeyDown", e.code, e.key); - return () => { - unlisten?.(); - }; - }, [isChatMode]); - - const openChatAI = async () => { - console.log("Chat AI opened."); - }; - - const [countdown, setCountdown] = useState(5); - - useEffect(() => { - if (connected) return; - if (countdown <= 0) { - ReconnectClick(); - return; - } - - const timer = setTimeout(() => { - setCountdown((prev) => prev - 1); - }, 1000); - - return () => clearTimeout(timer); - }, [countdown, connected]); - - const ReconnectClick = () => { - setCountdown(5); - reconnect(); - }; - - return ( -
-
-
- {!isChatMode && !sourceData ? ( - - ) : ( - !isChatMode && sourceData ? ( - setSourceData(undefined)} - /> - ) : null - )} - - {isChatMode ? ( - { - if (e.key === "Enter") { - e.preventDefault(); - handleSubmit(); + if (e.key === "Escape") { + console.log("Escape:" + inputValue); + if (inputValue) { + changeInput(""); + return; + } else { + console.log("empty value, but Escape key pressed."); + invoke("hide_coco").then(() => { + console.log("Hide Coco"); + }).finally(() => { + console.log("Hide Coco"); + }) } - }} - /> - ) : ( - { - onSend(e.target.value); - }} - /> - )} - {showTooltip && isCommandPressed && !isChatMode && sourceData ? ( + } + + + pressedKeys.add(e.key); + + if (e.key === metaOrCtrlKey()) { + setIsCommandPressed(true); + } + + if (pressedKeys.has(metaOrCtrlKey())) { + // e.preventDefault(); + switch (e.code) { + case "Comma": + setIsCommandPressed(false); + break; + case "KeyI": + handleToggleFocus(); + break; + case "ArrowLeft": + setSourceData(undefined); + break; + case "KeyM": + console.log("KeyM"); + break; + case "Enter": + isChatMode && (curChatEnd ? handleSubmit() : disabledChange?.()); + break; + case "KeyO": + console.log("KeyO"); + break; + case "KeyU": + console.log("KeyU"); + break; + case "KeyN": + console.log("KeyN"); + break; + case "KeyG": + console.log("KeyG"); + break; + default: + break; + } + } + }, + [ + handleToggleFocus, + isChatMode, + handleSubmit, + setSourceData, + setIsCommandPressed, + disabledChange, + curChatEnd, + ] + ); + + const handleKeyUp = useCallback((e: KeyboardEvent) => { + pressedKeys.delete(e.key); + if (e.key === metaOrCtrlKey()) { + setIsCommandPressed(false); + } + }, []); + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown); + window.addEventListener("keyup", handleKeyUp); + + return () => { + window.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("keyup", handleKeyUp); + }; + }, [handleKeyDown, handleKeyUp]); + + useEffect(() => { + if (!isTauri()) return; + const setupListener = async () => { + const unlisten = await listen("tauri://focus", () => { + // console.log("Window focused!"); + if (isChatMode) { + textareaRef.current?.focus(); + } else { + inputRef.current?.focus(); + } + }); + + return unlisten; + }; + + let unlisten: (() => void) | undefined; + + setupListener().then((unlistener) => { + unlisten = unlistener; + }); + + return () => { + unlisten?.(); + }; + }, [isChatMode]); + + const openChatAI = async () => { + console.log("Chat AI opened."); + }; + + const [countdown, setCountdown] = useState(5); + + useEffect(() => { + if (connected) return; + if (countdown <= 0) { + ReconnectClick(); + return; + } + + const timer = setTimeout(() => { + setCountdown((prev) => prev - 1); + }, 1000); + + return () => clearTimeout(timer); + }, [countdown, connected]); + + const ReconnectClick = () => { + setCountdown(5); + reconnect(); + }; + + return ( +
- ← + className="p-2 flex items-center dark:text-[#D8D8D8] bg-[#ededed] dark:bg-[#202126] rounded transition-all relative"> +
+ {!isChatMode && !sourceData ? ( + + ) : ( + !isChatMode && sourceData ? ( + setSourceData(undefined)} + /> + ) : null + )} + + {isChatMode ? ( + { + if (e.key === "Enter") { + e.preventDefault(); + handleSubmit(); + } + }} + /> + ) : ( + { + onSend(e.target.value); + }} + /> + )} + {showTooltip && isCommandPressed && !isChatMode && sourceData ? ( +
+ ← +
+ ) : null} + {showTooltip && isCommandPressed ? ( +
+ I +
+ ) : null} +
+ + {isChatMode ? ( + + ) : null} + + {isChatMode && curChatEnd ? ( + + ) : null} + {isChatMode && !curChatEnd ? ( + + ) : null} + + {showTooltip && isChatMode && isCommandPressed ? ( +
+ M +
+ ) : null} + + {showTooltip && isChatMode && isCommandPressed ? ( +
+ ↩︎ +
+ ) : null} + + {!connected && isChatMode ? ( +
+ Unable to connect to the server +
+ Reconnect ({countdown}) +
+
+ ) : null}
- ) : null} - {showTooltip && isCommandPressed ? ( +
- I + {isChatMode ? ( +
+ + + {showTooltip && isCommandPressed ? ( +
+ O +
+ ) : null} + {showTooltip && isCommandPressed ? ( +
+ U +
+ ) : null} +
+ ) : ( +
+ + + {showTooltip && isCommandPressed ? ( +
+ N +
+ ) : null} + {showTooltip && isCommandPressed ? ( +
+ G +
+ ) : null} +
+ )} + +
+ {showTooltip && isCommandPressed ? ( +
+ T +
+ ) : null} + { + value && disabledChange(); + changeMode(value); + setSourceData(undefined); + }} + /> +
- ) : null}
- - {isChatMode ? ( - - ) : null} - - {isChatMode && curChatEnd ? ( - - ) : null} - {isChatMode && !curChatEnd ? ( - - ) : null} - - {showTooltip && isChatMode && isCommandPressed ? ( -
- M -
- ) : null} - - {showTooltip && isChatMode && isCommandPressed ? ( -
- ↩︎ -
- ) : null} - - {!connected && isChatMode ? ( -
- Unable to connect to the server -
- Reconnect ({countdown}) -
-
- ) : null} -
- -
- {isChatMode ? ( -
- - - {showTooltip && isCommandPressed ? ( -
- O -
- ) : null} - {showTooltip && isCommandPressed ? ( -
- U -
- ) : null} -
- ) : ( -
- - - {showTooltip && isCommandPressed ? ( -
- N -
- ) : null} - {showTooltip && isCommandPressed ? ( -
- G -
- ) : null} -
- )} - -
- {showTooltip && isCommandPressed ? ( -
- T -
- ) : null} - { - value && disabledChange(); - changeMode(value); - setSourceData(undefined); - }} - /> -
-
-
- ); + ); } diff --git a/src/hooks/useEscape.ts b/src/hooks/useEscape.ts index 64125b51..18ab629d 100644 --- a/src/hooks/useEscape.ts +++ b/src/hooks/useEscape.ts @@ -1,32 +1,36 @@ -import { useEffect } from "react"; -import { isTauri, invoke } from "@tauri-apps/api/core"; -import { listen } from "@tauri-apps/api/event"; +import {useEffect} from "react"; +import {invoke, isTauri} from "@tauri-apps/api/core"; +import {listen} from "@tauri-apps/api/event"; const useEscape = () => { - const handleEscape = async (event: KeyboardEvent) => { - if (event.key === "Escape") { - event.preventDefault(); - // Hide the Tauri app window when 'Esc' is pressed - await invoke("hide_coco"); - console.log("App window hidden successfully."); - } - }; + const handleEscape = async (event: KeyboardEvent) => { + if (event.key === "Escape") { + console.log("Escape key pressed."); - useEffect(() => { - if(!isTauri()) return; - - const unlisten = listen("tauri://focus", () => { - // Add event listener for keydown - window.addEventListener("keydown", handleEscape); - }); + event.preventDefault(); - // Cleanup event listener on component unmount - return () => { - unlisten.then((unlistenFn) => unlistenFn()); + // Hide the Tauri app window when 'Esc' is pressed + await invoke("hide_coco"); - window.removeEventListener("keydown", handleEscape); + console.log("App window hidden successfully."); + } }; - }, []); + + useEffect(() => { + if (!isTauri()) return; + + const unlisten = listen("tauri://focus", () => { + // Add event listener for keydown + window.addEventListener("keydown", handleEscape); + }); + + // Cleanup event listener on component unmount + return () => { + unlisten.then((unlistenFn) => unlistenFn()); + + window.removeEventListener("keydown", handleEscape); + }; + }, []); }; export default useEscape; diff --git a/src/pages/main/index.tsx b/src/pages/main/index.tsx index b4b757c0..d4966aa6 100644 --- a/src/pages/main/index.tsx +++ b/src/pages/main/index.tsx @@ -1,124 +1,149 @@ -import { useState, useRef, useEffect } from "react"; -import { isTauri } from "@tauri-apps/api/core"; -import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; -import { LogicalSize } from "@tauri-apps/api/dpi"; +import {useEffect, useRef, useState} from "react"; +import {invoke, isTauri} from "@tauri-apps/api/core"; +import {getCurrentWebviewWindow} from "@tauri-apps/api/webviewWindow"; +import {LogicalSize} from "@tauri-apps/api/dpi"; import InputBox from "@/components/Search/InputBox"; import Search from "@/components/Search/Search"; -import ChatAI, { ChatAIRef } from "@/components/Assistant/Chat"; -import { useAppStore } from "@/stores/appStore"; -import { useAuthStore } from "@/stores/authStore"; +import ChatAI, {ChatAIRef} from "@/components/Assistant/Chat"; +import {useAppStore} from "@/stores/appStore"; +import {useAuthStore} from "@/stores/authStore"; import ApiDetails from "@/components/Common/ApiDetails"; export default function DesktopApp() { - const initializeListeners = useAppStore((state) => state.initializeListeners); - const initializeListeners_auth = useAuthStore( - (state) => state.initializeListeners - ); + const initializeListeners = useAppStore((state) => state.initializeListeners); + const initializeListeners_auth = useAuthStore( + (state) => state.initializeListeners + ); - useEffect(() => { - initializeListeners(); - initializeListeners_auth(); - }, []); + useEffect(() => { + initializeListeners(); + initializeListeners_auth(); - const chatAIRef = useRef(null); + // Listen for window focus and blur events + const handleBlur = () => { + console.log("Window blurred"); + invoke('hide_coco').then(() => { + console.log("Hide Coco"); + }).finally(() => { + console.log("Hide Coco"); + }); + }; - const [isChatMode, setIsChatMode] = useState(false); - const [input, setInput] = useState(""); - const [isTransitioned, setIsTransitioned] = useState(false); + const handleFocus = () => { + // Optionally, show the window if needed when focus is regained + console.log("Window focused"); + }; - async function changeMode(value: boolean) { - setIsChatMode(value); - setIsTransitioned(value); - } + window.addEventListener('blur', handleBlur); + window.addEventListener('focus', handleFocus); - async function changeInput(value: string) { - setInput(value); - } + // Clean up event listeners on component unmount + return () => { + window.removeEventListener('blur', handleBlur); + window.removeEventListener('focus', handleFocus); + }; - const handleSendMessage = async (value: string) => { - setInput(value); - if (isChatMode) { - if (isTauri()) { - await getCurrentWebviewWindow()?.setSize(new LogicalSize(680, 596)); - } - chatAIRef.current?.init(); + }, []); + + const chatAIRef = useRef(null); + + const [isChatMode, setIsChatMode] = useState(false); + const [input, setInput] = useState(""); + const [isTransitioned, setIsTransitioned] = useState(false); + + async function changeMode(value: boolean) { + setIsChatMode(value); + setIsTransitioned(value); } - }; - const cancelChat = () => { - chatAIRef.current?.cancelChat(); - }; - const reconnect = () => { - chatAIRef.current?.reconnect(); - }; - const isTyping = false; + async function changeInput(value: string) { + setInput(value); + } - return ( -
-
- { - cancelChat(); - }} - changeMode={changeMode} - changeInput={changeInput} - reconnect={reconnect} - /> -
+ const handleSendMessage = async (value: string) => { + setInput(value); + if (isChatMode) { + if (isTauri()) { + await getCurrentWebviewWindow()?.setSize(new LogicalSize(680, 596)); + } + chatAIRef.current?.init(); + } + }; + const cancelChat = () => { + chatAIRef.current?.cancelChat(); + }; -
- -
+ const reconnect = () => { + chatAIRef.current?.reconnect(); + }; + const isTyping = false; -
- {isTransitioned && isChatMode ? ( - - ) : null} -
+ return ( +
+
+ { + cancelChat(); + }} + changeMode={changeMode} + changeInput={changeInput} + reconnect={reconnect} + /> +
- -
- ); +
+ +
+ +
+ {isTransitioned && isChatMode ? ( + + ) : null} +
+ + +
+ ); }