diff --git a/.github/workflows/release-tauri.yml b/.github/workflows/release-tauri.yml new file mode 100644 index 0000000..897e7cb --- /dev/null +++ b/.github/workflows/release-tauri.yml @@ -0,0 +1,98 @@ +name: Release Build + +on: + push: + tags: + - "v*" # Trigger on version tags like v0.1.0, v1.2.3 + workflow_dispatch: # Allow manual trigger + inputs: + optimized: + description: "Build optimized release (LTO, size optimization)" + required: true + type: boolean + default: false + +jobs: + release: + permissions: + contents: write + packages: write + strategy: + fail-fast: false + matrix: + include: + - platform: "macos-latest" # macOS Apple Silicon + args: "--target aarch64-apple-darwin" + rust_target: "aarch64-apple-darwin" + - platform: "macos-latest" # macOS Intel + args: "--target x86_64-apple-darwin" + rust_target: "x86_64-apple-darwin" + - platform: "ubuntu-22.04" # Linux + args: "" + rust_target: "" + - platform: "windows-latest" # Windows + args: "" + rust_target: "" + + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install dependencies (Ubuntu only) + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libasound2-dev + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + # Cross-compilation targets (macOS only) + targets: ${{ matrix.rust_target }} + + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: "./src-tauri -> target" + + - name: Install frontend dependencies + run: cd ui-react && pnpm install + + - name: Setup optimized build config (if enabled) + if: github.event.inputs.optimized == 'true' || startsWith(github.ref, 'refs/tags/') + shell: bash + run: | + cp src-tauri/.cargo/config-optimized.toml src-tauri/.cargo/config.toml + + - name: Build Tauri app + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_OPTIONS: "--max-old-space-size=8192" # Increase Node.js heap size to 8GB + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + with: + tagName: ${{ github.ref_name }} + releaseName: "RetroChat v__VERSION__" + releaseBody: "See the assets to download and install this version." + releaseDraft: true + prerelease: false + args: ${{ matrix.args }} + includeUpdaterJson: true + + - name: Cleanup optimized config + if: always() && (github.event.inputs.optimized == 'true' || startsWith(github.ref, 'refs/tags/')) + shell: bash + run: | + rm -f src-tauri/.cargo/config.toml diff --git a/.gitignore b/.gitignore index d2eba89..e1094c8 100644 --- a/.gitignore +++ b/.gitignore @@ -86,4 +86,5 @@ examples/local_* # Tauri build artifacts src-tauri/target/ src-tauri/WixTools/ -src-tauri/gen/ \ No newline at end of file +src-tauri/gen/ +src-tauri/.cargo/config.toml \ No newline at end of file diff --git a/src-tauri/.cargo/BUNDLE_SIZE.md b/src-tauri/.cargo/BUNDLE_SIZE.md new file mode 100644 index 0000000..3d3429b --- /dev/null +++ b/src-tauri/.cargo/BUNDLE_SIZE.md @@ -0,0 +1,17 @@ +# Bundle Sizes + +Default `cargo tauri build` output: + +1. **DMG size**: 10 MB +2. **Binary size**: 25.9 MB + +Optimized `cargo tauri build` output: + +``` +cp src-tauri/.cargo/config-optimized.toml src-tauri/.cargo/config.toml +cargo tauri build +rm src-tauri/.cargo/config.toml +``` + +1. **DMG size**: 4.9 MB +2. **Binary size**: 8.7 MB diff --git a/src-tauri/.cargo/config-optimized.toml b/src-tauri/.cargo/config-optimized.toml new file mode 100644 index 0000000..54ed0b0 --- /dev/null +++ b/src-tauri/.cargo/config-optimized.toml @@ -0,0 +1,19 @@ +[profile.release] +# Basic size optimizations that work on stable Rust +opt-level = "z" # Optimize for size instead of speed +lto = true # Enable Link Time Optimization +codegen-units = 1 # Single codegen unit for better optimization +strip = true # Remove symbols from binary +panic = "abort" # Abort on panic instead of unwinding + +[target.x86_64-apple-darwin] +# Stable-only optimizations (safe for dev builds) +rustflags = [ + "-C", "link-arg=-Wl,-dead_strip", # Remove unused code at link time +] + +[target.aarch64-apple-darwin] +# Stable-only optimizations (safe for dev builds) +rustflags = [ + "-C", "link-arg=-Wl,-dead_strip", # Remove unused code at link time +] diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7a0eee5..33eef69 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -144,6 +144,15 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -944,6 +953,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.109", +] + [[package]] name = "derive_more" version = "0.99.20" @@ -1312,6 +1332,18 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + [[package]] name = "find-msvc-tools" version = "0.1.4" @@ -1670,8 +1702,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1681,9 +1715,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -2095,9 +2131,26 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.32", - "rustls", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.3.1", + "hyper 1.7.0", + "hyper-util", + "rustls 0.23.35", + "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", + "tower-service", + "webpki-roots 1.0.4", ] [[package]] @@ -2679,6 +2732,12 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mac" version = "0.1.1" @@ -2753,6 +2812,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "minisign-verify" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e856fdd13623a2f5f2f54676a4ee49502a96a80ef4a62bcedd23d52427c44d43" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3215,6 +3280,18 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.10.0", + "objc2 0.6.3", + "objc2-app-kit", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -3350,6 +3427,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2 0.6.3", + "objc2-foundation 0.3.2", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.17", +] + [[package]] name = "pango" version = "0.18.3" @@ -3795,6 +3886,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.35", + "socket2 0.6.1", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.35", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.1", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.42" @@ -4054,7 +4200,7 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", - "hyper-rustls", + "hyper-rustls 0.24.2", "ipnet", "js-sys", "log", @@ -4062,7 +4208,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -4070,13 +4216,13 @@ dependencies = [ "sync_wrapper 0.1.2", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg 0.50.0", ] @@ -4094,16 +4240,21 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.7.0", + "hyper-rustls 0.27.7", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "quinn", + "rustls 0.23.35", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", + "tokio-rustls 0.26.4", "tokio-util", "tower", "tower-http", @@ -4113,6 +4264,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots 1.0.4", ] [[package]] @@ -4174,6 +4326,7 @@ dependencies = [ "tauri-plugin-fs", "tauri-plugin-log", "tauri-plugin-shell", + "tauri-plugin-updater", "tokio", "uuid", ] @@ -4296,6 +4449,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -4339,10 +4498,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -4352,6 +4525,16 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +dependencies = [ + "web-time", + "zeroize", +] + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4362,6 +4545,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -4939,7 +5133,7 @@ dependencies = [ "once_cell", "paste", "percent-encoding", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -4952,7 +5146,7 @@ dependencies = [ "tracing", "url", "uuid", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -5356,6 +5550,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -5576,6 +5781,38 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-updater" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27cbc31740f4d507712550694749572ec0e43bdd66992db7599b89fbfd6b167b" +dependencies = [ + "base64 0.22.1", + "dirs 6.0.0", + "flate2", + "futures-util", + "http 1.3.1", + "infer", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.12.24", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.17", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip", +] + [[package]] name = "tauri-runtime" version = "2.9.1" @@ -5852,7 +6089,17 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls 0.23.35", "tokio", ] @@ -6603,6 +6850,15 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.0" @@ -7274,6 +7530,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.2", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -7442,6 +7708,18 @@ dependencies = [ "syn 2.0.109", ] +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.12.0", + "memchr", +] + [[package]] name = "zvariant" version = "5.8.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 782c99c..64b5e06 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -34,3 +34,4 @@ chrono = { version = "0.4", features = ["serde"] } tauri-plugin-fs = "2" tauri-plugin-log = "2" log = "0.4.28" +tauri-plugin-updater = "2.9.0" diff --git a/src-tauri/src/commands/session.rs b/src-tauri/src/commands/session.rs index 82e4fde..a6de73f 100644 --- a/src-tauri/src/commands/session.rs +++ b/src-tauri/src/commands/session.rs @@ -241,8 +241,8 @@ pub async fn get_providers(_state: State<'_, Arc>>) -> Result anyhow::Result<()> { .setup(|app| { log::debug!("Running Tauri setup"); + // Initialize updater plugin for desktop platforms + #[cfg(desktop)] + { + log::info!("Initializing updater plugin"); + app.handle() + .plugin(tauri_plugin_updater::Builder::new().build())?; + } + #[cfg(debug_assertions)] { log::debug!("Debug mode: initializing devtools"); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ea727fd..a6c6d02 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -5,12 +5,20 @@ "build": { "frontendDist": "../ui-react/dist", "devUrl": "http://localhost:5173", - "beforeDevCommand": "pnpm run dev", - "beforeBuildCommand": "pnpm run build" + "beforeDevCommand": "cd ../ui-react && pnpm run dev", + "beforeBuildCommand": "cd ../ui-react && pnpm run build" }, "bundle": { - "active": false, + "active": true, "targets": "all", + "createUpdaterArtifacts": true, + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ], "fileAssociations": [ { "ext": ["json"], @@ -49,10 +57,19 @@ "dialog:default", "core:event:default", "fs:allow-stat", - "log:default" + "log:default", + "updater:default" ] } ] } + }, + "plugins": { + "updater": { + "endpoints": [ + "https://github.com/wafflestudio/retrochat/releases/latest/download/latest.json" + ], + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQ0NjUyQzkxRjlBRjc2RDkKUldUWmRxLzVrU3hsUkNCSDc1WWpOY2o5dGliS2tBYWppdVN6c1k0OVIwUTVtQk1xb1dRdGJoVU4K" + } } } diff --git a/ui-react/package.json b/ui-react/package.json index d246985..10eeb98 100644 --- a/ui-react/package.json +++ b/ui-react/package.json @@ -48,6 +48,8 @@ "@tauri-apps/api": "^2.9.0", "@tauri-apps/plugin-dialog": "^2.4.2", "@tauri-apps/plugin-fs": "^2.4.2", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-updater": "^2.9.0", "@vercel/analytics": "1.3.1", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", diff --git a/ui-react/pnpm-lock.yaml b/ui-react/pnpm-lock.yaml index 36dcb0b..5c42695 100644 --- a/ui-react/pnpm-lock.yaml +++ b/ui-react/pnpm-lock.yaml @@ -107,6 +107,12 @@ importers: '@tauri-apps/plugin-fs': specifier: ^2.4.2 version: 2.4.4 + '@tauri-apps/plugin-process': + specifier: ^2.3.1 + version: 2.3.1 + '@tauri-apps/plugin-updater': + specifier: ^2.9.0 + version: 2.9.0 '@vercel/analytics': specifier: 1.3.1 version: 1.3.1(react@19.2.0) @@ -1497,6 +1503,12 @@ packages: '@tauri-apps/plugin-fs@2.4.4': resolution: {integrity: sha512-MTorXxIRmOnOPT1jZ3w96vjSuScER38ryXY88vl5F0uiKdnvTKKTtaEjTEo8uPbl4e3gnUtfsDVwC7h77GQLvQ==} + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} + + '@tauri-apps/plugin-updater@2.9.0': + resolution: {integrity: sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -3904,6 +3916,14 @@ snapshots: dependencies: '@tauri-apps/api': 2.9.0 + '@tauri-apps/plugin-process@2.3.1': + dependencies: + '@tauri-apps/api': 2.9.0 + + '@tauri-apps/plugin-updater@2.9.0': + dependencies: + '@tauri-apps/api': 2.9.0 + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.5 diff --git a/ui-react/src/App.tsx b/ui-react/src/App.tsx index 4bf39ec..3671c09 100644 --- a/ui-react/src/App.tsx +++ b/ui-react/src/App.tsx @@ -1,8 +1,12 @@ import { Toaster } from 'sonner' import { SessionManager } from './components/session-manager' import { ThemeProvider } from './components/theme-provider' +import { useUpdater } from './hooks/use-updater' function App() { + // Initialize updater (checks for updates on startup) + useUpdater() + return ( diff --git a/ui-react/src/components/search-view.tsx b/ui-react/src/components/search-view.tsx index b2d5ecc..12f1010 100644 --- a/ui-react/src/components/search-view.tsx +++ b/ui-react/src/components/search-view.tsx @@ -39,8 +39,8 @@ export function SearchView({ searchQuery, onSearchQueryChange, onSessionSelect } }, [searchQuery, performSearch]) return ( -
-
+
+
- -
+ +
{searchQuery.trim().length < 3 ? (
Enter at least 3 characters to search diff --git a/ui-react/src/components/ui/scroll-area.tsx b/ui-react/src/components/ui/scroll-area.tsx index b634b8e..1e3510a 100644 --- a/ui-react/src/components/ui/scroll-area.tsx +++ b/ui-react/src/components/ui/scroll-area.tsx @@ -11,7 +11,7 @@ function ScrollArea({ return ( >) => { + if (!update) return + + setIsDownloading(true) + setDownloadProgress(0) + + const toastId = toast.loading('Downloading update...', { + description: '0%', + }) + + try { + await update.downloadAndInstall((event) => { + if (event.event === 'Started') { + setDownloadProgress(0) + } else if (event.event === 'Progress') { + const progress = Math.round((event.data.downloaded / event.data.contentLength) * 100) + setDownloadProgress(progress) + toast.loading('Downloading update...', { + id: toastId, + description: `${progress}%`, + }) + } + }) + + toast.success('Update installed successfully', { + id: toastId, + description: 'Restarting app...', + }) + + // Relaunch the app after a short delay + setTimeout(async () => { + await relaunch() + }, 1000) + } catch (error) { + console.error('Failed to download and install update:', error) + toast.error('Failed to install update', { + id: toastId, + }) + } finally { + setIsDownloading(false) + } + }, []) + + const checkForUpdates = useCallback( + async (showToast = true) => { + if (isChecking || isDownloading) return + + setIsChecking(true) + + try { + const update = await check() + + if (update?.available) { + const version = update.version + toast.info(`Update available: v${version}`, { + description: 'Click to download and install', + duration: Number.POSITIVE_INFINITY, + action: { + label: 'Update', + onClick: () => downloadAndInstall(update), + }, + cancel: { + label: 'Later', + onClick: () => {}, + }, + }) + } else if (showToast) { + toast.success("You're up to date!") + } + } catch (error) { + console.error('Failed to check for updates:', error) + if (showToast) { + toast.error('Failed to check for updates') + } + } finally { + setIsChecking(false) + } + }, + [isChecking, isDownloading, downloadAndInstall] + ) + + useEffect(() => { + // Check for updates on app startup (after a short delay) + const checkTimeout = setTimeout(() => { + checkForUpdates(false) // silent check on startup + }, 3000) + + return () => clearTimeout(checkTimeout) + }, [checkForUpdates]) + + return { + checkForUpdates, + isChecking, + isDownloading, + downloadProgress, + } +} diff --git a/ui-react/src/lib/api.ts b/ui-react/src/lib/api.ts index a0ab274..abe6813 100644 --- a/ui-react/src/lib/api.ts +++ b/ui-react/src/lib/api.ts @@ -868,7 +868,7 @@ export async function getProviders(): Promise { return await invoke('get_providers') } catch (_error) { console.log('[v0] Using mock data for getProviders') - return ['claude', 'gpt4', 'gemini'] + return ['Claude Code', 'Gemini CLI', 'Codex'] } }