Rebased onto latest main (conflict-resolution squash for PR #568).
Co-authored-by: Alexander <ab@digital-benefits.de>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(settings): even out dropdowns and tidy the About page
Pin combos to a shared right-hand column so long labels cannot stretch a
row. Fold the dictation-tone preview into a two-stage Start/Stop button,
quiet the model status card, shorten Performance copy, and rebuild About
with combined links, channel+Check, family platform tiles, and Talk to us
moved up next to the license.
* fix(settings): keep tone preview on start if the cue fails
Do not flip Start/Stop when preview_tone_cue returns false, so a missing
file or silent player cannot skip the start cue. Also annotate
PreferenceRow.set_title / set_subtitle.
* fix(settings): paint About icons for dark dialogs
Dark ink on Adwaita/Yaru-dark buttons made the family tiles and Talk to us
marks disappear. Pick a light foreground on dark themes (and keep the
original ink on light ones) so the marks match the labels.
* fix(settings): show Android and iOS marks on VocaPhone
VocaPhone covers both, so the family tile now uses the Android robot and
the Apple mark instead of a single phone icon.
* fix(settings): cut repeated About copy
Name the AGPL once on the License row. The VocaHQ blurb is just the
private-dictation line. VocaPhone says TestFlight, not source-only, and
Gateway is self-hosted and headless.
* fix(settings): space About icons from their labels
Talk-to-us buttons and the family tiles were packing the mark against the
text. Give the image an end margin and a wider gap in the family tile.
* feat(shortcuts): default to hold Right Alt push-to-talk on first install
Double-tap Ctrl conflicts with many desktop and app shortcuts, so new
installs now use hold Right Alt (Option) in push-to-talk mode. Existing
config.json values are preserved unchanged.
Updates README, user guide, AGENTS.md, first-run dialog, website copy,
and tests to match the new defaults.
* refactor(shortcuts): trim redundant default-change test and comments
Drop the fresh-install assertion that duplicated DEFAULT_CONFIG coverage,
remove the duplicated Right-Alt rationale from config_manager, and drop a
vacuous source-string check from the settings shortcuts test.
* fix(shortcuts): align install.sh seeds and preserve legacy toggle mode
install.sh still wrote ctrl+ctrl without mode, so merges with the new
push_to_talk default became hold-Ctrl. Seed right_alt + push_to_talk for
new installs, and migrate existing shortcut-only configs to mode=toggle
so prior behavior is unchanged.
* style(config): fix black formatting after shortcuts migration
* fix(shortcuts): preserve legacy defaults when shortcuts section is missing
Existing config files without a shortcuts section previously inherited
ctrl+ctrl toggle from DEFAULT_CONFIG. Pin those historical defaults on
upgrade so the new first-install Right Alt push-to-talk defaults do not
silently change behavior.
* docs(agents): unwrap hard-broken dictation control paragraph
Keep the AGENTS.md paragraph as a single line like the surrounding prose.
* fix(settings): restore custom shortcut Record/Set controls
Gtk.Widget.show_all() is a no-op while no_show_all is True, so the
contextual Custom Shortcut row never appeared after selecting it in the
searchable settings UI. Clear the flag before showing and restore it on
hide.
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
* fix(settings): sync custom row when reverting shortcut separators
Selecting Custom Shortcut then a combo group separator restored the
preset id but left Record/Set visible. Reuse _sync_shortcut_selection_ui
so combo and custom-row visibility stay aligned.
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
* fix(settings): clear stale Record/Set hint on separator revert
Selecting Custom Shortcut sets a temporary info hint; clicking a combo
separator already restored the row visibility but left that hint in
place. Refresh the mode description after syncing.
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
* feat(settings): replace tab navigation with searchable sidebar and topic pages
- Sidebar (icon + label) with Gtk.Stack replaces the 7-tab notebook
- Live settings search (Ctrl+F): filters rows in place, per-page match
counts, dims pages without hits, empty-results state
- Topic-based pages per GNOME HIG: Dictation, Speech Model, Audio,
Performance, Application, Advanced
- Persistent bottom status strip: recognition state, shared mic level
bar, and Test Dictation with inline transcription output
- GPU device selection moves out of the power-user gate onto the
Performance page; Reset to Defaults moves into the Advanced page
- Consolidated model info card (info + warning + legend in one surface)
- Contextual custom-shortcut row, in-list auto-pause empty state,
uniform control widths, aligned group descriptions
- Esc clears search, Ctrl+W closes
* test(settings): update source-inspection tests for sidebar redesign, add search coverage
* fix(settings): index unlocked advanced controls in search
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
* fix(settings): restore selected page after clearing search
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
---------
Co-authored-by: Jatin K Malik <jatinkrmalik@users.noreply.github.com>
Selecting a preset after recording a custom combo left the custom entry
filled, so both looked active. Sync the dropdown to "Custom Shortcut"
when a non-preset is set, and clear the custom field when a preset wins.
Allow users to bind voice commands to specific modifier key sides (e.g.,
Right Ctrl only), preventing conflicts with common left-side shortcuts
like Alt+Tab or Ctrl+C.
- Expand SUPPORTED_SHORTCUTS from 3 to 9 entries (either/left/right)
- Add SHORTCUT_GROUPS for grouped display in settings UI
- Implement side-aware key matching in pynput and evdev backends
- Update settings dialog with grouped shortcut selector
- Add comprehensive tests for side-specific shortcuts
Closes#306
* feat(shortcuts): add push-to-talk mode as alternative to toggle
This commit implements push-to-talk functionality (Issue #252) as an
alternative to the existing double-tap toggle mode:
- Add keyboard shortcut mode setting (toggle vs push_to_talk)
- In push-to-talk mode: hold key to speak, release to stop
- Update both pynput and evdev backends to support press/release callbacks
- Add UI controls in settings dialog for mode selection
- Maintain backward compatibility with existing toggle mode as default
Closes#252
Related: Closes#185 (superseded by push-to-talk approach)
* fix(shortcuts): remove duplicate code blocks that broke settings dialog
The previous push-to-talk implementation had duplicate code blocks
in tray_indicator.py and settings_dialog.py that caused the settings
dialog to fail silently. This commit cleans up the duplicate code
while preserving the push-to-talk functionality.
Fixes issue where settings dialog would not open.
* fix(ui): add set_subtitle method to PreferenceRow class
The PreferenceRow class was missing a set_subtitle method, which is called by
_update_shortcut_ui_for_mode when switching between toggle and push-to-talk
modes. This caused an AttributeError that silently crashed the settings dialog
during initialization.
Also stores subtitle_label as instance variable for later updates.
* fix(keyboard): remove duplicate code and fix mode handling
Critical fixes for keyboard backends:
- evdev_backend.py: Removed duplicate __init__ and _handle_key_event methods
- pynput_backend.py: Removed duplicate __init__, _on_press, _on_release methods
- Both backends now properly handle toggle and push-to-talk modes
Fixed first_run_dialog.py:
- Added do_response() override to properly close dialog
- Added explicit destroy() call in show_first_run_dialog()
Fixed tray_indicator.py:
- Added _unregister_keyboard_callbacks() method
- Updated _setup_keyboard_shortcuts() to properly clean up old callbacks
- Updated update_shortcut() to properly handle mode switching
Fixed settings_dialog.py:
- Added set_subtitle() method to PreferenceRow class
- Updated UI text for push-to-talk mode
- Fixed _on_shortcut_changed to properly pass mode to callback
All 547 tests pass locally.
* feat(shortcuts): improve mode switching and add mode-aware display names
Key improvements:
- Add SHORTCUT_MODE_DISPLAY_NAMES for mode-specific shortcut names
- Add get_shortcut_display_name() function for mode-aware display
- Fix restart_with_shortcut() to properly save and restore all callbacks
- Fix update_shortcut() to properly handle mode changes
- Fix _setup_keyboard_shortcuts() to clear old callbacks before registering new ones
- Ensure only one type of callback is active at a time
This fixes issues where:
- Old callbacks remained registered after mode switch
- Single-tap triggered action after switching from push-to-talk to toggle
- Display names weren't appropriate for the selected mode
* fix(shortcuts): stabilize live mode switching callback lifecycle
* test(shortcuts): cover callback reset on mode transitions
* fix(shortcuts): remove super preset and migrate legacy config
* test(shortcuts): update coverage for super preset removal
* feat(shortcuts): add customizable keyboard shortcuts via GUI
Implement configurable keyboard shortcuts for toggling voice recognition,
allowing users to choose between Ctrl+Ctrl, Alt+Alt, Shift+Shift, or
Super+Super (Windows key) double-tap shortcuts.
Changes:
- Add shortcuts configuration section to settings dialog with dropdown
- Update keyboard backends (pynput and evdev) to support configurable
modifier keys for double-tap detection
- Add SUPPORTED_SHORTCUTS, SHORTCUT_DISPLAY_NAMES constants in base module
- Update KeyboardShortcutManager to accept and pass shortcut configuration
- TrayIndicator now reads shortcut from config on startup
- Add comprehensive tests for shortcut configuration and parsing
The default shortcut remains Ctrl+Ctrl for backward compatibility.
Changes take effect after app restart (noted in UI).
Closes#16
* feat(shortcuts): enable live shortcut switching without app restart
Add restart_with_shortcut() method to KeyboardShortcutManager that stops
the listener, updates the shortcut configuration, and restarts with the
new shortcut. The settings dialog now applies shortcut changes immediately
with visual feedback indicating success.
- Add restart_with_shortcut() to KeyboardShortcutManager
- Add update_shortcut() to TrayIndicator for exposing live updates
- Update SettingsDialog to use callback for immediate shortcut changes
- Show green success message when live switching succeeds
- Add 7 new tests for restart_with_shortcut functionality
* fix: use Python 3.8 compatible context manager syntax
Convert parenthesized context manager (Python 3.10+) to nested with
statements for Python 3.8 compatibility in test_keyboard_shortcuts.py