@arcforgelabs/dictate 2026.6.20 → 2026.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,18 +6,17 @@ Desktop dictation that types into the focused app.
6
6
  speak, and it transcribes into whatever app you are already using.
7
7
 
8
8
  Current status: early desktop app. Linux installs, Windows 11 source installs,
9
- tray controls, startup integration, recent history, model selection, API key
10
- storage, update, and uninstall paths are implemented. Windows Store/signed
11
- installer packaging is being prepared; see `docs/goal.md`.
9
+ tray controls, startup integration, local dictation history, update, and
10
+ uninstall paths are implemented. Microsoft Store packaging and submission
11
+ automation are maintained separately from GitHub releases; see `docs/GOALS.md`.
12
12
 
13
13
  ## Install
14
14
 
15
15
  Windows 11 normal install:
16
16
 
17
- The target public channel is Microsoft Store distribution. Until that listing is
18
- ready, use the GitHub release installer artifacts for internal validation only.
19
- The PowerShell bootstrap installer below is a developer/source path, not the
20
- normal public install route.
17
+ The target public channel is Microsoft Store distribution. GitHub release
18
+ artifacts and the PowerShell bootstrap installer below are developer/source or
19
+ internal validation paths, not the normal public Windows install route.
21
20
 
22
21
  Windows developer/source install from the hosted bootstrap:
23
22
 
@@ -27,13 +26,13 @@ powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/
27
26
 
28
27
  Open **Dictate** from the Start Menu after install.
29
28
 
30
- Ubuntu/Debian source install:
29
+ Linux default install (per-user, no sudo for app updates):
31
30
 
32
31
  ```bash
33
32
  ./install-ubuntu.sh
34
33
  ```
35
34
 
36
- Generic Linux source install:
35
+ Generic Linux user install:
37
36
 
38
37
  ```bash
39
38
  ./install.sh
@@ -41,6 +40,14 @@ Generic Linux source install:
41
40
 
42
41
  Open **Dictate** from the app launcher after install.
43
42
 
43
+ Linux system package install is also supported when you explicitly want a
44
+ machine-wide `.deb` install:
45
+
46
+ ```bash
47
+ DICTATE_BUNDLES=deb scripts/build-linux-desktop.sh
48
+ ./install.sh --system
49
+ ```
50
+
44
51
  Windows developer/source install, from the repo/source directory:
45
52
 
46
53
  ```powershell
@@ -60,15 +67,13 @@ npx @arcforgelabs/dictate install
60
67
 
61
68
  ```text
62
69
  Open Dictate
63
- Select Model
64
- Set API key if using a hosted model
65
- Set push-to-talk shortcut if desired
70
+ Use the mic button or configured push-to-talk shortcut
66
71
  Hold shortcut, speak, release
67
- Review Recent History when needed
72
+ Review Dictations when needed
68
73
  ```
69
74
 
70
75
  By default, Dictate installs a normal app launcher entry and starts on sign-in.
71
- Startup can be changed from Settings.
76
+ Advanced configuration is available through the `dictate config` CLI.
72
77
 
73
78
  ## Update And Uninstall
74
79
 
@@ -86,13 +91,19 @@ powershell -ExecutionPolicy Bypass -File .\update-windows.ps1
86
91
  powershell -ExecutionPolicy Bypass -File .\uninstall-windows.ps1
87
92
  ```
88
93
 
89
- Linux:
94
+ Linux user install:
90
95
 
91
96
  ```bash
92
97
  ./update.sh
93
98
  ./uninstall.sh
