@arcforgelabs/dictate 2026.6.20 → 2026.7.4-unstable.3.1
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 +105 -43
- package/assets/dictate.png +0 -0
- package/config/default-config.yaml +12 -2
- package/install-windows.ps1 +12 -3
- package/install.ps1 +6 -2
- package/install.sh +176 -21
- package/package.json +1 -1
- package/uninstall.sh +3 -0
- package/update.ps1 +6 -2
- package/update.sh +37 -1
package/README.md
CHANGED
|
@@ -6,18 +6,19 @@ 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,
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
12
|
+
`docs/msstore-automation.md`. The current transcription/model deployment plan is
|
|
13
|
+
`docs/TRANSCRIPTION_PLAN.md`.
|
|
12
14
|
|
|
13
15
|
## Install
|
|
14
16
|
|
|
15
17
|
Windows 11 normal install:
|
|
16
18
|
|
|
17
|
-
The target public channel is Microsoft Store distribution.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
normal public install route.
|
|
19
|
+
The target public channel is Microsoft Store distribution. GitHub release
|
|
20
|
+
artifacts and the PowerShell bootstrap installer below are developer/source or
|
|
21
|
+
internal validation paths, not the normal public Windows install route.
|
|
21
22
|
|
|
22
23
|
Windows developer/source install from the hosted bootstrap:
|
|
23
24
|
|
|
@@ -25,15 +26,21 @@ Windows developer/source install from the hosted bootstrap:
|
|
|
25
26
|
powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@latest/install.ps1 | iex"
|
|
26
27
|
```
|
|
27
28
|
|
|
29
|
+
Unstable developer/source bootstrap for pre-stable feature testing:
|
|
30
|
+
|
|
31
|
+
```powershell
|
|
32
|
+
powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@unstable/install.ps1 | iex"
|
|
33
|
+
```
|
|
34
|
+
|
|
28
35
|
Open **Dictate** from the Start Menu after install.
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
Linux default install (per-user, no sudo for app updates):
|
|
31
38
|
|
|
32
39
|
```bash
|
|
33
40
|
./install-ubuntu.sh
|
|
34
41
|
```
|
|
35
42
|
|
|
36
|
-
Generic Linux
|
|
43
|
+
Generic Linux user install:
|
|
37
44
|
|
|
38
45
|
```bash
|
|
39
46
|
./install.sh
|
|
@@ -41,6 +48,14 @@ Generic Linux source install:
|
|
|
41
48
|
|
|
42
49
|
Open **Dictate** from the app launcher after install.
|
|
43
50
|
|
|
51
|
+
Linux system package install is also supported when you explicitly want a
|
|
52
|
+
machine-wide `.deb` install:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
DICTATE_BUNDLES=deb scripts/build-linux-desktop.sh
|
|
56
|
+
./install.sh --system
|
|
57
|
+
```
|
|
58
|
+
|
|
44
59
|
Windows developer/source install, from the repo/source directory:
|
|
45
60
|
|
|
46
61
|
```powershell
|
|
@@ -60,15 +75,13 @@ npx @arcforgelabs/dictate install
|
|
|
60
75
|
|
|
61
76
|
```text
|
|
62
77
|
Open Dictate
|
|
63
|
-
|
|
64
|
-
Set API key if using a hosted model
|
|
65
|
-
Set push-to-talk shortcut if desired
|
|
78
|
+
Use the mic button or configured push-to-talk shortcut
|
|
66
79
|
Hold shortcut, speak, release
|
|
67
|
-
Review
|
|
80
|
+
Review Dictations when needed
|
|
68
81
|
```
|
|
69
82
|
|
|
70
83
|
By default, Dictate installs a normal app launcher entry and starts on sign-in.
|
|
71
|
-
|
|
84
|
+
Advanced configuration is available through the `dictate config` CLI.
|
|
72
85
|
|
|
73
86
|
## Update And Uninstall
|
|
74
87
|
|
|
@@ -79,6 +92,20 @@ powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/
|
|
|
79
92
|
powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@latest/uninstall.ps1 | iex"
|
|
80
93
|
```
|
|
81
94
|
|
|
95
|
+
To test updates before they are promoted to stable:
|
|
96
|
+
|
|
97
|
+
```powershell
|
|
98
|
+
powershell -ExecutionPolicy Bypass -Command "iwr -useb https://cdn.jsdelivr.net/npm/@arcforgelabs/dictate@unstable/update.ps1 | iex"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Users on the app's npm-backed update path can opt into or out of unstable
|
|
102
|
+
updates from the CLI:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
dictate config set-update-channel unstable
|
|
106
|
+
dictate config set-update-channel stable
|
|
107
|
+
```
|
|
108
|
+
|
|
82
109
|
Windows from source:
|
|
83
110
|
|
|
84
111
|
```powershell
|
|
@@ -86,13 +113,19 @@ powershell -ExecutionPolicy Bypass -File .\update-windows.ps1
|
|
|
86
113
|
powershell -ExecutionPolicy Bypass -File .\uninstall-windows.ps1
|
|
87
114
|
```
|
|
88
115
|
|
|
89
|
-
Linux:
|
|
116
|
+
Linux user install:
|
|
90
117
|
|
|
91
118
|
```bash
|
|
92
119
|
./update.sh
|
|
93
120
|
./uninstall.sh
|
|
94
121
|
```
|
|
95
122
|
|
|
123
|
+
Linux system package update:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
./update.sh --system
|
|
127
|
+
```
|
|
128
|
+
|
|
96
129
|
Use `-RemoveUserData` on Windows or `--remove-user-data` on Linux only when you
|
|
97
130
|
also want to remove config, logs, history, and downloaded model data.
|
|
98
131
|
|
|
@@ -102,23 +135,37 @@ also want to remove config, logs, history, and downloaded model data.
|
|
|
102
135
|
- Runs as a tray app
|
|
103
136
|
- Types dictated text into the focused app
|
|
104
137
|
- Supports configurable push-to-talk
|
|
105
|
-
-
|
|
138
|
+
- Presents a simple capture-first desktop UI
|
|
106
139
|
- Supports launch on startup
|
|
107
|
-
- Stores hosted-provider API keys in the OS secret store
|
|
108
|
-
- Keeps a small
|
|
140
|
+
- Stores CLI-configured hosted-provider API keys in the OS secret store
|
|
141
|
+
- Keeps a small local dictations history for copy/paste recovery
|
|
109
142
|
- Provides installer, updater, uninstaller, and doctor paths
|
|
110
143
|
|
|
111
144
|
## Models
|
|
112
145
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
146
|
+
The default local English path is Parakeet where the runtime is available. The
|
|
147
|
+
desktop UI keeps engine names out of the primary workflow; advanced users and
|
|
148
|
+
tests can still configure explicit local or hosted providers through CLI options
|
|
149
|
+
and `dictate config`.
|
|
150
|
+
|
|
151
|
+
GPU lanes are explicit:
|
|
152
|
+
|
|
153
|
+
- NVIDIA CUDA: install with the `gpu` extra and verify with
|
|
154
|
+
`dictate doctor --stt-backend parakeet --device cuda --quick`.
|
|
155
|
+
- Windows AMD GPU: install with the `amd` extra for ONNX Runtime DirectML and
|
|
156
|
+
verify with `dictate doctor --stt-backend parakeet --device amd --quick`.
|
|
157
|
+
- Linux AMD GPU: install a ROCm/MIGraphX-capable ONNX Runtime build, then verify
|
|
158
|
+
with `dictate doctor --stt-backend parakeet --device amd --quick`.
|
|
159
|
+
- Meeting uses a dedicated speaker-attribution lane. Inspect it with
|
|
160
|
+
`dictate config show`; set it with
|
|
161
|
+
`dictate config set-meeting-model parakeet-pyannote/parakeet-tdt-0.6b-v2`.
|
|
162
|
+
Source installs can add pyannote support with `./install.sh --meeting` or
|
|
163
|
+
`.\install-windows.ps1 -Meeting`, then verify with
|
|
164
|
+
`dictate doctor --stt-backend parakeet-pyannote --device cuda --quick`.
|
|
165
|
+
Experimental preflight targets also exist for
|
|
166
|
+
`parakeet-diarizen/parakeet-tdt-0.6b-v2` and
|
|
167
|
+
`parakeet-sortformer/parakeet-tdt-0.6b-v2`; these still require their
|
|
168
|
+
runtime-specific DiariZen or NeMo setup before selection.
|
|
122
169
|
|
|
123
170
|
## Commands
|
|
124
171
|
|
|
@@ -132,11 +179,13 @@ dictate doctor --quick --fix
|
|
|
132
179
|
dictate doctor --check-model-load
|
|
133
180
|
```
|
|
134
181
|
|
|
135
|
-
|
|
136
|
-
automation and testing:
|
|
182
|
+
Advanced configuration remains available for automation and testing:
|
|
137
183
|
|
|
138
184
|
```bash
|
|
139
185
|
dictate --stt-backend faster-whisper --model turbo
|
|
186
|
+
dictate config show
|
|
187
|
+
dictate config set-provider online
|
|
188
|
+
dictate config set-key xai xai-YOUR_KEY_HERE
|
|
140
189
|
dictate --stt-backend openai --model gpt-4o-mini-transcribe
|
|
141
190
|
dictate --stt-backend xai --model grok-speech-to-text
|
|
142
191
|
dictate --stt-backend gemini --model gemini-3-flash-preview
|
|
@@ -164,33 +213,45 @@ and should not be packaged into the public repo default config.
|
|
|
164
213
|
## Safety
|
|
165
214
|
|
|
166
215
|
- Dictate does not intentionally write raw API keys to `config.yaml`.
|
|
167
|
-
- API keys configured
|
|
216
|
+
- API keys configured through the CLI use the OS secret store.
|
|
168
217
|
- Dictation text can be sensitive; check logs and issue reports before sharing.
|
|
169
218
|
- Important transcriptions should be verified before relying on them.
|
|
170
219
|
- Support and maintenance are best-effort.
|
|
171
220
|
|
|
172
221
|
## Desktop UI — the Quiet Console (preview)
|
|
173
222
|
|
|
174
|
-
A
|
|
223
|
+
A quiet desktop window sits alongside the tray — a capture home (the mic is the
|
|
224
|
+
record button), the notes list, and the ⌘K palette; no settings menu (config via
|
|
225
|
+
the `dictate config` CLI).
|
|
175
226
|
|
|
176
|
-
- [`ui/`](ui/README.md) — React/Vite front-end
|
|
177
|
-
|
|
227
|
+
- [`ui/`](ui/README.md) — React/Vite front-end. For UI work, **`cd ui && npm run
|
|
228
|
+
dev`** opens the whole app at `http://localhost:5173` against a built-in mock
|
|
229
|
+
(no engine, no Tauri, no STT) — the fast UI loop in a browser. Details, and
|
|
230
|
+
when to use the Tauri shell instead, are in that README's *Develop* section.
|
|
178
231
|
- [`ui-shell/`](ui-shell/README.md) — Tauri 2 shell that hosts it on Linux.
|
|
179
232
|
- `src/dictate/ui_server.py` — the loopback control server the UI talks to; the
|
|
180
|
-
tray
|
|
233
|
+
tray can launch the shell for the desktop capture surface.
|
|
181
234
|
- See [`design/PLAN.md`](design/PLAN.md) for the cross-platform plan.
|
|
182
235
|
|
|
183
|
-
**Install it like a normal app:**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
236
|
+
**Install it like a normal app:** the default Linux channel is a per-user install
|
|
237
|
+
under `~/.local/share/dictate` with launchers in `~/.local/bin` and
|
|
238
|
+
`~/.local/share/applications`. App updates do not need sudo.
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
./install.sh
|
|
242
|
+
./update.sh
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Tagged releases can also attach a **self-contained** Linux **`.deb`** for users
|
|
246
|
+
who explicitly want a system package. It bundles the frozen Python engine inside
|
|
247
|
+
(PyInstaller sidecar), so there's no separate Python/pip step:
|
|
187
248
|
|
|
188
249
|
```bash
|
|
189
|
-
sudo apt install ./
|
|
250
|
+
sudo apt install ./Dictate_*_amd64.deb
|
|
190
251
|
```
|
|
191
252
|
|
|
192
|
-
The app lives in the tray
|
|
193
|
-
|
|
253
|
+
The app lives in the tray and desktop shell and does push-to-talk straight away.
|
|
254
|
+
Build the package yourself in one step with
|
|
194
255
|
[`scripts/build-linux-desktop.sh`](scripts/build-linux-desktop.sh) — see
|
|
195
256
|
[`ui-shell/README.md`](ui-shell/README.md). The `pip`/`install.sh` route remains
|
|
196
257
|
for source/dev installs.
|
|
@@ -198,13 +259,14 @@ for source/dev installs.
|
|
|
198
259
|
## Docs
|
|
199
260
|
|
|
200
261
|
- [Windows 11 support](docs/windows-11.md)
|
|
201
|
-
- [
|
|
262
|
+
- [Transcription deployment plan](docs/TRANSCRIPTION_PLAN.md)
|
|
202
263
|
- [Release/versioning](docs/release-versioning.md)
|
|
203
264
|
- [Desktop packaging & CI runbook](docs/desktop-packaging.md)
|
|
204
265
|
- [Microsoft Store automation](docs/msstore-automation.md)
|
|
205
266
|
- [Microsoft Store listing draft](docs/msstore-listing.md)
|
|
206
|
-
- [
|
|
267
|
+
- [Deployment security](docs/deployment-security.md)
|
|
207
268
|
- [Development streams](docs/development-streams.md)
|
|
269
|
+
- [Archived docs index](docs/archive/README.md)
|
|
208
270
|
- [Security policy](SECURITY.md)
|
|
209
271
|
|
|
210
272
|
## License
|
package/assets/dictate.png
CHANGED
|
Binary file
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
hotwords: []
|
|
2
2
|
push_to_talk_combo: ctrl_r
|
|
3
|
-
stt_backend: faster-whisper
|
|
4
3
|
stt_compute_type: int8
|
|
5
4
|
stt_device: auto
|
|
6
|
-
#
|
|
5
|
+
# Leave stt_backend/stt_model unset for the hardware-aware local default:
|
|
6
|
+
# English Parakeet v2 on CPU, CUDA, and AMD when the Parakeet runtime is
|
|
7
|
+
# available. Accelerator readiness is checked by doctor/preflight.
|
|
8
|
+
# Set stt_backend/stt_model explicitly to override:
|
|
9
|
+
# stt_backend: parakeet
|
|
10
|
+
# stt_model: parakeet-tdt-0.6b-v2
|
|
11
|
+
# Meeting mode uses a separate speaker-attribution lane. Leave these unset for
|
|
12
|
+
# the default local Parakeet+pyannote Meeting backend, or set them explicitly.
|
|
13
|
+
# meeting_stt_backend: parakeet-pyannote
|
|
14
|
+
# meeting_stt_model: parakeet-tdt-0.6b-v2
|
|
15
|
+
# stt_backend: faster-whisper
|
|
7
16
|
# stt_model: turbo
|
|
17
|
+
# stt_model: large-v3
|
|
8
18
|
# To avoid local compute, set one hosted backend:
|
|
9
19
|
# stt_backend: openai
|
|
10
20
|
# stt_model: gpt-4o-mini-transcribe
|
package/install-windows.ps1
CHANGED
|
@@ -3,6 +3,7 @@ param(
|
|
|
3
3
|
[switch]$NoPrepareTurbo,
|
|
4
4
|
[switch]$NoShortcut,
|
|
5
5
|
[switch]$NoStartup,
|
|
6
|
+
[switch]$Meeting,
|
|
6
7
|
[switch]$RecreateVenv
|
|
7
8
|
)
|
|
8
9
|
|
|
@@ -257,7 +258,7 @@ function Register-InstalledApp {
|
|
|
257
258
|
$keyPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Dictate"
|
|
258
259
|
New-Item -Force -Path $keyPath | Out-Null
|
|
259
260
|
New-ItemProperty -Force -Path $keyPath -Name "DisplayName" -Value "Dictate" -PropertyType String | Out-Null
|
|
260
|
-
New-ItemProperty -Force -Path $keyPath -Name "DisplayVersion" -Value "2026.
|
|
261
|
+
New-ItemProperty -Force -Path $keyPath -Name "DisplayVersion" -Value "2026.7.4" -PropertyType String | Out-Null
|
|
261
262
|
New-ItemProperty -Force -Path $keyPath -Name "Publisher" -Value "Arc Forge Labs" -PropertyType String | Out-Null
|
|
262
263
|
New-ItemProperty -Force -Path $keyPath -Name "InstallLocation" -Value $InstallLocation -PropertyType String | Out-Null
|
|
263
264
|
if (Test-Path $DisplayIcon) {
|
|
@@ -383,7 +384,12 @@ if (-not (Test-Path $venvPython)) {
|
|
|
383
384
|
}
|
|
384
385
|
|
|
385
386
|
Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "pip", "install", "--upgrade", "pip") -Description "Upgrading pip"
|
|
386
|
-
|
|
387
|
+
$installExtras = @("windows")
|
|
388
|
+
if ($Meeting) {
|
|
389
|
+
$installExtras += "meeting"
|
|
390
|
+
}
|
|
391
|
+
$installTarget = "${PSScriptRoot}[$($installExtras -join ',')]"
|
|
392
|
+
Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "pip", "install", "-e", $installTarget) -Description "Installing Dictate Windows package"
|
|
387
393
|
|
|
388
394
|
Seed-Config
|
|
389
395
|
Write-LauncherScripts -ScriptsDir $scriptsDir
|
|
@@ -395,7 +401,10 @@ Install-StartupShortcut -TargetPath $wscript -Arguments $trayArgs -WorkingDirect
|
|
|
395
401
|
Register-InstalledApp -InstallLocation $PSScriptRoot -DisplayIcon (Join-Path $PSScriptRoot "assets\dictate.ico")
|
|
396
402
|
|
|
397
403
|
if (-not $NoPrepareTurbo) {
|
|
398
|
-
|
|
404
|
+
# Keep the legacy switch name for installer compatibility, but prepare the
|
|
405
|
+
# fresh local default: Parakeet v2. Explicit faster-whisper users can still
|
|
406
|
+
# prepare that backend manually.
|
|
407
|
+
Invoke-Checked -Exe $venvPython -ArgumentList @("-m", "dictate", "prepare-model", "--stt-backend", "parakeet", "--device", "auto", "--compute-type", "int8") -Description "Preparing Parakeet model"
|
|
399
408
|
}
|
|
400
409
|
|
|
401
410
|
if (-not $NoVerify) {
|
package/install.ps1
CHANGED
|
@@ -10,10 +10,10 @@ param(
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
|
-
$DictateVersion = "2026.
|
|
13
|
+
$DictateVersion = "2026.7.4-unstable.3.1"
|
|
14
14
|
|
|
15
15
|
if (-not $ArchiveUrl) {
|
|
16
|
-
$ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/
|
|
16
|
+
$ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/3f6f050451d0d0e140193534f102c6b5f2b58b19.zip"
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
if (-not $InstallRoot) {
|
|
@@ -76,6 +76,10 @@ try {
|
|
|
76
76
|
if ($LASTEXITCODE -ne 0) {
|
|
77
77
|
throw "Dictate Windows installer failed with exit code $LASTEXITCODE."
|
|
78
78
|
}
|
|
79
|
+
$dictateExe = Join-Path $installRootPath ".venv\Scripts\dictate.exe"
|
|
80
|
+
if (Test-Path $dictateExe) {
|
|
81
|
+
& $dictateExe set-installed-package-version $DictateVersion | Out-Null
|
|
82
|
+
}
|
|
79
83
|
} finally {
|
|
80
84
|
if (Test-Path $stagingRoot) {
|
|
81
85
|
Remove-Item -Recurse -Force $stagingRoot
|
package/install.sh
CHANGED
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
7
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
PACKAGE_VERSION="$(sed -n 's/^ "version": "\([^"]*\)",/\1/p' "$SCRIPT_DIR/package.json" | head -n 1 || true)"
|
|
8
9
|
INSTALL_DIR="$HOME/.local/share/dictate"
|
|
9
10
|
BIN_DIR="$HOME/.local/bin"
|
|
10
11
|
DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
|
|
11
12
|
AUTOSTART_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/autostart"
|
|
12
13
|
ICON_DIR="$INSTALL_DIR/share/icons"
|
|
13
14
|
ICON_PATH="$ICON_DIR/dictate-simple.png"
|
|
14
|
-
|
|
15
|
+
APP_ICON_SOURCE="$SCRIPT_DIR/ui-shell/src-tauri/icons/icon.png"
|
|
16
|
+
DESKTOP_PATH="$DESKTOP_DIR/dictate.desktop"
|
|
15
17
|
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/dictate"
|
|
16
18
|
CONFIG_PATH="$CONFIG_DIR/config.yaml"
|
|
17
19
|
DEFAULT_CONFIG_SOURCE="$SCRIPT_DIR/config/default-config.yaml"
|
|
@@ -20,6 +22,9 @@ PREPARE_TURBO=1
|
|
|
20
22
|
SEED_DEFAULT_CONFIG=1
|
|
21
23
|
STARTUP=1
|
|
22
24
|
INSTALL_UI=1
|
|
25
|
+
INSTALL_GPU="${DICTATE_INSTALL_GPU:-0}"
|
|
26
|
+
INSTALL_MEETING="${DICTATE_INSTALL_MEETING:-0}"
|
|
27
|
+
INSTALL_SCOPE="user"
|
|
23
28
|
PYTHON_BIN="${PYTHON_BIN:-python3}"
|
|
24
29
|
|
|
25
30
|
# Prefer the distro Python so --system-site-packages can see modules such as
|
|
@@ -30,17 +35,58 @@ fi
|
|
|
30
35
|
|
|
31
36
|
usage() {
|
|
32
37
|
cat <<EOF
|
|
33
|
-
Usage: $0 [--no-verify] [--no-prepare-turbo] [--no-seed-default-config] [--no-startup] [--no-ui] [--session-backend auto|x11|wayland]
|
|
38
|
+
Usage: $0 [--user|--system] [--gpu] [--meeting] [--no-verify] [--no-prepare-turbo] [--no-seed-default-config] [--no-startup] [--no-ui] [--session-backend auto|x11|wayland]
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
Default: --user.
|
|
41
|
+
|
|
42
|
+
--user installs dictate into ~/.local/share/dictate, links ~/.local/bin/dictate and
|
|
36
43
|
~/.local/bin/dictate-ui-server, seeds the default config on first install,
|
|
37
|
-
creates app launcher/autostart entries, prepares the
|
|
44
|
+
creates app launcher/autostart entries, prepares the Parakeet local model,
|
|
38
45
|
and installs the desktop "Quiet Console" UI shell when a build toolchain is
|
|
39
46
|
present (unless disabled). All steps degrade gracefully when prerequisites are
|
|
40
47
|
missing.
|
|
48
|
+
|
|
49
|
+
--system installs a Linux desktop package into system paths using apt/pkexec or
|
|
50
|
+
sudo. It is intentionally explicit because it requires administrator approval.
|
|
51
|
+
|
|
52
|
+
--gpu installs Dictate's GPU optional dependencies for local CUDA/ONNX Runtime
|
|
53
|
+
testing on capable machines.
|
|
54
|
+
|
|
55
|
+
--meeting installs Dictate's pyannote/torch optional dependencies for the
|
|
56
|
+
parakeet-pyannote Meeting lane. DiariZen and Sortformer still require their
|
|
57
|
+
runtime-specific setup before selecting those experimental lanes.
|
|
41
58
|
EOF
|
|
42
59
|
}
|
|
43
60
|
|
|
61
|
+
install_system_package() {
|
|
62
|
+
if [ "$(uname -s)" != "Linux" ]; then
|
|
63
|
+
echo "--system is only supported by this installer on Linux."
|
|
64
|
+
exit 2
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
local deb_path="${DICTATE_DEB:-}"
|
|
68
|
+
if [ -z "$deb_path" ]; then
|
|
69
|
+
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)"
|
|
70
|
+
fi
|
|
71
|
+
if [ -z "$deb_path" ] || [ ! -f "$deb_path" ]; then
|
|
72
|
+
echo "No local .deb found. Build one first:"
|
|
73
|
+
echo " DICTATE_BUNDLES=deb scripts/build-linux-desktop.sh"
|
|
74
|
+
echo "Or pass one explicitly:"
|
|
75
|
+
echo " DICTATE_DEB=/path/to/Dictate_..._amd64.deb $0 --system"
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
echo "Installing system package: $deb_path"
|
|
80
|
+
if command -v pkexec >/dev/null 2>&1; then
|
|
81
|
+
pkexec apt-get install -y "$deb_path"
|
|
82
|
+
elif command -v sudo >/dev/null 2>&1; then
|
|
83
|
+
sudo apt-get install -y "$deb_path"
|
|
84
|
+
else
|
|
85
|
+
echo "Need pkexec or sudo to install a system package."
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
89
|
+
|
|
44
90
|
detect_session_backend() {
|
|
45
91
|
if [ -n "${WAYLAND_DISPLAY:-}" ] || [ "${XDG_SESSION_TYPE:-}" = "wayland" ]; then
|
|
46
92
|
printf 'wayland\n'
|
|
@@ -80,6 +126,24 @@ while [ "$#" -gt 0 ]; do
|
|
|
80
126
|
--no-ui)
|
|
81
127
|
INSTALL_UI=0
|
|
82
128
|
;;
|
|
129
|
+
--gpu)
|
|
130
|
+
INSTALL_GPU=1
|
|
131
|
+
;;
|
|
132
|
+
--no-gpu)
|
|
133
|
+
INSTALL_GPU=0
|
|
134
|
+
;;
|
|
135
|
+
--meeting)
|
|
136
|
+
INSTALL_MEETING=1
|
|
137
|
+
;;
|
|
138
|
+
--no-meeting)
|
|
139
|
+
INSTALL_MEETING=0
|
|
140
|
+
;;
|
|
141
|
+
--user)
|
|
142
|
+
INSTALL_SCOPE="user"
|
|
143
|
+
;;
|
|
144
|
+
--system)
|
|
145
|
+
INSTALL_SCOPE="system"
|
|
146
|
+
;;
|
|
83
147
|
--session-backend)
|
|
84
148
|
shift
|
|
85
149
|
SESSION_BACKEND="${1:-}"
|
|
@@ -99,17 +163,33 @@ while [ "$#" -gt 0 ]; do
|
|
|
99
163
|
shift
|
|
100
164
|
done
|
|
101
165
|
|
|
166
|
+
if [ "$INSTALL_SCOPE" = "system" ]; then
|
|
167
|
+
install_system_package
|
|
168
|
+
exit 0
|
|
169
|
+
fi
|
|
170
|
+
|
|
102
171
|
if [ "$SESSION_BACKEND" = "auto" ]; then
|
|
103
172
|
SESSION_BACKEND="$(detect_session_backend)"
|
|
104
173
|
fi
|
|
105
174
|
|
|
106
|
-
|
|
175
|
+
EXTRAS=()
|
|
107
176
|
if [ "$SESSION_BACKEND" = "x11" ]; then
|
|
108
|
-
|
|
177
|
+
EXTRAS+=("x11")
|
|
109
178
|
elif [ "$SESSION_BACKEND" = "wayland" ]; then
|
|
110
|
-
|
|
179
|
+
EXTRAS+=("wayland")
|
|
111
180
|
elif [ "$SESSION_BACKEND" = "unknown" ]; then
|
|
112
|
-
|
|
181
|
+
EXTRAS+=("x11" "wayland")
|
|
182
|
+
fi
|
|
183
|
+
if [ "$INSTALL_GPU" -eq 1 ]; then
|
|
184
|
+
EXTRAS+=("gpu")
|
|
185
|
+
fi
|
|
186
|
+
if [ "$INSTALL_MEETING" -eq 1 ]; then
|
|
187
|
+
EXTRAS+=("meeting")
|
|
188
|
+
fi
|
|
189
|
+
PIP_TARGET="$SCRIPT_DIR"
|
|
190
|
+
if [ "${#EXTRAS[@]}" -gt 0 ]; then
|
|
191
|
+
extras_csv="$(IFS=,; printf '%s' "${EXTRAS[*]}")"
|
|
192
|
+
PIP_TARGET="${SCRIPT_DIR}[${extras_csv}]"
|
|
113
193
|
fi
|
|
114
194
|
|
|
115
195
|
echo "Detected install session backend: $SESSION_BACKEND"
|
|
@@ -125,6 +205,34 @@ elif command -v rpm >/dev/null 2>&1 && rpm -q dictate >/dev/null 2>&1; then
|
|
|
125
205
|
echo " Use one install method. To remove the package first: sudo dnf remove dictate"
|
|
126
206
|
fi
|
|
127
207
|
|
|
208
|
+
# Stop any running Dictate engine before we overwrite it, so the new install can
|
|
209
|
+
# claim the single-instance lock cleanly instead of colliding with a stale daemon.
|
|
210
|
+
stop_running_dictate() {
|
|
211
|
+
# Prefer the installed CLI's own clean stop; it knows the lock location.
|
|
212
|
+
if command -v dictate >/dev/null 2>&1 && dictate stop --quiet 2>/dev/null; then
|
|
213
|
+
return 0
|
|
214
|
+
fi
|
|
215
|
+
# Fallback (older builds without `dictate stop`): kill via the lock PID file.
|
|
216
|
+
local lockdir
|
|
217
|
+
if [ -n "${XDG_RUNTIME_DIR:-}" ]; then
|
|
218
|
+
lockdir="$XDG_RUNTIME_DIR/dictate-daemon.lockdir"
|
|
219
|
+
else
|
|
220
|
+
lockdir="/tmp/dictate-daemon-$(id -u).lockdir"
|
|
221
|
+
fi
|
|
222
|
+
local pidfile="$lockdir/pid"
|
|
223
|
+
[ -f "$pidfile" ] || return 0
|
|
224
|
+
local pid
|
|
225
|
+
pid="$(cat "$pidfile" 2>/dev/null || true)"
|
|
226
|
+
[ -n "$pid" ] || return 0
|
|
227
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
228
|
+
kill "$pid" 2>/dev/null || true
|
|
229
|
+
for _ in 1 2 3 4 5 6 7 8 9 10; do kill -0 "$pid" 2>/dev/null || break; sleep 0.3; done
|
|
230
|
+
kill -9 "$pid" 2>/dev/null || true
|
|
231
|
+
fi
|
|
232
|
+
}
|
|
233
|
+
echo "Stopping any running Dictate engine ..."
|
|
234
|
+
stop_running_dictate
|
|
235
|
+
|
|
128
236
|
echo "Creating venv at $INSTALL_DIR ..."
|
|
129
237
|
uv venv "$INSTALL_DIR/venv" --python "$PYTHON_BIN" --system-site-packages --quiet
|
|
130
238
|
|
|
@@ -140,11 +248,15 @@ ln -sf "$INSTALL_DIR/venv/bin/dictate-ui-server" "$BIN_DIR/dictate-ui-server"
|
|
|
140
248
|
echo "Installing icon ..."
|
|
141
249
|
mkdir -p "$ICON_DIR"
|
|
142
250
|
rm -f "$ICON_DIR/dictate-controls.png" "$ICON_DIR/dictate.png"
|
|
143
|
-
|
|
251
|
+
if [ -f "$APP_ICON_SOURCE" ]; then
|
|
252
|
+
install -m 644 "$APP_ICON_SOURCE" "$ICON_PATH"
|
|
253
|
+
else
|
|
254
|
+
install -m 644 "$SCRIPT_DIR/assets/dictate.png" "$ICON_PATH"
|
|
255
|
+
fi
|
|
144
256
|
|
|
145
257
|
echo "Installing desktop entry ..."
|
|
146
258
|
mkdir -p "$DESKTOP_DIR"
|
|
147
|
-
rm -f "$DESKTOP_DIR/dictate-settings.desktop" "$DESKTOP_DIR/dictate.desktop"
|
|
259
|
+
rm -f "$DESKTOP_DIR/dictate-settings.desktop" "$DESKTOP_DIR/Dictate.desktop" "$DESKTOP_DIR/dictate.desktop"
|
|
148
260
|
cat > "$DESKTOP_PATH" <<EOF
|
|
149
261
|
[Desktop Entry]
|
|
150
262
|
Name=Dictate
|
|
@@ -155,6 +267,10 @@ Type=Application
|
|
|
155
267
|
Categories=AudioVideo;Audio;
|
|
156
268
|
Keywords=voice;speech;transcription;dictation;asr;whisper;canary;
|
|
157
269
|
Terminal=false
|
|
270
|
+
# The Tauri shell window reports app_id "dictate-ui-shell" (GTK prgname). Without
|
|
271
|
+
# this, GNOME can't match the running window to this launcher and shows a second,
|
|
272
|
+
# icon-less "dictate-ui-shell" entry with a generic fallback icon.
|
|
273
|
+
StartupWMClass=dictate-ui-shell
|
|
158
274
|
EOF
|
|
159
275
|
|
|
160
276
|
update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true
|
|
@@ -172,6 +288,7 @@ Type=Application
|
|
|
172
288
|
Categories=AudioVideo;Audio;
|
|
173
289
|
Keywords=voice;speech;transcription;dictation;asr;whisper;canary;
|
|
174
290
|
Terminal=false
|
|
291
|
+
StartupWMClass=dictate-ui-shell
|
|
175
292
|
X-GNOME-Autostart-enabled=true
|
|
176
293
|
EOF
|
|
177
294
|
fi
|
|
@@ -206,22 +323,58 @@ install_desktop_ui() {
|
|
|
206
323
|
npm --prefix "$SCRIPT_DIR/ui" ci >/dev/null 2>&1 \
|
|
207
324
|
|| npm --prefix "$SCRIPT_DIR/ui" install >/dev/null 2>&1 \
|
|
208
325
|
|| { echo "UI front-end install failed; skipping the optional shell."; print_ui_hint; return 0; }
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
326
|
+
npm --prefix "$shell_src" ci >/dev/null 2>&1 \
|
|
327
|
+
|| npm --prefix "$shell_src" install >/dev/null 2>&1 \
|
|
328
|
+
|| { echo "UI shell tooling install failed; skipping the optional shell."; print_ui_hint; return 0; }
|
|
329
|
+
# Tauri validates configured bundle resources even for --no-bundle builds.
|
|
330
|
+
# Stage a per-user engine wrapper before the build so the resource glob exists,
|
|
331
|
+
# then install the same wrapper next to the shell binary below.
|
|
332
|
+
local bundled_engine_dir="$shell_src/src-tauri/engine"
|
|
333
|
+
mkdir -p "$bundled_engine_dir"
|
|
334
|
+
cat > "$bundled_engine_dir/dictate-engine" <<WRAP
|
|
335
|
+
#!/bin/sh
|
|
336
|
+
exec "$INSTALL_DIR/venv/bin/dictate" "\$@"
|
|
337
|
+
WRAP
|
|
338
|
+
chmod 755 "$bundled_engine_dir/dictate-engine"
|
|
339
|
+
# Build through the Tauri CLI, NOT a raw `cargo build`. The CLI enables the
|
|
340
|
+
# `custom-protocol` feature that embeds the frontend and serves it over the
|
|
341
|
+
# app protocol. A plain cargo build omits that feature, so the shell falls
|
|
342
|
+
# back to loading the dev server (localhost:5173) and shows "Could not connect
|
|
343
|
+
# to localhost: Connection refused" at runtime. --no-bundle: we only need the
|
|
344
|
+
# binary here, not a .deb/AppImage. (beforeBuildCommand rebuilds ui/dist.)
|
|
345
|
+
if ! ( cd "$shell_src" && npm run tauri -- build --no-bundle ); then
|
|
213
346
|
echo "Desktop UI shell build failed; the engine + tray remain fully functional."
|
|
214
347
|
print_ui_hint
|
|
215
348
|
return 0
|
|
216
349
|
fi
|
|
217
350
|
install -m 755 "$shell_src/src-tauri/target/release/dictate-ui-shell" "$target"
|
|
218
351
|
echo "Installed desktop UI shell: $target"
|
|
352
|
+
|
|
353
|
+
# Give the shell an absolute-path engine right next to it. The shell's
|
|
354
|
+
# bundled-engine lookup checks <exe_dir>/engine first — before any system
|
|
355
|
+
# path (e.g. a leftover /usr/lib/Dictate from an old .deb) and independent of
|
|
356
|
+
# the minimal PATH a desktop launcher provides. Pointing it at the per-user
|
|
357
|
+
# venv means one process both dictates and serves, and keeps sys.executable
|
|
358
|
+
# inside ~/.local so the install reads as "linux-user" and the in-app updater
|
|
359
|
+
# never needs sudo/pkexec.
|
|
360
|
+
local engine_dir="$BIN_DIR/engine"
|
|
361
|
+
mkdir -p "$engine_dir"
|
|
362
|
+
install -m 755 "$bundled_engine_dir/dictate-engine" "$engine_dir/dictate-engine"
|
|
363
|
+
echo "Installed per-user engine launcher: $engine_dir/dictate-engine"
|
|
219
364
|
}
|
|
220
365
|
|
|
221
366
|
if [ "$INSTALL_UI" -eq 1 ]; then
|
|
222
367
|
install_desktop_ui
|
|
223
368
|
fi
|
|
224
369
|
|
|
370
|
+
if [ -x "$BIN_DIR/dictate-ui-shell" ]; then
|
|
371
|
+
echo "Pointing launcher entries at the desktop UI shell ..."
|
|
372
|
+
sed -i "s|^Exec=.*|Exec=$BIN_DIR/dictate-ui-shell|" "$DESKTOP_PATH"
|
|
373
|
+
if [ "$STARTUP" -eq 1 ] && [ -f "$AUTOSTART_DIR/dictate.desktop" ]; then
|
|
374
|
+
sed -i "s|^Exec=.*|Exec=$BIN_DIR/dictate-ui-shell|" "$AUTOSTART_DIR/dictate.desktop"
|
|
375
|
+
fi
|
|
376
|
+
fi
|
|
377
|
+
|
|
225
378
|
if [ "$SEED_DEFAULT_CONFIG" -eq 1 ]; then
|
|
226
379
|
if [ ! -f "$CONFIG_PATH" ]; then
|
|
227
380
|
if [ ! -f "$DEFAULT_CONFIG_SOURCE" ]; then
|
|
@@ -240,6 +393,9 @@ if [ "$SEED_DEFAULT_CONFIG" -eq 1 ]; then
|
|
|
240
393
|
fi
|
|
241
394
|
|
|
242
395
|
DICTATE_BIN="$INSTALL_DIR/venv/bin/dictate"
|
|
396
|
+
if [ -n "$PACKAGE_VERSION" ]; then
|
|
397
|
+
"$DICTATE_BIN" set-installed-package-version "$PACKAGE_VERSION" >/dev/null 2>&1 || true
|
|
398
|
+
fi
|
|
243
399
|
|
|
244
400
|
run_logged_check() {
|
|
245
401
|
local label="$1"
|
|
@@ -266,14 +422,15 @@ run_logged_check() {
|
|
|
266
422
|
|
|
267
423
|
if [ "$PREPARE_TURBO" -eq 1 ]; then
|
|
268
424
|
PREPARE_LOG="/tmp/dictate-install-prepare.log"
|
|
425
|
+
# Keep the legacy flag name for installer compatibility, but prepare the
|
|
426
|
+
# fresh local default: Parakeet v2.
|
|
269
427
|
run_logged_check \
|
|
270
|
-
"dictate prepare-model --stt-backend
|
|
428
|
+
"dictate prepare-model --stt-backend parakeet --device auto --compute-type int8" \
|
|
271
429
|
"$PREPARE_LOG" \
|
|
272
430
|
1800 \
|
|
273
431
|
"$DICTATE_BIN" \
|
|
274
432
|
prepare-model \
|
|
275
|
-
--stt-backend
|
|
276
|
-
--model turbo \
|
|
433
|
+
--stt-backend parakeet \
|
|
277
434
|
--device auto \
|
|
278
435
|
--compute-type int8
|
|
279
436
|
fi
|
|
@@ -283,14 +440,12 @@ if [ "$VERIFY" -eq 1 ]; then
|
|
|
283
440
|
run_logged_check "dictate --help" "$VERIFY_LOG" 20 "$DICTATE_BIN" --help
|
|
284
441
|
run_logged_check "dictate benchmark --help" "$VERIFY_LOG" 20 "$DICTATE_BIN" benchmark --help
|
|
285
442
|
run_logged_check \
|
|
286
|
-
"dictate doctor --quick
|
|
443
|
+
"dictate doctor --quick" \
|
|
287
444
|
"$VERIFY_LOG" \
|
|
288
445
|
20 \
|
|
289
446
|
"$DICTATE_BIN" \
|
|
290
447
|
doctor \
|
|
291
|
-
--quick
|
|
292
|
-
--stt-backend faster-whisper \
|
|
293
|
-
--model turbo
|
|
448
|
+
--quick
|
|
294
449
|
fi
|
|
295
450
|
|
|
296
451
|
if [ "$STARTUP" -eq 1 ]; then
|
package/package.json
CHANGED
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,10 +10,10 @@ param(
|
|
|
10
10
|
)
|
|
11
11
|
|
|
12
12
|
$ErrorActionPreference = "Stop"
|
|
13
|
-
$DictateVersion = "2026.
|
|
13
|
+
$DictateVersion = "2026.7.4-unstable.3.1"
|
|
14
14
|
|
|
15
15
|
if (-not $ArchiveUrl) {
|
|
16
|
-
$ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/
|
|
16
|
+
$ArchiveUrl = "https://github.com/arcforgelabs/dictate/archive/3f6f050451d0d0e140193534f102c6b5f2b58b19.zip"
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
if (-not $InstallRoot) {
|
|
@@ -105,6 +105,10 @@ try {
|
|
|
105
105
|
if ($LASTEXITCODE -ne 0) {
|
|
106
106
|
throw "Dictate Windows updater failed with exit code $LASTEXITCODE."
|
|
107
107
|
}
|
|
108
|
+
$dictateExe = Join-Path $installRootPath ".venv\Scripts\dictate.exe"
|
|
109
|
+
if (Test-Path $dictateExe) {
|
|
110
|
+
& $dictateExe set-installed-package-version $DictateVersion | Out-Null
|
|
111
|
+
}
|
|
108
112
|
} finally {
|
|
109
113
|
if (Test-Path $stagingRoot) {
|
|
110
114
|
Remove-Item -Recurse -Force $stagingRoot
|
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
|