Add Models UI section with per-model status (Loading/Downloading/Ready/Error) and Fix Models button for user-initiated recovery when model downloads are corrupted.
- Add models.py with shared utilities for HuggingFace cache management
- Add validation to detect corrupted/incomplete model downloads
- Add Models group at top of UI showing OCR and Translation status
- Add Fix Models button that clears caches and re-downloads failed models
- Disable Start Capture button until both models are loaded
Fixes#139🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Persists banner overlay position (X and Y) across application sessions
- Position saved to config file on app exit, restored on startup
- Position preserved when switching between Banner and Inplace modes
- Multi-monitor support: banner resizes to match screen width when dragged between monitors
- Snap-to-screen option: optionally snap banner to screen edges (configurable via checkbox)
- Works on all platforms including Linux/Wayland
## Implementation
- Added `banner_x`, `banner_y`, and `banner_snap_to_screen` fields to Config
- Added `set_position()` and `get_position()` methods to both Qt (macOS/Windows) and Tkinter (Linux) overlays
- Qt overlay: Added `_snap_to_current_screen()` and `_resize_to_fit()` for Windows/macOS multi-monitor support
- Fixed Windows DPI scaling issues during cross-screen transitions
Closes#154🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Fix Windows crash (access violation 0xc0000374) when using background thread for OCR/translation
- Use Python's native `threading.Thread` instead of Qt's `QThread` to avoid conflicts between ONNX runtime and windows-capture
- Add CUDA inference test during model load for better GPU fallback
## Root Cause
Investigation revealed that QThread + ONNX + windows-capture causes access violations on Windows. The crash occurs during ONNX inference on a QThread when windows-capture is active on the main thread.
## Solution
Replace QThread with Python's native `threading.Thread` and a `FrameBuffer` (Lock + Condition pattern):
1. **workers.py**: Complete rewrite to use Python threading
- New `FrameBuffer` class using `Lock` + `Condition` for thread-safe signaling
- "Latest frame" pattern: only the most recent frame is kept, stale frames are automatically dropped
- Models load on worker thread (non-blocking startup)
- Results emitted via Qt signals (thread-safe)
2. **main_window.py**: Simplified to use new worker API
- Removed QThread setup and platform-specific code paths
- Connected to `models_ready`/`models_failed` signals for async model loading
3. **translate.py**: Added CUDA inference test during load
- Tests actual inference after model loading (not just load)
- Catches cuBLAS failures early
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Fix app freezing after 30-60 seconds on Arch Linux by moving OCR/translation to a background QThread
- Fix Wayland capture threading issues (missing thread.join, unsynchronized flag access)
- Fix hotkey not being saved/restored from config
- Add error signal for model loading failures
## Details
The app was freezing because OCR and translation ran synchronously on the main Qt thread, blocking the event loop for 500-1100ms per frame.
**Threading fix:**
- Move `ProcessWorker` to a dedicated `QThread` using `moveToThread()`
- Add `process_frame_slot` with skip-if-busy logic to prevent queue buildup
- Initialize OCR/Translator models on worker thread
- Use Qt Signal for cross-thread frame passing
**Wayland fix:**
- Add `thread.join()` in `WaylandCaptureStream.stop()` to prevent race conditions
- Use `threading.Event` for thread-safe `window_invalid` flag access
**Hotkey fix:**
- Load hotkey from `config.hotkeys["toggle_overlay"]` on init
- Save hotkey to config when changed in UI
**Error handling:**
- Add `models_failed` signal to notify UI if model loading fails
Closes#160🤖 Generated with [Claude Code](https://claude.com/claude-code)
Exclude .tmpl.py files from bytecode compilation as they are
Jinja templates, not valid Python.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
uv-managed Python 3.12 includes tkinter, while system Python 3.13+ often
doesn't have it installed. This ensures the Linux tkinter overlay works
out of the box.
- Update install.sh to use Python 3.12
- Update .python-version to 3.12 for local development
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
uv-managed Python doesn't include tkinter by default. This adds a Qt-based
fallback overlay for Linux that's used when tkinter is not available.
- Add overlay_linux_qt.py with Qt-based BannerOverlay and InplaceOverlay
- Update overlay.py to try tkinter first, fall back to Qt
- Qt overlay may have some limitations but works without system dependencies
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
pynput depends on evdev on Linux, which requires Python headers to build.
This change uses python-xlib (already a dependency for capture) for global
hotkey handling on Linux, avoiding the compilation requirement.
- Add gui/keyboard.py with platform-specific keyboard listener
- Make pynput conditional on non-Linux platforms in pyproject.toml
- Update main_window.py to use the new keyboard abstraction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>