94
99
  ```
95
100
 
101
+ Linux system package update:
102
+
103
+ ```bash
104
+ ./update.sh --system
105
+ ```
106
+
96
107
  Use `-RemoveUserData` on Windows or `--remove-user-data` on Linux only when you
97
108
  also want to remove config, logs, history, and downloaded model data.
98
109
 
@@ -102,23 +113,18 @@ also want to remove config, logs, history, and downloaded model data.
102
113
  - Runs as a tray app
103
114
  - Types dictated text into the focused app
104
115
  - Supports configurable push-to-talk
105
- - Shows selected model/status in the app UI
116
+ - Presents a simple capture-first desktop UI
106
117
  - Supports launch on startup
107
- - Stores hosted-provider API keys in the OS secret store
108
- - Keeps a small Recent History for copy/paste recovery
118
+ - Stores CLI-configured hosted-provider API keys in the OS secret store
119
+ - Keeps a small local dictations history for copy/paste recovery
109
120
  - Provides installer, updater, uninstaller, and doctor paths
110
121
 
111
122
  ## Models
112
123
 
113
- Supported provider defaults:
114
-
115
- - `faster-whisper/turbo` for local transcription
116
- - `openai/gpt-4o-mini-transcribe`
117
- - `xai/grok-speech-to-text`
118
- - `gemini/gemini-3-flash-preview`
119
-
120
- Local transcription can use CPU or GPU where supported. Hosted providers require
121
- an API key before they can be selected.
124
+ The default path uses local faster-whisper transcription. The desktop UI keeps
125
+ model/provider choices out of the primary workflow. Advanced users and tests can
126
+ still configure explicit local or hosted providers through CLI options and
127
+ `dictate config`.
122
128
 
123
129
  ## Commands
124
130
 
@@ -132,11 +138,13 @@ dictate doctor --quick --fix
132
138
  dictate doctor --check-model-load
133
139
  ```
134
140
 
135
- Hotword and model options are available from Settings. CLI flags still exist for
136
- automation and testing:
141
+ Advanced configuration remains available for automation and testing:
137
142
 
138
143
  ```bash
139
144
  dictate --stt-backend faster-whisper --model turbo
145
+ dictate config show
146
+ dictate config set-provider online
147
+ dictate config set-key xai xai-YOUR_KEY_HERE
140
148
  dictate --stt-backend openai --model gpt-4o-mini-transcribe
141
149
  dictate --stt-backend xai --model grok-speech-to-text
142
150
  dictate --stt-backend gemini --model gemini-3-flash-preview
@@ -164,33 +172,45 @@ and should not be packaged into the public repo default config.
164
172
  ## Safety
165
173
 
166
174
  - Dictate does not intentionally write raw API keys to `config.yaml`.
167
- - API keys configured in the app use the OS secret store.
175
+ - API keys configured through the CLI use the OS secret store.
168
176
  - Dictation text can be sensitive; check logs and issue reports before sharing.
169
177
  - Important transcriptions should be verified before relying on them.
170
178
  - Support and maintenance are best-effort.
171
179
 
172
180
  ## Desktop UI — the Quiet Console (preview)
173
181
 
174
- A design-system desktop Settings window is being built alongside the tray:
182
+ A quiet desktop window sits alongside the tray — a capture home (the mic is the
183
+ record button), the notes list, and the ⌘K palette; no settings menu (config via
184
+ the `dictate config` CLI).
175
185
 
176
- - [`ui/`](ui/README.md) — React/Vite front-end (the Quiet Console: seven views,
177
- ⌘K palette, listening HUD, light/dark, GNOME/KDE chrome).
186
+ - [`ui/`](ui/README.md) — React/Vite front-end. For UI work, **`cd ui && npm run
187
+ dev`** opens the whole app at `http://localhost:5173` against a built-in mock
188
+ (no engine, no Tauri, no STT) — the fast UI loop in a browser. Details, and
189
+ when to use the Tauri shell instead, are in that README's *Develop* section.
178
190
  - [`ui-shell/`](ui-shell/README.md) — Tauri 2 shell that hosts it on Linux.
179
191
  - `src/dictate/ui_server.py` — the loopback control server the UI talks to; the
