- Guard _refresh_status() with hasattr checks so opening STT Engines
before LLM Engines page is built no longer crashes the builder silently
- ww_status label: add max_width_chars(30) + ellipsize END so long model
lists don't widen the Wakeword page
- Benchmark STT sel_sw: NEVER→AUTOMATIC horizontal policy so wide engine
names scroll internally instead of propagating to the dialog
- _combo()/_type_combo(): set_size_request(10,-1) so ComboBoxText widgets
(e.g. long microphone names) can shrink below natural width
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_switch_row description labels had set_line_wrap(True) but no
set_max_width_chars, so GTK computed their natural width as the full
un-wrapped text (~700px for 87-char descriptions). With NEVER horizontal
policy on the page ScrolledWindow this propagated to the dialog, making
Keyboard/Wakeword/Engines/Benchmark pages 1000–1360px wide.
Fix: add set_max_width_chars(50) to description labels, and change the
page SW horizontal policy from NEVER to AUTOMATIC as a safety net.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Stack.set_homogeneous(True) was requesting the max natural size of all
children (incl. wide Benchmark TreeViews) and forcing the dialog to 1000+px
wide for every page. Reverted to False so the window stays at 860×700 and
pages scroll if taller than the viewport.
Also removed the NEVER/NEVER ScrolledWindow policy on both benchmark pages —
it was propagating natural TreeView width up to the Stack, amplifying the
homogeneous bug.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Stack.set_homogeneous(True) keeps dialog height constant when switching pages
- Benchmark WW paned position raised 260→390 so all TTS config, engine
checkboxes, wakeword/samples/run fields are visible without scrolling
- Both benchmark pages disable their page-level ScrolledWindow so the
Paned fills the viewport rather than growing to natural height
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>
Fix a desktop-session freeze (forced logout/reboot) caused by the overlay's
AT-SPI caret tracker: it subscribed to the high-frequency object:text-caret-moved
signal and made synchronous, blocking AT-SPI reads from inside the event handler,
re-entering the a11y dispatcher and getting stormed by the app's own xdotool
typing until GNOME stopped responding. Now track focus changes only and read the
caret rectangle lazily, once, when the overlay shows — never on the hot path.
Fuse voice-routing feedback into the overlay instead of a desktop notification:
show the matched preset's emoji, name, and spoken keyword on a banner, narrate
the phase (Transcribing -> Rewriting), and stream the LLM rewrite into the bubble
token-by-token. Redundant per-dictation notifications are suppressed when the
overlay is present (errors still notify); headless/overlay-off is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A full circle wraps the mic glyph and drains clockwise as the trailing-
silence timer runs out, recolouring cyan→amber→red and emptying exactly
as auto-stop fires. The daemon emits the countdown from the same VAD loop
that decides auto-stop, so the ring stays in sync; the overlay drains it
against its own clock for smooth motion and fades it in/out so word gaps
don't flicker it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Show a translucent, click-through bubble the moment recording starts (by
hotkey or wakeword): a pulsing microphone, a live waveform of the mic
level, and the recognised text (word-by-word when streaming, otherwise a
brief final-result confirmation). The tail points at the text caret via
AT-SPI accessibility, falling back to the mouse pointer, then a screen
corner. Also gives hands-free wakeword sessions visible feedback, whose
notifications are suppressed by design. X11 only.
- overlay.py: GTK override-redirect HUD (mic + waveform + bubble), drawn
with Cairo; thread-safe, marshalled onto the GTK loop.
- caret.py: best-effort anchor (AT-SPI caret -> pointer -> window/corner).
- daemon: optional level_cb/text_cb hooks; reuses the VAD level meter for
non-streaming, a dedicated meter for streaming. Stays UI-agnostic.
- gtkui: instantiate the overlay, drive show/update/hide from status.
- General settings: "Visual overlay" toggle; config overlay_enabled /
overlay_anchor (default on, "caret").
- Docs: CHANGELOG 1.5.0, version bump, README + MANUAL.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump version to 1.4.0 and cut the [1.4.0] - 2026-06-07 changelog section:
- "Announce matched preset" notification (shown even hands-free) + per-preset
emoji icons.
- Voice-routing no-keyword default now prefers a transcribe preset instead of
the first preset.
Refresh the README status line.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bump version to 1.3.0 and cut the [1.3.0] - 2026-06-07 changelog section
(wakeword pause toggle, independent hands-free audio cues, configurable
auto-stop silence, notification hygiene, PortAudio noise suppression, and the
new MANUAL.md). Refresh the README status line.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
packaging/build-deb.sh builds an installable blitztext_<ver>_arm64.deb with a
desktop entry, app icon, and launcher. Bundles a relocatable venv (all Python
deps, no pip at install) and declares system deps (python3-gi, xdotool,
libnotify-bin, recorder). Built on /usr/bin/python3 so the tray works out of
the box. Installs via the Software app or `apt install ./…deb`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Minimal flat design with the Ubuntu font: clickable workflow rows with hover
(click to record/stop), muted descriptions and hotkey hints, subtle dividers,
and text-style Settings/Quit. Drops monogram avatars and per-row buttons.
Settings window restyled to match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The upstream app is macOS-only (Swift/SwiftUI, CoreML/WhisperKit) and can't
run on Linux or in a container. This adds a native host tool under linux/ that
reproduces the workflow: focus any text field, press a hotkey, speak, and the
optionally-rewritten text is typed into that field.
- Engine: pynput global hotkeys → mic record → local faster-whisper →
optional OpenAI-compatible rewrite → xdotool typing into the focused window
- Frontends: system tray (AppIndicator, default), tkinter control panel,
and headless modes
- Config-driven workflows in ~/.config/blitztext/config.toml with per-workflow
prompt/model/temperature overrides
- Packaging: install.sh, requirements.txt, systemd user unit
- Targets X11; local transcription runs CPU int8 on this arm64 host
See linux/CHANGELOG.md and linux/README.md for details.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>