Ships the Capture release end to end. Global-hotkey dictation with
synthetic paste into the focused app on macOS and Windows, an on-screen
pill across recording / transcribing / refining, customizable push-to-
talk and toggle chords, and an accessibility-permission prompt scoped to
Settings → Captures with inline re-check feedback.
Voice profiles gain optional personalities that power compose / rewrite /
respond actions via a local Qwen3 LLM — shared with refinement, so there
is one local LLM in the app, not two.
Refinement hardened with deterministic Whisper-loop collapse before the
LLM sees the transcript, per-capture flag snapshots for re-runs, and a
ten-transcript evaluation harness across every bundled refinement size.
Version bump 0.4.5 → 0.5.0.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): add i18next foundation with English + zh-CN locales
Installs i18next + react-i18next + language detector and wires up a
language selector in the General settings page. Extracts strings from
the highest-visibility surfaces: all settings tabs, model management,
sidebar nav, main editor, and the floating generate box. Remaining
strings (profile forms, history, stories/effects/voices/audio tabs)
can land in follow-up PRs.
Closes#411.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(i18n): ensure language switch actually re-renders the tree
- `nonExplicitSupportedLngs: true` was normalizing `zh-CN` → `zh` in
some code paths; since we have explicit `zh-CN` resources, swap it
for `load: 'currentOnly'` which keeps the code as-is.
- `react: { useSuspense: false }` — react-i18next v17 defaults Suspense
on, which can silently suspend components mid-switch and look like
"nothing happens" to the user.
- Use `i18n.language` (the raw current code) instead of
`resolvedLanguage` in the selector so the dropdown always mirrors
what we just set.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize ProfileCard, HistoryTable, and relative dates
- ProfileCard: "No description", "designed" badge, aria-labels, and
delete dialog.
- HistoryTable: delete / clear-failed / import / effects dialogs.
- formatDate: switch date-fns `formatDistance` locale based on
`i18n.language` so "5 minutes ago" becomes "5 分钟前" under zh-CN.
HistoryTable now subscribes via useTranslation so the table
re-renders when language flips.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize Stories tab (list, content, dialogs, toasts)
Covers the title + "New Story" button, empty states, story row
metadata (item count, updated time), the create/edit/delete dialogs,
and all toast notifications. Also handles StoryContent: "Select a
story" placeholder, search popover, "Export Audio" button, and the
"Generating N audios" pending indicator.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize history item and story item dropdown menus
Covers the "..." action menu on both the History table (Play, Export
Audio, Export Package, Apply Effects, Regenerate, Delete) and on
individual story chat items (Play from here, Remove from Story).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize Effects tab (list, detail, dialogs, toasts)
Covers EffectsList (title, "New Preset", section headers, preset
cards) and EffectsDetail (header buttons for Save / Save as Custom /
Delete, name/description fields, preview section, Save as Custom
dialog, all toasts).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize EffectsChainEditor and built-in preset names
- Effect type labels (Chorus/Flanger, Reverb, Delay, Compressor, Gain,
High-Pass, Low-Pass, Pitch Shift) and every param label (LFO speed,
Modulation depth, Threshold, Ratio, etc.) go through
`effects.types.<type>.{label,params.<param>}` with the backend string
as defaultValue fallback.
- Chain-level controls: "Load preset…", "Add effect…", "Clear",
Power/Remove button titles.
- Built-in preset names + descriptions (Robotic, Radio, Echo Chamber,
Deep Voice) are translated client-side; user-created presets keep
their original names.
Backend keeps returning English — frontend intercepts and translates
via key lookup, defaulting to the backend string so unknown
effects/params don't break.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize Create/Edit Voice modal and audio sample panels
ProfileForm now routes its title, description, voice-source toggle
(Clone from audio / Built-in voice), field labels (Name, Description,
Language, Engine, Voice, Reference Text, Default Engine, Default
Effects), sample tabs (Upload / Record / System Audio), action
buttons, and every toast + Zod validation message through i18n.
Also covers the three AudioSample panels (Upload/Record/System) — the
choose-file / start-recording / start-capture call-to-actions, the
"N remaining" countdown, "Recording complete" / "Capture complete"
states, and the Play / Transcribe / Remove / Record Again buttons.
SampleList too — the "No samples yet" empty state, per-sample edit
mode, mini-player aria labels, Delete Sample dialog, and toasts.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize Audio Channels tab (list, dialogs, device picker)
Covers the "Audio Channels" title and "New Channel" button, the empty
state, per-channel section labels (Output Devices / Assigned Voices),
the Available Devices right pane with its three contextual hints, the
"No voices assigned" fallback, and both Create/Edit dialogs (titles,
descriptions, field labels, Select placeholders, and the "(default)"
badge).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize Voices tab (table header, search, inspector)
VoicesTab now translates the "Voices" title, search placeholder,
"New Voice" button, all six table column headers (Name, Language,
Generations, Samples, Effects, Channels), the avatar alt text, and
the per-row channel MultiSelect (placeholder + "(Default)" suffix).
VoiceInspector routes its form labels through the existing
`profileForm.fields.*` keys, has its own "Default Effects" hint and
avatar/save toasts, and reuses the ProfileForm Zod validation.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): localize ProfileList unsupported-model note and empty state
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): add Traditional Chinese (zh-TW) locale
Adds a zh-TW translation with Taiwan vocabulary conventions
(e.g. 預設 / 儲存 / 載入 / 匯入 / 匯出 / 設定 / 檔案 / 伺服器 / 裝置 / 網路).
Registers it alongside en and zh-CN; the language dropdown picks it up
automatically from SUPPORTED_LANGUAGES.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* feat(i18n): add Japanese (ja) locale
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(i18n): localize relative dates for ja and zh-TW
formatDate only mapped zh-CN, so history timestamps stayed in English for
ja and zh-TW users even after the rest of the UI translated. Extend the
switch to ja and zhTW from date-fns/locale.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
* fix(i18n): address PR review feedback
Bugs:
- GeneralPage: network access toast used the keep-server-running title key
(wrong semantic scope). Add networkAccess.updatedTitle and use it.
- GeneralPage: fallback "Unknown" version was stored as a translated string
in state, so it stayed stale across language switches. Store null, resolve
the label at render time.
- GeneralPage: memoize the zod resolver on t and retrigger validation when
the locale changes so existing error messages retranslate.
- GpuPage: adding t to the CUDA progress EventSource effect deps caused the
SSE connection to be torn down and reopened on every language change,
potentially dropping in-flight download events. Capture t in a ref.
- HistoryTable: Effects dialog still rendered English "Source" / "Select
source version" / "Cancel" / "Apply" / "Applying..." — localize them.
- Locales: zh-CN / zh-TW / ja devSuffix was missing the leading space before
"(开发版)"/"(開發版)"/"(開発版)", so dev builds rendered "v0.4.2(开发版)"
instead of "v0.4.2 (开发版)".
Nits:
- ModelManagement: rename .find((t) => ...) callback param to avoid
shadowing useTranslation().t.
- GenerationPage: rename chunkLimit.value interpolation key from count →
chars so i18next doesn't silently activate pluralization if a translator
later adds _one/_other forms.
- LanguageSelect: narrow onValueChange handler param to LanguageCode.
Key count now 559 across en/zh-CN/zh-TW/ja (added 4 effectsDialog keys
plus networkAccess.updatedTitle).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
When user attempts to change model storage location with no models
downloaded, the migration API returns moved=0 early. Previously the UI
would still call setCustomModelsDir() and restart the server, causing
unexpected behavior (hang/connection lost).
This change checks migrationResult.moved === 0 and shows a helpful
toast message instead of proceeding with the storage change.
Fixes: #426
Co-authored-by: fuleinist <[email protected]>
Add Kokoro-82M as a new TTS engine — 82M params, CPU realtime, 8 languages,
Apache 2.0. Unlike cloning engines, Kokoro uses pre-built voice styles, which
required a new profile type system to support non-cloning engines cleanly.
Kokoro engine:
- New kokoro_backend.py implementing TTSBackend protocol
- 50 built-in voices across en/es/fr/hi/it/pt/ja/zh
- KPipeline API with language-aware G2P routing via misaki
- PyInstaller bundling for misaki, language_tags, espeakng_loader, en_core_web_sm
Voice profile type system:
- New voice_type column: 'cloned' | 'preset' | 'designed' (future)
- Preset profiles store engine + voice ID instead of audio samples
- default_engine field on profiles — auto-selects engine on profile pick
- Create Voice dialog: toggle between 'Clone from audio' and 'Built-in voice'
- Edit dialog shows preset voice info instead of sample list for preset profiles
- Engine selector locks to preset engine when preset profile is selected
- Profile grid filters by engine — shows Kokoro voices when Kokoro selected
- Custom empty state when no preset profiles exist for selected engine
Bug fixes:
- Fix relative audio paths in DB causing 404s in production builds
- config.set_data_dir() now resolves to absolute paths
- Startup migration converts existing relative paths to absolute
Also updates PROJECT_STATUS.md and tts-engines.mdx developer guide.
Integrates HumeAI's TADA (Text-Acoustic Dual Alignment) speech-language
model as a new TTS engine. TADA uses a novel 1:1 token-audio alignment
that produces coherent speech over long sequences (700s+).
Two model variants:
- tada-1b: English-only, ~4GB, built on Llama 3.2 1B
- tada-3b-ml: 10 languages, ~8GB, built on Llama 3.2 3B
Backend uses the Encoder for voice prompt encoding with caching, and
TadaForCausalLM with flow-matching diffusion for generation. Supports
bf16 inference on CUDA, forces CPU on macOS (MPS compatibility).
Installed with --no-deps due to torch>=2.7 pin conflict; descript-audio-codec
and torchaudio added as explicit sub-dependencies.
- Add openPath and pickDirectory to PlatformFilesystem interface
- Remove direct @tauri-apps/plugin-shell and plugin-dialog imports from app
- Merge build-cuda.yml into release.yml as a parallel job
- Add model folder migration with byte-level progress tracking (backend + UI)
- Custom models directory support via VOICEBOX_MODELS_DIR env var passed to sidecar
- Hardcoded model descriptions displayed in model detail cards
- Open model folder button in storage location row
- Remove 'not downloaded' badge from model cards
- Fix server settings scroll offset for audio player
- Fix shell open permission to allow file paths
- Add normalize toggle to generation settings
- Split chunking/crossfade sliders into dedicated GenerationSettings card
- Merge connection status badges into ConnectionForm (remove ServerStatus card)
- 2-column grid layout for the entire settings page
- GPU Acceleration: remove icon, badge, and MLX info card
- Models: merge 'Other Voice Models' into single 'Voice Generation' list
- Model detail: remove 'Downloaded' badge, border above actions, swap
badges above stats row, match disk size font to stats
- POST /models/{model_name}/unload — unloads a specific model from
memory without deleting from disk, supports all engine types
- Frontend: Unload button in model detail dialog when model is loaded
- Delete button remains disabled while loaded (unload first)
- Add HFProgressTracker to LuxTTS and Chatterbox backends so tqdm-based
file-level download progress reaches the frontend (previously only Qwen
had this, LuxTTS/Chatterbox showed a static spinner)
- Add progress/current/total/filename fields to ActiveDownloadTask so the
/tasks/active polling endpoint carries progress data
- Show inline progress bar + bytes in the model list and detail modal,
poll at 1s during active downloads (5s otherwise)
- Fix GpuAcceleration crash: cudaStatusLoading was referenced before
initialization in its own useQuery declaration
- New ChatterboxTTSBackend wrapping ChatterboxMultilingualTTS (ResembleAI/chatterbox)
- Supports 23 languages including Hebrew, forces CPU on macOS (MPS issue)
- Monkey-patches torch.load for CPU loading, forces eager attention for compatibility
- trim_tts_output utility cuts trailing silence/hallucination from Chatterbox output
- Full engine integration: /generate, /generate/stream, model status/download/delete
- Hebrew (he) added to supported languages in frontend and backend validation
- Single flat model dropdown extended with Chatterbox option in both generation UIs
- ModelManagement UI groups LuxTTS and Chatterbox under 'Other Voice Models' section
Introduce LuxTTS (ZipVoice) alongside Qwen TTS, enabling users to choose
between engines at generation time. LuxTTS offers fast, English-focused
voice cloning at 48kHz with ~1GB VRAM.
Backend:
- Add LuxTTSBackend with encode_prompt/generate_speech integration
- Multi-engine registry (get_tts_backend_for_engine) replacing singleton
- Engine-prefixed voice prompt cache keys to avoid collisions
- Engine field on GenerationRequest (default 'qwen' for backward compat)
- Engine dispatch in /generate and /generate/stream endpoints
- LuxTTS in model status, download, and delete maps
Frontend:
- TTS Engine selector dropdown in GenerationForm (Qwen TTS / LuxTTS)
- Conditionally hide Model Size and Delivery Instructions for LuxTTS
- Engine field added to TypeScript types and Zod schema
- LuxTTS section in Model Management page
- Audio player: aria-labels for Play/Pause, Loop, Mute, Close; labelled playback and volume sliders
- Generation: aria-labels for Generate speech and Fine tune instructions buttons
- Voice cards: focusable, labelled, Enter/Space to select
- History rows: focusable, labelled, Enter/Space to play; transcript textarea labelled
- Voices tab: focusable rows, labelled, Enter/Space to edit; Actions button labelled
- Model management: focusable model rows and labelled Download/Delete buttons
- Server tab: regions with aria-label and tabIndex for Connection, Status, App Updates
- Stories: focusable story rows, labelled, Enter/Space to select; Actions and track editor buttons labelled
- Voice profile samples: Play/Pause/Stop and mini-player slider labelled
Tested with NVDA and Narrator on Windows. See docs/PR-ACCESSIBILITY.md for full description.
Made-with: Cursor
- Fix transcribe_audio to use whisper-large-v3 mapping (not openai/whisper-large)
- Propagate error field in progress-only fallback path for get_active_tasks
- Use removed return value in cancel endpoint to vary response message
- Add error rollback to handleCancel with toast on failure
- Make isCancelling per-model instead of global
- Fix inverted chevron icons in Problems panel
- Move all clears under lock in clear_all_tasks
- Simplify cancel_download to use dict.pop()
- Add cancel (X) button on downloading and errored model items
- Add collapsible Problems panel (VS Code-style) showing error details
- Add "Clear All" button to reset all stale download/error state
- Add POST /models/download/cancel endpoint to dismiss individual downloads
- Add POST /tasks/clear endpoint to reset all task and progress state
- Include error messages in /tasks/active response for visibility
- Capture SSE error messages client-side for immediate display
- Fix whisper-large using wrong HF repo (openai/whisper-large → openai/whisper-large-v3)
- Fix Whisper HF repo mapping in both PyTorch and MLX backends
- Shorten error toast to point users to Problems panel instead of wall of text
- Rearranged imports for consistency across components.
- Enhanced the ModelManagement component to include detailed logging for download actions and errors.
- Updated the ModelProgress component to connect to SSE only when actively downloading, preventing connection exhaustion.
- Added a downloading state to the model status to indicate ongoing downloads.
- Improved toast notifications for model downloads with completion and error callbacks.
- Refactored the useModelDownloadToast hook to support new callbacks for download completion and error handling.
- Updated backend model status to reflect downloading state during active downloads.
- Updated ConnectionForm to utilize a Checkbox component for managing the "keep server running" setting, enhancing user interaction.
- Refactored Checkbox component to use a button element for better accessibility and visual feedback.
- Streamlined import statements and improved code organization across multiple components for better readability.
- Eliminated CheckCircle2 icon from ModelManagement, ModelProgress, ServerStatus, and UpdateStatus components to streamline the visual presentation.
- Updated import statements accordingly to reflect the removal of unused icons.
- Updated App and AudioTab components to utilize TOP_SAFE_AREA_PADDING and BOTTOM_SAFE_AREA_PADDING constants for better layout adjustments.
- Enhanced HistoryTable and ModelManagement components for improved visual consistency.
- Streamlined import statements and component structure across various files for better organization and readability.
- Updated App, AppFrame, and AudioTab components to utilize new TOP_SAFE_AREA_PADDING and BOTTOM_SAFE_AREA_PADDING constants for consistent layout adjustments.
- Enhanced Sidebar and MainEditor components for better organization and user experience.
- Improved VoicesTab and ProfileList components by integrating new layout features and removing redundant import functionality.
- Streamlined HistoryTable and ModelManagement components for better visual consistency and interaction.
- Added new Tauri plugins: @tauri-apps/plugin-process and @tauri-apps/plugin-updater to improve application capabilities.
- Introduced UpdateStatus component to display update information in the UI.
- Enhanced GenerationForm to include an optional instruct field for additional input.
- Refactored various components for improved styling and responsiveness, including Sidebar, AudioPlayer, and ProfileCard.
- Updated API models and schemas to accommodate new instruct parameter in generation requests and responses.
- Improved documentation for autoupdater setup and usage.