180
- tray's **Open Settings…** launches the shell (falling back to native dialogs).
192
+ tray can launch the shell for the desktop capture surface.
181
193
  - See [`design/PLAN.md`](design/PLAN.md) for the cross-platform plan.
182
194
 
183
- **Install it like a normal app:** tagged releases attach a **self-contained**
184
- Linux **`.deb`** and **`.AppImage`** they bundle the frozen Python engine
185
- inside (PyInstaller sidecar), so there's no separate Python/pip step. Download,
186
- install, launch; speech models download on first use.
195
+ **Install it like a normal app:** the default Linux channel is a per-user install
196
+ under `~/.local/share/dictate` with launchers in `~/.local/bin` and
197
+ `~/.local/share/applications`. App updates do not need sudo.
198
+
199
+ ```bash
200
+ ./install.sh
201
+ ./update.sh
202
+ ```
203
+
204
+ Tagged releases can also attach a **self-contained** Linux **`.deb`** for users
205
+ who explicitly want a system package. It bundles the frozen Python engine inside
206
+ (PyInstaller sidecar), so there's no separate Python/pip step:
187
207
 
188
208
  ```bash
189
- sudo apt install ./dictate_*_amd64.deb # or: chmod +x Dictate_*.AppImage && ./Dictate_*.AppImage
209
+ sudo apt install ./Dictate_*_amd64.deb
190
210
  ```
191
211
 
192
- The app lives in the tray (Open Settings / Quit) and does push-to-talk straight
193
- away. Build the package yourself in one step with
212
+ The app lives in the tray and desktop shell and does push-to-talk straight away.
213
+ Build the package yourself in one step with
194
214
  [`scripts/build-linux-desktop.sh`](scripts/build-linux-desktop.sh) — see
195
215
  [`ui-shell/README.md`](ui-shell/README.md). The `pip`/`install.sh` route remains
196
216
  for source/dev installs.
@@ -203,7 +223,8 @@ for source/dev installs.
203
223
  - [Desktop packaging & CI runbook](docs/desktop-packaging.md)
204
224
  - [Microsoft Store automation](docs/msstore-automation.md)
205
225
  - [Microsoft Store listing draft](docs/msstore-listing.md)
206
- - [Windows release goal](docs/goal.md)
226
+ - [Deployment security](docs/deployment-security.md)
227
+ - [Goals](docs/GOALS.md)
207
228
  - [Development streams](docs/development-streams.md)
208
229
  - [Security policy](SECURITY.md)
209
230
 
@@ -3,7 +3,9 @@ push_to_talk_combo: ctrl_r
3
3
  stt_backend: faster-whisper
4
4
  stt_compute_type: int8
5
5
  stt_device: auto
6
- # Local Whisper uses the single supported local model:
6
+ # Local Whisper picks a hardware-aware default model when stt_model is unset:
7
+ # turbo on CUDA or a capable CPU (~8 GB RAM + >=8 cores), otherwise small.
8
+ # Set stt_model explicitly to override (e.g. tiny/base/small/medium/turbo):
7
9
  # stt_model: turbo
8
10
  # To avoid local compute, set one hosted backend:
9
11
  # stt_backend: openai
