ac783e53bd6767de3d31e8d8a2fa764cca04e873
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
dca937fc21 |
feat(dictionary): combined custom dictionary with corrections (#890)
* feat(dictionary): combined custom dictionary with corrections Rebased onto latest main (conflict-resolution squash for PR #818). * fix(dictionary): stabilize the terms and corrections file contracts - corrections_path() stays at the fixed config_dir() location promised by the file contract instead of following a custom or --dictionary-file terms path, which could strand or duplicate corrections; a leftover file beside the terms file is still read once and copied over. - _atomic_write() preserves the replaced file's permission mode instead of resetting it to the process umask. - build_initial_prompt() streams terms and stops at the configured cap instead of reading and processing the whole file for every segment. * fix(settings): restore the selected dictionary pane after a search Searching can auto-switch the Custom Dictionary management pane to reveal a match, but clearing the query only restored widget visibility. Record the pane selection in the search baseline snapshot and restore it alongside the previous page so users return to the pane they chose. * fix(dictionary): close the remaining custom dictionary file-contract gaps - A --dictionary-file session override no longer imports a corrections file found beside the override into the standard config directory; the override scopes to terms only. - Terms reads validate the whole file before yielding: corrupt UTF-8 mid-file now yields no terms at all, matching the invalid status Settings reports instead of leaking a partial dictionary to recognition. - The terms read is bounded by MAX_TERMS_FILE_BYTES (1 MiB); a larger file is invalid everywhere with a dedicated status instead of being read on every segment. * fix(dictionary): keep usable terms prefix on oversized files and legacy corrections under overrides * fix(dictionary): cap yielded terms and keep added terms visible on oversized files * fix(dictionary): keep added terms visible without dropping scanner-owned lines * fix(dictionary): keep oversized terms manageable and honor mid-search pane switches - terms list gains a Show all / Show first 500 toggle; adding a term beyond the cap auto-expands so the new row renders with a Remove button instead of being invisible until the file is hand-edited - a manual dictionary pane switch during an active search drops the saved pre-search pane so clearing the query no longer rolls back the user's newer selection - annotate the new settings tests and type model_info so the parakeet branch stops adding fresh mypy errors * fix(settings): do not auto-expand terms on add past display cap Rebuilding up to thousands of term rows on the UI thread stalls the dialog; the overflow button already shows the true count, so the added term stays one click away. * test(dictionary): stub segment_callbacks on bare manager for history dispatch * fix(dictionary): stream oversized terms files when building the prompt build_initial_prompt decoded the whole 1 MiB read window before every transcription segment even though it only needs the first max_words terms. _iter_terms_for_prompt now reads oversized files line by line and stops at the prompt limit, keeping the same contract: complete leading lines only, invalid UTF-8 inside the usable window yields nothing. * fix(history): only the ended session's worker may extend its snippet A late segment could extend the newest history entry regardless of which session produced it, so a worker finishing after the next dictation session leaked its text into that session's entry. Track the worker that owned the ended session and require late segments to arrive on the same thread before merging; anything else forms its own snippet. * fix(speech): transcribe queued segments the reload worker never drained The recognition worker can exit during stop_recognition's bounded audio-thread join, so the released key's final buffer lands in the queue after the worker is gone and was silently dropped when the reload teardown replaced the queue. Drain whatever remains before swapping it — except on capture failure or an explicit cancel, which still discard the session's leftovers. * fix(settings): build term rows in idle slices and pin hidden additions Two problems shared one cause. Rebuilding up to 4096 rows synchronously stalled the dialog, and a term appended past the 500-row cap reported success while staying invisible. Rows now build in bounded GLib idle slices guarded by a refresh token, and a saved term outside the visible window is pinned to the top of the capped list until it disappears or becomes visible on its own. Adding under "show all" scrolls to the new term instead. * test(engine): annotate new test method return types * fix(ci): match a candidate row that leads the latest track block snapcraft status normally prints candidate as a continuation line under the latest track (channel in column 1). If candidate ever leads the track block it rides the track line instead (channel in column 3), and the resolver missed it and refused to promote. Match both layouts in the resolve and the pre-release re-verify. * fix(dictionary): bound prompt reads and scroll to landed terms - _iter_terms_for_prompt now reads each line via readline() capped by the remaining byte window, so an overlong line can never pull more than MAX_TERMS_FILE_BYTES into memory - the prompt path keeps decoding every complete line inside the usable window after the term limit is collected, so invalid UTF-8 anywhere in the window still yields no terms and matches the reader's invalid status - under "show all" Settings scrolls to the index where an added term landed (prepended or appended), verified against the row's label, instead of always scrolling to the end * fix(history,reload): keep late-segment ownership and drain stamped tuples - Track session_workers_seen so a worker from an earlier session can never merge into a newer snippet while a session is open - Unpack (segment, language) queue tuples in _transcribe_queued_segments; passing the tuple as audio raised TypeError and lost final speech - Refuse 'delete that' when a manual pad edit blurred segment boundaries - Translate term-row coordinates into the scrolled card's space so the scroll actually reaches rows below the fold - Type _on_dictionary_pane_changed's varargs * style: drop unneeded nonlocal for mutated worker set --------- Co-authored-by: Graham <graham@thegeeky.ninja> |
||
|
|
dbe0215aad |
feat(shortcuts): add language switching shortcuts, fix PTT races, and add dictation history (#880)
* feat(shortcuts): per-language dictation shortcuts (#805) Bind extra keys that start dictation in a specific catalog language for one utterance, then restore the configured language. Bindings live in shortcuts.language_shortcuts as [{shortcut, language}], normalized against the language catalog and the backend's shortcut parser. The tray spawns one KeyboardShortcutManager per binding in the active mode; toggle presses route through start_recognition_with_language, and push-to-talk arms press/release the same way. The manager keeps the override one-shot: it applies before the utterance starts (outranking follow-keyboard-layout) and unwinds on the next IDLE/ERROR transition or when a mid-dictation reconfigure picks a new language. Engines that cannot re-language without a reload (VOSK, Parakeet, English-only whisper.cpp) refuse with the error cue and a notification instead of dictating in the wrong language. Settings gains a Language Shortcuts group under Shortcuts: each row is a language picker, an editable key entry, a Record button that retargets the shared key-capture, and a remove button; edits persist immediately and rebuild the live listeners. A non-dict shortcuts section now rebuilds instead of crashing config load. * fix(shortcuts): class-level default for language shortcut listeners TrayIndicator stubs built with __new__ (e.g. test_suspend_handler) skip __init__ but still run _quit(); give _language_shortcut_managers a class-level default so teardown iterates an empty list. * Fix Greptile review findings on per-language shortcuts (#805) - Bind each dictation session's language for transcription workers so the one-shot restore cannot rewrite a queued segment's language - Judge a shortcut's capability against the model the layout refresh will serve (whisper.cpp sibling swap) and refuse requests the engine drops - Add _oneshot_language_restore to the reconfigure snapshot and settle a revived pending restore on failure - Dedupe language bindings by gesture (modifier reordering, enter/return alias, pure-modifier side sets) instead of by string - Stop per-language listeners under external activation and never re-arm - Gate push-to-talk release on the binding that started the session, and stop a held hold before rebuilding listeners - Keep a row's last valid binding during partial edits, report rejected duplicates, cancel a capture aimed at a removed row, disarm the previous Record target, persist a recorded key once, and add the missing type hints * fix(shortcuts): resolve remaining Greptile race findings on PR #880 - Stamp each queued audio segment with the session language it was recorded under, so a worker that outlives stop_recognition's joins transcribes each utterance in its own language rather than whatever a newer session stored in _session_language. - Serialize push-to-talk press/release behind _ptt_lock: a release landing while start_recognition runs now waits for the owner claim instead of being dropped, so a quick key tap cannot leave the microphone running. - Add the missing return type hint on _stop_language_shortcut_managers. * test(shortcuts): stub _segment_started_at on bare manager stubs * fix(shortcuts): carry segment language as attribute, default language on bare managers * fix(shortcuts): keep (segment, language) queue tuples, stamp segment.language too * fix(settings): apply history toggle and limit to the live store --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> |
||
|
|
a89966a744 |
feat(recognition): record while an idle model reloads (#851)
* feat(recognition): buffer audio during model reload * fix(recognition): recover buffered reload failures * test(recognition): cover buffered reload edge cases * fix(recognition): narrow buffered reload errors * fix(settings): wait for IDLE before restoring Test Dictation callbacks Buffered reload's stop_recognition() returns while PROCESSING. Test Dictation must keep its text callbacks until recognition reaches IDLE so the test utterance cannot inject into the focused app. * style: apply Black formatting to PR 851 files * fix(settings): cancel buffered reload before Test Dictation timeout restore On the ~3min idle-wait timeout, do not restore live injector callbacks while a buffered reload worker may still deliver text. Cancel via _cancel_reload_recording off the GTK thread, then restore; if cancel is unavailable or fails, leave test callbacks installed and show an error. Add return/param annotations and regression coverage. * fix(settings): keep Test Dictation waiting if buffered cancel fails After the idle-wait timeout, if cancel is unavailable or raises, keep polling with test callbacks and _test_active instead of marking the test done. Ending early left the test callback as the only text destination, so later normal dictation could land in the hidden test buffer. Restore live injectors only once recognition reaches IDLE. * fix(settings): reset Test Dictation timeout-cancel flag per session Clear _test_timeout_cancel_attempted when a test starts, when entering the buffered idle-wait, and when restoring UI so a later Test Dictation can still cancel after timeout. Narrow the cancel-worker except clause to AttributeError/OSError/RuntimeError/TypeError so unrelated defects are not treated as ordinary cancel failure. * fix(recognition): signal capture completion when buffered capture dies A dead microphone during a buffered model reload left should_record=false with no stop_recognition handoff, so the reload worker blocked forever on _capture_finished.wait(). Signal the event from the audio thread's failure teardown paths instead. Refs VocaHQ/vocalinux#851 --------- Co-authored-by: Jatin K Malik <jatinkrmalik@gmail.com> |