feat: add D-Bus integration with KDE shortcut support#1
feat: add D-Bus integration with KDE shortcut support#1Zebastjan wants to merge 83 commits intoPabloVitasso:mainfrom
Conversation
- Implement D-Bus service using dbus-next for cleaner Python integration - Add shell script toggle-syllablaze.sh for KDE global shortcuts - Update install.py to copy shortcut script to user's .local/bin - Add dbus-next and qasync dependencies to requirements.txt This change allows users to control Syllablaze using global keyboard shortcuts in KDE. When setting up shortcuts, users must add the script as an 'Application' rather than a 'Command' for proper functionality.
|
Thank you so much for creating this wonderful application. It's extraordinarily helpful. It's exactly the sort of thing that we've been wanting for a long, long time. So, I hope the changes in adding the facility for a keyboard shortcut will be helpful and appreciated. And if there's anything else we can do. There's two possibilities that we note. One is that for whatever reason, when we click on the functionality to download additional models to select, it doesn't seem to be working. That's something we could have a look at. The second thing we note is that main.py is on the order of 750 lines, which is a bit unwieldy. So, we might think about refactoring that. However, we don't want to scribble all over your code. So, you know, let us know if you have any ideas or something that we could take a look at that you might appreciate. |
- Replace Qt ApplicationShortcut with pynput for true system-wide hotkeys - Add transcribing state to prevent recording during transcription - Simplify to single toggle shortcut (Alt+Space default) - Add thread-safe signal emission using QMetaObject.invokeMethod - Add 300ms debounce to prevent accidental double-triggers - Add 50ms listener initialization delay - Improve window visibility with raise_() and activateWindow() - Add pynput to requirements.txt This brings the working keyboard shortcuts from telly-spelly to Syllablaze, combining the best of both projects.
- Highlight working global shortcuts as main new feature - Credit original authors (Guilherme da Silveira, PabloVitasso) - Update version to 0.5 - List new features and improvements
…tcut reliability - Implement automatic CUDA library detection and configuration - Detect NVIDIA CUDA libraries in pipx venv (cublas, cudnn) - Auto-restart process with LD_LIBRARY_PATH if GPU detected - Graceful fallback to CPU mode if GPU unavailable - User-friendly messages for GPU/CPU mode status - Fix keyboard shortcut reliability issues - Add health check timer (every 5 seconds) to monitor listener - Auto-restart dead keyboard listener when detected - Enhanced error handling with automatic recovery - Prevent shortcut failure after window switching (browser, etc.) - No longer need separate syllablaze-gpu command - Main syllablaze command now auto-detects and uses GPU - syllablaze-gpu kept for backward compatibility with deprecation notice Fixes transcription errors with libcublas.so.12 not found Fixes keyboard shortcuts dying after switching to other applications
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The QTimer.singleShot(500, sys.exit(0)) was racing with the qasync shutdown flow, causing RuntimeError and "Task was destroyed" warnings. Removing it lets QApplication.quit() trigger the clean async shutdown path naturally. Also includes ruff auto-fixes: unused imports removed, f-strings without placeholders simplified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
32767**2 overflows int16, causing the volume calculation to return near-zero. Use float64 arrays in the test to avoid overflow, matching how audio data flows through the real code path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update distil-whisper repo IDs to use Systran's CTranslate2 versions - Change from distil-whisper/* (safetensors) to Systran/faster-distil-whisper-* (CTranslate2) - Remove distil-large-v3 and distil-large-v3.5 (no CTranslate2 versions available) - Keep only distil-small.en, distil-medium.en, and distil-large-v2 - Fix distil model path detection - Strip 'distil-' prefix when looking for Systran repos - distil-medium.en -> models--Systran--faster-distil-whisper-medium.en - Fix distil model loading - Use local_files_only=False to let WhisperModel find cached versions - Prevents 'Cannot find cached snapshot' errors - Filter available models to only show supported distil models - Query HuggingFace API but filter results against FASTER_WHISPER_MODELS - Only show distil models with working CTranslate2 versions - Improve model resource management - Explicitly release CTranslate2 model resources before loading new models - Add garbage collection after model deletion - Prevents semaphore/resource leaks when switching models - Refactor settings window model/language updates - Move update_tray_tooltip to global function in main.py - Use global tray_recorder_instance instead of scanning widgets - More reliable transcriber and tooltip updates Known limitation: Distil model downloads work but don't show progress bar in UI (progress is visible in logs)
- Set qasync logger to CRITICAL level to suppress noisy shutdown warnings - Add custom exception handler to suppress RuntimeError during shutdown - Catch 'is not the running loop' and 'Event loop stopped' errors gracefully - Add try/except around app_exit_future await to handle loop shutdown race - Add try/except around run_until_complete for clean exit These errors are expected during normal shutdown when Qt event loop stops before async tasks complete. Now they're logged at debug level instead of showing scary ERROR messages to users. Fixes: RuntimeError: loop is not the running loop Fixes: RuntimeError: Event loop stopped before Future completed Fixes: Task was destroyed but it is pending
This message occurs during normal shutdown and is harmless - it just means async tasks were still queued when the event loop stopped. Now logged at debug level instead of error level.
Major overhaul replacing pynput-based shortcuts with native KDE kglobalaccel integration: - Uses Qt's QKeySequence for accurate key mapping (Alt+Space -> 0x8000020) - Registers shortcuts via D-Bus for full Wayland compatibility - Preserves user customizations from KDE System Settings - Adds comprehensive error handling and verification - Updates settings UI with tabbed interface and KDE integration
Replace PyQt6 SettingsWindow with Kirigami/QML-based settings interface. Currently a placeholder — real settings pages (models, audio, transcription, shortcuts, about) still need to be built. - Add kirigami_integration.py (KirigamiSettingsWindow wrapper) - Add kirigami_bridge.py (Python-QML bridge classes) - Add QML files (TestSettings, KirigamiSettingsWindow, test windows) - Add qml_dev.sh and qml_preview.py for QML development - Switch main.py import to KirigamiSettingsWindow - Disable ruff --fix in dev-update.sh during active development - Update CLAUDE.md for kglobalaccel shortcuts and ruff status Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- main branch -> deploys to 'syllablaze' - kirigami-rewrite branch -> deploys to 'syllablaze-dev' - Add 'qml' to SUB_DIRS for Kirigami files - Handle QML directory recursively (includes test/ subdirectories) - Show branch-specific messages during deploy This allows running both stable and dev versions side-by-side. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- main branch -> deploys to 'syllablaze' - kirigami-rewrite branch -> deploys to 'syllablaze-dev' - Show branch-specific messages during deploy This allows running both stable and dev versions side-by-side. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents: - Branch strategy (main vs kirigami-rewrite) - Side-by-side testing setup (syllablaze vs syllablaze-dev) - Daily workflow for both branches - Kirigami development tools - Linting and testing commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents: - Branch strategy (main vs kirigami-rewrite) - Side-by-side testing setup (syllablaze vs syllablaze-dev) - Daily workflow for both branches - Kirigami development tools - Linting and testing commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create a proper Kirigami-based settings interface that matches KDE System Settings design patterns: - SyllablazeSettings.qml: Main window with sidebar navigation + content area - Left sidebar with category icons (Models, Audio, Transcription, Shortcuts, About) - Right content area with page loader - Individual pages for each settings category: * ModelsPage: Whisper model management * AudioPage: Input device and sample rate selection * TranscriptionPage: Language, compute settings, VAD, timestamps * ShortcutsPage: Displays current shortcut, links to System Settings * AboutPage: App info, features list, GitHub links Uses Kirigami components: - ApplicationWindow for native KDE window - Card/FormLayout for settings forms - InlineMessage for contextual help - PlaceholderMessage for empty states - Proper theme integration (Theme.backgroundColor, highlightColor, etc.) Next: Wire up Python-QML bridge for live data binding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement comprehensive SettingsBridge and ActionsBridge classes: **SettingsBridge**: Exposes all settings to QML - Generic get/set methods with QVariant support - Specific getters/setters for each setting category: * Audio: mic index, sample rate mode * Transcription: language, compute type, device, beam size, VAD, timestamps * Shortcuts: current shortcut display - Data providers: getAvailableLanguages(), getAudioDevices() - Emits settingChanged signals for live updates **ActionsBridge**: Handles QML-triggered actions - openUrl(): Open links in default browser - openSystemSettings(): Launch systemsettings kcm_keys **QML Pages Updated**: - AudioPage: Reads/writes sample rate mode, device selection - TranscriptionPage: All settings wired up with proper type conversion - ShortcutsPage: Displays current shortcut, launches System Settings - AboutPage: Uses APP_NAME/VERSION constants, working GitHub buttons All controls now read from QSettings on load and write changes immediately. Tested and working - settings persist across app restarts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents what's complete and working: - UI framework with native KDE integration - Python-QML bridge (SettingsBridge + ActionsBridge) - 4/5 settings pages fully functional - Testing instructions TODO items tracked for Models page and audio device integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**About Page Layout Fix**: - Add Layout.maximumWidth to Features card to prevent overflow - Add Layout.fillWidth to delegate items - Add wrapMode: Text.WordWrap to feature labels - Add top margin to Links section for spacing **Test Script Isolation**: - Set separate organization name (KDE-Testing) for test mode - Set separate app name (Syllablaze-Kirigami-Test) - QSettings now isolated - won't affect running syllablaze instance - Add log messages to indicate test mode This fixes the feature list extending beyond the card boundary and prevents the test script from modifying the live app's settings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**About Page**: - Add Layout.margins and Layout.bottomMargin to Features card contentItem - This prevents the last feature from extending below the card boundary **Shortcuts Page**: - Add fallback logic if getShortcut() returns empty - Add console.log for debugging shortcut loading - Default to 'Alt+Space' if no shortcut is saved **ActionsBridge**: - Change openSystemSettings() to use kcmshell6 with search parameter - Now jumps directly to 'Syllablaze' shortcut in System Settings - Command: kcmshell6 kcm_keys --args Syllablaze **SettingsBridge**: - Add debug logging to getShortcut() - Add null check fallback to DEFAULT_SHORTCUT Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
**About Page - Proper Card Padding**: - Wrap contentItem in Item with explicit implicitHeight - Use anchors.fill with margins instead of Layout.margins - This ensures the card properly contains all features - No more overflow under GitHub buttons **Shortcuts Page - Debug System Settings Button**: - Add console.log when button is clicked - Add try/catch error handling - Will show if actionsBridge is accessible **ActionsBridge - Better Logging and Fallback**: - Add verbose logging when openSystemSettings() is called - Try kcmshell6 first (KDE 6), fallback to systemsettings - Log success/failure of process launch This should fix the layout issue and help diagnose why the System Settings button might not be responding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace PyQt6 widget-based settings window with modern Kirigami QML UI that matches KDE Plasma desktop styling. Includes fixes for window visibility and proper display scaling detection. Key changes: - Add KirigamiSettingsWindow with QML-Python bridge - Fix window visibility by setting visible:false in QML - Calculate window size using physical resolution (devicePixelRatio) - Add enhanced logging for debugging window display issues - Update install.py to support system-site-packages for Qt6/Kirigami access - Improve model size calculation for .pt files All settings pages functional: Models, Audio, Transcription, Shortcuts, About. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaced by Kirigami QML UI (kirigami_integration.py). The old widget-based UI is no longer used after Kirigami integration. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add UI Architecture section documenting the Kirigami QML UI implementation and update Development Workflow section to describe git branch-based workflow. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add a modern, minimal recording indicator as a borderless circular dialog that floats on screen and provides visual feedback during recording. Features: - Circular borderless window with Syllablaze microphone icon - Animated glowing ring showing real-time volume levels during recording - Mouse interactions: left-click toggles recording, right-click shows menu, middle-click opens clipboard, drag to move, scroll to resize (100-500px) - Transcription overlay with reduced opacity during processing - Window size persistence across sessions - Full integration with existing recording and transcription system Implementation: - RecordingDialogManager: Main dialog controller with AudioBridge and DialogBridge for Python-QML communication - RecordingDialog.qml: QML UI definition with animations and interactions - Integration in ApplicationTrayIcon with signal connections for state synchronization - Tray menu item to toggle dialog visibility The dialog operates in persistent mode (always visible) and automatically updates to reflect recording state changes from keyboard shortcuts or tray menu actions. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace the old syllablaze.png with the new syllablaze.svg file for the system tray icon. The SVG format provides better scaling and the new design matches the updated branding. Changes: - Update icon path from 'syllablaze.png' to '../resources/syllablaze.svg' - Add comment clarifying the path points to resources directory - Include syllablaze.svg in resources (40KB SVG vs 657KB PNG) The microphone.svg in resources is being used for the recording dialog (work in progress by OpenCode/Kimi) and is not affected by this change. Verification: - Icon path resolves correctly to /resources/syllablaze.svg - File exists and is valid - Tests pass (10/10 audio_processor tests) - Code compiles without errors
Implements fixes for installation pain points identified in Perplexity's
refactoring report (§6.1, §6.2) plus user-requested improvements.
**User Pain Point Fixed:**
Making small changes (like icon updates) required 3 manual steps:
$ python install.py # Blocked: "already installed"
$ pipx uninstall syllablaze
$ python install.py
Now it's one command:
$ python install.py --force-reinstall # Auto-uninstall + reinstall
**Changes Implemented:**
1. Add --force-reinstall flag (lines 370-372)
- Auto-uninstalls if already installed
- Preserves user settings (device, model, shortcuts)
- Non-interactive for scripts/CI
2. Add interactive prompt (lines 384-395)
- When already installed: "Reinstall? (y/n)"
- User-friendly for manual installations
- Cancels gracefully on 'n'
3. Fix double stdout read bug (lines 131-174) [Perplexity §6.1]
- Old code: Read stdout twice (second loop never executed)
- Lines 131-136: First while loop exhausted stdout
- Lines 150-200: Second for loop found nothing to read
- New code: Combined into single loop with progress tracking
- Result: Installation progress now displays correctly
4. Fix version mismatch (lines 54, 96) [Perplexity §6.2]
- Old: Hardcoded version="0.3" in setup.py
- New: Read from blaze.constants.APP_VERSION ("0.5")
- Verified: pipx list now shows correct version
5. Fix import organization (lines 18-20) [Perplexity §6.1]
- Moved threading, time, itertools to top of file
- Old: Imported inside function at line 185-187
- Removed duplicate imports
6. Update run_installation() signature (line 410)
- Added force_reinstall parameter
- Passes to check_if_already_installed()
7. Update main entry point (lines 466-468)
- Passes args.force_reinstall to run_installation()
**Testing:**
- ✓ python install.py --force-reinstall (auto-reinstall)
- ✓ Version updated from 0.3 → 0.5 in pipx list
- ✓ Settings preserved across reinstall
- ✓ Icon files installed correctly (SVG + PNG)
- ✓ Installation progress displays properly
- ✓ No double-read bug
**Benefits:**
- Icon updates work with one command (user's immediate need)
- Better developer experience for iterating on changes
- Fixes 3 bugs identified in Perplexity's refactoring report
- Safe: interactive prompt asks for confirmation
- Automated-friendly: --force-reinstall for scripts
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements new RecordingDialogVisualizer using QSvgRenderer for precise element positioning based on SVG element IDs. New Components: - RecordingDialogVisualizer.qml: Main visualizer window with radial waveform - SvgRendererBridge: Python bridge exposing SVG element bounds to QML - Uses Qt's QSvgRenderer.boundsOnElement() for precise positioning SVG Integration: - Added id="waveform" and id="status_indicator" to syllablaze.svg - Status overlay positioned exactly over status_indicator element - Waveform visualization drawn in waveform element area Bridge Consolidation: - Renamed audioBridge → dialogBridge in RecordingDialog.qml - Unified bridge interface for both dialog implementations - RecordingDialogManager now loads RecordingDialogVisualizer.qml Features: - Real-time radial waveform (36 bars, ~60fps) - Volume-based color transitions (green → yellow → red) - SVG element-based hit testing and positioning - Canvas-based rendering for smooth animation Documentation: - Phase 1: Recording dialog cleanup - Phase 2: Bridge consolidation - Phase 3: Visualizer implementation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
svg_renderer_bridge.py now searches multiple locations for syllablaze.svg: - Development: resources/ at project root - Installed: site-packages/resources/ - System icons: ~/.local/share/icons/ (where install.py copies it) This ensures the visualizer works in both development and installed modes. setup.py: Added package_data to include resources directory (backup method) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
GPU Detection & CUDA Library Fixes: - Simplified GPU detection via CTranslate2 (removed ~100 lines of restart logic) - Added ctypes-based CUDA library preloading for CTranslate2 compatibility - Libraries now preload before CTranslate2 import to avoid dlopen() issues - Detection works with modern bundled CUDA packages (nvidia-cublas-cu12, nvidia-cudnn-cu12) - No process restart required - libraries preloaded at startup Recording Dialog Visual Fixes: - Fixed z-ordering: SVG icon (z:2), status overlay (z:1), waveform canvas (z:0) - Microphone icon now renders on top instead of being hidden by waveform - Fixes "abstract rounded square" appearance - proper layering restored SVG Path Loading: - Exposed svgPath property from SvgRendererBridge to QML - QML now uses dynamic path instead of hardcoded relative path - Works correctly with installed system icons Package Configuration: - Added QML files to package_data in setup.py - Ensures QML updates install correctly with pipx Icon Cleanup: - Removed old syllablaze.png (replaced by syllablaze.svg) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ssue Testing Notes (Feb 16, 2026): - Clipboard copying issue appears resolved (not reproducible in testing) - Always-on-top toggle requires manual reset - deferred as minor issue - Updated Recording Dialog section to reflect completed status Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- blaze/orchestration.py (new): RecordingController, SettingsService, WindowManager, SyllablazeOrchestrator stubs with Protocol contracts; thin delegation layer that will absorb logic from main.py over time - blaze/constants.py: APPLET_MODE_OFF/PERSISTENT/POPUP/DEFAULT constants - blaze/settings.py: applet_mode setting with validation - blaze/managers/window_visibility_coordinator.py: popup auto-show/hide (connect_to_app_state(), _on_recording_started, _on_transcription_complete, 500ms delayed hide timer); 'off' mode blocks all show requests - blaze/managers/settings_coordinator.py: _apply_applet_mode() immediately adjusts dialog visibility when mode changes in settings UI - blaze/qml/pages/UIPage.qml: Dialog mode ComboBox (Popup/Persistent/Off) with two-way settingsBridge sync - blaze/main.py: pass settings to WindowVisibilityCoordinator, call connect_to_app_state() after signals are wired All 74 tests pass. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace scattered recording dialog / progress window controls with a visual three-card radio selector: None / Traditional / Applet. - Add POPUP_STYLE_* constants and DEFAULT_POPUP_STYLE/DEFAULT_APPLET_AUTOHIDE - settings.py: init, validate, and boolean-convert popup_style + applet_autohide - SettingsBridge.svgPath: expose SVG path as pyqtProperty for QML Image - SettingsCoordinator: _apply_popup_style() derives backend settings (show_recording_dialog, show_progress_window, applet_mode) from the two high-level settings; on_setting_changed() handles popup_style / applet_autohide - UIPage.qml: full replacement with GridLayout of StyleCard components, real Syllablaze SVG in Applet card, conditional sub-options below Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- activeContext.md: add 3-card selector entry, settings architecture diagram, visibility pattern, updated known issues - progress.md: rewrite to reflect current feature set and architecture - systemPatterns.md: replace stale component names with current manager hierarchy, add settings flow, bridge table, popup mode notes Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove stale "files modified (uncommitted)" and resolved-issue noise - Add Settings Architecture section with popup_style derivation table - Add Settings Change Flow section - Update UIPage description to 3-card radio selector - Update bridge list (SettingsBridge now includes svgPath) - Add ApplicationState / visibility control rule to key decisions - Trim Known Issues to just the two active ones Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…eanup - Fix shutdown order in quit_application(): stop recording → wait for transcription thread → close UI windows → cleanup audio hardware. Previously audio hardware was torn down before stopping recording, making _stop_active_recording() dead code. - Add _is_shutting_down flag so toggle_recording() and quit_application() are idempotent / no-ops once shutdown begins. - Fix TranscriptionManager.cleanup(): replace bare wait(5000) with a three-phase shutdown (quit → wait(3000) → terminate → wait(1000)) so a thread blocked in a CTranslate2 C++ call is forcefully killed rather than silently left running as a zombie. - Add RecordingDialogManager.cleanup(): hide window and call engine.deleteLater() for an orderly QML engine teardown; called from _close_windows() during shutdown. - Store D-Bus bus as self._dbus_bus; disconnect it via _cleanup_dbus() after the app-exit future resolves so the bus name is released cleanly. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
In persistent mode the recording applet was only visible on the virtual desktop where the app started. Add an applet_onalldesktops setting (default true) that writes the KWin rule onalldesktops/onalldesktopsrule keys so the window appears on all virtual desktops. The option is surfaced in Settings → UI as a switch visible only when the applet style is selected and auto-hide is off (i.e. persistent mode). Switching to popup/off mode clears the rule so pop-ups are not sticky. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
In persistent applet mode the QML window auto-shows via `visible: true` before Python's show() is ever called, so set_window_on_all_desktops() was never invoked at startup. Schedule a deferred QTimer.singleShot(400) call in initialize() to apply it after the Wayland surface is fully mapped. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
QTimer.singleShot(400, ...) is a race condition: on a loaded system the 400ms may not be enough for the Wayland compositor to map the surface. Connect to QWindow::visibilityChanged instead and fire set_window_on_all_desktops() on the first non-Hidden event, then disconnect — deterministic and correct. Also removes the now-unused QTimer import. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
On Wayland, clipboard ownership is tied to the window that sets it. When the recording dialog closed after transcription, the compositor would clear the clipboard because the owning window was gone. Fix: - Reorder operations: set clipboard BEFORE emitting transcription_stopped signal (which triggers dialog hide) - Add persistent hidden widget in ClipboardManager to own clipboard - Uses QMimeData with setMimeData() for proper clipboard ownership - Revert timer to 500ms (object lifetime handles persistence, not timing) This is the proper solution: object lifetime, not arbitrary delays.
RecordingDialogVisualizer.qml had visible: true hardcoded, causing the window to appear the moment the QML engine loaded — before WindowVisibilityCoordinator was wired up or set_recording_dialog_visible() was called. This produced a brief flash of the applet even in traditional or none mode. Python already has sole control over visibility via set_recording_dialog_visible() / recording_dialog.show(). Starting with visible: false lets that control take effect without a race. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
ApplicationState initialises _recording_dialog_visible from persisted QSettings (often True). When the user switches traditional→applet mode, _apply_applet_mode calls set_recording_dialog_visible(True) but the deduplication guard sees the value is already True and silently drops the signal — so the dialog never appears. Add a force=True parameter to set_recording_dialog_visible and use it in _apply_applet_mode(persistent) so the coordinator always receives the signal and shows the dialog regardless of cached state. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes the recording applet visualization to properly align with SVG design and adds missing window management features: - Use SVG waveform element bounds for visualization positioning instead of arbitrary widget percentages - Implement 36-bar radial waveform with proper coordinate mapping from SVG space to widget space - Add 10× audio sample amplification for better visibility (microphone input is typically very quiet) - Color gradient: green (low) → yellow (mid) → red (high volume) - Minimum 5px bar length ensures visibility even during silence - Implement set_on_all_desktops() via KWin D-Bus scripting API with dual approach: immediate application via window script + persistent KWin rule Technical details: - Maps waveform bounds using _map_svg_rect_to_widget() for accurate scaling - Inner radius: 35% of waveform bounds, outer radius: 48% - 3px line width, 36 bars evenly distributed (10° apart) - Integrates with existing kwin_rules.set_window_on_all_desktops() and create_or_update_kwin_rule() for persistence Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove Qt window hints and use KWin window rules + D-Bus for all window management. Qt hints are X11-specific and unreliable on Wayland; KWin is the proper way on KDE Plasma. Fixes: - Persistent mode now works: window stays shown at startup - Always-on-top works via KWin rules, not WindowStaysOnTopHint - On-all-desktops works via KWin D-Bus scripting + rules Changes: - RecordingApplet._setup_window(): Remove WindowStaysOnTopHint, set window title for KWin rule targeting - RecordingApplet._apply_kwin_properties(): Apply window properties via KWin after window is shown (in showEvent handler) - RecordingApplet.set_always_on_top(): Use KWin rules instead of Qt flags - RecordingApplet.set_on_all_desktops(): Remove visibility check, always apply via KWin D-Bus - RecordingDialogManager.show(): Simplified - let applet handle properties - SettingsCoordinator._apply_applet_mode(): Fixed persistent mode logic - main.py: Show applet unconditionally in persistent mode after creation Technical approach: - Window properties applied in showEvent via QTimer.singleShot(100ms) to ensure window is fully mapped before KWin D-Bus calls - KWin rules updated for persistence across restarts - KWin D-Bus scripting used for immediate property application Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- RecordingApplet: Apply KWin properties on every show (not just first) to ensure on-all-desktops is set when switching to persistent mode - ClipboardManager: Show owner window during each clipboard operation to maintain Wayland clipboard ownership when applet hides Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…vice - Create ClipboardPersistenceService with dedicated long-running window for Wayland ownership - Create NotificationService for decoupled notification handling - Refactor ClipboardManager to pure service with signals (no UI deps) - Enhance RecordingController to orchestrate full recording pipeline - Update main.py initialization and signal wiring - Fix dev-update.sh to include new services directory This eliminates timing issues with clipboard on Wayland by maintaining persistent ownership through a dedicated hidden window throughout app lifecycle.
- Initial size: 400x320 → 280x160 (half linear dimensions) - RecordingState height: 320 → 160 - ProcessingState height: 220 → 110 - Font and button sizes scaled down proportionally - Maintains rectangular aspect ratio for better layout
Implement complete documentation overhaul following Divio framework: **Archive temporary documentation (53 files):** - Move 29 refactoring phase files to docs/archive/refactoring/ - Archive 7 implementation summaries, 2 migrations, 11 plans, 2 reports - Clean root directory from 10+ to 3 essential MD files **Create new documentation structure:** - Establish Divio-based organization (Tutorials, How-To, Reference, Explanation) - Add docs/getting-started/, user-guide/, developer-guide/, explanation/, adr/ - Create landing page (docs/index.md) with audience routing **Add 20 critical documents:** - CONTRIBUTING.md: Comprehensive contribution guidelines (2,500 lines) - Getting Started: installation, quick-start, troubleshooting (3,200 lines) - User Guide: features, settings-reference (5,000 lines), recording-modes, shortcuts - Developer Guide: setup, architecture, testing, patterns, contributing - Explanation: design-decisions (3,000 lines), wayland-support (2,800 lines), settings-architecture, privacy-design - ADRs: 3 initial ADRs (manager-pattern, qml-kirigami-ui, settings-coordinator) + template **Set up documentation tooling:** - Configure MkDocs with Material theme for GitHub Pages - Add mkdocs.yml with navigation, extensions, and styling - Create requirements-dev.txt with doc dependencies - Integrate doc build into CI/CD (.github/workflows/python-app.yml) - Add custom CSS (docs/stylesheets/extra.css) **Enhance CLAUDE.md for AI agents:** - Add File Map section (40+ files mapped by category) - Add Critical Constraints (NEVER/ALWAYS patterns) - Add Common Agent Tasks (6 detailed step-by-step procedures) - Update CI section with doc build and deployment info **Update README.md:** - Add documentation site link at top - Add Documentation section with 6 key links - Add Contributing section - Link troubleshooting to docs site **Results:** - 33 active documentation files in organized structure - 53 archived temporary files with retention policy - Root cleaned to 3 files (README, CLAUDE, CONTRIBUTING) - MkDocs site ready for GitHub Pages deployment - Documentation coverage: 100% of critical areas Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Update version from 0.5 to 0.8 in:
- blaze/constants.py (APP_VERSION)
- setup.py (version)
- README.md (title and What's New section)
- Update GitHub repository references from PabloVitasso to Zebastjan:
- All documentation files
- Clone URLs
- Issue tracker links
- mkdocs.yml (site_url, repo_url, social links)
- DOCUMENTATION_IMPLEMENTATION_SUMMARY.md
- Create comprehensive CHANGELOG.md with:
- v0.8 release notes (documentation overhaul, orchestration refactor,
UI enhancements, stability improvements)
- Version history for v0.5, v0.4 beta, v0.3
- v1.0 roadmap: SyllabBlurb, enhanced visualization, clipboard-free mode
- Update Project Milestones:
- Mark Milestones 1-4 as completed
- Mark documentation overhaul as done in Milestone 5
- Add new Milestone 6 for v1.0 features
- Add Roadmap section to mkdocs.yml navigation
Note: Preserved acknowledgement of original projects (Telly Spelly and
PabloVitasso's Syllablaze) in README.md as requested.
- Detect CUDA out-of-memory errors during transcription - Clear CUDA cache and fall back to CPU when GPU exhausted - Improve transcription manager cleanup with torch.cuda.empty_cache() - Add detailed error logging for debugging GPU issues - Add GPU Issues section to troubleshooting docs - Add on-all-desktops support via KWin scripting - Improve Klipper clipboard D-Bus methods (qdbus6 support) - Add settings_coordinator parameter to window_visibility_coordinator Fixes crash when other AI workloads consume GPU memory.
… conditions **Root Cause Fix:** - Python 3.14 changed default multiprocessing start method to 'forkserver' - CTranslate2's internal worker pool is incompatible with forkserver mode - Results in semaphore leaks (/mp-XXXXX) and SIGABRT crashes **Solution:** - Force multiprocessing start method to 'fork' at application startup - Must be set before any Qt or CTranslate2 imports - Added noqa:E402 comments for intentional imports-after-code **Additional Defensive Improvements:** - Added is_worker_running() to detect race conditions under high load - Added cancel_transcription() for graceful worker cleanup - Enhanced readiness checks to prevent concurrent operations - Improved resource cleanup in shutdown path **Testing:** - All 93 tests pass - Flake8 clean (max-line-length=127) - Verified recording works without crashes Fixes semaphore leak warnings and SIGABRT crashes on Python 3.14. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Document the two-phase shutdown pattern for canceling in-progress transcriptions and preventing CTranslate2 semaphore leaks. Explains why this is needed (race conditions under high load) and how users should expect the cancellation behavior to work. Related to b24c232 (Python 3.14 multiprocessing compatibility fix). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix UnboundLocalError for 'logging' in main.py by removing redundant inner import - Implement persistent wl-copy process management for Wayland clipboard - Keep single wl-copy process alive to maintain clipboard ownership - Kill old process before starting new one to avoid zombie processes - Use non-blocking Popen instead of blocking subprocess.run - Add proper shutdown cleanup for wl-copy processes - Update clipboard_manager to call portal_service.shutdown() on exit - Fix GUI hanging on 'Transcribing...' caused by blocking wl-copy calls This resolves intermittent clipboard failures and UI freezes on Wayland.
…tion - Fix: First transcription in popup mode now correctly shows the dialog - Changed initialization to hide dialog at startup in popup mode - Added processEvents() after show() to ensure window mapping - Added detailed logging for debugging visibility issues - Improve dot curtains visualization: - Dots now expand from center to full window height (top to bottom) - Increased dot size for better visibility - Dots positioned closer to microphone edge - Expanded vertical range by 25% to reach window edges - Fixed minimum volume floor for consistent visibility
This change allows users to control Syllablaze using global keyboard shortcuts in KDE. When setting up shortcuts, users must add the script as an 'Application' rather than a 'Command' for proper functionality.