@@ -257,7 +257,7 @@ function Register-InstalledApp {
257
257
  $keyPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Dictate"
258
258
  New-Item -Force -Path $keyPath | Out-Null
259
259
  New-ItemProperty -Force -Path $keyPath -Name "DisplayName" -Value "Dictate" -PropertyType String | Out-Null
260
- New-ItemProperty -Force -Path $keyPath -Name "DisplayVersion" -Value "2026.6.20" -PropertyType String | Out-Null
260
+ New-ItemProperty -Force -Path $keyPath -Name "DisplayVersion" -Value "2026.7.4" -PropertyType String | Out-Null
261
261
  New-ItemProperty -Force -Path $keyPath -Name "Publisher" -Value "Arc Forge Labs" -PropertyType String | Out-Null
262
262
  New-ItemProperty -Force -Path $keyPath -Name "InstallLocation" -Value $InstallLocation -PropertyType String | Out-Null
263
263
  if (Test-Path $DisplayIcon) {
@@ -395,7 +395,9 @@ Install-StartupShortcut -TargetPath $wscript -Arguments $trayArgs -WorkingDirect
395
395
  Register-InstalledApp -InstallLocation $PSScriptRoot -DisplayIcon (Join-Path $PSScriptRoot "assets\dictate.ico")
396
396
 
397
397
  if (-not $NoPrepareTurbo) {
398
- Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "dictate", "prepare-model", "--stt-backend", "faster-whisper", "--model", "turbo", "--device", "auto", "--compute-type", "int8") -Description "Preparing faster-whisper turbo model"
398
+ # Omit --model so prepare-model resolves the hardware-aware local default
399
+ # (turbo on capable hardware, small on a weak CPU) instead of forcing turbo.
400
+ Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "dictate", "prepare-model", "--stt-backend", "faster-whisper", "--device", "auto", "--compute-type", "int8") -Description "Preparing faster-whisper model"
399
401
  }
400
402
 
401
403
  if (-not $NoVerify) {
package/install.ps1 CHANGED
@@ -10,7 +10,7 @@ param(
10
10
  )
11
11
 
12
12
  $ErrorActionPreference = "Stop"
13
- $DictateVersion = "2026.6.20"
13
+ $DictateVersion = "2026.7.4"
14
14
 
15
15
  if (-not $ArchiveUrl) {
16
16
  $ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/refs/tags/v$DictateVersion.zip"
package/install.sh CHANGED
@@ -11,7 +11,7 @@ DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
11
11
  AUTOSTART_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/autostart"
12
12
  ICON_DIR="$INSTALL_DIR/share/icons"
13
13
  ICON_PATH="$ICON_DIR/dictate-simple.png"
14
- DESKTOP_PATH="$DESKTOP_DIR/Dictate.desktop"
14
+ DESKTOP_PATH="$DESKTOP_DIR/dictate.desktop"
15
15
  CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/dictate"
16
16
  CONFIG_PATH="$CONFIG_DIR/config.yaml"
17
17
  DEFAULT_CONFIG_SOURCE="$SCRIPT_DIR/config/default-config.yaml"
@@ -20,6 +20,7 @@ PREPARE_TURBO=1
20
20
  SEED_DEFAULT_CONFIG=1
21
21
  STARTUP=1
22
22
  INSTALL_UI=1
23
+ INSTALL_SCOPE="user"
23
24
  PYTHON_BIN="${PYTHON_BIN:-python3}"
24
25
 
25
26
  # Prefer the distro Python so --system-site-packages can see modules such as
@@ -30,17 +31,51 @@ fi
30
31
 
31
32
  usage() {
32
33
  cat <<EOF
33
- Usage: $0 [--no-verify] [--no-prepare-turbo] [--no-seed-default-config] [--no-startup] [--no-ui] [--session-backend auto|x11|wayland]
34
+ Usage: $0 [--user|--system] [--no-verify] [--no-prepare-turbo] [--no-seed-default-config] [--no-startup] [--no-ui] [--session-backend auto|x11|wayland]
34
35
 
35
- Installs dictate into ~/.local/share/dictate, links ~/.local/bin/dictate and
36
+ Default: --user.
37
+
38
+ --user installs dictate into ~/.local/share/dictate, links ~/.local/bin/dictate and
36
39
  ~/.local/bin/dictate-ui-server, seeds the default config on first install,
37
- creates app launcher/autostart entries, prepares the faster-whisper turbo model,
40
+ creates app launcher/autostart entries, prepares the hardware-aware faster-whisper model,
38
41
  and installs the desktop "Quiet Console" UI shell when a build toolchain is
39
42
  present (unless disabled). All steps degrade gracefully when prerequisites are
40
43
  missing.
44
+
45
+ --system installs a Linux desktop package into system paths using apt/pkexec or
46
+ sudo. It is intentionally explicit because it requires administrator approval.
41
47
  EOF
42
48
  }
43
49
 
50
+ install_system_package() {
51
+ if [ "$(uname -s)" != "Linux" ]; then
52
+ echo "--system is only supported by this installer on Linux."
53
+ exit 2
54
+ fi
55
+
56
+ local deb_path="${DICTATE_DEB:-}"
57
+ if [ -z "$deb_path" ]; then
58
+ deb_path="$(find "$SCRIPT_DIR/ui-shell/src-tauri/target/release/bundle/deb" -maxdepth 1 -name 'Dictate_*_amd64.deb' 2>/dev/null | sort | tail -n 1 || true)"
59
+ fi
60
+ if [ -z "$deb_path" ] || [ ! -f "$deb_path" ]; then
61
+ echo "No local .deb found. Build one first:"
62
+ echo " DICTATE_BUNDLES=deb scripts/build-linux-desktop.sh"
63
+ echo "Or pass one explicitly:"
64
+ echo " DICTATE_DEB=/path/to/Dictate_..._amd64.deb $0 --system"
65
+ exit 1
66
+ fi
67
+
68
+ echo "Installing system package: $deb_path"
69
+ if command -v pkexec >/dev/null 2>&1; then
70
+ pkexec apt-get install -y "$deb_path"
71
+ elif command -v sudo >/dev/null 2>&1; then
72
+ sudo apt-get install -y "$deb_path"
73
+ else
74
+ echo "Need pkexec or sudo to install a system package."
75
+ exit 1
76
+ fi
77
+ }
78
+
44
79
  detect_session_backend() {
45
80
  if [ -n "${WAYLAND_DISPLAY:-}" ] || [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then
46
81
  printf 'wayland\n'
@@ -80,6 +115,12 @@ while [ "$#" -gt 0 ]; do
80
115
  --no-ui)
81
116
  INSTALL_UI=0
82
117
  ;;
118
+ --user)
119
+ INSTALL_SCOPE="user"
120
+ ;;
121
+ --system)
122
+ INSTALL_SCOPE="system"
123
+ ;;
83
124
  --session-backend)
