The matched-preset feedback used _dnotify, which is suppressed for hands-free
sessions — so wakeword users never saw which keyword/preset fired.
- Add a dedicated "Announce matched preset" notification (_rnotify), gated by
a new [general] notify_routing flag (default on) and independent of the
hands-free silence, so it shows for wakeword commands too. It only fires on a
real routing match, so it never spams when nothing is said.
- Show each preset's emoji in that notification; add a per-preset "Icon (emoji)"
field to the Presets editor so matches are visually distinct.
- General-tab toggle; MANUAL + CHANGELOG updated. Adds a test that the match is
announced even when _session_silent is set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reported issues from hands-free use:
- Wakeword WAVs didn't play: the new "Play audio cues" master switch
([sounds] enabled) also gated the hands-free Sound: detected/captured cues,
so enabled=false silenced them. The cues live in a separate UI section, so
this was surprising. Wakeword cues are now independent of that switch: they
play whenever a file is set, and an empty field means silent (no system-chime
fallback) — which is also how you turn a hands-free cue off. The master switch
now governs only the manual (keyboard) before/after chimes.
- Clarified the four sound fields' tooltips/labels (detected/captured = hands-free
only; before/after = manual only) and the empty-field behaviour.
- New "Silence to stop (s)" setting (Settings → Input → Hands-free, or
[wakeword] silence_seconds, default 2.0): user-defined trailing-silence
timeout for hands-free auto-stop (was hard-coded to 2.5 s).
- Add MANUAL.md documenting every setting in every tab; link it from the README.
Tests: cue independence + manual-gating + roundtrip (17 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two follow-ups from hands-free testing:
- Audio cues had no off-switch: empty sound fields fall back to the freedesktop
system chime, so wakeword/recording always made noise. Add a [sounds] enabled
master flag (default true) exposed as "Play audio cues" in Settings → Input.
When off, _play_cue/_play_sound are no-ops — fully silent operation.
- The VAD level meter (sounddevice/PortAudio) leaked harmless thread-teardown
errors ("pthread_join ... failed", "PaUnixThread_Terminate ... failed") to the
terminal on every clip end. PortAudio writes these straight to fd 2, so wrap
the stream open/close in a fd-level stderr suppressor (_quiet_c_stderr).
Tests: cue gating respects the master switch (16 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two optional WAVs in the Hands-free section: "Sound: detected" plays when the
wakeword fires (your cue to speak now) and "Sound: captured" plays when the
command is taken (silence/stop). For hands-free sessions these take precedence
over the general [sounds] cues, falling back to them and then to the built-in
system sound. The general cues are relabelled "Audio cues (manual dictation)".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a plain-language info box to the top of every Settings tab (Presets,
Engines, Input, General, Benchmark, Log, About), exposed to screen readers, and
tooltips (wired to ATK descriptions) on the preset, general, and input-mode
controls — for non-technical and blind users (barrierefrei).
Complete the unreleased 1.2.0 changelog with the engine manager, Benchmark tab,
custom audio cues, Log tab, click-to-bind hotkeys, the GTK rebuild, voice
routing, the modifier hotkey scheme, and the quality gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New sound.py plays a user WAV (or a built-in system sound) without blocking.
Config gains [sounds] before/after paths. The daemon plays the "before" cue when
recording starts and the "after" cue on every stop (stop+paste, stop+paste+Enter,
or auto-stop on silence — all funnel through finish_dictation). Input tab gets an
Audio cues section: file pickers with play-test and clear-to-default buttons.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Wayland support: text delivery using wtype or ydotool (paste.py)
- Testing: added Pytest coverage for routing, quality, and config logic
- CI/CD: added test-linux job to .github/workflows/ci.yml
- Feature: Hands-free dictation using an external wyoming-openwakeword
server, respecting the /tmp/wake_muted toggle.
- BenchRow gains a device field; Transcriber records its resolved device, so
local engines report CPU or GPU and remote engines show "remote". New Device
column in the results table.
- Accuracy is now case-sensitive by default (capitalisation counts) so an
all-lowercase transcript no longer scores 100%. Punctuation is still ignored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New benchmark.py: word-error-rate accuracy + a run() that times each STT engine
on a reference clip. Settings gains a Benchmark tab: pick a .wav and a matching
.txt, run all STT engines, see Time + Accuracy per engine and a summary of the
fastest and most accurate. Add presets for each model you want compared.
Fix "Local engine selected but the model isn't loaded." on Test: a cached
_transcriber_for() loads the local model on demand (the daemon only preloads it
when a local engine is active). Tested: local small 2.17s/100% vs remote :8010.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both STT and LLM engine editors gain a Name field (first field). Editing it
renames the engine and updates the dropdown on commit; Save persists it. Fixes
the inability to rename "New STT"/"New LLM" after Add.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Local-Whisper Device + Compute type now live under the STT engine section
("Local engine — device & precision"), removed from the General tab. The
local engine's Model field shows the effective whisper size.
- LLM engines gain a Type field (local | cloud), mirroring STT, stored in config.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New logbuffer.py captures app messages (log()) and library logs (faster-whisper,
huggingface_hub) into a ring buffer, mirrored to stderr. Settings gains a Log
tab: monospace view, 1s refresh, auto-scroll, Copy, Clear — so model
download/load progress is visible instead of an opaque "Loading…". transcribe
and daemon now log via the buffer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the model combo with a ModelPicker: an editable field plus a ▾ button
that opens a popover with a SearchEntry on top and a scrollable, filtered list
of models (HuggingFace-style). Add a refresh icon next to each engine URL that
loads models from {url}/models on demand. Verified: 8010 -> 428 models, typing
"turbo" filters to the turbo variants. You can still type a custom model name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Each key field (Start/Stop+paste/Stop+paste+Enter/Cancel, and the preset
Hotkey) gets a Set button that captures the next keypress and writes it in the
config format. Captures modifier-only chords (e.g. Ctrl+Win -> <ctrl>+<cmd>) by
accumulating pressed keys and finalising on first release; key events are
swallowed while binding so they don't leak into the UI. Tested: token mapping,
combo formatting, and the bind flow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The engine Model field is now an editable combo with a type-to-search
EntryCompletion (substring match). When you enter a remote engine URL it fetches
the model list from {url}/models (OpenAI `data[].id` or Ollama `models[].name`)
and fills the dropdown; local STT offers the whisper sizes. stt.list_models
added; tested live (8010 -> 428 models, 28080 -> 18, 19001 -> 1). You can still
type a custom model name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
set_placeholder_text on the preset and engine entries (URL, model, API key env,
keywords, hotkey, temperature, language) so empty fields show light-grey
examples of what to enter.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New GTK settings with a dropdown+editor pattern across four tabs:
- Presets: select/add/delete prompt presets; edit name, keywords, hotkey, mode,
per-preset model/temperature, and a clearly-bordered prompt textfield.
- Engines: STT and LLM engine managers with green/red online-offline status
dots, add/edit/delete, active selector, and an STT record-and-benchmark Test.
- Input: input scheme + keys + quality-gate settings.
- General: microphone picker with a live sounddevice level meter, output,
language, notifications, launch-on-login toggle, and local-Whisper settings.
Adds audio.py (mic enumeration via pactl + LevelMeter) and autostart.py
(~/.config/autostart entry). Recorder + daemon honour the selected mic.
Requirements gain sounddevice + tomli-w.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the tkinter panel with a GTK3 control panel (gtkui.py) + settings
dialog (gtksettings.py): rounded CSD window, gradient background, circular icon
avatars, hotkey pills, hover states, Ubuntu font. Runs on the GTK main loop,
unified with the AppIndicator tray (no tkinter event-loop pump). Status updates
marshalled via GLib.idle_add. Removes the old tkinter gui.py.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>