From d2c19e1cedbf589dac116ab32130619647709d68 Mon Sep 17 00:00:00 2001 From: Daniele Dellafiore <66707+ildella@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:32:44 +0200 Subject: [PATCH 1/2] feat: signed AppImage updater artifacts and latest.json Wire Tauri updater plugin, release-linux signing, write-latest-json, and UI update check. Keep createUpdaterArtifacts off in tauri.conf so unsigned PR AppImage CI still works. --- .github/workflows/release-linux.yml | 29 ++- AGENTS.md | 2 +- LINUX.md | 10 +- README.md | 6 +- app/package-lock.json | 33 +++ app/package.json | 5 + app/src/routes/+page.svelte | 80 ++++++- scripts/write-latest-json.sh | 29 +++ src-tauri/Cargo.lock | 326 +++++++++++++++++++++++++++- src-tauri/Cargo.toml | 2 + src-tauri/capabilities/default.json | 6 +- src-tauri/src/main.rs | 2 + src-tauri/tauri.conf.json | 8 + 13 files changed, 520 insertions(+), 18 deletions(-) create mode 100755 scripts/write-latest-json.sh diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index e0d4649..9209d89 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -1,5 +1,6 @@ # Build Linux GUI packages with the Node sidecar inside. -# PR CI stays --no-bundle; this job is tags (and manual dispatch) only. +# PR CI stays unsigned; this job is tags (and manual dispatch) only. +# AppImage is the auto-update channel (signed). deb/rpm stay distro packages. name: release-linux on: @@ -14,9 +15,12 @@ concurrency: group: release-linux-${{ github.ref }} cancel-in-progress: true +env: + APPIMAGE_EXTRACT_AND_RUN: "1" + jobs: linux: - name: deb + rpm (ubuntu-24.04) + name: deb + rpm + appimage (ubuntu-24.04) runs-on: ubuntu-24.04 steps: @@ -47,8 +51,16 @@ jobs: npm ci npm --prefix app ci - - name: Bundle deb and rpm - run: npx tauri build -b deb,rpm + - name: Bundle deb, rpm, and signed AppImage + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: | + test -n "$TAURI_SIGNING_PRIVATE_KEY" + npx tauri build -b deb,rpm,appimage --config '{"bundle":{"createUpdaterArtifacts":true}}' + + - name: Write latest.json + run: sh scripts/write-latest-json.sh "${{ github.ref_name }}" "${{ github.repository }}" - uses: actions/upload-artifact@v7 with: @@ -56,6 +68,9 @@ jobs: path: | src-tauri/target/release/bundle/deb/*.deb src-tauri/target/release/bundle/rpm/*.rpm + src-tauri/target/release/bundle/appimage/*.AppImage + src-tauri/target/release/bundle/appimage/*.AppImage.sig + latest.json - name: Publish GitHub Release if: startsWith(github.ref, 'refs/tags/') @@ -65,8 +80,14 @@ jobs: gh release create "${{ github.ref_name }}" \ src-tauri/target/release/bundle/deb/*.deb \ src-tauri/target/release/bundle/rpm/*.rpm \ + src-tauri/target/release/bundle/appimage/*.AppImage \ + src-tauri/target/release/bundle/appimage/*.AppImage.sig \ + latest.json \ --generate-notes --verify-tag \ || gh release upload "${{ github.ref_name }}" \ src-tauri/target/release/bundle/deb/*.deb \ src-tauri/target/release/bundle/rpm/*.rpm \ + src-tauri/target/release/bundle/appimage/*.AppImage \ + src-tauri/target/release/bundle/appimage/*.AppImage.sig \ + latest.json \ --clobber diff --git a/AGENTS.md b/AGENTS.md index 74c4427..f4023ec 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,7 @@ Crucial reminders for future sessions. See LINUX.md for full architecture. - **Node ≥ 20**, Rust, **mpv** (playback), `wl-paste`/`xclip`/`xsel` (clipboard). - Root deps: `@tauri-apps/cli`. App and sidecar have their own `package.json`. - Dev: `npm run dev` starts sidecar + `tauri dev`. `beforeDevCommand` runs from project root with `--prefix app`. -- CI: `npm run build:ci` (`tauri build --no-bundle`) on PRs (Ubuntu 24.04, Node 26). Local GUI package is `npm run build:linux` (`.deb` only). `npm run build:appimage` / `npm run build:rpm` for the other formats. Tag workflow still ships deb+rpm. `beforeBundleCommand` stages `src-tauri/sidecar-bundle/`. +- CI: `npm run build:ci` (`tauri build --no-bundle`) on PRs (Ubuntu 24.04, Node 26). Local GUI package is `npm run build:linux` (`.deb` only). `npm run build:appimage` / `npm run build:rpm` for the other formats. Tag workflow ships deb+rpm+signed AppImage + `latest.json`. Do **not** set `createUpdaterArtifacts` in `tauri.conf.json` (breaks unsigned PR AppImage CI). Sign with the Say It key (`~/.tauri/sayit.key`), never the Nucube `TAURI_SIGNING_*` env. `beforeBundleCommand` stages `src-tauri/sidecar-bundle/`. - GUI binary is `sayit-desktop`. CLI remains `sayit`. - Sidecar search order: `$SAYIT_SIDECAR_DIR` → bundled resources → `~/.local/share/sayit/sidecar`. diff --git a/LINUX.md b/LINUX.md index 6b45be0..c4e41aa 100644 --- a/LINUX.md +++ b/LINUX.md @@ -160,6 +160,12 @@ disk. Anything that needs the API should resolve the token the same way. is `sayit-desktop` so it never shadows the CLI `sayit`. Spawn order: `$SAYIT_SIDECAR_DIR` → bundled resources → `~/.local/share/sayit/sidecar`. If 7878 is already healthy, the GUI does not spawn a second engine. +- Auto-update is **AppImage only** (`tauri-plugin-updater`). The tag + workflow signs the AppImage with the Say It key (CI secrets + `TAURI_SIGNING_PRIVATE_KEY` / `_PASSWORD` — not another project's + global env) and uploads `latest.json`. `createUpdaterArtifacts` is + enabled only on that job so PR AppImage CI stays unsigned. Settings + → Check for updates is user-initiated; it is not telemetry. ## 8. Known limitations (vs the original) @@ -183,7 +189,7 @@ disk. Anything that needs the API should resolve the token the same way. - **engine.js is a boundary** — engine-agnostic interface out, kokoro-js in. - **Models come from the catalog** — do not hardcode Hugging Face ids in the UI. - **Offline after first model download**; no analytics, no telemetry, no - passive clipboard monitoring (the original's privacy posture is part of - the product). + passive clipboard monitoring. AppImage update checks hit GitHub only + when the user clicks Check for updates. - CLI command surface stays compatible with the original where the feature exists. diff --git a/README.md b/README.md index f476ab0..bef9512 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,7 @@ The Linux GUI is an **AppImage**. It embeds the sidecar. Needs **Node ≥ 20** and **mpv** on the machine (same as the CLI). No sudo. Download it from -[Releases](https://github.com/ildella/sayit/releases) (or the Linux CI -artifact), then: +[Releases](https://github.com/ildella/sayit/releases), then: ```sh chmod +x SayIt-*.AppImage @@ -82,6 +81,9 @@ chmod +x SayIt-*.AppImage The window binary is `sayit-desktop`; it does not replace the CLI `sayit`. You can run both: whoever starts first owns port 7878; the other connects. +Auto-update (Settings → Check for updates) only applies to the AppImage. +`.deb` / `.rpm` stay distro packages. + ### Terminal The install includes a `sayit` CLI for speech, playback, models, and diff --git a/app/package-lock.json b/app/package-lock.json index 87b0ffc..b819638 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -7,6 +7,11 @@ "": { "name": "sayit-app", "version": "0.1.0", + "dependencies": { + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-updater": "^2.11.0" + }, "devDependencies": { "@sveltejs/adapter-static": "^3.0.8", "@sveltejs/kit": "^2.16.0", @@ -1032,6 +1037,34 @@ "vite": "^6.0.0" } }, + "node_modules/@tauri-apps/api": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/plugin-process": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz", + "integrity": "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, + "node_modules/@tauri-apps/plugin-updater": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.11.0.tgz", + "integrity": "sha512-AE36XkOoSna24G40jZMY15nzAnkXEPL/73tGoseGrtGOHuI/cZwWzHpZFLjKXDPgzYZ435z1gHu28LgrsBwIxQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.11.0" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", diff --git a/app/package.json b/app/package.json index 4bdda4d..17dcbf0 100644 --- a/app/package.json +++ b/app/package.json @@ -14,5 +14,10 @@ "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.19.0", "vite": "^6.0.0" + }, + "dependencies": { + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-updater": "^2.11.0" } } diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index afafa0a..255aa5e 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -7,8 +7,18 @@ let text = $state(''); let voice = $state('af_heart'); let speed = $state(1.0); - - onMount(() => { initStore(); }); + let appVersion = $state(''); + let updatePhase = $state('idle'); + let updateInfo = $state(null); + let downloadPct = $state(0); + + onMount(() => { + initStore(); + import('@tauri-apps/api/app') + .then(({ getVersion }) => getVersion()) + .then((v) => { appVersion = v; }) + .catch(() => {}); + }); // Fill the speak-page pickers once voices arrive. Do not assign only in // onMount: a second initStore() used to return before voices existed, so @@ -114,6 +124,50 @@ await api.deleteEntry(id); appState.history = appState.history.filter((e) => e.id !== id); } + + async function checkForUpdates() { + updatePhase = 'checking'; + updateInfo = null; + try { + const { check } = await import('@tauri-apps/plugin-updater'); + const update = await check(); + if (!update) { + updatePhase = 'none'; + return; + } + updateInfo = update; + updatePhase = 'available'; + } catch (err) { + updatePhase = 'error'; + appState.error = err.message; + } + } + + async function installUpdate() { + if (!updateInfo) return; + updatePhase = 'downloading'; + downloadPct = 0; + try { + let downloaded = 0; + let total = 0; + await updateInfo.downloadAndInstall((event) => { + if (event.event === 'Started') total = event.data.contentLength ?? 0; + if (event.event === 'Progress') { + downloaded += event.data.chunkLength; + downloadPct = total ? Math.min(100, Math.round((downloaded / total) * 100)) : 0; + } + if (event.event === 'Finished') { + downloadPct = 100; + updatePhase = 'installing'; + } + }); + const { relaunch } = await import('@tauri-apps/plugin-process'); + await relaunch(); + } catch (err) { + updatePhase = 'error'; + appState.error = err.message; + } + }
@@ -342,6 +396,28 @@ /> + {#if appVersion} +

Updates

+

AppImage only. Version {appVersion}. Checks GitHub Releases when you ask — nothing is sent in the background.

+ {#if updatePhase === 'idle' || updatePhase === 'error'} + + {:else if updatePhase === 'checking'} +

Checking…

+ {:else if updatePhase === 'none'} +

You are on the latest version.

+ + {:else if updatePhase === 'available'} +

Version {updateInfo.version} is available.

+ {#if updateInfo.body}

{updateInfo.body}

{/if} + + {:else if updatePhase === 'downloading'} +

Downloading… {downloadPct}%

+
+ {:else if updatePhase === 'installing'} +

Installing… the app will restart.

+ {/if} + {/if} +

Hotkeys & clipboard

Ctrl+Alt+V speaks the clipboard from anywhere diff --git a/scripts/write-latest-json.sh b/scripts/write-latest-json.sh new file mode 100755 index 0000000..9408504 --- /dev/null +++ b/scripts/write-latest-json.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# Build latest.json for tauri-plugin-updater from a signed AppImage + .sig. +set -eu + +tag="$1" +repo="${2:-ildella/sayit}" +bundle="${3:-src-tauri/target/release/bundle/appimage}" +out="${4:-latest.json}" + +appimage=$(ls "$bundle"/*.AppImage | head -n 1) +sig=$(ls "$bundle"/*.AppImage.sig | head -n 1) +name=$(basename "$appimage") +version=$(node -p "require('./src-tauri/tauri.conf.json').version") +pub_date=$(date -u +%Y-%m-%dT%H:%M:%SZ) +notes=$(git log -1 --pretty=%s) +url="https://github.com/${repo}/releases/download/${tag}/${name}" +signature=$(cat "$sig") + +node -e ' +const [version, notes, pub_date, url, signature] = process.argv.slice(1); +process.stdout.write(JSON.stringify({ + version, + notes, + pub_date, + platforms: { "linux-x86_64": { url, signature } }, +}, null, 2) + "\n"); +' "$version" "$notes" "$pub_date" "$url" "$signature" > "$out" + +echo "wrote $out for $name ($version)" diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 4c3686f..ec8899a 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -47,6 +47,15 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "atk" version = "0.18.2" @@ -584,6 +593,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.119", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -829,6 +849,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + [[package]] name = "find-msvc-tools" version = "0.1.10" @@ -1759,6 +1789,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.20", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.119", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -1954,6 +2014,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minisign-verify" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2203,6 +2269,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags 2.13.1", "block2", + "libc", "objc2", "objc2-core-foundation", ] @@ -2218,6 +2285,18 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0" +dependencies = [ + "bitflags 2.13.1", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.3.2" @@ -2330,6 +2409,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "osakit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b" +dependencies = [ + "objc2", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 2.0.20", +] + [[package]] name = "pango" version = "0.18.3" @@ -2833,15 +2926,20 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "js-sys", "log", "percent-encoding", "pin-project-lite", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", "sync_wrapper", "tokio", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -2909,6 +3007,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" version = "1.15.1" @@ -2919,6 +3029,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni 0.22.4", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.14" @@ -2963,6 +3100,8 @@ dependencies = [ "tauri", "tauri-build", "tauri-plugin-global-shortcut", + "tauri-plugin-process", + "tauri-plugin-updater", ] [[package]] @@ -3277,6 +3416,22 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" +[[package]] +name = "simd_cesu8" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" version = "1.0.3" @@ -3510,7 +3665,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", + "jni 0.21.1", "libc", "log", "ndk", @@ -3543,6 +3698,17 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "target-lexicon" version = "0.12.16" @@ -3566,7 +3732,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -3693,6 +3859,49 @@ dependencies = [ "thiserror 2.0.20", ] +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + +[[package]] +name = "tauri-plugin-updater" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28d8cabdeb0564f03ae261963de4bc3d98321cd3d213e76a81b7d344e5df606" +dependencies = [ + "base64 0.22.1", + "dirs", + "flate2", + "futures-util", + "http", + "infer", + "log", + "minisign-verify", + "osakit", + "percent-encoding", + "reqwest 0.13.4", + "rustls", + "semver", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-plugin", + "tempfile", + "thiserror 2.0.20", + "time", + "tokio", + "url", + "windows-sys 0.60.2", + "zip", +] + [[package]] name = "tauri-runtime" version = "2.11.3" @@ -3703,7 +3912,7 @@ dependencies = [ "dpi", "gtk", "http", - "jni", + "jni 0.21.1", "objc2", "objc2-ui-kit", "objc2-web-kit", @@ -3726,7 +3935,7 @@ checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" dependencies = [ "gtk", "http", - "jni", + "jni 0.21.1", "log", "objc2", "objc2-app-kit", @@ -4500,6 +4709,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.9" @@ -4768,6 +4986,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -4801,13 +5028,30 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + [[package]] name = "windows-threading" version = "0.1.0" @@ -4838,6 +5082,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -4850,6 +5100,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -4862,12 +5118,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -4880,6 +5148,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -4892,6 +5166,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4904,6 +5184,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -4916,6 +5202,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.5.40" @@ -4980,7 +5272,7 @@ dependencies = [ "gtk", "http", "javascriptcore-rs", - "jni", + "jni 0.21.1", "libc", "ndk", "objc2", @@ -5044,6 +5336,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + [[package]] name = "xkeysym" version = "0.2.1" @@ -5133,6 +5435,18 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "zip" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1" +dependencies = [ + "arbitrary", + "crc32fast", + "indexmap 2.14.0", + "memchr", +] + [[package]] name = "zmij" version = "1.0.23" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5c9d608..eefeb94 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -16,6 +16,8 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" reqwest = { version = "0.12", features = ["json", "rustls-tls", "blocking"] } dirs = "6" +tauri-plugin-updater = "2.11.0" +tauri-plugin-process = "2.3.1" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index e27a472..b34a8fc 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -3,5 +3,9 @@ "identifier": "default", "description": "Default capabilities for the main window", "windows": ["main"], - "permissions": ["core:default"] + "permissions": [ + "core:default", + "updater:default", + "process:default" + ] } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 26d9fd7..23d4c2d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -375,6 +375,8 @@ fn main() { let shutdown = Arc::new(AtomicBool::new(false)); tauri::Builder::default() + .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_process::init()) .plugin( tauri_plugin_global_shortcut::Builder::new() .with_handler(|app, _shortcut, event| { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2b326e5..a0be31f 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -46,5 +46,13 @@ "resources": { "sidecar-bundle": "sidecar" } + }, + "plugins": { + "updater": { + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDc2RTRBNjUyMTc0QTYyOUMKUldTY1lrb1hVcWJrZGpSdUwzYzVzWEdWSFdTMmJGT3l3Q1VlOGxDdU9uK3ZLTkdJbkVuaG1WaU0K", + "endpoints": [ + "https://github.com/ildella/sayit/releases/latest/download/latest.json" + ] + } } } From 29dcaa142df136eb33b6a07681bf4ca69d125964 Mon Sep 17 00:00:00 2001 From: Daniele Dellafiore <66707+ildella@users.noreply.github.com> Date: Thu, 10 Sep 2026 18:11:57 +0200 Subject: [PATCH 2/2] feat: json5 config and tag-triggered releases Version comes from package.json; bump it with Cargo.toml then tag v*. PR CI builds unsigned deb/rpm/AppImage. tauri-action ships on tags. --- .github/workflows/ci-linux.yml | 20 ++++++---- .github/workflows/release-linux.yml | 52 ++++++------------------- AGENTS.md | 4 +- LINUX.md | 17 ++++---- scripts/write-latest-json.sh | 29 -------------- src-tauri/Cargo.lock | 60 +++++++++++++++++++++++++++++ src-tauri/Cargo.toml | 4 +- src-tauri/tauri.conf.json | 58 ---------------------------- src-tauri/tauri.conf.json5 | 59 ++++++++++++++++++++++++++++ 9 files changed, 157 insertions(+), 146 deletions(-) delete mode 100755 scripts/write-latest-json.sh delete mode 100644 src-tauri/tauri.conf.json create mode 100644 src-tauri/tauri.conf.json5 diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index b7e77b6..4a1808b 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -1,4 +1,5 @@ -# Linux CI: AppImage with sidecar inside. macOS/Windows stay --no-bundle. +# Linux CI: full unsigned bundles (deb + rpm + AppImage). +# Tags ship signed artifacts via release-linux.yml. name: ci-linux on: @@ -16,16 +17,16 @@ env: jobs: build: - name: appimage (ubuntu-24.04) + name: deb + rpm + appimage (ubuntu-24.04) runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 - - name: Install Linux WebKitGTK deps + - name: Install Linux WebKitGTK + rpm tooling run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf + sudo apt-get install -y libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf rpm - uses: actions/setup-node@v7 with: @@ -47,8 +48,11 @@ jobs: npm ci npm --prefix app ci - - name: Bundle AppImage - run: npm run build:appimage + - name: Bundle Linux packages + run: npx tauri build -b deb,rpm,appimage - - name: Check AppImage - run: ls -lh src-tauri/target/release/bundle/appimage/*.AppImage + - name: Check bundles + run: | + ls -lh src-tauri/target/release/bundle/deb/*.deb + ls -lh src-tauri/target/release/bundle/rpm/*.rpm + ls -lh src-tauri/target/release/bundle/appimage/*.AppImage diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index 9209d89..efe9114 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -1,12 +1,10 @@ -# Build Linux GUI packages with the Node sidecar inside. -# PR CI stays unsigned; this job is tags (and manual dispatch) only. -# AppImage is the auto-update channel (signed). deb/rpm stay distro packages. +# Tag v* → signed Linux packages + GitHub Release + updater latest.json. +# PR/master CI stays unsigned (ci-linux.yml). Do not merge the two. name: release-linux on: push: tags: ["v*"] - workflow_dispatch: permissions: contents: write @@ -20,7 +18,7 @@ env: jobs: linux: - name: deb + rpm + appimage (ubuntu-24.04) + name: publish (ubuntu-24.04) runs-on: ubuntu-24.04 steps: @@ -51,43 +49,15 @@ jobs: npm ci npm --prefix app ci - - name: Bundle deb, rpm, and signed AppImage + - uses: tauri-apps/tauri-action@v1 env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - run: | - test -n "$TAURI_SIGNING_PRIVATE_KEY" - npx tauri build -b deb,rpm,appimage --config '{"bundle":{"createUpdaterArtifacts":true}}' - - - name: Write latest.json - run: sh scripts/write-latest-json.sh "${{ github.ref_name }}" "${{ github.repository }}" - - - uses: actions/upload-artifact@v7 with: - name: linux-packages - path: | - src-tauri/target/release/bundle/deb/*.deb - src-tauri/target/release/bundle/rpm/*.rpm - src-tauri/target/release/bundle/appimage/*.AppImage - src-tauri/target/release/bundle/appimage/*.AppImage.sig - latest.json - - - name: Publish GitHub Release - if: startsWith(github.ref, 'refs/tags/') - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release create "${{ github.ref_name }}" \ - src-tauri/target/release/bundle/deb/*.deb \ - src-tauri/target/release/bundle/rpm/*.rpm \ - src-tauri/target/release/bundle/appimage/*.AppImage \ - src-tauri/target/release/bundle/appimage/*.AppImage.sig \ - latest.json \ - --generate-notes --verify-tag \ - || gh release upload "${{ github.ref_name }}" \ - src-tauri/target/release/bundle/deb/*.deb \ - src-tauri/target/release/bundle/rpm/*.rpm \ - src-tauri/target/release/bundle/appimage/*.AppImage \ - src-tauri/target/release/bundle/appimage/*.AppImage.sig \ - latest.json \ - --clobber + tagName: ${{ github.ref_name }} + releaseName: ${{ github.ref_name }} + generateReleaseNotes: true + args: >- + -b deb,rpm,appimage + --config {"bundle":{"createUpdaterArtifacts":true}} diff --git a/AGENTS.md b/AGENTS.md index f4023ec..eb1f24f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,7 +7,9 @@ Crucial reminders for future sessions. See LINUX.md for full architecture. - **Node ≥ 20**, Rust, **mpv** (playback), `wl-paste`/`xclip`/`xsel` (clipboard). - Root deps: `@tauri-apps/cli`. App and sidecar have their own `package.json`. - Dev: `npm run dev` starts sidecar + `tauri dev`. `beforeDevCommand` runs from project root with `--prefix app`. -- CI: `npm run build:ci` (`tauri build --no-bundle`) on PRs (Ubuntu 24.04, Node 26). Local GUI package is `npm run build:linux` (`.deb` only). `npm run build:appimage` / `npm run build:rpm` for the other formats. Tag workflow ships deb+rpm+signed AppImage + `latest.json`. Do **not** set `createUpdaterArtifacts` in `tauri.conf.json` (breaks unsigned PR AppImage CI). Sign with the Say It key (`~/.tauri/sayit.key`), never the Nucube `TAURI_SIGNING_*` env. `beforeBundleCommand` stages `src-tauri/sidecar-bundle/`. +- Config is `src-tauri/tauri.conf.json5` (`config-json5` on `tauri` + `tauri-build`). Version is `../package.json`; bump **root `package.json` and `src-tauri/Cargo.toml` together**, then tag `v*`. +- CI Linux (`ci-linux.yml`): unsigned `deb,rpm,appimage` on PRs and `master`. macOS/Windows stay `npm run build:ci` (`tauri build --no-bundle`). Local: `build:linux` (deb), `build:appimage`, `build:rpm`. +- Release: push a `v*` tag → `release-linux.yml` (`tauri-action`, signed AppImage + `latest.json`). Do **not** set `createUpdaterArtifacts` in the config (breaks unsigned PR CI). Sign with the Say It key (`~/.tauri/sayit.key`), never the Nucube `TAURI_SIGNING_*` env. `beforeBundleCommand` stages `src-tauri/sidecar-bundle/`. - GUI binary is `sayit-desktop`. CLI remains `sayit`. - Sidecar search order: `$SAYIT_SIDECAR_DIR` → bundled resources → `~/.local/share/sayit/sidecar`. diff --git a/LINUX.md b/LINUX.md index c4e41aa..ea3c950 100644 --- a/LINUX.md +++ b/LINUX.md @@ -153,19 +153,22 @@ disk. Anything that needs the API should resolve the token the same way. produces an ENOENT two directories away from the real problem. - The tray icon has a 1×1 transparent fallback (`Image::new_owned`) because `default_window_icon()` can be `None` in some bundling configurations. -- CSP in `tauri.conf.json` must keep `connect-src http://127.0.0.1:7878` or +- Config is `src-tauri/tauri.conf.json5`. App version is `version: '../package.json'`; + bump root `package.json` and `src-tauri/Cargo.toml` together before a `v*` tag. +- CSP in `tauri.conf.json5` must keep `connect-src http://127.0.0.1:7878` or the webview can't reach the sidecar. - GUI packages (`npm run build:linux`) run `scripts/prepare-sidecar-bundle.sh` then embed that tree as `bundle.resources` → `sidecar/`. The window binary is `sayit-desktop` so it never shadows the CLI `sayit`. Spawn order: `$SAYIT_SIDECAR_DIR` → bundled resources → `~/.local/share/sayit/sidecar`. If 7878 is already healthy, the GUI does not spawn a second engine. -- Auto-update is **AppImage only** (`tauri-plugin-updater`). The tag - workflow signs the AppImage with the Say It key (CI secrets - `TAURI_SIGNING_PRIVATE_KEY` / `_PASSWORD` — not another project's - global env) and uploads `latest.json`. `createUpdaterArtifacts` is - enabled only on that job so PR AppImage CI stays unsigned. Settings - → Check for updates is user-initiated; it is not telemetry. +- Auto-update is **AppImage only** (`tauri-plugin-updater`). Push a `v*` + tag; `release-linux.yml` runs `tauri-action`, which signs the AppImage + (CI secrets `TAURI_SIGNING_PRIVATE_KEY` / `_PASSWORD` — not another + project's global env), creates the GitHub Release, and uploads + `latest.json`. `createUpdaterArtifacts` is only in that job's `--config` + so PR AppImage CI stays unsigned. Settings → Check for updates is + user-initiated; it is not telemetry. ## 8. Known limitations (vs the original) diff --git a/scripts/write-latest-json.sh b/scripts/write-latest-json.sh deleted file mode 100755 index 9408504..0000000 --- a/scripts/write-latest-json.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# Build latest.json for tauri-plugin-updater from a signed AppImage + .sig. -set -eu - -tag="$1" -repo="${2:-ildella/sayit}" -bundle="${3:-src-tauri/target/release/bundle/appimage}" -out="${4:-latest.json}" - -appimage=$(ls "$bundle"/*.AppImage | head -n 1) -sig=$(ls "$bundle"/*.AppImage.sig | head -n 1) -name=$(basename "$appimage") -version=$(node -p "require('./src-tauri/tauri.conf.json').version") -pub_date=$(date -u +%Y-%m-%dT%H:%M:%SZ) -notes=$(git log -1 --pretty=%s) -url="https://github.com/${repo}/releases/download/${tag}/${name}" -signature=$(cat "$sig") - -node -e ' -const [version, notes, pub_date, url, signature] = process.argv.slice(1); -process.stdout.write(JSON.stringify({ - version, - notes, - pub_date, - platforms: { "linux-x86_64": { url, signature } }, -}, null, 2) + "\n"); -' "$version" "$notes" "$pub_date" "$url" "$signature" > "$out" - -echo "wrote $out for $name ($version)" diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ec8899a..12601e6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1870,6 +1870,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + [[package]] name = "jsonptr" version = "0.6.3" @@ -2477,6 +2488,48 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pest_meta" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e" +dependencies = [ + "pest", +] + [[package]] name = "phf" version = "0.13.1" @@ -3969,6 +4022,7 @@ dependencies = [ "http", "infer", "json-patch", + "json5", "log", "memchr", "phf", @@ -4391,6 +4445,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unic-char-property" version = "0.9.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index eefeb94..dca8c86 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" build = "build.rs" # ← add this [build-dependencies] # ← add this section -tauri-build = { version = "2", features = [] } +tauri-build = { version = "2", features = ["config-json5"] } [dependencies] -tauri = { version = "2", features = ["tray-icon"] } +tauri = { version = "2", features = ["tray-icon", "config-json5"] } tauri-plugin-global-shortcut = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json deleted file mode 100644 index a0be31f..0000000 --- a/src-tauri/tauri.conf.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "$schema": "https://schema.tauri.app/config/2", - "productName": "SayIt", - "mainBinaryName": "sayit-desktop", - "version": "0.1.0", - "identifier": "dev.sayit.linux", - "build": { - "frontendDist": "../app/build", - "devUrl": "http://localhost:1420", - "beforeDevCommand": "npm --prefix app run dev", - "beforeBuildCommand": "node -e \"require('fs').mkdirSync('src-tauri/sidecar-bundle',{recursive:true})\" && npm --prefix app run build", - "beforeBundleCommand": "sh scripts/prepare-sidecar-bundle.sh" - }, - "app": { - "withGlobalTauri": true, - "windows": [ - { - "label": "main", - "title": "Say It", - "width": 760, - "height": 640, - "minWidth": 520, - "minHeight": 480, - "visible": true - } - ], - "security": { - "csp": "default-src 'self'; connect-src 'self' http://127.0.0.1:7878; img-src 'self' data:" - } - }, - "bundle": { - "active": true, - "targets": ["deb"], - "category": "Utility", - "homepage": "https://github.com/ildella/sayit", - "licenseFile": "../LICENSE", - "shortDescription": "Private, local text-to-speech", - "longDescription": "Say It turns copied text into speech with open models running locally.", - "icon": [ - "icons/32x32.png", - "icons/128x128.png", - "icons/256x256.png", - "icons/icon.png", - "icons/icon.ico" - ], - "resources": { - "sidecar-bundle": "sidecar" - } - }, - "plugins": { - "updater": { - "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDc2RTRBNjUyMTc0QTYyOUMKUldTY1lrb1hVcWJrZGpSdUwzYzVzWEdWSFdTMmJGT3l3Q1VlOGxDdU9uK3ZLTkdJbkVuaG1WaU0K", - "endpoints": [ - "https://github.com/ildella/sayit/releases/latest/download/latest.json" - ] - } - } -} diff --git a/src-tauri/tauri.conf.json5 b/src-tauri/tauri.conf.json5 new file mode 100644 index 0000000..1c6ea5d --- /dev/null +++ b/src-tauri/tauri.conf.json5 @@ -0,0 +1,59 @@ +{ + $schema: "https://schema.tauri.app/config/2", + productName: "SayIt", + mainBinaryName: "sayit-desktop", + // App version lives in the repo-root package.json. Bump that and Cargo.toml together. + version: "../package.json", + identifier: "dev.sayit.linux", + build: { + frontendDist: "../app/build", + devUrl: "http://localhost:1420", + beforeDevCommand: "npm --prefix app run dev", + beforeBuildCommand: "node -e \"require('fs').mkdirSync('src-tauri/sidecar-bundle',{recursive:true})\" && npm --prefix app run build", + beforeBundleCommand: "sh scripts/prepare-sidecar-bundle.sh", + }, + app: { + withGlobalTauri: true, + windows: [ + { + label: "main", + title: "Say It", + width: 760, + height: 640, + minWidth: 520, + minHeight: 480, + visible: true, + }, + ], + security: { + csp: "default-src 'self'; connect-src 'self' http://127.0.0.1:7878; img-src 'self' data:", + }, + }, + bundle: { + active: true, + targets: ["deb"], + category: "Utility", + homepage: "https://github.com/ildella/sayit", + licenseFile: "../LICENSE", + shortDescription: "Private, local text-to-speech", + longDescription: "Say It turns copied text into speech with open models running locally.", + icon: [ + "icons/32x32.png", + "icons/128x128.png", + "icons/256x256.png", + "icons/icon.png", + "icons/icon.ico", + ], + resources: { + "sidecar-bundle": "sidecar", + }, + }, + plugins: { + updater: { + pubkey: "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDc2RTRBNjUyMTc0QTYyOUMKUldTY1lrb1hVcWJrZGpSdUwzYzVzWEdWSFdTMmJGT3l3Q1VlOGxDdU9uK3ZLTkdJbkVuaG1WaU0K", + endpoints: [ + "https://github.com/ildella/sayit/releases/latest/download/latest.json", + ], + }, + }, +}