bcd5313a1e648abe21817c659cb1c8d9853fc042
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
be0614010d |
refactor: systematic code quality improvements across 20 dimensions (#377)
* refactor: improve code quality across multiple dimensions Code quality improvements identified and driven by desloppify analysis: **action_handler.py** - Replace 9 repetitive shortcut forwarding methods with data-driven _SHORTCUT_ACTIONS class-level dispatch table - Add _make_shortcut_handler() factory for shortcut closures - Add explicit type annotation for action_handlers dict **config_manager.py** - Move 'import copy' from __init__ to module level (stdlib best practice) - Add _VALID_SECTIONS frozenset for section name validation in set() - Narrow broad Exception catch to (KeyError, TypeError) in set() - Add deprecation warning to save_settings() guiding toward save_config() **tray_indicator.py** - Consolidate all imports at module top level - Remove noqa E402 suppressions for mid-file imports - Move ResourceManager import alongside other local imports **recognition_manager.py** - Narrow broad 'except Exception' to 'except OSError' for audio device enumeration errors - Narrow 'except Exception' to 'except (FileNotFoundError, OSError)' for notification subprocess errors **Tests** - Update test assertions to use specific exception types matching narrowed catches (OSError, FileNotFoundError instead of Exception/RuntimeError) * refactor: improve type safety, callback contracts, and method decomposition **main.py** — Mid-level elegance - Document callback wiring contract: text_callback, action_callback, state_callback with threading context, argument types, and behavior - Add return type annotations to nested callback functions - Reorder: define callbacks before registration for readability **config_manager.py** — Type safety, error consistency - Add typed accessors: get_str(), get_bool(), get_int(), get_float(), get_optional_int() eliminating Any returns for common access patterns - Narrow load_config exception to (json.JSONDecodeError, OSError) - Narrow save_config exception to (OSError, TypeError) **recognition_manager.py** — Low-level elegance - Decompose _init_whispercpp (141 lines) into three focused methods: - _init_whispercpp: validation and download gating (35 lines) - _load_whispercpp_model: backend detection and model loading (55 lines) - _handle_gpu_fallback: GPU incompatibility recovery (40 lines) - Narrow exception catch from broad Exception to specific types **tray_indicator.py, settings_dialog.py** — Type safety - Migrate config access from untyped get() to typed get_str(), get_bool(), get_optional_int() across 8 call sites * refactor: narrow 11 more broad Exception catches to specific types Systematically replaced broad 'except Exception' with specific exception types across recognition_manager.py: - test_audio_input: (OSError, ValueError) for audio read errors - test_audio_input outer: (OSError, ValueError, RuntimeError) for unexpected - _init_whisper: (RuntimeError, OSError) for engine init failures - _transcribe_with_whisper: (RuntimeError, OSError, ValueError) for transcription - _download_whispercpp_model: (OSError, RuntimeError, ValueError) for downloads - _download_vosk_model: (OSError, RuntimeError, ValueError) for downloads - _download_whisper_model: (OSError, RuntimeError, ValueError) for downloads This brings the total narrowed exception catches to 18+ across the codebase, improving error consistency and making failure modes more predictable. * style: fix black formatting issues Auto-format with black to fix CI lint failures in recognition_manager.py, settings_dialog.py, and test_settings_shortcuts.py. * test: add coverage for typed config accessors Add TestTypedAccessors class with 15 tests covering: - get_str(): string values, defaults, non-string conversion, None handling - get_bool(): boolean values, defaults - get_int(): integer values, invalid input fallback, missing key defaults - get_float(): float values, invalid input fallback - get_optional_int(): int values, None values, missing keys, invalid input Fixes codecov/patch coverage requirement for new typed accessor methods. |
||
|
|
80108c3456 |
feat(shortcuts): add left/right modifier key distinction for keyboard shortcuts (#308)
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 |
||
|
|
86ca22ff5c |
feat(shortcuts): add push-to-talk mode and improve shortcut reliability (#278)
* 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 |
||
|
|
7a1c42aae6 |
feat(shortcuts): add customizable keyboard shortcuts via GUI (#137)
* 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 |