84
125
  shift
85
126
  SESSION_BACKEND="${1:-}"
@@ -99,6 +140,11 @@ while [ "$#" -gt 0 ]; do
99
140
  shift
100
141
  done
101
142
 
143
+ if [ "$INSTALL_SCOPE" = "system" ]; then
144
+ install_system_package
145
+ exit 0
146
+ fi
147
+
102
148
  if [ "$SESSION_BACKEND" = "auto" ]; then
103
149
  SESSION_BACKEND="$(detect_session_backend)"
104
150
  fi
@@ -125,6 +171,34 @@ elif command -v rpm >/dev/null 2>&1 && rpm -q dictate >/dev/null 2>&1; then
125
171
  echo " Use one install method. To remove the package first: sudo dnf remove dictate"
126
172
  fi
127
173
 
174
+ # Stop any running Dictate engine before we overwrite it, so the new install can
175
+ # claim the single-instance lock cleanly instead of colliding with a stale daemon.
176
+ stop_running_dictate() {
177
+ # Prefer the installed CLI's own clean stop; it knows the lock location.
178
+ if command -v dictate >/dev/null 2>&1 && dictate stop --quiet 2>/dev/null; then
179
+ return 0
180
+ fi
181
+ # Fallback (older builds without `dictate stop`): kill via the lock PID file.
182
+ local lockdir
183
+ if [ -n "${XDG_RUNTIME_DIR:-}" ]; then
184
+ lockdir="$XDG_RUNTIME_DIR/dictate-daemon.lockdir"
185
+ else
186
+ lockdir="/tmp/dictate-daemon-$(id -u).lockdir"
187
+ fi
188
+ local pidfile="$lockdir/pid"
189
+ [ -f "$pidfile" ] || return 0
190
+ local pid
191
+ pid="$(cat "$pidfile" 2>/dev/null || true)"
192
+ [ -n "$pid" ] || return 0
193
+ if kill -0 "$pid" 2>/dev/null; then
194
+ kill "$pid" 2>/dev/null || true
195
+ for _ in 1 2 3 4 5 6 7 8 9 10; do kill -0 "$pid" 2>/dev/null || break; sleep 0.3; done
196
+ kill -9 "$pid" 2>/dev/null || true
197
+ fi
198
+ }
199
+ echo "Stopping any running Dictate engine ..."
200
+ stop_running_dictate
201
+
128
202
  echo "Creating venv at $INSTALL_DIR ..."
