Commit Graph
6 Commits
Author SHA1 Message Date
debpalash 772e3e82b4 feat(uninstall): --app/-RemoveApp removes the prebuilt binary install
The default curl|sh and irm|iex installs now put a real app on disk
(/Applications or ~/Applications, ~/.local/bin/VoiceStudio, MSI product).
Both uninstallers gain an opt-in flag that targets exactly those:
- uninstall.sh --app: adds the app bundle / AppImage to the dry-run plan
- uninstall.ps1 -RemoveApp: resolves the MSI product across HKLM/HKCU/
  WOW6432Node and uninstalls it silently under -Yes
2026-08-22 01:58:39 +05:30
Palash Debnath 93025d9a81 fix(dictation): stop the widget stranding an empty square, repair the swept data dirs (#1398)
The dictation hotkey could leave a blank dark square stuck on the desktop with no way to dismiss it. Three defects compounded: the tray listener's effect depended on [state], so it detached across an await on every state change and a press landing in that gap was lost; an idle pill renders null, so the window Rust had already shown was empty; and the opaque chrome background made that empty window a hard-edged square. Nothing could hide it — dismiss() is only reachable from the X button, Esc, or a post-session timer, none of which exist for a session that never started.

Fixed at the invariant rather than the call sites: the listener subscribes once for the component's lifetime, the widget window's chrome background is transparent, and an idle-but-visible window reconciles itself to hidden. The reconcile is polled (a dropped press changes no React state, so there is nothing to key an effect off) and aborts if its effect is torn down mid-check, so it can never hide a dictation that has just started.

Also in scope:

- The rename sweep had repointed three data-dir literals at a brand-named directory that does not exist, so smoke-test.sh verified a directory the backend never writes and desktop-prod.sh silently stopped clearing backend state on Windows. Both invisible on macOS, where they are usually run. A guard test now pins the assignments specifically.
- The dictation model picker's download sizes were wrong for all seven models, in both directions — Parakeet TDT v3 (the recommended default) understated 180 MB against an actual 670 MB, while the low-RAM fallbacks were overstated threefold, discouraging exactly the choice that would have helped. Measured from the published repos and pinned by a test.
- The 0.6B Parakeet models now decode on more threads, capped by host cores and still overridable.
- uninstall.ps1 gained a UTF-8 BOM (Windows PowerShell 5.1 mis-decodes its non-ASCII output without one), and sponsor.yml lost its last OmniVoice references.
2026-08-07 05:39:41 +05:30
debpalash 08e19e0c1c feat(uninstall): opt-in, content-free app_uninstalled ping in the uninstall scripts
Before deleting anything, uninstall.sh / uninstall.ps1 now send ONE
best-effort `app_uninstalled` event — but only when the user opted in:
consent is read from the same prefs.json the app writes, AND the ping needs
the backend-written analytics_info.json (present only while analytics is
enabled — consent + build token — and removed on opt-out), because the
generic scripts ship no token of their own. Payload is content-free: app
version, OS name, random per-install id. 2-second timeout, silent failure,
one honest console line ('Sending anonymous uninstall ping (you opted in to
analytics).'); not opted in => nothing sent, nothing printed, and the
dry-run never sends either way.

Tested by exercising the bash script for real (fake $HOME + a curl shim
recording argv: consented/not-consented/dry-run/missing-info flows, plus
data-still-deleted-after-ping) and by static contract checks on the ps1
(consent gate, TimeoutSec 2, try/catch, no baked token). docs/install/
uninstall.md documents the behavior (docs-sync).
2026-07-16 19:59:19 +05:30
Palash Debnathandmergetest 4e5d795832 fix(uninstall,storage): remove the saved-env leftover; count sidecar engines in disk usage (#1108)
Two recon findings from the reset work, fixed properly (whole class + tests +
docs), plus the destructive reset path is now exercised end-to-end.

1. ~/.config/omnivoice/env survived every uninstall. The app persists the
   model-cache location (and a possible HF_TOKEN) there via
   backend/core/user_env.py, but the in-app "Remove all data" (uninstall.rs),
   uninstall.sh, and uninstall.ps1 all walked past it — so a reinstall silently
   inherited the old file and redirected downloads to a maybe-deleted location.
   All three now remove it. It's the same expanduser("~/.config/omnivoice/env")
   path on every OS, so the Windows script uses %USERPROFILE%\.config\omnivoice.
   is_recognizably_ours accepts it (contains "omnivoice"); docs tables updated.

2. Disk usage measured the wrong engines dir. storage_report.default_engines_dir()
   returned backend/engines (built-in engine *modules*, no venvs), while sidecar
   installs live in DATA_DIR/engines/<id>. So a multi-GB IndexTTS-2 install was
   invisible in the engine-venv category and rolled into data/"other". Now points
   at DATA_DIR/engines and sizes the WHOLE install (venv + checkout + weights),
   with the data category claiming that subtree so it isn't double-counted.

Reset hardening: extracted purge_scopes() as a pure fs function (no AppHandle),
so the actual delete loop runs in tests against a real on-disk install tree —
"everything" wipes the install but spares the venv/foreign temp/sibling folders,
a settings reset keeps content+config+models, and a poisoned data_dir="$HOME"
deletes NOTHING. This is the live drive-through of the destructive path, minus
the GUI.

Also: gitignore the node_modules symlink form (the directory rule node_modules/
never matched a worktree symlink, so it kept slipping into commits).

Tests: Rust 78 (6 new), storage_report 20 (2 new incl. once-not-twice count +
default-dir guard), frontend 1207, i18n probe green, format+lint clean.

Co-authored-by: mergetest <nizam4103@gmail.com>
2026-07-12 15:39:07 +05:30
0421be966e feat(settings): in-app uninstall — Settings → Storage → Remove all data (#1089) (#1099)
* feat(settings): in-app uninstall — Settings → Storage → "Remove all data"

The v0.3.19 uninstaller was a SCRIPT, which never reaches the people who need
it: anyone who installed the .dmg / .msi / AppImage has no repo to run
scripts/uninstall.sh from — exactly the reporter in #1089, an AppImage user.
"Where is uninstall in the app?" had no answer. Now it does.

New Tauri commands (uninstall.rs):
- uninstall_scan  — every folder this install owns, with real sizes, resolved
  through the same setup.rs helpers the app itself uses, so custom + portable
  locations are cleaned instead of the defaults being assumed.
- uninstall_purge — stops the backend (marking the kill intentional so the #567
  supervisor doesn't respawn one into the directories being deleted), removes
  the folders, and lets the UI quit the app: the Python env it runs on is gone,
  so there is nothing to return to.

This lives in the Rust shell, not the backend, because the biggest thing to
remove is the managed Python environment and the backend is RUNNING FROM IT — a
process can't delete its own interpreter (and Windows locks the files).

Safety: every path must pass is_recognizably_ours() before any remove_dir_all —
absolute, not `/` or $HOME, and carrying an OmniVoice-owned component (unit
tested both ways). The shared Hugging Face cache is reported separately and is
OPT-IN behind its own checkbox with the caveat spelled out: it's the standard HF
cache other ML tools share, so sweeping it up silently would delete models this
app never downloaded. Deleting voices/projects is irreversible, so the confirm
requires TYPING the word, not just a click.

Also fixes a real bug in what shipped in v0.3.19: the scripts and docs missed
where the BACKEND writes its logs — ~/.local/state/OmniVoice on Linux and
%LOCALAPPDATA%\OmniVoice\Logs on Windows (backend_log_path(), backend.rs) — so
every Linux/Windows uninstall left a stray log dir behind. Covered now in the
scripts, the docs, and the in-app scan.

And the scripts now ship as release assets, so cleanup is possible without
launching the app at all.

Rust: 2 new guard tests. Frontend: 6 new tests (the size on the confirm button
must equal what actually gets deleted); suite 1182 passed. Docs synced.

Refs #1089

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(settings): drop token border utilities from UninstallPanel (design guard)

tests/test_no_literal_borders.py::test_no_token_border_utilities_in_jsx is a
backend guard that scans JSX — so a frontend-only test run misses it. It forbids
`border-[var(--chrome-border)]` structural utilities: the app-wide border removal
converted every panel/row frame away from them, and they render a stray hairline
the moment the token doesn't resolve transparent.

Row dividers → spacing + an alternating `--chrome-hover-bg` tint; the opt-in
checkbox card → a background tint; the confirm input → the sanctioned arbitrary
`[border:1px_solid_var(--chrome-border)]` property form the other settings inputs
already use (explicitly not flagged by the guard).

Guard green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: mergetest <nizam4103@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:35:17 +05:30
b1a7ddc374 feat(install): clean uninstaller + a straight answer to "where is my data?" (#1097)
A Linux AppImage user asked which folders to delete to remove OmniVoice and
whether an uninstaller exists (#1089) — they had to guess. They shouldn't have
to: the app is fully local, so uninstalling IS just deleting the folders it
wrote, and we never documented them.

- scripts/uninstall.sh (macOS/Linux) + scripts/uninstall.ps1 (Windows): find
  every OmniVoice folder — app data, the multi-GB managed Python env, config,
  logs — plus, listed SEPARATELY because it is a shared cache, the Hugging Face
  model cache. Print each with its size as a DRY RUN and stop; delete only on
  --yes (--models / -Models to include the shared cache). They honor the same
  env overrides the app reads (OMNIVOICE_DATA_DIR, OMNIVOICE_CACHE_DIR,
  HF_HOME, HF_HUB_CACHE), and never touch the app binary or anything outside
  the paths they list.
- docs/install/uninstall.md: the complete per-platform path table (what each
  folder holds and how big it is), the shared-HF-cache caveat, custom/portable
  locations, per-platform steps to remove the app itself, and what to keep if
  you plan to reinstall.
- Linked from the README FAQ, SUPPORT.md, and install troubleshooting.

Paths mirror backend/core/config.py + frontend/src-tauri/src/setup.rs.
Verified on macOS: dry-run lists the real dirs; sandboxed HOME runs confirm
--yes removes app folders while KEEPING the shared cache, --models removes it,
and the env overrides retarget correctly.

Closes #1089

Co-authored-by: mergetest <nizam4103@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 12:14:39 +05:30