- Section header icons were pushed down by bt-section CSS margin-top;
now only applied to the row container, not the image widget
- Wakeword benchmark shows a full TreeView table: per-voice Detected/Total/
Recall%/False-fires/Time with colour coding, plus aggregate row per engine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Engine checkboxes let you pick which wakeword servers to include in the run
- Wakeword combo selects which model/phrase to test; leave empty for each
engine's own, pick a specific one (e.g. okay_computer) to override all
- Also fixes: TTS ⟳ no longer fills model combo with Kokoro voice names
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sound fields accept WAV/MP3/OGG/FLAC/M4A/AAC/AIFF/Opus. Browse dialog
auto-plays each file on selection so you can preview before confirming.
sound.py falls back to ffplay/gst-play-1.0 for formats not supported
by pw-play/paplay.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add/Quickstart/Reload/Delete buttons + Name field mirror the STT engines UI.
Four quickstart templates for common wyoming-openwakeword setups.
Migration: existing wakeword_uri/model auto-promoted to first preset.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Device/Compute rows already only show for local engines; the titled section
break was redundant and visually separated fields that belong together.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HeaderBar replaces bottom button row — Save and Save & Restart appear in the
title bar on the right, X button closes. Section header icons vertically
centered using SMALL_TOOLBAR size and valign=CENTER.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GTK symbolic icons on every tab label and every section/card header.
Also fixes the resize grip landing in the tab bar instead of the bottom-right corner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Draws a classic dotted SE-corner grip overlaid on the bottom-right of the
notebook so users know the settings dialog is resizable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Probe remote engines' /metrics for process_resident_memory_bytes or
container_memory_rss; show actual server-side MB in RAM column.
Falls back to "server" when the endpoint is not exposed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removes whitespace gap between STT config card and device/precision card
by hiding the latter when a Server or Realtime engine type is selected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
String sort put "14.85" before "2.47". Now uses a custom comparator
that strips % and non-numeric markers before comparing as float.
Non-numeric values ("—", "server") sort to the bottom.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remote engines run models server-side so local RSS never changes.
Show "server" instead of "—" to make the reason clear.
Local engines show measured MB; local already-loaded shows "—".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
300s was too long; nobody waits 5 min for a result.
30s gives slow remote servers a fair window while still failing fast.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add _api_base() to strip endpoint-specific path suffixes before probing
/models, /metadata, /info — fixes /transcribe/models 404 spam when URL
ends with a custom path like /v1/transcribe
- Increase default transcribe() timeout 60s → 300s — WhisperX with speaker
diarization (pyannote) takes 2-5 min and was always timing out
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add "WhisperX server" quickstart template pre-filled with /transcribe endpoint
- Add tooltip to STT URL field explaining that non-standard servers (WhisperX)
should use the full endpoint as the URL, not /v1
- _url_field_lb accepts optional tooltip= kwarg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- benchmark.py: measure RSS delta via /proc/self/status before/after each
transcription; add ram_mb field to BenchRow
- gtksettings.py: add RAM (MB) column to results table (index 8); tooltip
column shifted to index 10
- CHANGELOG.md: full history from v2.02.00 through v2.03.01
- README.md: benchmark description updated to mention RAM column
- MANUAL.md: benchmark result columns as table; Log tab documents
level filter dropdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stability: remove blocking TCP socket call from _collect() — was
freezing the GTK main thread on Save when wakeword server unreachable;
now checks async in background and logs result
- Thread safety: fix _ww_load() reading GTK widget from background thread;
capture URI on main thread before spawning
- WhisperX 404: _transcribe_remote now detects non-standard paths
(anything other than /v1) and uses the URL as the full endpoint,
so http://host/transcribe works without /audio/transcriptions appended
- Log levels: logbuffer stores (ts, level, msg) tuples; log() accepts
level= (DEBUG/INFO/WARNING/ERROR); Log tab gets a Level dropdown
(Verbose/Info/Warning/Error) that filters displayed entries live
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a "Server preset" combo in the Hands-free wakeword card that lists
all configured wakeword engines by name. Selecting one auto-fills the
URI and model fields and re-probes the connection. Selection persists
via wakeword_active in config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- About tab: render License with _md_panel instead of _text_panel
- Benchmark pane: set_size_request 320px min, shrink=False on both sides
so the engine list and results table are always visible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After running a benchmark, each engine's best result (time, accuracy)
is persisted to config and shown as a small info line in the Engines tab
when that engine is selected. Updates live as the benchmark runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- stt.py: add ModelMeta dataclass, fmt_languages(), list_models_meta(),
detect_remote_device(); refactor list_models() to delegate
- benchmark.py: add languages field to BenchRow; fetch via _get_langs()
with URL-level caching using list_models_meta()
- gtksettings.py: show language labels per engine in checkbox list;
add language codes to search filter; add Lang column to results table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tab font-size 12px → 14px (matches body text)
- Inactive tabs: muted foreground color so they're clearly readable
but visually distinct from the active tab
- Active tab: bold + blue (#1a73e8), slightly more padding (8px 18px)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Schema: MAJOR.FEATURE.FIX
MAJOR — breaking changes / major redesign
FEATURE — two-digit, new user-visible features (00-99)
FIX — two-digit, bug fixes within a feature release (00-99)
Renamed 2.1.2 → 2.01.02 to start the new format.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_stt_commit() accesses self.stt_name which only exists after the Engines
tab is lazily built. Guard with hasattr so running a benchmark directly
from the Benchmark tab no longer throws AttributeError.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Engine checklist and results table now split by a Gtk.Paned (vertical)
so the user can drag the divider to give more room to either panel
- WAV and reference .txt paths are written to disk (save()) the moment a
file is picked via the file chooser, without needing to click Save;
also saved on Run if they changed since the last disk write
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Benchmark tab: scrollable engine checklist above Run with live
reachability dots (green/red), name+model+URL filter, All/None buttons;
_run_bench respects selection and shows clear message when nothing ticked
- About tab: changelog now rendered via _md_panel (markdown headers, bold,
lists) instead of plain monospace _text_panel
- Manual tab: graceful fallback with clickable GitHub link when MANUAL.md
is not installed; build-deb.sh now copies MANUAL.md from repo root so
/opt/blitztext/MANUAL.md exists in future installs
- STT quickstart templates expanded: Speaches docker, whisper.cpp server,
NVIDIA NIM/Parakeet, five built-in local model sizes (tiny→large-v3)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap ListStore in TreeModelSort and set sort_column_id on every column
so clicking any header sorts ascending/descending.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Engines tab: move Test button out of the toolbar, place it in a row
directly beside the result label so button and output are co-located;
result label is now selectable so error text can be copied
- Benchmark tab: add bench_wav/bench_ref/bench_expand_models to Config;
file pickers restore last-used paths on open; any change (file-set,
toggle, or Run) writes directly to cfg so paths survive without Save;
[benchmark] section written to config.toml on Save
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- stt.detect_remote_device(): probes /info (faster-whisper-server) then
/metadata (NVIDIA NIM) to detect CUDA vs CPU; cached per unique URL
- BenchRow gains url field; Device column now shows "CUDA" for GPU remotes
instead of the generic "remote"
- Benchmark table gains URL column (scheme stripped, max 180px wide)
- "Test all models per engine" checkbox: fetches list_models() for each
remote engine and expands to one row per model when checked
- benchmark.run() gains expand_models parameter
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- _run_bench: deduplicate STT engines by name before running; show a
warning in the summary line listing which names were skipped
- _bench_add_row: replace raw HTTP error strings with human-readable
reasons ("Wrong model name", "Server offline", "Timed out", etc.);
full raw error stored in hidden column 7 shown as row tooltip on hover
- bench_store: added 8th column (tooltip text); tree.set_tooltip_column(7)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BenchRow gains `best_for` field: "Short clips" / "Short / medium" /
"Long / batch" / "Streaming" — derived from engine type and model name
- Device now shows "CUDA" instead of "GPU" for clarity
- Benchmark table gains a "Best for" column between Device and Time(s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- stt._transcribe_remote: no longer falls back to "whisper-1" when
engine.model is empty — omits the field entirely so Riva/NIM uses its
default model instead of rejecting the request with HTTP 400
- gtksettings._page: switch vertical scroll policy to ALWAYS and disable
overlay scrolling so the scrollbar is permanently visible, making
it obvious when a tab has more content below the visible area
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause of session freeze confirmed: a 15 000-char code block was typed
character-by-character via xdotool at 12ms/char = ~3 min, flooding the X11
per-client event buffer until the entire session froze.
- paste.py: any text >300 chars or containing newlines auto-upgrades to
clipboard paste (instant Ctrl+V) regardless of configured output mode.
xdotool type is kept only for short single-line text where it matters.
- daemon.py: replace on_token "".join(acc) accumulation (O(n²) for long
code blocks) with a sliding deque that shows only the last 400 chars in
the overlay — Pango no longer re-lays out a growing 15 KB string on each
incoming token.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Emoji picker: SearchEntry at top filters all categories via unicodedata.name()
in real time; category view hides while searching, restores on clear
- Manual tab: add pkg_dir/MANUAL.md to _app_paths() search list so it works in
both venv and deb installs (MANUAL.md deployed alongside the package)
- bt-infobox CSS: replace @theme_selected_bg_color (saturated blue) with a
neutral 5% mix of fg/bg so banner text is always readable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Every _labeled and _switch_row field now gets a clickable ⓘ info button
that opens a plain-language help popover — targeted at non-technical users
- New Manual tab in Settings renders MANUAL.md directly inside the dialog
- STT and LLM toolbars each get a "Quickstart ▾" button: a menu of common
providers (OpenAI, Groq, OpenRouter, Ollama, LM Studio, vLLM, llama-swap,
faster-whisper-server, NVIDIA Riva) that pre-fills the engine form in one click
- Engine type combos now show human-readable labels ("Internal — faster-whisper",
"LAN server — runs on your machine", "GPU (CUDA)", "int8 — fast, less memory")
while storing the same internal key values (no config migration needed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a 😀 button next to the Icon (emoji) entry in Settings → Presets.
Clicking it opens a GTK popover with 60 common emojis in a scrollable
flow grid; selecting one writes it into the field and closes the picker.
The entry still accepts direct keyboard input as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gtksettings: build each notebook tab on first view instead of all up front, so
the dialog opens instantly (was ~1.3s building Input/Benchmark file-choosers);
_collect() force-builds unvisited tabs before saving so no field is missed.
- gtksettings: connection dot now sits left of the URL entry (like the Engines
tab) instead of at the far right.
- gtkui: open_settings raises the existing dialog instead of opening a second.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Green/red/grey reachability dot next to the wakeword and TTS endpoint fields,
matching the existing STT/LLM engine dots. Lightweight background TCP probe,
refreshed on open, on reload (⟳), and on focus-out — never blocks dialog build.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On headless/minimal desktops the gvfs org.gtk.vfs.UDisks2VolumeMonitor dbus
service often fails to activate; each Gtk.FileChooserButton then blocked ~25s on
a StartServiceByName timeout while realizing, so the Settings dialog never
appeared and the stalled main loop froze the control panel too. Set
GIO_USE_VOLUME_MONITOR=unix in run_gui() before any window is realized.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Send by voice: a configured edge-anchored phrase (e.g. "computer send") is
stripped and the rest is delivered AND submitted with Enter. Off by default;
[routing] send_keywords + Settings → Input.
- Wakeword benchmark (Settings → Benchmark): synthesize the wake phrase in
random voices via any OpenAI-compatible TTS server, stream to
wyoming-openwakeword, report recall / false-fires / per-voice breakdown.
New [tts] config block.
- Tests: test_voice_send.py, test_wakeword_bench.py.
- Ignore agent workspace folder jules/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The live waveform and silence auto-stop countdown were driven by a level
meter that was the last user of sounddevice/PortAudio, which hangs opening
the default input on PipeWire systems — so both stayed blank on the hotkey
and wakeword paths alike. Rewrite LevelMeter to stream raw PCM from the same
recorder as the WAV path (pw-record/parecord/arecord) and RMS it; identical
API, scaling, and ~10 Hz cadence. Also fixes the Settings mic-level preview.
Set GLib prgname/application name to "Blitztext" before any window is
realized (and add StartupWMClass to the .desktop) so the taskbar and GNOME's
"… is not responding" dialog show the app name instead of "__main__.py",
without touching the `python -m blitztext` entry point.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a configurable voice cancel: saying "abbrechen" (or "cancel") at the start
or end of a clip discards the whole dictation — it is never routed onward,
rewritten, or typed. The rescue for accidentally triggered (e.g. wakeword)
recordings. Matched the same edge-anchored, ASR-tolerant way as routing keywords
via routing.is_cancel(), so the word buried mid-sentence won't trip it; checked
in Daemon._process right after transcription, before routing/rewrite/delivery.
Configurable via [routing] cancel_keywords (default ["abbrechen", "cancel"];
empty disables) and Settings -> Mic/Cues -> "Cancel words". The overlay briefly
shows "Abgebrochen". Docs (both READMEs) and CHANGELOG updated; tests cover the
matcher, the config round-trip, and the discard/deliver pipeline branches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>