129
203
  uv venv "$INSTALL_DIR/venv" --python "$PYTHON_BIN" --system-site-packages --quiet
130
204
 
@@ -144,7 +218,7 @@ install -m 644 "$SCRIPT_DIR/assets/dictate.png" "$ICON_PATH"
144
218
 
145
219
  echo "Installing desktop entry ..."
146
220
  mkdir -p "$DESKTOP_DIR"
147
- rm -f "$DESKTOP_DIR/dictate-settings.desktop" "$DESKTOP_DIR/dictate.desktop"
221
+ rm -f "$DESKTOP_DIR/dictate-settings.desktop" "$DESKTOP_DIR/Dictate.desktop" "$DESKTOP_DIR/dictate.desktop"
148
222
  cat > "$DESKTOP_PATH" <<EOF
149
223
  [Desktop Entry]
150
224
  Name=Dictate
@@ -206,22 +280,52 @@ install_desktop_ui() {
206
280
  npm --prefix "$SCRIPT_DIR/ui" ci >/dev/null 2>&1 \
207
281
  || npm --prefix "$SCRIPT_DIR/ui" install >/dev/null 2>&1 \
208
282
  || { echo "UI front-end install failed; skipping the optional shell."; print_ui_hint; return 0; }
209
- if ! npm --prefix "$SCRIPT_DIR/ui" run build >/dev/null 2>&1; then
210
- echo "UI front-end build failed; skipping the optional shell."; print_ui_hint; return 0
211
- fi
212
- if ! ( cd "$shell_src" && cargo build --release --manifest-path src-tauri/Cargo.toml ); then
283
+ npm --prefix "$shell_src" ci >/dev/null 2>&1 \
284
+ || npm --prefix "$shell_src" install >/dev/null 2>&1 \
285
+ || { echo "UI shell tooling install failed; skipping the optional shell."; print_ui_hint; return 0; }
286
+ # Build through the Tauri CLI, NOT a raw `cargo build`. The CLI enables the
287
+ # `custom-protocol` feature that embeds the frontend and serves it over the
288
+ # app protocol. A plain cargo build omits that feature, so the shell falls
289
+ # back to loading the dev server (localhost:5173) and shows "Could not connect
290
+ # to localhost: Connection refused" at runtime. --no-bundle: we only need the
291
+ # binary here, not a .deb/AppImage. (beforeBuildCommand rebuilds ui/dist.)
292
+ if ! ( cd "$shell_src" && npm run tauri -- build --no-bundle ); then
213
293
  echo "Desktop UI shell build failed; the engine + tray remain fully functional."
214
294
  print_ui_hint
215
295
  return 0
216
296
  fi
217
297
  install -m 755 "$shell_src/src-tauri/target/release/dictate-ui-shell" "$target"
218
298
  echo "Installed desktop UI shell: $target"
299
+
300
+ # Give the shell an absolute-path engine right next to it. The shell's
301
+ # bundled-engine lookup checks <exe_dir>/engine first — before any system
302
+ # path (e.g. a leftover /usr/lib/Dictate from an old .deb) and independent of
303
+ # the minimal PATH a desktop launcher provides. Pointing it at the per-user
304
+ # venv means one process both dictates and serves, and keeps sys.executable
305
+ # inside ~/.local so the install reads as "linux-user" and the in-app updater
306
+ # never needs sudo/pkexec.
307
+ local engine_dir="$BIN_DIR/engine"
308
+ mkdir -p "$engine_dir"
309
+ cat > "$engine_dir/dictate-engine" <<WRAP
310
+ #!/bin/sh
311
+ exec "$INSTALL_DIR/venv/bin/dictate" "\$@"
312
+ WRAP
313
+ chmod 755 "$engine_dir/dictate-engine"
314
+ echo "Installed per-user engine launcher: $engine_dir/dictate-engine"
219
315
  }
