From 5458fdc1eb4396c410acc2f6631b48d9edb0c04b Mon Sep 17 00:00:00 2001 From: mARTin-B78 Date: Tue, 9 Jun 2026 18:29:01 +0200 Subject: [PATCH] engines: Test button inline with result + selectable; benchmark: persist WAV/ref paths (v2.0.7) - 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 --- linux/blitztext/__init__.py | 2 +- linux/blitztext/config.py | 12 ++++++++++ linux/blitztext/gtksettings.py | 40 ++++++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/linux/blitztext/__init__.py b/linux/blitztext/__init__.py index 944024a..f62dbea 100644 --- a/linux/blitztext/__init__.py +++ b/linux/blitztext/__init__.py @@ -6,4 +6,4 @@ counterpart to the macOS Blitztext menu bar app: it runs natively on the host (not in a container) so it can type into any application via xdotool. """ -__version__ = "2.0.6" +__version__ = "2.0.7" diff --git a/linux/blitztext/config.py b/linux/blitztext/config.py index b61fa1d..1b08fd8 100644 --- a/linux/blitztext/config.py +++ b/linux/blitztext/config.py @@ -120,6 +120,10 @@ class Config: # Wakeword engines to compare in the benchmark (each a wyoming-openwakeword # endpoint). Empty → the benchmark falls back to the live [wakeword] above. wakeword_engines: list[WakewordEngine] = field(default_factory=list) + # STT benchmark: last-used WAV / reference transcript paths and options + bench_wav: str = "" + bench_ref: str = "" + bench_expand_models: bool = False # workflows workflows: list[Workflow] = field(default_factory=list) @@ -236,6 +240,9 @@ def load(path: Path = CONFIG_PATH) -> Config: tts_api_key_env=tts.get("api_key_env", ""), tts_model=tts.get("model", ""), tts_voices=list(tts.get("voices", ["alloy", "echo", "fable", "onyx", "nova", "shimmer"])), + bench_wav=data.get("benchmark", {}).get("wav", ""), + bench_ref=data.get("benchmark", {}).get("ref", ""), + bench_expand_models=bool(data.get("benchmark", {}).get("expand_models", False)), ) for entry in data.get("workflow", []): @@ -371,6 +378,11 @@ def save(cfg: Config, path: Path = CONFIG_PATH) -> None: "model": cfg.tts_model, "voices": cfg.tts_voices, }, + "benchmark": { + "wav": cfg.bench_wav, + "ref": cfg.bench_ref, + "expand_models": cfg.bench_expand_models, + }, "wakeword_engine": [ {"name": e.name, "uri": e.uri, "model": e.model} for e in cfg.wakeword_engines ], diff --git a/linux/blitztext/gtksettings.py b/linux/blitztext/gtksettings.py index ff6b037..ee34652 100644 --- a/linux/blitztext/gtksettings.py +++ b/linux/blitztext/gtksettings.py @@ -1146,9 +1146,8 @@ notebook.bt-nb tab:checked label { qs.set_tooltip_text("Fill the form from a common service template") qs.connect("clicked", self._show_stt_templates) bar.pack_start(qs, False, False, 0) - # Right: actions + # Right: actions (Test moved to its own row below the config) for label, cb, tip in ( - ("Test", self._stt_test, "Record 4 s and transcribe to test this engine"), ("Delete", self._stt_delete, "Remove this engine preset"), ("⟳", lambda _b: self._refresh_status(), "Re-check connection status"), ): @@ -1185,8 +1184,19 @@ notebook.bt-nb tab:checked label { tooltip="Precision of the model. int8 is fastest and uses least memory. " "float16 is most accurate. Auto lets Blitztext decide.") - self.stt_result = Gtk.Label(xalign=0.0); self.stt_result.set_line_wrap(True) - box.pack_start(self.stt_result, False, False, 2) + test_row = Gtk.Box(spacing=10) + test_row.set_margin_top(6) + stt_test_btn = Gtk.Button(label="Test") + stt_test_btn.set_tooltip_text("Record 4 s and transcribe to test this engine") + stt_test_btn.connect("clicked", self._stt_test) + stt_test_btn.set_valign(Gtk.Align.START) + test_row.pack_start(stt_test_btn, False, False, 0) + self.stt_result = Gtk.Label(xalign=0.0) + self.stt_result.set_line_wrap(True) + self.stt_result.set_selectable(True) + self.stt_result.set_valign(Gtk.Align.START) + test_row.pack_start(self.stt_result, True, True, 0) + box.pack_start(test_row, False, False, 2) self._stt_load(self.stt_combo.get_active()) return box @@ -1842,17 +1852,31 @@ notebook.bt-nb tab:checked label { ft = Gtk.FileFilter(); ft.set_name("Text (.txt)"); ft.add_pattern("*.txt"); reff.add_filter(ft) self.bench_ref = _labeled(page, "Reference (.txt)", reff) + # Restore saved paths + if self.cfg.bench_wav and Path(self.cfg.bench_wav).exists(): + wavf.set_filename(self.cfg.bench_wav) + if self.cfg.bench_ref and Path(self.cfg.bench_ref).exists(): + reff.set_filename(self.cfg.bench_ref) + def _on_wav_set(_b): fn = wavf.get_filename() if not fn: return + self.cfg.bench_wav = fn p = Path(fn) for ext in (".reference.txt", ".txt"): cand = p.with_name(p.stem + ext) if cand.exists(): reff.set_filename(str(cand)) + self.cfg.bench_ref = str(cand) break wavf.connect("file-set", _on_wav_set) + def _on_ref_set(_b): + fn = reff.get_filename() + if fn: + self.cfg.bench_ref = fn + reff.connect("file-set", _on_ref_set) + run_row = Gtk.Box(spacing=16) run_row.set_margin_bottom(2) run = Gtk.Button(label="Run benchmark"); run.connect("clicked", self._run_bench) @@ -1862,6 +1886,9 @@ notebook.bt-nb tab:checked label { self.bench_expand.set_tooltip_text( "Fetch every available model from each remote engine and run a separate " "benchmark row per model — useful to compare models on the same server.") + self.bench_expand.set_active(self.cfg.bench_expand_models) + self.bench_expand.connect("toggled", + lambda cb: setattr(self.cfg, "bench_expand_models", cb.get_active())) run_row.pack_start(self.bench_expand, False, False, 0) page.pack_start(run_row, False, False, 6) @@ -1945,6 +1972,11 @@ notebook.bt-nb tab:checked label { def _run_bench(self, _b) -> None: wav = self.bench_wav.get_filename() refp = self.bench_ref.get_filename() + # Persist immediately so paths survive without clicking Save + if wav: + self.cfg.bench_wav = wav + if refp: + self.cfg.bench_ref = refp if not wav or not refp: self._error("Pick a .wav file and a matching reference .txt file.") return