- Change default transcription mode from local to byok (cloud/Deepgram)
- Move Transcription Mode selector to top of settings for visibility
- Hide local-only settings (model, VAD, timing) when cloud mode selected
- Disable Start button until API key (byok) or login (managed) is configured
- Add room creation and share code flow to Shared Captions section
- Add POST /api/create-room endpoint to Node.js sync server
- Update default sync URL placeholder to caption.shadowdao.com
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On macOS, Cmd+Q triggers ExitRequested before Exit. If the app is
force-quit or closed via Cmd+Q, the Exit event may not fire,
leaving the sidecar process orphaned with ports 8080/8081 in use.
Now handles both ExitRequested and Exit to ensure the sidecar is
always stopped when the app closes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sidecar process was orphaned when the Tauri app closed, leaving
ports 8080/8081 in use. On next launch the new sidecar couldn't bind
those ports and failed to start.
Added RunEvent::Exit handler that stops the sidecar before the app
process terminates.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New features:
- Settings > Transcription Engine > "Change Transcription Engine"
button stops the sidecar, deletes downloaded files, and reloads
the app to show the engine selection screen
- Improved SidecarSetup descriptions with detailed explanations
of each variant and "Recommended" tag on Cloud (Deepgram)
- Cloud option listed first as the recommended choice
- New reset_sidecar Tauri command that cleans up sidecar files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Dev mode: use `uv run python` instead of bare `python` to ensure
the project venv is used. Also use CARGO_MANIFEST_DIR to find the
project root reliably.
2. Engine reload: changing remote.mode (local/managed/byok) now
triggers a full engine reload. Previously only model and device
changes triggered reload, so switching to Deepgram had no effect
until the app was restarted.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PyInstaller frozen executables buffer stdout when piped to a
subprocess (no TTY). Even with flush=True in Python, the OS-level
pipe buffer can delay output. This prevented the ready event from
reaching the Tauri app, causing the "Starting sidecar..." hang.
Fix: set PYTHONUNBUFFERED=1 env var on both prod and dev sidecar
commands, plus -u flag for dev mode Python.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two issues caused the app to freeze on "Starting sidecar...":
1. wait_for_ready() used a blocking BufReader::lines() iterator
with a timeout check between lines. If the sidecar produced no
stdout output (crashed, missing binary, or slow model loading),
the read blocked forever. Now uses a background thread with
mpsc::recv_timeout() for a real 120s deadline.
2. start_sidecar was a synchronous Tauri command that blocked the
main thread during the entire sidecar startup (up to 120s).
Now async via tokio::spawn_blocking, keeping the UI responsive.
Also logs all sidecar stdout lines to stderr with [sidecar-stdout]
prefix for debugging.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major version bump reflecting the architecture change from PySide6/Qt
to Tauri v2 + Svelte 5 with cross-platform support for Windows,
macOS, and Linux.
Key changes since v1.4.0:
- Tauri v2 native desktop shell replacing PySide6/Qt
- Svelte 5 reactive frontend
- Headless Python backend as a downloadable sidecar
- Deepgram cloud transcription (managed + BYOK)
- Gitea CI/CD with per-OS builds and automated releases
- Sidecar auto-update checking on startup
- 63-test suite (Python + Svelte + Rust)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs preventing sidecar from starting:
1. Directory was "sidecar-sidecar-v1.0.3" (double prefix) because
sidecar_dir_for_version() prepended "sidecar-" to a version that
already contained it. Now uses the tag directly as the dir name.
2. After a crash, the Python InstanceLock PID file at
~/.local-transcription/app.lock remained, blocking the next launch
with "Another instance is already running". Now clears the stale
lock file before spawning the sidecar.
Also fixed cleanup_old_versions() and tests to match the corrected
directory naming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the sidecar process exits before sending the ready event, the
error message now includes the last 10 lines of stderr. Stderr is
captured in a background thread and written to sidecar.log in the
app data directory.
This helps diagnose why the PyInstaller sidecar fails to start
(missing DLLs, import errors, permission issues, etc.).
Log location: %APPDATA%\net.anhonesthost.local-transcription\sidecar.log
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Test suite covering all three layers:
Python backend (25 tests):
- AppController: state machine, start/stop, callbacks, settings reload
- API server: REST endpoints, config CRUD, status, devices
- Config: dot-notation get/set, persistence, nested paths
- Main headless: ready event port format validation
Svelte frontend (14 tests via Vitest):
- Backend store: exported properties/methods, port derivation, URLs
- Config store: method names (fetchConfig not loadConfig), defaults
- Transcriptions store: add/clear/plaintext
- File extension regression: ensures $state runes only in .svelte.ts
Rust sidecar (24 tests via cargo test):
- Platform/arch detection, asset name construction
- Ready event deserialization (with extra fields tolerance)
- Path construction, version read/write, old version cleanup
- Zip extraction, SidecarManager lifecycle
CI workflow (.gitea/workflows/test.yml):
- Runs on push to main and PRs
- Three parallel jobs: Python, Frontend, Rust
Also fixes three bugs found during test planning:
- Settings: /api/check-updates -> GET /api/check-update
- Settings: /api/remote/login -> /api/login
- Settings: /api/remote/register -> /api/register
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tauri v2 requires explicit permission grants. The SidecarSetup
component uses listen() from @tauri-apps/api/event to receive
download progress, which requires core:event:allow-listen.
Added default capability with core, event, shell, dialog, and
process permissions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added write_log Tauri command that writes to frontend.log in app data dir
- App.svelte now logs each startup step (Tauri import, sidecar check, launch)
- Startup overlays use inline styles as fallback so they're visible even if
CSS variables fail to load
- Debug status shown on the checking/connecting screens
- Rust side logs startup info to app.log (resource dir, data dir)
Log files location: %APPDATA%/net.anhonesthost.local-transcription/ (Windows)
or ~/Library/Application Support/net.anhonesthost.local-transcription/ (macOS)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On first launch, the app now prompts users to download the Python
sidecar (CPU or CUDA variant) from Gitea releases, matching the
voice-to-notes pattern. On subsequent launches, it auto-launches
the sidecar and connects.
New Rust module (src-tauri/src/sidecar/):
- download_sidecar: streams download with progress events, extracts zip
- check_sidecar: verifies installed sidecar binary exists
- check_sidecar_update: compares local vs latest release version
- SidecarManager: launches binary, waits for ready JSON, manages lifecycle
- Dev mode: runs `python -m backend.main_headless` directly
- start_sidecar/stop_sidecar/get_sidecar_port: Tauri commands
New Svelte component (SidecarSetup.svelte):
- First-time setup overlay with CPU/CUDA variant selection
- Download progress bar with byte counter
- Error state with retry, success state with auto-continue
Updated App.svelte state machine:
- checking -> needs_setup -> starting -> connected
- Falls back to direct connection in browser dev mode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bundled .ico had non-RGBA PNGs which caused Tauri's macOS bundler
to fail with "The PNG is not in RGBA format!". Regenerated all icons
from the source PNG as proper RGBA, and added icon.icns for macOS.
Also fixed bundle identifier from "com.localtranscription.app" (the
.app suffix conflicts with macOS bundle extension) to
"net.anhonesthost.local-transcription".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>