220
316
 
221
317
  if [ "$INSTALL_UI" -eq 1 ]; then
222
318
  install_desktop_ui
223
319
  fi
224
320
 
321
+ if [ -x "$BIN_DIR/dictate-ui-shell" ]; then
322
+ echo "Pointing launcher entries at the desktop UI shell ..."
323
+ sed -i "s|^Exec=.*|Exec=$BIN_DIR/dictate-ui-shell|" "$DESKTOP_PATH"
324
+ if [ "$STARTUP" -eq 1 ] && [ -f "$AUTOSTART_DIR/dictate.desktop" ]; then
325
+ sed -i "s|^Exec=.*|Exec=$BIN_DIR/dictate-ui-shell|" "$AUTOSTART_DIR/dictate.desktop"
326
+ fi
327
+ fi
328
+
225
329
  if [ "$SEED_DEFAULT_CONFIG" -eq 1 ]; then
226
330
  if [ ! -f "$CONFIG_PATH" ]; then
227
331
  if [ ! -f "$DEFAULT_CONFIG_SOURCE" ]; then
@@ -266,14 +370,15 @@ run_logged_check() {
266
370
 
267
371
  if [ "$PREPARE_TURBO" -eq 1 ]; then
268
372
  PREPARE_LOG="/tmp/dictate-install-prepare.log"
373
+ # Omit --model so prepare-model resolves the hardware-aware local default
374
+ # (turbo on capable hardware, small on a weak CPU) instead of forcing turbo.
269
375
  run_logged_check \
270
- "dictate prepare-model --stt-backend faster-whisper --model turbo --device auto --compute-type int8" \
376
+ "dictate prepare-model --stt-backend faster-whisper --device auto --compute-type int8" \
271
377
  "$PREPARE_LOG" \
272
378
  1800 \
273
379
  "$DICTATE_BIN" \
274
380
  prepare-model \
275
381
  --stt-backend faster-whisper \
276
- --model turbo \
277
382
  --device auto \
278
383
  --compute-type int8
279
384
  fi
@@ -283,14 +388,13 @@ if [ "$VERIFY" -eq 1 ]; then
283
388
  run_logged_check "dictate --help" "$VERIFY_LOG" 20 "$DICTATE_BIN" --help
284
389
  run_logged_check "dictate benchmark --help" "$VERIFY_LOG" 20 "$DICTATE_BIN" benchmark --help
285
390
  run_logged_check \
286
- "dictate doctor --quick --stt-backend faster-whisper --model turbo" \
391
+ "dictate doctor --quick --stt-backend faster-whisper" \
287
392
  "$VERIFY_LOG" \
288
393
  20 \
289
394
  "$DICTATE_BIN" \
290
395
  doctor \
291
396
  --quick \
292
- --stt-backend faster-whisper \
293
- --model turbo
397
+ --stt-backend faster-whisper
294
398
  fi
295
399
 
296
400
  if [ "$STARTUP" -eq 1 ]; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arcforgelabs/dictate",
3
- "version": "2026.6.20",
3
+ "version": "2026.7.4",
4
4
  "description": "Installer shim for Dictate desktop dictation.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/uninstall.sh CHANGED
@@ -4,6 +4,7 @@ set -euo pipefail
4
4
 
5
5
  INSTALL_DIR="$HOME/.local/share/dictate"
6
6
  BIN_PATH="$HOME/.local/bin/dictate"
7
+ UI_SHELL_BIN_PATH="$HOME/.local/bin/dictate-ui-shell"
7
8
  UI_SERVER_BIN_PATH="$HOME/.local/bin/dictate-ui-server"
8
9
  DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
9
10
  AUTOSTART_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/autostart"
@@ -95,10 +96,12 @@ remove_managed_symlink() {
95
96
  stop_source_processes
96
97
 
97
98
  remove_file "$DESKTOP_DIR/dictate.desktop"
99
+ remove_file "$DESKTOP_DIR/Dictate.desktop"
98
100
  remove_file "$DESKTOP_DIR/dictate-settings.desktop"
99
101
  remove_file "$AUTOSTART_DIR/dictate.desktop"
100
102
 
101
103
  remove_managed_symlink "$BIN_PATH" "$INSTALL_DIR/venv/bin/dictate"
104
+ remove_file "$UI_SHELL_BIN_PATH"
102
105
  remove_managed_symlink "$UI_SERVER_BIN_PATH" "$INSTALL_DIR/venv/bin/dictate-ui-server"
103
106
 
104
107
  rm -rf "$INSTALL_DIR/venv" "$INSTALL_DIR/share/icons" "$INSTALL_DIR/logs"
package/update.ps1 CHANGED
@@ -10,7 +10,7 @@ param(
10
10
  )
11
11
 
12
12
  $ErrorActionPreference = "Stop"
13
- $DictateVersion = "2026.6.20"
13
+ $DictateVersion = "2026.7.4"
14
14
 
15
15
  if (-not $ArchiveUrl) {
16
16
  $ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/refs/tags/v$DictateVersion.zip"
package/update.sh CHANGED
@@ -8,19 +8,55 @@ DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
8
8
  AUTOSTART_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/autostart"
9
9
  ICON_DIR="$INSTALL_DIR/share/icons"
10
10
  AUTOSTART_PATH="$AUTOSTART_DIR/dictate.desktop"
11
+ UPDATE_SCOPE="user"
12
+
13
+ usage() {
14
+ cat <<EOF
15
+ Usage: $0 [--user|--system] [install.sh options]
16
+
17
+ Default: --user.
18
+
19
+ --user updates the per-user install in ~/.local/share/dictate without sudo.
20
+ --system updates a Linux desktop package in system paths using apt/pkexec or sudo.
21
+ EOF
22
+ }
11
23
 
12
24
  cleanup_legacy_entries() {
13
25
  rm -f "$DESKTOP_DIR/dictate-settings.desktop"
14
26
  rm -f "$ICON_DIR/dictate-controls.png" "$ICON_DIR/dictate.png"
15
27
  }
16
28
 
29
+ args=()
30
+ while [ "$#" -gt 0 ]; do
31
+ case "$1" in
32
+ --user)
33
+ UPDATE_SCOPE="user"
34
+ ;;
35
+ --system)
36
+ UPDATE_SCOPE="system"
37
+ ;;
38
+ -h|--help)
39
+ usage
40
+ exit 0
41
+ ;;
42
+ *)
43
+ args+=("$1")
44
+ ;;
45
+ esac
46
+ shift
47
+ done
48
+
49
+ if [ "$UPDATE_SCOPE" = "system" ]; then
50
+ "$SCRIPT_DIR/install.sh" --system "${args[@]}"
51
+ exit 0
52
+ fi
53
+
17
54
  if [ -d "$SCRIPT_DIR/.git" ]; then
18
55
  git -C "$SCRIPT_DIR" pull --ff-only
19
56
  fi
20
57
 
21
58
  cleanup_legacy_entries
22
59
 
23
- args=("$@")
24
60
  startup_option_seen=0
25
61
  for arg in "${args[@]}"; do
26
62
  if [ "$arg" = "--no-startup" ]; then