@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 +63 -42
- package/config/default-config.yaml +3 -1
- package/install-windows.ps1 +4 -2
- package/install.ps1 +1 -1
- package/install.sh +118 -14
- package/package.json +1 -1
- package/uninstall.sh +3 -0
- package/update.ps1 +1 -1
- package/update.sh +37 -1
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,
|
|
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 `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.
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
177
|
-
|
|
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
|
|
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:**
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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 ./
|
|
209
|
+
sudo apt install ./Dictate_*_amd64.deb
|
|
190
210
|
```
|
|
191
211
|
|
|
192
|
-
The app lives in the tray
|
|
193
|
-
|
|
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
|
-
- [
|
|
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
|
|
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
|
package/install-windows.ps1
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
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/
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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 --
|
|
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
|
|
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
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
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
|