generated from Labyricorn/labyricorn-project-template
rebrand: rename VoiceBox to TalkBox throughout codebase
CI / frontend-quality (push) Canceled after 0s
CI / frontend-quality (push) Canceled after 0s
- All 'voicebox'/'Voicebox'/'VOICEBOX' strings replaced with 'talkbox'/'TalkBox'/'TALKBOX' - Port changed from 17493 to 17494 (avoids conflict with upstream VoiceBox) - MCP tool namespace: voicebox.* -> talkbox.* - App bundle ID: sh.voicebox.app -> com.talkbox.app - Binary names: voicebox-server -> talkbox-server, voicebox-mcp -> talkbox-mcp - Docker user/group: voicebox -> talkbox - Database: voicebox.db -> talkbox.db - Env vars: VOICEBOX_* -> TALKBOX_* - Asset files renamed: voicebox-logo.* -> talkbox-logo.*, etc. - External binaries in tauri.conf.json updated to talkbox-server/talkbox-mcp
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
name: add-tts-engine
|
name: add-tts-engine
|
||||||
description: Use this skill to add a new TTS engine to Voicebox. It walks through dependency research, backend implementation, frontend wiring, PyInstaller bundling, and frozen-build testing. Always start with Phase 0 (dependency audit) before writing any code.
|
description: Use this skill to add a new TTS engine to TalkBox. It walks through dependency research, backend implementation, frontend wiring, PyInstaller bundling, and frozen-build testing. Always start with Phase 0 (dependency audit) before writing any code.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Add TTS Engine
|
# Add TTS Engine
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
Integrate a new text-to-speech engine into Voicebox end-to-end: dependency research, backend protocol implementation, frontend UI wiring, PyInstaller bundling, and frozen-build verification. The user should only need to test the final build locally.
|
Integrate a new text-to-speech engine into TalkBox end-to-end: dependency research, backend protocol implementation, frontend UI wiring, PyInstaller bundling, and frozen-build verification. The user should only need to test the final build locally.
|
||||||
|
|
||||||
## Reference Doc
|
## Reference Doc
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ Tie it to concrete shipped changes. No vague hype.>
|
|||||||
|
|
||||||
### <Feature/Theme Group>
|
### <Feature/Theme Group>
|
||||||
- Bullet points with specifics
|
- Bullet points with specifics
|
||||||
- Reference PRs where available: ([#123](https://github.com/jamiepine/voicebox/pull/123))
|
- Reference PRs where available: ([#123](https://github.com/jamiepine/talkbox/pull/123))
|
||||||
|
|
||||||
### <Another Group>
|
### <Another Group>
|
||||||
- ...
|
- ...
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ Finalize the changelog draft, bump the version across all tracked files, and cre
|
|||||||
- Add a new link for the new version
|
- Add a new link for the new version
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
[Unreleased]: https://github.com/jamiepine/voicebox/compare/vX.Y.Z...HEAD
|
[Unreleased]: https://github.com/jamiepine/talkbox/compare/vX.Y.Z...HEAD
|
||||||
[X.Y.Z]: https://github.com/jamiepine/voicebox/compare/vPREVIOUS...vX.Y.Z
|
[X.Y.Z]: https://github.com/jamiepine/talkbox/compare/vPREVIOUS...vX.Y.Z
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Stage the changelog.**
|
4. **Stage the changelog.**
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ This skill pairs with `draft-release-notes` and `release-bump`: triage first, th
|
|||||||
```bash
|
```bash
|
||||||
git worktree list # check for stale ones first
|
git worktree list # check for stale ones first
|
||||||
git worktree prune
|
git worktree prune
|
||||||
git worktree add ../voicebox-pr-review -b pr-review-<VERSION> main
|
git worktree add ../talkbox-pr-review -b pr-review-<VERSION> main
|
||||||
```
|
```
|
||||||
|
|
||||||
Keep the main worktree for release-prep work (changelog drafts, direct-to-main follow-ups). Keep the review worktree for `gh pr checkout` — each checkout moves HEAD to a contributor branch, which you don't want to do in the main worktree.
|
Keep the main worktree for release-prep work (changelog drafts, direct-to-main follow-ups). Keep the review worktree for `gh pr checkout` — each checkout moves HEAD to a contributor branch, which you don't want to do in the main worktree.
|
||||||
@@ -121,7 +121,7 @@ For each PR in the tier-1 / tier-2 list:
|
|||||||
|
|
||||||
**a. Checkout in the review worktree:**
|
**a. Checkout in the review worktree:**
|
||||||
```bash
|
```bash
|
||||||
cd ../voicebox-pr-review
|
cd ../talkbox-pr-review
|
||||||
git checkout pr-review-<VERSION> # reset to neutral base
|
git checkout pr-review-<VERSION> # reset to neutral base
|
||||||
gh pr checkout <N>
|
gh pr checkout <N>
|
||||||
```
|
```
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"voicebox": {
|
"talkbox": {
|
||||||
"type": "http",
|
"type": "http",
|
||||||
"url": "http://127.0.0.1:17493/mcp",
|
"url": "http://127.0.0.1:17494/mcp",
|
||||||
"headers": {
|
"headers": {
|
||||||
"X-Voicebox-Client-Id": "claude-code"
|
"X-TalkBox-Client-Id": "claude-code"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+149
-149
@@ -18,13 +18,13 @@
|
|||||||
|
|
||||||
## [0.5.0] - 2026-04-22
|
## [0.5.0] - 2026-04-22
|
||||||
|
|
||||||
**The Capture release.** Voicebox stops being just a voice-cloning studio and becomes a full AI voice studio. Hold a key anywhere on your machine, speak, release — the transcript lands in the focused text field. Flip the primitive around and any MCP-aware agent — Claude Code, Cursor, Spacebot — speaks back through an on-screen pill in one of your cloned voices. A local LLM sits between the two, so transcripts come out clean and voice profiles can carry a personality that reshapes what the agent says before it gets spoken.
|
**The Capture release.** TalkBox stops being just a voice-cloning studio and becomes a full AI voice studio. Hold a key anywhere on your machine, speak, release — the transcript lands in the focused text field. Flip the primitive around and any MCP-aware agent — Claude Code, Cursor, Spacebot — speaks back through an on-screen pill in one of your cloned voices. A local LLM sits between the two, so transcripts come out clean and voice profiles can carry a personality that reshapes what the agent says before it gets spoken.
|
||||||
|
|
||||||
### Dictation — speak anywhere, paste anywhere
|
### Dictation — speak anywhere, paste anywhere
|
||||||
|
|
||||||
- **Global hotkey capture.** Hold a customizable chord anywhere on your machine (defaults: right-Cmd + right-Option on macOS, right-Ctrl + right-Shift on Windows), speak, release. A floating on-screen pill walks through recording → transcribing → refining → done with a live elapsed timer. The transcript lands as clean text.
|
- **Global hotkey capture.** Hold a customizable chord anywhere on your machine (defaults: right-Cmd + right-Option on macOS, right-Ctrl + right-Shift on Windows), speak, release. A floating on-screen pill walks through recording → transcribing → refining → done with a live elapsed timer. The transcript lands as clean text.
|
||||||
- **Push-to-talk and toggle modes, each with its own chord.** The default toggle chord adds Space to the push-to-talk chord. Holding PTT and tapping Space mid-hold upgrades a hold into a hands-free session without a gap in the recording.
|
- **Push-to-talk and toggle modes, each with its own chord.** The default toggle chord adds Space to the push-to-talk chord. Holding PTT and tapping Space mid-hold upgrades a hold into a hands-free session without a gap in the recording.
|
||||||
- **Auto-paste into the focused app.** Once transcription finishes, Voicebox synthesizes a paste into whatever text field had focus when you started the chord — not wherever focus drifted while you were talking. Works across Dvorak / AZERTY layouts. Your clipboard is saved before and restored after.
|
- **Auto-paste into the focused app.** Once transcription finishes, TalkBox synthesizes a paste into whatever text field had focus when you started the chord — not wherever focus drifted while you were talking. Works across Dvorak / AZERTY layouts. Your clipboard is saved before and restored after.
|
||||||
- **Chord picker UI.** Customize either chord from Settings → Captures by holding the keys you want. Left/right modifier badges show whether a key is the left or right variant.
|
- **Chord picker UI.** Customize either chord from Settings → Captures by holding the keys you want. Left/right modifier badges show whether a key is the left or right variant.
|
||||||
- **Defaults stay out of your way.** macOS defaults avoid left-hand Cmd+Option chords so the system shortcuts they collide with stay yours. Windows defaults route around AltGr collisions on German / French / Spanish layouts.
|
- **Defaults stay out of your way.** macOS defaults avoid left-hand Cmd+Option chords so the system shortcuts they collide with stay yours. Windows defaults route around AltGr collisions on German / French / Spanish layouts.
|
||||||
- **Accessibility permission is scoped.** If macOS Accessibility isn't granted, dictation still runs and transcripts still land in the Captures tab — only synthetic paste is disabled. The permission prompt lives inline next to the auto-paste toggle, not as a global banner.
|
- **Accessibility permission is scoped.** If macOS Accessibility isn't granted, dictation still runs and transcripts still land in the Captures tab — only synthetic paste is disabled. The permission prompt lives inline next to the auto-paste toggle, not as a global banner.
|
||||||
@@ -38,20 +38,20 @@ Voice profiles now carry an optional **personality** — a free-form description
|
|||||||
|
|
||||||
The same LLM doubles as the refinement model, so there's one local LLM in the app, not two.
|
The same LLM doubles as the refinement model, so there's one local LLM in the app, not two.
|
||||||
|
|
||||||
**API surface.** `POST /generate`, `POST /speak`, and the MCP `voicebox.speak` tool accept `personality: bool`. `POST /profiles/{id}/compose` powers the shuffle button. MCP client bindings carry a `default_personality: bool` that applies when `personality` isn't passed explicitly.
|
**API surface.** `POST /generate`, `POST /speak`, and the MCP `talkbox.speak` tool accept `personality: bool`. `POST /profiles/{id}/compose` powers the shuffle button. MCP client bindings carry a `default_personality: bool` that applies when `personality` isn't passed explicitly.
|
||||||
|
|
||||||
### Agents — any MCP-aware agent gets a voice
|
### Agents — any MCP-aware agent gets a voice
|
||||||
|
|
||||||
Voicebox ships a built-in **Model Context Protocol** server at `http://127.0.0.1:17493/mcp` so Claude Code, Cursor, Windsurf, Cline, VS Code MCP extensions — any MCP-aware agent — can call into your local Voicebox install. Four tools ship with dotted names:
|
TalkBox ships a built-in **Model Context Protocol** server at `http://127.0.0.1:17494/mcp` so Claude Code, Cursor, Windsurf, Cline, VS Code MCP extensions — any MCP-aware agent — can call into your local TalkBox install. Four tools ship with dotted names:
|
||||||
|
|
||||||
- **`voicebox.speak`** — speak text in any voice profile, with optional `personality: true` to run through the profile's personality LLM first
|
- **`talkbox.speak`** — speak text in any voice profile, with optional `personality: true` to run through the profile's personality LLM first
|
||||||
- **`voicebox.transcribe`** — Whisper transcription of a base64 blob or an absolute local path. Path mode is restricted to loopback callers so a Voicebox bound on `0.0.0.0` doesn't double as an unauthenticated arbitrary-local-file read primitive.
|
- **`talkbox.transcribe`** — Whisper transcription of a base64 blob or an absolute local path. Path mode is restricted to loopback callers so a TalkBox bound on `0.0.0.0` doesn't double as an unauthenticated arbitrary-local-file read primitive.
|
||||||
- **`voicebox.list_captures`** — recent captures with their transcripts
|
- **`talkbox.list_captures`** — recent captures with their transcripts
|
||||||
- **`voicebox.list_profiles`** — available voice profiles (cloned + preset)
|
- **`talkbox.list_profiles`** — available voice profiles (cloned + preset)
|
||||||
|
|
||||||
- **Streamable HTTP as primary transport.** Cursor / Windsurf / VS Code / Claude Code all support it out of the box — drop a `mcpServers` block with the URL and an `X-Voicebox-Client-Id` header.
|
- **Streamable HTTP as primary transport.** Cursor / Windsurf / VS Code / Claude Code all support it out of the box — drop a `mcpServers` block with the URL and an `X-TalkBox-Client-Id` header.
|
||||||
- **Stdio shim for clients that don't speak HTTP MCP.** A `voicebox-mcp` binary ships inside the app bundle as a Tauri sidecar. The Settings page renders the install snippet with the right absolute path pre-filled.
|
- **Stdio shim for clients that don't speak HTTP MCP.** A `talkbox-mcp` binary ships inside the app bundle as a Tauri sidecar. The Settings page renders the install snippet with the right absolute path pre-filled.
|
||||||
- **Per-client voice binding.** Pin Claude Code to Morgan, Cursor to Scarlett, Cline to its own voice — the `X-Voicebox-Client-Id` header resolves to a bound voice whenever `speak` is called without an explicit `profile`. Managed in **Settings → MCP**.
|
- **Per-client voice binding.** Pin Claude Code to Morgan, Cursor to Scarlett, Cline to its own voice — the `X-TalkBox-Client-Id` header resolves to a bound voice whenever `speak` is called without an explicit `profile`. Managed in **Settings → MCP**.
|
||||||
- **Profile resolution precedence.** Explicit `profile` arg (name or id, case-insensitive) → per-client binding → global default from `capture_settings.default_playback_voice_id` → error with a pointer to Settings.
|
- **Profile resolution precedence.** Explicit `profile` arg (name or id, case-insensitive) → per-client binding → global default from `capture_settings.default_playback_voice_id` → error with a pointer to Settings.
|
||||||
- **Speaking pill.** Agent-initiated speech surfaces the same on-screen pill as dictation, in a `speaking` state with the profile name and an elapsed timer. Silent background TTS is a trust hazard — the pill always shows what's coming out of your machine.
|
- **Speaking pill.** Agent-initiated speech surfaces the same on-screen pill as dictation, in a `speaking` state with the profile name and an elapsed timer. Silent background TTS is a trust hazard — the pill always shows what's coming out of your machine.
|
||||||
- **`POST /speak` REST wrapper.** Same code path and voice resolution for shell scripts, ACP, A2A, GitHub Actions, or anything else that isn't MCP-native.
|
- **`POST /speak` REST wrapper.** Same code path and voice resolution for shell scripts, ACP, A2A, GitHub Actions, or anything else that isn't MCP-native.
|
||||||
@@ -59,7 +59,7 @@ Voicebox ships a built-in **Model Context Protocol** server at `http://127.0.0.1
|
|||||||
**Claude Code one-liner:**
|
**Claude Code one-liner:**
|
||||||
|
|
||||||
```
|
```
|
||||||
claude mcp add voicebox --transport http --url http://127.0.0.1:17493/mcp --header "X-Voicebox-Client-Id: claude-code"
|
claude mcp add talkbox --transport http --url http://127.0.0.1:17494/mcp --header "X-TalkBox-Client-Id: claude-code"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Refinement
|
### Refinement
|
||||||
@@ -103,11 +103,11 @@ Same dictation flow on Windows. Right-hand default chord (Ctrl+Shift) avoids Alt
|
|||||||
|
|
||||||
## [0.4.5] - 2026-04-22
|
## [0.4.5] - 2026-04-22
|
||||||
|
|
||||||
Second hotfix for the "offline mode is enabled" crash on model load. 0.4.4 reverted the inference-path offline guards but kept the same trap on the load path, so users who updated to 0.4.4 kept hitting the exact error the release was supposed to fix ([#526](https://github.com/jamiepine/voicebox/issues/526)). This release removes the load-path guards and patches the transformers tokenizer load to be robust to HuggingFace metadata failures at the source, so the class of bug can't recur.
|
Second hotfix for the "offline mode is enabled" crash on model load. 0.4.4 reverted the inference-path offline guards but kept the same trap on the load path, so users who updated to 0.4.4 kept hitting the exact error the release was supposed to fix ([#526](https://github.com/jamiepine/talkbox/issues/526)). This release removes the load-path guards and patches the transformers tokenizer load to be robust to HuggingFace metadata failures at the source, so the class of bug can't recur.
|
||||||
|
|
||||||
### Reliability
|
### Reliability
|
||||||
|
|
||||||
- **Load no longer fails with "offline mode is enabled"** ([#530](https://github.com/jamiepine/voicebox/pull/530), fixes [#526](https://github.com/jamiepine/voicebox/issues/526)). transformers 4.57.x added an unconditional `huggingface_hub.model_info()` call inside `AutoTokenizer.from_pretrained` (via `_patch_mistral_regex`) that runs for every non-local repo load, regardless of cache state or whether the target model is actually a Mistral variant. The load-time `HF_HUB_OFFLINE` guard from 0.4.2 turned that into a hard crash for cached online users the moment 0.4.4 removed the inference-path guard that had been masking the problem. Fix wraps `_patch_mistral_regex` so any exception from the HF metadata check is caught and the tokenizer is returned unchanged — matching the success-path behavior for non-Mistral repos. The wrapper installs at `backend.backends` import time so it covers Qwen Base, Qwen CustomVoice, TADA, and every other transformers-backed engine on Windows, Linux, and CUDA alike. The load-time `force_offline_if_cached` guards were removed — with the wrapper in place they provide zero value and only risk re-introducing the same failure mode.
|
- **Load no longer fails with "offline mode is enabled"** ([#530](https://github.com/jamiepine/talkbox/pull/530), fixes [#526](https://github.com/jamiepine/talkbox/issues/526)). transformers 4.57.x added an unconditional `huggingface_hub.model_info()` call inside `AutoTokenizer.from_pretrained` (via `_patch_mistral_regex`) that runs for every non-local repo load, regardless of cache state or whether the target model is actually a Mistral variant. The load-time `HF_HUB_OFFLINE` guard from 0.4.2 turned that into a hard crash for cached online users the moment 0.4.4 removed the inference-path guard that had been masking the problem. Fix wraps `_patch_mistral_regex` so any exception from the HF metadata check is caught and the tokenizer is returned unchanged — matching the success-path behavior for non-Mistral repos. The wrapper installs at `backend.backends` import time so it covers Qwen Base, Qwen CustomVoice, TADA, and every other transformers-backed engine on Windows, Linux, and CUDA alike. The load-time `force_offline_if_cached` guards were removed — with the wrapper in place they provide zero value and only risk re-introducing the same failure mode.
|
||||||
- **No more 30s pause when generating without a network.** The HuggingFace metadata timeout called out as a known caveat in 0.4.4 is covered by the same patch; offline users no longer wait for the check to time out before load completes.
|
- **No more 30s pause when generating without a network.** The HuggingFace metadata timeout called out as a known caveat in 0.4.4 is covered by the same patch; offline users no longer wait for the check to time out before load completes.
|
||||||
|
|
||||||
## [0.4.4] - 2026-04-21
|
## [0.4.4] - 2026-04-21
|
||||||
@@ -116,27 +116,27 @@ Hotfix for a regression in 0.4.3 where generation and transcription could fail o
|
|||||||
|
|
||||||
### Reliability
|
### Reliability
|
||||||
|
|
||||||
- **Inference no longer fails with "offline mode is enabled" while online** ([#524](https://github.com/jamiepine/voicebox/pull/524), reverts the inference-path guards from [#503](https://github.com/jamiepine/voicebox/pull/503)). 0.4.3 wrapped every inference body (`generate`, `transcribe`, `create_voice_clone_prompt`) with a process-wide `HF_HUB_OFFLINE` flip to stop lazy HuggingFace lookups from hanging when the network drops mid-inference ([#462](https://github.com/jamiepine/voicebox/issues/462)). That flag also blocks legitimate metadata calls (e.g. `HfApi().model_info` for revision resolution) so online users started seeing generation fail outright. Inference now runs with the process's default HF state. Load-time offline guards — which weren't the source of the regression — stay in place.
|
- **Inference no longer fails with "offline mode is enabled" while online** ([#524](https://github.com/jamiepine/talkbox/pull/524), reverts the inference-path guards from [#503](https://github.com/jamiepine/talkbox/pull/503)). 0.4.3 wrapped every inference body (`generate`, `transcribe`, `create_voice_clone_prompt`) with a process-wide `HF_HUB_OFFLINE` flip to stop lazy HuggingFace lookups from hanging when the network drops mid-inference ([#462](https://github.com/jamiepine/talkbox/issues/462)). That flag also blocks legitimate metadata calls (e.g. `HfApi().model_info` for revision resolution) so online users started seeing generation fail outright. Inference now runs with the process's default HF state. Load-time offline guards — which weren't the source of the regression — stay in place.
|
||||||
|
|
||||||
**Known caveat**: users generating without an internet connection may see brief pauses during inference while HuggingFace metadata lookups time out (typically ~30s, after which the library recovers). A proper offline-mode toggle is planned for 0.4.5.
|
**Known caveat**: users generating without an internet connection may see brief pauses during inference while HuggingFace metadata lookups time out (typically ~30s, after which the library recovers). A proper offline-mode toggle is planned for 0.4.5.
|
||||||
|
|
||||||
## [0.4.3] - 2026-04-20
|
## [0.4.3] - 2026-04-20
|
||||||
|
|
||||||
A patch focused on two user-impacting reliability fixes: macOS DMG notarization (unblocks `brew install voicebox` on macOS 15 Sequoia and fixes spurious "app isn't signed" Gatekeeper dialogs on older Intel Macs) and Kokoro Japanese voice initialization on fresh installs.
|
A patch focused on two user-impacting reliability fixes: macOS DMG notarization (unblocks `brew install talkbox` on macOS 15 Sequoia and fixes spurious "app isn't signed" Gatekeeper dialogs on older Intel Macs) and Kokoro Japanese voice initialization on fresh installs.
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
- **DMGs are now notarized and stapled** ([#523](https://github.com/jamiepine/voicebox/pull/523)). Tauri's bundler notarizes the `.app` inside the DMG but ships the DMG wrapper itself unnotarized. Gatekeeper rejects that on macOS 15 Sequoia (confirmed by Homebrew Cask CI failing on both arm and intel Sequoia runners) and causes the "the app is not signed" dialog on older Intel Macs when Apple's notarization servers are slow or unreachable ([#509](https://github.com/jamiepine/voicebox/issues/509)). The release workflow now submits each DMG to `notarytool`, staples the ticket, verifies with `spctl`, and overwrites the draft-release asset `tauri-action` uploaded. Adds ~5-10 min per macOS job.
|
- **DMGs are now notarized and stapled** ([#523](https://github.com/jamiepine/talkbox/pull/523)). Tauri's bundler notarizes the `.app` inside the DMG but ships the DMG wrapper itself unnotarized. Gatekeeper rejects that on macOS 15 Sequoia (confirmed by Homebrew Cask CI failing on both arm and intel Sequoia runners) and causes the "the app is not signed" dialog on older Intel Macs when Apple's notarization servers are slow or unreachable ([#509](https://github.com/jamiepine/talkbox/issues/509)). The release workflow now submits each DMG to `notarytool`, staples the ticket, verifies with `spctl`, and overwrites the draft-release asset `tauri-action` uploaded. Adds ~5-10 min per macOS job.
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
|
|
||||||
- **Kokoro Japanese voices no longer crash on fresh installs** ([#521](https://github.com/jamiepine/voicebox/pull/521), fixes [#514](https://github.com/jamiepine/voicebox/issues/514)). `misaki[ja]` pulls in `fugashi`, which needs a MeCab dictionary on disk. The `unidic` package that was being installed ships no data and expects a ~526MB runtime download that `just setup` doesn't run (and which wouldn't survive PyInstaller anyway). Swapped to `unidic-lite`, which bundles a MeCab-compatible dict inside the wheel (~50MB). Collected in `build_binary.py` so frozen builds pick up `unidic_lite/dicdir/`.
|
- **Kokoro Japanese voices no longer crash on fresh installs** ([#521](https://github.com/jamiepine/talkbox/pull/521), fixes [#514](https://github.com/jamiepine/talkbox/issues/514)). `misaki[ja]` pulls in `fugashi`, which needs a MeCab dictionary on disk. The `unidic` package that was being installed ships no data and expects a ~526MB runtime download that `just setup` doesn't run (and which wouldn't survive PyInstaller anyway). Swapped to `unidic-lite`, which bundles a MeCab-compatible dict inside the wheel (~50MB). Collected in `build_binary.py` so frozen builds pick up `unidic_lite/dicdir/`.
|
||||||
|
|
||||||
## [0.4.2] - 2026-04-20
|
## [0.4.2] - 2026-04-20
|
||||||
|
|
||||||
This release localizes the entire app. English, Simplified Chinese (zh-CN), Traditional Chinese (zh-TW), and Japanese (ja) are wired up end-to-end across every tab, modal, dialog, and toast — 559 translation keys per locale, parity verified. Plus a batch of reliability fixes: offline-mode now actually stays offline, Chatterbox accepts reference samples it used to reject, MLX Qwen 0.6B points at the right repo, and macOS system audio survives backgrounding.
|
This release localizes the entire app. English, Simplified Chinese (zh-CN), Traditional Chinese (zh-TW), and Japanese (ja) are wired up end-to-end across every tab, modal, dialog, and toast — 559 translation keys per locale, parity verified. Plus a batch of reliability fixes: offline-mode now actually stays offline, Chatterbox accepts reference samples it used to reject, MLX Qwen 0.6B points at the right repo, and macOS system audio survives backgrounding.
|
||||||
|
|
||||||
### Internationalization ([#508](https://github.com/jamiepine/voicebox/pull/508))
|
### Internationalization ([#508](https://github.com/jamiepine/talkbox/pull/508))
|
||||||
- **i18next foundation** with an in-app language switcher that re-renders the tree on change — lazy-loaded components were holding stale strings without an explicit key-bump on the React root.
|
- **i18next foundation** with an in-app language switcher that re-renders the tree on change — lazy-loaded components were holding stale strings without an explicit key-bump on the React root.
|
||||||
- **Four locales** at full coverage: English, Simplified Chinese, Traditional Chinese, Japanese. No partial/English-fallback surfaces.
|
- **Four locales** at full coverage: English, Simplified Chinese, Traditional Chinese, Japanese. No partial/English-fallback surfaces.
|
||||||
- **Every user-visible surface translated**: Stories (list, content editor, dialogs, toasts), Effects (list, detail, chain editor, built-in preset names), Voices (table, search, inspector, Create/Edit modal, audio sample panels), Audio Channels (list, dialogs, device picker), history + story dropdown menus, ProfileCard / ProfileList / HistoryTable, and the unsupported-model note.
|
- **Every user-visible surface translated**: Stories (list, content editor, dialogs, toasts), Effects (list, detail, chain editor, built-in preset names), Voices (table, search, inspector, Create/Edit modal, audio sample panels), Audio Channels (list, dialogs, device picker), history + story dropdown menus, ProfileCard / ProfileList / HistoryTable, and the unsupported-model note.
|
||||||
@@ -145,23 +145,23 @@ This release localizes the entire app. English, Simplified Chinese (zh-CN), Trad
|
|||||||
- **559 translation keys** across all four locales.
|
- **559 translation keys** across all four locales.
|
||||||
|
|
||||||
### Reliability
|
### Reliability
|
||||||
- **`HF_HUB_OFFLINE` now guards every inference path** ([#503](https://github.com/jamiepine/voicebox/pull/503)) — some engines were still attempting a HuggingFace metadata roundtrip on first load when offline mode was enabled, causing hangs on airgapped or flaky networks.
|
- **`HF_HUB_OFFLINE` now guards every inference path** ([#503](https://github.com/jamiepine/talkbox/pull/503)) — some engines were still attempting a HuggingFace metadata roundtrip on first load when offline mode was enabled, causing hangs on airgapped or flaky networks.
|
||||||
- **Chatterbox reference samples are preprocessed instead of rejected** ([#502](https://github.com/jamiepine/voicebox/pull/502)) — samples outside the expected sample rate or channel layout are resampled to match, rather than failing with an opaque error.
|
- **Chatterbox reference samples are preprocessed instead of rejected** ([#502](https://github.com/jamiepine/talkbox/pull/502)) — samples outside the expected sample rate or channel layout are resampled to match, rather than failing with an opaque error.
|
||||||
- **MLX Qwen 0.6B repo path fixed** ([#501](https://github.com/jamiepine/voicebox/pull/501)) — now points at the published `mlx-community` repo so the model actually downloads on Apple Silicon.
|
- **MLX Qwen 0.6B repo path fixed** ([#501](https://github.com/jamiepine/talkbox/pull/501)) — now points at the published `mlx-community` repo so the model actually downloads on Apple Silicon.
|
||||||
- **macOS system audio survives backgrounding** ([#486](https://github.com/jamiepine/voicebox/pull/486), closes [#41](https://github.com/jamiepine/voicebox/issues/41)) — WKWebView was tearing down the audio session when the app lost focus, silently killing system-audio capture.
|
- **macOS system audio survives backgrounding** ([#486](https://github.com/jamiepine/talkbox/pull/486), closes [#41](https://github.com/jamiepine/talkbox/issues/41)) — WKWebView was tearing down the audio session when the app lost focus, silently killing system-audio capture.
|
||||||
- **MLX backend `miniaudio` dependency pinned** ([#506](https://github.com/jamiepine/voicebox/pull/506)) — `mlx_audio.stt` needs it at runtime and nothing else transitively pulled it in, so `--no-deps` installs were breaking on first use.
|
- **MLX backend `miniaudio` dependency pinned** ([#506](https://github.com/jamiepine/talkbox/pull/506)) — `mlx_audio.stt` needs it at runtime and nothing else transitively pulled it in, so `--no-deps` installs were breaking on first use.
|
||||||
|
|
||||||
### Landing / Docs
|
### Landing / Docs
|
||||||
- **New `/download` page** ([#487](https://github.com/jamiepine/voicebox/pull/487)) — no more dumping first-time visitors onto the GitHub releases list. The API example snippet on the landing page also got an accuracy pass.
|
- **New `/download` page** ([#487](https://github.com/jamiepine/talkbox/pull/487)) — no more dumping first-time visitors onto the GitHub releases list. The API example snippet on the landing page also got an accuracy pass.
|
||||||
- **Download redirects work behind reverse proxies** ([#498](https://github.com/jamiepine/voicebox/pull/498)) — uses the public origin instead of `localhost` when resolving platform-specific installer URLs.
|
- **Download redirects work behind reverse proxies** ([#498](https://github.com/jamiepine/talkbox/pull/498)) — uses the public origin instead of `localhost` when resolving platform-specific installer URLs.
|
||||||
- **MDX docs audited against the multi-engine backend** ([#484](https://github.com/jamiepine/voicebox/pull/484)) — stale single-engine assumptions removed.
|
- **MDX docs audited against the multi-engine backend** ([#484](https://github.com/jamiepine/talkbox/pull/484)) — stale single-engine assumptions removed.
|
||||||
- **Three more tutorials + mobile navbar / hero CTA fixes** ([#483](https://github.com/jamiepine/voicebox/pull/483)).
|
- **Three more tutorials + mobile navbar / hero CTA fixes** ([#483](https://github.com/jamiepine/talkbox/pull/483)).
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
- **Still not shipping.** The re-enable attempt ([#488](https://github.com/jamiepine/voicebox/pull/488)) landed on `main` but CI still hangs in the `tauri-action` bundler step on `ubuntu-22.04` — no output for 25+ minutes after `rpm` bundling, even with `createUpdaterArtifacts: false` and `--bundles deb,rpm`. The matrix entry is disabled again for 0.4.2; the ubuntu-specific setup steps stay in the workflow so re-enabling is a one-line change once we identify the hang. Next release will take another pass.
|
- **Still not shipping.** The re-enable attempt ([#488](https://github.com/jamiepine/talkbox/pull/488)) landed on `main` but CI still hangs in the `tauri-action` bundler step on `ubuntu-22.04` — no output for 25+ minutes after `rpm` bundling, even with `createUpdaterArtifacts: false` and `--bundles deb,rpm`. The matrix entry is disabled again for 0.4.2; the ubuntu-specific setup steps stay in the workflow so re-enabling is a one-line change once we identify the hang. Next release will take another pass.
|
||||||
|
|
||||||
### New Contributors
|
### New Contributors
|
||||||
- [@shekharyv](https://github.com/shekharyv) — download redirects behind reverse proxies ([#498](https://github.com/jamiepine/voicebox/pull/498))
|
- [@shekharyv](https://github.com/shekharyv) — download redirects behind reverse proxies ([#498](https://github.com/jamiepine/talkbox/pull/498))
|
||||||
|
|
||||||
## [0.4.1] - 2026-04-18
|
## [0.4.1] - 2026-04-18
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ A fast follow-up to 0.4.0 focused on making the new engines actually load in the
|
|||||||
|
|
||||||
0.4.0 introduced three new TTS engines, but the frozen PyInstaller binary tripped over several Python-ecosystem quirks that don't show up in the dev venv: `transformers` opening `.py` sources at runtime, `scipy.stats._distn_infrastructure` hitting a frozen-importer `NameError`, and `chatterbox-multilingual` failing to find its Chinese segmenter dictionary. This release patches all of those in one sweep.
|
0.4.0 introduced three new TTS engines, but the frozen PyInstaller binary tripped over several Python-ecosystem quirks that don't show up in the dev venv: `transformers` opening `.py` sources at runtime, `scipy.stats._distn_infrastructure` hitting a frozen-importer `NameError`, and `chatterbox-multilingual` failing to find its Chinese segmenter dictionary. This release patches all of those in one sweep.
|
||||||
|
|
||||||
### Frozen-Binary Reliability ([#438](https://github.com/jamiepine/voicebox/pull/438))
|
### Frozen-Binary Reliability ([#438](https://github.com/jamiepine/talkbox/pull/438))
|
||||||
- **Kokoro** now bundles `.py` sources alongside `.pyc` via `--collect-all kokoro` so `transformers`' `_can_set_attn_implementation` regex scan can read them — previously `FileNotFoundError: kokoro/modules.py` killed Kokoro loading in production builds
|
- **Kokoro** now bundles `.py` sources alongside `.pyc` via `--collect-all kokoro` so `transformers`' `_can_set_attn_implementation` regex scan can read them — previously `FileNotFoundError: kokoro/modules.py` killed Kokoro loading in production builds
|
||||||
- **Chatterbox Multilingual** now bundles `spacy_pkuseg/dicts/default.pkl` and the package's native `.so` extensions via `--collect-all spacy_pkuseg` — previously the Chinese word segmenter crashed with `FileNotFoundError` on first load
|
- **Chatterbox Multilingual** now bundles `spacy_pkuseg/dicts/default.pkl` and the package's native `.so` extensions via `--collect-all spacy_pkuseg` — previously the Chinese word segmenter crashed with `FileNotFoundError` on first load
|
||||||
- **scipy.stats._distn_infrastructure** — new runtime hook source-patches the trailing `del obj` (which raises `NameError` under PyInstaller's frozen importer because the preceding list comprehension evaluates empty) to `globals().pop('obj', None)`, unblocking `librosa` → `scipy.signal` → `scipy.stats` for every TTS engine that depends on librosa
|
- **scipy.stats._distn_infrastructure** — new runtime hook source-patches the trailing `del obj` (which raises `NameError` under PyInstaller's frozen importer because the preceding list comprehension evaluates empty) to `globals().pop('obj', None)`, unblocking `librosa` → `scipy.signal` → `scipy.stats` for every TTS engine that depends on librosa
|
||||||
@@ -178,23 +178,23 @@ A fast follow-up to 0.4.0 focused on making the new engines actually load in the
|
|||||||
- `.spec` paths are now repo-relative instead of absolute, so the generated spec is portable across machines and CI
|
- `.spec` paths are now repo-relative instead of absolute, so the generated spec is portable across machines and CI
|
||||||
|
|
||||||
### Generation
|
### Generation
|
||||||
- **Cancel queued or running generations** ([#444](https://github.com/jamiepine/voicebox/pull/444)) — new `/generate/{id}/cancel` endpoint and a Stop button on the history row while generating. The serial queue now tracks per-ID state (queued / running / cancelled) so queued jobs are skipped before the worker picks them up and running jobs are `.cancel()`-ed mid-flight; `run_generation` catches `CancelledError` and marks the row `failed` with a "cancelled" error.
|
- **Cancel queued or running generations** ([#444](https://github.com/jamiepine/talkbox/pull/444)) — new `/generate/{id}/cancel` endpoint and a Stop button on the history row while generating. The serial queue now tracks per-ID state (queued / running / cancelled) so queued jobs are skipped before the worker picks them up and running jobs are `.cancel()`-ed mid-flight; `run_generation` catches `CancelledError` and marks the row `failed` with a "cancelled" error.
|
||||||
- **Legacy `data/` path prefix resolution** ([#440](https://github.com/jamiepine/voicebox/pull/440)) — generations stored with the old `data/` prefix under pre-0.4 installs now resolve correctly after the storage root moved, fixing 404s for historical audio.
|
- **Legacy `data/` path prefix resolution** ([#440](https://github.com/jamiepine/talkbox/pull/440)) — generations stored with the old `data/` prefix under pre-0.4 installs now resolve correctly after the storage root moved, fixing 404s for historical audio.
|
||||||
|
|
||||||
### Model Migration
|
### Model Migration
|
||||||
- Migration dialog no longer hangs when the cache is empty ([#439](https://github.com/jamiepine/voicebox/pull/439)) — the backend now emits a completion SSE event even when zero models are moved.
|
- Migration dialog no longer hangs when the cache is empty ([#439](https://github.com/jamiepine/talkbox/pull/439)) — the backend now emits a completion SSE event even when zero models are moved.
|
||||||
- Storage-change flow surfaces a toast when there's nothing to migrate ([#433](https://github.com/jamiepine/voicebox/pull/433)) instead of proceeding with a no-op move and restarting the server.
|
- Storage-change flow surfaces a toast when there's nothing to migrate ([#433](https://github.com/jamiepine/talkbox/pull/433)) instead of proceeding with a no-op move and restarting the server.
|
||||||
- Deleting all generations from a voice profile now deletes the associated version files and DB rows too ([#447](https://github.com/jamiepine/voicebox/pull/447)) — previously orphaned versions accumulated in storage.
|
- Deleting all generations from a voice profile now deletes the associated version files and DB rows too ([#447](https://github.com/jamiepine/talkbox/pull/447)) — previously orphaned versions accumulated in storage.
|
||||||
|
|
||||||
### Platform
|
### Platform
|
||||||
- **Linux system audio capture** ([#457](https://github.com/jamiepine/voicebox/pull/457)) — `cpal`'s ALSA backend doesn't expose PulseAudio/PipeWire monitor sources by name, so the previous device-name search never matched and silently fell back to the microphone. Detection now uses `pactl get-default-sink` + `pactl list short sources` and routes via `PULSE_SOURCE`, with the name-based search retained as a fallback when `pactl` is absent.
|
- **Linux system audio capture** ([#457](https://github.com/jamiepine/talkbox/pull/457)) — `cpal`'s ALSA backend doesn't expose PulseAudio/PipeWire monitor sources by name, so the previous device-name search never matched and silently fell back to the microphone. Detection now uses `pactl get-default-sink` + `pactl list short sources` and routes via `PULSE_SOURCE`, with the name-based search retained as a fallback when `pactl` is absent.
|
||||||
|
|
||||||
### Frontend CI
|
### Frontend CI
|
||||||
- First PR-time quality gate ([#418](https://github.com/jamiepine/voicebox/pull/418)) — new `.github/workflows/ci.yml` runs `bun run typecheck` + `bun run build:web` on every PR. Fixed pre-existing type issues that were being suppressed with `@ts-expect-error`, cleaned up a dep-array typo (`[platform.metadata.isTauricheckOnMountcheckForUpdates]`) in `useAutoUpdater`, and removed 100+ lines of dead `ModelItem` code from `ModelManagement.tsx`.
|
- First PR-time quality gate ([#418](https://github.com/jamiepine/talkbox/pull/418)) — new `.github/workflows/ci.yml` runs `bun run typecheck` + `bun run build:web` on every PR. Fixed pre-existing type issues that were being suppressed with `@ts-expect-error`, cleaned up a dep-array typo (`[platform.metadata.isTauricheckOnMountcheckForUpdates]`) in `useAutoUpdater`, and removed 100+ lines of dead `ModelItem` code from `ModelManagement.tsx`.
|
||||||
- Follow-up: widened `apiClient.migrateModels()` return type to include `moved` and `errors` so the storage-change handler typechecks against the real backend response ([#470](https://github.com/jamiepine/voicebox/pull/470)).
|
- Follow-up: widened `apiClient.migrateModels()` return type to include `moved` and `errors` so the storage-change handler typechecks against the real backend response ([#470](https://github.com/jamiepine/talkbox/pull/470)).
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
- Clarified in the Quick Start + README that paralinguistic tags (`[laugh]`, `[sigh]`) only work with Chatterbox Turbo; other engines read them as literal text ([#450](https://github.com/jamiepine/voicebox/pull/450)).
|
- Clarified in the Quick Start + README that paralinguistic tags (`[laugh]`, `[sigh]`) only work with Chatterbox Turbo; other engines read them as literal text ([#450](https://github.com/jamiepine/talkbox/pull/450)).
|
||||||
|
|
||||||
### New Contributors
|
### New Contributors
|
||||||
- [@Bortlesboat](https://github.com/Bortlesboat) — generation cancellation (#444)
|
- [@Bortlesboat](https://github.com/Bortlesboat) — generation cancellation (#444)
|
||||||
@@ -205,13 +205,13 @@ A fast follow-up to 0.4.0 focused on making the new engines actually load in the
|
|||||||
|
|
||||||
## [0.4.0] - 2026-04-16
|
## [0.4.0] - 2026-04-16
|
||||||
|
|
||||||
The biggest Voicebox release yet. Three new TTS engines bring the lineup to **seven** — HumeAI TADA, Kokoro 82M, and Qwen CustomVoice join Qwen3-TTS, LuxTTS, Chatterbox Multilingual, and Chatterbox Turbo. GPU support broadens to Intel Arc (XPU) and NVIDIA Blackwell (RTX 50-series), with runtime diagnostics that warn when your PyTorch build doesn't match your GPU. The CUDA backend is now split into independently versioned server and library archives, so upgrading no longer redownloads 4 GB of PyTorch/CUDA DLLs.
|
The biggest TalkBox release yet. Three new TTS engines bring the lineup to **seven** — HumeAI TADA, Kokoro 82M, and Qwen CustomVoice join Qwen3-TTS, LuxTTS, Chatterbox Multilingual, and Chatterbox Turbo. GPU support broadens to Intel Arc (XPU) and NVIDIA Blackwell (RTX 50-series), with runtime diagnostics that warn when your PyTorch build doesn't match your GPU. The CUDA backend is now split into independently versioned server and library archives, so upgrading no longer redownloads 4 GB of PyTorch/CUDA DLLs.
|
||||||
|
|
||||||
This release also marks a big community moment: **13 new contributors** shipped fixes and features in 0.4.0. Thirty-plus bug fixes target the most-reported issues in the tracker — numpy 2.x TTS crashes, Windows background-server reliability, macOS 11 launch failures, audio playback silence, Stories clip-splitting races, history status staleness, and more.
|
This release also marks a big community moment: **13 new contributors** shipped fixes and features in 0.4.0. Thirty-plus bug fixes target the most-reported issues in the tracker — numpy 2.x TTS crashes, Windows background-server reliability, macOS 11 launch failures, audio playback silence, Stories clip-splitting races, history status staleness, and more.
|
||||||
|
|
||||||
### New TTS Engines
|
### New TTS Engines
|
||||||
|
|
||||||
#### HumeAI TADA — Expressive English & Multilingual ([#296](https://github.com/jamiepine/voicebox/pull/296))
|
#### HumeAI TADA — Expressive English & Multilingual ([#296](https://github.com/jamiepine/talkbox/pull/296))
|
||||||
- Added `tada-1b` (English) and `tada-3b-ml` (multilingual) backends
|
- Added `tada-1b` (English) and `tada-3b-ml` (multilingual) backends
|
||||||
- Replaced `descript-audio-codec` with a lightweight DAC shim to cut dependencies
|
- Replaced `descript-audio-codec` with a lightweight DAC shim to cut dependencies
|
||||||
- Switched audio decoding to `soundfile` to sidestep `torchcodec` bundling issues
|
- Switched audio decoding to `soundfile` to sidestep `torchcodec` bundling issues
|
||||||
@@ -219,19 +219,19 @@ This release also marks a big community moment: **13 new contributors** shipped
|
|||||||
- Fixed tokenizer patch that was corrupting `AutoTokenizer` for other engines
|
- Fixed tokenizer patch that was corrupting `AutoTokenizer` for other engines
|
||||||
- Fixed TorchScript error in frozen builds
|
- Fixed TorchScript error in frozen builds
|
||||||
|
|
||||||
#### Kokoro 82M — Fast Lightweight TTS ([#325](https://github.com/jamiepine/voicebox/pull/325))
|
#### Kokoro 82M — Fast Lightweight TTS ([#325](https://github.com/jamiepine/talkbox/pull/325))
|
||||||
- Added Kokoro 82M engine with a new voice profile type system that distinguishes preset voices from cloned profiles
|
- Added Kokoro 82M engine with a new voice profile type system that distinguishes preset voices from cloned profiles
|
||||||
- Profile grid now handles engine compatibility directly — removed redundant dropdown filtering
|
- Profile grid now handles engine compatibility directly — removed redundant dropdown filtering
|
||||||
- Tightened Kokoro profile handling so preset voices can't be edited like cloned profiles
|
- Tightened Kokoro profile handling so preset voices can't be edited like cloned profiles
|
||||||
|
|
||||||
#### Qwen CustomVoice ([#328](https://github.com/jamiepine/voicebox/pull/328))
|
#### Qwen CustomVoice ([#328](https://github.com/jamiepine/talkbox/pull/328))
|
||||||
- Added `qwen-custom-voice` preset engine backed by Qwen3-TTS
|
- Added `qwen-custom-voice` preset engine backed by Qwen3-TTS
|
||||||
- Enforced preset/profile engine compatibility across the generation flow
|
- Enforced preset/profile engine compatibility across the generation flow
|
||||||
- Floating generator now shows all engines instead of silently filtering
|
- Floating generator now shows all engines instead of silently filtering
|
||||||
|
|
||||||
### Voice Profile UX
|
### Voice Profile UX
|
||||||
|
|
||||||
Until 0.4, every engine in Voicebox was a cloning model, so every voice profile was usable with every engine and the profile grid just showed them all. Introducing Kokoro and Qwen CustomVoice — which work from preset voices rather than cloned samples — broke that assumption for the first time. An early cut on `main` filtered the grid by the selected engine, which left users running pre-release builds thinking their cloned voices had vanished whenever they switched to a preset-only engine.
|
Until 0.4, every engine in TalkBox was a cloning model, so every voice profile was usable with every engine and the profile grid just showed them all. Introducing Kokoro and Qwen CustomVoice — which work from preset voices rather than cloned samples — broke that assumption for the first time. An early cut on `main` filtered the grid by the selected engine, which left users running pre-release builds thinking their cloned voices had vanished whenever they switched to a preset-only engine.
|
||||||
|
|
||||||
This release ships the resolution before it ever reaches a tagged version:
|
This release ships the resolution before it ever reaches a tagged version:
|
||||||
|
|
||||||
@@ -245,72 +245,72 @@ This release ships the resolution before it ever reaches a tagged version:
|
|||||||
|
|
||||||
### GPU & Platform
|
### GPU & Platform
|
||||||
|
|
||||||
#### Intel Arc (XPU) Support ([#320](https://github.com/jamiepine/voicebox/pull/320))
|
#### Intel Arc (XPU) Support ([#320](https://github.com/jamiepine/talkbox/pull/320))
|
||||||
- First-class Intel Arc support across all PyTorch-based backends
|
- First-class Intel Arc support across all PyTorch-based backends
|
||||||
- Device-aware seeding, XPU detection in the GPU status panel, and setup flow detection
|
- Device-aware seeding, XPU detection in the GPU status panel, and setup flow detection
|
||||||
- Reports correct device name and VRAM in settings
|
- Reports correct device name and VRAM in settings
|
||||||
|
|
||||||
#### Blackwell / RTX 50-series Support ([#316](https://github.com/jamiepine/voicebox/pull/316), [#401](https://github.com/jamiepine/voicebox/pull/401))
|
#### Blackwell / RTX 50-series Support ([#316](https://github.com/jamiepine/talkbox/pull/316), [#401](https://github.com/jamiepine/talkbox/pull/401))
|
||||||
- Upgraded the CUDA backend from cu126 → cu128 for RTX 50-series support
|
- Upgraded the CUDA backend from cu126 → cu128 for RTX 50-series support
|
||||||
- Added `sm_120+PTX` to the CUDA build via `TORCH_CUDA_ARCH_LIST` for forward-compatibility with Blackwell architectures (closes 5 open reports: #386, #395, #396, #399, #400)
|
- Added `sm_120+PTX` to the CUDA build via `TORCH_CUDA_ARCH_LIST` for forward-compatibility with Blackwell architectures (closes 5 open reports: #386, #395, #396, #399, #400)
|
||||||
- GPU settings UI fixes around install/uninstall state
|
- GPU settings UI fixes around install/uninstall state
|
||||||
|
|
||||||
#### GPU Compatibility Diagnostics ([#367](https://github.com/jamiepine/voicebox/pull/367), adapted)
|
#### GPU Compatibility Diagnostics ([#367](https://github.com/jamiepine/talkbox/pull/367), adapted)
|
||||||
- New `check_cuda_compatibility()` compares the current device's compute capability against the bundled PyTorch's architecture list
|
- New `check_cuda_compatibility()` compares the current device's compute capability against the bundled PyTorch's architecture list
|
||||||
- Health endpoint exposes a `gpu_compatibility_warning` field so the UI can surface mismatches
|
- Health endpoint exposes a `gpu_compatibility_warning` field so the UI can surface mismatches
|
||||||
- Startup logs a `WARN` when the installed PyTorch build doesn't support the detected GPU
|
- Startup logs a `WARN` when the installed PyTorch build doesn't support the detected GPU
|
||||||
- GPU status label shows `[UNSUPPORTED - see logs]` — no more silent "no kernel image" failures
|
- GPU status label shows `[UNSUPPORTED - see logs]` — no more silent "no kernel image" failures
|
||||||
|
|
||||||
#### Split CUDA Backend ([#298](https://github.com/jamiepine/voicebox/pull/298))
|
#### Split CUDA Backend ([#298](https://github.com/jamiepine/talkbox/pull/298))
|
||||||
- CUDA backend now ships as two independently versioned archives: a small server binary and a large libs archive (the ~4 GB of PyTorch/CUDA DLLs)
|
- CUDA backend now ships as two independently versioned archives: a small server binary and a large libs archive (the ~4 GB of PyTorch/CUDA DLLs)
|
||||||
- Upgrading Voicebox no longer redownloads the libs archive when only the server binary changed
|
- Upgrading TalkBox no longer redownloads the libs archive when only the server binary changed
|
||||||
- Added `asyncio.Lock` around `download_cuda_binary()` so auto-update and manual download can't race on the same temp file ([#428](https://github.com/jamiepine/voicebox/pull/428))
|
- Added `asyncio.Lock` around `download_cuda_binary()` so auto-update and manual download can't race on the same temp file ([#428](https://github.com/jamiepine/talkbox/pull/428))
|
||||||
- Updated `package_cuda.py` for PyInstaller 6.18 onedir layout
|
- Updated `package_cuda.py` for PyInstaller 6.18 onedir layout
|
||||||
- Temp archives are always cleaned up on failure, even when the install aborts mid-extract
|
- Temp archives are always cleaned up on failure, even when the install aborts mid-extract
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
#### Critical: TTS Generation
|
#### Critical: TTS Generation
|
||||||
- **numpy 2.x `torch.from_numpy` crash** ([#361](https://github.com/jamiepine/voicebox/pull/361)) — torch compiled against numpy 1.x ABI fails silently when paired with numpy 2.x, causing `RuntimeError: Numpy is not available` / `Unable to create tensor` on every TTS request in bundled macOS Intel / Rosetta builds. Pinned `numpy<2.0` in requirements and added a PyInstaller runtime hook with a `ctypes.memmove` fallback as belt-and-suspenders. Hardened afterward to raise on unknown dtypes instead of silently reinterpreting bytes as float32.
|
- **numpy 2.x `torch.from_numpy` crash** ([#361](https://github.com/jamiepine/talkbox/pull/361)) — torch compiled against numpy 1.x ABI fails silently when paired with numpy 2.x, causing `RuntimeError: Numpy is not available` / `Unable to create tensor` on every TTS request in bundled macOS Intel / Rosetta builds. Pinned `numpy<2.0` in requirements and added a PyInstaller runtime hook with a `ctypes.memmove` fallback as belt-and-suspenders. Hardened afterward to raise on unknown dtypes instead of silently reinterpreting bytes as float32.
|
||||||
|
|
||||||
#### Platform Reliability
|
#### Platform Reliability
|
||||||
- **Windows background server** ([#402](https://github.com/jamiepine/voicebox/pull/402)) — "keep server running after close" now actually keeps the server running. The HTTP `/watchdog/disable` request could lose the race against process exit on Windows; added a `.keep-running` sentinel file as a synchronous fallback, with stale-sentinel cleanup on startup to avoid orphan server processes
|
- **Windows background server** ([#402](https://github.com/jamiepine/talkbox/pull/402)) — "keep server running after close" now actually keeps the server running. The HTTP `/watchdog/disable` request could lose the race against process exit on Windows; added a `.keep-running` sentinel file as a synchronous fallback, with stale-sentinel cleanup on startup to avoid orphan server processes
|
||||||
- **macOS 11 launch crash** ([#424](https://github.com/jamiepine/voicebox/pull/424)) — weak-linked ScreenCaptureKit so the app can launch on macOS < 12.3 instead of crashing at dyld resolution. Gated system audio capture behind a real `sw_vers` version check so unsupported systems cleanly advertise "not available" rather than crashing at runtime
|
- **macOS 11 launch crash** ([#424](https://github.com/jamiepine/talkbox/pull/424)) — weak-linked ScreenCaptureKit so the app can launch on macOS < 12.3 instead of crashing at dyld resolution. Gated system audio capture behind a real `sw_vers` version check so unsupported systems cleanly advertise "not available" rather than crashing at runtime
|
||||||
- **macOS Intel (x86_64) setup** ([#416](https://github.com/jamiepine/voicebox/pull/416)) — relaxed `torch>=2.7.0` → `torch>=2.2.0`. PyTorch dropped pre-built x86_64 wheels after 2.2.2, so Intel Mac devs could no longer `pip install`. Now resolves to the latest compatible torch per platform
|
- **macOS Intel (x86_64) setup** ([#416](https://github.com/jamiepine/talkbox/pull/416)) — relaxed `torch>=2.7.0` → `torch>=2.2.0`. PyTorch dropped pre-built x86_64 wheels after 2.2.2, so Intel Mac devs could no longer `pip install`. Now resolves to the latest compatible torch per platform
|
||||||
- **Offline model loading** ([#318](https://github.com/jamiepine/voicebox/pull/318)) — Qwen TTS and Whisper force offline mode when loading cached models, so startup works without network access
|
- **Offline model loading** ([#318](https://github.com/jamiepine/talkbox/pull/318)) — Qwen TTS and Whisper force offline mode when loading cached models, so startup works without network access
|
||||||
- **GUI startup with external server** ([#319](https://github.com/jamiepine/voicebox/pull/319)) — fixed GUI launch when pointed at a remote/external server, and added data refresh on server switch; hardened health validation and error handling
|
- **GUI startup with external server** ([#319](https://github.com/jamiepine/talkbox/pull/319)) — fixed GUI launch when pointed at a remote/external server, and added data refresh on server switch; hardened health validation and error handling
|
||||||
- **Qwen3-TTS cache split on Windows** (adapted from [#218](https://github.com/jamiepine/voicebox/pull/218)) — route `Qwen3TTSModel.from_pretrained` through `hf_constants.HF_HUB_CACHE` so the speech tokenizer and `preprocessor_config.json` resolve from a single cache root
|
- **Qwen3-TTS cache split on Windows** (adapted from [#218](https://github.com/jamiepine/talkbox/pull/218)) — route `Qwen3TTSModel.from_pretrained` through `hf_constants.HF_HUB_CACHE` so the speech tokenizer and `preprocessor_config.json` resolve from a single cache root
|
||||||
- **Qwen3-TTS bundling** ([#305](https://github.com/jamiepine/voicebox/pull/305)) — bundle `qwen_tts` source files in the PyInstaller build to fix `inspect.getsource` errors in frozen builds
|
- **Qwen3-TTS bundling** ([#305](https://github.com/jamiepine/talkbox/pull/305)) — bundle `qwen_tts` source files in the PyInstaller build to fix `inspect.getsource` errors in frozen builds
|
||||||
- **Backend import paths** ([#345](https://github.com/jamiepine/voicebox/pull/345)) — moved lazy imports to top-level with absolute paths to resolve the "Failed to Save" preset error caused by `ModuleNotFoundError` in production builds
|
- **Backend import paths** ([#345](https://github.com/jamiepine/talkbox/pull/345)) — moved lazy imports to top-level with absolute paths to resolve the "Failed to Save" preset error caused by `ModuleNotFoundError` in production builds
|
||||||
- **Effects service import** ([#384](https://github.com/jamiepine/voicebox/pull/384)) — fixed `ModuleNotFoundError` on preset create/update by switching to relative imports (#349)
|
- **Effects service import** ([#384](https://github.com/jamiepine/talkbox/pull/384)) — fixed `ModuleNotFoundError` on preset create/update by switching to relative imports (#349)
|
||||||
|
|
||||||
#### Audio & Playback
|
#### Audio & Playback
|
||||||
- **cpal stream silent playback** ([#405](https://github.com/jamiepine/voicebox/pull/405)) — `cpal::Stream` was dropped on function return immediately after `play()`, causing every playback to fall silent. Now holds the stream until either the buffer drains or the stop flag fires (#404)
|
- **cpal stream silent playback** ([#405](https://github.com/jamiepine/talkbox/pull/405)) — `cpal::Stream` was dropped on function return immediately after `play()`, causing every playback to fall silent. Now holds the stream until either the buffer drains or the stop flag fires (#404)
|
||||||
|
|
||||||
#### Stories & History
|
#### Stories & History
|
||||||
- **Clip-splitting race** ([#403](https://github.com/jamiepine/voicebox/pull/403)) — rapid double-clicks on split could race through `split_story_item` with inconsistent state. Added `with_for_update()` row locking on the backend and an `isPending` guard on the frontend (#366)
|
- **Clip-splitting race** ([#403](https://github.com/jamiepine/talkbox/pull/403)) — rapid double-clicks on split could race through `split_story_item` with inconsistent state. Added `with_for_update()` row locking on the backend and an `isPending` guard on the frontend (#366)
|
||||||
- **History `status` staleness** ([#394](https://github.com/jamiepine/voicebox/pull/394)) — `GET /history/{id}` was hardcoding `status="completed"` regardless of the DB row, breaking any client polling for job completion. Now returns `status`, `error`, `engine`, `model_size`, and `is_favorited` from the actual row
|
- **History `status` staleness** ([#394](https://github.com/jamiepine/talkbox/pull/394)) — `GET /history/{id}` was hardcoding `status="completed"` regardless of the DB row, breaking any client polling for job completion. Now returns `status`, `error`, `engine`, `model_size`, and `is_favorited` from the actual row
|
||||||
- **"Clear failed" bulk button** ([#412](https://github.com/jamiepine/voicebox/pull/412)) — new `DELETE /history/failed` endpoint and a header strip showing `"N failed generations"` with a Clear button, complementing the per-row trash icon added in #321 (#410)
|
- **"Clear failed" bulk button** ([#412](https://github.com/jamiepine/talkbox/pull/412)) — new `DELETE /history/failed` endpoint and a header strip showing `"N failed generations"` with a Clear button, complementing the per-row trash icon added in #321 (#410)
|
||||||
- **Delete failed generations** ([#321](https://github.com/jamiepine/voicebox/pull/321)) — added a trash icon next to the retry button so failed entries can be cleaned up without having to retry first
|
- **Delete failed generations** ([#321](https://github.com/jamiepine/talkbox/pull/321)) — added a trash icon next to the retry button so failed entries can be cleaned up without having to retry first
|
||||||
|
|
||||||
#### Security & Safety
|
#### Security & Safety
|
||||||
- **Voice prompt cache hardening** ([#429](https://github.com/jamiepine/voicebox/pull/429)) — `torch.load(weights_only=True)` on cached voice prompts per PyTorch 2.6 recommendation; replaced string-based SPA path guard with `Path.is_relative_to()` for more robust path-traversal protection
|
- **Voice prompt cache hardening** ([#429](https://github.com/jamiepine/talkbox/pull/429)) — `torch.load(weights_only=True)` on cached voice prompts per PyTorch 2.6 recommendation; replaced string-based SPA path guard with `Path.is_relative_to()` for more robust path-traversal protection
|
||||||
|
|
||||||
#### Infrastructure & Docker
|
#### Infrastructure & Docker
|
||||||
- **Docker web build** ([#344](https://github.com/jamiepine/voicebox/pull/344)) — include `CHANGELOG.md` in the Docker web build so the in-app changelog page works in Docker deployments
|
- **Docker web build** ([#344](https://github.com/jamiepine/talkbox/pull/344)) — include `CHANGELOG.md` in the Docker web build so the in-app changelog page works in Docker deployments
|
||||||
- **Docker numba cache** ([#425](https://github.com/jamiepine/voicebox/pull/425)) — set `NUMBA_CACHE_DIR` in docker-compose so numba can write its JIT cache in container runtime (#308)
|
- **Docker numba cache** ([#425](https://github.com/jamiepine/talkbox/pull/425)) — set `NUMBA_CACHE_DIR` in docker-compose so numba can write its JIT cache in container runtime (#308)
|
||||||
- **Relative media paths** ([#332](https://github.com/jamiepine/voicebox/pull/332)) — media paths now stored relative to the configured data dir rather than resolved against CWD, so the data directory is portable between installs
|
- **Relative media paths** ([#332](https://github.com/jamiepine/talkbox/pull/332)) — media paths now stored relative to the configured data dir rather than resolved against CWD, so the data directory is portable between installs
|
||||||
|
|
||||||
### Developer Tooling
|
### Developer Tooling
|
||||||
|
|
||||||
- New `triage-prs` agent skill — encodes the end-to-end PR-speedrun workflow (classification → triage doc → rebase → squash-merge → follow-ups) so future release cycles can reproduce it
|
- New `triage-prs` agent skill — encodes the end-to-end PR-speedrun workflow (classification → triage doc → rebase → squash-merge → follow-ups) so future release cycles can reproduce it
|
||||||
- Rewrote the TTS engine guide with the patterns learned from adding TADA and Kokoro
|
- Rewrote the TTS engine guide with the patterns learned from adding TADA and Kokoro
|
||||||
- Added the API refactor plan and CUDA libs addon design doc
|
- Added the API refactor plan and CUDA libs addon design doc
|
||||||
- Fixed broken links in the Get Started section ([#332](https://github.com/jamiepine/voicebox/pull/332))
|
- Fixed broken links in the Get Started section ([#332](https://github.com/jamiepine/talkbox/pull/332))
|
||||||
|
|
||||||
### New Contributors
|
### New Contributors
|
||||||
|
|
||||||
Huge thank you to everyone who contributed their first PR to Voicebox in this release:
|
Huge thank you to everyone who contributed their first PR to TalkBox in this release:
|
||||||
|
|
||||||
[@liorshahverdi](https://github.com/liorshahverdi), [@nicoschtein](https://github.com/nicoschtein), [@ArfianID](https://github.com/ArfianID), [@aimaaaimaa](https://github.com/aimaaaimaa), [@maxmcoding](https://github.com/maxmcoding), [@Khalodddd](https://github.com/Khalodddd), [@LuisSambrano](https://github.com/LuisSambrano), [@shaun0927](https://github.com/shaun0927), [@malletfils](https://github.com/malletfils), [@mvanhorn](https://github.com/mvanhorn), [@kuishou68](https://github.com/kuishou68), [@txhno](https://github.com/txhno), [@MukundaKatta](https://github.com/MukundaKatta)
|
[@liorshahverdi](https://github.com/liorshahverdi), [@nicoschtein](https://github.com/nicoschtein), [@ArfianID](https://github.com/ArfianID), [@aimaaaimaa](https://github.com/aimaaaimaa), [@maxmcoding](https://github.com/maxmcoding), [@Khalodddd](https://github.com/Khalodddd), [@LuisSambrano](https://github.com/LuisSambrano), [@shaun0927](https://github.com/shaun0927), [@malletfils](https://github.com/malletfils), [@mvanhorn](https://github.com/mvanhorn), [@kuishou68](https://github.com/kuishou68), [@txhno](https://github.com/txhno), [@MukundaKatta](https://github.com/MukundaKatta)
|
||||||
|
|
||||||
@@ -320,21 +320,21 @@ This release rewrites the backend into a modular architecture, overhauls the set
|
|||||||
|
|
||||||
The backend's 3,000-line monolith `main.py` has been decomposed into domain routers, a services layer, and a proper database package. A style guide and ruff configuration now enforce consistency. On the frontend, settings have been split into dedicated routed pages with server logs, a changelog viewer, and an about page. The audio player no longer freezes mid-playback, and model loading status is now visible in the UI. Seven user-reported bugs have been fixed, including server crashes during sample uploads, generation list staleness, cryptic error messages, and CUDA support for RTX 50-series GPUs.
|
The backend's 3,000-line monolith `main.py` has been decomposed into domain routers, a services layer, and a proper database package. A style guide and ruff configuration now enforce consistency. On the frontend, settings have been split into dedicated routed pages with server logs, a changelog viewer, and an about page. The audio player no longer freezes mid-playback, and model loading status is now visible in the UI. Seven user-reported bugs have been fixed, including server crashes during sample uploads, generation list staleness, cryptic error messages, and CUDA support for RTX 50-series GPUs.
|
||||||
|
|
||||||
### Settings Overhaul ([#294](https://github.com/jamiepine/voicebox/pull/294))
|
### Settings Overhaul ([#294](https://github.com/jamiepine/talkbox/pull/294))
|
||||||
- Split settings into routed sub-tabs: General, Generation, GPU, Logs, Changelog, About
|
- Split settings into routed sub-tabs: General, Generation, GPU, Logs, Changelog, About
|
||||||
- Added live server log viewer with auto-scroll
|
- Added live server log viewer with auto-scroll
|
||||||
- Added in-app changelog page that parses `CHANGELOG.md` at build time
|
- Added in-app changelog page that parses `CHANGELOG.md` at build time
|
||||||
- Added About page with version info, license, and generation folder quick-open
|
- Added About page with version info, license, and generation folder quick-open
|
||||||
- Extracted reusable `SettingRow` component for consistent setting layouts
|
- Extracted reusable `SettingRow` component for consistent setting layouts
|
||||||
|
|
||||||
### Audio Player Fix ([#293](https://github.com/jamiepine/voicebox/pull/293))
|
### Audio Player Fix ([#293](https://github.com/jamiepine/talkbox/pull/293))
|
||||||
- Fixed audio player freezing during playback
|
- Fixed audio player freezing during playback
|
||||||
- Improved playback UX with better state management and listener cleanup
|
- Improved playback UX with better state management and listener cleanup
|
||||||
- Fixed restart race condition during regeneration
|
- Fixed restart race condition during regeneration
|
||||||
- Added stable keys for audio element re-rendering
|
- Added stable keys for audio element re-rendering
|
||||||
- Improved accessibility across player controls
|
- Improved accessibility across player controls
|
||||||
|
|
||||||
### Backend Refactor ([#285](https://github.com/jamiepine/voicebox/pull/285))
|
### Backend Refactor ([#285](https://github.com/jamiepine/talkbox/pull/285))
|
||||||
- Extracted all routes from `main.py` into 13 domain routers under `backend/routes/` — `main.py` dropped from ~3,100 lines to ~10
|
- Extracted all routes from `main.py` into 13 domain routers under `backend/routes/` — `main.py` dropped from ~3,100 lines to ~10
|
||||||
- Moved CRUD and service modules into `backend/services/`, platform detection into `backend/utils/`
|
- Moved CRUD and service modules into `backend/services/`, platform detection into `backend/utils/`
|
||||||
- Split monolithic `database.py` into a `database/` package with separate `models`, `session`, `migrations`, and `seed` modules
|
- Split monolithic `database.py` into a `database/` package with separate `models`, `session`, `migrations`, and `seed` modules
|
||||||
@@ -347,7 +347,7 @@ The backend's 3,000-line monolith `main.py` has been decomposed into domain rout
|
|||||||
- Handled null duration in `story_items` migration
|
- Handled null duration in `story_items` migration
|
||||||
- Reject model migration when target is a subdirectory of source cache
|
- Reject model migration when target is a subdirectory of source cache
|
||||||
|
|
||||||
### Documentation Rewrite ([#288](https://github.com/jamiepine/voicebox/pull/288))
|
### Documentation Rewrite ([#288](https://github.com/jamiepine/talkbox/pull/288))
|
||||||
- Migrated docs site from Mintlify to Fumadocs (Next.js-based)
|
- Migrated docs site from Mintlify to Fumadocs (Next.js-based)
|
||||||
- Rewrote introduction and root page with content from README
|
- Rewrote introduction and root page with content from README
|
||||||
- Added "Edit on GitHub" links and last-updated timestamps on all pages
|
- Added "Edit on GitHub" links and last-updated timestamps on all pages
|
||||||
@@ -357,25 +357,25 @@ The backend's 3,000-line monolith `main.py` has been decomposed into domain rout
|
|||||||
- Added OG image metadata and `/og` preview page
|
- Added OG image metadata and `/og` preview page
|
||||||
|
|
||||||
### UI & Frontend
|
### UI & Frontend
|
||||||
- Added model loading status indicator and effects preset dropdown ([3187344](https://github.com/jamiepine/voicebox/commit/3187344))
|
- Added model loading status indicator and effects preset dropdown ([3187344](https://github.com/jamiepine/talkbox/commit/3187344))
|
||||||
- Fixed take-label race condition during regeneration
|
- Fixed take-label race condition during regeneration
|
||||||
- Added accessible focus styling to select component
|
- Added accessible focus styling to select component
|
||||||
- Softened select focus indicator opacity
|
- Softened select focus indicator opacity
|
||||||
- Addressed 4 critical and 12 major issues from CodeRabbit review
|
- Addressed 4 critical and 12 major issues from CodeRabbit review
|
||||||
|
|
||||||
### Bug Fixes ([#295](https://github.com/jamiepine/voicebox/pull/295))
|
### Bug Fixes ([#295](https://github.com/jamiepine/talkbox/pull/295))
|
||||||
- Fixed sample uploads crashing the server — audio decoding now runs in a thread pool instead of blocking the async event loop ([#278](https://github.com/jamiepine/voicebox/issues/278))
|
- Fixed sample uploads crashing the server — audio decoding now runs in a thread pool instead of blocking the async event loop ([#278](https://github.com/jamiepine/talkbox/issues/278))
|
||||||
- Fixed generation list not updating when a generation completes — switched to `refetchQueries` for reliable cache busting, added SSE error fallback, and page reset on completion ([#231](https://github.com/jamiepine/voicebox/issues/231))
|
- Fixed generation list not updating when a generation completes — switched to `refetchQueries` for reliable cache busting, added SSE error fallback, and page reset on completion ([#231](https://github.com/jamiepine/talkbox/issues/231))
|
||||||
- Fixed error toasts showing `[object Object]` instead of the actual error message ([#290](https://github.com/jamiepine/voicebox/issues/290))
|
- Fixed error toasts showing `[object Object]` instead of the actual error message ([#290](https://github.com/jamiepine/talkbox/issues/290))
|
||||||
- Added Whisper model selection (`base`, `small`, `medium`, `large`, `turbo`) and expanded language support to the `/transcribe` endpoint ([#233](https://github.com/jamiepine/voicebox/issues/233))
|
- Added Whisper model selection (`base`, `small`, `medium`, `large`, `turbo`) and expanded language support to the `/transcribe` endpoint ([#233](https://github.com/jamiepine/talkbox/issues/233))
|
||||||
- Upgraded CUDA backend build from cu121 to cu126 for RTX 50-series (Blackwell) GPU support ([#289](https://github.com/jamiepine/voicebox/issues/289))
|
- Upgraded CUDA backend build from cu121 to cu126 for RTX 50-series (Blackwell) GPU support ([#289](https://github.com/jamiepine/talkbox/issues/289))
|
||||||
- Handled client disconnects in SSE and streaming endpoints to suppress `[Errno 32] Broken Pipe` errors ([#248](https://github.com/jamiepine/voicebox/issues/248))
|
- Handled client disconnects in SSE and streaming endpoints to suppress `[Errno 32] Broken Pipe` errors ([#248](https://github.com/jamiepine/talkbox/issues/248))
|
||||||
- Fixed Docker build failure from pip hash mismatch on Qwen3-TTS dependencies ([#286](https://github.com/jamiepine/voicebox/issues/286))
|
- Fixed Docker build failure from pip hash mismatch on Qwen3-TTS dependencies ([#286](https://github.com/jamiepine/talkbox/issues/286))
|
||||||
- Added 50 MB upload size limit with chunked reads to prevent unbounded memory allocation on sample uploads
|
- Added 50 MB upload size limit with chunked reads to prevent unbounded memory allocation on sample uploads
|
||||||
- Eliminated redundant double audio decode in sample processing pipeline
|
- Eliminated redundant double audio decode in sample processing pipeline
|
||||||
|
|
||||||
### Platform Fixes
|
### Platform Fixes
|
||||||
- Replaced `netstat` with `TcpStream` + PowerShell for Windows port detection ([#277](https://github.com/jamiepine/voicebox/pull/277))
|
- Replaced `netstat` with `TcpStream` + PowerShell for Windows port detection ([#277](https://github.com/jamiepine/talkbox/pull/277))
|
||||||
- Fixed Docker frontend build and cleaned up Docker docs
|
- Fixed Docker frontend build and cleaned up Docker docs
|
||||||
- Fixed macOS download links to use `.dmg` instead of `.app.tar.gz`
|
- Fixed macOS download links to use `.dmg` instead of `.app.tar.gz`
|
||||||
- Added dynamic download redirect routes to landing site
|
- Added dynamic download redirect routes to landing site
|
||||||
@@ -431,13 +431,13 @@ The v0.2.1/v0.2.2 builds could not download or load models that weren't already
|
|||||||
|
|
||||||
## [0.2.1] - 2026-03-15
|
## [0.2.1] - 2026-03-15
|
||||||
|
|
||||||
Voicebox v0.1.x was a single-engine voice cloning app built around Qwen3-TTS. v0.2.0 is a ground-up rethink: four TTS engines, 23 languages, paralinguistic emotion controls, a post-processing effects pipeline, unlimited generation length, an async generation queue, and support for every major GPU vendor. Plus Docker.
|
TalkBox v0.1.x was a single-engine voice cloning app built around Qwen3-TTS. v0.2.0 is a ground-up rethink: four TTS engines, 23 languages, paralinguistic emotion controls, a post-processing effects pipeline, unlimited generation length, an async generation queue, and support for every major GPU vendor. Plus Docker.
|
||||||
|
|
||||||
### New TTS Engines
|
### New TTS Engines
|
||||||
|
|
||||||
#### Multi-Engine Architecture
|
#### Multi-Engine Architecture
|
||||||
|
|
||||||
Voicebox now runs **four independent TTS engines** behind a thread-safe per-engine backend registry. Switch engines per-generation from a single dropdown — no restart required.
|
TalkBox now runs **four independent TTS engines** behind a thread-safe per-engine backend registry. Switch engines per-generation from a single dropdown — no restart required.
|
||||||
|
|
||||||
| Engine | Languages | Size | Key Strengths |
|
| Engine | Languages | Size | Key Strengths |
|
||||||
| --------------------------- | --------- | ------- | --------------------------------------------- |
|
| --------------------------- | --------- | ------- | --------------------------------------------- |
|
||||||
@@ -447,25 +447,25 @@ Voicebox now runs **four independent TTS engines** behind a thread-safe per-engi
|
|||||||
| **Chatterbox Multilingual** | 23 | ~3.2 GB | Broadest language coverage, zero-shot cloning |
|
| **Chatterbox Multilingual** | 23 | ~3.2 GB | Broadest language coverage, zero-shot cloning |
|
||||||
| **Chatterbox Turbo** | English | ~1.5 GB | 350M params, low latency, paralinguistic tags |
|
| **Chatterbox Turbo** | English | ~1.5 GB | 350M params, low latency, paralinguistic tags |
|
||||||
|
|
||||||
#### Chatterbox Multilingual — 23 Languages ([#257](https://github.com/jamiepine/voicebox/pull/257))
|
#### Chatterbox Multilingual — 23 Languages ([#257](https://github.com/jamiepine/talkbox/pull/257))
|
||||||
|
|
||||||
Zero-shot voice cloning in Arabic, Chinese, Danish, Dutch, English, Finnish, French, German, Greek, Hebrew, Hindi, Italian, Japanese, Korean, Malay, Norwegian, Polish, Portuguese, Russian, Spanish, Swahili, Swedish, and Turkish.
|
Zero-shot voice cloning in Arabic, Chinese, Danish, Dutch, English, Finnish, French, German, Greek, Hebrew, Hindi, Italian, Japanese, Korean, Malay, Norwegian, Polish, Portuguese, Russian, Spanish, Swahili, Swedish, and Turkish.
|
||||||
|
|
||||||
#### LuxTTS — Lightweight English TTS ([#254](https://github.com/jamiepine/voicebox/pull/254))
|
#### LuxTTS — Lightweight English TTS ([#254](https://github.com/jamiepine/talkbox/pull/254))
|
||||||
|
|
||||||
A fast, CPU-friendly English engine. ~300 MB download, 48 kHz output, runs at 150x realtime on CPU.
|
A fast, CPU-friendly English engine. ~300 MB download, 48 kHz output, runs at 150x realtime on CPU.
|
||||||
|
|
||||||
#### Chatterbox Turbo — Expressive English ([#258](https://github.com/jamiepine/voicebox/pull/258))
|
#### Chatterbox Turbo — Expressive English ([#258](https://github.com/jamiepine/talkbox/pull/258))
|
||||||
|
|
||||||
A fast 350M-parameter English model with inline paralinguistic tags.
|
A fast 350M-parameter English model with inline paralinguistic tags.
|
||||||
|
|
||||||
#### Paralinguistic Tags Autocomplete ([#265](https://github.com/jamiepine/voicebox/pull/265))
|
#### Paralinguistic Tags Autocomplete ([#265](https://github.com/jamiepine/talkbox/pull/265))
|
||||||
|
|
||||||
Type `/` in the text input with Chatterbox Turbo selected to open an autocomplete for **9 expressive tags**: `[laugh]` `[chuckle]` `[gasp]` `[cough]` `[sigh]` `[groan]` `[sniff]` `[shush]` `[clear throat]`
|
Type `/` in the text input with Chatterbox Turbo selected to open an autocomplete for **9 expressive tags**: `[laugh]` `[chuckle]` `[gasp]` `[cough]` `[sigh]` `[groan]` `[sniff]` `[shush]` `[clear throat]`
|
||||||
|
|
||||||
### Generation
|
### Generation
|
||||||
|
|
||||||
#### Unlimited Generation Length — Auto-Chunking ([#266](https://github.com/jamiepine/voicebox/pull/266))
|
#### Unlimited Generation Length — Auto-Chunking ([#266](https://github.com/jamiepine/talkbox/pull/266))
|
||||||
|
|
||||||
Long text is now automatically split at sentence boundaries, generated per-chunk, and crossfaded back together. Engine-agnostic.
|
Long text is now automatically split at sentence boundaries, generated per-chunk, and crossfaded back together. Engine-agnostic.
|
||||||
|
|
||||||
@@ -474,7 +474,7 @@ Long text is now automatically split at sentence boundaries, generated per-chunk
|
|||||||
- Max text length raised to 50,000 characters
|
- Max text length raised to 50,000 characters
|
||||||
- Smart splitting respects abbreviations, CJK punctuation, and `[tags]`
|
- Smart splitting respects abbreviations, CJK punctuation, and `[tags]`
|
||||||
|
|
||||||
#### Asynchronous Generation Queue ([#269](https://github.com/jamiepine/voicebox/pull/269))
|
#### Asynchronous Generation Queue ([#269](https://github.com/jamiepine/talkbox/pull/269))
|
||||||
|
|
||||||
Generation is now fully non-blocking. Serial execution queue prevents GPU contention. Real-time SSE status streaming.
|
Generation is now fully non-blocking. Serial execution queue prevents GPU contention. Real-time SSE status streaming.
|
||||||
|
|
||||||
@@ -482,42 +482,42 @@ Generation is now fully non-blocking. Serial execution queue prevents GPU conten
|
|||||||
|
|
||||||
Every generation now supports multiple versions with provenance tracking — original, effects versions, takes, source tracking, version pinning in stories, and favorites.
|
Every generation now supports multiple versions with provenance tracking — original, effects versions, takes, source tracking, version pinning in stories, and favorites.
|
||||||
|
|
||||||
### Post-Processing Effects ([#271](https://github.com/jamiepine/voicebox/pull/271))
|
### Post-Processing Effects ([#271](https://github.com/jamiepine/talkbox/pull/271))
|
||||||
|
|
||||||
A full audio effects system powered by Spotify's `pedalboard` library: Pitch Shift, Reverb, Delay, Chorus/Flanger, Compressor, Gain, High-Pass Filter, Low-Pass Filter. 4 built-in presets, custom presets, per-profile default effects, and live preview.
|
A full audio effects system powered by Spotify's `pedalboard` library: Pitch Shift, Reverb, Delay, Chorus/Flanger, Compressor, Gain, High-Pass Filter, Low-Pass Filter. 4 built-in presets, custom presets, per-profile default effects, and live preview.
|
||||||
|
|
||||||
### Platform Support
|
### Platform Support
|
||||||
|
|
||||||
- **Windows Support** ([#272](https://github.com/jamiepine/voicebox/pull/272)) — Full Windows support with CUDA GPU detection
|
- **Windows Support** ([#272](https://github.com/jamiepine/talkbox/pull/272)) — Full Windows support with CUDA GPU detection
|
||||||
- **Linux** ([#262](https://github.com/jamiepine/voicebox/pull/262)) — AMD ROCm, NVIDIA GBM fix, WebKitGTK mic access (build from source)
|
- **Linux** ([#262](https://github.com/jamiepine/talkbox/pull/262)) — AMD ROCm, NVIDIA GBM fix, WebKitGTK mic access (build from source)
|
||||||
- **NVIDIA CUDA Backend Swap** ([#252](https://github.com/jamiepine/voicebox/pull/252)) — Download and swap in CUDA backend from within the app
|
- **NVIDIA CUDA Backend Swap** ([#252](https://github.com/jamiepine/talkbox/pull/252)) — Download and swap in CUDA backend from within the app
|
||||||
- **Intel Arc (XPU) and DirectML** — PyTorch backend supports Intel Arc and DirectML
|
- **Intel Arc (XPU) and DirectML** — PyTorch backend supports Intel Arc and DirectML
|
||||||
- **Docker + Web Deployment** ([#161](https://github.com/jamiepine/voicebox/pull/161)) — 3-stage build, non-root runtime, health checks
|
- **Docker + Web Deployment** ([#161](https://github.com/jamiepine/talkbox/pull/161)) — 3-stage build, non-root runtime, health checks
|
||||||
- **Whisper Turbo** — Added `openai/whisper-large-v3-turbo` as a transcription model option
|
- **Whisper Turbo** — Added `openai/whisper-large-v3-turbo` as a transcription model option
|
||||||
|
|
||||||
### Model Management ([#268](https://github.com/jamiepine/voicebox/pull/268))
|
### Model Management ([#268](https://github.com/jamiepine/talkbox/pull/268))
|
||||||
|
|
||||||
Per-model unload, custom models directory, model folder migration, download cancel/clear UI ([#238](https://github.com/jamiepine/voicebox/pull/238)), restructured settings UI.
|
Per-model unload, custom models directory, model folder migration, download cancel/clear UI ([#238](https://github.com/jamiepine/talkbox/pull/238)), restructured settings UI.
|
||||||
|
|
||||||
### Security & Reliability
|
### Security & Reliability
|
||||||
|
|
||||||
- CORS hardening ([#88](https://github.com/jamiepine/voicebox/pull/88))
|
- CORS hardening ([#88](https://github.com/jamiepine/talkbox/pull/88))
|
||||||
- Network access toggle ([#133](https://github.com/jamiepine/voicebox/pull/133))
|
- Network access toggle ([#133](https://github.com/jamiepine/talkbox/pull/133))
|
||||||
- Offline crash fix ([#152](https://github.com/jamiepine/voicebox/pull/152))
|
- Offline crash fix ([#152](https://github.com/jamiepine/talkbox/pull/152))
|
||||||
- Atomic audio saves ([#263](https://github.com/jamiepine/voicebox/pull/263))
|
- Atomic audio saves ([#263](https://github.com/jamiepine/talkbox/pull/263))
|
||||||
- Filesystem health endpoint
|
- Filesystem health endpoint
|
||||||
- Chatterbox float64 dtype fix ([#264](https://github.com/jamiepine/voicebox/pull/264))
|
- Chatterbox float64 dtype fix ([#264](https://github.com/jamiepine/talkbox/pull/264))
|
||||||
|
|
||||||
### Accessibility ([#243](https://github.com/jamiepine/voicebox/pull/243))
|
### Accessibility ([#243](https://github.com/jamiepine/talkbox/pull/243))
|
||||||
|
|
||||||
Screen reader support, keyboard navigation, state-aware `aria-label` attributes on all interactive controls.
|
Screen reader support, keyboard navigation, state-aware `aria-label` attributes on all interactive controls.
|
||||||
|
|
||||||
### UI Polish
|
### UI Polish
|
||||||
|
|
||||||
- Redesigned landing page ([#274](https://github.com/jamiepine/voicebox/pull/274))
|
- Redesigned landing page ([#274](https://github.com/jamiepine/talkbox/pull/274))
|
||||||
- Voices tab overhaul with inline inspector
|
- Voices tab overhaul with inline inspector
|
||||||
- Responsive layout improvements
|
- Responsive layout improvements
|
||||||
- Duplicate profile name validation ([#175](https://github.com/jamiepine/voicebox/pull/175))
|
- Duplicate profile name validation ([#175](https://github.com/jamiepine/talkbox/pull/175))
|
||||||
|
|
||||||
### Community Contributors
|
### Community Contributors
|
||||||
|
|
||||||
@@ -527,25 +527,25 @@ Screen reader support, keyboard navigation, state-aware `aria-label` attributes
|
|||||||
|
|
||||||
### Stability and reliability
|
### Stability and reliability
|
||||||
|
|
||||||
- [#95](https://github.com/jamiepine/voicebox/pull/95) Fix: selecting 0.6B model still downloads and uses 1.7B
|
- [#95](https://github.com/jamiepine/talkbox/pull/95) Fix: selecting 0.6B model still downloads and uses 1.7B
|
||||||
- [#93](https://github.com/jamiepine/voicebox/pull/93) fix(mlx): bundle native libs and broaden error handling for Apple Silicon
|
- [#93](https://github.com/jamiepine/talkbox/pull/93) fix(mlx): bundle native libs and broaden error handling for Apple Silicon
|
||||||
- [#79](https://github.com/jamiepine/voicebox/pull/79) fix: handle non-ASCII filenames in Content-Disposition headers
|
- [#79](https://github.com/jamiepine/talkbox/pull/79) fix: handle non-ASCII filenames in Content-Disposition headers
|
||||||
- [#78](https://github.com/jamiepine/voicebox/pull/78) fix: guard getUserMedia call against undefined mediaDevices in non-secure contexts
|
- [#78](https://github.com/jamiepine/talkbox/pull/78) fix: guard getUserMedia call against undefined mediaDevices in non-secure contexts
|
||||||
- [#77](https://github.com/jamiepine/voicebox/pull/77) fix: await for confirmation before deleting voices and channels
|
- [#77](https://github.com/jamiepine/talkbox/pull/77) fix: await for confirmation before deleting voices and channels
|
||||||
- [#128](https://github.com/jamiepine/voicebox/pull/128) fix: resolve multiple issues (#96, #119, #111, #108, #121, #125, #127)
|
- [#128](https://github.com/jamiepine/talkbox/pull/128) fix: resolve multiple issues (#96, #119, #111, #108, #121, #125, #127)
|
||||||
- [#40](https://github.com/jamiepine/voicebox/pull/40) Fix: audio export path resolution
|
- [#40](https://github.com/jamiepine/talkbox/pull/40) Fix: audio export path resolution
|
||||||
|
|
||||||
### Build and packaging
|
### Build and packaging
|
||||||
|
|
||||||
- [#122](https://github.com/jamiepine/voicebox/pull/122) fix(web): add @tailwindcss/vite plugin to web config
|
- [#122](https://github.com/jamiepine/talkbox/pull/122) fix(web): add @tailwindcss/vite plugin to web config
|
||||||
- [#126](https://github.com/jamiepine/voicebox/pull/126) Create requirements.txt
|
- [#126](https://github.com/jamiepine/talkbox/pull/126) Create requirements.txt
|
||||||
|
|
||||||
### UX and docs
|
### UX and docs
|
||||||
|
|
||||||
- [#44](https://github.com/jamiepine/voicebox/pull/44) Enhances floating generate box UX
|
- [#44](https://github.com/jamiepine/talkbox/pull/44) Enhances floating generate box UX
|
||||||
- [#57](https://github.com/jamiepine/voicebox/pull/57) chore: updates repo URL in README
|
- [#57](https://github.com/jamiepine/talkbox/pull/57) chore: updates repo URL in README
|
||||||
- [#146](https://github.com/jamiepine/voicebox/pull/146) Add Spacebot banner to landing page
|
- [#146](https://github.com/jamiepine/talkbox/pull/146) Add Spacebot banner to landing page
|
||||||
- [#1](https://github.com/jamiepine/voicebox/pull/1) Improvements
|
- [#1](https://github.com/jamiepine/talkbox/pull/1) Improvements
|
||||||
|
|
||||||
## [0.1.12] - 2026-01-31
|
## [0.1.12] - 2026-01-31
|
||||||
|
|
||||||
@@ -691,7 +691,7 @@ Fixed recording length limit at 0:29 to auto stop instead of passing the limit a
|
|||||||
|
|
||||||
### Server Management
|
### Server Management
|
||||||
|
|
||||||
- Changed default URL from `localhost:8000` to `127.0.0.1:17493`
|
- Changed default URL from `localhost:8000` to `127.0.0.1:17494`
|
||||||
- Server reuse logic, "keep server running" preference, orphaned process handling
|
- Server reuse logic, "keep server running" preference, orphaned process handling
|
||||||
|
|
||||||
### Build & Release
|
### Build & Release
|
||||||
@@ -707,7 +707,7 @@ Fixed recording length limit at 0:29 to auto stop instead of passing the limit a
|
|||||||
|
|
||||||
## [0.1.0] - 2026-01-27
|
## [0.1.0] - 2026-01-27
|
||||||
|
|
||||||
The first public release of Voicebox — an open-source voice synthesis studio powered by Qwen3-TTS.
|
The first public release of TalkBox — an open-source voice synthesis studio powered by Qwen3-TTS.
|
||||||
|
|
||||||
### Voice Cloning with Qwen3-TTS
|
### Voice Cloning with Qwen3-TTS
|
||||||
|
|
||||||
@@ -751,28 +751,28 @@ The first public release of Voicebox — an open-source voice synthesis studio p
|
|||||||
|
|
||||||
Tauri v2, React, TypeScript, Tailwind CSS, FastAPI, Qwen3-TTS, Whisper, SQLite
|
Tauri v2, React, TypeScript, Tailwind CSS, FastAPI, Qwen3-TTS, Whisper, SQLite
|
||||||
|
|
||||||
[0.5.0]: https://github.com/jamiepine/voicebox/compare/v0.4.5...v0.5.0
|
[0.5.0]: https://github.com/jamiepine/talkbox/compare/v0.4.5...v0.5.0
|
||||||
[0.4.5]: https://github.com/jamiepine/voicebox/compare/v0.4.4...v0.4.5
|
[0.4.5]: https://github.com/jamiepine/talkbox/compare/v0.4.4...v0.4.5
|
||||||
[0.4.4]: https://github.com/jamiepine/voicebox/compare/v0.4.3...v0.4.4
|
[0.4.4]: https://github.com/jamiepine/talkbox/compare/v0.4.3...v0.4.4
|
||||||
[0.4.3]: https://github.com/jamiepine/voicebox/compare/v0.4.2...v0.4.3
|
[0.4.3]: https://github.com/jamiepine/talkbox/compare/v0.4.2...v0.4.3
|
||||||
[0.4.2]: https://github.com/jamiepine/voicebox/compare/v0.4.1...v0.4.2
|
[0.4.2]: https://github.com/jamiepine/talkbox/compare/v0.4.1...v0.4.2
|
||||||
[0.4.1]: https://github.com/jamiepine/voicebox/compare/v0.4.0...v0.4.1
|
[0.4.1]: https://github.com/jamiepine/talkbox/compare/v0.4.0...v0.4.1
|
||||||
[0.4.0]: https://github.com/jamiepine/voicebox/compare/v0.3.0...v0.4.0
|
[0.4.0]: https://github.com/jamiepine/talkbox/compare/v0.3.0...v0.4.0
|
||||||
[0.3.0]: https://github.com/jamiepine/voicebox/compare/v0.2.3...v0.3.0
|
[0.3.0]: https://github.com/jamiepine/talkbox/compare/v0.2.3...v0.3.0
|
||||||
[0.2.3]: https://github.com/jamiepine/voicebox/compare/v0.2.2...v0.2.3
|
[0.2.3]: https://github.com/jamiepine/talkbox/compare/v0.2.2...v0.2.3
|
||||||
[0.2.2]: https://github.com/jamiepine/voicebox/compare/v0.2.1...v0.2.2
|
[0.2.2]: https://github.com/jamiepine/talkbox/compare/v0.2.1...v0.2.2
|
||||||
[0.2.1]: https://github.com/jamiepine/voicebox/compare/v0.1.13...v0.2.1
|
[0.2.1]: https://github.com/jamiepine/talkbox/compare/v0.1.13...v0.2.1
|
||||||
[0.1.13]: https://github.com/jamiepine/voicebox/compare/v0.1.12...v0.1.13
|
[0.1.13]: https://github.com/jamiepine/talkbox/compare/v0.1.12...v0.1.13
|
||||||
[0.1.12]: https://github.com/jamiepine/voicebox/compare/v0.1.11...v0.1.12
|
[0.1.12]: https://github.com/jamiepine/talkbox/compare/v0.1.11...v0.1.12
|
||||||
[0.1.11]: https://github.com/jamiepine/voicebox/compare/v0.1.10...v0.1.11
|
[0.1.11]: https://github.com/jamiepine/talkbox/compare/v0.1.10...v0.1.11
|
||||||
[0.1.10]: https://github.com/jamiepine/voicebox/compare/v0.1.9...v0.1.10
|
[0.1.10]: https://github.com/jamiepine/talkbox/compare/v0.1.9...v0.1.10
|
||||||
[0.1.9]: https://github.com/jamiepine/voicebox/compare/v0.1.8...v0.1.9
|
[0.1.9]: https://github.com/jamiepine/talkbox/compare/v0.1.8...v0.1.9
|
||||||
[0.1.8]: https://github.com/jamiepine/voicebox/compare/v0.1.7...v0.1.8
|
[0.1.8]: https://github.com/jamiepine/talkbox/compare/v0.1.7...v0.1.8
|
||||||
[0.1.7]: https://github.com/jamiepine/voicebox/compare/v0.1.6...v0.1.7
|
[0.1.7]: https://github.com/jamiepine/talkbox/compare/v0.1.6...v0.1.7
|
||||||
[0.1.6]: https://github.com/jamiepine/voicebox/compare/v0.1.5...v0.1.6
|
[0.1.6]: https://github.com/jamiepine/talkbox/compare/v0.1.5...v0.1.6
|
||||||
[0.1.5]: https://github.com/jamiepine/voicebox/compare/v0.1.4...v0.1.5
|
[0.1.5]: https://github.com/jamiepine/talkbox/compare/v0.1.4...v0.1.5
|
||||||
[0.1.4]: https://github.com/jamiepine/voicebox/compare/v0.1.3...v0.1.4
|
[0.1.4]: https://github.com/jamiepine/talkbox/compare/v0.1.3...v0.1.4
|
||||||
[0.1.3]: https://github.com/jamiepine/voicebox/compare/v0.1.2...v0.1.3
|
[0.1.3]: https://github.com/jamiepine/talkbox/compare/v0.1.2...v0.1.3
|
||||||
[0.1.2]: https://github.com/jamiepine/voicebox/compare/v0.1.1...v0.1.2
|
[0.1.2]: https://github.com/jamiepine/talkbox/compare/v0.1.1...v0.1.2
|
||||||
[0.1.1]: https://github.com/jamiepine/voicebox/compare/v0.1.0...v0.1.1
|
[0.1.1]: https://github.com/jamiepine/talkbox/compare/v0.1.0...v0.1.1
|
||||||
[0.1.0]: https://github.com/jamiepine/voicebox/releases/tag/v0.1.0
|
[0.1.0]: https://github.com/jamiepine/talkbox/releases/tag/v0.1.0
|
||||||
|
|||||||
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
# Contributing to Voicebox
|
# Contributing to TalkBox
|
||||||
|
|
||||||
Thank you for your interest in contributing to Voicebox! This document provides guidelines and instructions for contributing.
|
Thank you for your interest in contributing to TalkBox! This document provides guidelines and instructions for contributing.
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
@@ -36,8 +36,8 @@ Thank you for your interest in contributing to Voicebox! This document provides
|
|||||||
Install [just](https://github.com/casey/just) (`brew install just`, `cargo install just`, or `winget install Casey.Just`), then:
|
Install [just](https://github.com/casey/just) (`brew install just`, `cargo install just`, or `winget install Casey.Just`), then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/YOUR_USERNAME/voicebox.git
|
git clone https://github.com/YOUR_USERNAME/talkbox.git
|
||||||
cd voicebox
|
cd talkbox
|
||||||
|
|
||||||
just setup # creates venv, installs Python + JS deps
|
just setup # creates venv, installs Python + JS deps
|
||||||
just dev # starts backend + desktop app
|
just dev # starts backend + desktop app
|
||||||
@@ -91,7 +91,7 @@ On Windows, to build with CUDA support for local testing:
|
|||||||
just build-local # Build CPU + CUDA server binaries + Tauri installer
|
just build-local # Build CPU + CUDA server binaries + Tauri installer
|
||||||
```
|
```
|
||||||
|
|
||||||
This builds the CPU sidecar (bundled with the app), the CUDA binary (placed in `%APPDATA%/sh.voicebox.app/backends/` for runtime GPU switching), and the installable Tauri app.
|
This builds the CPU sidecar (bundled with the app), the CUDA binary (placed in `%APPDATA%/sh.talkbox.app/backends/` for runtime GPU switching), and the installable Tauri app.
|
||||||
|
|
||||||
Creates platform-specific installers (`.dmg`, `.msi`, `.AppImage`) in `tauri/src-tauri/target/release/bundle/`.
|
Creates platform-specific installers (`.dmg`, `.msi`, `.AppImage`) in `tauri/src-tauri/target/release/bundle/`.
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ def create_profile(name, language):
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
voicebox/
|
talkbox/
|
||||||
├── app/ # Shared React frontend
|
├── app/ # Shared React frontend
|
||||||
│ └── src/
|
│ └── src/
|
||||||
│ ├── components/ # UI components
|
│ ├── components/ # UI components
|
||||||
@@ -365,7 +365,7 @@ See [docs/content/docs/overview/troubleshooting.mdx](docs/content/docs/overview/
|
|||||||
|
|
||||||
- **Backend won't start:** Check Python version (3.11+), ensure venv is activated, install dependencies
|
- **Backend won't start:** Check Python version (3.11+), ensure venv is activated, install dependencies
|
||||||
- **Tauri build fails:** Ensure Rust is installed, clean build with `cd tauri/src-tauri && cargo clean`
|
- **Tauri build fails:** Ensure Rust is installed, clean build with `cd tauri/src-tauri && cargo clean`
|
||||||
- **OpenAPI client generation fails:** Ensure backend is running, check `curl http://localhost:17493/openapi.json`
|
- **OpenAPI client generation fails:** Ensure backend is running, check `curl http://localhost:17494/openapi.json`
|
||||||
|
|
||||||
## Questions?
|
## Questions?
|
||||||
|
|
||||||
@@ -389,4 +389,4 @@ By contributing, you agree that your contributions will be licensed under the MI
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Thank you for contributing to Voicebox! 🎉
|
Thank you for contributing to TalkBox! 🎉
|
||||||
|
|||||||
+10
-10
@@ -1,5 +1,5 @@
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
# Voicebox — Local TTS Server with Web UI
|
# TalkBox — Local TTS Server with Web UI
|
||||||
# 3-stage build: Frontend → Python deps → Runtime
|
# 3-stage build: Frontend → Python deps → Runtime
|
||||||
#
|
#
|
||||||
# Build variants:
|
# Build variants:
|
||||||
@@ -71,8 +71,8 @@ RUN pip install --no-cache-dir --prefix=/install \
|
|||||||
FROM python:3.11-slim
|
FROM python:3.11-slim
|
||||||
|
|
||||||
# Create non-root user; the entrypoint joins GPU device groups at runtime.
|
# Create non-root user; the entrypoint joins GPU device groups at runtime.
|
||||||
RUN groupadd -r voicebox && \
|
RUN groupadd -r talkbox && \
|
||||||
useradd -r -g voicebox -m -s /bin/bash voicebox
|
useradd -r -g talkbox -m -s /bin/bash talkbox
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -87,27 +87,27 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
COPY --from=backend-builder /install /usr/local
|
COPY --from=backend-builder /install /usr/local
|
||||||
|
|
||||||
# Copy backend application code
|
# Copy backend application code
|
||||||
COPY --chown=voicebox:voicebox backend/ /app/backend/
|
COPY --chown=talkbox:talkbox backend/ /app/backend/
|
||||||
|
|
||||||
# Copy built frontend from frontend stage
|
# Copy built frontend from frontend stage
|
||||||
COPY --from=frontend --chown=voicebox:voicebox /build/web/dist /app/frontend/
|
COPY --from=frontend --chown=talkbox:talkbox /build/web/dist /app/frontend/
|
||||||
|
|
||||||
# Create data directories owned by non-root user
|
# Create data directories owned by non-root user
|
||||||
RUN mkdir -p /app/data/generations /app/data/profiles /app/data/cache \
|
RUN mkdir -p /app/data/generations /app/data/profiles /app/data/cache \
|
||||||
&& chown -R voicebox:voicebox /app/data
|
&& chown -R talkbox:talkbox /app/data
|
||||||
|
|
||||||
# Expose the API port
|
# Expose the API port
|
||||||
EXPOSE 17493
|
EXPOSE 17494
|
||||||
|
|
||||||
# Health check — auto-restart if the server hangs
|
# Health check — auto-restart if the server hangs
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=60s \
|
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=60s \
|
||||||
CMD curl -f http://localhost:17493/health || exit 1
|
CMD curl -f http://localhost:17494/health || exit 1
|
||||||
|
|
||||||
# Entrypoint joins GPU groups then drops to the voicebox user.
|
# Entrypoint joins GPU groups then drops to the talkbox user.
|
||||||
# Normalize CRLF (a Windows checkout otherwise leaves the shebang as
|
# Normalize CRLF (a Windows checkout otherwise leaves the shebang as
|
||||||
# `#!/bin/sh\r`, which Linux can't resolve — reported as a misleading
|
# `#!/bin/sh\r`, which Linux can't resolve — reported as a misleading
|
||||||
# "no such file or directory" even though the file exists).
|
# "no such file or directory" even though the file exists).
|
||||||
COPY --chmod=755 scripts/rocm-entrypoint.sh /usr/local/bin/entrypoint.sh
|
COPY --chmod=755 scripts/rocm-entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
RUN sed -i 's/\r$//' /usr/local/bin/entrypoint.sh
|
RUN sed -i 's/\r$//' /usr/local/bin/entrypoint.sh
|
||||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "17493"]
|
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "17494"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2026 Voicebox Contributors
|
Copyright (c) 2026 TalkBox Contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src=".github/assets/icon-dark.webp" alt="Voicebox" width="120" height="120" />
|
<img src=".github/assets/icon-dark.webp" alt="TalkBox" width="120" height="120" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1 align="center">Voicebox</h1>
|
<h1 align="center">TalkBox</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>The open-source AI voice studio.</strong><br/>
|
<strong>The open-source AI voice studio.</strong><br/>
|
||||||
@@ -11,30 +11,30 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/jamiepine/voicebox/releases">
|
<a href="https://github.com/jamiepine/talkbox/releases">
|
||||||
<img src="https://img.shields.io/github/downloads/jamiepine/voicebox/total?style=flat&color=blue" alt="Downloads" />
|
<img src="https://img.shields.io/github/downloads/jamiepine/talkbox/total?style=flat&color=blue" alt="Downloads" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/jamiepine/voicebox/releases/latest">
|
<a href="https://github.com/jamiepine/talkbox/releases/latest">
|
||||||
<img src="https://img.shields.io/github/v/release/jamiepine/voicebox?style=flat" alt="Release" />
|
<img src="https://img.shields.io/github/v/release/jamiepine/talkbox?style=flat" alt="Release" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/jamiepine/voicebox/stargazers">
|
<a href="https://github.com/jamiepine/talkbox/stargazers">
|
||||||
<img src="https://img.shields.io/github/stars/jamiepine/voicebox?style=flat" alt="Stars" />
|
<img src="https://img.shields.io/github/stars/jamiepine/talkbox?style=flat" alt="Stars" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/jamiepine/voicebox/blob/main/LICENSE">
|
<a href="https://github.com/jamiepine/talkbox/blob/main/LICENSE">
|
||||||
<img src="https://img.shields.io/github/license/jamiepine/voicebox?style=flat" alt="License" />
|
<img src="https://img.shields.io/github/license/jamiepine/talkbox?style=flat" alt="License" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://deepwiki.com/jamiepine/voicebox">
|
<a href="https://deepwiki.com/jamiepine/talkbox">
|
||||||
<img src="https://img.shields.io/static/v1?label=Ask&message=DeepWiki&color=5B6EF7" alt="Ask DeepWiki" />
|
<img src="https://img.shields.io/static/v1?label=Ask&message=DeepWiki&color=5B6EF7" alt="Ask DeepWiki" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://trendshift.io/repositories/21213" target="_blank"><img src="https://trendshift.io/api/badge/repositories/21213" alt="jamiepine%2Fvoicebox | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/21213" target="_blank"><img src="https://trendshift.io/api/badge/repositories/21213" alt="jamiepine%2Ftalkbox | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://voicebox.sh">voicebox.sh</a> •
|
<a href="https://talkbox.sh">talkbox.sh</a> •
|
||||||
<a href="https://docs.voicebox.sh">Docs</a> •
|
<a href="https://docs.talkbox.sh">Docs</a> •
|
||||||
<a href="#download">Download</a> •
|
<a href="#download">Download</a> •
|
||||||
<a href="#features">Features</a> •
|
<a href="#features">Features</a> •
|
||||||
<a href="#api">API</a> •
|
<a href="#api">API</a> •
|
||||||
@@ -44,32 +44,32 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://voicebox.sh">
|
<a href="https://talkbox.sh">
|
||||||
<img src="landing/public/assets/app-screenshot-1.webp" alt="Voicebox App Screenshot" width="800" />
|
<img src="landing/public/assets/app-screenshot-1.webp" alt="TalkBox App Screenshot" width="800" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<em>Click the image above to watch the demo video on <a href="https://voicebox.sh">voicebox.sh</a></em>
|
<em>Click the image above to watch the demo video on <a href="https://talkbox.sh">talkbox.sh</a></em>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="landing/public/assets/app-screenshot-2.webp" alt="Voicebox Screenshot 2" width="800" />
|
<img src="landing/public/assets/app-screenshot-2.webp" alt="TalkBox Screenshot 2" width="800" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="landing/public/assets/app-screenshot-3.webp" alt="Voicebox Screenshot 3" width="800" />
|
<img src="landing/public/assets/app-screenshot-3.webp" alt="TalkBox Screenshot 3" width="800" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## What is Voicebox?
|
## What is TalkBox?
|
||||||
|
|
||||||
Voicebox is a **local-first AI voice studio** — a free and open-source alternative to **ElevenLabs** and **WisprFlow** in one app. Clone voices from a few seconds of audio, generate speech in 23 languages across 7 TTS engines, dictate into any text field with a global hotkey, and give any MCP-aware AI agent a voice of your choosing.
|
TalkBox is a **local-first AI voice studio** — a free and open-source alternative to **ElevenLabs** and **WisprFlow** in one app. Clone voices from a few seconds of audio, generate speech in 23 languages across 7 TTS engines, dictate into any text field with a global hotkey, and give any MCP-aware AI agent a voice of your choosing.
|
||||||
|
|
||||||
The two cloud incumbents sit on opposite halves of the voice I/O loop — ElevenLabs on output, WisprFlow on input. Voicebox does both, bridges them with a bundled local LLM for refinement and per-profile personas, and runs the whole thing on your machine.
|
The two cloud incumbents sit on opposite halves of the voice I/O loop — ElevenLabs on output, WisprFlow on input. TalkBox does both, bridges them with a bundled local LLM for refinement and per-profile personas, and runs the whole thing on your machine.
|
||||||
|
|
||||||
- **Complete privacy** — models, voice data, and captures never leave your machine
|
- **Complete privacy** — models, voice data, and captures never leave your machine
|
||||||
- **7 TTS engines** — Qwen3-TTS, Qwen CustomVoice, LuxTTS, Chatterbox Multilingual, Chatterbox Turbo, HumeAI TADA, and Kokoro
|
- **7 TTS engines** — Qwen3-TTS, Qwen CustomVoice, LuxTTS, Chatterbox Multilingual, Chatterbox Turbo, HumeAI TADA, and Kokoro
|
||||||
@@ -80,7 +80,7 @@ The two cloud incumbents sit on opposite halves of the voice I/O loop — Eleven
|
|||||||
- **Unlimited length** — auto-chunking with crossfade for scripts, articles, and chapters
|
- **Unlimited length** — auto-chunking with crossfade for scripts, articles, and chapters
|
||||||
- **Stories editor** — multi-track timeline for conversations, podcasts, and narratives
|
- **Stories editor** — multi-track timeline for conversations, podcasts, and narratives
|
||||||
- **Voice input** — global dictation hotkey with push-to-talk and toggle modes, accessibility-verified auto-paste on macOS, in-app mic on every text field, Whisper-based STT
|
- **Voice input** — global dictation hotkey with push-to-talk and toggle modes, accessibility-verified auto-paste on macOS, in-app mic on every text field, Whisper-based STT
|
||||||
- **Agent voice output** — one tool call (`voicebox.speak`) and any MCP-aware agent (Claude Code, Cursor, Cline) speaks to you in a voice you've cloned
|
- **Agent voice output** — one tool call (`talkbox.speak`) and any MCP-aware agent (Claude Code, Cursor, Cline) speaks to you in a voice you've cloned
|
||||||
- **Voice personalities** — attach a free-form persona to any voice profile, then Compose, Rewrite, or Respond via a bundled local LLM — agents can invoke the same modes over MCP
|
- **Voice personalities** — attach a free-form persona to any voice profile, then Compose, Rewrite, or Respond via a bundled local LLM — agents can invoke the same modes over MCP
|
||||||
- **API-first** — REST API plus a built-in MCP server for integrating voice I/O into your own apps and agents
|
- **API-first** — REST API plus a built-in MCP server for integrating voice I/O into your own apps and agents
|
||||||
- **Native performance** — built with Tauri (Rust), not Electron
|
- **Native performance** — built with Tauri (Rust), not Electron
|
||||||
@@ -92,14 +92,14 @@ The two cloud incumbents sit on opposite halves of the voice I/O loop — Eleven
|
|||||||
|
|
||||||
| Platform | Download |
|
| Platform | Download |
|
||||||
| --------------------- | ------------------------------------------------------ |
|
| --------------------- | ------------------------------------------------------ |
|
||||||
| macOS (Apple Silicon) | [Download DMG](https://voicebox.sh/download/mac-arm) |
|
| macOS (Apple Silicon) | [Download DMG](https://talkbox.sh/download/mac-arm) |
|
||||||
| macOS (Intel) | [Download DMG](https://voicebox.sh/download/mac-intel) |
|
| macOS (Intel) | [Download DMG](https://talkbox.sh/download/mac-intel) |
|
||||||
| Windows | [Download MSI](https://voicebox.sh/download/windows) |
|
| Windows | [Download MSI](https://talkbox.sh/download/windows) |
|
||||||
| Docker | `docker compose up` |
|
| Docker | `docker compose up` |
|
||||||
|
|
||||||
> **[View all binaries →](https://github.com/jamiepine/voicebox/releases/latest)**
|
> **[View all binaries →](https://github.com/jamiepine/talkbox/releases/latest)**
|
||||||
|
|
||||||
> **Linux** — Pre-built binaries are not yet available. See [voicebox.sh/linux-install](https://voicebox.sh/linux-install) for build-from-source instructions.
|
> **Linux** — Pre-built binaries are not yet available. See [talkbox.sh/linux-install](https://talkbox.sh/linux-install) for build-from-source instructions.
|
||||||
|
|
||||||
> **Having trouble?** See the [Troubleshooting Guide](docs/content/docs/overview/troubleshooting.mdx) for common install, generation, model-download, and GPU issues.
|
> **Having trouble?** See the [Troubleshooting Guide](docs/content/docs/overview/troubleshooting.mdx) for common install, generation, model-download, and GPU issues.
|
||||||
|
|
||||||
@@ -196,18 +196,18 @@ Multi-voice timeline editor for conversations, podcasts, and narratives.
|
|||||||
|
|
||||||
### Global Dictation & Voice Input
|
### Global Dictation & Voice Input
|
||||||
|
|
||||||
The other half of the voice I/O loop. Hold a hotkey anywhere on your system, speak, release — on macOS the transcript pastes straight into the focused text field. Or hit the mic on any Voicebox text input and dictate directly into the app.
|
The other half of the voice I/O loop. Hold a hotkey anywhere on your system, speak, release — on macOS the transcript pastes straight into the focused text field. Or hit the mic on any TalkBox text input and dictate directly into the app.
|
||||||
|
|
||||||
- **Configurable chord bindings** — hold-to-speak and tap-to-toggle chords, each rebindable in the in-app chord picker. Holding push-to-talk and tapping `Space` mid-hold upgrades into a toggle session without a gap in audio
|
- **Configurable chord bindings** — hold-to-speak and tap-to-toggle chords, each rebindable in the in-app chord picker. Holding push-to-talk and tapping `Space` mid-hold upgrades into a toggle session without a gap in audio
|
||||||
- **Target-aware paste (macOS)** — accessibility-verified injection into the focused text field, with atomic clipboard save/restore so your clipboard isn't clobbered
|
- **Target-aware paste (macOS)** — accessibility-verified injection into the focused text field, with atomic clipboard save/restore so your clipboard isn't clobbered
|
||||||
- **First-run permissions UX** — in-app gates walk you through the macOS Accessibility and Input Monitoring grants with deep-links to System Settings
|
- **First-run permissions UX** — in-app gates walk you through the macOS Accessibility and Input Monitoring grants with deep-links to System Settings
|
||||||
- **In-app mic button** on every Voicebox text field — generation form, profile descriptions, story titles, anywhere you'd type
|
- **In-app mic button** on every TalkBox text field — generation form, profile descriptions, story titles, anywhere you'd type
|
||||||
- **LLM refinement** — optional cleanup of ums, stutters, and false starts before paste
|
- **LLM refinement** — optional cleanup of ums, stutters, and false starts before paste
|
||||||
- **On-screen pill** — floating overlay surfacing `recording`, `transcribing`, `refining`, and `speaking` states. Same pill agents use when they speak to you, so there's one mental model for both directions of the loop
|
- **On-screen pill** — floating overlay surfacing `recording`, `transcribing`, `refining`, and `speaking` states. Same pill agents use when they speak to you, so there's one mental model for both directions of the loop
|
||||||
|
|
||||||
### Speech-to-Text
|
### Speech-to-Text
|
||||||
|
|
||||||
Voicebox runs OpenAI Whisper for transcription — the same model that backs dictation, the Captures tab, and the `/transcribe` API. Running on MLX (Apple Silicon) or PyTorch (CUDA / ROCm / DirectML / CPU) depending on your platform.
|
TalkBox runs OpenAI Whisper for transcription — the same model that backs dictation, the Captures tab, and the `/transcribe` API. Running on MLX (Apple Silicon) or PyTorch (CUDA / ROCm / DirectML / CPU) depending on your platform.
|
||||||
|
|
||||||
| Size | Notes |
|
| Size | Notes |
|
||||||
| ----------------------------- | -------------------------------------------------- |
|
| ----------------------------- | -------------------------------------------------- |
|
||||||
@@ -224,7 +224,7 @@ Every dictation, in-app recording, and uploaded audio file lands in the Captures
|
|||||||
- **Edit inline** — tweak the transcript and save on blur
|
- **Edit inline** — tweak the transcript and save on blur
|
||||||
- **Play as voice profile** — turn any capture into speech with a cloned voice, one click
|
- **Play as voice profile** — turn any capture into speech with a cloned voice, one click
|
||||||
- **Promote to voice sample** — use a capture's audio + transcript as a reference sample on any voice profile
|
- **Promote to voice sample** — use a capture's audio + transcript as a reference sample on any voice profile
|
||||||
- **Local capture storage** — original audio and transcript stay in your Voicebox data directory, with a folder shortcut in Settings
|
- **Local capture storage** — original audio and transcript stay in your TalkBox data directory, with a folder shortcut in Settings
|
||||||
|
|
||||||
### Agent Voice Output
|
### Agent Voice Output
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ Every agent gets a voice. One tool call and any MCP-aware agent can speak to you
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
// In any MCP-aware agent:
|
// In any MCP-aware agent:
|
||||||
await voicebox.speak({
|
await talkbox.speak({
|
||||||
text: "Deploy complete.",
|
text: "Deploy complete.",
|
||||||
profile: "Morgan",
|
profile: "Morgan",
|
||||||
});
|
});
|
||||||
@@ -243,7 +243,7 @@ Also exposed as `POST /speak` for anything that doesn't speak MCP — ACP, A2A,
|
|||||||
- **Bidirectional pill** — `recording`, `transcribing`, `refining`, and `speaking` are all states of the same OS-level overlay, so dictation and agent speech share one surface
|
- **Bidirectional pill** — `recording`, `transcribing`, `refining`, and `speaking` are all states of the same OS-level overlay, so dictation and agent speech share one surface
|
||||||
- **Per-agent voice binding** — in **Settings → MCP**, pin Claude Code to Morgan and Cursor to Scarlett so you can tell which agent is talking without looking. Each client's `last_seen_at` timestamp confirms the install actually took
|
- **Per-agent voice binding** — in **Settings → MCP**, pin Claude Code to Morgan and Cursor to Scarlett so you can tell which agent is talking without looking. Each client's `last_seen_at` timestamp confirms the install actually took
|
||||||
- **Always visible** — no silent background TTS; every agent-initiated speak surfaces the pill with the voice profile name for the full duration
|
- **Always visible** — no silent background TTS; every agent-initiated speak surfaces the pill with the voice profile name for the full duration
|
||||||
- **HTTP + stdio transports** — install as a URL in Claude Code / Cursor / Windsurf / VS Code MCP, or point stdio-only clients at the bundled `voicebox-mcp` binary
|
- **HTTP + stdio transports** — install as a URL in Claude Code / Cursor / Windsurf / VS Code MCP, or point stdio-only clients at the bundled `talkbox-mcp` binary
|
||||||
|
|
||||||
### Voice Personalities
|
### Voice Personalities
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ Attach a free-form personality to any voice profile — who this voice is, how t
|
|||||||
- **Compose** — a shuffle button that drops a fresh in-character line into the textarea; edit and speak, or click again for a different take
|
- **Compose** — a shuffle button that drops a fresh in-character line into the textarea; edit and speak, or click again for a different take
|
||||||
- **Speak in character** — a toggle that routes your input text through the personality LLM to be rewritten in their voice before TTS
|
- **Speak in character** — a toggle that routes your input text through the personality LLM to be rewritten in their voice before TTS
|
||||||
|
|
||||||
Agents can reach the same rewrite path over MCP by passing `personality: true` to `voicebox.speak`, turning the tool into a text-in → personality-LLM → TTS pipeline. The same LLM backs dictation's refinement step — one LLM in the app, one model cache, one GPU-memory footprint.
|
Agents can reach the same rewrite path over MCP by passing `personality: true` to `talkbox.speak`, turning the tool into a text-in → personality-LLM → TTS pipeline. The same LLM backs dictation's refinement step — one LLM in the app, one model cache, one GPU-memory footprint.
|
||||||
|
|
||||||
**Local LLM options:** Qwen3 0.6B / 1.7B / 4B, sharing the TTS runtime (MLX on Apple Silicon, PyTorch elsewhere).
|
**Local LLM options:** Qwen3 0.6B / 1.7B / 4B, sharing the TTS runtime (MLX on Apple Silicon, PyTorch elsewhere).
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ Use cases: agent dev loops (dictate a question, hear the answer in a cloned voic
|
|||||||
### Model Management
|
### Model Management
|
||||||
|
|
||||||
- Per-model unload to free GPU memory without deleting downloads
|
- Per-model unload to free GPU memory without deleting downloads
|
||||||
- Custom models directory via `VOICEBOX_MODELS_DIR`
|
- Custom models directory via `TALKBOX_MODELS_DIR`
|
||||||
- Model folder migration with progress tracking
|
- Model folder migration with progress tracking
|
||||||
- Download cancel/clear UI
|
- Download cancel/clear UI
|
||||||
|
|
||||||
@@ -281,42 +281,42 @@ Use cases: agent dev loops (dictate a question, hear the answer in a cloned voic
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
Voicebox exposes a REST API for integrating voice I/O into your own apps and agents.
|
TalkBox exposes a REST API for integrating voice I/O into your own apps and agents.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate speech
|
# Generate speech
|
||||||
curl -X POST http://127.0.0.1:17493/generate \
|
curl -X POST http://127.0.0.1:17494/generate \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"text": "Hello world", "profile_id": "abc123", "language": "en"}'
|
-d '{"text": "Hello world", "profile_id": "abc123", "language": "en"}'
|
||||||
|
|
||||||
# Agent voice output — any app or script can speak in a cloned voice
|
# Agent voice output — any app or script can speak in a cloned voice
|
||||||
curl -X POST http://127.0.0.1:17493/speak \
|
curl -X POST http://127.0.0.1:17494/speak \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-H "X-Voicebox-Client-Id: my-script" \
|
-H "X-TalkBox-Client-Id: my-script" \
|
||||||
-d '{"text": "Deploy complete.", "profile": "Morgan"}'
|
-d '{"text": "Deploy complete.", "profile": "Morgan"}'
|
||||||
|
|
||||||
# Transcribe an audio file
|
# Transcribe an audio file
|
||||||
curl -X POST http://127.0.0.1:17493/transcribe \
|
curl -X POST http://127.0.0.1:17494/transcribe \
|
||||||
-F "[email protected]" \
|
-F "[email protected]" \
|
||||||
-F "model=whisper-turbo"
|
-F "model=whisper-turbo"
|
||||||
|
|
||||||
# List voice profiles
|
# List voice profiles
|
||||||
curl http://127.0.0.1:17493/profiles
|
curl http://127.0.0.1:17494/profiles
|
||||||
```
|
```
|
||||||
|
|
||||||
`POST /speak` accepts `profile` as a name (case-insensitive) or id, and resolves via the same precedence as the MCP tool: explicit arg → per-client binding → `capture_settings.default_playback_voice_id`.
|
`POST /speak` accepts `profile` as a name (case-insensitive) or id, and resolves via the same precedence as the MCP tool: explicit arg → per-client binding → `capture_settings.default_playback_voice_id`.
|
||||||
|
|
||||||
### MCP server
|
### MCP server
|
||||||
|
|
||||||
Voicebox ships a built-in **Model Context Protocol** server so any MCP-aware agent (Claude Code, Cursor, Windsurf, Cline, VS Code MCP extensions) can speak, transcribe, and browse captures and profiles.
|
TalkBox ships a built-in **Model Context Protocol** server so any MCP-aware agent (Claude Code, Cursor, Windsurf, Cline, VS Code MCP extensions) can speak, transcribe, and browse captures and profiles.
|
||||||
|
|
||||||
**Claude Code one-liner:**
|
**Claude Code one-liner:**
|
||||||
|
|
||||||
```
|
```
|
||||||
claude mcp add voicebox \
|
claude mcp add talkbox \
|
||||||
--transport http \
|
--transport http \
|
||||||
--url http://127.0.0.1:17493/mcp \
|
--url http://127.0.0.1:17494/mcp \
|
||||||
--header "X-Voicebox-Client-Id: claude-code"
|
--header "X-TalkBox-Client-Id: claude-code"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Any HTTP MCP client** (Cursor, Windsurf, VS Code, etc.):
|
**Any HTTP MCP client** (Cursor, Windsurf, VS Code, etc.):
|
||||||
@@ -324,32 +324,32 @@ claude mcp add voicebox \
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"voicebox": {
|
"talkbox": {
|
||||||
"url": "http://127.0.0.1:17493/mcp",
|
"url": "http://127.0.0.1:17494/mcp",
|
||||||
"headers": { "X-Voicebox-Client-Id": "cursor" }
|
"headers": { "X-TalkBox-Client-Id": "cursor" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Stdio fallback** for clients that don't speak HTTP MCP — point at the bundled `voicebox-mcp` binary inside the app:
|
**Stdio fallback** for clients that don't speak HTTP MCP — point at the bundled `talkbox-mcp` binary inside the app:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"voicebox": {
|
"talkbox": {
|
||||||
"command": "/Applications/Voicebox.app/Contents/MacOS/voicebox-mcp",
|
"command": "/Applications/TalkBox.app/Contents/MacOS/talkbox-mcp",
|
||||||
"env": { "VOICEBOX_CLIENT_ID": "claude-desktop" }
|
"env": { "TALKBOX_CLIENT_ID": "claude-desktop" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Four tools ship: `voicebox.speak`, `voicebox.transcribe`, `voicebox.list_captures`, `voicebox.list_profiles`. Per-client voice bindings are managed in **Voicebox → Settings → MCP**. See the [full MCP guide](docs/content/docs/overview/mcp-server.mdx) for tool signatures, resolution precedence, the speaking-pill contract, and security notes.
|
Four tools ship: `talkbox.speak`, `talkbox.transcribe`, `talkbox.list_captures`, `talkbox.list_profiles`. Per-client voice bindings are managed in **TalkBox → Settings → MCP**. See the [full MCP guide](docs/content/docs/overview/mcp-server.mdx) for tool signatures, resolution precedence, the speaking-pill contract, and security notes.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// In any MCP-aware agent:
|
// In any MCP-aware agent:
|
||||||
await voicebox.speak({
|
await talkbox.speak({
|
||||||
text: "Tests passing. Ready to merge.",
|
text: "Tests passing. Ready to merge.",
|
||||||
profile: "Morgan", // optional — falls back to the per-client binding
|
profile: "Morgan", // optional — falls back to the per-client binding
|
||||||
personality: true, // optional — rewrites text through the profile's personality LLM first
|
personality: true, // optional — rewrites text through the profile's personality LLM first
|
||||||
@@ -358,7 +358,7 @@ await voicebox.speak({
|
|||||||
|
|
||||||
**Use cases:** agent dev loops (voice in, voice out), game dialogue, podcast production, accessibility tools, voice assistants, content automation.
|
**Use cases:** agent dev loops (voice in, voice out), game dialogue, podcast production, accessibility tools, voice assistants, content automation.
|
||||||
|
|
||||||
Full API documentation available at `http://127.0.0.1:17493/docs`.
|
Full API documentation available at `http://127.0.0.1:17494/docs`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ Full API documentation available at `http://127.0.0.1:17493/docs`.
|
|||||||
| **Long-form capture** | Dual-stream recorder (mic + system audio) with summary LLM transform |
|
| **Long-form capture** | Dual-stream recorder (mic + system audio) with summary LLM transform |
|
||||||
| **Platform sinks** | Apple Notes, Obsidian, and other opt-in integrations |
|
| **Platform sinks** | Apple Notes, Obsidian, and other opt-in integrations |
|
||||||
| **Plugin architecture** | Extend with custom models, transforms, and sinks |
|
| **Plugin architecture** | Extend with custom models, transforms, and sinks |
|
||||||
| **Mobile companion** | Control Voicebox from your phone |
|
| **Mobile companion** | Control TalkBox from your phone |
|
||||||
|
|
||||||
For the **full engineering status, open-issue triage, and prioritized work queue**, see [`docs/PROJECT_STATUS.md`](docs/PROJECT_STATUS.md) — a living document that tracks what's shipped, what's in-flight, candidate TTS engines under evaluation, and why we've accepted or backlogged specific integrations.
|
For the **full engineering status, open-issue triage, and prioritized work queue**, see [`docs/PROJECT_STATUS.md`](docs/PROJECT_STATUS.md) — a living document that tracks what's shipped, what's in-flight, candidate TTS engines under evaluation, and why we've accepted or backlogged specific integrations.
|
||||||
|
|
||||||
@@ -408,8 +408,8 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed setup and contribution guide
|
|||||||
### Quick Start
|
### Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/jamiepine/voicebox.git
|
git clone https://github.com/jamiepine/talkbox.git
|
||||||
cd voicebox
|
cd talkbox
|
||||||
|
|
||||||
just setup # creates Python venv, installs all deps
|
just setup # creates Python venv, installs all deps
|
||||||
just dev # starts backend + desktop app
|
just dev # starts backend + desktop app
|
||||||
@@ -419,7 +419,7 @@ Install [just](https://github.com/casey/just): `brew install just` or `cargo ins
|
|||||||
|
|
||||||
**Prerequisites:** [Bun](https://bun.sh), [Rust](https://rustup.rs), [Python 3.11+](https://python.org), [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/), and [Xcode](https://developer.apple.com/xcode/) on macOS.
|
**Prerequisites:** [Bun](https://bun.sh), [Rust](https://rustup.rs), [Python 3.11+](https://python.org), [Tauri Prerequisites](https://v2.tauri.app/start/prerequisites/), and [Xcode](https://developer.apple.com/xcode/) on macOS.
|
||||||
|
|
||||||
The repo ships a pre-wired `.mcp.json` at the root — running Claude Code inside this checkout picks up the Voicebox MCP tools automatically once the dev app is running.
|
The repo ships a pre-wired `.mcp.json` at the root — running Claude Code inside this checkout picks up the TalkBox MCP tools automatically once the dev app is running.
|
||||||
|
|
||||||
### Building Locally
|
### Building Locally
|
||||||
|
|
||||||
@@ -437,7 +437,7 @@ The guide is optimized for AI coding agents. An [agent skill](.agents/skills/add
|
|||||||
### Project Structure
|
### Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
voicebox/
|
talkbox/
|
||||||
├── app/ # Shared React frontend
|
├── app/ # Shared React frontend
|
||||||
├── tauri/ # Desktop app (Tauri + Rust)
|
├── tauri/ # Desktop app (Tauri + Rust)
|
||||||
├── web/ # Web deployment
|
├── web/ # Web deployment
|
||||||
@@ -470,5 +470,5 @@ MIT License — see [LICENSE](LICENSE) for details.
|
|||||||
---
|
---
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://voicebox.sh">voicebox.sh</a>
|
<a href="https://talkbox.sh">talkbox.sh</a>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
# Responsible Use
|
# Responsible Use
|
||||||
|
|
||||||
Voicebox is a local-first AI voice studio. It can clone voices from short audio samples, generate speech, and make AI agents speak through voice profiles. That capability is useful for accessibility, creative production, prototyping, game development, and personal tools, but it can also be misused.
|
TalkBox is a local-first AI voice studio. It can clone voices from short audio samples, generate speech, and make AI agents speak through voice profiles. That capability is useful for accessibility, creative production, prototyping, game development, and personal tools, but it can also be misused.
|
||||||
|
|
||||||
Voicebox does not and cannot independently verify who owns a voice sample. You are responsible for making sure you have the right to use every voice you clone, import, or generate with.
|
TalkBox does not and cannot independently verify who owns a voice sample. You are responsible for making sure you have the right to use every voice you clone, import, or generate with.
|
||||||
|
|
||||||
## Allowed Uses
|
## Allowed Uses
|
||||||
|
|
||||||
@@ -22,6 +22,6 @@ Voicebox does not and cannot independently verify who owns a voice sample. You a
|
|||||||
|
|
||||||
## Disclosure And Compliance
|
## Disclosure And Compliance
|
||||||
|
|
||||||
If you publish or distribute synthetic audio, disclose that it is AI-generated where required by law, platform policy, or audience expectations. Developers building products on top of Voicebox should treat consent records, disclosure, and jurisdiction-specific requirements as part of their own application design.
|
If you publish or distribute synthetic audio, disclose that it is AI-generated where required by law, platform policy, or audience expectations. Developers building products on top of TalkBox should treat consent records, disclosure, and jurisdiction-specific requirements as part of their own application design.
|
||||||
|
|
||||||
Voicebox runs locally to protect user privacy. That privacy model does not remove your responsibility to respect other people's voices.
|
TalkBox runs locally to protect user privacy. That privacy model does not remove your responsibility to respect other people's voices.
|
||||||
|
|||||||
+4
-4
@@ -14,7 +14,7 @@ We release patches for security vulnerabilities. Which versions are eligible for
|
|||||||
If you discover a security vulnerability, please report it responsibly:
|
If you discover a security vulnerability, please report it responsibly:
|
||||||
|
|
||||||
1. **Do not** open a public GitHub issue
|
1. **Do not** open a public GitHub issue
|
||||||
2. Email security details to: [security@voicebox.sh](mailto:security@voicebox.sh)
|
2. Email security details to: [security@talkbox.sh](mailto:security@talkbox.sh)
|
||||||
3. Include:
|
3. Include:
|
||||||
- Description of the vulnerability
|
- Description of the vulnerability
|
||||||
- Steps to reproduce
|
- Steps to reproduce
|
||||||
@@ -31,7 +31,7 @@ We will:
|
|||||||
|
|
||||||
### For Users
|
### For Users
|
||||||
|
|
||||||
- **Keep Voicebox updated** - Updates include security patches
|
- **Keep TalkBox updated** - Updates include security patches
|
||||||
- **Verify downloads** - Only download from official releases
|
- **Verify downloads** - Only download from official releases
|
||||||
- **Local processing** - Voice data stays on your machine
|
- **Local processing** - Voice data stays on your machine
|
||||||
- **Network security** - Use HTTPS when connecting to remote servers
|
- **Network security** - Use HTTPS when connecting to remote servers
|
||||||
@@ -47,7 +47,7 @@ We will:
|
|||||||
|
|
||||||
### Local Processing
|
### Local Processing
|
||||||
|
|
||||||
Voicebox processes all audio locally by default. Your voice data never leaves your machine unless you explicitly enable remote server mode.
|
TalkBox processes all audio locally by default. Your voice data never leaves your machine unless you explicitly enable remote server mode.
|
||||||
|
|
||||||
### Remote Server Mode
|
### Remote Server Mode
|
||||||
|
|
||||||
@@ -89,4 +89,4 @@ Security updates will be:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Thank you for helping keep Voicebox secure! 🔒
|
Thank you for helping keep TalkBox secure! 🔒
|
||||||
|
|||||||
+2
-2
@@ -4,12 +4,12 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>voicebox</title>
|
<title>talkbox</title>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
try {
|
try {
|
||||||
var theme = 'system';
|
var theme = 'system';
|
||||||
var raw = localStorage.getItem('voicebox-ui');
|
var raw = localStorage.getItem('talkbox-ui');
|
||||||
if (raw) {
|
if (raw) {
|
||||||
var parsed = JSON.parse(raw);
|
var parsed = JSON.parse(raw);
|
||||||
if (parsed && parsed.state && parsed.state.theme) theme = parsed.state.theme;
|
if (parsed && parsed.state && parsed.state.theme) theme = parsed.state.theme;
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@voicebox/app",
|
"name": "@talkbox/app",
|
||||||
"version": "0.5.0",
|
"version": "0.5.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+15
-15
@@ -1,6 +1,6 @@
|
|||||||
import { RouterProvider } from '@tanstack/react-router';
|
import { RouterProvider } from '@tanstack/react-router';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import voiceboxLogo from '@/assets/voicebox-logo.png';
|
import talkboxLogo from '@/assets/talkbox-logo.png';
|
||||||
import { DictateWindow } from '@/components/DictateWindow/DictateWindow';
|
import { DictateWindow } from '@/components/DictateWindow/DictateWindow';
|
||||||
import ShinyText from '@/components/ShinyText';
|
import ShinyText from '@/components/ShinyText';
|
||||||
import { TitleBarDragRegion } from '@/components/TitleBarDragRegion';
|
import { TitleBarDragRegion } from '@/components/TitleBarDragRegion';
|
||||||
@@ -16,7 +16,7 @@ import { router } from '@/router';
|
|||||||
import { useLogStore } from '@/stores/logStore';
|
import { useLogStore } from '@/stores/logStore';
|
||||||
import {
|
import {
|
||||||
getDefaultServerUrl,
|
getDefaultServerUrl,
|
||||||
isLoopbackVoiceboxServerUrl,
|
isLoopbackTalkBoxServerUrl,
|
||||||
useServerStore,
|
useServerStore,
|
||||||
} from '@/stores/serverStore';
|
} from '@/stores/serverStore';
|
||||||
|
|
||||||
@@ -26,10 +26,10 @@ function isDictateView(): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate that a health response has the expected Voicebox-specific shape.
|
* Validate that a health response has the expected TalkBox-specific shape.
|
||||||
* Prevents misidentifying an unrelated service on the same port.
|
* Prevents misidentifying an unrelated service on the same port.
|
||||||
*/
|
*/
|
||||||
function isVoiceboxHealthResponse(health: HealthResponse): boolean {
|
function isTalkBoxHealthResponse(health: HealthResponse): boolean {
|
||||||
return (
|
return (
|
||||||
health?.status === 'healthy' &&
|
health?.status === 'healthy' &&
|
||||||
typeof health.model_loaded === 'boolean' &&
|
typeof health.model_loaded === 'boolean' &&
|
||||||
@@ -136,7 +136,7 @@ function MainApp() {
|
|||||||
if (!platform.metadata.isTauri) {
|
if (!platform.metadata.isTauri) {
|
||||||
const serverUrl = getDefaultServerUrl();
|
const serverUrl = getDefaultServerUrl();
|
||||||
const currentServerUrl = useServerStore.getState().serverUrl;
|
const currentServerUrl = useServerStore.getState().serverUrl;
|
||||||
if (currentServerUrl !== serverUrl && isLoopbackVoiceboxServerUrl(currentServerUrl)) {
|
if (currentServerUrl !== serverUrl && isLoopbackTalkBoxServerUrl(currentServerUrl)) {
|
||||||
useServerStore.getState().setServerUrl(serverUrl);
|
useServerStore.getState().setServerUrl(serverUrl);
|
||||||
}
|
}
|
||||||
setServerReady(true); // Web assumes server is running
|
setServerReady(true); // Web assumes server is running
|
||||||
@@ -155,7 +155,7 @@ function MainApp() {
|
|||||||
console.log('Dev mode: Skipping auto-start of server (run it separately)');
|
console.log('Dev mode: Skipping auto-start of server (run it separately)');
|
||||||
setServerReady(true); // Mark as ready so UI doesn't show loading screen
|
setServerReady(true); // Mark as ready so UI doesn't show loading screen
|
||||||
// Mark that server was not started by app (so we don't try to stop it on close)
|
// Mark that server was not started by app (so we don't try to stop it on close)
|
||||||
window.__voiceboxServerStartedByApp = false;
|
window.__talkboxServerStartedByApp = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,12 +177,12 @@ function MainApp() {
|
|||||||
useServerStore.getState().setServerUrl(serverUrl);
|
useServerStore.getState().setServerUrl(serverUrl);
|
||||||
setServerReady(true);
|
setServerReady(true);
|
||||||
// Mark that we started the server (so we know to stop it on close)
|
// Mark that we started the server (so we know to stop it on close)
|
||||||
window.__voiceboxServerStartedByApp = true;
|
window.__talkboxServerStartedByApp = true;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Failed to auto-start server:', error);
|
console.error('Failed to auto-start server:', error);
|
||||||
serverStartingRef.current = false;
|
serverStartingRef.current = false;
|
||||||
window.__voiceboxServerStartedByApp = false;
|
window.__talkboxServerStartedByApp = false;
|
||||||
|
|
||||||
// Only fall back to health-check polling when the error indicates the
|
// Only fall back to health-check polling when the error indicates the
|
||||||
// port is occupied (likely an external server). For real failures
|
// port is occupied (likely an external server). For real failures
|
||||||
@@ -196,17 +196,17 @@ function MainApp() {
|
|||||||
|
|
||||||
// Fall back to polling: the server may already be running externally
|
// Fall back to polling: the server may already be running externally
|
||||||
// (e.g. started via python/uvicorn/Docker). Poll the health endpoint
|
// (e.g. started via python/uvicorn/Docker). Poll the health endpoint
|
||||||
// until it responds with a valid Voicebox payload, then transition to
|
// until it responds with a valid TalkBox payload, then transition to
|
||||||
// the main UI.
|
// the main UI.
|
||||||
console.log('Falling back to health-check polling...');
|
console.log('Falling back to health-check polling...');
|
||||||
const pollInterval = setInterval(async () => {
|
const pollInterval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const health = await apiClient.getHealth();
|
const health = await apiClient.getHealth();
|
||||||
if (!isVoiceboxHealthResponse(health)) {
|
if (!isTalkBoxHealthResponse(health)) {
|
||||||
console.log('Health response is not from a Voicebox server, keep polling...');
|
console.log('Health response is not from a TalkBox server, keep polling...');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log('External Voicebox server detected via health check');
|
console.log('External TalkBox server detected via health check');
|
||||||
clearInterval(pollInterval);
|
clearInterval(pollInterval);
|
||||||
setServerReady(true);
|
setServerReady(true);
|
||||||
} catch {
|
} catch {
|
||||||
@@ -219,7 +219,7 @@ function MainApp() {
|
|||||||
clearInterval(pollInterval);
|
clearInterval(pollInterval);
|
||||||
serverStartingRef.current = false;
|
serverStartingRef.current = false;
|
||||||
setStartupError(
|
setStartupError(
|
||||||
'Could not connect to a Voicebox server within 2 minutes. ' +
|
'Could not connect to a TalkBox server within 2 minutes. ' +
|
||||||
'Please check that the server is running and try again.',
|
'Please check that the server is running and try again.',
|
||||||
);
|
);
|
||||||
}, 120_000);
|
}, 120_000);
|
||||||
@@ -264,8 +264,8 @@ function MainApp() {
|
|||||||
<div className="w-48 h-48 rounded-full bg-accent/20 blur-3xl" />
|
<div className="w-48 h-48 rounded-full bg-accent/20 blur-3xl" />
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
src={voiceboxLogo}
|
src={talkboxLogo}
|
||||||
alt="Voicebox"
|
alt="TalkBox"
|
||||||
className="w-48 h-48 object-contain animate-fade-in-scale relative z-10"
|
className="w-48 h-48 object-contain animate-fade-in-scale relative z-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 4.0 MiB |
@@ -35,7 +35,7 @@ interface ChordPickerProps {
|
|||||||
* before clicking Save (otherwise they'd be saving while still holding
|
* before clicking Save (otherwise they'd be saving while still holding
|
||||||
* the shortcut, which is awkward).
|
* the shortcut, which is awkward).
|
||||||
*
|
*
|
||||||
* Browser limitation: we can only capture keys while Voicebox has key
|
* Browser limitation: we can only capture keys while TalkBox has key
|
||||||
* focus, so the picker pulls focus to a hidden capture surface inside
|
* focus, so the picker pulls focus to a hidden capture surface inside
|
||||||
* the dialog. The actual chord runs through the Rust global hook —
|
* the dialog. The actual chord runs through the Rust global hook —
|
||||||
* this picker only writes the configuration the hook reads.
|
* this picker only writes the configuration the hook reads.
|
||||||
@@ -122,7 +122,7 @@ export function ChordPicker({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Wire global listeners only while open. Capture phase so Voicebox's
|
// Wire global listeners only while open. Capture phase so TalkBox's
|
||||||
// own command palette / global shortcuts don't swallow the chord first.
|
// own command palette / global shortcuts don't swallow the chord first.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export function MainEditor() {
|
|||||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
if (!file.name.endsWith('.voicebox.zip')) {
|
if (!file.name.endsWith('.talkbox.zip')) {
|
||||||
toast({
|
toast({
|
||||||
title: t('main.import.invalidTitle'),
|
title: t('main.import.invalidTitle'),
|
||||||
description: t('main.import.invalidDescription'),
|
description: t('main.import.invalidDescription'),
|
||||||
@@ -84,7 +84,7 @@ export function MainEditor() {
|
|||||||
|
|
||||||
<div className="absolute top-0 left-0 right-0 z-10">
|
<div className="absolute top-0 left-0 right-0 z-10">
|
||||||
<div className="flex items-center justify-between mb-4 px-1">
|
<div className="flex items-center justify-between mb-4 px-1">
|
||||||
<h2 className="text-2xl font-bold">Voicebox</h2>
|
<h2 className="text-2xl font-bold">TalkBox</h2>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="outline" onClick={handleImportClick}>
|
<Button variant="outline" onClick={handleImportClick}>
|
||||||
<Upload className="mr-2 h-4 w-4" />
|
<Upload className="mr-2 h-4 w-4" />
|
||||||
@@ -93,7 +93,7 @@ export function MainEditor() {
|
|||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
accept=".voicebox.zip"
|
accept=".talkbox.zip"
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ export function ConnectionForm() {
|
|||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>Server URL</FormLabel>
|
<FormLabel>Server URL</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="http://127.0.0.1:17493" {...field} />
|
<Input placeholder="http://127.0.0.1:17494" {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormDescription>Enter the URL of your voicebox backend server</FormDescription>
|
<FormDescription>Enter the URL of your talkbox backend server</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ArrowUpRight } from 'lucide-react';
|
|||||||
import type { CSSProperties, ReactNode } from 'react';
|
import type { CSSProperties, ReactNode } from 'react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
import voiceboxLogo from '@/assets/voicebox-logo.png';
|
import talkboxLogo from '@/assets/talkbox-logo.png';
|
||||||
import { usePlatform } from '@/platform/PlatformContext';
|
import { usePlatform } from '@/platform/PlatformContext';
|
||||||
|
|
||||||
function FadeIn({ delay = 0, children }: { delay?: number; children: ReactNode }) {
|
function FadeIn({ delay = 0, children }: { delay?: number; children: ReactNode }) {
|
||||||
@@ -45,12 +45,12 @@ export function AboutPage() {
|
|||||||
<div className="max-w-md mx-auto h-full flex items-center">
|
<div className="max-w-md mx-auto h-full flex items-center">
|
||||||
<div className="flex flex-col items-center text-center space-y-5">
|
<div className="flex flex-col items-center text-center space-y-5">
|
||||||
<FadeIn delay={0}>
|
<FadeIn delay={0}>
|
||||||
<img src={voiceboxLogo} alt="Voicebox" className="w-20 h-20 object-contain" />
|
<img src={talkboxLogo} alt="TalkBox" className="w-20 h-20 object-contain" />
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
|
|
||||||
<FadeIn delay={80}>
|
<FadeIn delay={80}>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<h1 className="text-lg font-semibold">Voicebox</h1>
|
<h1 className="text-lg font-semibold">TalkBox</h1>
|
||||||
<p className="text-xs text-muted-foreground/60 h-4">
|
<p className="text-xs text-muted-foreground/60 h-4">
|
||||||
{version ? `v${version}` : '\u00A0'}
|
{version ? `v${version}` : '\u00A0'}
|
||||||
</p>
|
</p>
|
||||||
@@ -97,7 +97,7 @@ export function AboutPage() {
|
|||||||
<ArrowUpRight className="h-3.5 w-3.5 text-muted-foreground/40 group-hover:text-muted-foreground transition-colors" />
|
<ArrowUpRight className="h-3.5 w-3.5 text-muted-foreground/40 group-hover:text-muted-foreground transition-colors" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href="https://github.com/jamiepine/voicebox"
|
href="https://github.com/jamiepine/talkbox"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group inline-flex items-center gap-2 rounded-lg border border-border/60 px-4 py-2 text-sm transition-colors hover:bg-muted/50"
|
className="group inline-flex items-center gap-2 rounded-lg border border-border/60 px-4 py-2 text-sm transition-colors hover:bg-muted/50"
|
||||||
@@ -124,7 +124,7 @@ export function AboutPage() {
|
|||||||
link: (
|
link: (
|
||||||
// biome-ignore lint/a11y/useAnchorContent: Trans fills content at runtime
|
// biome-ignore lint/a11y/useAnchorContent: Trans fills content at runtime
|
||||||
<a
|
<a
|
||||||
href="https://github.com/jamiepine/voicebox/blob/main/LICENSE"
|
href="https://github.com/jamiepine/talkbox/blob/main/LICENSE"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="hover:text-muted-foreground/60 transition-colors"
|
className="hover:text-muted-foreground/60 transition-colors"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function CloudSection() {
|
|||||||
if (connected && polling) {
|
if (connected && polling) {
|
||||||
setPolling(false);
|
setPolling(false);
|
||||||
toast({
|
toast({
|
||||||
title: 'Connected to Voicebox Cloud',
|
title: 'Connected to TalkBox Cloud',
|
||||||
description: `Linked as ${status?.device_name ?? 'this device'}.`,
|
description: `Linked as ${status?.device_name ?? 'this device'}.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ export function CloudSection() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingSection
|
<SettingSection
|
||||||
title="Voicebox Cloud"
|
title="TalkBox Cloud"
|
||||||
description="End-to-end encrypted backup & sync across your devices."
|
description="End-to-end encrypted backup & sync across your devices."
|
||||||
>
|
>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
@@ -138,7 +138,7 @@ export function CloudSection() {
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
className="text-sm text-accent hover:underline"
|
className="text-sm text-accent hover:underline"
|
||||||
href={status?.dashboard_url ?? 'https://voicebox.sh/account'}
|
href={status?.dashboard_url ?? 'https://talkbox.sh/account'}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export function GeneralPage() {
|
|||||||
<div className="space-y-8 max-w-2xl">
|
<div className="space-y-8 max-w-2xl">
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<a
|
<a
|
||||||
href="https://docs.voicebox.sh"
|
href="https://docs.talkbox.sh"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="group flex items-center gap-3 rounded-lg border border-border/60 p-4 transition-colors hover:bg-muted/50"
|
className="group flex items-center gap-3 rounded-lg border border-border/60 p-4 transition-colors hover:bg-muted/50"
|
||||||
@@ -82,7 +82,7 @@ export function GeneralPage() {
|
|||||||
<Book className="h-5 w-5 shrink-0 text-accent" strokeWidth={2.5} />
|
<Book className="h-5 w-5 shrink-0 text-accent" strokeWidth={2.5} />
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="text-sm font-medium">{t('settings.general.docs.title')}</div>
|
<div className="text-sm font-medium">{t('settings.general.docs.title')}</div>
|
||||||
<div className="text-xs text-muted-foreground">docs.voicebox.sh</div>
|
<div className="text-xs text-muted-foreground">docs.talkbox.sh</div>
|
||||||
</div>
|
</div>
|
||||||
<ArrowUpRight className="h-4 w-4 text-muted-foreground/40 group-hover:text-muted-foreground transition-colors" />
|
<ArrowUpRight className="h-4 w-4 text-muted-foreground/40 group-hover:text-muted-foreground transition-colors" />
|
||||||
</a>
|
</a>
|
||||||
@@ -126,7 +126,7 @@ export function GeneralPage() {
|
|||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem className="flex-1">
|
<FormItem className="flex-1">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="http://127.0.0.1:17493" {...field} />
|
<Input placeholder="http://127.0.0.1:17494" {...field} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -18,23 +18,23 @@ import { SettingRow, SettingSection } from './SettingRow';
|
|||||||
|
|
||||||
function getStdioShimCommand(): string {
|
function getStdioShimCommand(): string {
|
||||||
if (typeof navigator === 'undefined') {
|
if (typeof navigator === 'undefined') {
|
||||||
return '/Applications/Voicebox.app/Contents/MacOS/voicebox-mcp';
|
return '/Applications/TalkBox.app/Contents/MacOS/talkbox-mcp';
|
||||||
}
|
}
|
||||||
|
|
||||||
const platform = `${navigator.platform} ${navigator.userAgent}`.toLowerCase();
|
const platform = `${navigator.platform} ${navigator.userAgent}`.toLowerCase();
|
||||||
if (platform.includes('win')) {
|
if (platform.includes('win')) {
|
||||||
return 'C:\\Program Files\\Voicebox\\voicebox-mcp.exe';
|
return 'C:\\Program Files\\TalkBox\\talkbox-mcp.exe';
|
||||||
}
|
}
|
||||||
if (platform.includes('linux')) {
|
if (platform.includes('linux')) {
|
||||||
return '/opt/voicebox/voicebox-mcp';
|
return '/opt/talkbox/talkbox-mcp';
|
||||||
}
|
}
|
||||||
return '/Applications/Voicebox.app/Contents/MacOS/voicebox-mcp';
|
return '/Applications/TalkBox.app/Contents/MacOS/talkbox-mcp';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings → MCP — configure per-agent voice binding and show copy-paste
|
* Settings → MCP — configure per-agent voice binding and show copy-paste
|
||||||
* install snippets for major MCP clients. Backend runs at /mcp on the
|
* install snippets for major MCP clients. Backend runs at /mcp on the
|
||||||
* existing Voicebox server; this page is the agent-onboarding surface.
|
* existing TalkBox server; this page is the agent-onboarding surface.
|
||||||
*/
|
*/
|
||||||
export function MCPPage() {
|
export function MCPPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -82,9 +82,9 @@ export function MCPPage() {
|
|||||||
snippet={JSON.stringify(
|
snippet={JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
voicebox: {
|
talkbox: {
|
||||||
url: mcpUrl,
|
url: mcpUrl,
|
||||||
headers: { 'X-Voicebox-Client-Id': 'claude-code' },
|
headers: { 'X-TalkBox-Client-Id': 'claude-code' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -95,7 +95,7 @@ export function MCPPage() {
|
|||||||
<SnippetRow
|
<SnippetRow
|
||||||
title={t('settings.mcp.install.claudeCode.title')}
|
title={t('settings.mcp.install.claudeCode.title')}
|
||||||
description={t('settings.mcp.install.claudeCode.description')}
|
description={t('settings.mcp.install.claudeCode.description')}
|
||||||
snippet={`claude mcp add voicebox --transport http --url ${mcpUrl} --header "X-Voicebox-Client-Id: claude-code"`}
|
snippet={`claude mcp add talkbox --transport http --url ${mcpUrl} --header "X-TalkBox-Client-Id: claude-code"`}
|
||||||
/>
|
/>
|
||||||
<SnippetRow
|
<SnippetRow
|
||||||
title={t('settings.mcp.install.stdio.title')}
|
title={t('settings.mcp.install.stdio.title')}
|
||||||
@@ -103,9 +103,9 @@ export function MCPPage() {
|
|||||||
snippet={JSON.stringify(
|
snippet={JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
voicebox: {
|
talkbox: {
|
||||||
command: stdioShimCommand,
|
command: stdioShimCommand,
|
||||||
env: { VOICEBOX_CLIENT_ID: 'claude-code' },
|
env: { TALKBOX_CLIENT_ID: 'claude-code' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -276,19 +276,19 @@ export function MCPPage() {
|
|||||||
<h3 className="text-sm font-semibold">{t('settings.mcp.sidebar.toolsTitle')}</h3>
|
<h3 className="text-sm font-semibold">{t('settings.mcp.sidebar.toolsTitle')}</h3>
|
||||||
<ul className="text-sm text-muted-foreground space-y-1.5 leading-relaxed">
|
<ul className="text-sm text-muted-foreground space-y-1.5 leading-relaxed">
|
||||||
<li>
|
<li>
|
||||||
<code className="text-accent">voicebox.speak</code>
|
<code className="text-accent">talkbox.speak</code>
|
||||||
<div>{t('settings.mcp.sidebar.tools.speak')}</div>
|
<div>{t('settings.mcp.sidebar.tools.speak')}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code className="text-accent">voicebox.transcribe</code>
|
<code className="text-accent">talkbox.transcribe</code>
|
||||||
<div>{t('settings.mcp.sidebar.tools.transcribe')}</div>
|
<div>{t('settings.mcp.sidebar.tools.transcribe')}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code className="text-accent">voicebox.list_captures</code>
|
<code className="text-accent">talkbox.list_captures</code>
|
||||||
<div>{t('settings.mcp.sidebar.tools.listCaptures')}</div>
|
<div>{t('settings.mcp.sidebar.tools.listCaptures')}</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code className="text-accent">voicebox.list_profiles</code>
|
<code className="text-accent">talkbox.list_profiles</code>
|
||||||
<div>{t('settings.mcp.sidebar.tools.listProfiles')}</div>
|
<div>{t('settings.mcp.sidebar.tools.listProfiles')}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Link, useMatchRoute } from '@tanstack/react-router';
|
|||||||
import { AudioLines, Box, Captions, type LucideIcon, Mic, Settings, Volume2, Wand2 } from 'lucide-react';
|
import { AudioLines, Box, Captions, type LucideIcon, Mic, Settings, Volume2, Wand2 } from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import voiceboxLogo from '@/assets/voicebox-logo.png';
|
import talkboxLogo from '@/assets/talkbox-logo.png';
|
||||||
import { cn } from '@/lib/utils/cn';
|
import { cn } from '@/lib/utils/cn';
|
||||||
import { usePlatform } from '@/platform/PlatformContext';
|
import { usePlatform } from '@/platform/PlatformContext';
|
||||||
import type { UpdateStatus } from '@/platform/types';
|
import type { UpdateStatus } from '@/platform/types';
|
||||||
@@ -47,7 +47,7 @@ export function Sidebar({ isMacOS }: SidebarProps) {
|
|||||||
>
|
>
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className="mb-2">
|
<div className="mb-2">
|
||||||
<img src={voiceboxLogo} alt="Voicebox" className="sidebar-logo w-12 h-12 object-contain" />
|
<img src={talkboxLogo} alt="TalkBox" className="sidebar-logo w-12 h-12 object-contain" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigation Buttons */}
|
{/* Navigation Buttons */}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
interface Window {
|
interface Window {
|
||||||
__voiceboxServerStartedByApp?: boolean;
|
__talkboxServerStartedByApp?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'virtual:changelog' {
|
declare module 'virtual:changelog' {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ i18n
|
|||||||
react: { useSuspense: false },
|
react: { useSuspense: false },
|
||||||
detection: {
|
detection: {
|
||||||
order: ['localStorage', 'navigator'],
|
order: ['localStorage', 'navigator'],
|
||||||
lookupLocalStorage: 'voicebox:lang',
|
lookupLocalStorage: 'talkbox:lang',
|
||||||
caches: ['localStorage'],
|
caches: ['localStorage'],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "Input Monitoring permission",
|
"label": "Input Monitoring permission",
|
||||||
"ready": "macOS allows Voicebox to detect your global shortcut.",
|
"ready": "macOS allows TalkBox to detect your global shortcut.",
|
||||||
"missing": "macOS needs to allow Voicebox to detect the global shortcut.",
|
"missing": "macOS needs to allow TalkBox to detect the global shortcut.",
|
||||||
"openSettings": "Open Settings"
|
"openSettings": "Open Settings"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "Accessibility permission",
|
"label": "Accessibility permission",
|
||||||
"ready": "Voicebox can paste transcriptions into other apps.",
|
"ready": "TalkBox can paste transcriptions into other apps.",
|
||||||
"missing": "Required so transcriptions can paste into the focused app.",
|
"missing": "Required so transcriptions can paste into the focused app.",
|
||||||
"openSettings": "Open Settings"
|
"openSettings": "Open Settings"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "Grant Accessibility permission to enable auto-paste",
|
"title": "Grant Accessibility permission to enable auto-paste",
|
||||||
"body": "Voicebox needs <path>System Settings → Privacy & Security → Accessibility</path> to paste transcriptions into other apps. Your dictation still lands in the Captures tab without it.",
|
"body": "TalkBox needs <path>System Settings → Privacy & Security → Accessibility</path> to paste transcriptions into other apps. Your dictation still lands in the Captures tab without it.",
|
||||||
"openSettings": "Open Settings",
|
"openSettings": "Open Settings",
|
||||||
"recheck": "I've enabled it",
|
"recheck": "I've enabled it",
|
||||||
"rechecking": "Checking…",
|
"rechecking": "Checking…",
|
||||||
"stillMissing": "Still not detected. macOS usually requires quitting and reopening Voicebox after toggling the permission."
|
"stillMissing": "Still not detected. macOS usually requires quitting and reopening TalkBox after toggling the permission."
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "Grant Input Monitoring to enable the global shortcut",
|
"title": "Grant Input Monitoring to enable the global shortcut",
|
||||||
"body": "Voicebox needs <path>System Settings → Privacy & Security → Input Monitoring</path> to detect your dictation chord. The toggle is on, but macOS is blocking key events until you allow it.",
|
"body": "TalkBox needs <path>System Settings → Privacy & Security → Input Monitoring</path> to detect your dictation chord. The toggle is on, but macOS is blocking key events until you allow it.",
|
||||||
"openSettings": "Open Settings",
|
"openSettings": "Open Settings",
|
||||||
"recheck": "I've enabled it",
|
"recheck": "I've enabled it",
|
||||||
"rechecking": "Checking…",
|
"rechecking": "Checking…",
|
||||||
"stillMissing": "Still not detected. macOS usually requires quitting and reopening Voicebox after toggling the permission."
|
"stillMissing": "Still not detected. macOS usually requires quitting and reopening TalkBox after toggling the permission."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "Create Voice",
|
"createVoice": "Create Voice",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "Invalid file type",
|
"invalidTitle": "Invalid file type",
|
||||||
"invalidDescription": "Please select a valid .voicebox.zip file",
|
"invalidDescription": "Please select a valid .talkbox.zip file",
|
||||||
"successTitle": "Profile imported",
|
"successTitle": "Profile imported",
|
||||||
"successDescription": "Voice profile imported successfully",
|
"successDescription": "Voice profile imported successfully",
|
||||||
"failedTitle": "Failed to import profile",
|
"failedTitle": "Failed to import profile",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "Language",
|
"label": "Language",
|
||||||
"description": "Choose the display language for Voicebox."
|
"description": "Choose the display language for TalkBox."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "Theme",
|
"label": "Theme",
|
||||||
@@ -769,7 +769,7 @@
|
|||||||
},
|
},
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "Server URL",
|
"title": "Server URL",
|
||||||
"description": "The address of your voicebox backend server.",
|
"description": "The address of your talkbox backend server.",
|
||||||
"invalidUrl": "Please enter a valid URL",
|
"invalidUrl": "Please enter a valid URL",
|
||||||
"updatedTitle": "Server URL updated",
|
"updatedTitle": "Server URL updated",
|
||||||
"updatedDescription": "Connected to {{url}}"
|
"updatedDescription": "Connected to {{url}}"
|
||||||
@@ -824,7 +824,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "API Access",
|
"title": "API Access",
|
||||||
"description": "Integrate Voicebox into your workflow via the REST API at <code>{{url}}</code>",
|
"description": "Integrate TalkBox into your workflow via the REST API at <code>{{url}}</code>",
|
||||||
"viewReference": "View the full API reference",
|
"viewReference": "View the full API reference",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "Generate speech",
|
"generate": "Generate speech",
|
||||||
@@ -913,7 +913,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "Auto-paste into focused text field",
|
"title": "Auto-paste into focused text field",
|
||||||
"description": "If a text input is focused in another app, paste directly into it. Voicebox saves and restores whatever was on your clipboard."
|
"description": "If a text input is focused in another app, paste directly into it. TalkBox saves and restores whatever was on your clipboard."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -921,7 +921,7 @@
|
|||||||
"description": "Pick which speech-to-text model runs on your captures.",
|
"description": "Pick which speech-to-text model runs on your captures.",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Transcription model",
|
"title": "Transcription model",
|
||||||
"description": "Whisper ships with Voicebox and runs entirely on your machine.",
|
"description": "Whisper ships with TalkBox and runs entirely on your machine.",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -997,7 +997,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "Storage",
|
"title": "Storage",
|
||||||
"description": "Captures are saved as paired audio and transcript files in your Voicebox data directory.",
|
"description": "Captures are saved as paired audio and transcript files in your TalkBox data directory.",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "Retention",
|
"title": "Retention",
|
||||||
"description": "How long to keep captures. Applies to both audio and transcripts.",
|
"description": "How long to keep captures. Applies to both audio and transcripts.",
|
||||||
@@ -1014,7 +1014,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "About Captures",
|
"aboutTitle": "About Captures",
|
||||||
"aboutBody": "Hold a shortcut anywhere on your machine, speak, and Voicebox turns your voice into text. Replay it in any cloned voice, paste it into any app, or pipe it into your coding agent.",
|
"aboutBody": "Hold a shortcut anywhere on your machine, speak, and TalkBox turns your voice into text. Replay it in any cloned voice, paste it into any app, or pipe it into your coding agent.",
|
||||||
"differencesTitle": "What's different",
|
"differencesTitle": "What's different",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "Fully local.",
|
"title": "Fully local.",
|
||||||
@@ -1030,14 +1030,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Heads-up on Windows",
|
"title": "Heads-up on Windows",
|
||||||
"body": "The shortcut won't fire while Voicebox itself or any app running as administrator is focused. Working on it."
|
"body": "The shortcut won't fire while TalkBox itself or any app running as administrator is focused. Working on it."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "Install into your agent",
|
"title": "Install into your agent",
|
||||||
"description": "Voicebox exposes a local MCP server whenever the app is open. Paste one of these snippets into your agent's MCP config.",
|
"description": "TalkBox exposes a local MCP server whenever the app is open. Paste one of these snippets into your agent's MCP config.",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP (recommended)",
|
"title": "HTTP (recommended)",
|
||||||
"description": "For clients that speak HTTP MCP — Claude Code, Cursor, Windsurf, VS Code."
|
"description": "For clients that speak HTTP MCP — Claude Code, Cursor, Windsurf, VS Code."
|
||||||
@@ -1055,15 +1055,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "Default voice",
|
"title": "Default voice",
|
||||||
"description": "Used when an agent calls voicebox.speak without a specific profile and has no per-client binding.",
|
"description": "Used when an agent calls talkbox.speak without a specific profile and has no per-client binding.",
|
||||||
"label": "Default playback voice",
|
"label": "Default playback voice",
|
||||||
"labelHint": "Shared with the Captures-tab 'Play as voice' dropdown — one default voice for passive playback.",
|
"labelHint": "Shared with the Captures-tab 'Play as voice' dropdown — one default voice for passive playback.",
|
||||||
"none": "(none)"
|
"none": "(none)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "Per-agent voice",
|
"title": "Per-agent voice",
|
||||||
"description": "Bind specific agents to specific voices so you can tell who's speaking without looking. The agent identifies itself by the X-Voicebox-Client-Id header (or VOICEBOX_CLIENT_ID env for stdio).",
|
"description": "Bind specific agents to specific voices so you can tell who's speaking without looking. The agent identifies itself by the X-TalkBox-Client-Id header (or TALKBOX_CLIENT_ID env for stdio).",
|
||||||
"empty": "No bindings yet. Add one below, then configure your MCP client to send the matching <code>X-Voicebox-Client-Id</code>.",
|
"empty": "No bindings yet. Add one below, then configure your MCP client to send the matching <code>X-TalkBox-Client-Id</code>.",
|
||||||
"lastSeen": "last seen {{when}}",
|
"lastSeen": "last seen {{when}}",
|
||||||
"lastSeenTitle": "Last seen {{when}}",
|
"lastSeenTitle": "Last seen {{when}}",
|
||||||
"neverConnected": "never connected",
|
"neverConnected": "never connected",
|
||||||
@@ -1078,7 +1078,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "About MCP",
|
"aboutTitle": "About MCP",
|
||||||
"aboutBody": "Model Context Protocol lets your AI coding agent — Claude Code, Cursor, Windsurf — call Voicebox tools. Speak in a cloned voice, transcribe audio, browse captures.",
|
"aboutBody": "Model Context Protocol lets your AI coding agent — Claude Code, Cursor, Windsurf — call TalkBox tools. Speak in a cloned voice, transcribe audio, browse captures.",
|
||||||
"toolsTitle": "Available tools",
|
"toolsTitle": "Available tools",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "Speak text in a voice profile.",
|
"speak": "Speak text in a voice profile.",
|
||||||
@@ -1137,7 +1137,7 @@
|
|||||||
"deleteCuda": "Failed to delete CUDA backend",
|
"deleteCuda": "Failed to delete CUDA backend",
|
||||||
"deleteRocm": "Failed to delete ROCm backend"
|
"deleteRocm": "Failed to delete ROCm backend"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox automatically detects and uses the best available GPU on your system. On Apple Silicon Macs, the MLX backend runs natively on the Neural Engine and GPU via Metal Performance Shaders (MPS), with no additional setup required. On Windows, you can download optional CUDA (NVIDIA) or ROCm (AMD) backends for hardware-accelerated inference. Intel XPU and DirectML are also supported where available through PyTorch. When no GPU is detected, Voicebox falls back to CPU — all engines still work, just slower.",
|
"footer": "TalkBox automatically detects and uses the best available GPU on your system. On Apple Silicon Macs, the MLX backend runs natively on the Neural Engine and GPU via Metal Performance Shaders (MPS), with no additional setup required. On Windows, you can download optional CUDA (NVIDIA) or ROCm (AMD) backends for hardware-accelerated inference. Intel XPU and DirectML are also supported where available through PyTorch. When no GPU is detected, TalkBox falls back to CPU — all engines still work, just slower.",
|
||||||
"rocm": {
|
"rocm": {
|
||||||
"title": "AMD ROCm Backend",
|
"title": "AMD ROCm Backend",
|
||||||
"activeTitle": "ROCm Backend Active",
|
"activeTitle": "ROCm Backend Active",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "Permiso de Monitorización de entrada",
|
"label": "Permiso de Monitorización de entrada",
|
||||||
"ready": "macOS permite que Voicebox detecte tu atajo global.",
|
"ready": "macOS permite que TalkBox detecte tu atajo global.",
|
||||||
"missing": "macOS debe permitir que Voicebox detecte el atajo global.",
|
"missing": "macOS debe permitir que TalkBox detecte el atajo global.",
|
||||||
"openSettings": "Abrir Ajustes"
|
"openSettings": "Abrir Ajustes"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "Permiso de Accesibilidad",
|
"label": "Permiso de Accesibilidad",
|
||||||
"ready": "Voicebox puede pegar transcripciones en otras apps.",
|
"ready": "TalkBox puede pegar transcripciones en otras apps.",
|
||||||
"missing": "Necesario para que las transcripciones se peguen en la app activa.",
|
"missing": "Necesario para que las transcripciones se peguen en la app activa.",
|
||||||
"openSettings": "Abrir Ajustes"
|
"openSettings": "Abrir Ajustes"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "Concede el permiso de Accesibilidad para habilitar el pegado automático",
|
"title": "Concede el permiso de Accesibilidad para habilitar el pegado automático",
|
||||||
"body": "Voicebox necesita <path>Ajustes del Sistema → Privacidad y seguridad → Accesibilidad</path> para pegar transcripciones en otras apps. Tu dictado igualmente aparece en la pestaña Capturas sin él.",
|
"body": "TalkBox necesita <path>Ajustes del Sistema → Privacidad y seguridad → Accesibilidad</path> para pegar transcripciones en otras apps. Tu dictado igualmente aparece en la pestaña Capturas sin él.",
|
||||||
"openSettings": "Abrir Ajustes",
|
"openSettings": "Abrir Ajustes",
|
||||||
"recheck": "Ya lo he activado",
|
"recheck": "Ya lo he activado",
|
||||||
"rechecking": "Comprobando…",
|
"rechecking": "Comprobando…",
|
||||||
"stillMissing": "Sigue sin detectarse. macOS suele requerir salir y reabrir Voicebox tras cambiar el permiso."
|
"stillMissing": "Sigue sin detectarse. macOS suele requerir salir y reabrir TalkBox tras cambiar el permiso."
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "Concede Monitorización de entrada para habilitar el atajo global",
|
"title": "Concede Monitorización de entrada para habilitar el atajo global",
|
||||||
"body": "Voicebox necesita <path>Ajustes del Sistema → Privacidad y seguridad → Monitorización de entrada</path> para detectar tu combinación de dictado. La opción está activada, pero macOS bloquea los eventos de teclado hasta que lo permitas.",
|
"body": "TalkBox necesita <path>Ajustes del Sistema → Privacidad y seguridad → Monitorización de entrada</path> para detectar tu combinación de dictado. La opción está activada, pero macOS bloquea los eventos de teclado hasta que lo permitas.",
|
||||||
"openSettings": "Abrir Ajustes",
|
"openSettings": "Abrir Ajustes",
|
||||||
"recheck": "Ya lo he activado",
|
"recheck": "Ya lo he activado",
|
||||||
"rechecking": "Comprobando…",
|
"rechecking": "Comprobando…",
|
||||||
"stillMissing": "Sigue sin detectarse. macOS suele requerir salir y reabrir Voicebox tras cambiar el permiso."
|
"stillMissing": "Sigue sin detectarse. macOS suele requerir salir y reabrir TalkBox tras cambiar el permiso."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "Crear voz",
|
"createVoice": "Crear voz",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "Tipo de archivo no válido",
|
"invalidTitle": "Tipo de archivo no válido",
|
||||||
"invalidDescription": "Selecciona un archivo .voicebox.zip válido",
|
"invalidDescription": "Selecciona un archivo .talkbox.zip válido",
|
||||||
"successTitle": "Perfil importado",
|
"successTitle": "Perfil importado",
|
||||||
"successDescription": "Perfil de voz importado correctamente",
|
"successDescription": "Perfil de voz importado correctamente",
|
||||||
"failedTitle": "Error al importar el perfil",
|
"failedTitle": "Error al importar el perfil",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "Idioma",
|
"label": "Idioma",
|
||||||
"description": "Elige el idioma de la interfaz de Voicebox."
|
"description": "Elige el idioma de la interfaz de TalkBox."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "Tema",
|
"label": "Tema",
|
||||||
@@ -769,7 +769,7 @@
|
|||||||
},
|
},
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "URL del servidor",
|
"title": "URL del servidor",
|
||||||
"description": "La dirección de tu servidor backend de Voicebox.",
|
"description": "La dirección de tu servidor backend de TalkBox.",
|
||||||
"invalidUrl": "Introduce una URL válida",
|
"invalidUrl": "Introduce una URL válida",
|
||||||
"updatedTitle": "URL del servidor actualizada",
|
"updatedTitle": "URL del servidor actualizada",
|
||||||
"updatedDescription": "Conectado a {{url}}"
|
"updatedDescription": "Conectado a {{url}}"
|
||||||
@@ -824,7 +824,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "Acceso a la API",
|
"title": "Acceso a la API",
|
||||||
"description": "Integra Voicebox en tu flujo de trabajo mediante la API REST en <code>{{url}}</code>",
|
"description": "Integra TalkBox en tu flujo de trabajo mediante la API REST en <code>{{url}}</code>",
|
||||||
"viewReference": "Ver la referencia completa de la API",
|
"viewReference": "Ver la referencia completa de la API",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "Generar voz",
|
"generate": "Generar voz",
|
||||||
@@ -913,7 +913,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "Pegar automáticamente en el campo de texto activo",
|
"title": "Pegar automáticamente en el campo de texto activo",
|
||||||
"description": "Si hay un campo de texto activo en otra app, pega directamente en él. Voicebox guarda y restaura lo que hubiera en tu portapapeles."
|
"description": "Si hay un campo de texto activo en otra app, pega directamente en él. TalkBox guarda y restaura lo que hubiera en tu portapapeles."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -921,7 +921,7 @@
|
|||||||
"description": "Elige qué modelo de voz a texto se ejecuta en tus capturas.",
|
"description": "Elige qué modelo de voz a texto se ejecuta en tus capturas.",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Modelo de transcripción",
|
"title": "Modelo de transcripción",
|
||||||
"description": "Whisper viene incluido con Voicebox y se ejecuta enteramente en tu equipo.",
|
"description": "Whisper viene incluido con TalkBox y se ejecuta enteramente en tu equipo.",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -997,7 +997,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "Almacenamiento",
|
"title": "Almacenamiento",
|
||||||
"description": "Las capturas se guardan como archivos emparejados de audio y transcripción en tu directorio de datos de Voicebox.",
|
"description": "Las capturas se guardan como archivos emparejados de audio y transcripción en tu directorio de datos de TalkBox.",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "Retención",
|
"title": "Retención",
|
||||||
"description": "Cuánto tiempo conservar las capturas. Se aplica tanto al audio como a las transcripciones.",
|
"description": "Cuánto tiempo conservar las capturas. Se aplica tanto al audio como a las transcripciones.",
|
||||||
@@ -1014,7 +1014,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "Acerca de Capturas",
|
"aboutTitle": "Acerca de Capturas",
|
||||||
"aboutBody": "Mantén pulsado un atajo en cualquier parte de tu equipo, habla, y Voicebox convierte tu voz en texto. Reprodúcelo con cualquier voz clonada, pégalo en cualquier app o canalízalo a tu agente de programación.",
|
"aboutBody": "Mantén pulsado un atajo en cualquier parte de tu equipo, habla, y TalkBox convierte tu voz en texto. Reprodúcelo con cualquier voz clonada, pégalo en cualquier app o canalízalo a tu agente de programación.",
|
||||||
"differencesTitle": "Qué lo diferencia",
|
"differencesTitle": "Qué lo diferencia",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "Totalmente local.",
|
"title": "Totalmente local.",
|
||||||
@@ -1030,14 +1030,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Aviso en Windows",
|
"title": "Aviso en Windows",
|
||||||
"body": "El atajo no se activará mientras Voicebox o cualquier app ejecutada como administrador esté en primer plano. Estamos en ello."
|
"body": "El atajo no se activará mientras TalkBox o cualquier app ejecutada como administrador esté en primer plano. Estamos en ello."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "Instalar en tu agente",
|
"title": "Instalar en tu agente",
|
||||||
"description": "Voicebox expone un servidor MCP local siempre que la app está abierta. Pega uno de estos fragmentos en la configuración MCP de tu agente.",
|
"description": "TalkBox expone un servidor MCP local siempre que la app está abierta. Pega uno de estos fragmentos en la configuración MCP de tu agente.",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP (recomendado)",
|
"title": "HTTP (recomendado)",
|
||||||
"description": "Para clientes que hablan MCP por HTTP: Claude Code, Cursor, Windsurf, VS Code."
|
"description": "Para clientes que hablan MCP por HTTP: Claude Code, Cursor, Windsurf, VS Code."
|
||||||
@@ -1055,15 +1055,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "Voz predeterminada",
|
"title": "Voz predeterminada",
|
||||||
"description": "Se usa cuando un agente llama a voicebox.speak sin un perfil específico y no tiene una vinculación por cliente.",
|
"description": "Se usa cuando un agente llama a talkbox.speak sin un perfil específico y no tiene una vinculación por cliente.",
|
||||||
"label": "Voz de reproducción predeterminada",
|
"label": "Voz de reproducción predeterminada",
|
||||||
"labelHint": "Compartida con el desplegable 'Reproducir como voz' de la pestaña Capturas: una voz predeterminada para la reproducción pasiva.",
|
"labelHint": "Compartida con el desplegable 'Reproducir como voz' de la pestaña Capturas: una voz predeterminada para la reproducción pasiva.",
|
||||||
"none": "(ninguna)"
|
"none": "(ninguna)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "Voz por agente",
|
"title": "Voz por agente",
|
||||||
"description": "Vincula agentes concretos a voces concretas para saber quién habla sin mirar. El agente se identifica mediante la cabecera X-Voicebox-Client-Id (o la variable de entorno VOICEBOX_CLIENT_ID para stdio).",
|
"description": "Vincula agentes concretos a voces concretas para saber quién habla sin mirar. El agente se identifica mediante la cabecera X-TalkBox-Client-Id (o la variable de entorno TALKBOX_CLIENT_ID para stdio).",
|
||||||
"empty": "Aún no hay vinculaciones. Añade una abajo y luego configura tu cliente MCP para que envíe el <code>X-Voicebox-Client-Id</code> correspondiente.",
|
"empty": "Aún no hay vinculaciones. Añade una abajo y luego configura tu cliente MCP para que envíe el <code>X-TalkBox-Client-Id</code> correspondiente.",
|
||||||
"lastSeen": "visto por última vez {{when}}",
|
"lastSeen": "visto por última vez {{when}}",
|
||||||
"lastSeenTitle": "Visto por última vez {{when}}",
|
"lastSeenTitle": "Visto por última vez {{when}}",
|
||||||
"neverConnected": "nunca conectado",
|
"neverConnected": "nunca conectado",
|
||||||
@@ -1078,7 +1078,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "Acerca de MCP",
|
"aboutTitle": "Acerca de MCP",
|
||||||
"aboutBody": "El Model Context Protocol permite que tu agente de programación con IA —Claude Code, Cursor, Windsurf— llame a las herramientas de Voicebox. Habla con una voz clonada, transcribe audio, explora capturas.",
|
"aboutBody": "El Model Context Protocol permite que tu agente de programación con IA —Claude Code, Cursor, Windsurf— llame a las herramientas de TalkBox. Habla con una voz clonada, transcribe audio, explora capturas.",
|
||||||
"toolsTitle": "Herramientas disponibles",
|
"toolsTitle": "Herramientas disponibles",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "Pronuncia texto con un perfil de voz.",
|
"speak": "Pronuncia texto con un perfil de voz.",
|
||||||
@@ -1135,7 +1135,7 @@
|
|||||||
"deleteCuda": "Error al eliminar el backend CUDA",
|
"deleteCuda": "Error al eliminar el backend CUDA",
|
||||||
"deleteRocm": "Error al eliminar el backend ROCm"
|
"deleteRocm": "Error al eliminar el backend ROCm"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox detecta y usa automáticamente la mejor GPU disponible en tu sistema. En Macs con Apple Silicon, el backend MLX se ejecuta de forma nativa en el Neural Engine y la GPU mediante Metal Performance Shaders (MPS), sin configuración adicional. En Windows, puedes descargar backends opcionales CUDA (NVIDIA) o ROCm (AMD) para inferencia acelerada por hardware. Intel XPU y DirectML también son compatibles cuando están disponibles a través de PyTorch. Cuando no se detecta ninguna GPU, Voicebox recurre a la CPU: todos los motores siguen funcionando, solo que más despacio.",
|
"footer": "TalkBox detecta y usa automáticamente la mejor GPU disponible en tu sistema. En Macs con Apple Silicon, el backend MLX se ejecuta de forma nativa en el Neural Engine y la GPU mediante Metal Performance Shaders (MPS), sin configuración adicional. En Windows, puedes descargar backends opcionales CUDA (NVIDIA) o ROCm (AMD) para inferencia acelerada por hardware. Intel XPU y DirectML también son compatibles cuando están disponibles a través de PyTorch. Cuando no se detecta ninguna GPU, TalkBox recurre a la CPU: todos los motores siguen funcionando, solo que más despacio.",
|
||||||
"activeBackend": {
|
"activeBackend": {
|
||||||
"description": "La aceleración por GPU está habilitada actualmente."
|
"description": "La aceleración por GPU está habilitada actualmente."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "Autorisation de surveillance d'entrée",
|
"label": "Autorisation de surveillance d'entrée",
|
||||||
"ready": "macOS permet à Voicebox de détecter votre raccourci global.",
|
"ready": "macOS permet à TalkBox de détecter votre raccourci global.",
|
||||||
"missing": "macOS doit autoriser Voicebox à détecter le raccourci global.",
|
"missing": "macOS doit autoriser TalkBox à détecter le raccourci global.",
|
||||||
"openSettings": "Ouvrir les réglages"
|
"openSettings": "Ouvrir les réglages"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "Autorisation d'accessibilité",
|
"label": "Autorisation d'accessibilité",
|
||||||
"ready": "Voicebox peut coller les transcriptions dans d'autres applications.",
|
"ready": "TalkBox peut coller les transcriptions dans d'autres applications.",
|
||||||
"missing": "Requis pour que les transcriptions puissent être collées dans l'application active.",
|
"missing": "Requis pour que les transcriptions puissent être collées dans l'application active.",
|
||||||
"openSettings": "Ouvrir les réglages"
|
"openSettings": "Ouvrir les réglages"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "Accordez l'autorisation d'accessibilité pour activer le collage automatique",
|
"title": "Accordez l'autorisation d'accessibilité pour activer le collage automatique",
|
||||||
"body": "Voicebox a besoin de <path>Réglages Système → Confidentialité et Sécurité → Accessibilité</path> pour coller les transcriptions dans d'autres applications. Vos dictées arriveront quand même dans l'onglet Captures sans cela.",
|
"body": "TalkBox a besoin de <path>Réglages Système → Confidentialité et Sécurité → Accessibilité</path> pour coller les transcriptions dans d'autres applications. Vos dictées arriveront quand même dans l'onglet Captures sans cela.",
|
||||||
"openSettings": "Ouvrir les réglages",
|
"openSettings": "Ouvrir les réglages",
|
||||||
"recheck": "Je l'ai activé",
|
"recheck": "Je l'ai activé",
|
||||||
"rechecking": "Vérification…",
|
"rechecking": "Vérification…",
|
||||||
"stillMissing": "Toujours pas détecté. macOS nécessite généralement de quitter et rouvrir Voicebox après avoir activé l'autorisation."
|
"stillMissing": "Toujours pas détecté. macOS nécessite généralement de quitter et rouvrir TalkBox après avoir activé l'autorisation."
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "Accordez la surveillance d'entrée pour activer le raccourci global",
|
"title": "Accordez la surveillance d'entrée pour activer le raccourci global",
|
||||||
"body": "Voicebox a besoin de <path>Réglages Système → Confidentialité et Sécurité → Surveillance d'entrée</path> pour détecter votre combinaison de dictée. L'interrupteur est activé, mais macOS bloque les événements clavier tant que vous ne l'autorisez pas.",
|
"body": "TalkBox a besoin de <path>Réglages Système → Confidentialité et Sécurité → Surveillance d'entrée</path> pour détecter votre combinaison de dictée. L'interrupteur est activé, mais macOS bloque les événements clavier tant que vous ne l'autorisez pas.",
|
||||||
"openSettings": "Ouvrir les réglages",
|
"openSettings": "Ouvrir les réglages",
|
||||||
"recheck": "Je l'ai activé",
|
"recheck": "Je l'ai activé",
|
||||||
"rechecking": "Vérification…",
|
"rechecking": "Vérification…",
|
||||||
"stillMissing": "Toujours pas détecté. macOS nécessite généralement de quitter et rouvrir Voicebox après avoir activé l'autorisation."
|
"stillMissing": "Toujours pas détecté. macOS nécessite généralement de quitter et rouvrir TalkBox après avoir activé l'autorisation."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "Créer une voix",
|
"createVoice": "Créer une voix",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "Type de fichier invalide",
|
"invalidTitle": "Type de fichier invalide",
|
||||||
"invalidDescription": "Veuillez sélectionner un fichier .voicebox.zip valide",
|
"invalidDescription": "Veuillez sélectionner un fichier .talkbox.zip valide",
|
||||||
"successTitle": "Profil importé",
|
"successTitle": "Profil importé",
|
||||||
"successDescription": "Profil vocal importé avec succès",
|
"successDescription": "Profil vocal importé avec succès",
|
||||||
"failedTitle": "Échec de l'importation du profil",
|
"failedTitle": "Échec de l'importation du profil",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "Langue",
|
"label": "Langue",
|
||||||
"description": "Choisissez la langue d'affichage de Voicebox."
|
"description": "Choisissez la langue d'affichage de TalkBox."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "Thème",
|
"label": "Thème",
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
"discord": { "title": "Rejoindre le Discord", "subtitle": "Obtenez de l'aide et partagez des voix" },
|
"discord": { "title": "Rejoindre le Discord", "subtitle": "Obtenez de l'aide et partagez des voix" },
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "URL du serveur",
|
"title": "URL du serveur",
|
||||||
"description": "L'adresse de votre serveur Voicebox.",
|
"description": "L'adresse de votre serveur TalkBox.",
|
||||||
"invalidUrl": "Veuillez saisir une URL valide",
|
"invalidUrl": "Veuillez saisir une URL valide",
|
||||||
"updatedTitle": "URL du serveur mise à jour",
|
"updatedTitle": "URL du serveur mise à jour",
|
||||||
"updatedDescription": "Connecté à {{url}}"
|
"updatedDescription": "Connecté à {{url}}"
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "Accès API",
|
"title": "Accès API",
|
||||||
"description": "Intégrez Voicebox dans votre flux de travail via l'API REST à <code>{{url}}</code>",
|
"description": "Intégrez TalkBox dans votre flux de travail via l'API REST à <code>{{url}}</code>",
|
||||||
"viewReference": "Voir la référence API complète",
|
"viewReference": "Voir la référence API complète",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "Générer de la parole",
|
"generate": "Générer de la parole",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "Coller automatiquement dans le champ de texte actif",
|
"title": "Coller automatiquement dans le champ de texte actif",
|
||||||
"description": "Si un champ de texte est actif dans une autre application, collez-y directement. Voicebox sauvegarde et restaure le contenu de votre presse-papiers."
|
"description": "Si un champ de texte est actif dans une autre application, collez-y directement. TalkBox sauvegarde et restaure le contenu de votre presse-papiers."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -916,7 +916,7 @@
|
|||||||
"description": "Choisissez quel modèle de reconnaissance vocale est utilisé pour vos captures.",
|
"description": "Choisissez quel modèle de reconnaissance vocale est utilisé pour vos captures.",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Modèle de transcription",
|
"title": "Modèle de transcription",
|
||||||
"description": "Whisper est fourni avec Voicebox et fonctionne entièrement sur votre machine.",
|
"description": "Whisper est fourni avec TalkBox et fonctionne entièrement sur votre machine.",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "Stockage",
|
"title": "Stockage",
|
||||||
"description": "Les captures sont sauvegardées sous forme de paires audio/transcription dans votre répertoire de données Voicebox.",
|
"description": "Les captures sont sauvegardées sous forme de paires audio/transcription dans votre répertoire de données TalkBox.",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "Rétention",
|
"title": "Rétention",
|
||||||
"description": "Durée de conservation des captures. S'applique à l'audio et aux transcriptions.",
|
"description": "Durée de conservation des captures. S'applique à l'audio et aux transcriptions.",
|
||||||
@@ -1009,7 +1009,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "À propos des Captures",
|
"aboutTitle": "À propos des Captures",
|
||||||
"aboutBody": "Maintenez un raccourci n'importe où sur votre machine, parlez, et Voicebox transforme votre voix en texte. Rejouez-la avec n'importe quelle voix clonée, collez-la dans n'importe quelle application, ou transmettez-la à votre agent de codage.",
|
"aboutBody": "Maintenez un raccourci n'importe où sur votre machine, parlez, et TalkBox transforme votre voix en texte. Rejouez-la avec n'importe quelle voix clonée, collez-la dans n'importe quelle application, ou transmettez-la à votre agent de codage.",
|
||||||
"differencesTitle": "Ce qui est différent",
|
"differencesTitle": "Ce qui est différent",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "Entièrement local.",
|
"title": "Entièrement local.",
|
||||||
@@ -1025,14 +1025,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Attention sur Windows",
|
"title": "Attention sur Windows",
|
||||||
"body": "Le raccourci ne fonctionne pas lorsque Voicebox ou une application en mode administrateur est active. En cours d'amélioration."
|
"body": "Le raccourci ne fonctionne pas lorsque TalkBox ou une application en mode administrateur est active. En cours d'amélioration."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "Installer dans votre agent",
|
"title": "Installer dans votre agent",
|
||||||
"description": "Voicebox expose un serveur MCP local dès que l'application est ouverte. Collez l'un de ces extraits dans la configuration MCP de votre agent.",
|
"description": "TalkBox expose un serveur MCP local dès que l'application est ouverte. Collez l'un de ces extraits dans la configuration MCP de votre agent.",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP (recommandé)",
|
"title": "HTTP (recommandé)",
|
||||||
"description": "Pour les clients qui parlent HTTP MCP — Claude Code, Cursor, Windsurf, VS Code."
|
"description": "Pour les clients qui parlent HTTP MCP — Claude Code, Cursor, Windsurf, VS Code."
|
||||||
@@ -1050,15 +1050,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "Voix par défaut",
|
"title": "Voix par défaut",
|
||||||
"description": "Utilisée quand un agent appelle voicebox.speak sans profil spécifique et sans liaison par client.",
|
"description": "Utilisée quand un agent appelle talkbox.speak sans profil spécifique et sans liaison par client.",
|
||||||
"label": "Voix de lecture par défaut",
|
"label": "Voix de lecture par défaut",
|
||||||
"labelHint": "Partagé avec la liste déroulante « Lire avec » de l'onglet Captures — une voix par défaut pour la lecture passive.",
|
"labelHint": "Partagé avec la liste déroulante « Lire avec » de l'onglet Captures — une voix par défaut pour la lecture passive.",
|
||||||
"none": "(aucune)"
|
"none": "(aucune)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "Voix par agent",
|
"title": "Voix par agent",
|
||||||
"description": "Liez des agents spécifiques à des voix spécifiques pour savoir qui parle sans regarder. L'agent s'identifie via l'en-tête X-Voicebox-Client-Id (ou la variable d'env VOICEBOX_CLIENT_ID pour stdio).",
|
"description": "Liez des agents spécifiques à des voix spécifiques pour savoir qui parle sans regarder. L'agent s'identifie via l'en-tête X-TalkBox-Client-Id (ou la variable d'env TALKBOX_CLIENT_ID pour stdio).",
|
||||||
"empty": "Encore aucune liaison. Ajoutez-en une ci-dessous, puis configurez votre client MCP pour envoyer le <code>X-Voicebox-Client-Id</code> correspondant.",
|
"empty": "Encore aucune liaison. Ajoutez-en une ci-dessous, puis configurez votre client MCP pour envoyer le <code>X-TalkBox-Client-Id</code> correspondant.",
|
||||||
"lastSeen": "vu {{when}}",
|
"lastSeen": "vu {{when}}",
|
||||||
"lastSeenTitle": "Dernière vue {{when}}",
|
"lastSeenTitle": "Dernière vue {{when}}",
|
||||||
"neverConnected": "jamais connecté",
|
"neverConnected": "jamais connecté",
|
||||||
@@ -1073,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "À propos de MCP",
|
"aboutTitle": "À propos de MCP",
|
||||||
"aboutBody": "Le Model Context Protocol permet à votre agent de codage IA — Claude Code, Cursor, Windsurf — d'appeler les outils de Voicebox. Parlez avec une voix clonée, transcrivez de l'audio, parcourez les captures.",
|
"aboutBody": "Le Model Context Protocol permet à votre agent de codage IA — Claude Code, Cursor, Windsurf — d'appeler les outils de TalkBox. Parlez avec une voix clonée, transcrivez de l'audio, parcourez les captures.",
|
||||||
"toolsTitle": "Outils disponibles",
|
"toolsTitle": "Outils disponibles",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "Prononcer un texte avec un profil vocal.",
|
"speak": "Prononcer un texte avec un profil vocal.",
|
||||||
@@ -1128,7 +1128,7 @@
|
|||||||
"switchCpu": "Échec du basculement vers CPU",
|
"switchCpu": "Échec du basculement vers CPU",
|
||||||
"deleteCuda": "Échec de la suppression du backend CUDA"
|
"deleteCuda": "Échec de la suppression du backend CUDA"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox détecte et utilise automatiquement le meilleur GPU disponible sur votre système. Sur les Mac Apple Silicon, le backend MLX fonctionne nativement sur le Neural Engine et le GPU via Metal Performance Shaders (MPS), sans configuration supplémentaire. Sur Windows et Linux avec GPU NVIDIA, vous pouvez télécharger un backend CUDA optionnel pour l'inférence accélérée par le matériel. AMD ROCm, Intel XPU et DirectML sont également pris en charge là où disponibles via PyTorch. Quand aucun GPU n'est détecté, Voicebox utilise le CPU — tous les moteurs fonctionnent, mais plus lentement."
|
"footer": "TalkBox détecte et utilise automatiquement le meilleur GPU disponible sur votre système. Sur les Mac Apple Silicon, le backend MLX fonctionne nativement sur le Neural Engine et le GPU via Metal Performance Shaders (MPS), sans configuration supplémentaire. Sur Windows et Linux avec GPU NVIDIA, vous pouvez télécharger un backend CUDA optionnel pour l'inférence accélérée par le matériel. AMD ROCm, Intel XPU et DirectML sont également pris en charge là où disponibles via PyTorch. Quand aucun GPU n'est détecté, TalkBox utilise le CPU — tous les moteurs fonctionnent, mais plus lentement."
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"title": "Journaux du serveur",
|
"title": "Journaux du serveur",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "Permesso Monitoraggio input",
|
"label": "Permesso Monitoraggio input",
|
||||||
"ready": "macOS consente a Voicebox di rilevare la tua scorciatoia globale.",
|
"ready": "macOS consente a TalkBox di rilevare la tua scorciatoia globale.",
|
||||||
"missing": "macOS deve consentire a Voicebox di rilevare la scorciatoia globale.",
|
"missing": "macOS deve consentire a TalkBox di rilevare la scorciatoia globale.",
|
||||||
"openSettings": "Apri Impostazioni"
|
"openSettings": "Apri Impostazioni"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "Permesso Accessibilità",
|
"label": "Permesso Accessibilità",
|
||||||
"ready": "Voicebox può incollare le trascrizioni in altre app.",
|
"ready": "TalkBox può incollare le trascrizioni in altre app.",
|
||||||
"missing": "Richiesto per poter incollare le trascrizioni nell'app in primo piano.",
|
"missing": "Richiesto per poter incollare le trascrizioni nell'app in primo piano.",
|
||||||
"openSettings": "Apri Impostazioni"
|
"openSettings": "Apri Impostazioni"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "Concedi il permesso di Accessibilità per attivare l'incollo automatico",
|
"title": "Concedi il permesso di Accessibilità per attivare l'incollo automatico",
|
||||||
"body": "Voicebox richiede <path>Impostazioni di Sistema → Privacy e Sicurezza → Accessibilità</path> per incollare le trascrizioni in altre app. Senza di questo, la tua dettatura verrà comunque salvata nella scheda Acquisizioni.",
|
"body": "TalkBox richiede <path>Impostazioni di Sistema → Privacy e Sicurezza → Accessibilità</path> per incollare le trascrizioni in altre app. Senza di questo, la tua dettatura verrà comunque salvata nella scheda Acquisizioni.",
|
||||||
"openSettings": "Apri Impostazioni",
|
"openSettings": "Apri Impostazioni",
|
||||||
"recheck": "L'ho abilitato",
|
"recheck": "L'ho abilitato",
|
||||||
"rechecking": "Verifica in corso…",
|
"rechecking": "Verifica in corso…",
|
||||||
"stillMissing": "Non ancora rilevato. Di solito macOS richiede la chiusura e la riapertura di Voicebox dopo aver attivato il permesso."
|
"stillMissing": "Non ancora rilevato. Di solito macOS richiede la chiusura e la riapertura di TalkBox dopo aver attivato il permesso."
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "Concedi il Monitoraggio input per attivare la scorciatoia globale",
|
"title": "Concedi il Monitoraggio input per attivare la scorciatoia globale",
|
||||||
"body": "Voicebox richiede <path>Impostazioni di Sistema → Privacy e Sicurezza → Monitoraggio input</path> per rilevare la tua combinazione di dettatura. L'opzione è attiva, ma macOS sta bloccando gli eventi della tastiera finché non lo consenti.",
|
"body": "TalkBox richiede <path>Impostazioni di Sistema → Privacy e Sicurezza → Monitoraggio input</path> per rilevare la tua combinazione di dettatura. L'opzione è attiva, ma macOS sta bloccando gli eventi della tastiera finché non lo consenti.",
|
||||||
"openSettings": "Opzioni Impostazioni",
|
"openSettings": "Opzioni Impostazioni",
|
||||||
"recheck": "L'ho abilitato",
|
"recheck": "L'ho abilitato",
|
||||||
"rechecking": "Verifica in corso…",
|
"rechecking": "Verifica in corso…",
|
||||||
"stillMissing": "Non ancora rilevato. Di solito macOS richiede la chiusura e la riapertura di Voicebox dopo aver attivato il permesso."
|
"stillMissing": "Non ancora rilevato. Di solito macOS richiede la chiusura e la riapertura di TalkBox dopo aver attivato il permesso."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "Crea voce",
|
"createVoice": "Crea voce",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "Tipo di file non valido",
|
"invalidTitle": "Tipo di file non valido",
|
||||||
"invalidDescription": "Seleziona un file .voicebox.zip valido",
|
"invalidDescription": "Seleziona un file .talkbox.zip valido",
|
||||||
"successTitle": "Profilo importato",
|
"successTitle": "Profilo importato",
|
||||||
"successDescription": "Profilo vocale importato con successo",
|
"successDescription": "Profilo vocale importato con successo",
|
||||||
"failedTitle": "Impossibile importare il profilo",
|
"failedTitle": "Impossibile importare il profilo",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "Lingua",
|
"label": "Lingua",
|
||||||
"description": "Scegli la lingua di visualizzazione di Voicebox."
|
"description": "Scegli la lingua di visualizzazione di TalkBox."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "Tema",
|
"label": "Tema",
|
||||||
@@ -769,7 +769,7 @@
|
|||||||
},
|
},
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "URL del server",
|
"title": "URL del server",
|
||||||
"description": "L'indirizzo del server backend di Voicebox.",
|
"description": "L'indirizzo del server backend di TalkBox.",
|
||||||
"invalidUrl": "Inserisci un URL valido",
|
"invalidUrl": "Inserisci un URL valido",
|
||||||
"updatedTitle": "URL del server aggiornato",
|
"updatedTitle": "URL del server aggiornato",
|
||||||
"updatedDescription": "Connesso a {{url}}"
|
"updatedDescription": "Connesso a {{url}}"
|
||||||
@@ -824,7 +824,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "Accesso API",
|
"title": "Accesso API",
|
||||||
"description": "Integra Voicebox nel tuo flusso di lavoro tramite l'API REST all'indirizzo <code>{{url}}</code>",
|
"description": "Integra TalkBox nel tuo flusso di lavoro tramite l'API REST all'indirizzo <code>{{url}}</code>",
|
||||||
"viewReference": "Visualizza il riferimento API completo",
|
"viewReference": "Visualizza il riferimento API completo",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "Genera testo parlato",
|
"generate": "Genera testo parlato",
|
||||||
@@ -913,7 +913,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "Incollo automatico nel campo di testo attivo",
|
"title": "Incollo automatico nel campo di testo attivo",
|
||||||
"description": "Se un campo di inserimento testo è attivo in un'altra app, incolla direttamente al suo interno. Voicebox salva e ripristina il contenuto precedente dei tuoi appunti."
|
"description": "Se un campo di inserimento testo è attivo in un'altra app, incolla direttamente al suo interno. TalkBox salva e ripristina il contenuto precedente dei tuoi appunti."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -921,7 +921,7 @@
|
|||||||
"description": "Scegli quale modello di riconoscimento vocale (speech-to-text) eseguire sulle tue acquisizioni.",
|
"description": "Scegli quale modello di riconoscimento vocale (speech-to-text) eseguire sulle tue acquisizioni.",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Modello di trascrizione",
|
"title": "Modello di trascrizione",
|
||||||
"description": "Whisper è integrato in Voicebox ed è eseguito interamente sul tuo computer.",
|
"description": "Whisper è integrato in TalkBox ed è eseguito interamente sul tuo computer.",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -997,7 +997,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "Archiviazione",
|
"title": "Archiviazione",
|
||||||
"description": "Le acquisizioni vengono salvate come file audio e di trascrizione accoppiati nella cartella dei dati di Voicebox.",
|
"description": "Le acquisizioni vengono salvate come file audio e di trascrizione accoppiati nella cartella dei dati di TalkBox.",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "Conservazione",
|
"title": "Conservazione",
|
||||||
"description": "Per quanto tempo conservare le acquisizioni. Si applica sia all'audio che alle trascrizioni.",
|
"description": "Per quanto tempo conservare le acquisizioni. Si applica sia all'audio che alle trascrizioni.",
|
||||||
@@ -1014,7 +1014,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "Informazioni sulle Acquisizioni",
|
"aboutTitle": "Informazioni sulle Acquisizioni",
|
||||||
"aboutBody": "Tieni premuta una scorciatoia in qualsiasi punto del tuo computer, parla e Voicebox trasformerà la tua voce in testo. Riproducilo con qualsiasi voce clonata, incollalo in qualsiasi app o invialo direttamente al tuo agente di programmazione.",
|
"aboutBody": "Tieni premuta una scorciatoia in qualsiasi punto del tuo computer, parla e TalkBox trasformerà la tua voce in testo. Riproducilo con qualsiasi voce clonata, incollalo in qualsiasi app o invialo direttamente al tuo agente di programmazione.",
|
||||||
"differencesTitle": "Cosa cambia",
|
"differencesTitle": "Cosa cambia",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "Interamente locale.",
|
"title": "Interamente locale.",
|
||||||
@@ -1030,14 +1030,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Attenzione su Windows",
|
"title": "Attenzione su Windows",
|
||||||
"body": "La scorciatoia non si attiverà mentre Voicebox stesso o qualsiasi applicazione eseguita come amministratore è in primo piano. Ci stiamo lavorando."
|
"body": "La scorciatoia non si attiverà mentre TalkBox stesso o qualsiasi applicazione eseguita come amministratore è in primo piano. Ci stiamo lavorando."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "Installa nel tuo agente",
|
"title": "Installa nel tuo agente",
|
||||||
"description": "Voicebox espone un server MCP locale ogni volta che l'app è aperta. Incolla uno di questi frammenti nella configurazione MCP del tuo agente.",
|
"description": "TalkBox espone un server MCP locale ogni volta che l'app è aperta. Incolla uno di questi frammenti nella configurazione MCP del tuo agente.",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP (consigliato)",
|
"title": "HTTP (consigliato)",
|
||||||
"description": "Per i client che supportano HTTP MCP — Claude Code, Cursor, Windsurf, VS Code."
|
"description": "Per i client che supportano HTTP MCP — Claude Code, Cursor, Windsurf, VS Code."
|
||||||
@@ -1055,15 +1055,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "Voce predefinita",
|
"title": "Voce predefinita",
|
||||||
"description": "Utilizzata quando un agente chiama voicebox.speak senza specificare un profilo e non ha un'associazione per singolo client.",
|
"description": "Utilizzata quando un agente chiama talkbox.speak senza specificare un profilo e non ha un'associazione per singolo client.",
|
||||||
"label": "Voce di riproduzione predefinita",
|
"label": "Voce di riproduzione predefinita",
|
||||||
"labelHint": "Condivisa con il menu a discesa 'Riproduci come voce' della scheda Acquisizioni — una sola voce predefinita per la riproduzione passiva.",
|
"labelHint": "Condivisa con il menu a discesa 'Riproduci come voce' della scheda Acquisizioni — una sola voce predefinita per la riproduzione passiva.",
|
||||||
"none": "(nessuna)"
|
"none": "(nessuna)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "Voce per singolo agente",
|
"title": "Voce per singolo agente",
|
||||||
"description": "Associa agenti specifici a voci specifiche, in modo da poter capire chi sta parlando senza guardare lo schermo. L'agente si identifica tramite l'intestazione X-Voicebox-Client-Id (o la variabile d'ambiente VOICEBOX_CLIENT_ID per stdio).",
|
"description": "Associa agenti specifici a voci specifiche, in modo da poter capire chi sta parlando senza guardare lo schermo. L'agente si identifica tramite l'intestazione X-TalkBox-Client-Id (o la variabile d'ambiente TALKBOX_CLIENT_ID per stdio).",
|
||||||
"empty": "Ancora nessuna associazione. Aggiungine una qui sotto, quindi configura il tuo client MCP per inviare il corrispondente <code>X-Voicebox-Client-Id</code>.",
|
"empty": "Ancora nessuna associazione. Aggiungine una qui sotto, quindi configura il tuo client MCP per inviare il corrispondente <code>X-TalkBox-Client-Id</code>.",
|
||||||
"lastSeen": "ultimo rilevamento {{when}}",
|
"lastSeen": "ultimo rilevamento {{when}}",
|
||||||
"lastSeenTitle": "Ultimo rilevamento {{when}}",
|
"lastSeenTitle": "Ultimo rilevamento {{when}}",
|
||||||
"neverConnected": "mai connesso",
|
"neverConnected": "mai connesso",
|
||||||
@@ -1078,7 +1078,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "Informazioni su MCP",
|
"aboutTitle": "Informazioni su MCP",
|
||||||
"aboutBody": "Il protocollo Model Context Protocol consente al tuo agente di programmazione IA — Claude Code, Cursor, Windsurf — di chiamare gli strumenti di Voicebox. Parla con una voce clonata, trascrivi tracce audio, sfoglia le acquisizioni.",
|
"aboutBody": "Il protocollo Model Context Protocol consente al tuo agente di programmazione IA — Claude Code, Cursor, Windsurf — di chiamare gli strumenti di TalkBox. Parla con una voce clonata, trascrivi tracce audio, sfoglia le acquisizioni.",
|
||||||
"toolsTitle": "Strumenti disponibili",
|
"toolsTitle": "Strumenti disponibili",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "Pronuncia il testo all'interno di un profilo vocale.",
|
"speak": "Pronuncia il testo all'interno di un profilo vocale.",
|
||||||
@@ -1137,7 +1137,7 @@
|
|||||||
"deleteCuda": "Impossibile eliminare il backend CUDA",
|
"deleteCuda": "Impossibile eliminare il backend CUDA",
|
||||||
"deleteRocm": "Impossibile eliminare il backend ROCm"
|
"deleteRocm": "Impossibile eliminare il backend ROCm"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox rileva e utilizza automaticamente la migliore GPU disponibile sul tuo sistema. Sui Mac con chip Apple Silicon, il backend MLX viene eseguito nativamente sul Neural Engine e sulla GPU tramite Metal Performance Shaders (MPS), senza richiedere alcuna configurazione aggiuntiva. Su Windows, puoi scaricare i backend opzionali CUDA (NVIDIA) o ROCm (AMD) per l'inferenza con accelerazione hardware. Dove disponibili tramite PyTorch, sono supportati anche Intel XPU e DirectML. Quando non viene rilevata alcuna GPU, Voicebox si affida alla CPU — tutti i motori continuano a funzionare, solo più lentamente.",
|
"footer": "TalkBox rileva e utilizza automaticamente la migliore GPU disponibile sul tuo sistema. Sui Mac con chip Apple Silicon, il backend MLX viene eseguito nativamente sul Neural Engine e sulla GPU tramite Metal Performance Shaders (MPS), senza richiedere alcuna configurazione aggiuntiva. Su Windows, puoi scaricare i backend opzionali CUDA (NVIDIA) o ROCm (AMD) per l'inferenza con accelerazione hardware. Dove disponibili tramite PyTorch, sono supportati anche Intel XPU e DirectML. Quando non viene rilevata alcuna GPU, TalkBox si affida alla CPU — tutti i motori continuano a funzionare, solo più lentamente.",
|
||||||
"rocm": {
|
"rocm": {
|
||||||
"title": "Backend AMD ROCm",
|
"title": "Backend AMD ROCm",
|
||||||
"activeTitle": "Backend ROCm attivo",
|
"activeTitle": "Backend ROCm attivo",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "入力監視の権限",
|
"label": "入力監視の権限",
|
||||||
"ready": "macOS が Voicebox にグローバルショートカットの検出を許可しています。",
|
"ready": "macOS が TalkBox にグローバルショートカットの検出を許可しています。",
|
||||||
"missing": "macOS で Voicebox にグローバルショートカットの検出を許可する必要があります。",
|
"missing": "macOS で TalkBox にグローバルショートカットの検出を許可する必要があります。",
|
||||||
"openSettings": "設定を開く"
|
"openSettings": "設定を開く"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "アクセシビリティの権限",
|
"label": "アクセシビリティの権限",
|
||||||
"ready": "Voicebox が他のアプリに文字起こしを貼り付けできます。",
|
"ready": "TalkBox が他のアプリに文字起こしを貼り付けできます。",
|
||||||
"missing": "フォーカス中のアプリに文字起こしを貼り付けるために必要です。",
|
"missing": "フォーカス中のアプリに文字起こしを貼り付けるために必要です。",
|
||||||
"openSettings": "設定を開く"
|
"openSettings": "設定を開く"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "自動貼り付けを有効にするためアクセシビリティの権限を付与してください",
|
"title": "自動貼り付けを有効にするためアクセシビリティの権限を付与してください",
|
||||||
"body": "他のアプリに文字起こしを貼り付けるには、Voicebox に <path>「システム設定」→「プライバシーとセキュリティ」→「アクセシビリティ」</path> の許可が必要です。許可がなくてもディクテーションはキャプチャタブに保存されます。",
|
"body": "他のアプリに文字起こしを貼り付けるには、TalkBox に <path>「システム設定」→「プライバシーとセキュリティ」→「アクセシビリティ」</path> の許可が必要です。許可がなくてもディクテーションはキャプチャタブに保存されます。",
|
||||||
"openSettings": "設定を開く",
|
"openSettings": "設定を開く",
|
||||||
"recheck": "有効にしました",
|
"recheck": "有効にしました",
|
||||||
"rechecking": "確認中…",
|
"rechecking": "確認中…",
|
||||||
"stillMissing": "まだ検出されません。macOS では権限を切り替えた後、Voicebox を終了して再起動する必要があります。"
|
"stillMissing": "まだ検出されません。macOS では権限を切り替えた後、TalkBox を終了して再起動する必要があります。"
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "グローバルショートカットを有効にするため入力監視の権限を付与してください",
|
"title": "グローバルショートカットを有効にするため入力監視の権限を付与してください",
|
||||||
"body": "ディクテーションのコードを検出するには、Voicebox に <path>「システム設定」→「プライバシーとセキュリティ」→「入力監視」</path> の許可が必要です。トグルは有効ですが、許可されるまで macOS がキーイベントをブロックしています。",
|
"body": "ディクテーションのコードを検出するには、TalkBox に <path>「システム設定」→「プライバシーとセキュリティ」→「入力監視」</path> の許可が必要です。トグルは有効ですが、許可されるまで macOS がキーイベントをブロックしています。",
|
||||||
"openSettings": "設定を開く",
|
"openSettings": "設定を開く",
|
||||||
"recheck": "有効にしました",
|
"recheck": "有効にしました",
|
||||||
"rechecking": "確認中…",
|
"rechecking": "確認中…",
|
||||||
"stillMissing": "まだ検出されません。macOS では権限を切り替えた後、Voicebox を終了して再起動する必要があります。"
|
"stillMissing": "まだ検出されません。macOS では権限を切り替えた後、TalkBox を終了して再起動する必要があります。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "ボイスを作成",
|
"createVoice": "ボイスを作成",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "無効なファイル形式",
|
"invalidTitle": "無効なファイル形式",
|
||||||
"invalidDescription": "有効な .voicebox.zip ファイルを選択してください",
|
"invalidDescription": "有効な .talkbox.zip ファイルを選択してください",
|
||||||
"successTitle": "プロファイルをインポートしました",
|
"successTitle": "プロファイルをインポートしました",
|
||||||
"successDescription": "ボイスプロファイルを正常にインポートしました",
|
"successDescription": "ボイスプロファイルを正常にインポートしました",
|
||||||
"failedTitle": "プロファイルのインポートに失敗しました",
|
"failedTitle": "プロファイルのインポートに失敗しました",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "言語",
|
"label": "言語",
|
||||||
"description": "Voicebox の表示言語を選択します。"
|
"description": "TalkBox の表示言語を選択します。"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "テーマ",
|
"label": "テーマ",
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
"discord": { "title": "Discord に参加", "subtitle": "ヘルプやボイスの共有" },
|
"discord": { "title": "Discord に参加", "subtitle": "ヘルプやボイスの共有" },
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "サーバー URL",
|
"title": "サーバー URL",
|
||||||
"description": "Voicebox バックエンドサーバーのアドレス。",
|
"description": "TalkBox バックエンドサーバーのアドレス。",
|
||||||
"invalidUrl": "有効な URL を入力してください",
|
"invalidUrl": "有効な URL を入力してください",
|
||||||
"updatedTitle": "サーバー URL を更新しました",
|
"updatedTitle": "サーバー URL を更新しました",
|
||||||
"updatedDescription": "{{url}} に接続しました"
|
"updatedDescription": "{{url}} に接続しました"
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "API アクセス",
|
"title": "API アクセス",
|
||||||
"description": "<code>{{url}}</code> の REST API を通じて Voicebox をワークフローに統合できます",
|
"description": "<code>{{url}}</code> の REST API を通じて TalkBox をワークフローに統合できます",
|
||||||
"viewReference": "API リファレンス全文を表示",
|
"viewReference": "API リファレンス全文を表示",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "音声を生成",
|
"generate": "音声を生成",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "フォーカス中のテキストフィールドに自動貼り付け",
|
"title": "フォーカス中のテキストフィールドに自動貼り付け",
|
||||||
"description": "他のアプリでテキスト入力欄がフォーカスされている場合、直接そこに貼り付けます。Voicebox はクリップボードの内容を一旦保存し、後で復元します。"
|
"description": "他のアプリでテキスト入力欄がフォーカスされている場合、直接そこに貼り付けます。TalkBox はクリップボードの内容を一旦保存し、後で復元します。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -916,7 +916,7 @@
|
|||||||
"description": "キャプチャに使う音声認識モデルを選びます。",
|
"description": "キャプチャに使う音声認識モデルを選びます。",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "文字起こしモデル",
|
"title": "文字起こしモデル",
|
||||||
"description": "Whisper は Voicebox に同梱されており、すべてマシン上で動作します。",
|
"description": "Whisper は TalkBox に同梱されており、すべてマシン上で動作します。",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "ストレージ",
|
"title": "ストレージ",
|
||||||
"description": "キャプチャは Voicebox のデータディレクトリに、音声と文字起こしのペアファイルとして保存されます。",
|
"description": "キャプチャは TalkBox のデータディレクトリに、音声と文字起こしのペアファイルとして保存されます。",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "保持期間",
|
"title": "保持期間",
|
||||||
"description": "キャプチャを保持する期間です。音声と文字起こしの両方に適用されます。",
|
"description": "キャプチャを保持する期間です。音声と文字起こしの両方に適用されます。",
|
||||||
@@ -1009,7 +1009,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "キャプチャについて",
|
"aboutTitle": "キャプチャについて",
|
||||||
"aboutBody": "マシン上のどこからでもショートカットを押し続けて話すと、Voicebox があなたの声をテキストに変換します。クローンしたどのボイスでも再生でき、任意のアプリに貼り付けたり、コーディングエージェントに渡したりできます。",
|
"aboutBody": "マシン上のどこからでもショートカットを押し続けて話すと、TalkBox があなたの声をテキストに変換します。クローンしたどのボイスでも再生でき、任意のアプリに貼り付けたり、コーディングエージェントに渡したりできます。",
|
||||||
"differencesTitle": "ここが違います",
|
"differencesTitle": "ここが違います",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "完全にローカル。",
|
"title": "完全にローカル。",
|
||||||
@@ -1025,14 +1025,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Windows での注意点",
|
"title": "Windows での注意点",
|
||||||
"body": "Voicebox 自体や管理者として実行中のアプリにフォーカスがあるあいだは、ショートカットが反応しません。現在対応中です。"
|
"body": "TalkBox 自体や管理者として実行中のアプリにフォーカスがあるあいだは、ショートカットが反応しません。現在対応中です。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "エージェントにインストール",
|
"title": "エージェントにインストール",
|
||||||
"description": "アプリが開いている間、Voicebox はローカルで MCP サーバーを公開します。以下のスニペットを、お使いのエージェントの MCP 設定に貼り付けてください。",
|
"description": "アプリが開いている間、TalkBox はローカルで MCP サーバーを公開します。以下のスニペットを、お使いのエージェントの MCP 設定に貼り付けてください。",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP(推奨)",
|
"title": "HTTP(推奨)",
|
||||||
"description": "HTTP MCP に対応するクライアント向け — Claude Code、Cursor、Windsurf、VS Code。"
|
"description": "HTTP MCP に対応するクライアント向け — Claude Code、Cursor、Windsurf、VS Code。"
|
||||||
@@ -1050,15 +1050,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "デフォルトボイス",
|
"title": "デフォルトボイス",
|
||||||
"description": "エージェントが特定のプロファイルを指定せず、クライアントごとのバインディングもない状態で voicebox.speak を呼び出したときに使われます。",
|
"description": "エージェントが特定のプロファイルを指定せず、クライアントごとのバインディングもない状態で talkbox.speak を呼び出したときに使われます。",
|
||||||
"label": "デフォルトの再生ボイス",
|
"label": "デフォルトの再生ボイス",
|
||||||
"labelHint": "キャプチャタブの「ボイスで再生」ドロップダウンと共有 — パッシブ再生用に 1 つのデフォルトボイスを設定します。",
|
"labelHint": "キャプチャタブの「ボイスで再生」ドロップダウンと共有 — パッシブ再生用に 1 つのデフォルトボイスを設定します。",
|
||||||
"none": "(なし)"
|
"none": "(なし)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "エージェントごとのボイス",
|
"title": "エージェントごとのボイス",
|
||||||
"description": "特定のエージェントに特定のボイスを割り当てて、見なくても誰が話しているか分かるようにします。エージェントは X-Voicebox-Client-Id ヘッダー(stdio の場合は VOICEBOX_CLIENT_ID 環境変数)で自身を識別します。",
|
"description": "特定のエージェントに特定のボイスを割り当てて、見なくても誰が話しているか分かるようにします。エージェントは X-TalkBox-Client-Id ヘッダー(stdio の場合は TALKBOX_CLIENT_ID 環境変数)で自身を識別します。",
|
||||||
"empty": "バインディングはまだありません。下から追加し、対応する <code>X-Voicebox-Client-Id</code> を送信するように MCP クライアントを設定してください。",
|
"empty": "バインディングはまだありません。下から追加し、対応する <code>X-TalkBox-Client-Id</code> を送信するように MCP クライアントを設定してください。",
|
||||||
"lastSeen": "最終接続 {{when}}",
|
"lastSeen": "最終接続 {{when}}",
|
||||||
"lastSeenTitle": "最終接続 {{when}}",
|
"lastSeenTitle": "最終接続 {{when}}",
|
||||||
"neverConnected": "未接続",
|
"neverConnected": "未接続",
|
||||||
@@ -1073,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "MCP について",
|
"aboutTitle": "MCP について",
|
||||||
"aboutBody": "Model Context Protocol を使うと、Claude Code、Cursor、Windsurf などの AI コーディングエージェントから Voicebox のツールを呼び出せます。クローンしたボイスで発話したり、音声を文字起こししたり、キャプチャを参照したりできます。",
|
"aboutBody": "Model Context Protocol を使うと、Claude Code、Cursor、Windsurf などの AI コーディングエージェントから TalkBox のツールを呼び出せます。クローンしたボイスで発話したり、音声を文字起こししたり、キャプチャを参照したりできます。",
|
||||||
"toolsTitle": "利用可能なツール",
|
"toolsTitle": "利用可能なツール",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "ボイスプロファイルでテキストを発話します。",
|
"speak": "ボイスプロファイルでテキストを発話します。",
|
||||||
@@ -1128,7 +1128,7 @@
|
|||||||
"switchCpu": "CPU への切り替えに失敗しました",
|
"switchCpu": "CPU への切り替えに失敗しました",
|
||||||
"deleteCuda": "CUDA バックエンドの削除に失敗しました"
|
"deleteCuda": "CUDA バックエンドの削除に失敗しました"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox はシステムで利用可能な最適な GPU を自動で検出し使用します。Apple Silicon Mac では、MLX バックエンドが Metal Performance Shaders(MPS)を介して Neural Engine と GPU 上でネイティブに動作し、追加のセットアップは不要です。NVIDIA GPU 搭載の Windows および Linux では、オプションの CUDA バックエンドをダウンロードしてハードウェアアクセラレーションによる推論が可能です。AMD ROCm、Intel XPU、DirectML も PyTorch を通じて利用可能な環境でサポートされます。GPU が検出されない場合、Voicebox は CPU にフォールバックし、すべてのエンジンはそのまま動作しますが速度は低下します。"
|
"footer": "TalkBox はシステムで利用可能な最適な GPU を自動で検出し使用します。Apple Silicon Mac では、MLX バックエンドが Metal Performance Shaders(MPS)を介して Neural Engine と GPU 上でネイティブに動作し、追加のセットアップは不要です。NVIDIA GPU 搭載の Windows および Linux では、オプションの CUDA バックエンドをダウンロードしてハードウェアアクセラレーションによる推論が可能です。AMD ROCm、Intel XPU、DirectML も PyTorch を通じて利用可能な環境でサポートされます。GPU が検出されない場合、TalkBox は CPU にフォールバックし、すべてのエンジンはそのまま動作しますが速度は低下します。"
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"title": "サーバーログ",
|
"title": "サーバーログ",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "입력 모니터링 권한",
|
"label": "입력 모니터링 권한",
|
||||||
"ready": "macOS가 Voicebox의 전역 단축키 감지를 허용합니다.",
|
"ready": "macOS가 TalkBox의 전역 단축키 감지를 허용합니다.",
|
||||||
"missing": "macOS에서 Voicebox의 전역 단축키 감지를 허용해야 합니다.",
|
"missing": "macOS에서 TalkBox의 전역 단축키 감지를 허용해야 합니다.",
|
||||||
"openSettings": "설정 열기"
|
"openSettings": "설정 열기"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "손쉬운 사용 권한",
|
"label": "손쉬운 사용 권한",
|
||||||
"ready": "Voicebox가 다른 앱에 대본을 붙여넣을 수 있습니다.",
|
"ready": "TalkBox가 다른 앱에 대본을 붙여넣을 수 있습니다.",
|
||||||
"missing": "대본을 포커스된 앱에 붙여넣는 데 필요합니다.",
|
"missing": "대본을 포커스된 앱에 붙여넣는 데 필요합니다.",
|
||||||
"openSettings": "설정 열기"
|
"openSettings": "설정 열기"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "자동 붙여넣기를 활성화하려면 손쉬운 사용 권한을 허용하세요",
|
"title": "자동 붙여넣기를 활성화하려면 손쉬운 사용 권한을 허용하세요",
|
||||||
"body": "Voicebox가 다른 앱에 대본을 붙여넣으려면 <path>시스템 설정 → 개인정보 보호 및 보안 → 손쉬운 사용</path> 권한이 필요합니다. 권한이 없어도 받아쓰기는 캡처 탭에 저장됩니다.",
|
"body": "TalkBox가 다른 앱에 대본을 붙여넣으려면 <path>시스템 설정 → 개인정보 보호 및 보안 → 손쉬운 사용</path> 권한이 필요합니다. 권한이 없어도 받아쓰기는 캡처 탭에 저장됩니다.",
|
||||||
"openSettings": "설정 열기",
|
"openSettings": "설정 열기",
|
||||||
"recheck": "활성화했습니다",
|
"recheck": "활성화했습니다",
|
||||||
"rechecking": "확인 중…",
|
"rechecking": "확인 중…",
|
||||||
"stillMissing": "아직 감지되지 않았습니다. macOS에서는 일반적으로 권한을 켠 후 Voicebox를 종료하고 다시 열어야 합니다."
|
"stillMissing": "아직 감지되지 않았습니다. macOS에서는 일반적으로 권한을 켠 후 TalkBox를 종료하고 다시 열어야 합니다."
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "전역 단축키를 활성화하려면 입력 모니터링을 허용하세요",
|
"title": "전역 단축키를 활성화하려면 입력 모니터링을 허용하세요",
|
||||||
"body": "Voicebox가 받아쓰기 단축키를 감지하려면 <path>시스템 설정 → 개인정보 보호 및 보안 → 입력 모니터링</path> 권한이 필요합니다. 스위치가 켜져 있지만 macOS에서 키 이벤트를 차단하고 있습니다.",
|
"body": "TalkBox가 받아쓰기 단축키를 감지하려면 <path>시스템 설정 → 개인정보 보호 및 보안 → 입력 모니터링</path> 권한이 필요합니다. 스위치가 켜져 있지만 macOS에서 키 이벤트를 차단하고 있습니다.",
|
||||||
"openSettings": "설정 열기",
|
"openSettings": "설정 열기",
|
||||||
"recheck": "활성화했습니다",
|
"recheck": "활성화했습니다",
|
||||||
"rechecking": "확인 중…",
|
"rechecking": "확인 중…",
|
||||||
"stillMissing": "아직 감지되지 않았습니다. macOS에서는 일반적으로 권한을 켠 후 Voicebox를 종료하고 다시 열어야 합니다."
|
"stillMissing": "아직 감지되지 않았습니다. macOS에서는 일반적으로 권한을 켠 후 TalkBox를 종료하고 다시 열어야 합니다."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "음성 만들기",
|
"createVoice": "음성 만들기",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "잘못된 파일 형식",
|
"invalidTitle": "잘못된 파일 형식",
|
||||||
"invalidDescription": "올바른 .voicebox.zip 파일을 선택해 주세요",
|
"invalidDescription": "올바른 .talkbox.zip 파일을 선택해 주세요",
|
||||||
"successTitle": "프로필 가져오기 완료",
|
"successTitle": "프로필 가져오기 완료",
|
||||||
"successDescription": "음성 프로필을 성공적으로 가져왔습니다",
|
"successDescription": "음성 프로필을 성공적으로 가져왔습니다",
|
||||||
"failedTitle": "프로필 가져오기 실패",
|
"failedTitle": "프로필 가져오기 실패",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "언어",
|
"label": "언어",
|
||||||
"description": "Voicebox의 표시 언어를 선택하세요."
|
"description": "TalkBox의 표시 언어를 선택하세요."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "테마",
|
"label": "테마",
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
"discord": { "title": "Discord 참여하기", "subtitle": "도움말 & 음성 공유" },
|
"discord": { "title": "Discord 참여하기", "subtitle": "도움말 & 음성 공유" },
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "서버 URL",
|
"title": "서버 URL",
|
||||||
"description": "voicebox 백엔드 서버 주소입니다.",
|
"description": "talkbox 백엔드 서버 주소입니다.",
|
||||||
"invalidUrl": "올바른 URL을 입력해 주세요",
|
"invalidUrl": "올바른 URL을 입력해 주세요",
|
||||||
"updatedTitle": "서버 URL 업데이트됨",
|
"updatedTitle": "서버 URL 업데이트됨",
|
||||||
"updatedDescription": "{{url}}에 연결되었습니다"
|
"updatedDescription": "{{url}}에 연결되었습니다"
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "API 액세스",
|
"title": "API 액세스",
|
||||||
"description": "<code>{{url}}</code>에서 REST API를 통해 Voicebox를 워크플로우에 통합하세요.",
|
"description": "<code>{{url}}</code>에서 REST API를 통해 TalkBox를 워크플로우에 통합하세요.",
|
||||||
"viewReference": "전체 API 참조 보기",
|
"viewReference": "전체 API 참조 보기",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "음성 생성",
|
"generate": "음성 생성",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "포커스된 텍스트 필드에 자동 붙여넣기",
|
"title": "포커스된 텍스트 필드에 자동 붙여넣기",
|
||||||
"description": "다른 앱에서 텍스트 입력이 포커스되어 있으면 직접 붙여넣습니다. Voicebox가 클립보드 내용을 저장하고 복원합니다."
|
"description": "다른 앱에서 텍스트 입력이 포커스되어 있으면 직접 붙여넣습니다. TalkBox가 클립보드 내용을 저장하고 복원합니다."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -916,7 +916,7 @@
|
|||||||
"description": "캡처에 사용할 음성-텍스트 모델을 선택하세요.",
|
"description": "캡처에 사용할 음성-텍스트 모델을 선택하세요.",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "변환 모델",
|
"title": "변환 모델",
|
||||||
"description": "Whisper가 Voicebox에 포함되어 있으며 기기에서 완전히 실행됩니다.",
|
"description": "Whisper가 TalkBox에 포함되어 있으며 기기에서 완전히 실행됩니다.",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "저장소",
|
"title": "저장소",
|
||||||
"description": "캡처는 Voicebox 데이터 디렉토리에 오디오와 대본 파일 쌍으로 저장됩니다.",
|
"description": "캡처는 TalkBox 데이터 디렉토리에 오디오와 대본 파일 쌍으로 저장됩니다.",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "보관 기간",
|
"title": "보관 기간",
|
||||||
"description": "캡처 보관 기간입니다. 오디오와 대본 모두에 적용됩니다.",
|
"description": "캡처 보관 기간입니다. 오디오와 대본 모두에 적용됩니다.",
|
||||||
@@ -1009,7 +1009,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "캡처 정보",
|
"aboutTitle": "캡처 정보",
|
||||||
"aboutBody": "어디서든 단축키를 누르고 말하면 Voicebox가 음성을 텍스트로 변환합니다. 복제된 음성으로 재생하고, 앱에 붙여넣고, 코딩 에이전트로 보내세요.",
|
"aboutBody": "어디서든 단축키를 누르고 말하면 TalkBox가 음성을 텍스트로 변환합니다. 복제된 음성으로 재생하고, 앱에 붙여넣고, 코딩 에이전트로 보내세요.",
|
||||||
"differencesTitle": "차이점",
|
"differencesTitle": "차이점",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "완전 로컬.",
|
"title": "완전 로컬.",
|
||||||
@@ -1025,14 +1025,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Windows 참고사항",
|
"title": "Windows 참고사항",
|
||||||
"body": "Voicebox 자체 또는 관리자 권한으로 실행 중인 앱이 포커스된 경우 단축키가 작동하지 않습니다. 개선 중입니다."
|
"body": "TalkBox 자체 또는 관리자 권한으로 실행 중인 앱이 포커스된 경우 단축키가 작동하지 않습니다. 개선 중입니다."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "에이전트에 설치",
|
"title": "에이전트에 설치",
|
||||||
"description": "Voicebox는 앱이 열려 있을 때 로컬 MCP 서버를 제공합니다. 다음 중 하나를 에이전트의 MCP 설정에 붙여넣으세요.",
|
"description": "TalkBox는 앱이 열려 있을 때 로컬 MCP 서버를 제공합니다. 다음 중 하나를 에이전트의 MCP 설정에 붙여넣으세요.",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP (권장)",
|
"title": "HTTP (권장)",
|
||||||
"description": "HTTP MCP를 사용하는 클라이언트용 — Claude Code, Cursor, Windsurf, VS Code."
|
"description": "HTTP MCP를 사용하는 클라이언트용 — Claude Code, Cursor, Windsurf, VS Code."
|
||||||
@@ -1050,15 +1050,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "기본 음성",
|
"title": "기본 음성",
|
||||||
"description": "에이전트가 특정 프로필 없이 voicebox.speak를 호출하고 클라이언트별 바인딩도 없을 때 사용됩니다.",
|
"description": "에이전트가 특정 프로필 없이 talkbox.speak를 호출하고 클라이언트별 바인딩도 없을 때 사용됩니다.",
|
||||||
"label": "기본 재생 음성",
|
"label": "기본 재생 음성",
|
||||||
"labelHint": "캡처 탭의 'Play as 음성' 드롭다운과 공유 — 수동 재생용 기본 음성입니다.",
|
"labelHint": "캡처 탭의 'Play as 음성' 드롭다운과 공유 — 수동 재생용 기본 음성입니다.",
|
||||||
"none": "(없음)"
|
"none": "(없음)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "에이전트별 음성",
|
"title": "에이전트별 음성",
|
||||||
"description": "특정 에이전트를 특정 음성에 바인딩하여 누가 말하는지 바로 알 수 있습니다. 에이전트는 X-Voicebox-Client-Id 헤더(또는 stdio용 VOICEBOX_CLIENT_ID 환경변수)로 자신을 식별합니다.",
|
"description": "특정 에이전트를 특정 음성에 바인딩하여 누가 말하는지 바로 알 수 있습니다. 에이전트는 X-TalkBox-Client-Id 헤더(또는 stdio용 TALKBOX_CLIENT_ID 환경변수)로 자신을 식별합니다.",
|
||||||
"empty": "아직 바인딩이 없습니다. 아래에서 추가한 후 MCP 클라이언트가 일치하는 <code>X-Voicebox-Client-Id</code>를 보내도록 설정하세요.",
|
"empty": "아직 바인딩이 없습니다. 아래에서 추가한 후 MCP 클라이언트가 일치하는 <code>X-TalkBox-Client-Id</code>를 보내도록 설정하세요.",
|
||||||
"lastSeen": "마지막 접속 {{when}}",
|
"lastSeen": "마지막 접속 {{when}}",
|
||||||
"lastSeenTitle": "마지막 접속 {{when}}",
|
"lastSeenTitle": "마지막 접속 {{when}}",
|
||||||
"neverConnected": "연결된 적 없음",
|
"neverConnected": "연결된 적 없음",
|
||||||
@@ -1073,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "MCP 정보",
|
"aboutTitle": "MCP 정보",
|
||||||
"aboutBody": "Model Context Protocol을 통해 AI 코딩 에이전트(Claude Code, Cursor, Windsurf)가 Voicebox 도구를 호출할 수 있습니다. 복제된 음성으로 말하고, 오디오를 변환하고, 캡처를 탐색하세요.",
|
"aboutBody": "Model Context Protocol을 통해 AI 코딩 에이전트(Claude Code, Cursor, Windsurf)가 TalkBox 도구를 호출할 수 있습니다. 복제된 음성으로 말하고, 오디오를 변환하고, 캡처를 탐색하세요.",
|
||||||
"toolsTitle": "사용 가능한 도구",
|
"toolsTitle": "사용 가능한 도구",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "음성 프로필로 텍스트 읽기.",
|
"speak": "음성 프로필로 텍스트 읽기.",
|
||||||
@@ -1133,7 +1133,7 @@
|
|||||||
"deleteCuda": "CUDA 백엔드 삭제 실패",
|
"deleteCuda": "CUDA 백엔드 삭제 실패",
|
||||||
"deleteRocm": "ROCm 백엔드 삭제 실패"
|
"deleteRocm": "ROCm 백엔드 삭제 실패"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox는 시스템에서 사용 가능한 최고의 GPU를 자동으로 감지하여 사용합니다. Apple Silicon Mac에서는 Metal Performance Shaders(MPS)를 통해 MLX 백엔드가 Neural Engine과 GPU에서 기본 실행되며 추가 설정이 필요하지 않습니다. Windows에서는 선택적 CUDA(NVIDIA) 또는 ROCm(AMD) 백엔드를 다운로드하여 하드웨어 가속 추론을 사용할 수 있습니다. Intel XPU와 DirectML도 PyTorch를 통해 지원됩니다. GPU가 감지되지 않으면 Voicebox가 CPU로 대체됩니다 — 모든 엔진이 작동하지만 더 느립니다.",
|
"footer": "TalkBox는 시스템에서 사용 가능한 최고의 GPU를 자동으로 감지하여 사용합니다. Apple Silicon Mac에서는 Metal Performance Shaders(MPS)를 통해 MLX 백엔드가 Neural Engine과 GPU에서 기본 실행되며 추가 설정이 필요하지 않습니다. Windows에서는 선택적 CUDA(NVIDIA) 또는 ROCm(AMD) 백엔드를 다운로드하여 하드웨어 가속 추론을 사용할 수 있습니다. Intel XPU와 DirectML도 PyTorch를 통해 지원됩니다. GPU가 감지되지 않으면 TalkBox가 CPU로 대체됩니다 — 모든 엔진이 작동하지만 더 느립니다.",
|
||||||
"rocm": {
|
"rocm": {
|
||||||
"title": "AMD ROCm 백엔드",
|
"title": "AMD ROCm 백엔드",
|
||||||
"activeTitle": "ROCm 백엔드 활성",
|
"activeTitle": "ROCm 백엔드 활성",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "Permissão de Monitoramento de Entrada",
|
"label": "Permissão de Monitoramento de Entrada",
|
||||||
"ready": "O macOS permite que o Voicebox detecte seu atalho global.",
|
"ready": "O macOS permite que o TalkBox detecte seu atalho global.",
|
||||||
"missing": "O macOS precisa permitir que o Voicebox detecte o atalho global.",
|
"missing": "O macOS precisa permitir que o TalkBox detecte o atalho global.",
|
||||||
"openSettings": "Abrir Ajustes"
|
"openSettings": "Abrir Ajustes"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "Permissão de Acessibilidade",
|
"label": "Permissão de Acessibilidade",
|
||||||
"ready": "O Voicebox pode colar transcrições em outros apps.",
|
"ready": "O TalkBox pode colar transcrições em outros apps.",
|
||||||
"missing": "Necessária para que as transcrições possam ser coladas no app em foco.",
|
"missing": "Necessária para que as transcrições possam ser coladas no app em foco.",
|
||||||
"openSettings": "Abrir Ajustes"
|
"openSettings": "Abrir Ajustes"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "Conceda a permissão de Acessibilidade para ativar a colagem automática",
|
"title": "Conceda a permissão de Acessibilidade para ativar a colagem automática",
|
||||||
"body": "O Voicebox precisa de <path>Ajustes do Sistema → Privacidade e Segurança → Acessibilidade</path> para colar transcrições em outros apps. Seu ditado ainda aparece na aba Capturas sem isso.",
|
"body": "O TalkBox precisa de <path>Ajustes do Sistema → Privacidade e Segurança → Acessibilidade</path> para colar transcrições em outros apps. Seu ditado ainda aparece na aba Capturas sem isso.",
|
||||||
"openSettings": "Abrir Ajustes",
|
"openSettings": "Abrir Ajustes",
|
||||||
"recheck": "Já ativei",
|
"recheck": "Já ativei",
|
||||||
"rechecking": "Verificando…",
|
"rechecking": "Verificando…",
|
||||||
"stillMissing": "Ainda não detectado. O macOS geralmente exige fechar e reabrir o Voicebox depois de alternar a permissão."
|
"stillMissing": "Ainda não detectado. O macOS geralmente exige fechar e reabrir o TalkBox depois de alternar a permissão."
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "Conceda Monitoramento de Entrada para ativar o atalho global",
|
"title": "Conceda Monitoramento de Entrada para ativar o atalho global",
|
||||||
"body": "O Voicebox precisa de <path>Ajustes do Sistema → Privacidade e Segurança → Monitoramento de Entrada</path> para detectar sua combinação de ditado. A opção está ligada, mas o macOS está bloqueando os eventos de tecla até você permitir.",
|
"body": "O TalkBox precisa de <path>Ajustes do Sistema → Privacidade e Segurança → Monitoramento de Entrada</path> para detectar sua combinação de ditado. A opção está ligada, mas o macOS está bloqueando os eventos de tecla até você permitir.",
|
||||||
"openSettings": "Abrir Ajustes",
|
"openSettings": "Abrir Ajustes",
|
||||||
"recheck": "Já ativei",
|
"recheck": "Já ativei",
|
||||||
"rechecking": "Verificando…",
|
"rechecking": "Verificando…",
|
||||||
"stillMissing": "Ainda não detectado. O macOS geralmente exige fechar e reabrir o Voicebox depois de alternar a permissão."
|
"stillMissing": "Ainda não detectado. O macOS geralmente exige fechar e reabrir o TalkBox depois de alternar a permissão."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "Criar Voz",
|
"createVoice": "Criar Voz",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "Tipo de arquivo inválido",
|
"invalidTitle": "Tipo de arquivo inválido",
|
||||||
"invalidDescription": "Selecione um arquivo .voicebox.zip válido",
|
"invalidDescription": "Selecione um arquivo .talkbox.zip válido",
|
||||||
"successTitle": "Perfil importado",
|
"successTitle": "Perfil importado",
|
||||||
"successDescription": "Perfil de voz importado com sucesso",
|
"successDescription": "Perfil de voz importado com sucesso",
|
||||||
"failedTitle": "Falha ao importar o perfil",
|
"failedTitle": "Falha ao importar o perfil",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "Idioma",
|
"label": "Idioma",
|
||||||
"description": "Escolha o idioma de exibição do Voicebox."
|
"description": "Escolha o idioma de exibição do TalkBox."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "Tema",
|
"label": "Tema",
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
"discord": { "title": "Entrar no Discord", "subtitle": "Tire dúvidas e compartilhe vozes" },
|
"discord": { "title": "Entrar no Discord", "subtitle": "Tire dúvidas e compartilhe vozes" },
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "URL do Servidor",
|
"title": "URL do Servidor",
|
||||||
"description": "O endereço do seu servidor backend do Voicebox.",
|
"description": "O endereço do seu servidor backend do TalkBox.",
|
||||||
"invalidUrl": "Digite uma URL válida",
|
"invalidUrl": "Digite uma URL válida",
|
||||||
"updatedTitle": "URL do servidor atualizada",
|
"updatedTitle": "URL do servidor atualizada",
|
||||||
"updatedDescription": "Conectado a {{url}}"
|
"updatedDescription": "Conectado a {{url}}"
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "Acesso à API",
|
"title": "Acesso à API",
|
||||||
"description": "Integre o Voicebox ao seu fluxo de trabalho via API REST em <code>{{url}}</code>",
|
"description": "Integre o TalkBox ao seu fluxo de trabalho via API REST em <code>{{url}}</code>",
|
||||||
"viewReference": "Ver a referência completa da API",
|
"viewReference": "Ver a referência completa da API",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "Gerar fala",
|
"generate": "Gerar fala",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "Colar automaticamente no campo de texto em foco",
|
"title": "Colar automaticamente no campo de texto em foco",
|
||||||
"description": "Se um campo de texto estiver em foco em outro app, cola diretamente nele. O Voicebox salva e restaura o que estava na sua área de transferência."
|
"description": "Se um campo de texto estiver em foco em outro app, cola diretamente nele. O TalkBox salva e restaura o que estava na sua área de transferência."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -916,7 +916,7 @@
|
|||||||
"description": "Escolha qual modelo de fala para texto roda nas suas capturas.",
|
"description": "Escolha qual modelo de fala para texto roda nas suas capturas.",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "Modelo de transcrição",
|
"title": "Modelo de transcrição",
|
||||||
"description": "O Whisper vem com o Voicebox e roda inteiramente na sua máquina.",
|
"description": "O Whisper vem com o TalkBox e roda inteiramente na sua máquina.",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "Armazenamento",
|
"title": "Armazenamento",
|
||||||
"description": "As capturas são salvas como arquivos pareados de áudio e transcrição no seu diretório de dados do Voicebox.",
|
"description": "As capturas são salvas como arquivos pareados de áudio e transcrição no seu diretório de dados do TalkBox.",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "Retenção",
|
"title": "Retenção",
|
||||||
"description": "Por quanto tempo manter as capturas. Vale tanto para áudio quanto para transcrições.",
|
"description": "Por quanto tempo manter as capturas. Vale tanto para áudio quanto para transcrições.",
|
||||||
@@ -1009,7 +1009,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "Sobre as Capturas",
|
"aboutTitle": "Sobre as Capturas",
|
||||||
"aboutBody": "Segure um atalho em qualquer lugar da sua máquina, fale, e o Voicebox transforma sua voz em texto. Reproduza-o em qualquer voz clonada, cole-o em qualquer app ou envie-o ao seu agente de programação.",
|
"aboutBody": "Segure um atalho em qualquer lugar da sua máquina, fale, e o TalkBox transforma sua voz em texto. Reproduza-o em qualquer voz clonada, cole-o em qualquer app ou envie-o ao seu agente de programação.",
|
||||||
"differencesTitle": "O que muda",
|
"differencesTitle": "O que muda",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "Totalmente local.",
|
"title": "Totalmente local.",
|
||||||
@@ -1025,14 +1025,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Atenção no Windows",
|
"title": "Atenção no Windows",
|
||||||
"body": "O atalho não dispara enquanto o próprio Voicebox ou qualquer app rodando como administrador estiver em foco. Estamos trabalhando nisso."
|
"body": "O atalho não dispara enquanto o próprio TalkBox ou qualquer app rodando como administrador estiver em foco. Estamos trabalhando nisso."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "Instalar no seu agente",
|
"title": "Instalar no seu agente",
|
||||||
"description": "O Voicebox expõe um servidor MCP local sempre que o app está aberto. Cole um destes trechos na configuração de MCP do seu agente.",
|
"description": "O TalkBox expõe um servidor MCP local sempre que o app está aberto. Cole um destes trechos na configuração de MCP do seu agente.",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP (recomendado)",
|
"title": "HTTP (recomendado)",
|
||||||
"description": "Para clientes que falam MCP via HTTP — Claude Code, Cursor, Windsurf, VS Code."
|
"description": "Para clientes que falam MCP via HTTP — Claude Code, Cursor, Windsurf, VS Code."
|
||||||
@@ -1050,15 +1050,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "Voz padrão",
|
"title": "Voz padrão",
|
||||||
"description": "Usada quando um agente chama voicebox.speak sem um perfil específico e não tem vínculo por cliente.",
|
"description": "Usada quando um agente chama talkbox.speak sem um perfil específico e não tem vínculo por cliente.",
|
||||||
"label": "Voz de reprodução padrão",
|
"label": "Voz de reprodução padrão",
|
||||||
"labelHint": "Compartilhada com o menu 'Reproduzir como voz' da aba Capturas — uma voz padrão para reprodução passiva.",
|
"labelHint": "Compartilhada com o menu 'Reproduzir como voz' da aba Capturas — uma voz padrão para reprodução passiva.",
|
||||||
"none": "(nenhuma)"
|
"none": "(nenhuma)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "Voz por agente",
|
"title": "Voz por agente",
|
||||||
"description": "Vincule agentes específicos a vozes específicas para saber quem está falando sem olhar. O agente se identifica pelo cabeçalho X-Voicebox-Client-Id (ou pela variável de ambiente VOICEBOX_CLIENT_ID no stdio).",
|
"description": "Vincule agentes específicos a vozes específicas para saber quem está falando sem olhar. O agente se identifica pelo cabeçalho X-TalkBox-Client-Id (ou pela variável de ambiente TALKBOX_CLIENT_ID no stdio).",
|
||||||
"empty": "Nenhum vínculo ainda. Adicione um abaixo e configure seu cliente MCP para enviar o <code>X-Voicebox-Client-Id</code> correspondente.",
|
"empty": "Nenhum vínculo ainda. Adicione um abaixo e configure seu cliente MCP para enviar o <code>X-TalkBox-Client-Id</code> correspondente.",
|
||||||
"lastSeen": "visto pela última vez {{when}}",
|
"lastSeen": "visto pela última vez {{when}}",
|
||||||
"lastSeenTitle": "Visto pela última vez {{when}}",
|
"lastSeenTitle": "Visto pela última vez {{when}}",
|
||||||
"neverConnected": "nunca conectado",
|
"neverConnected": "nunca conectado",
|
||||||
@@ -1073,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "Sobre o MCP",
|
"aboutTitle": "Sobre o MCP",
|
||||||
"aboutBody": "O Model Context Protocol permite que seu agente de programação com IA — Claude Code, Cursor, Windsurf — chame as ferramentas do Voicebox. Fale em uma voz clonada, transcreva áudio, navegue pelas capturas.",
|
"aboutBody": "O Model Context Protocol permite que seu agente de programação com IA — Claude Code, Cursor, Windsurf — chame as ferramentas do TalkBox. Fale em uma voz clonada, transcreva áudio, navegue pelas capturas.",
|
||||||
"toolsTitle": "Ferramentas disponíveis",
|
"toolsTitle": "Ferramentas disponíveis",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "Falar texto em um perfil de voz.",
|
"speak": "Falar texto em um perfil de voz.",
|
||||||
@@ -1128,7 +1128,7 @@
|
|||||||
"switchCpu": "Falha ao mudar para CPU",
|
"switchCpu": "Falha ao mudar para CPU",
|
||||||
"deleteCuda": "Falha ao excluir o backend CUDA"
|
"deleteCuda": "Falha ao excluir o backend CUDA"
|
||||||
},
|
},
|
||||||
"footer": "O Voicebox detecta e usa automaticamente a melhor GPU disponível no seu sistema. Em Macs com Apple Silicon, o backend MLX roda nativamente no Neural Engine e na GPU via Metal Performance Shaders (MPS), sem configuração adicional. No Windows e no Linux com GPUs NVIDIA, você pode baixar um backend CUDA opcional para inferência acelerada por hardware. AMD ROCm, Intel XPU e DirectML também são suportados quando disponíveis através do PyTorch. Quando nenhuma GPU é detectada, o Voicebox recorre à CPU — todos os motores ainda funcionam, só que mais devagar."
|
"footer": "O TalkBox detecta e usa automaticamente a melhor GPU disponível no seu sistema. Em Macs com Apple Silicon, o backend MLX roda nativamente no Neural Engine e na GPU via Metal Performance Shaders (MPS), sem configuração adicional. No Windows e no Linux com GPUs NVIDIA, você pode baixar um backend CUDA opcional para inferência acelerada por hardware. AMD ROCm, Intel XPU e DirectML também são suportados quando disponíveis através do PyTorch. Quando nenhuma GPU é detectada, o TalkBox recorre à CPU — todos os motores ainda funcionam, só que mais devagar."
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"title": "Logs do Servidor",
|
"title": "Logs do Servidor",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "「输入监控」权限",
|
"label": "「输入监控」权限",
|
||||||
"ready": "macOS 允许 Voicebox 检测您的全局快捷键。",
|
"ready": "macOS 允许 TalkBox 检测您的全局快捷键。",
|
||||||
"missing": "macOS 需要允许 Voicebox 检测全局快捷键。",
|
"missing": "macOS 需要允许 TalkBox 检测全局快捷键。",
|
||||||
"openSettings": "打开设置"
|
"openSettings": "打开设置"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "「辅助功能」权限",
|
"label": "「辅助功能」权限",
|
||||||
"ready": "Voicebox 可以将转录粘贴到其他应用中。",
|
"ready": "TalkBox 可以将转录粘贴到其他应用中。",
|
||||||
"missing": "需要此权限,转录才能粘贴到当前焦点应用。",
|
"missing": "需要此权限,转录才能粘贴到当前焦点应用。",
|
||||||
"openSettings": "打开设置"
|
"openSettings": "打开设置"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "授予「辅助功能」权限以启用自动粘贴",
|
"title": "授予「辅助功能」权限以启用自动粘贴",
|
||||||
"body": "Voicebox 需要在 <path>系统设置 → 隐私与安全性 → 辅助功能</path> 中获得权限,才能将转录粘贴到其他应用。即使没有此权限,听写仍会保存到「捕获」标签页。",
|
"body": "TalkBox 需要在 <path>系统设置 → 隐私与安全性 → 辅助功能</path> 中获得权限,才能将转录粘贴到其他应用。即使没有此权限,听写仍会保存到「捕获」标签页。",
|
||||||
"openSettings": "打开设置",
|
"openSettings": "打开设置",
|
||||||
"recheck": "我已启用",
|
"recheck": "我已启用",
|
||||||
"rechecking": "检查中…",
|
"rechecking": "检查中…",
|
||||||
"stillMissing": "仍未检测到。切换权限后,macOS 通常需要退出并重新打开 Voicebox。"
|
"stillMissing": "仍未检测到。切换权限后,macOS 通常需要退出并重新打开 TalkBox。"
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "授予「输入监控」权限以启用全局快捷键",
|
"title": "授予「输入监控」权限以启用全局快捷键",
|
||||||
"body": "Voicebox 需要在 <path>系统设置 → 隐私与安全性 → 输入监控</path> 中获得权限,才能检测您的听写组合键。开关已开启,但在您允许之前 macOS 会拦截按键事件。",
|
"body": "TalkBox 需要在 <path>系统设置 → 隐私与安全性 → 输入监控</path> 中获得权限,才能检测您的听写组合键。开关已开启,但在您允许之前 macOS 会拦截按键事件。",
|
||||||
"openSettings": "打开设置",
|
"openSettings": "打开设置",
|
||||||
"recheck": "我已启用",
|
"recheck": "我已启用",
|
||||||
"rechecking": "检查中…",
|
"rechecking": "检查中…",
|
||||||
"stillMissing": "仍未检测到。切换权限后,macOS 通常需要退出并重新打开 Voicebox。"
|
"stillMissing": "仍未检测到。切换权限后,macOS 通常需要退出并重新打开 TalkBox。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "创建声音",
|
"createVoice": "创建声音",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "文件类型无效",
|
"invalidTitle": "文件类型无效",
|
||||||
"invalidDescription": "请选择有效的 .voicebox.zip 文件",
|
"invalidDescription": "请选择有效的 .talkbox.zip 文件",
|
||||||
"successTitle": "声音已导入",
|
"successTitle": "声音已导入",
|
||||||
"successDescription": "成功导入声音档案",
|
"successDescription": "成功导入声音档案",
|
||||||
"failedTitle": "导入声音档案失败",
|
"failedTitle": "导入声音档案失败",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "语言",
|
"label": "语言",
|
||||||
"description": "选择 Voicebox 的显示语言。"
|
"description": "选择 TalkBox 的显示语言。"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "主题",
|
"label": "主题",
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
"discord": { "title": "加入 Discord", "subtitle": "获取帮助 & 分享声音" },
|
"discord": { "title": "加入 Discord", "subtitle": "获取帮助 & 分享声音" },
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "服务器 URL",
|
"title": "服务器 URL",
|
||||||
"description": "Voicebox 后端服务器的地址。",
|
"description": "TalkBox 后端服务器的地址。",
|
||||||
"invalidUrl": "请输入有效的 URL",
|
"invalidUrl": "请输入有效的 URL",
|
||||||
"updatedTitle": "服务器 URL 已更新",
|
"updatedTitle": "服务器 URL 已更新",
|
||||||
"updatedDescription": "已连接到 {{url}}"
|
"updatedDescription": "已连接到 {{url}}"
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "API 访问",
|
"title": "API 访问",
|
||||||
"description": "通过 <code>{{url}}</code> 的 REST API 将 Voicebox 集成到您的工作流程中",
|
"description": "通过 <code>{{url}}</code> 的 REST API 将 TalkBox 集成到您的工作流程中",
|
||||||
"viewReference": "查看完整的 API 参考",
|
"viewReference": "查看完整的 API 参考",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "生成语音",
|
"generate": "生成语音",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "自动粘贴到当前焦点的文本字段",
|
"title": "自动粘贴到当前焦点的文本字段",
|
||||||
"description": "如果其他应用中有焦点输入框,则直接粘贴进去。Voicebox 会保存并恢复您剪贴板原有的内容。"
|
"description": "如果其他应用中有焦点输入框,则直接粘贴进去。TalkBox 会保存并恢复您剪贴板原有的内容。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -916,7 +916,7 @@
|
|||||||
"description": "选择捕获时使用哪个语音转文本模型。",
|
"description": "选择捕获时使用哪个语音转文本模型。",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "转录模型",
|
"title": "转录模型",
|
||||||
"description": "Whisper 随 Voicebox 一同发布,完全在您的设备上运行。",
|
"description": "Whisper 随 TalkBox 一同发布,完全在您的设备上运行。",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "存储",
|
"title": "存储",
|
||||||
"description": "捕获以配对的音频和转录文件保存在您的 Voicebox 数据目录中。",
|
"description": "捕获以配对的音频和转录文件保存在您的 TalkBox 数据目录中。",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "保留",
|
"title": "保留",
|
||||||
"description": "捕获保留多久。同时适用于音频和转录。",
|
"description": "捕获保留多久。同时适用于音频和转录。",
|
||||||
@@ -1009,7 +1009,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "关于「捕获」",
|
"aboutTitle": "关于「捕获」",
|
||||||
"aboutBody": "在系统的任何位置按住快捷键说话,Voicebox 就会把您的声音转换成文本。可用任何克隆的声音回放、粘贴到任何应用,或导入到您的编程代理中。",
|
"aboutBody": "在系统的任何位置按住快捷键说话,TalkBox 就会把您的声音转换成文本。可用任何克隆的声音回放、粘贴到任何应用,或导入到您的编程代理中。",
|
||||||
"differencesTitle": "不同之处",
|
"differencesTitle": "不同之处",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "完全本地。",
|
"title": "完全本地。",
|
||||||
@@ -1025,14 +1025,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Windows 上的提示",
|
"title": "Windows 上的提示",
|
||||||
"body": "当 Voicebox 自身或任何以管理员身份运行的应用处于焦点时,快捷键不会触发。我们正在解决这个问题。"
|
"body": "当 TalkBox 自身或任何以管理员身份运行的应用处于焦点时,快捷键不会触发。我们正在解决这个问题。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "安装到您的代理",
|
"title": "安装到您的代理",
|
||||||
"description": "只要应用打开,Voicebox 就会暴露一个本地 MCP 服务器。将以下任一片段粘贴到您的代理 MCP 配置中。",
|
"description": "只要应用打开,TalkBox 就会暴露一个本地 MCP 服务器。将以下任一片段粘贴到您的代理 MCP 配置中。",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP(推荐)",
|
"title": "HTTP(推荐)",
|
||||||
"description": "适用于支持 HTTP MCP 的客户端——Claude Code、Cursor、Windsurf、VS Code。"
|
"description": "适用于支持 HTTP MCP 的客户端——Claude Code、Cursor、Windsurf、VS Code。"
|
||||||
@@ -1050,15 +1050,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "默认声音",
|
"title": "默认声音",
|
||||||
"description": "当代理调用 voicebox.speak 但未指定具体档案、且没有按客户端绑定时使用。",
|
"description": "当代理调用 talkbox.speak 但未指定具体档案、且没有按客户端绑定时使用。",
|
||||||
"label": "默认播放声音",
|
"label": "默认播放声音",
|
||||||
"labelHint": "与「捕获」标签页的「播放为」下拉菜单共享——被动播放的统一默认声音。",
|
"labelHint": "与「捕获」标签页的「播放为」下拉菜单共享——被动播放的统一默认声音。",
|
||||||
"none": "(无)"
|
"none": "(无)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "按代理设置声音",
|
"title": "按代理设置声音",
|
||||||
"description": "将特定代理绑定到特定声音,这样不用看也能分辨谁在说话。代理通过 X-Voicebox-Client-Id 请求头(stdio 则用 VOICEBOX_CLIENT_ID 环境变量)来标识自己。",
|
"description": "将特定代理绑定到特定声音,这样不用看也能分辨谁在说话。代理通过 X-TalkBox-Client-Id 请求头(stdio 则用 TALKBOX_CLIENT_ID 环境变量)来标识自己。",
|
||||||
"empty": "暂无绑定。在下方添加一个,然后将您的 MCP 客户端配置为发送匹配的 <code>X-Voicebox-Client-Id</code>。",
|
"empty": "暂无绑定。在下方添加一个,然后将您的 MCP 客户端配置为发送匹配的 <code>X-TalkBox-Client-Id</code>。",
|
||||||
"lastSeen": "最后活跃 {{when}}",
|
"lastSeen": "最后活跃 {{when}}",
|
||||||
"lastSeenTitle": "最后活跃 {{when}}",
|
"lastSeenTitle": "最后活跃 {{when}}",
|
||||||
"neverConnected": "从未连接",
|
"neverConnected": "从未连接",
|
||||||
@@ -1073,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "关于 MCP",
|
"aboutTitle": "关于 MCP",
|
||||||
"aboutBody": "Model Context Protocol 让您的 AI 编程代理——Claude Code、Cursor、Windsurf——可以调用 Voicebox 工具。以克隆的声音朗读、转录音频、浏览捕获。",
|
"aboutBody": "Model Context Protocol 让您的 AI 编程代理——Claude Code、Cursor、Windsurf——可以调用 TalkBox 工具。以克隆的声音朗读、转录音频、浏览捕获。",
|
||||||
"toolsTitle": "可用工具",
|
"toolsTitle": "可用工具",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "用声音档案朗读文本。",
|
"speak": "用声音档案朗读文本。",
|
||||||
@@ -1128,7 +1128,7 @@
|
|||||||
"switchCpu": "切换到 CPU 失败",
|
"switchCpu": "切换到 CPU 失败",
|
||||||
"deleteCuda": "删除 CUDA 后端失败"
|
"deleteCuda": "删除 CUDA 后端失败"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox 会自动检测并使用系统上可用的最佳 GPU。在 Apple Silicon Mac 上,MLX 后端通过 Metal Performance Shaders (MPS) 在神经引擎和 GPU 上原生运行,无需额外设置。在配备 NVIDIA GPU 的 Windows 和 Linux 上,您可以下载可选的 CUDA 后端以获得硬件加速推理。AMD ROCm、Intel XPU 和 DirectML 也通过 PyTorch 获得支持。未检测到 GPU 时,Voicebox 会退回到 CPU——所有引擎仍可工作,只是速度较慢。"
|
"footer": "TalkBox 会自动检测并使用系统上可用的最佳 GPU。在 Apple Silicon Mac 上,MLX 后端通过 Metal Performance Shaders (MPS) 在神经引擎和 GPU 上原生运行,无需额外设置。在配备 NVIDIA GPU 的 Windows 和 Linux 上,您可以下载可选的 CUDA 后端以获得硬件加速推理。AMD ROCm、Intel XPU 和 DirectML 也通过 PyTorch 获得支持。未检测到 GPU 时,TalkBox 会退回到 CPU——所有引擎仍可工作,只是速度较慢。"
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"title": "服务器日志",
|
"title": "服务器日志",
|
||||||
|
|||||||
@@ -135,13 +135,13 @@
|
|||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"label": "輸入監控權限",
|
"label": "輸入監控權限",
|
||||||
"ready": "macOS 允許 Voicebox 偵測您的全域快捷鍵。",
|
"ready": "macOS 允許 TalkBox 偵測您的全域快捷鍵。",
|
||||||
"missing": "macOS 需要允許 Voicebox 偵測全域快捷鍵。",
|
"missing": "macOS 需要允許 TalkBox 偵測全域快捷鍵。",
|
||||||
"openSettings": "開啟設定"
|
"openSettings": "開啟設定"
|
||||||
},
|
},
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"label": "輔助使用權限",
|
"label": "輔助使用權限",
|
||||||
"ready": "Voicebox 可將轉錄文字貼到其他 App。",
|
"ready": "TalkBox 可將轉錄文字貼到其他 App。",
|
||||||
"missing": "需要此權限才能將轉錄文字貼到目前作用中的 App。",
|
"missing": "需要此權限才能將轉錄文字貼到目前作用中的 App。",
|
||||||
"openSettings": "開啟設定"
|
"openSettings": "開啟設定"
|
||||||
}
|
}
|
||||||
@@ -149,19 +149,19 @@
|
|||||||
"permissions": {
|
"permissions": {
|
||||||
"accessibility": {
|
"accessibility": {
|
||||||
"title": "授予輔助使用權限以啟用自動貼上",
|
"title": "授予輔助使用權限以啟用自動貼上",
|
||||||
"body": "Voicebox 需要 <path>系統設定 → 私隱與安全性 → 輔助使用</path> 才能將轉錄文字貼到其他 App。即使沒有此權限,口述內容仍會出現在「擷取」分頁。",
|
"body": "TalkBox 需要 <path>系統設定 → 私隱與安全性 → 輔助使用</path> 才能將轉錄文字貼到其他 App。即使沒有此權限,口述內容仍會出現在「擷取」分頁。",
|
||||||
"openSettings": "開啟設定",
|
"openSettings": "開啟設定",
|
||||||
"recheck": "我已啟用",
|
"recheck": "我已啟用",
|
||||||
"rechecking": "檢查中…",
|
"rechecking": "檢查中…",
|
||||||
"stillMissing": "仍未偵測到。macOS 通常需要在切換權限後結束並重新開啟 Voicebox。"
|
"stillMissing": "仍未偵測到。macOS 通常需要在切換權限後結束並重新開啟 TalkBox。"
|
||||||
},
|
},
|
||||||
"inputMonitoring": {
|
"inputMonitoring": {
|
||||||
"title": "授予輸入監控權限以啟用全域快捷鍵",
|
"title": "授予輸入監控權限以啟用全域快捷鍵",
|
||||||
"body": "Voicebox 需要 <path>系統設定 → 私隱與安全性 → 輸入監控</path> 才能偵測您的口述組合鍵。功能已開啟,但 macOS 在您允許前會封鎖按鍵事件。",
|
"body": "TalkBox 需要 <path>系統設定 → 私隱與安全性 → 輸入監控</path> 才能偵測您的口述組合鍵。功能已開啟,但 macOS 在您允許前會封鎖按鍵事件。",
|
||||||
"openSettings": "開啟設定",
|
"openSettings": "開啟設定",
|
||||||
"recheck": "我已啟用",
|
"recheck": "我已啟用",
|
||||||
"rechecking": "檢查中…",
|
"rechecking": "檢查中…",
|
||||||
"stillMissing": "仍未偵測到。macOS 通常需要在切換權限後結束並重新開啟 Voicebox。"
|
"stillMissing": "仍未偵測到。macOS 通常需要在切換權限後結束並重新開啟 TalkBox。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
"createVoice": "建立聲音",
|
"createVoice": "建立聲音",
|
||||||
"import": {
|
"import": {
|
||||||
"invalidTitle": "檔案類型無效",
|
"invalidTitle": "檔案類型無效",
|
||||||
"invalidDescription": "請選擇有效的 .voicebox.zip 檔案",
|
"invalidDescription": "請選擇有效的 .talkbox.zip 檔案",
|
||||||
"successTitle": "聲音已匯入",
|
"successTitle": "聲音已匯入",
|
||||||
"successDescription": "成功匯入聲音檔案",
|
"successDescription": "成功匯入聲音檔案",
|
||||||
"failedTitle": "匯入聲音檔案失敗",
|
"failedTitle": "匯入聲音檔案失敗",
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
},
|
},
|
||||||
"language": {
|
"language": {
|
||||||
"label": "語言",
|
"label": "語言",
|
||||||
"description": "選擇 Voicebox 的顯示語言。"
|
"description": "選擇 TalkBox 的顯示語言。"
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"label": "佈景主題",
|
"label": "佈景主題",
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
"discord": { "title": "加入 Discord", "subtitle": "取得協助與分享聲音" },
|
"discord": { "title": "加入 Discord", "subtitle": "取得協助與分享聲音" },
|
||||||
"serverUrl": {
|
"serverUrl": {
|
||||||
"title": "伺服器 URL",
|
"title": "伺服器 URL",
|
||||||
"description": "Voicebox 後端伺服器的位址。",
|
"description": "TalkBox 後端伺服器的位址。",
|
||||||
"invalidUrl": "請輸入有效的 URL",
|
"invalidUrl": "請輸入有效的 URL",
|
||||||
"updatedTitle": "伺服器 URL 已更新",
|
"updatedTitle": "伺服器 URL 已更新",
|
||||||
"updatedDescription": "已連線至 {{url}}"
|
"updatedDescription": "已連線至 {{url}}"
|
||||||
@@ -819,7 +819,7 @@
|
|||||||
},
|
},
|
||||||
"api": {
|
"api": {
|
||||||
"title": "API 存取",
|
"title": "API 存取",
|
||||||
"description": "透過 <code>{{url}}</code> 的 REST API 將 Voicebox 整合到您的工作流程中",
|
"description": "透過 <code>{{url}}</code> 的 REST API 將 TalkBox 整合到您的工作流程中",
|
||||||
"viewReference": "檢視完整的 API 參考",
|
"viewReference": "檢視完整的 API 參考",
|
||||||
"endpoints": {
|
"endpoints": {
|
||||||
"generate": "生成語音",
|
"generate": "生成語音",
|
||||||
@@ -908,7 +908,7 @@
|
|||||||
},
|
},
|
||||||
"autoPaste": {
|
"autoPaste": {
|
||||||
"title": "自動貼到目前作用中的文字欄位",
|
"title": "自動貼到目前作用中的文字欄位",
|
||||||
"description": "若另一個 App 中已聚焦於文字輸入,直接貼進去。Voicebox 會儲存並還原您原本剪貼簿上的內容。"
|
"description": "若另一個 App 中已聚焦於文字輸入,直接貼進去。TalkBox 會儲存並還原您原本剪貼簿上的內容。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"transcription": {
|
"transcription": {
|
||||||
@@ -916,7 +916,7 @@
|
|||||||
"description": "選擇用於擷取的語音轉文字模型。",
|
"description": "選擇用於擷取的語音轉文字模型。",
|
||||||
"model": {
|
"model": {
|
||||||
"title": "轉錄模型",
|
"title": "轉錄模型",
|
||||||
"description": "Whisper 隨 Voicebox 提供,完全在您的電腦上執行。",
|
"description": "Whisper 隨 TalkBox 提供,完全在您的電腦上執行。",
|
||||||
"base": "Whisper Base · 74M · {{tail}}",
|
"base": "Whisper Base · 74M · {{tail}}",
|
||||||
"small": "Whisper Small · 244M · {{tail}}",
|
"small": "Whisper Small · 244M · {{tail}}",
|
||||||
"medium": "Whisper Medium · 769M · {{tail}}",
|
"medium": "Whisper Medium · 769M · {{tail}}",
|
||||||
@@ -992,7 +992,7 @@
|
|||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"title": "儲存",
|
"title": "儲存",
|
||||||
"description": "擷取會以成對的音訊與轉錄文字檔形式,儲存在您的 Voicebox 資料目錄中。",
|
"description": "擷取會以成對的音訊與轉錄文字檔形式,儲存在您的 TalkBox 資料目錄中。",
|
||||||
"retention": {
|
"retention": {
|
||||||
"title": "保留期限",
|
"title": "保留期限",
|
||||||
"description": "擷取保留的時間長度。同時適用於音訊與轉錄文字。",
|
"description": "擷取保留的時間長度。同時適用於音訊與轉錄文字。",
|
||||||
@@ -1009,7 +1009,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "關於擷取",
|
"aboutTitle": "關於擷取",
|
||||||
"aboutBody": "在電腦上任何位置按住快捷鍵說話,Voicebox 會將您的聲音轉成文字。可以用任何複製的聲音重播、貼到任何 App,或送進您的程式碼代理。",
|
"aboutBody": "在電腦上任何位置按住快捷鍵說話,TalkBox 會將您的聲音轉成文字。可以用任何複製的聲音重播、貼到任何 App,或送進您的程式碼代理。",
|
||||||
"differencesTitle": "有何不同",
|
"differencesTitle": "有何不同",
|
||||||
"local": {
|
"local": {
|
||||||
"title": "完全在本機。",
|
"title": "完全在本機。",
|
||||||
@@ -1025,14 +1025,14 @@
|
|||||||
},
|
},
|
||||||
"windowsCaveat": {
|
"windowsCaveat": {
|
||||||
"title": "Windows 上的提醒",
|
"title": "Windows 上的提醒",
|
||||||
"body": "當 Voicebox 本身或任何以系統管理員身分執行的應用程式取得焦點時,快捷鍵不會觸發。我們正在處理中。"
|
"body": "當 TalkBox 本身或任何以系統管理員身分執行的應用程式取得焦點時,快捷鍵不會觸發。我們正在處理中。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
"install": {
|
"install": {
|
||||||
"title": "安裝到您的代理",
|
"title": "安裝到您的代理",
|
||||||
"description": "App 開啟時 Voicebox 會提供本地 MCP 伺服器。將以下其中一段程式碼貼到您的代理 MCP 設定中。",
|
"description": "App 開啟時 TalkBox 會提供本地 MCP 伺服器。將以下其中一段程式碼貼到您的代理 MCP 設定中。",
|
||||||
"http": {
|
"http": {
|
||||||
"title": "HTTP(建議)",
|
"title": "HTTP(建議)",
|
||||||
"description": "適用於支援 HTTP MCP 的客戶端——Claude Code、Cursor、Windsurf、VS Code。"
|
"description": "適用於支援 HTTP MCP 的客戶端——Claude Code、Cursor、Windsurf、VS Code。"
|
||||||
@@ -1050,15 +1050,15 @@
|
|||||||
},
|
},
|
||||||
"defaultVoice": {
|
"defaultVoice": {
|
||||||
"title": "預設聲音",
|
"title": "預設聲音",
|
||||||
"description": "當代理呼叫 voicebox.speak 卻未指定聲音檔案,且沒有對應客戶端綁定時使用。",
|
"description": "當代理呼叫 talkbox.speak 卻未指定聲音檔案,且沒有對應客戶端綁定時使用。",
|
||||||
"label": "預設播放聲音",
|
"label": "預設播放聲音",
|
||||||
"labelHint": "與「擷取」分頁的「以聲音播放」下拉選單共用——一個用於被動播放的預設聲音。",
|
"labelHint": "與「擷取」分頁的「以聲音播放」下拉選單共用——一個用於被動播放的預設聲音。",
|
||||||
"none": "(無)"
|
"none": "(無)"
|
||||||
},
|
},
|
||||||
"bindings": {
|
"bindings": {
|
||||||
"title": "個別代理聲音",
|
"title": "個別代理聲音",
|
||||||
"description": "將特定代理綁定到特定聲音,讓您不用看就能聽出是誰在說話。代理透過 X-Voicebox-Client-Id 標頭(stdio 則用 VOICEBOX_CLIENT_ID 環境變數)識別自己。",
|
"description": "將特定代理綁定到特定聲音,讓您不用看就能聽出是誰在說話。代理透過 X-TalkBox-Client-Id 標頭(stdio 則用 TALKBOX_CLIENT_ID 環境變數)識別自己。",
|
||||||
"empty": "尚無綁定。請在下方新增,然後將您的 MCP 客戶端設定為傳送對應的 <code>X-Voicebox-Client-Id</code>。",
|
"empty": "尚無綁定。請在下方新增,然後將您的 MCP 客戶端設定為傳送對應的 <code>X-TalkBox-Client-Id</code>。",
|
||||||
"lastSeen": "最後出現於 {{when}}",
|
"lastSeen": "最後出現於 {{when}}",
|
||||||
"lastSeenTitle": "最後出現於 {{when}}",
|
"lastSeenTitle": "最後出現於 {{when}}",
|
||||||
"neverConnected": "從未連線",
|
"neverConnected": "從未連線",
|
||||||
@@ -1073,7 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"aboutTitle": "關於 MCP",
|
"aboutTitle": "關於 MCP",
|
||||||
"aboutBody": "Model Context Protocol 讓您的 AI 程式碼代理——Claude Code、Cursor、Windsurf——可以呼叫 Voicebox 工具。以複製的聲音說話、轉錄音訊、瀏覽擷取。",
|
"aboutBody": "Model Context Protocol 讓您的 AI 程式碼代理——Claude Code、Cursor、Windsurf——可以呼叫 TalkBox 工具。以複製的聲音說話、轉錄音訊、瀏覽擷取。",
|
||||||
"toolsTitle": "可用工具",
|
"toolsTitle": "可用工具",
|
||||||
"tools": {
|
"tools": {
|
||||||
"speak": "以聲音檔案說出文字。",
|
"speak": "以聲音檔案說出文字。",
|
||||||
@@ -1128,7 +1128,7 @@
|
|||||||
"switchCpu": "切換到 CPU 失敗",
|
"switchCpu": "切換到 CPU 失敗",
|
||||||
"deleteCuda": "刪除 CUDA 後端失敗"
|
"deleteCuda": "刪除 CUDA 後端失敗"
|
||||||
},
|
},
|
||||||
"footer": "Voicebox 會自動偵測並使用系統上可用的最佳 GPU。在 Apple Silicon Mac 上,MLX 後端透過 Metal Performance Shaders (MPS) 在神經引擎與 GPU 上原生執行,無需額外設定。在配備 NVIDIA GPU 的 Windows 與 Linux 上,可以下載選用的 CUDA 後端以取得硬體加速推論。AMD ROCm、Intel XPU 與 DirectML 也透過 PyTorch 獲得支援。未偵測到 GPU 時,Voicebox 會退回到 CPU——所有引擎仍可運作,只是速度較慢。"
|
"footer": "TalkBox 會自動偵測並使用系統上可用的最佳 GPU。在 Apple Silicon Mac 上,MLX 後端透過 Metal Performance Shaders (MPS) 在神經引擎與 GPU 上原生執行,無需額外設定。在配備 NVIDIA GPU 的 Windows 與 Linux 上,可以下載選用的 CUDA 後端以取得硬體加速推論。AMD ROCm、Intel XPU 與 DirectML 也透過 PyTorch 獲得支援。未偵測到 GPU 時,TalkBox 會退回到 CPU——所有引擎仍可運作,只是速度較慢。"
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"title": "伺服器日誌",
|
"title": "伺服器日誌",
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ export function useExportGeneration() {
|
|||||||
.substring(0, 30)
|
.substring(0, 30)
|
||||||
.replace(/[^a-z0-9]/gi, '-')
|
.replace(/[^a-z0-9]/gi, '-')
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
const filename = `generation-${safeText}-${generationId.substring(0, 8)}.voicebox.zip`;
|
const filename = `generation-${safeText}-${generationId.substring(0, 8)}.talkbox.zip`;
|
||||||
|
|
||||||
await platform.filesystem.saveFile(filename, blob, [
|
await platform.filesystem.saveFile(filename, blob, [
|
||||||
{
|
{
|
||||||
name: 'Voicebox Generation',
|
name: 'TalkBox Generation',
|
||||||
extensions: ['zip'],
|
extensions: ['zip'],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -126,11 +126,11 @@ export function useExportProfile() {
|
|||||||
// Get profile name for filename
|
// Get profile name for filename
|
||||||
const profile = await apiClient.getProfile(profileId);
|
const profile = await apiClient.getProfile(profileId);
|
||||||
const safeName = profile.name.replace(/[^a-z0-9]/gi, '-').toLowerCase();
|
const safeName = profile.name.replace(/[^a-z0-9]/gi, '-').toLowerCase();
|
||||||
const filename = `profile-${safeName}.voicebox.zip`;
|
const filename = `profile-${safeName}.talkbox.zip`;
|
||||||
|
|
||||||
await platform.filesystem.saveFile(filename, blob, [
|
await platform.filesystem.saveFile(filename, blob, [
|
||||||
{
|
{
|
||||||
name: 'Voicebox Profile',
|
name: 'TalkBox Profile',
|
||||||
extensions: ['zip'],
|
extensions: ['zip'],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const useAudioChannelStore = create<AudioChannelStore>()(
|
|||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'voicebox-audio-channels',
|
name: 'talkbox-audio-channels',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function invalidateAllServerData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultServerUrl(): string {
|
export function getDefaultServerUrl(): string {
|
||||||
const fallback = 'http://127.0.0.1:17493';
|
const fallback = 'http://127.0.0.1:17494';
|
||||||
|
|
||||||
if (!import.meta.env.PROD || typeof window === 'undefined') {
|
if (!import.meta.env.PROD || typeof window === 'undefined') {
|
||||||
return fallback;
|
return fallback;
|
||||||
@@ -46,11 +46,11 @@ export function getDefaultServerUrl(): string {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isLoopbackVoiceboxServerUrl(url: string): boolean {
|
export function isLoopbackTalkBoxServerUrl(url: string): boolean {
|
||||||
try {
|
try {
|
||||||
const parsed = new URL(url);
|
const parsed = new URL(url);
|
||||||
return (
|
return (
|
||||||
parsed.port === '17493' &&
|
parsed.port === '17494' &&
|
||||||
(parsed.hostname === '127.0.0.1' ||
|
(parsed.hostname === '127.0.0.1' ||
|
||||||
parsed.hostname === 'localhost' ||
|
parsed.hostname === 'localhost' ||
|
||||||
parsed.hostname === '[::1]' ||
|
parsed.hostname === '[::1]' ||
|
||||||
@@ -86,7 +86,7 @@ export const useServerStore = create<ServerStore>()(
|
|||||||
setCustomModelsDir: (dir) => set({ customModelsDir: dir }),
|
setCustomModelsDir: (dir) => set({ customModelsDir: dir }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'voicebox-server',
|
name: 'talkbox-server',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: 'voicebox-ui',
|
name: 'talkbox-ui',
|
||||||
partialize: (state) => ({
|
partialize: (state) => ({
|
||||||
selectedProfileId: state.selectedProfileId,
|
selectedProfileId: state.selectedProfileId,
|
||||||
theme: state.theme,
|
theme: state.theme,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Shared TypeScript types for the voicebox application
|
// Shared TypeScript types for the talkbox application
|
||||||
|
|
||||||
export interface VoiceProfile {
|
export interface VoiceProfile {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
+8
-8
@@ -1,4 +1,4 @@
|
|||||||
# Voicebox Backend
|
# TalkBox Backend
|
||||||
|
|
||||||
FastAPI server powering voice cloning, speech generation, and audio processing. Runs locally as a Tauri sidecar or standalone via `python -m backend.main`.
|
FastAPI server powering voice cloning, speech generation, and audio processing. Runs locally as a Tauri sidecar or standalone via `python -m backend.main`.
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ FastAPI server powering voice cloning, speech generation, and audio processing.
|
|||||||
just dev:server
|
just dev:server
|
||||||
|
|
||||||
# Standalone
|
# Standalone
|
||||||
python -m backend.main --host 127.0.0.1 --port 17493
|
python -m backend.main --host 127.0.0.1 --port 17494
|
||||||
|
|
||||||
# With custom data directory
|
# With custom data directory
|
||||||
python -m backend.main --data-dir /path/to/data
|
python -m backend.main --data-dir /path/to/data
|
||||||
@@ -75,7 +75,7 @@ Detection is handled by `utils/platform_detect.py`. Both backends implement the
|
|||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
90 endpoints organized by domain. Full interactive documentation available at `http://localhost:17493/docs` when the server is running.
|
90 endpoints organized by domain. Full interactive documentation available at `http://localhost:17494/docs` when the server is running.
|
||||||
|
|
||||||
| Domain | Prefix | Description |
|
| Domain | Prefix | Description |
|
||||||
|--------|--------|-------------|
|
|--------|--------|-------------|
|
||||||
@@ -96,29 +96,29 @@ Detection is handled by `utils/platform_detect.py`. Both backends implement the
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate speech
|
# Generate speech
|
||||||
curl -X POST http://localhost:17493/generate \
|
curl -X POST http://localhost:17494/generate \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"text": "Hello world", "profile_id": "...", "language": "en"}'
|
-d '{"text": "Hello world", "profile_id": "...", "language": "en"}'
|
||||||
|
|
||||||
# List profiles
|
# List profiles
|
||||||
curl http://localhost:17493/profiles
|
curl http://localhost:17494/profiles
|
||||||
|
|
||||||
# Stream generation status (SSE)
|
# Stream generation status (SSE)
|
||||||
curl http://localhost:17493/generate/{id}/status
|
curl http://localhost:17494/generate/{id}/status
|
||||||
```
|
```
|
||||||
|
|
||||||
## Data directory
|
## Data directory
|
||||||
|
|
||||||
```
|
```
|
||||||
{data_dir}/
|
{data_dir}/
|
||||||
voicebox.db # SQLite database
|
talkbox.db # SQLite database
|
||||||
profiles/{id}/ # Voice samples per profile
|
profiles/{id}/ # Voice samples per profile
|
||||||
generations/ # Generated audio files
|
generations/ # Generated audio files
|
||||||
cache/ # Voice prompt cache (memory + disk)
|
cache/ # Voice prompt cache (memory + disk)
|
||||||
backends/ # Downloaded CUDA binary (if applicable)
|
backends/ # Downloaded CUDA binary (if applicable)
|
||||||
```
|
```
|
||||||
|
|
||||||
Default location is the OS-specific app data directory. Override with `--data-dir` or the `VOICEBOX_DATA_DIR` environment variable.
|
Default location is the OS-specific app data directory. Override with `--data-dir` or the `TALKBOX_DATA_DIR` environment variable.
|
||||||
|
|
||||||
## Code quality
|
## Code quality
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -139,7 +139,7 @@ def create_app() -> FastAPI:
|
|||||||
mcp_app = mcp.http_app(path="/", transport="http")
|
mcp_app = mcp.http_app(path="/", transport="http")
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def voicebox_lifespan(app: FastAPI):
|
async def talkbox_lifespan(app: FastAPI):
|
||||||
await _run_startup(app)
|
await _run_startup(app)
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
@@ -149,16 +149,16 @@ def create_app() -> FastAPI:
|
|||||||
# startup still unloads whatever models were loaded.
|
# startup still unloads whatever models were loaded.
|
||||||
await _run_shutdown()
|
await _run_shutdown()
|
||||||
|
|
||||||
# compose_lifespan enters factories in order (voicebox startup →
|
# compose_lifespan enters factories in order (talkbox startup →
|
||||||
# MCP startup) and exits in LIFO (MCP teardown first → models
|
# MCP startup) and exits in LIFO (MCP teardown first → models
|
||||||
# unload last). That ordering matters on shutdown: FastMCP's
|
# unload last). That ordering matters on shutdown: FastMCP's
|
||||||
# __aexit__ cancels in-flight session tasks, and we want that to
|
# __aexit__ cancels in-flight session tasks, and we want that to
|
||||||
# happen *before* _run_shutdown yanks the TTS / Whisper / LLM
|
# happen *before* _run_shutdown yanks the TTS / Whisper / LLM
|
||||||
# models out from under any MCP request that was still generating.
|
# models out from under any MCP request that was still generating.
|
||||||
lifespan = compose_lifespan(voicebox_lifespan, mcp_app.router.lifespan_context)
|
lifespan = compose_lifespan(talkbox_lifespan, mcp_app.router.lifespan_context)
|
||||||
|
|
||||||
application = FastAPI(
|
application = FastAPI(
|
||||||
title="voicebox API",
|
title="talkbox API",
|
||||||
description="Production-quality Qwen3-TTS voice cloning API",
|
description="Production-quality Qwen3-TTS voice cloning API",
|
||||||
version=__version__,
|
version=__version__,
|
||||||
lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
@@ -179,13 +179,13 @@ def _configure_cors(application: FastAPI) -> None:
|
|||||||
default_origins = [
|
default_origins = [
|
||||||
"http://localhost:5173", # Vite dev server
|
"http://localhost:5173", # Vite dev server
|
||||||
"http://127.0.0.1:5173",
|
"http://127.0.0.1:5173",
|
||||||
"http://localhost:17493",
|
"http://localhost:17494",
|
||||||
"http://127.0.0.1:17493",
|
"http://127.0.0.1:17494",
|
||||||
"tauri://localhost", # Tauri webview (macOS)
|
"tauri://localhost", # Tauri webview (macOS)
|
||||||
"https://tauri.localhost", # Tauri webview (Windows/Linux)
|
"https://tauri.localhost", # Tauri webview (Windows/Linux)
|
||||||
"http://tauri.localhost", # Tauri webview (Windows, some builds)
|
"http://tauri.localhost", # Tauri webview (Windows, some builds)
|
||||||
]
|
]
|
||||||
env_origins = os.environ.get("VOICEBOX_CORS_ORIGINS", "")
|
env_origins = os.environ.get("TALKBOX_CORS_ORIGINS", "")
|
||||||
all_origins = default_origins + [o.strip() for o in env_origins.split(",") if o.strip()]
|
all_origins = default_origins + [o.strip() for o in env_origins.split(",") if o.strip()]
|
||||||
|
|
||||||
application.add_middleware(
|
application.add_middleware(
|
||||||
@@ -276,7 +276,7 @@ async def _run_startup(application: FastAPI) -> None:
|
|||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
logger.info("Voicebox v%s starting up", __version__)
|
logger.info("TalkBox v%s starting up", __version__)
|
||||||
logger.info(
|
logger.info(
|
||||||
"Python %s on %s %s (%s)",
|
"Python %s on %s %s (%s)",
|
||||||
sys.version.split()[0],
|
sys.version.split()[0],
|
||||||
@@ -358,7 +358,7 @@ async def _run_startup(application: FastAPI) -> None:
|
|||||||
|
|
||||||
async def _run_shutdown() -> None:
|
async def _run_shutdown() -> None:
|
||||||
"""Unload models on lifespan exit."""
|
"""Unload models on lifespan exit."""
|
||||||
logger.info("Voicebox server shutting down...")
|
logger.info("TalkBox server shutting down...")
|
||||||
try:
|
try:
|
||||||
tts.unload_tts_model()
|
tts.unload_tts_model()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
+14
-14
@@ -27,9 +27,9 @@ def build_server(cuda=False, rocm=False):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
cuda: If True, build with CUDA support and name the binary
|
cuda: If True, build with CUDA support and name the binary
|
||||||
voicebox-server-cuda instead of voicebox-server.
|
talkbox-server-cuda instead of talkbox-server.
|
||||||
rocm: If True, build with ROCm support and name the binary
|
rocm: If True, build with ROCm support and name the binary
|
||||||
voicebox-server-rocm instead of voicebox-server.
|
talkbox-server-rocm instead of talkbox-server.
|
||||||
"""
|
"""
|
||||||
if cuda and rocm:
|
if cuda and rocm:
|
||||||
raise ValueError("Cannot build with both CUDA and ROCm support")
|
raise ValueError("Cannot build with both CUDA and ROCm support")
|
||||||
@@ -37,11 +37,11 @@ def build_server(cuda=False, rocm=False):
|
|||||||
backend_dir = Path(__file__).parent
|
backend_dir = Path(__file__).parent
|
||||||
|
|
||||||
if rocm:
|
if rocm:
|
||||||
binary_name = "voicebox-server-rocm"
|
binary_name = "talkbox-server-rocm"
|
||||||
elif cuda:
|
elif cuda:
|
||||||
binary_name = "voicebox-server-cuda"
|
binary_name = "talkbox-server-cuda"
|
||||||
else:
|
else:
|
||||||
binary_name = "voicebox-server"
|
binary_name = "talkbox-server"
|
||||||
|
|
||||||
# PyInstaller arguments
|
# PyInstaller arguments
|
||||||
# CUDA and ROCm builds use --onedir so we can split the output into two archives:
|
# CUDA and ROCm builds use --onedir so we can split the output into two archives:
|
||||||
@@ -305,7 +305,7 @@ def build_server(cuda=False, rocm=False):
|
|||||||
"unidic_lite",
|
"unidic_lite",
|
||||||
"--hidden-import",
|
"--hidden-import",
|
||||||
"loguru",
|
"loguru",
|
||||||
# MCP server — Streamable-HTTP endpoint and the 4 voicebox.* tools.
|
# MCP server — Streamable-HTTP endpoint and the 4 talkbox.* tools.
|
||||||
# FastMCP pulls in a chain of deps (mcp, cyclopts, openapi-pydantic,
|
# FastMCP pulls in a chain of deps (mcp, cyclopts, openapi-pydantic,
|
||||||
# etc.) that don't auto-discover cleanly under PyInstaller, so we
|
# etc.) that don't auto-discover cleanly under PyInstaller, so we
|
||||||
# collect them whole. Small compared to torch.
|
# collect them whole. Small compared to torch.
|
||||||
@@ -673,10 +673,10 @@ def build_server(cuda=False, rocm=False):
|
|||||||
|
|
||||||
|
|
||||||
def build_shim():
|
def build_shim():
|
||||||
"""Build the voicebox-mcp stdio shim as a tiny standalone binary.
|
"""Build the talkbox-mcp stdio shim as a tiny standalone binary.
|
||||||
|
|
||||||
This is the bridge for MCP clients that only speak stdio — it proxies
|
This is the bridge for MCP clients that only speak stdio — it proxies
|
||||||
JSON-RPC to the main voicebox-server's /mcp endpoint. Keep it small: no
|
JSON-RPC to the main talkbox-server's /mcp endpoint. Keep it small: no
|
||||||
torch, no ML deps, just httpx + asyncio.
|
torch, no ML deps, just httpx + asyncio.
|
||||||
"""
|
"""
|
||||||
backend_dir = Path(__file__).parent
|
backend_dir = Path(__file__).parent
|
||||||
@@ -685,7 +685,7 @@ def build_shim():
|
|||||||
"mcp_shim/__main__.py",
|
"mcp_shim/__main__.py",
|
||||||
"--onefile",
|
"--onefile",
|
||||||
"--name",
|
"--name",
|
||||||
"voicebox-mcp",
|
"talkbox-mcp",
|
||||||
# Stdio-only — no console hiding needed on Windows since the parent
|
# Stdio-only — no console hiding needed on Windows since the parent
|
||||||
# MCP client is spawning this as a child process and wants stdio.
|
# MCP client is spawning this as a child process and wants stdio.
|
||||||
"--hidden-import",
|
"--hidden-import",
|
||||||
@@ -759,25 +759,25 @@ def build_shim():
|
|||||||
|
|
||||||
os.chdir(backend_dir)
|
os.chdir(backend_dir)
|
||||||
PyInstaller.__main__.run(args)
|
PyInstaller.__main__.run(args)
|
||||||
logger.info("Shim built: %s", backend_dir / "dist" / "voicebox-mcp")
|
logger.info("Shim built: %s", backend_dir / "dist" / "talkbox-mcp")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Build voicebox binaries")
|
parser = argparse.ArgumentParser(description="Build talkbox binaries")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--cuda",
|
"--cuda",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Build CUDA-enabled binary (voicebox-server-cuda)",
|
help="Build CUDA-enabled binary (talkbox-server-cuda)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--rocm",
|
"--rocm",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Build ROCm-enabled binary (voicebox-server-rocm) for AMD GPUs",
|
help="Build ROCm-enabled binary (talkbox-server-rocm) for AMD GPUs",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--shim",
|
"--shim",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Build the voicebox-mcp stdio shim binary instead of the server",
|
help="Build the talkbox-mcp stdio shim binary instead of the server",
|
||||||
)
|
)
|
||||||
cli_args = parser.parse_args()
|
cli_args = parser.parse_args()
|
||||||
if cli_args.shim:
|
if cli_args.shim:
|
||||||
|
|||||||
+12
-12
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Configuration module for voicebox backend.
|
Configuration module for talkbox backend.
|
||||||
|
|
||||||
Handles data directory configuration for production bundling.
|
Handles data directory configuration for production bundling.
|
||||||
"""
|
"""
|
||||||
@@ -11,9 +11,9 @@ from pathlib import Path
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Allow users to override the HuggingFace model download directory.
|
# Allow users to override the HuggingFace model download directory.
|
||||||
# Set VOICEBOX_MODELS_DIR to an absolute path before starting the server.
|
# Set TALKBOX_MODELS_DIR to an absolute path before starting the server.
|
||||||
# This sets HF_HUB_CACHE so all huggingface_hub downloads go to that path.
|
# This sets HF_HUB_CACHE so all huggingface_hub downloads go to that path.
|
||||||
_custom_models_dir = os.environ.get("VOICEBOX_MODELS_DIR")
|
_custom_models_dir = os.environ.get("TALKBOX_MODELS_DIR")
|
||||||
if _custom_models_dir:
|
if _custom_models_dir:
|
||||||
os.environ["HF_HUB_CACHE"] = _custom_models_dir
|
os.environ["HF_HUB_CACHE"] = _custom_models_dir
|
||||||
logger.info("Model download path set to: %s", _custom_models_dir)
|
logger.info("Model download path set to: %s", _custom_models_dir)
|
||||||
@@ -107,7 +107,7 @@ def resolve_storage_path(path: str | Path | None) -> Path | None:
|
|||||||
|
|
||||||
def get_db_path() -> Path:
|
def get_db_path() -> Path:
|
||||||
"""Get database file path."""
|
"""Get database file path."""
|
||||||
return _data_dir / "voicebox.db"
|
return _data_dir / "talkbox.db"
|
||||||
|
|
||||||
|
|
||||||
def get_profiles_dir() -> Path:
|
def get_profiles_dir() -> Path:
|
||||||
@@ -145,15 +145,15 @@ def get_models_dir() -> Path:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
# Voicebox Cloud (backup & sync). Two hosts: the web app owns auth + device
|
# TalkBox Cloud (backup & sync). Two hosts: the web app owns auth + device
|
||||||
# pairing (voicebox.sh), the API owns sync + account endpoints
|
# pairing (talkbox.sh), the API owns sync + account endpoints
|
||||||
# (api.voicebox.sh). Override both for local development, e.g.
|
# (api.talkbox.sh). Override both for local development, e.g.
|
||||||
# VOICEBOX_CLOUD_URL=http://localhost:17592 VOICEBOX_CLOUD_API_URL=http://localhost:17593
|
# TALKBOX_CLOUD_URL=http://localhost:17592 TALKBOX_CLOUD_API_URL=http://localhost:17593
|
||||||
def get_cloud_web_url() -> str:
|
def get_cloud_web_url() -> str:
|
||||||
"""Base URL of the Voicebox Cloud web app (auth + /connect + exchange)."""
|
"""Base URL of the TalkBox Cloud web app (auth + /connect + exchange)."""
|
||||||
return os.environ.get("VOICEBOX_CLOUD_URL", "https://voicebox.sh").rstrip("/")
|
return os.environ.get("TALKBOX_CLOUD_URL", "https://talkbox.sh").rstrip("/")
|
||||||
|
|
||||||
|
|
||||||
def get_cloud_api_url() -> str:
|
def get_cloud_api_url() -> str:
|
||||||
"""Base URL of the Voicebox Cloud API (bearer-authenticated sync/account)."""
|
"""Base URL of the TalkBox Cloud API (bearer-authenticated sync/account)."""
|
||||||
return os.environ.get("VOICEBOX_CLOUD_API_URL", "https://api.voicebox.sh").rstrip("/")
|
return os.environ.get("TALKBOX_CLOUD_API_URL", "https://api.talkbox.sh").rstrip("/")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""Column-level migrations for the voicebox SQLite database.
|
"""Column-level migrations for the talkbox SQLite database.
|
||||||
|
|
||||||
Why not Alembic? voicebox is a single-user desktop app shipping as a
|
Why not Alembic? talkbox is a single-user desktop app shipping as a
|
||||||
PyInstaller binary. Every user has exactly one SQLite file. Alembic's
|
PyInstaller binary. Every user has exactly one SQLite file. Alembic's
|
||||||
strengths -- migration tracking across environments, rollback, team
|
strengths -- migration tracking across environments, rollback, team
|
||||||
coordination -- don't apply here and would add bundling complexity
|
coordination -- don't apply here and would add bundling complexity
|
||||||
@@ -249,7 +249,7 @@ def _migrate_mcp_bindings(engine, inspector, tables: set[str]) -> None:
|
|||||||
"""Drop the legacy ``default_intent`` column and add ``default_personality``.
|
"""Drop the legacy ``default_intent`` column and add ``default_personality``.
|
||||||
|
|
||||||
The intent tri-state (respond / rewrite / compose) has been collapsed
|
The intent tri-state (respond / rewrite / compose) has been collapsed
|
||||||
to a boolean: when true, ``voicebox.speak`` rewrites input through the
|
to a boolean: when true, ``talkbox.speak`` rewrites input through the
|
||||||
profile's personality LLM before TTS.
|
profile's personality LLM before TTS.
|
||||||
"""
|
"""
|
||||||
if "mcp_client_bindings" not in tables:
|
if "mcp_client_bindings" not in tables:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""ORM model definitions for the voicebox SQLite database."""
|
"""ORM model definitions for the talkbox SQLite database."""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import uuid
|
import uuid
|
||||||
@@ -207,7 +207,7 @@ class CaptureSettings(Base):
|
|||||||
# Default OFF — opting in is what triggers the macOS Input Monitoring TCC
|
# Default OFF — opting in is what triggers the macOS Input Monitoring TCC
|
||||||
# prompt. We deliberately don't spawn the global keyboard tap until the
|
# prompt. We deliberately don't spawn the global keyboard tap until the
|
||||||
# user flips this on so a fresh-install user doesn't see a scary
|
# user flips this on so a fresh-install user doesn't see a scary
|
||||||
# "Voicebox would like to receive keystrokes from any application" dialog
|
# "TalkBox would like to receive keystrokes from any application" dialog
|
||||||
# before they've even opened the Captures tab.
|
# before they've even opened the Captures tab.
|
||||||
hotkey_enabled = Column(Boolean, nullable=False, default=False)
|
hotkey_enabled = Column(Boolean, nullable=False, default=False)
|
||||||
# Lists of keytap key names (e.g. "MetaRight", "ControlRight"). Right-hand
|
# Lists of keytap key names (e.g. "MetaRight", "ControlRight"). Right-hand
|
||||||
@@ -235,12 +235,12 @@ class GenerationSettings(Base):
|
|||||||
|
|
||||||
|
|
||||||
class CloudSettings(Base):
|
class CloudSettings(Base):
|
||||||
"""Singleton row holding the link to a Voicebox Cloud account.
|
"""Singleton row holding the link to a TalkBox Cloud account.
|
||||||
|
|
||||||
Populated by the "Log in with browser" pairing flow (see services/cloud.py):
|
Populated by the "Log in with browser" pairing flow (see services/cloud.py):
|
||||||
the browser hands back a one-time code, which the backend exchanges for an
|
the browser hands back a one-time code, which the backend exchanges for an
|
||||||
``api_key`` it stores here. The key is a bearer credential for
|
``api_key`` it stores here. The key is a bearer credential for
|
||||||
api.voicebox.sh — auth only, never an encryption key (E2E key material lives
|
api.talkbox.sh — auth only, never an encryption key (E2E key material lives
|
||||||
elsewhere). Stored in the local app database alongside the user's other data;
|
elsewhere). Stored in the local app database alongside the user's other data;
|
||||||
moving it to the OS keychain is a future hardening step. The ``id`` is
|
moving it to the OS keychain is a future hardening step. The ``id`` is
|
||||||
always 1; a null ``api_key`` means "not connected".
|
always 1; a null ``api_key`` means "not connected".
|
||||||
@@ -261,9 +261,9 @@ class MCPClientBinding(Base):
|
|||||||
|
|
||||||
Lets users bind distinct voices to distinct agents — e.g. Claude Code
|
Lets users bind distinct voices to distinct agents — e.g. Claude Code
|
||||||
speaks in "Morgan," Cursor in "Scarlett." The MCP client identifies
|
speaks in "Morgan," Cursor in "Scarlett." The MCP client identifies
|
||||||
itself via the ``X-Voicebox-Client-Id`` HTTP header; direct-HTTP
|
itself via the ``X-TalkBox-Client-Id`` HTTP header; direct-HTTP
|
||||||
clients set it in their MCP config's ``headers`` block, the stdio
|
clients set it in their MCP config's ``headers`` block, the stdio
|
||||||
shim forwards it from the ``VOICEBOX_CLIENT_ID`` env var.
|
shim forwards it from the ``TALKBOX_CLIENT_ID`` env var.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tablename__ = "mcp_client_bindings"
|
__tablename__ = "mcp_client_bindings"
|
||||||
@@ -272,7 +272,7 @@ class MCPClientBinding(Base):
|
|||||||
label = Column(String, nullable=True) # display name
|
label = Column(String, nullable=True) # display name
|
||||||
profile_id = Column(String, ForeignKey("profiles.id"), nullable=True)
|
profile_id = Column(String, ForeignKey("profiles.id"), nullable=True)
|
||||||
default_engine = Column(String, nullable=True)
|
default_engine = Column(String, nullable=True)
|
||||||
# When true, voicebox.speak routes through the profile's personality LLM
|
# When true, talkbox.speak routes through the profile's personality LLM
|
||||||
# (rewrite) before TTS by default. Callers can still override per call.
|
# (rewrite) before TTS by default. Callers can still override per call.
|
||||||
default_personality = Column(Boolean, nullable=False, default=False)
|
default_personality = Column(Boolean, nullable=False, default=False)
|
||||||
last_seen_at = Column(DateTime, nullable=True)
|
last_seen_at = Column(DateTime, nullable=True)
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
"""Entry point for the voicebox backend.
|
"""Entry point for the talkbox backend.
|
||||||
|
|
||||||
Imports the configured FastAPI app and provides a ``python -m backend.main``
|
Imports the configured FastAPI app and provides a ``python -m backend.main``
|
||||||
entry point for development.
|
entry point for development.
|
||||||
@@ -11,7 +11,7 @@ from .app import app # noqa: F401 -- re-export for uvicorn "backend.main:app"
|
|||||||
from . import config, database
|
from . import config, database
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="voicebox backend server")
|
parser = argparse.ArgumentParser(description="talkbox backend server")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--host",
|
"--host",
|
||||||
type=str,
|
type=str,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Voicebox MCP server
|
# TalkBox MCP server
|
||||||
|
|
||||||
Local **Model Context Protocol** server — lets any MCP-aware agent
|
Local **Model Context Protocol** server — lets any MCP-aware agent
|
||||||
(Claude Code, Cursor, Windsurf, VS Code MCP extensions, etc.) speak text
|
(Claude Code, Cursor, Windsurf, VS Code MCP extensions, etc.) speak text
|
||||||
in your cloned voices, transcribe audio, and browse captures.
|
in your cloned voices, transcribe audio, and browse captures.
|
||||||
|
|
||||||
The server runs inside the same `uvicorn` process as the rest of Voicebox
|
The server runs inside the same `uvicorn` process as the rest of TalkBox
|
||||||
and is mounted at `/mcp` (Streamable HTTP transport).
|
and is mounted at `/mcp` (Streamable HTTP transport).
|
||||||
|
|
||||||
## Install into your agent
|
## Install into your agent
|
||||||
@@ -14,23 +14,23 @@ Preferred — direct HTTP:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"voicebox": {
|
"talkbox": {
|
||||||
"url": "http://127.0.0.1:17493/mcp",
|
"url": "http://127.0.0.1:17494/mcp",
|
||||||
"headers": { "X-Voicebox-Client-Id": "claude-code" }
|
"headers": { "X-TalkBox-Client-Id": "claude-code" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Fallback — stdio shim (when the client doesn't speak HTTP MCP). The
|
Fallback — stdio shim (when the client doesn't speak HTTP MCP). The
|
||||||
`voicebox-mcp` binary ships inside the Voicebox.app bundle:
|
`talkbox-mcp` binary ships inside the TalkBox.app bundle:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"mcpServers": {
|
"mcpServers": {
|
||||||
"voicebox": {
|
"talkbox": {
|
||||||
"command": "/Applications/Voicebox.app/Contents/MacOS/voicebox-mcp",
|
"command": "/Applications/TalkBox.app/Contents/MacOS/talkbox-mcp",
|
||||||
"env": { "VOICEBOX_CLIENT_ID": "claude-code" }
|
"env": { "TALKBOX_CLIENT_ID": "claude-code" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,25 +39,25 @@ Fallback — stdio shim (when the client doesn't speak HTTP MCP). The
|
|||||||
Claude Code one-liner:
|
Claude Code one-liner:
|
||||||
|
|
||||||
```
|
```
|
||||||
claude mcp add voicebox \
|
claude mcp add talkbox \
|
||||||
--transport http \
|
--transport http \
|
||||||
--url http://127.0.0.1:17493/mcp \
|
--url http://127.0.0.1:17494/mcp \
|
||||||
--header "X-Voicebox-Client-Id: claude-code"
|
--header "X-TalkBox-Client-Id: claude-code"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
| Name | Purpose |
|
| Name | Purpose |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `voicebox.speak` | Speak text in a voice profile. Returns a generation id you can poll. |
|
| `talkbox.speak` | Speak text in a voice profile. Returns a generation id you can poll. |
|
||||||
| `voicebox.transcribe` | Whisper transcription of a base64 blob or an absolute local path. |
|
| `talkbox.transcribe` | Whisper transcription of a base64 blob or an absolute local path. |
|
||||||
| `voicebox.list_captures` | Recent captures (dictation / recording / file) with transcripts. |
|
| `talkbox.list_captures` | Recent captures (dictation / recording / file) with transcripts. |
|
||||||
| `voicebox.list_profiles` | Available voice profiles (cloned + preset). |
|
| `talkbox.list_profiles` | Available voice profiles (cloned + preset). |
|
||||||
|
|
||||||
All tools resolve voice profiles in this precedence:
|
All tools resolve voice profiles in this precedence:
|
||||||
|
|
||||||
1. Explicit `profile` arg (name or id — case-insensitive)
|
1. Explicit `profile` arg (name or id — case-insensitive)
|
||||||
2. Per-client binding keyed by `X-Voicebox-Client-Id`
|
2. Per-client binding keyed by `X-TalkBox-Client-Id`
|
||||||
3. `capture_settings.default_playback_voice_id` (global default)
|
3. `capture_settings.default_playback_voice_id` (global default)
|
||||||
|
|
||||||
Bindings are managed via `GET|PUT /mcp/bindings` or in the app under
|
Bindings are managed via `GET|PUT /mcp/bindings` or in the app under
|
||||||
@@ -66,11 +66,11 @@ Settings → MCP.
|
|||||||
## Debug with MCP Inspector
|
## Debug with MCP Inspector
|
||||||
|
|
||||||
```
|
```
|
||||||
npx @modelcontextprotocol/inspector http://127.0.0.1:17493/mcp
|
npx @modelcontextprotocol/inspector http://127.0.0.1:17494/mcp
|
||||||
```
|
```
|
||||||
|
|
||||||
Point it at the URL, hit "List tools," call `voicebox.list_profiles`
|
Point it at the URL, hit "List tools," call `talkbox.list_profiles`
|
||||||
first to confirm wiring, then `voicebox.speak` for end-to-end.
|
first to confirm wiring, then `talkbox.speak` for end-to-end.
|
||||||
|
|
||||||
## Non-MCP REST surface
|
## Non-MCP REST surface
|
||||||
|
|
||||||
@@ -78,9 +78,9 @@ first to confirm wiring, then `voicebox.speak` for end-to-end.
|
|||||||
don't speak MCP (shell scripts, ACP, A2A):
|
don't speak MCP (shell scripts, ACP, A2A):
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -X POST http://127.0.0.1:17493/speak \
|
curl -X POST http://127.0.0.1:17494/speak \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-H 'X-Voicebox-Client-Id: claude-code' \
|
-H 'X-TalkBox-Client-Id: claude-code' \
|
||||||
-d '{"text":"Build complete.","profile":"Morgan"}'
|
-d '{"text":"Build complete.","profile":"Morgan"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Model Context Protocol server — exposes Voicebox tools to local AI agents.
|
"""Model Context Protocol server — exposes TalkBox tools to local AI agents.
|
||||||
|
|
||||||
Mounts a FastMCP instance at /mcp on the main FastAPI app (Streamable HTTP).
|
Mounts a FastMCP instance at /mcp on the main FastAPI app (Streamable HTTP).
|
||||||
A bundled stdio shim (backend/mcp_shim) forwards JSON-RPC into the same
|
A bundled stdio shim (backend/mcp_shim) forwards JSON-RPC into the same
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"""Per-request client identity for MCP calls.
|
"""Per-request client identity for MCP calls.
|
||||||
|
|
||||||
MCP clients identify themselves via an ``X-Voicebox-Client-Id`` HTTP header
|
MCP clients identify themselves via an ``X-TalkBox-Client-Id`` HTTP header
|
||||||
(direct-HTTP clients set it in their MCP config; the stdio shim forwards it
|
(direct-HTTP clients set it in their MCP config; the stdio shim forwards it
|
||||||
from the ``VOICEBOX_CLIENT_ID`` env var). Middleware copies the value into a
|
from the ``TALKBOX_CLIENT_ID`` env var). Middleware copies the value into a
|
||||||
ContextVar so tool implementations can read it without plumbing the request
|
ContextVar so tool implementations can read it without plumbing the request
|
||||||
object through every service call.
|
object through every service call.
|
||||||
"""
|
"""
|
||||||
@@ -25,7 +25,7 @@ logger = logging.getLogger(__name__)
|
|||||||
# don't get garbage-collected mid-flight (cf. asyncio.create_task docs).
|
# don't get garbage-collected mid-flight (cf. asyncio.create_task docs).
|
||||||
_pending_stamps: set[asyncio.Task] = set()
|
_pending_stamps: set[asyncio.Task] = set()
|
||||||
|
|
||||||
CLIENT_ID_HEADER = "X-Voicebox-Client-Id"
|
CLIENT_ID_HEADER = "X-TalkBox-Client-Id"
|
||||||
|
|
||||||
# Tool handlers read this to apply per-client voice bindings.
|
# Tool handlers read this to apply per-client voice bindings.
|
||||||
current_client_id: ContextVar[str | None] = ContextVar(
|
current_client_id: ContextVar[str | None] = ContextVar(
|
||||||
@@ -33,7 +33,7 @@ current_client_id: ContextVar[str | None] = ContextVar(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Remote address of the in-flight request. Used by tools that gate
|
# Remote address of the in-flight request. Used by tools that gate
|
||||||
# host-filesystem access to loopback callers (see voicebox.transcribe).
|
# host-filesystem access to loopback callers (see talkbox.transcribe).
|
||||||
current_remote_addr: ContextVar[str | None] = ContextVar(
|
current_remote_addr: ContextVar[str | None] = ContextVar(
|
||||||
"current_remote_addr", default=None
|
"current_remote_addr", default=None
|
||||||
)
|
)
|
||||||
@@ -54,26 +54,26 @@ def request_is_loopback() -> bool:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Endpoints that consume X-Voicebox-Client-Id for its MCP-semantic
|
# Endpoints that consume X-TalkBox-Client-Id for its MCP-semantic
|
||||||
# meaning (per-client profile resolution + per-client default_personality).
|
# meaning (per-client profile resolution + per-client default_personality).
|
||||||
# These are the paths where a stamp into last_seen_at is accurate.
|
# These are the paths where a stamp into last_seen_at is accurate.
|
||||||
# Unrelated REST traffic that happens to set the header is intentionally
|
# Unrelated REST traffic that happens to set the header is intentionally
|
||||||
# ignored so the Settings UI's "last heard from" column only reflects
|
# ignored so the Settings UI's "last heard from" column only reflects
|
||||||
# calls that actually acted on the client's bindings.
|
# calls that actually acted on the client's bindings.
|
||||||
#
|
#
|
||||||
# - /mcp — FastMCP tool calls (voicebox.speak, voicebox.transcribe, …)
|
# - /mcp — FastMCP tool calls (talkbox.speak, talkbox.transcribe, …)
|
||||||
# and the /mcp/bindings admin surface. The admin surface is never
|
# and the /mcp/bindings admin surface. The admin surface is never
|
||||||
# called with the header in practice (the frontend manages bindings
|
# called with the header in practice (the frontend manages bindings
|
||||||
# over plain REST), so the `startswith("/mcp")` match doesn't cause
|
# over plain REST), so the `startswith("/mcp")` match doesn't cause
|
||||||
# false stamps.
|
# false stamps.
|
||||||
# - /speak — REST mirror of voicebox.speak for non-MCP agents (shell
|
# - /speak — REST mirror of talkbox.speak for non-MCP agents (shell
|
||||||
# scripts, ACP, A2A). Uses the same per-client binding lookup, so its
|
# scripts, ACP, A2A). Uses the same per-client binding lookup, so its
|
||||||
# callers belong in the last-seen list too.
|
# callers belong in the last-seen list too.
|
||||||
_STAMPED_PATH_PREFIXES: tuple[str, ...] = ("/mcp", "/speak")
|
_STAMPED_PATH_PREFIXES: tuple[str, ...] = ("/mcp", "/speak")
|
||||||
|
|
||||||
|
|
||||||
class ClientIdMiddleware(BaseHTTPMiddleware):
|
class ClientIdMiddleware(BaseHTTPMiddleware):
|
||||||
"""Copy X-Voicebox-Client-Id into a ContextVar and stamp last_seen_at
|
"""Copy X-TalkBox-Client-Id into a ContextVar and stamp last_seen_at
|
||||||
for requests that act on the caller's MCP bindings."""
|
for requests that act on the caller's MCP bindings."""
|
||||||
|
|
||||||
def __init__(self, app: ASGIApp) -> None:
|
def __init__(self, app: ASGIApp) -> None:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""In-memory pub/sub for speaking-pill SSE broadcasts.
|
"""In-memory pub/sub for speaking-pill SSE broadcasts.
|
||||||
|
|
||||||
MCP ``voicebox.speak`` calls and the REST ``POST /speak`` route publish
|
MCP ``talkbox.speak`` calls and the REST ``POST /speak`` route publish
|
||||||
start/end events that DictateWindow subscribes to via /events/speak, so the
|
start/end events that DictateWindow subscribes to via /events/speak, so the
|
||||||
floating pill surfaces whenever an agent is speaking.
|
floating pill surfaces whenever an agent is speaking.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
The MCP endpoint lives at ``/mcp`` (Streamable HTTP transport). Modern MCP
|
The MCP endpoint lives at ``/mcp`` (Streamable HTTP transport). Modern MCP
|
||||||
clients (Claude Code, Cursor, Windsurf, VS Code MCP extensions) connect
|
clients (Claude Code, Cursor, Windsurf, VS Code MCP extensions) connect
|
||||||
directly via URL; older stdio-only clients use the ``voicebox-mcp`` shim
|
directly via URL; older stdio-only clients use the ``talkbox-mcp`` shim
|
||||||
binary bundled with the desktop app.
|
binary bundled with the desktop app.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -23,12 +23,12 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
def build_mcp_server() -> FastMCP:
|
def build_mcp_server() -> FastMCP:
|
||||||
"""Create the FastMCP instance with Voicebox tools registered."""
|
"""Create the FastMCP instance with TalkBox tools registered."""
|
||||||
mcp = FastMCP(
|
mcp = FastMCP(
|
||||||
name="voicebox",
|
name="talkbox",
|
||||||
instructions=(
|
instructions=(
|
||||||
"Voicebox is a local voice I/O layer. Use `voicebox.speak` to "
|
"TalkBox is a local voice I/O layer. Use `talkbox.speak` to "
|
||||||
"play text in a voice profile, `voicebox.transcribe` for "
|
"play text in a voice profile, `talkbox.transcribe` for "
|
||||||
"audio→text, and the `list_*` tools to discover profiles and "
|
"audio→text, and the `list_*` tools to discover profiles and "
|
||||||
"captures."
|
"captures."
|
||||||
),
|
),
|
||||||
@@ -63,7 +63,7 @@ def mount_into(
|
|||||||
def compose_lifespan(*lifespans):
|
def compose_lifespan(*lifespans):
|
||||||
"""Combine multiple async context managers into a single FastAPI lifespan.
|
"""Combine multiple async context managers into a single FastAPI lifespan.
|
||||||
|
|
||||||
Used by ``create_app`` to run the existing Voicebox startup/shutdown
|
Used by ``create_app`` to run the existing TalkBox startup/shutdown
|
||||||
together with FastMCP's session manager (which MUST run in the
|
together with FastMCP's session manager (which MUST run in the
|
||||||
ASGI lifespan for Streamable HTTP to work).
|
ASGI lifespan for Streamable HTTP to work).
|
||||||
"""
|
"""
|
||||||
|
|||||||
+17
-17
@@ -1,7 +1,7 @@
|
|||||||
"""Voicebox MCP tool implementations.
|
"""TalkBox MCP tool implementations.
|
||||||
|
|
||||||
Thin wrappers over existing services/routes. Tools are registered with dotted
|
Thin wrappers over existing services/routes. Tools are registered with dotted
|
||||||
names (``voicebox.speak`` etc.) so they look natural in agent logs —
|
names (``talkbox.speak`` etc.) so they look natural in agent logs —
|
||||||
the Python function name stays snake_case.
|
the Python function name stays snake_case.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -33,17 +33,17 @@ MAX_TRANSCRIBE_BYTES = 200 * 1024 * 1024 # 200 MB
|
|||||||
|
|
||||||
|
|
||||||
def register_tools(mcp: FastMCP) -> None:
|
def register_tools(mcp: FastMCP) -> None:
|
||||||
"""Attach all Voicebox tools to the given FastMCP instance."""
|
"""Attach all TalkBox tools to the given FastMCP instance."""
|
||||||
|
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
name="voicebox.speak",
|
name="talkbox.speak",
|
||||||
description=(
|
description=(
|
||||||
"Speak text in a Voicebox voice profile. Returns a generation id "
|
"Speak text in a TalkBox voice profile. Returns a generation id "
|
||||||
"the caller can poll at /generate/{id}/status. Audio plays on the "
|
"the caller can poll at /generate/{id}/status. Audio plays on the "
|
||||||
"user's speakers and is saved to the Captures / History tab."
|
"user's speakers and is saved to the Captures / History tab."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def voicebox_speak(
|
async def talkbox_speak(
|
||||||
text: str,
|
text: str,
|
||||||
profile: str | None = None,
|
profile: str | None = None,
|
||||||
engine: str | None = None,
|
engine: str | None = None,
|
||||||
@@ -79,7 +79,7 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
raise ValueError(
|
raise ValueError(
|
||||||
"No voice profile resolved. Pass `profile=` with a "
|
"No voice profile resolved. Pass `profile=` with a "
|
||||||
"voice profile name or id, or set a default voice in "
|
"voice profile name or id, or set a default voice in "
|
||||||
"Voicebox → Settings → MCP."
|
"TalkBox → Settings → MCP."
|
||||||
)
|
)
|
||||||
|
|
||||||
binding = None
|
binding = None
|
||||||
@@ -113,14 +113,14 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
name="voicebox.transcribe",
|
name="talkbox.transcribe",
|
||||||
description=(
|
description=(
|
||||||
"Transcribe an audio clip to text using Voicebox's local Whisper. "
|
"Transcribe an audio clip to text using TalkBox's local Whisper. "
|
||||||
"Pass exactly one of `audio_base64` (bytes as base64) or "
|
"Pass exactly one of `audio_base64` (bytes as base64) or "
|
||||||
"`audio_path` (absolute local file path — loopback callers only)."
|
"`audio_path` (absolute local file path — loopback callers only)."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def voicebox_transcribe(
|
async def talkbox_transcribe(
|
||||||
audio_base64: str | None = None,
|
audio_base64: str | None = None,
|
||||||
audio_path: str | None = None,
|
audio_path: str | None = None,
|
||||||
language: str | None = None,
|
language: str | None = None,
|
||||||
@@ -132,7 +132,7 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Absolute-path mode: validate and transcribe in place. Restricted
|
# Absolute-path mode: validate and transcribe in place. Restricted
|
||||||
# to loopback callers so a Voicebox bound on 0.0.0.0 doesn't double
|
# to loopback callers so a TalkBox bound on 0.0.0.0 doesn't double
|
||||||
# as an unauthenticated arbitrary-local-file read primitive.
|
# as an unauthenticated arbitrary-local-file read primitive.
|
||||||
if audio_path is not None:
|
if audio_path is not None:
|
||||||
if not request_is_loopback():
|
if not request_is_loopback():
|
||||||
@@ -171,13 +171,13 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
tmp_path.unlink(missing_ok=True)
|
tmp_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
name="voicebox.list_captures",
|
name="talkbox.list_captures",
|
||||||
description=(
|
description=(
|
||||||
"List recent voice captures (dictations, recordings, uploads) "
|
"List recent voice captures (dictations, recordings, uploads) "
|
||||||
"with their transcripts. Most-recent first."
|
"with their transcripts. Most-recent first."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def voicebox_list_captures(
|
async def talkbox_list_captures(
|
||||||
limit: int = 20, offset: int = 0
|
limit: int = 20, offset: int = 0
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
if not (1 <= limit <= 200):
|
if not (1 <= limit <= 200):
|
||||||
@@ -199,13 +199,13 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
@mcp.tool(
|
@mcp.tool(
|
||||||
name="voicebox.list_profiles",
|
name="talkbox.list_profiles",
|
||||||
description=(
|
description=(
|
||||||
"List available voice profiles (both cloned voices and presets). "
|
"List available voice profiles (both cloned voices and presets). "
|
||||||
"Use the returned `name` with voicebox.speak(profile=...)."
|
"Use the returned `name` with talkbox.speak(profile=...)."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def voicebox_list_profiles() -> dict[str, Any]:
|
async def talkbox_list_profiles() -> dict[str, Any]:
|
||||||
db = next(get_db())
|
db = next(get_db())
|
||||||
try:
|
try:
|
||||||
profiles = await profiles_service.list_profiles(db)
|
profiles = await profiles_service.list_profiles(db)
|
||||||
@@ -318,7 +318,7 @@ async def _transcribe_file(
|
|||||||
) and not whisper._is_model_cached(model_size):
|
) and not whisper._is_model_cached(model_size):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Whisper model '{model_size}' is not yet downloaded. Open "
|
f"Whisper model '{model_size}' is not yet downloaded. Open "
|
||||||
"Voicebox → Settings → Models to download it first."
|
"TalkBox → Settings → Models to download it first."
|
||||||
)
|
)
|
||||||
|
|
||||||
text = await whisper.transcribe(str(path), language, model_size)
|
text = await whisper.transcribe(str(path), language, model_size)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"""Stdio → Streamable HTTP bridge for the Voicebox MCP server.
|
"""Stdio → Streamable HTTP bridge for the TalkBox MCP server.
|
||||||
|
|
||||||
Some MCP clients only know how to spawn a subprocess and talk to it over
|
Some MCP clients only know how to spawn a subprocess and talk to it over
|
||||||
stdin/stdout (the "stdio" transport). This package is a ~150-line adapter:
|
stdin/stdout (the "stdio" transport). This package is a ~150-line adapter:
|
||||||
the client spawns us as ``voicebox-mcp``; we proxy every JSON-RPC frame
|
the client spawns us as ``talkbox-mcp``; we proxy every JSON-RPC frame
|
||||||
to http://127.0.0.1:17493/mcp/ and stream responses back out.
|
to http://127.0.0.1:17494/mcp/ and stream responses back out.
|
||||||
|
|
||||||
All the real work (tools, models, inference) lives in the Voicebox server
|
All the real work (tools, models, inference) lives in the TalkBox server
|
||||||
process — this package contains no business logic.
|
process — this package contains no business logic.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
"""voicebox-mcp — stdio ↔ Streamable-HTTP MCP proxy.
|
"""talkbox-mcp — stdio ↔ Streamable-HTTP MCP proxy.
|
||||||
|
|
||||||
Some MCP clients only speak stdio. They spawn this binary, we pipe each
|
Some MCP clients only speak stdio. They spawn this binary, we pipe each
|
||||||
JSON-RPC message to ``http://127.0.0.1:<port>/mcp/``, and stream the
|
JSON-RPC message to ``http://127.0.0.1:<port>/mcp/``, and stream the
|
||||||
server's response back. The Voicebox server does all the real work.
|
server's response back. The TalkBox server does all the real work.
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
VOICEBOX_PORT Voicebox server port (default 17493).
|
TALKBOX_PORT TalkBox server port (default 17494).
|
||||||
VOICEBOX_HOST Host (default 127.0.0.1).
|
TALKBOX_HOST Host (default 127.0.0.1).
|
||||||
VOICEBOX_CLIENT_ID Forwarded as X-Voicebox-Client-Id on every request.
|
TALKBOX_CLIENT_ID Forwarded as X-TalkBox-Client-Id on every request.
|
||||||
|
|
||||||
Stdout is JSON-RPC only. Diagnostics go to stderr.
|
Stdout is JSON-RPC only. Diagnostics go to stderr.
|
||||||
Exit 0 on clean EOF, 1 on transport error, 2 if backend never answers.
|
Exit 0 on clean EOF, 1 on transport error, 2 if backend never answers.
|
||||||
@@ -24,19 +24,19 @@ from typing import Any
|
|||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
|
|
||||||
CLIENT_ID_HEADER = "X-Voicebox-Client-Id"
|
CLIENT_ID_HEADER = "X-TalkBox-Client-Id"
|
||||||
SESSION_HEADER = "mcp-session-id"
|
SESSION_HEADER = "mcp-session-id"
|
||||||
HEALTH_TIMEOUT_S = 30.0
|
HEALTH_TIMEOUT_S = 30.0
|
||||||
DEFAULT_PORT = 17493
|
DEFAULT_PORT = 17494
|
||||||
|
|
||||||
|
|
||||||
def _err(msg: str) -> None:
|
def _err(msg: str) -> None:
|
||||||
print(f"voicebox-mcp: {msg}", file=sys.stderr, flush=True)
|
print(f"talkbox-mcp: {msg}", file=sys.stderr, flush=True)
|
||||||
|
|
||||||
|
|
||||||
def _base_url() -> tuple[str, str]:
|
def _base_url() -> tuple[str, str]:
|
||||||
host = os.environ.get("VOICEBOX_HOST", "127.0.0.1")
|
host = os.environ.get("TALKBOX_HOST", "127.0.0.1")
|
||||||
port = int(os.environ.get("VOICEBOX_PORT", str(DEFAULT_PORT)))
|
port = int(os.environ.get("TALKBOX_PORT", str(DEFAULT_PORT)))
|
||||||
return f"http://{host}:{port}/mcp/", f"http://{host}:{port}/health"
|
return f"http://{host}:{port}/mcp/", f"http://{host}:{port}/health"
|
||||||
|
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ async def _handle_request(
|
|||||||
"error": {
|
"error": {
|
||||||
"code": -32000,
|
"code": -32000,
|
||||||
"message": (
|
"message": (
|
||||||
f"Voicebox MCP proxy got HTTP {response.status_code}"
|
f"TalkBox MCP proxy got HTTP {response.status_code}"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -155,7 +155,7 @@ async def _handle_request(
|
|||||||
async def _run() -> int:
|
async def _run() -> int:
|
||||||
url, health_url = _base_url()
|
url, health_url = _base_url()
|
||||||
forward_headers: dict[str, str] = {}
|
forward_headers: dict[str, str] = {}
|
||||||
client_id = os.environ.get("VOICEBOX_CLIENT_ID")
|
client_id = os.environ.get("TALKBOX_CLIENT_ID")
|
||||||
if client_id:
|
if client_id:
|
||||||
forward_headers[CLIENT_ID_HEADER] = client_id
|
forward_headers[CLIENT_ID_HEADER] = client_id
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ async def _run() -> int:
|
|||||||
async with httpx.AsyncClient(timeout=httpx.Timeout(300.0)) as client:
|
async with httpx.AsyncClient(timeout=httpx.Timeout(300.0)) as client:
|
||||||
if not await _wait_for_backend(client, health_url):
|
if not await _wait_for_backend(client, health_url):
|
||||||
_err(
|
_err(
|
||||||
f"timed out waiting for Voicebox at {health_url} — is the app open?"
|
f"timed out waiting for TalkBox at {health_url} — is the app open?"
|
||||||
)
|
)
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -309,7 +309,7 @@ class GenerationSettingsUpdate(BaseModel):
|
|||||||
|
|
||||||
class MCPClientBindingResponse(BaseModel):
|
class MCPClientBindingResponse(BaseModel):
|
||||||
"""Per-MCP-client voice binding — what voice / engine the server should
|
"""Per-MCP-client voice binding — what voice / engine the server should
|
||||||
use when a given client_id calls voicebox.speak without args, plus an
|
use when a given client_id calls talkbox.speak without args, plus an
|
||||||
opt-in personality-rewrite default."""
|
opt-in personality-rewrite default."""
|
||||||
|
|
||||||
client_id: str
|
client_id: str
|
||||||
@@ -346,7 +346,7 @@ class MCPClientBindingListResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class SpeakRequest(BaseModel):
|
class SpeakRequest(BaseModel):
|
||||||
"""Body for POST /speak — non-MCP REST surface that mirrors voicebox.speak."""
|
"""Body for POST /speak — non-MCP REST surface that mirrors talkbox.speak."""
|
||||||
|
|
||||||
text: str = Field(..., min_length=1, max_length=10000)
|
text: str = Field(..., min_length=1, max_length=10000)
|
||||||
profile: Optional[str] = Field(
|
profile: Optional[str] = Field(
|
||||||
@@ -807,7 +807,7 @@ class CloudLoginStartResponse(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class CloudStatusResponse(BaseModel):
|
class CloudStatusResponse(BaseModel):
|
||||||
"""Current link between this device and a Voicebox Cloud account."""
|
"""Current link between this device and a TalkBox Cloud account."""
|
||||||
|
|
||||||
connected: bool
|
connected: bool
|
||||||
device_name: Optional[str] = None
|
device_name: Optional[str] = None
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ trips the decorator chain.
|
|||||||
|
|
||||||
Fix
|
Fix
|
||||||
---
|
---
|
||||||
voicebox never uses torch.compile / torch._dynamo for inference, so we
|
talkbox never uses torch.compile / torch._dynamo for inference, so we
|
||||||
replace torch._dynamo with a no-op stub module before transformers is
|
replace torch._dynamo with a no-op stub module before transformers is
|
||||||
imported. Any attribute access on the stub returns a pass-through callable,
|
imported. Any attribute access on the stub returns a pass-through callable,
|
||||||
so `@torch._dynamo.allow_in_graph`, `torch._dynamo.is_compiling()`,
|
so `@torch._dynamo.allow_in_graph`, `torch._dynamo.is_compiling()`,
|
||||||
@@ -60,7 +60,7 @@ import types
|
|||||||
# Diagnostics — log hook activity to a file alongside the bundle so we can
|
# Diagnostics — log hook activity to a file alongside the bundle so we can
|
||||||
# see what's happening when the server is run as a sidecar (no stdout for
|
# see what's happening when the server is run as a sidecar (no stdout for
|
||||||
# runtime hook prints). Safe no-op if the file can't be written.
|
# runtime hook prints). Safe no-op if the file can't be written.
|
||||||
_DIAG_PATH = os.path.join(tempfile.gettempdir(), "voicebox_rt_hook.log")
|
_DIAG_PATH = os.path.join(tempfile.gettempdir(), "talkbox_rt_hook.log")
|
||||||
|
|
||||||
|
|
||||||
def _diag(msg: str) -> None:
|
def _diag(msg: str) -> None:
|
||||||
@@ -193,7 +193,7 @@ class _TransformersStubFinder:
|
|||||||
unbound before `del obj`.
|
unbound before `del obj`.
|
||||||
|
|
||||||
The exports (AssistedCandidateGenerator, EarlyExitCandidateGenerator,
|
The exports (AssistedCandidateGenerator, EarlyExitCandidateGenerator,
|
||||||
etc.) are speculative-decoding helpers voicebox's TTS engines do not
|
etc.) are speculative-decoding helpers talkbox's TTS engines do not
|
||||||
use; a no-op stub module satisfies the imports.
|
use; a no-op stub module satisfies the imports.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "voicebox-backend"
|
name = "talkbox-backend"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ src = ["."]
|
|||||||
|
|
||||||
# Files/dirs to skip entirely.
|
# Files/dirs to skip entirely.
|
||||||
extend-exclude = [
|
extend-exclude = [
|
||||||
"voicebox-server.spec",
|
"talkbox-server.spec",
|
||||||
"build_binary.py",
|
"build_binary.py",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ pedalboard>=0.9.0
|
|||||||
httpx>=0.27.0
|
httpx>=0.27.0
|
||||||
|
|
||||||
# MCP server (Model Context Protocol) — lets local AI agents call
|
# MCP server (Model Context Protocol) — lets local AI agents call
|
||||||
# voicebox.speak / .transcribe / .list_captures / .list_profiles
|
# talkbox.speak / .transcribe / .list_captures / .list_profiles
|
||||||
fastmcp>=3.0,<4.0
|
fastmcp>=3.0,<4.0
|
||||||
sse-starlette>=2.0
|
sse-starlette>=2.0
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Route registration for the voicebox API."""
|
"""Route registration for the talkbox API."""
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Voicebox Cloud device login routes.
|
"""TalkBox Cloud device login routes.
|
||||||
|
|
||||||
The browser-based pairing flow:
|
The browser-based pairing flow:
|
||||||
1. POST /cloud/login/start — opens the browser to the cloud authorize page.
|
1. POST /cloud/login/start — opens the browser to the cloud authorize page.
|
||||||
@@ -23,7 +23,7 @@ router = APIRouter(prefix="/cloud", tags=["cloud"])
|
|||||||
|
|
||||||
def _callback_url(request: Request) -> str:
|
def _callback_url(request: Request) -> str:
|
||||||
# Always loopback — the cloud only redirects codes to 127.0.0.1/localhost.
|
# Always loopback — the cloud only redirects codes to 127.0.0.1/localhost.
|
||||||
port = request.url.port or 17493
|
port = request.url.port or 17494
|
||||||
return f"http://127.0.0.1:{port}/cloud/callback"
|
return f"http://127.0.0.1:{port}/cloud/callback"
|
||||||
|
|
||||||
|
|
||||||
@@ -45,14 +45,14 @@ async def cloud_callback(
|
|||||||
heading = "You're connected" if ok else "Couldn't connect"
|
heading = "You're connected" if ok else "Couldn't connect"
|
||||||
accent = "#16a34a" if ok else "#dc2626"
|
accent = "#16a34a" if ok else "#dc2626"
|
||||||
sub = (
|
sub = (
|
||||||
"Voicebox is now linked to your account. You can close this tab and return to the app."
|
"TalkBox is now linked to your account. You can close this tab and return to the app."
|
||||||
if ok
|
if ok
|
||||||
else message
|
else message
|
||||||
)
|
)
|
||||||
html = f"""<!doctype html>
|
html = f"""<!doctype html>
|
||||||
<html lang="en"><head><meta charset="utf-8" />
|
<html lang="en"><head><meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>Voicebox Cloud</title>
|
<title>TalkBox Cloud</title>
|
||||||
<style>
|
<style>
|
||||||
body {{ margin:0; min-height:100vh; display:flex; align-items:center; justify-content:center;
|
body {{ margin:0; min-height:100vh; display:flex; align-items:center; justify-content:center;
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; background:#0b0b0d; color:#e7e7ea; }}
|
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; background:#0b0b0d; color:#e7e7ea; }}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ async def root():
|
|||||||
index = _frontend_dir / "index.html"
|
index = _frontend_dir / "index.html"
|
||||||
if index.is_file():
|
if index.is_file():
|
||||||
return FileResponse(index, media_type="text/html")
|
return FileResponse(index, media_type="text/html")
|
||||||
return {"message": "voicebox API", "version": __version__}
|
return {"message": "talkbox API", "version": __version__}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/shutdown")
|
@router.post("/shutdown")
|
||||||
@@ -185,7 +185,7 @@ async def health():
|
|||||||
gpu_type=gpu_type,
|
gpu_type=gpu_type,
|
||||||
vram_used_mb=vram_used,
|
vram_used_mb=vram_used,
|
||||||
backend_type=backend_type,
|
backend_type=backend_type,
|
||||||
backend_variant=os.environ.get("VOICEBOX_BACKEND_VARIANT", default_variant),
|
backend_variant=os.environ.get("TALKBOX_BACKEND_VARIANT", default_variant),
|
||||||
supports_rocm=is_amd_gpu_windows(),
|
supports_rocm=is_amd_gpu_windows(),
|
||||||
gpu_compatibility_warning=gpu_compat_warning,
|
gpu_compatibility_warning=gpu_compat_warning,
|
||||||
)
|
)
|
||||||
@@ -211,7 +211,7 @@ async def filesystem_health():
|
|||||||
writable = False
|
writable = False
|
||||||
error = None
|
error = None
|
||||||
if exists:
|
if exists:
|
||||||
probe = dir_path / ".voicebox_probe"
|
probe = dir_path / ".talkbox_probe"
|
||||||
try:
|
try:
|
||||||
probe.write_text("ok")
|
probe.write_text("ok")
|
||||||
probe.unlink()
|
probe.unlink()
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ async def export_generation(
|
|||||||
safe_text = "generation"
|
safe_text = "generation"
|
||||||
# Append a short id so exports of similarly-worded generations don't collide
|
# Append a short id so exports of similarly-worded generations don't collide
|
||||||
# on the same filename (the first 30 chars are frequently identical).
|
# on the same filename (the first 30 chars are frequently identical).
|
||||||
filename = f"generation-{safe_text}-{generation_id[:8]}.voicebox.zip"
|
filename = f"generation-{safe_text}-{generation_id[:8]}.talkbox.zip"
|
||||||
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(zip_bytes),
|
io.BytesIO(zip_bytes),
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
The Settings UI uses these to let users configure distinct voices per
|
The Settings UI uses these to let users configure distinct voices per
|
||||||
agent (Claude Code in Morgan, Cursor in Scarlett, ...). The ``client_id``
|
agent (Claude Code in Morgan, Cursor in Scarlett, ...). The ``client_id``
|
||||||
column is the same value the MCP client sends in ``X-Voicebox-Client-Id``
|
column is the same value the MCP client sends in ``X-TalkBox-Client-Id``
|
||||||
(or the stdio shim pulls from ``VOICEBOX_CLIENT_ID``).
|
(or the stdio shim pulls from ``TALKBOX_CLIENT_ID``).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ async def export_profile(
|
|||||||
safe_name = "".join(c for c in profile.name if c.isalnum() or c in (" ", "-", "_")).strip()
|
safe_name = "".join(c for c in profile.name if c.isalnum() or c in (" ", "-", "_")).strip()
|
||||||
if not safe_name:
|
if not safe_name:
|
||||||
safe_name = "profile"
|
safe_name = "profile"
|
||||||
filename = f"profile-{safe_name}.voicebox.zip"
|
filename = f"profile-{safe_name}.talkbox.zip"
|
||||||
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(zip_bytes),
|
io.BytesIO(zip_bytes),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"""POST /speak — REST wrapper around voicebox.speak for non-MCP callers.
|
"""POST /speak — REST wrapper around talkbox.speak for non-MCP callers.
|
||||||
|
|
||||||
Shell scripts, ACP, A2A, or any agent that doesn't speak MCP can hit this
|
Shell scripts, ACP, A2A, or any agent that doesn't speak MCP can hit this
|
||||||
endpoint to play text through a cloned voice. Uses the same profile
|
endpoint to play text through a cloned voice. Uses the same profile
|
||||||
resolution and generation pipeline as the MCP tool, so per-client
|
resolution and generation pipeline as the MCP tool, so per-client
|
||||||
bindings (via X-Voicebox-Client-Id) work identically.
|
bindings (via X-TalkBox-Client-Id) work identically.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -30,13 +30,13 @@ async def speak(
|
|||||||
request: Request,
|
request: Request,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
):
|
):
|
||||||
"""Speak text in a voice profile. Mirrors voicebox.speak (MCP).
|
"""Speak text in a voice profile. Mirrors talkbox.speak (MCP).
|
||||||
|
|
||||||
Response shape matches POST /generate — a ``GenerationResponse`` with
|
Response shape matches POST /generate — a ``GenerationResponse`` with
|
||||||
``status="generating"`` and an ``id`` the caller polls at
|
``status="generating"`` and an ``id`` the caller polls at
|
||||||
``GET /generate/{id}/status``.
|
``GET /generate/{id}/status``.
|
||||||
"""
|
"""
|
||||||
client_id = request.headers.get("X-Voicebox-Client-Id")
|
client_id = request.headers.get("X-TalkBox-Client-Id")
|
||||||
profile = resolve_profile(data.profile, client_id, db)
|
profile = resolve_profile(data.profile, client_id, db)
|
||||||
if profile is None:
|
if profile is None:
|
||||||
if data.profile:
|
if data.profile:
|
||||||
@@ -48,7 +48,7 @@ async def speak(
|
|||||||
status_code=400,
|
status_code=400,
|
||||||
detail=(
|
detail=(
|
||||||
"No voice profile resolved. Pass `profile` (name or id), "
|
"No voice profile resolved. Pass `profile` (name or id), "
|
||||||
"or configure a default in Voicebox → Settings → MCP."
|
"or configure a default in TalkBox → Settings → MCP."
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Entry point for PyInstaller-bundled voicebox server.
|
Entry point for PyInstaller-bundled talkbox server.
|
||||||
|
|
||||||
This module provides an entry point that works with PyInstaller by using
|
This module provides an entry point that works with PyInstaller by using
|
||||||
absolute imports instead of relative imports.
|
absolute imports instead of relative imports.
|
||||||
@@ -45,18 +45,18 @@ if getattr(sys, 'frozen', False):
|
|||||||
# version check doesn't block for 30+ seconds loading torch etc.
|
# version check doesn't block for 30+ seconds loading torch etc.
|
||||||
if "--version" in sys.argv:
|
if "--version" in sys.argv:
|
||||||
from backend import __version__
|
from backend import __version__
|
||||||
print(f"voicebox-server {__version__}")
|
print(f"talkbox-server {__version__}")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Detect backend variant from binary name BEFORE importing backend modules
|
# Detect backend variant from binary name BEFORE importing backend modules
|
||||||
# so that env-var guards in app.py (e.g. HSA_OVERRIDE_GFX_VERSION) fire at import time.
|
# so that env-var guards in app.py (e.g. HSA_OVERRIDE_GFX_VERSION) fire at import time.
|
||||||
_binary_name = os.path.basename(sys.executable).lower()
|
_binary_name = os.path.basename(sys.executable).lower()
|
||||||
if re.search(r"voicebox-server-rocm(\.exe)?$", _binary_name):
|
if re.search(r"talkbox-server-rocm(\.exe)?$", _binary_name):
|
||||||
os.environ["VOICEBOX_BACKEND_VARIANT"] = "rocm"
|
os.environ["TALKBOX_BACKEND_VARIANT"] = "rocm"
|
||||||
elif re.search(r"voicebox-server-cuda(\.exe)?$", _binary_name):
|
elif re.search(r"talkbox-server-cuda(\.exe)?$", _binary_name):
|
||||||
os.environ["VOICEBOX_BACKEND_VARIANT"] = "cuda"
|
os.environ["TALKBOX_BACKEND_VARIANT"] = "cuda"
|
||||||
else:
|
else:
|
||||||
os.environ.setdefault("VOICEBOX_BACKEND_VARIANT", "cpu")
|
os.environ.setdefault("TALKBOX_BACKEND_VARIANT", "cpu")
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
@@ -71,7 +71,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
# Log startup immediately to confirm binary execution
|
# Log startup immediately to confirm binary execution
|
||||||
logger.info("=" * 60)
|
logger.info("=" * 60)
|
||||||
logger.info("voicebox-server starting up...")
|
logger.info("talkbox-server starting up...")
|
||||||
logger.info(f"Python version: {sys.version}")
|
logger.info(f"Python version: {sys.version}")
|
||||||
logger.info(f"Executable: {sys.executable}")
|
logger.info(f"Executable: {sys.executable}")
|
||||||
logger.info(f"Arguments: {sys.argv}")
|
logger.info(f"Arguments: {sys.argv}")
|
||||||
@@ -237,7 +237,7 @@ def _start_parent_watchdog(parent_pid, data_dir=None):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
parser = argparse.ArgumentParser(description="voicebox backend server")
|
parser = argparse.ArgumentParser(description="talkbox backend server")
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--host",
|
"--host",
|
||||||
type=str,
|
type=str,
|
||||||
@@ -272,7 +272,7 @@ if __name__ == "__main__":
|
|||||||
if args.parent_pid is not None and args.parent_pid <= 0:
|
if args.parent_pid is not None and args.parent_pid <= 0:
|
||||||
parser.error("--parent-pid must be a positive integer")
|
parser.error("--parent-pid must be a positive integer")
|
||||||
|
|
||||||
logger.info(f"Backend variant: {os.environ.get('VOICEBOX_BACKEND_VARIANT', 'cpu').upper()}")
|
logger.info(f"Backend variant: {os.environ.get('TALKBOX_BACKEND_VARIANT', 'cpu').upper()}")
|
||||||
|
|
||||||
# Register parent watchdog to start after server is fully ready
|
# Register parent watchdog to start after server is fully ready
|
||||||
if args.parent_pid is not None:
|
if args.parent_pid is not None:
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"""
|
"""
|
||||||
Voicebox Cloud device login — the "Log in with browser" flow.
|
TalkBox Cloud device login — the "Log in with browser" flow.
|
||||||
|
|
||||||
The desktop opens the browser to ``{web}/connect``; the user authorizes while
|
The desktop opens the browser to ``{web}/connect``; the user authorizes while
|
||||||
signed in; the cloud redirects a single-use code back to this backend's loopback
|
signed in; the cloud redirects a single-use code back to this backend's loopback
|
||||||
callback. We exchange that code (server-to-server, over TLS) for a ``voicebox_…``
|
callback. We exchange that code (server-to-server, over TLS) for a ``talkbox_…``
|
||||||
API key, verify the key against the API, and store it locally. The key never
|
API key, verify the key against the API, and store it locally. The key never
|
||||||
travels through a browser URL, and an unfinished flow leaves nothing behind.
|
travels through a browser URL, and an unfinished flow leaves nothing behind.
|
||||||
|
|
||||||
@@ -97,11 +97,11 @@ async def handle_callback(db: Session, code: str, state: str) -> tuple[bool, str
|
|||||||
payload = _json_dict(exchanged)
|
payload = _json_dict(exchanged)
|
||||||
if payload is None:
|
if payload is None:
|
||||||
logger.warning("cloud exchange returned a non-JSON payload")
|
logger.warning("cloud exchange returned a non-JSON payload")
|
||||||
return False, "Voicebox Cloud returned an unexpected response."
|
return False, "TalkBox Cloud returned an unexpected response."
|
||||||
api_key = payload.get("key")
|
api_key = payload.get("key")
|
||||||
device_name = payload.get("label")
|
device_name = payload.get("label")
|
||||||
if not api_key:
|
if not api_key:
|
||||||
return False, "Voicebox Cloud did not return a key."
|
return False, "TalkBox Cloud did not return a key."
|
||||||
|
|
||||||
# Confirm the freshly minted key actually authenticates the API.
|
# Confirm the freshly minted key actually authenticates the API.
|
||||||
me = await client.get(
|
me = await client.get(
|
||||||
@@ -116,10 +116,10 @@ async def handle_callback(db: Session, code: str, state: str) -> tuple[bool, str
|
|||||||
account_user_id = data.get("userId") if isinstance(data, dict) else None
|
account_user_id = data.get("userId") if isinstance(data, dict) else None
|
||||||
except httpx.HTTPError:
|
except httpx.HTTPError:
|
||||||
logger.exception("network error during cloud exchange")
|
logger.exception("network error during cloud exchange")
|
||||||
return False, "Could not reach Voicebox Cloud. Check your connection and try again."
|
return False, "Could not reach TalkBox Cloud. Check your connection and try again."
|
||||||
|
|
||||||
_store_key(db, api_key=api_key, device_name=device_name, account_user_id=account_user_id)
|
_store_key(db, api_key=api_key, device_name=device_name, account_user_id=account_user_id)
|
||||||
logger.info("connected to Voicebox Cloud as device %r", device_name)
|
logger.info("connected to TalkBox Cloud as device %r", device_name)
|
||||||
return True, "Connected"
|
return True, "Connected"
|
||||||
|
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ def get_status(db: Session) -> dict:
|
|||||||
"""Local view of the cloud link — never returns the full key."""
|
"""Local view of the cloud link — never returns the full key."""
|
||||||
row = _get_or_create_row(db)
|
row = _get_or_create_row(db)
|
||||||
connected = bool(row.api_key)
|
connected = bool(row.api_key)
|
||||||
# Prefix only: "voicebox_" (9) + 8 chars, matching the cloud's key_prefix.
|
# Prefix only: "talkbox_" (9) + 8 chars, matching the cloud's key_prefix.
|
||||||
key_prefix = row.api_key[:17] if row.api_key else None
|
key_prefix = row.api_key[:17] if row.api_key else None
|
||||||
return {
|
return {
|
||||||
"connected": connected,
|
"connected": connected,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
CUDA backend download, assembly, and verification.
|
CUDA backend download, assembly, and verification.
|
||||||
|
|
||||||
Downloads two archives from GitHub Releases:
|
Downloads two archives from GitHub Releases:
|
||||||
1. Server core (voicebox-server-cuda.tar.gz) — the exe + non-NVIDIA deps,
|
1. Server core (talkbox-server-cuda.tar.gz) — the exe + non-NVIDIA deps,
|
||||||
versioned with the app.
|
versioned with the app.
|
||||||
2. CUDA libs (cuda-libs-{version}.tar.gz) — NVIDIA runtime libraries,
|
2. CUDA libs (cuda-libs-{version}.tar.gz) — NVIDIA runtime libraries,
|
||||||
versioned independently (only redownloaded on CUDA toolkit bump).
|
versioned independently (only redownloaded on CUDA toolkit bump).
|
||||||
@@ -27,7 +27,7 @@ from ..utils.progress import get_progress_manager
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
GITHUB_RELEASES_URL = "https://github.com/jamiepine/voicebox/releases/download"
|
GITHUB_RELEASES_URL = "https://github.com/jamiepine/talkbox/releases/download"
|
||||||
|
|
||||||
PROGRESS_KEY = "cuda-backend"
|
PROGRESS_KEY = "cuda-backend"
|
||||||
|
|
||||||
@@ -61,8 +61,8 @@ def get_cuda_dir() -> Path:
|
|||||||
def get_cuda_exe_name() -> str:
|
def get_cuda_exe_name() -> str:
|
||||||
"""Platform-specific CUDA executable filename."""
|
"""Platform-specific CUDA executable filename."""
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
return "voicebox-server-cuda.exe"
|
return "talkbox-server-cuda.exe"
|
||||||
return "voicebox-server-cuda"
|
return "talkbox-server-cuda"
|
||||||
|
|
||||||
|
|
||||||
def is_cuda_download_supported() -> bool:
|
def is_cuda_download_supported() -> bool:
|
||||||
@@ -115,7 +115,7 @@ def is_cuda_active() -> bool:
|
|||||||
|
|
||||||
The CUDA binary sets this env var on startup (see server.py).
|
The CUDA binary sets this env var on startup (see server.py).
|
||||||
"""
|
"""
|
||||||
return os.environ.get("VOICEBOX_BACKEND_VARIANT") == "cuda"
|
return os.environ.get("TALKBOX_BACKEND_VARIANT") == "cuda"
|
||||||
|
|
||||||
|
|
||||||
def get_cuda_status() -> dict:
|
def get_cuda_status() -> dict:
|
||||||
@@ -312,7 +312,7 @@ async def _download_cuda_binary_locked(version: Optional[str] = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
base_url = f"{GITHUB_RELEASES_URL}/{version}"
|
base_url = f"{GITHUB_RELEASES_URL}/{version}"
|
||||||
server_archive = "voicebox-server-cuda.tar.gz"
|
server_archive = "talkbox-server-cuda.tar.gz"
|
||||||
libs_archive = f"cuda-libs-{CUDA_LIBS_VERSION}.tar.gz"
|
libs_archive = f"cuda-libs-{CUDA_LIBS_VERSION}.tar.gz"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -394,9 +394,9 @@ def get_cuda_binary_version() -> Optional[str]:
|
|||||||
timeout=30,
|
timeout=30,
|
||||||
cwd=str(cuda_path.parent), # Run from the onedir directory
|
cwd=str(cuda_path.parent), # Run from the onedir directory
|
||||||
)
|
)
|
||||||
# Output format: "voicebox-server 0.3.0"
|
# Output format: "talkbox-server 0.3.0"
|
||||||
for line in result.stdout.strip().splitlines():
|
for line in result.stdout.strip().splitlines():
|
||||||
if "voicebox-server" in line:
|
if "talkbox-server" in line:
|
||||||
return line.split()[-1]
|
return line.split()[-1]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not get CUDA binary version: {e}")
|
logger.warning(f"Could not get CUDA binary version: {e}")
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
ROCm backend download, assembly, and verification.
|
ROCm backend download, assembly, and verification.
|
||||||
|
|
||||||
Downloads two archives from GitHub Releases:
|
Downloads two archives from GitHub Releases:
|
||||||
1. Server core (voicebox-server-rocm.tar.gz) — the exe + non-AMD deps,
|
1. Server core (talkbox-server-rocm.tar.gz) — the exe + non-AMD deps,
|
||||||
versioned with the app.
|
versioned with the app.
|
||||||
2. ROCm libs (rocm-libs-{version}.tar.gz) — AMD runtime libraries,
|
2. ROCm libs (rocm-libs-{version}.tar.gz) — AMD runtime libraries,
|
||||||
versioned independently (only redownloaded on ROCm toolkit bump).
|
versioned independently (only redownloaded on ROCm toolkit bump).
|
||||||
@@ -28,7 +28,7 @@ from .. import __version__
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
GITHUB_RELEASES_URL = "https://github.com/jamiepine/voicebox/releases/download"
|
GITHUB_RELEASES_URL = "https://github.com/jamiepine/talkbox/releases/download"
|
||||||
|
|
||||||
PROGRESS_KEY = "rocm-backend"
|
PROGRESS_KEY = "rocm-backend"
|
||||||
|
|
||||||
@@ -60,8 +60,8 @@ def get_rocm_dir() -> Path:
|
|||||||
def get_rocm_exe_name() -> str:
|
def get_rocm_exe_name() -> str:
|
||||||
"""Platform-specific ROCm executable filename."""
|
"""Platform-specific ROCm executable filename."""
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
return "voicebox-server-rocm.exe"
|
return "talkbox-server-rocm.exe"
|
||||||
return "voicebox-server-rocm"
|
return "talkbox-server-rocm"
|
||||||
|
|
||||||
|
|
||||||
def get_rocm_binary_path() -> Optional[Path]:
|
def get_rocm_binary_path() -> Optional[Path]:
|
||||||
@@ -95,7 +95,7 @@ def is_rocm_active() -> bool:
|
|||||||
|
|
||||||
The ROCm binary sets this env var on startup (see server.py).
|
The ROCm binary sets this env var on startup (see server.py).
|
||||||
"""
|
"""
|
||||||
return os.environ.get("VOICEBOX_BACKEND_VARIANT") == "rocm"
|
return os.environ.get("TALKBOX_BACKEND_VARIANT") == "rocm"
|
||||||
|
|
||||||
|
|
||||||
def get_rocm_status() -> dict:
|
def get_rocm_status() -> dict:
|
||||||
@@ -287,7 +287,7 @@ async def _download_rocm_binary_locked(version: Optional[str] = None):
|
|||||||
# release tag; the libs content version is encoded in the filename only.
|
# release tag; the libs content version is encoded in the filename only.
|
||||||
server_base_url = f"{GITHUB_RELEASES_URL}/{version}"
|
server_base_url = f"{GITHUB_RELEASES_URL}/{version}"
|
||||||
libs_base_url = server_base_url
|
libs_base_url = server_base_url
|
||||||
server_archive = "voicebox-server-rocm.tar.gz"
|
server_archive = "talkbox-server-rocm.tar.gz"
|
||||||
libs_archive = f"rocm-libs-{ROCM_LIBS_VERSION}.tar.gz"
|
libs_archive = f"rocm-libs-{ROCM_LIBS_VERSION}.tar.gz"
|
||||||
|
|
||||||
# Always stage when any download is needed, then atomically rename over
|
# Always stage when any download is needed, then atomically rename over
|
||||||
@@ -409,9 +409,9 @@ def get_rocm_binary_version() -> Optional[str]:
|
|||||||
timeout=30,
|
timeout=30,
|
||||||
cwd=str(rocm_path.parent), # Run from the onedir directory
|
cwd=str(rocm_path.parent), # Run from the onedir directory
|
||||||
)
|
)
|
||||||
# Output format: "voicebox-server 0.3.0"
|
# Output format: "talkbox-server 0.3.0"
|
||||||
for line in result.stdout.strip().splitlines():
|
for line in result.stdout.strip().splitlines():
|
||||||
if "voicebox-server" in line:
|
if "talkbox-server" in line:
|
||||||
return line.split()[-1]
|
return line.split()[-1]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not get ROCm binary version: {e}")
|
logger.warning(f"Could not get ROCm binary version: {e}")
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ Search order — **first hit wins**:
|
|||||||
|
|
||||||
| Platform | Path | Build type |
|
| Platform | Path | Build type |
|
||||||
|----------|------|------------|
|
|----------|------|------------|
|
||||||
| macOS | `backend/dist/voicebox-server-cuda/voicebox-server-cuda` | onedir (CUDA, rarely on Mac) |
|
| macOS | `backend/dist/talkbox-server-cuda/talkbox-server-cuda` | onedir (CUDA, rarely on Mac) |
|
||||||
| macOS | `backend/dist/voicebox-server` | onefile (CPU) |
|
| macOS | `backend/dist/talkbox-server` | onefile (CPU) |
|
||||||
| Windows | `backend\dist\voicebox-server-cuda\voicebox-server-cuda.exe` | onedir (CUDA) |
|
| Windows | `backend\dist\talkbox-server-cuda\talkbox-server-cuda.exe` | onedir (CUDA) |
|
||||||
| Windows | `backend\dist\voicebox-server.exe` | onefile (CPU) |
|
| Windows | `backend\dist\talkbox-server.exe` | onefile (CPU) |
|
||||||
|
|
||||||
If none exist, run `python backend/build_binary.py` and wait for it to finish (can take 5-20 min). Fail with a clear error if the build itself fails. `--skip-build` flag forces "error out if no binary" instead of building.
|
If none exist, run `python backend/build_binary.py` and wait for it to finish (can take 5-20 min). Fail with a clear error if the build itself fails. `--skip-build` flag forces "error out if no binary" instead of building.
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ Mirrors Tauri's launch in `tauri/src-tauri/src/main.rs:369-388`:
|
|||||||
```
|
```
|
||||||
|
|
||||||
- **Port**: bind to `0` first in Python to grab a free port, then pass that number.
|
- **Port**: bind to `0` first in Python to grab a free port, then pass that number.
|
||||||
- **Data dir**: `tempfile.mkdtemp(prefix="voicebox-e2e-")`. Deleted after the run unless `--keep-data-dir`. Profiles and generated WAVs land here.
|
- **Data dir**: `tempfile.mkdtemp(prefix="talkbox-e2e-")`. Deleted after the run unless `--keep-data-dir`. Profiles and generated WAVs land here.
|
||||||
- **Parent PID**: current Python PID — ensures the backend dies if the test crashes (watchdog in `server.py:102-224`).
|
- **Parent PID**: current Python PID — ensures the backend dies if the test crashes (watchdog in `server.py:102-224`).
|
||||||
- **stdout/stderr**: tee to both a log file in `./results/server-<timestamp>.log` and a rolling in-memory buffer. On model failure, last 100 lines of the buffer are attached to that model's error record.
|
- **stdout/stderr**: tee to both a log file in `./results/server-<timestamp>.log` and a rolling in-memory buffer. On model failure, last 100 lines of the buffer are attached to that model's error record.
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ On timeout: cancel the SSE stream, mark the row `timeout`, and continue to the n
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"platform": "darwin-arm64",
|
"platform": "darwin-arm64",
|
||||||
"binary": "/abs/path/voicebox-server",
|
"binary": "/abs/path/talkbox-server",
|
||||||
"binary_size_mb": 612,
|
"binary_size_mb": 612,
|
||||||
"started_at": "2026-04-16T12:34:56Z",
|
"started_at": "2026-04-16T12:34:56Z",
|
||||||
"finished_at": "...",
|
"finished_at": "...",
|
||||||
@@ -160,7 +160,7 @@ On timeout: cancel the SSE stream, mark the row `timeout`, and continue to the n
|
|||||||
Companion `./results/e2e-<...>.md`:
|
Companion `./results/e2e-<...>.md`:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Voicebox E2E — darwin-arm64 — 2026-04-16 12:34
|
# TalkBox E2E — darwin-arm64 — 2026-04-16 12:34
|
||||||
|
|
||||||
| Engine | Size | Status | Elapsed | Error |
|
| Engine | Size | Status | Elapsed | Error |
|
||||||
|---------------------|------|--------|---------|-------|
|
|---------------------|------|--------|---------|-------|
|
||||||
@@ -208,7 +208,7 @@ The script uses only stdlib + `httpx` (or `requests`) + `sseclient-py` — all a
|
|||||||
|
|
||||||
- Always kill the spawned binary in a `try/finally`. On Windows, `taskkill /F /T` the whole tree (Tauri does the same).
|
- Always kill the spawned binary in a `try/finally`. On Windows, `taskkill /F /T` the whole tree (Tauri does the same).
|
||||||
- Verify the port is free on shutdown (Tauri port-reuse check in `main.rs:114-186` could otherwise pick up a ghost).
|
- Verify the port is free on shutdown (Tauri port-reuse check in `main.rs:114-186` could otherwise pick up a ghost).
|
||||||
- Don't touch the user's HF cache by default — let the server use `HF_HUB_CACHE` / `VOICEBOX_MODELS_DIR`. Passing `--isolated-cache` would point both env vars at the tempdir for a true cold-start run (opt-in only; would re-download every time).
|
- Don't touch the user's HF cache by default — let the server use `HF_HUB_CACHE` / `TALKBOX_MODELS_DIR`. Passing `--isolated-cache` would point both env vars at the tempdir for a true cold-start run (opt-in only; would re-download every time).
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Test suite for Voicebox backend.
|
Test suite for TalkBox backend.
|
||||||
|
|
||||||
This directory contains manual test scripts for debugging and validating
|
This directory contains manual test scripts for debugging and validating
|
||||||
progress tracking, model downloads, and generation functionality.
|
progress tracking, model downloads, and generation functionality.
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ def find_binary() -> Optional[Path]:
|
|||||||
is_win = platform.system() == "Windows"
|
is_win = platform.system() == "Windows"
|
||||||
exe = ".exe" if is_win else ""
|
exe = ".exe" if is_win else ""
|
||||||
candidates = [
|
candidates = [
|
||||||
DIST_DIR / "voicebox-server-cuda" / f"voicebox-server-cuda{exe}",
|
DIST_DIR / "talkbox-server-cuda" / f"talkbox-server-cuda{exe}",
|
||||||
DIST_DIR / f"voicebox-server{exe}",
|
DIST_DIR / f"talkbox-server{exe}",
|
||||||
]
|
]
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
if c.exists() and c.is_file():
|
if c.exists() and c.is_file():
|
||||||
@@ -381,7 +381,7 @@ def write_reports(
|
|||||||
json_path.write_text(json.dumps(doc, indent=2))
|
json_path.write_text(json.dumps(doc, indent=2))
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
f"# Voicebox E2E — {plat} — {started_at.strftime('%Y-%m-%d %H:%M UTC')}",
|
f"# TalkBox E2E — {plat} — {started_at.strftime('%Y-%m-%d %H:%M UTC')}",
|
||||||
"",
|
"",
|
||||||
f"Binary: `{binary}` ",
|
f"Binary: `{binary}` ",
|
||||||
f"Elapsed: {doc['elapsed_seconds']:.1f}s",
|
f"Elapsed: {doc['elapsed_seconds']:.1f}s",
|
||||||
@@ -424,8 +424,8 @@ def write_reports(
|
|||||||
# ── Main ─────────────────────────────────────────────────────────────
|
# ── Main ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
p = argparse.ArgumentParser(description="Voicebox E2E model generation test")
|
p = argparse.ArgumentParser(description="TalkBox E2E model generation test")
|
||||||
p.add_argument("--binary", type=Path, help="Path to voicebox-server binary (overrides auto-detect)")
|
p.add_argument("--binary", type=Path, help="Path to talkbox-server binary (overrides auto-detect)")
|
||||||
p.add_argument("--skip-build", action="store_true", help="Error if binary missing instead of building")
|
p.add_argument("--skip-build", action="store_true", help="Error if binary missing instead of building")
|
||||||
p.add_argument(
|
p.add_argument(
|
||||||
"--reference-wav",
|
"--reference-wav",
|
||||||
@@ -516,7 +516,7 @@ def main() -> int:
|
|||||||
print(f"[fixture] reference text: {ref_text!r}", flush=True)
|
print(f"[fixture] reference text: {ref_text!r}", flush=True)
|
||||||
|
|
||||||
# Tempdir + log path
|
# Tempdir + log path
|
||||||
data_dir = Path(tempfile.mkdtemp(prefix="voicebox-e2e-"))
|
data_dir = Path(tempfile.mkdtemp(prefix="talkbox-e2e-"))
|
||||||
args.output_dir.mkdir(parents=True, exist_ok=True)
|
args.output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
||||||
log_path = args.output_dir / f"server-{ts}.log"
|
log_path = args.output_dir / f"server-{ts}.log"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Tests for CORS origin restrictions.
|
Tests for CORS origin restrictions.
|
||||||
|
|
||||||
Validates that the CORS middleware only allows known local origins
|
Validates that the CORS middleware only allows known local origins
|
||||||
and respects the VOICEBOX_CORS_ORIGINS environment variable.
|
and respects the TALKBOX_CORS_ORIGINS environment variable.
|
||||||
|
|
||||||
Uses a minimal FastAPI app that mirrors the exact CORS configuration
|
Uses a minimal FastAPI app that mirrors the exact CORS configuration
|
||||||
from backend/main.py, so tests run without heavy ML dependencies.
|
from backend/main.py, so tests run without heavy ML dependencies.
|
||||||
@@ -32,8 +32,8 @@ def _build_app(env_origins: str = "") -> FastAPI:
|
|||||||
_default_origins = [
|
_default_origins = [
|
||||||
"http://localhost:5173",
|
"http://localhost:5173",
|
||||||
"http://127.0.0.1:5173",
|
"http://127.0.0.1:5173",
|
||||||
"http://localhost:17493",
|
"http://localhost:17494",
|
||||||
"http://127.0.0.1:17493",
|
"http://127.0.0.1:17494",
|
||||||
"tauri://localhost",
|
"tauri://localhost",
|
||||||
"https://tauri.localhost",
|
"https://tauri.localhost",
|
||||||
]
|
]
|
||||||
@@ -88,8 +88,8 @@ class TestCORSDefaultOrigins:
|
|||||||
@pytest.mark.parametrize("origin", [
|
@pytest.mark.parametrize("origin", [
|
||||||
"http://localhost:5173",
|
"http://localhost:5173",
|
||||||
"http://127.0.0.1:5173",
|
"http://127.0.0.1:5173",
|
||||||
"http://localhost:17493",
|
"http://localhost:17494",
|
||||||
"http://127.0.0.1:17493",
|
"http://127.0.0.1:17494",
|
||||||
"tauri://localhost",
|
"tauri://localhost",
|
||||||
"https://tauri.localhost",
|
"https://tauri.localhost",
|
||||||
])
|
])
|
||||||
@@ -121,7 +121,7 @@ class TestCORSDefaultOrigins:
|
|||||||
|
|
||||||
|
|
||||||
class TestCORSCustomOrigins:
|
class TestCORSCustomOrigins:
|
||||||
"""VOICEBOX_CORS_ORIGINS env var should extend the allowlist."""
|
"""TALKBOX_CORS_ORIGINS env var should extend the allowlist."""
|
||||||
|
|
||||||
def test_custom_origin_allowed(self, client_with_custom_origins):
|
def test_custom_origin_allowed(self, client_with_custom_origins):
|
||||||
headers = _get_with_origin(client_with_custom_origins, "https://custom.example.com")
|
headers = _get_with_origin(client_with_custom_origins, "https://custom.example.com")
|
||||||
@@ -141,7 +141,7 @@ class TestCORSCustomOrigins:
|
|||||||
|
|
||||||
|
|
||||||
class TestCORSEnvVarParsing:
|
class TestCORSEnvVarParsing:
|
||||||
"""Edge cases for VOICEBOX_CORS_ORIGINS parsing."""
|
"""Edge cases for TALKBOX_CORS_ORIGINS parsing."""
|
||||||
|
|
||||||
def test_empty_env_var(self):
|
def test_empty_env_var(self):
|
||||||
app = _build_app("")
|
app = _build_app("")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Tests for the voicebox.speak MCP tool's ``model_size`` plumbing (issue #884).
|
"""Tests for the talkbox.speak MCP tool's ``model_size`` plumbing (issue #884).
|
||||||
|
|
||||||
The MCP speak path used to build its ``GenerationRequest`` without a
|
The MCP speak path used to build its ``GenerationRequest`` without a
|
||||||
``model_size``, so every agent-triggered generation silently fell back to the
|
``model_size``, so every agent-triggered generation silently fell back to the
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class TestIsRocmFile:
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"rel_path",
|
"rel_path",
|
||||||
[
|
[
|
||||||
"voicebox-server-rocm.exe",
|
"talkbox-server-rocm.exe",
|
||||||
"_internal/python312.dll",
|
"_internal/python312.dll",
|
||||||
"_internal/torch/lib/torch_cpu.dll",
|
"_internal/torch/lib/torch_cpu.dll",
|
||||||
"_internal/torch/lib/c10.dll",
|
"_internal/torch/lib/c10.dll",
|
||||||
@@ -68,8 +68,8 @@ class TestPackage:
|
|||||||
"""End-to-end split of a synthetic onedir into the two archives."""
|
"""End-to-end split of a synthetic onedir into the two archives."""
|
||||||
|
|
||||||
def test_split_and_manifest(self, tmp_path):
|
def test_split_and_manifest(self, tmp_path):
|
||||||
onedir = tmp_path / "voicebox-server-rocm"
|
onedir = tmp_path / "talkbox-server-rocm"
|
||||||
_write(onedir / "voicebox-server-rocm.exe")
|
_write(onedir / "talkbox-server-rocm.exe")
|
||||||
_write(onedir / "_internal" / "python312.dll")
|
_write(onedir / "_internal" / "python312.dll")
|
||||||
_write(onedir / "_internal" / "rocm_sdk" / "__init__.py")
|
_write(onedir / "_internal" / "rocm_sdk" / "__init__.py")
|
||||||
_write(onedir / "_internal" / "torch" / "lib" / "torch_cpu.dll")
|
_write(onedir / "_internal" / "torch" / "lib" / "torch_cpu.dll")
|
||||||
@@ -88,11 +88,11 @@ class TestPackage:
|
|||||||
out = tmp_path / "release-assets"
|
out = tmp_path / "release-assets"
|
||||||
package_rocm.package(onedir, out, "rocm7.2-v1", ">=2.9.0,<2.10.0")
|
package_rocm.package(onedir, out, "rocm7.2-v1", ">=2.9.0,<2.10.0")
|
||||||
|
|
||||||
server = out / "voicebox-server-rocm.tar.gz"
|
server = out / "talkbox-server-rocm.tar.gz"
|
||||||
libs = out / "rocm-libs-rocm7.2-v1.tar.gz"
|
libs = out / "rocm-libs-rocm7.2-v1.tar.gz"
|
||||||
assert server.exists()
|
assert server.exists()
|
||||||
assert libs.exists()
|
assert libs.exists()
|
||||||
assert (out / "voicebox-server-rocm.tar.gz.sha256").exists()
|
assert (out / "talkbox-server-rocm.tar.gz.sha256").exists()
|
||||||
assert (out / "rocm-libs-rocm7.2-v1.tar.gz.sha256").exists()
|
assert (out / "rocm-libs-rocm7.2-v1.tar.gz.sha256").exists()
|
||||||
|
|
||||||
with tarfile.open(libs) as tar:
|
with tarfile.open(libs) as tar:
|
||||||
@@ -106,15 +106,15 @@ class TestPackage:
|
|||||||
"_internal/_rocm_sdk_libraries_custom/lib/rocblas/library/TensileLibrary.dat"
|
"_internal/_rocm_sdk_libraries_custom/lib/rocblas/library/TensileLibrary.dat"
|
||||||
in lib_names
|
in lib_names
|
||||||
)
|
)
|
||||||
assert "voicebox-server-rocm.exe" in core_names
|
assert "talkbox-server-rocm.exe" in core_names
|
||||||
assert "_internal/torch/lib/torch_cpu.dll" in core_names
|
assert "_internal/torch/lib/torch_cpu.dll" in core_names
|
||||||
assert "_internal/rocm_sdk/__init__.py" in core_names
|
assert "_internal/rocm_sdk/__init__.py" in core_names
|
||||||
# Archives must be disjoint.
|
# Archives must be disjoint.
|
||||||
assert lib_names.isdisjoint(core_names)
|
assert lib_names.isdisjoint(core_names)
|
||||||
|
|
||||||
def test_empty_rocm_set_exits(self, tmp_path):
|
def test_empty_rocm_set_exits(self, tmp_path):
|
||||||
onedir = tmp_path / "voicebox-server-rocm"
|
onedir = tmp_path / "talkbox-server-rocm"
|
||||||
_write(onedir / "voicebox-server-rocm.exe")
|
_write(onedir / "talkbox-server-rocm.exe")
|
||||||
_write(onedir / "_internal" / "torch" / "lib" / "torch_cpu.dll")
|
_write(onedir / "_internal" / "torch" / "lib" / "torch_cpu.dll")
|
||||||
|
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ def score(
|
|||||||
# ── Runner ────────────────────────────────────────────────────────────
|
# ── Runner ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_PORTS = (8000, 8765, 8899, 17493)
|
DEFAULT_PORTS = (8000, 8765, 8899, 17494)
|
||||||
THROWAWAY_PROFILE_PREFIX = "personality-harness-"
|
THROWAWAY_PROFILE_PREFIX = "personality-harness-"
|
||||||
KOKORO_PROBE_VOICE = "af_heart"
|
KOKORO_PROBE_VOICE = "af_heart"
|
||||||
"""Any valid kokoro voice id works — compose never calls into TTS, it
|
"""Any valid kokoro voice id works — compose never calls into TTS, it
|
||||||
@@ -204,7 +204,7 @@ def detect_backend_port(hint: Optional[int]) -> int:
|
|||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
raise SystemExit(
|
raise SystemExit(
|
||||||
"No running Voicebox backend found. Start it (`python backend/main.py`) "
|
"No running TalkBox backend found. Start it (`python backend/main.py`) "
|
||||||
f"or pass --port. Tried: {candidates}"
|
f"or pass --port. Tried: {candidates}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ async def test_full_integration():
|
|||||||
async def main():
|
async def main():
|
||||||
"""Run all tests."""
|
"""Run all tests."""
|
||||||
print("\n" + "=" * 60)
|
print("\n" + "=" * 60)
|
||||||
print("Voicebox Progress Tracking Test Suite")
|
print("TalkBox Progress Tracking Test Suite")
|
||||||
print("=" * 60)
|
print("=" * 60)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Usage:
|
|||||||
python backend/tests/test_refinement_samples.py
|
python backend/tests/test_refinement_samples.py
|
||||||
|
|
||||||
# Hit a non-default port (auto-detected via /health probe when omitted):
|
# Hit a non-default port (auto-detected via /health probe when omitted):
|
||||||
python backend/tests/test_refinement_samples.py --port 17493
|
python backend/tests/test_refinement_samples.py --port 17494
|
||||||
|
|
||||||
# Only test one model size:
|
# Only test one model size:
|
||||||
python backend/tests/test_refinement_samples.py --model 4B
|
python backend/tests/test_refinement_samples.py --model 4B
|
||||||
@@ -316,7 +316,7 @@ def score(sample: Sample, model: str, refined: str, latency_ms: int) -> Scorecar
|
|||||||
# ── Runner ────────────────────────────────────────────────────────────
|
# ── Runner ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_PORTS = (8000, 8765, 8899, 17493)
|
DEFAULT_PORTS = (8000, 8765, 8899, 17494)
|
||||||
|
|
||||||
|
|
||||||
def detect_backend_port(hint: Optional[int]) -> int:
|
def detect_backend_port(hint: Optional[int]) -> int:
|
||||||
@@ -339,7 +339,7 @@ def detect_backend_port(hint: Optional[int]) -> int:
|
|||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
raise SystemExit(
|
raise SystemExit(
|
||||||
"No running Voicebox backend found. Start it (`python backend/main.py`) "
|
"No running TalkBox backend found. Start it (`python backend/main.py`) "
|
||||||
f"or pass --port. Tried: {candidates}"
|
f"or pass --port. Tried: {candidates}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -407,7 +407,7 @@ def format_report(cards: list[Scorecard]) -> str:
|
|||||||
def main() -> int:
|
def main() -> int:
|
||||||
ap = argparse.ArgumentParser(description=__doc__)
|
ap = argparse.ArgumentParser(description=__doc__)
|
||||||
ap.add_argument("--port", type=int, default=None,
|
ap.add_argument("--port", type=int, default=None,
|
||||||
help="Voicebox backend port (auto-detected if omitted)")
|
help="TalkBox backend port (auto-detected if omitted)")
|
||||||
ap.add_argument("--model", choices=("0.6B", "1.7B", "4B"), action="append",
|
ap.add_argument("--model", choices=("0.6B", "1.7B", "4B"), action="append",
|
||||||
help="Refinement model size(s) to test (repeat to run several)")
|
help="Refinement model size(s) to test (repeat to run several)")
|
||||||
ap.add_argument("--json", type=Path, default=None,
|
ap.add_argument("--json", type=Path, default=None,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class TestRocmBuildArgs:
|
|||||||
|
|
||||||
def test_binary_name(self, captured_args):
|
def test_binary_name(self, captured_args):
|
||||||
idx = captured_args.index("--name")
|
idx = captured_args.index("--name")
|
||||||
assert captured_args[idx + 1] == "voicebox-server-rocm"
|
assert captured_args[idx + 1] == "talkbox-server-rocm"
|
||||||
|
|
||||||
def test_pack_mode_is_onedir(self, captured_args):
|
def test_pack_mode_is_onedir(self, captured_args):
|
||||||
assert "--onedir" in captured_args
|
assert "--onedir" in captured_args
|
||||||
@@ -91,8 +91,8 @@ class TestRocmBuildE2E:
|
|||||||
backend_dir = Path(__file__).parent.parent
|
backend_dir = Path(__file__).parent.parent
|
||||||
build_script = backend_dir / "build_binary.py"
|
build_script = backend_dir / "build_binary.py"
|
||||||
dist_dir = backend_dir / "dist"
|
dist_dir = backend_dir / "dist"
|
||||||
binary_dir = dist_dir / "voicebox-server-rocm"
|
binary_dir = dist_dir / "talkbox-server-rocm"
|
||||||
binary_exe = binary_dir / "voicebox-server-rocm.exe"
|
binary_exe = binary_dir / "talkbox-server-rocm.exe"
|
||||||
|
|
||||||
# Clean previous dist if it exists to ensure a fresh build
|
# Clean previous dist if it exists to ensure a fresh build
|
||||||
if binary_dir.exists():
|
if binary_dir.exists():
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ def fake_tar_gz():
|
|||||||
buf = BytesIO()
|
buf = BytesIO()
|
||||||
with tarfile.open(fileobj=buf, mode="w:gz") as tar:
|
with tarfile.open(fileobj=buf, mode="w:gz") as tar:
|
||||||
data = b"fake binary content"
|
data = b"fake binary content"
|
||||||
info = tarfile.TarInfo(name="voicebox-server-rocm.exe")
|
info = tarfile.TarInfo(name="talkbox-server-rocm.exe")
|
||||||
info.size = len(data)
|
info.size = len(data)
|
||||||
tar.addfile(info, BytesIO(data))
|
tar.addfile(info, BytesIO(data))
|
||||||
buf.seek(0)
|
buf.seek(0)
|
||||||
@@ -137,18 +137,18 @@ async def test_download_rocm_binary_progress_reporting(mock_backends_dir, fake_t
|
|||||||
libs_sha = hashlib.sha256(fake_tar_gz).hexdigest()
|
libs_sha = hashlib.sha256(fake_tar_gz).hexdigest()
|
||||||
|
|
||||||
responses = {
|
responses = {
|
||||||
"https://github.com/jamiepine/voicebox/releases/download/v0.2.3/voicebox-server-rocm.tar.gz": FakeResponse(
|
"https://github.com/jamiepine/talkbox/releases/download/v0.2.3/talkbox-server-rocm.tar.gz": FakeResponse(
|
||||||
content=fake_tar_gz,
|
content=fake_tar_gz,
|
||||||
headers={"content-length": str(len(fake_tar_gz))},
|
headers={"content-length": str(len(fake_tar_gz))},
|
||||||
),
|
),
|
||||||
"https://github.com/jamiepine/voicebox/releases/download/v0.2.3/voicebox-server-rocm.tar.gz.sha256": FakeResponse(
|
"https://github.com/jamiepine/talkbox/releases/download/v0.2.3/talkbox-server-rocm.tar.gz.sha256": FakeResponse(
|
||||||
content=f"{server_sha} voicebox-server-rocm.tar.gz\n".encode(),
|
content=f"{server_sha} talkbox-server-rocm.tar.gz\n".encode(),
|
||||||
),
|
),
|
||||||
f"https://github.com/jamiepine/voicebox/releases/download/v0.2.3/rocm-libs-{rocm.ROCM_LIBS_VERSION}.tar.gz": FakeResponse(
|
f"https://github.com/jamiepine/talkbox/releases/download/v0.2.3/rocm-libs-{rocm.ROCM_LIBS_VERSION}.tar.gz": FakeResponse(
|
||||||
content=fake_tar_gz,
|
content=fake_tar_gz,
|
||||||
headers={"content-length": str(len(fake_tar_gz))},
|
headers={"content-length": str(len(fake_tar_gz))},
|
||||||
),
|
),
|
||||||
f"https://github.com/jamiepine/voicebox/releases/download/v0.2.3/rocm-libs-{rocm.ROCM_LIBS_VERSION}.tar.gz.sha256": FakeResponse(
|
f"https://github.com/jamiepine/talkbox/releases/download/v0.2.3/rocm-libs-{rocm.ROCM_LIBS_VERSION}.tar.gz.sha256": FakeResponse(
|
||||||
content=f"{libs_sha} rocm-libs.tar.gz\n".encode(),
|
content=f"{libs_sha} rocm-libs.tar.gz\n".encode(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ async def test_download_rocm_binary_progress_reporting(mock_backends_dir, fake_t
|
|||||||
|
|
||||||
# Verify extraction
|
# Verify extraction
|
||||||
rocm_dir = rocm.get_rocm_dir()
|
rocm_dir = rocm.get_rocm_dir()
|
||||||
assert (rocm_dir / "voicebox-server-rocm.exe").exists()
|
assert (rocm_dir / "talkbox-server-rocm.exe").exists()
|
||||||
|
|
||||||
# Verify manifest written
|
# Verify manifest written
|
||||||
manifest_path = rocm.get_rocm_libs_manifest_path()
|
manifest_path = rocm.get_rocm_libs_manifest_path()
|
||||||
@@ -177,13 +177,13 @@ async def test_download_rocm_binary_progress_reporting(mock_backends_dir, fake_t
|
|||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_is_rocm_active(mock_backends_dir, monkeypatch):
|
async def test_is_rocm_active(mock_backends_dir, monkeypatch):
|
||||||
monkeypatch.setenv("VOICEBOX_BACKEND_VARIANT", "rocm")
|
monkeypatch.setenv("TALKBOX_BACKEND_VARIANT", "rocm")
|
||||||
assert rocm.is_rocm_active() is True
|
assert rocm.is_rocm_active() is True
|
||||||
|
|
||||||
monkeypatch.setenv("VOICEBOX_BACKEND_VARIANT", "cpu")
|
monkeypatch.setenv("TALKBOX_BACKEND_VARIANT", "cpu")
|
||||||
assert rocm.is_rocm_active() is False
|
assert rocm.is_rocm_active() is False
|
||||||
|
|
||||||
monkeypatch.delenv("VOICEBOX_BACKEND_VARIANT", raising=False)
|
monkeypatch.delenv("TALKBOX_BACKEND_VARIANT", raising=False)
|
||||||
assert rocm.is_rocm_active() is False
|
assert rocm.is_rocm_active() is False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ class TestRocmRequirements:
|
|||||||
|
|
||||||
@pytest.mark.timeout(900)
|
@pytest.mark.timeout(900)
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
not os.environ.get("VOICEBOX_TEST_ROCM_INSTALL"),
|
not os.environ.get("TALKBOX_TEST_ROCM_INSTALL"),
|
||||||
reason="Set VOICEBOX_TEST_ROCM_INSTALL=1 to run the heavy install test",
|
reason="Set TALKBOX_TEST_ROCM_INSTALL=1 to run the heavy install test",
|
||||||
)
|
)
|
||||||
def test_rocm_torch_installs_and_detects_amd(self, backend_dir):
|
def test_rocm_torch_installs_and_detects_amd(self, backend_dir):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from typing import List, Tuple
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
logger = logging.getLogger("voicebox.chunked-tts")
|
logger = logging.getLogger("talkbox.chunked-tts")
|
||||||
|
|
||||||
# Default chunk size in characters. Can be overridden per-request via
|
# Default chunk size in characters. Can be overridden per-request via
|
||||||
# the ``max_chunk_chars`` field on GenerationRequest.
|
# the ``max_chunk_chars`` field on GenerationRequest.
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ def patch_transformers_mistral_regex():
|
|||||||
variant. That call raises on ``HF_HUB_OFFLINE=1`` and on plain network
|
variant. That call raises on ``HF_HUB_OFFLINE=1`` and on plain network
|
||||||
failures, killing unrelated loads (Qwen TTS, TADA, etc.).
|
failures, killing unrelated loads (Qwen TTS, TADA, etc.).
|
||||||
|
|
||||||
Voicebox never loads Mistral models, so the rewrite the function would
|
TalkBox never loads Mistral models, so the rewrite the function would
|
||||||
apply is a no-op for us anyway. Wrap the method so any exception from the
|
apply is a no-op for us anyway. Wrap the method so any exception from the
|
||||||
metadata lookup returns the tokenizer unchanged — matching the success-path
|
metadata lookup returns the tokenizer unchanged — matching the success-path
|
||||||
behavior for non-Mistral repos (transformers 4.57.3,
|
behavior for non-Mistral repos (transformers 4.57.3,
|
||||||
@@ -264,7 +264,7 @@ def ensure_original_qwen_config_cached():
|
|||||||
logger.warning("could not create cache symlink for %s", original_repo, exc_info=True)
|
logger.warning("could not create cache symlink for %s", original_repo, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
if os.environ.get("VOICEBOX_OFFLINE_PATCH", "1") != "0":
|
if os.environ.get("TALKBOX_OFFLINE_PATCH", "1") != "0":
|
||||||
patch_huggingface_hub_offline()
|
patch_huggingface_hub_offline()
|
||||||
patch_transformers_mistral_regex()
|
patch_transformers_mistral_regex()
|
||||||
ensure_original_qwen_config_cached()
|
ensure_original_qwen_config_cached()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
# ROCm (AMD GPU) overlay for Voicebox
|
# ROCm (AMD GPU) overlay for TalkBox
|
||||||
#
|
#
|
||||||
# docker compose -f docker-compose.yml -f docker-compose.rocm.yml up --build
|
# docker compose -f docker-compose.yml -f docker-compose.rocm.yml up --build
|
||||||
#
|
#
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
# RDNA4 (RX 9000): export ROCM_VERSION=7.2 (default 6.3 covers RDNA1-3).
|
# RDNA4 (RX 9000): export ROCM_VERSION=7.2 (default 6.3 covers RDNA1-3).
|
||||||
|
|
||||||
services:
|
services:
|
||||||
voicebox:
|
talkbox:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
args:
|
args:
|
||||||
|
|||||||
+10
-10
@@ -1,17 +1,17 @@
|
|||||||
# Voicebox — CPU build (default)
|
# TalkBox — CPU build (default)
|
||||||
# For AMD ROCm GPU acceleration use the overlay:
|
# For AMD ROCm GPU acceleration use the overlay:
|
||||||
# docker compose -f docker-compose.yml -f docker-compose.rocm.yml up --build
|
# docker compose -f docker-compose.yml -f docker-compose.rocm.yml up --build
|
||||||
|
|
||||||
services:
|
services:
|
||||||
voicebox:
|
talkbox:
|
||||||
build: .
|
build: .
|
||||||
container_name: voicebox
|
container_name: talkbox
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
# Host-side moved to 17600 so the dev/installed Voicebox can keep 17493.
|
# Host-side moved to 17600 so the dev/installed TalkBox can keep 17494.
|
||||||
# Container still listens on its native port internally.
|
# Container still listens on its native port internally.
|
||||||
- "127.0.0.1:17600:17493"
|
- "127.0.0.1:17600:17494"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
# Bind-mount for generated audio (customize the host path as needed)
|
# Bind-mount for generated audio (customize the host path as needed)
|
||||||
@@ -20,17 +20,17 @@ services:
|
|||||||
- ./output:/app/data/generations
|
- ./output:/app/data/generations
|
||||||
|
|
||||||
# Named volume for profiles, DB, cache (persists across container restarts)
|
# Named volume for profiles, DB, cache (persists across container restarts)
|
||||||
- voicebox-data:/app/data
|
- talkbox-data:/app/data
|
||||||
|
|
||||||
# HuggingFace model cache (so models aren't re-downloaded on rebuild)
|
# HuggingFace model cache (so models aren't re-downloaded on rebuild)
|
||||||
- huggingface-cache:/home/voicebox/.cache/huggingface
|
- huggingface-cache:/home/talkbox/.cache/huggingface
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- LOG_LEVEL=info
|
- LOG_LEVEL=info
|
||||||
- NUMBA_CACHE_DIR=/tmp/numba_cache
|
- NUMBA_CACHE_DIR=/tmp/numba_cache
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
- voicebox-net
|
- talkbox-net
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
@@ -39,9 +39,9 @@ services:
|
|||||||
memory: 8G
|
memory: 8G
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
voicebox-net:
|
talkbox-net:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
voicebox-data:
|
talkbox-data:
|
||||||
huggingface-cache:
|
huggingface-cache:
|
||||||
|
|||||||
+14
-14
@@ -1,4 +1,4 @@
|
|||||||
# Voicebox Project Status & Roadmap
|
# TalkBox Project Status & Roadmap
|
||||||
|
|
||||||
> Last updated: 2026-07-02 | Current version: **v0.5.0** | 402 open issues | 88 open PRs | 1.3M downloads · 34.8k stars
|
> Last updated: 2026-07-02 | Current version: **v0.5.0** | 402 open issues | 88 open PRs | 1.3M downloads · 34.8k stars
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
**Tauri shell (Rust)** hosts a **React frontend** (`app/`) that talks over HTTP on `localhost:17493` to a **FastAPI backend** (`backend/`).
|
**Tauri shell (Rust)** hosts a **React frontend** (`app/`) that talks over HTTP on `localhost:17494` to a **FastAPI backend** (`backend/`).
|
||||||
|
|
||||||
The backend exposes:
|
The backend exposes:
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ POST /generate
|
|||||||
- MCP rough edges — dotted tool names violate Claude Desktop's name pattern (#790), audio scrambled over MCP (#780).
|
- MCP rough edges — dotted tool names violate Claude Desktop's name pattern (#790), audio scrambled over MCP (#780).
|
||||||
- Refinement silently translates non-English transcripts to English (#603).
|
- Refinement silently translates non-English transcripts to English (#603).
|
||||||
|
|
||||||
**Funding model — `$VOICEBOX` token (#806):** the two-month gap was a solo-dev decision about long-term sustainability, not neglect or a compromise. `$VOICEBOX` (Solana) is the **official, dev-controlled** token and the chosen revenue path — donations/sponsors didn't cover full-time work. The app stays 100% free, open-source, local-first, no subscriptions. Dev supply is being bought back and burned (done twice), liquidity locked. It has funded ~2–3 months of full-time work, so the cadence resumes this week. The #806 thread was a community concern, addressed transparently and resolved amicably; keep an eye out for actual impersonator/community tokens, which are a separate thing.
|
**Funding model — `$TALKBOX` token (#806):** the two-month gap was a solo-dev decision about long-term sustainability, not neglect or a compromise. `$TALKBOX` (Solana) is the **official, dev-controlled** token and the chosen revenue path — donations/sponsors didn't cover full-time work. The app stays 100% free, open-source, local-first, no subscriptions. Dev supply is being bought back and burned (done twice), liquidity locked. It has funded ~2–3 months of full-time work, so the cadence resumes this week. The #806 thread was a community concern, addressed transparently and resolved amicably; keep an eye out for actual impersonator/community tokens, which are a separate thing.
|
||||||
|
|
||||||
**Other trust/security signals:** macOS malware-flag reports continue (#369); a DNS-rebinding / Host-header exposure on the local API+MCP server was reported with fixes attached (#778).
|
**Other trust/security signals:** macOS malware-flag reports continue (#369); a DNS-rebinding / Host-header exposure on the local API+MCP server was reported with fixes attached (#778).
|
||||||
|
|
||||||
@@ -118,10 +118,10 @@ POST /generate
|
|||||||
|
|
||||||
### What's Shipped (v0.5.0 — the Capture release)
|
### What's Shipped (v0.5.0 — the Capture release)
|
||||||
|
|
||||||
Shipped 2026-04-25 (PR #544). Voicebox went from a voice-cloning studio to a full voice studio — dictation in, agent speech out, a local LLM in the middle.
|
Shipped 2026-04-25 (PR #544). TalkBox went from a voice-cloning studio to a full voice studio — dictation in, agent speech out, a local LLM in the middle.
|
||||||
|
|
||||||
- **Dictation** — global hotkey capture (push-to-talk + toggle chords), on-screen pill with live state, auto-paste into the focused field with clipboard save/restore, chord-picker UI. Scoped Accessibility permission (transcripts still land if paste is denied).
|
- **Dictation** — global hotkey capture (push-to-talk + toggle chords), on-screen pill with live state, auto-paste into the focused field with clipboard save/restore, chord-picker UI. Scoped Accessibility permission (transcripts still land if paste is denied).
|
||||||
- **MCP server** at `http://127.0.0.1:17493/mcp` — `voicebox.speak` / `.transcribe` / `.list_captures` / `.list_profiles`. Streamable HTTP primary transport, stdio sidecar shim, per-client voice binding via `X-Voicebox-Client-Id`. Speaking pill always shows agent-initiated output.
|
- **MCP server** at `http://127.0.0.1:17494/mcp` — `talkbox.speak` / `.transcribe` / `.list_captures` / `.list_profiles`. Streamable HTTP primary transport, stdio sidecar shim, per-client voice binding via `X-TalkBox-Client-Id`. Speaking pill always shows agent-initiated output.
|
||||||
- **Personality** — voice profiles carry an optional ≤2000-char persona. Compose (shuffle an in-character line) and Speak-in-character (rewrite input before TTS), both on a local Qwen3 LLM that doubles as the refinement model.
|
- **Personality** — voice profiles carry an optional ≤2000-char persona. Compose (shuffle an in-character line) and Speak-in-character (rewrite input before TTS), both on a local Qwen3 LLM that doubles as the refinement model.
|
||||||
- **Refinement** — on-device Qwen3 strips fillers, fixes punctuation, optional self-correction rewrites; Whisper hallucination-loop stripping at a 6-token threshold; per-capture flag snapshots; model picker (0.6B / 1.7B / 4B).
|
- **Refinement** — on-device Qwen3 strips fillers, fixes punctuation, optional self-correction rewrites; Whisper hallucination-loop stripping at a 6-token threshold; per-capture flag snapshots; model picker (0.6B / 1.7B / 4B).
|
||||||
- **`POST /speak` REST wrapper** and **i18next foundation** (English + zh-CN) also landed.
|
- **`POST /speak` REST wrapper** and **i18next foundation** (English + zh-CN) also landed.
|
||||||
@@ -189,7 +189,7 @@ Shipped 2026-04-25 (PR #544). Voicebox went from a voice-cloning studio to a ful
|
|||||||
| Model | PR / Branch | Reason |
|
| Model | PR / Branch | Reason |
|
||||||
|-------|-------------|--------|
|
|-------|-------------|--------|
|
||||||
| **CosyVoice2/3** | PR #311 | Output quality too poor. Heavy deps, no PyPI, needed 5+ shims. PR should be closed. |
|
| **CosyVoice2/3** | PR #311 | Output quality too poor. Heavy deps, no PyPI, needed 5+ shims. PR should be closed. |
|
||||||
| **VoxCPM 1.5 / VoxCPM2** | `voicebox-new-models` research (2026-04-18) | **Backlogged.** See detailed analysis below. |
|
| **VoxCPM 1.5 / VoxCPM2** | `talkbox-new-models` research (2026-04-18) | **Backlogged.** See detailed analysis below. |
|
||||||
|
|
||||||
#### VoxCPM — Evaluation Notes (2026-04-18)
|
#### VoxCPM — Evaluation Notes (2026-04-18)
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ Shipped 2026-04-25 (PR #544). Voicebox went from a voice-cloning studio to a ful
|
|||||||
- **CPU unsupported in the Python package** — issue #256 shows `voxcpm --device cpu` rejected with `unrecognized arguments`. The only CPU path is the third-party **VoxCPM.cpp** GGML engine, which is a separate ecosystem project, not `pip install voxcpm`.
|
- **CPU unsupported in the Python package** — issue #256 shows `voxcpm --device cpu` rejected with `unrecognized arguments`. The only CPU path is the third-party **VoxCPM.cpp** GGML engine, which is a separate ecosystem project, not `pip install voxcpm`.
|
||||||
- **macOS source install fails** — issue #233 open with no resolution.
|
- **macOS source install fails** — issue #233 open with no resolution.
|
||||||
- Would require CUDA-only gating in UI (new `requires_cuda` flag on `ModelConfig`, lock icon + "Requires NVIDIA GPU" in `ModelManagement.tsx` / `EngineModelSelector.tsx`) plus a hard error at `load_model()` as safety net. Doable but adds first-class platform gating that doesn't exist for any other engine today.
|
- Would require CUDA-only gating in UI (new `requires_cuda` flag on `ModelConfig`, lock icon + "Requires NVIDIA GPU" in `ModelManagement.tsx` / `EngineModelSelector.tsx`) plus a hard error at `load_model()` as safety net. Doable but adds first-class platform gating that doesn't exist for any other engine today.
|
||||||
- Voicebox's user base skews Apple Silicon (MLX is a primary backend). Shipping a CUDA-only model sets a precedent worth a separate scoping discussion (see issues #419 engine sprawl, #420 platform tiers, PR #465).
|
- TalkBox's user base skews Apple Silicon (MLX is a primary backend). Shipping a CUDA-only model sets a precedent worth a separate scoping discussion (see issues #419 engine sprawl, #420 platform tiers, PR #465).
|
||||||
|
|
||||||
**What would change the decision:**
|
**What would change the decision:**
|
||||||
- Upstream fixes MPS crashes (watch issues #232, #248).
|
- Upstream fixes MPS crashes (watch issues #232, #248).
|
||||||
@@ -220,7 +220,7 @@ Shipped 2026-04-25 (PR #544). Voicebox went from a voice-cloning studio to a ful
|
|||||||
|
|
||||||
### Funded Roadmap (2026-H2)
|
### Funded Roadmap (2026-H2)
|
||||||
|
|
||||||
`$VOICEBOX` funded ~2–3 months of full-time work; cadence resumes the week of 2026-06-27. Direction committed publicly in #806:
|
`$TALKBOX` funded ~2–3 months of full-time work; cadence resumes the week of 2026-06-27. Direction committed publicly in #806:
|
||||||
|
|
||||||
| Item | Notes |
|
| Item | Notes |
|
||||||
|------|-------|
|
|------|-------|
|
||||||
@@ -239,7 +239,7 @@ Shipped 2026-04-25 (PR #544). Voicebox went from a voice-cloning studio to a ful
|
|||||||
| Engine sprawl cleanup | issue #419 | First-class vs experimental TTS backends distinction |
|
| Engine sprawl cleanup | issue #419 | First-class vs experimental TTS backends distinction |
|
||||||
| Frontend tech-debt burn-down | issue #421 | Biome + a11y debt before gating CI |
|
| Frontend tech-debt burn-down | issue #421 | Biome + a11y debt before gating CI |
|
||||||
| Docker registry auto-publish | PR #463, issue #453 | ghcr.io image on tag push |
|
| Docker registry auto-publish | PR #463, issue #453 | ghcr.io image on tag push |
|
||||||
| New model research | `voicebox-new-models` branch | Evaluating Fish Speech, XTTS-v2, Pocket TTS, VibeVoice, Fish Audio S2, index-tts2. **2026-06-27 sweep** added dots.tts, LongCat-AudioDiT, SoproTTS, NeuTTS, Nemotron/Cohere STT — see Landscape → New Candidate Sweep |
|
| New model research | `talkbox-new-models` branch | Evaluating Fish Speech, XTTS-v2, Pocket TTS, VibeVoice, Fish Audio S2, index-tts2. **2026-06-27 sweep** added dots.tts, LongCat-AudioDiT, SoproTTS, NeuTTS, Nemotron/Cohere STT — see Landscape → New Candidate Sweep |
|
||||||
|
|
||||||
### TTS Engine Comparison
|
### TTS Engine Comparison
|
||||||
|
|
||||||
@@ -407,7 +407,7 @@ One contributor opened a large, coherent quality suite in a single day. Review a
|
|||||||
| Long-form / chunking / export | ~16 | Pause control, speed control, audiobook export, >50k chars |
|
| Long-form / chunking / export | ~16 | Pause control, speed control, audiobook export, >50k chars |
|
||||||
| Linux / Wayland | ~11 | Build failures, Wayland panics, CUDA-on-Linux packaging |
|
| Linux / Wayland | ~11 | Build failures, Wayland panics, CUDA-on-Linux packaging |
|
||||||
| MCP / agent / API | ~9 | Dotted tool names (#790), scrambled audio (#780), OpenAI compat (#10) |
|
| MCP / agent / API | ~9 | Dotted tool names (#790), scrambled audio (#780), OpenAI compat (#10) |
|
||||||
| Security / trust | ~4 | DNS-rebinding (#778), malware flag (#369); funding via official $VOICEBOX token (#806) |
|
| Security / trust | ~4 | DNS-rebinding (#778), malware flag (#369); funding via official $TALKBOX token (#806) |
|
||||||
|
|
||||||
**Highest-engagement open issues:** #185 Fine-tune instructions (32c) · #98 Connecting to Download (16c) · #301 CUDA generation failure (18c) · #20 Model download failed (13c) · #364 Voxtral-TTS FR (11r) · #341 Arch Linux build · #513 server startup failed (12c) · #138 ONNX/Piper export (9r) · #10 OpenAI API compat.
|
**Highest-engagement open issues:** #185 Fine-tune instructions (32c) · #98 Connecting to Download (16c) · #301 CUDA generation failure (18c) · #20 Model download failed (13c) · #364 Voxtral-TTS FR (11r) · #341 Arch Linux build · #513 server startup failed (12c) · #138 ONNX/Piper export (9r) · #10 OpenAI API compat.
|
||||||
|
|
||||||
@@ -420,7 +420,7 @@ One contributor opened a large, coherent quality suite in a single day. Review a
|
|||||||
- **GPU expansion requests:** #676 DirectML (AMD/Intel), #759 Intel Arc, #684 RTX 5060 Ti CUDA 13, #774 CUDA 11.x for older cards, #767 Linux CUDA installs Windows `.exe`.
|
- **GPU expansion requests:** #676 DirectML (AMD/Intel), #759 Intel Arc, #684 RTX 5060 Ti CUDA 13, #774 CUDA 11.x for older cards, #767 Linux CUDA installs Windows `.exe`.
|
||||||
- **New engines/langs:** #791 OmniVoice, #633 VoxCPM2, #690 Dramabox, #638 Bengali, #754 zh-TW, #761 Filipino.
|
- **New engines/langs:** #791 OmniVoice, #633 VoxCPM2, #690 Dramabox, #638 Bengali, #754 zh-TW, #761 Filipino.
|
||||||
- **Open plugin interface (#771):** request for a community engine/provider plugin API — ties into engine-sprawl (#419) and platform-gating work.
|
- **Open plugin interface (#771):** request for a community engine/provider plugin API — ties into engine-sprawl (#419) and platform-gating work.
|
||||||
- **Trust/security:** #806 — `$VOICEBOX` is the official dev-backed funding token (concern raised and resolved on-thread; see funding note above); #778 DNS-rebinding/Host-header exposure on local API+MCP (fixes attached) — genuine security item; #369 macOS malware flag (ongoing).
|
- **Trust/security:** #806 — `$TALKBOX` is the official dev-backed funding token (concern raised and resolved on-thread; see funding note above); #778 DNS-rebinding/Host-header exposure on local API+MCP (fixes attached) — genuine security item; #369 macOS malware flag (ongoing).
|
||||||
|
|
||||||
### GPU / Hardware Detection — still the top category
|
### GPU / Hardware Detection — still the top category
|
||||||
|
|
||||||
@@ -544,7 +544,7 @@ Notable:
|
|||||||
| macOS | #441 (older macOS), #369 (malware flag), #334 (microphone permission), #287 (`check_model_inputs` ImportError — regression), #171 (ARM64 binary won't open) |
|
| macOS | #441 (older macOS), #369 (malware flag), #334 (microphone permission), #287 (`check_model_inputs` ImportError — regression), #171 (ARM64 binary won't open) |
|
||||||
| Profile/UI | #360 (Kokoro profile hides others — partly addressed by auto-switch), #299 (drag-drop on Win11), #329 (size selector state bug), #393 (stuck loading screen after reinstall to new dir) |
|
| Profile/UI | #360 (Kokoro profile hides others — partly addressed by auto-switch), #299 (drag-drop on Win11), #329 (size selector state bug), #393 (stuck loading screen after reinstall to new dir) |
|
||||||
| Integrations | #397 (SAMMI-bot 422 Unprocessable Entity) |
|
| Integrations | #397 (SAMMI-bot 422 Unprocessable Entity) |
|
||||||
| Audio playback / session | **#41** (macOS: Voicebox goes silent after another app takes audio output; restart restores it) — see deep-dive below |
|
| Audio playback / session | **#41** (macOS: TalkBox goes silent after another app takes audio output; restart restores it) — see deep-dive below |
|
||||||
| Database | #174 (sqlite3 IntegrityError) |
|
| Database | #174 (sqlite3 IntegrityError) |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -773,8 +773,8 @@ Feeds the planned streaming-transcription roadmap; Whisper alternatives.
|
|||||||
|
|
||||||
| Branch | PR | Status | Notes |
|
| Branch | PR | Status | Notes |
|
||||||
|--------|-----|--------|-------|
|
|--------|-----|--------|-------|
|
||||||
| `voicebox-new-models` | — | **Active** | New model research (Fish Speech, Pocket TTS, VibeVoice, etc.); VoxCPM evaluated & backlogged |
|
| `talkbox-new-models` | — | **Active** | New model research (Fish Speech, Pocket TTS, VibeVoice, etc.); VoxCPM evaluated & backlogged |
|
||||||
| `fix/kokoro-pyinstaller-source-files` | — | Active | Kokoro frozen-build source bundling (parent of `voicebox-new-models`) |
|
| `fix/kokoro-pyinstaller-source-files` | — | Active | Kokoro frozen-build source bundling (parent of `talkbox-new-models`) |
|
||||||
| `feat/cosyvoice-engine` | #311 | Open — closing | CosyVoice2/3 — abandoned, poor quality |
|
| `feat/cosyvoice-engine` | #311 | Open — closing | CosyVoice2/3 — abandoned, poor quality |
|
||||||
| `feat/kokoro` | #325 | **Merged** | Kokoro 82M + voice profile type system |
|
| `feat/kokoro` | #325 | **Merged** | Kokoro 82M + voice profile type system |
|
||||||
| `feat/qwen-custom-voice` | #328 | **Merged** | Qwen CustomVoice preset engine |
|
| `feat/qwen-custom-voice` | #328 | **Merged** | Qwen CustomVoice preset engine |
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: "Documentation README"
|
title: "Documentation README"
|
||||||
description: "Voicebox documentation development guide"
|
description: "TalkBox documentation development guide"
|
||||||
---
|
---
|
||||||
|
|
||||||
This directory contains the documentation for Voicebox, built with [Fumadocs](https://fumadocs.dev).
|
This directory contains the documentation for TalkBox, built with [Fumadocs](https://fumadocs.dev).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
title: "Architecture"
|
title: "Architecture"
|
||||||
description: "Understanding Voicebox's technical architecture"
|
description: "Understanding TalkBox's technical architecture"
|
||||||
---
|
---
|
||||||
|
|
||||||
## System Overview
|
## System Overview
|
||||||
|
|
||||||
Voicebox uses a client-server architecture with a React frontend and Python backend. The desktop app is built with Tauri and contains two main layers:
|
TalkBox uses a client-server architecture with a React frontend and Python backend. The desktop app is built with Tauri and contains two main layers:
|
||||||
|
|
||||||
**Frontend Layer:** A React application that handles the UI components, state management with Zustand, and data fetching with React Query (TanStack Query).
|
**Frontend Layer:** A React application that handles the UI components, state management with Zustand, and data fetching with React Query (TanStack Query).
|
||||||
|
|
||||||
**Backend Layer:** A Python FastAPI server that hosts the REST API, runs a pluggable registry of TTS and STT engines, manages the SQLite database, and handles audio processing.
|
**Backend Layer:** A Python FastAPI server that hosts the REST API, runs a pluggable registry of TTS and STT engines, manages the SQLite database, and handles audio processing.
|
||||||
|
|
||||||
These two layers communicate via HTTP on `localhost:17493`, with the frontend making API requests to the backend. In production the backend is compiled with PyInstaller and launched as a Tauri sidecar; in development it's run manually via `uvicorn`.
|
These two layers communicate via HTTP on `localhost:17494`, with the frontend making API requests to the backend. In production the backend is compiled with PyInstaller and launched as a Tauri sidecar; in development it's run manually via `uvicorn`.
|
||||||
|
|
||||||
## Frontend Architecture
|
## Frontend Architecture
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ Shipped engines:
|
|||||||
| `tada` | TADA | Cloned |
|
| `tada` | TADA | Cloned |
|
||||||
| `kokoro` | Kokoro | Preset |
|
| `kokoro` | Kokoro | Preset |
|
||||||
|
|
||||||
See [TTS Engines](/developer/tts-engines) for the full contract and integration phases, and [PROJECT_STATUS.md](https://github.com/jamiepine/voicebox/blob/main/docs/PROJECT_STATUS.md) for candidates under evaluation.
|
See [TTS Engines](/developer/tts-engines) for the full contract and integration phases, and [PROJECT_STATUS.md](https://github.com/jamiepine/talkbox/blob/main/docs/PROJECT_STATUS.md) for candidates under evaluation.
|
||||||
|
|
||||||
### Key Modules
|
### Key Modules
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@ See [Building](/developer/building) for what PyInstaller does and how the CUDA b
|
|||||||
|
|
||||||
### Current
|
### Current
|
||||||
|
|
||||||
- Local-only by default (bound to `127.0.0.1:17493`)
|
- Local-only by default (bound to `127.0.0.1:17494`)
|
||||||
- No authentication (localhost trust)
|
- No authentication (localhost trust)
|
||||||
- File system sandboxing via Tauri
|
- File system sandboxing via Tauri
|
||||||
|
|
||||||
@@ -281,6 +281,6 @@ See [Building](/developer/building) for what PyInstaller does and how the CUDA b
|
|||||||
How to add a new engine
|
How to add a new engine
|
||||||
</Card>
|
</Card>
|
||||||
<Card title="Contributing" href="/developer/contributing">
|
<Card title="Contributing" href="/developer/contributing">
|
||||||
Contribute to Voicebox
|
Contribute to TalkBox
|
||||||
</Card>
|
</Card>
|
||||||
</Cards>
|
</Cards>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Audio Channels"
|
title: "Audio Channels"
|
||||||
description: "How audio output routing works in Voicebox"
|
description: "How audio output routing works in TalkBox"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
title: "Auto-Updater"
|
title: "Auto-Updater"
|
||||||
description: "How Voicebox automatic updates work"
|
description: "How TalkBox automatic updates work"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Voicebox uses Tauri's built-in auto-updater to deliver signed updates to users. The system verifies updates cryptographically before installation.
|
TalkBox uses Tauri's built-in auto-updater to deliver signed updates to users. The system verifies updates cryptographically before installation.
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
When Voicebox launches (in production Tauri builds only), it checks GitHub Releases for a `latest.json` manifest. If a newer version is available:
|
When TalkBox launches (in production Tauri builds only), it checks GitHub Releases for a `latest.json` manifest. If a newer version is available:
|
||||||
|
|
||||||
1. **Notification** - An update banner appears at the top of the app
|
1. **Notification** - An update banner appears at the top of the app
|
||||||
2. **Download** - User clicks "Install Now" to download the update package
|
2. **Download** - User clicks "Install Now" to download the update package
|
||||||
@@ -30,7 +30,7 @@ The updater is configured in `tauri/src-tauri/tauri.conf.json`:
|
|||||||
"active": true,
|
"active": true,
|
||||||
"dialog": false,
|
"dialog": false,
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
"https://github.com/jamiepine/voicebox/releases/latest/download/latest.json"
|
"https://github.com/jamiepine/talkbox/releases/latest/download/latest.json"
|
||||||
],
|
],
|
||||||
"pubkey": "PASTE_PUBLIC_KEY_CONTENT_HERE"
|
"pubkey": "PASTE_PUBLIC_KEY_CONTENT_HERE"
|
||||||
}
|
}
|
||||||
@@ -55,19 +55,19 @@ The `latest.json` file defines available updates per platform:
|
|||||||
"platforms": {
|
"platforms": {
|
||||||
"darwin-aarch64": {
|
"darwin-aarch64": {
|
||||||
"signature": "base64_encoded_signature",
|
"signature": "base64_encoded_signature",
|
||||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_aarch64.app.tar.gz"
|
"url": "https://github.com/jamiepine/talkbox/releases/download/v0.2.0/talkbox_0.2.0_aarch64.app.tar.gz"
|
||||||
},
|
},
|
||||||
"darwin-x86_64": {
|
"darwin-x86_64": {
|
||||||
"signature": "base64_encoded_signature",
|
"signature": "base64_encoded_signature",
|
||||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64.app.tar.gz"
|
"url": "https://github.com/jamiepine/talkbox/releases/download/v0.2.0/talkbox_0.2.0_x64.app.tar.gz"
|
||||||
},
|
},
|
||||||
"linux-x86_64": {
|
"linux-x86_64": {
|
||||||
"signature": "base64_encoded_signature",
|
"signature": "base64_encoded_signature",
|
||||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_amd64.AppImage"
|
"url": "https://github.com/jamiepine/talkbox/releases/download/v0.2.0/talkbox_0.2.0_amd64.AppImage"
|
||||||
},
|
},
|
||||||
"windows-x86_64": {
|
"windows-x86_64": {
|
||||||
"signature": "base64_encoded_signature",
|
"signature": "base64_encoded_signature",
|
||||||
"url": "https://github.com/jamiepine/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64_en-US.msi"
|
"url": "https://github.com/jamiepine/talkbox/releases/download/v0.2.0/talkbox_0.2.0_x64_en-US.msi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,11 +79,11 @@ Updates must be cryptographically signed to be accepted. The signing process:
|
|||||||
|
|
||||||
1. **Generate keys** (one-time setup):
|
1. **Generate keys** (one-time setup):
|
||||||
```bash
|
```bash
|
||||||
bun tauri signer generate -w ~/.tauri/voicebox.key
|
bun tauri signer generate -w ~/.tauri/talkbox.key
|
||||||
```
|
```
|
||||||
This creates:
|
This creates:
|
||||||
- Private key: `~/.tauri/voicebox.key` (stored in GitHub Secrets, never committed)
|
- Private key: `~/.tauri/talkbox.key` (stored in GitHub Secrets, never committed)
|
||||||
- Public key: `~/.tauri/voicebox.key.pub` (pasted into `tauri.conf.json`)
|
- Public key: `~/.tauri/talkbox.key.pub` (pasted into `tauri.conf.json`)
|
||||||
|
|
||||||
2. **Build with signing** (GitHub Actions handles this):
|
2. **Build with signing** (GitHub Actions handles this):
|
||||||
- Set `TAURI_SIGNING_PRIVATE_KEY` environment variable
|
- Set `TAURI_SIGNING_PRIVATE_KEY` environment variable
|
||||||
@@ -110,7 +110,7 @@ git tag v0.2.0 && git push --tags
|
|||||||
|
|
||||||
GitHub Actions needs these secrets set:
|
GitHub Actions needs these secrets set:
|
||||||
|
|
||||||
- `TAURI_SIGNING_PRIVATE_KEY` - Content of `~/.tauri/voicebox.key`
|
- `TAURI_SIGNING_PRIVATE_KEY` - Content of `~/.tauri/talkbox.key`
|
||||||
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` - Password for the key (if set)
|
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` - Password for the key (if set)
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
@@ -149,17 +149,17 @@ The CUDA-enabled backend is distributed separately from the main app because bun
|
|||||||
|
|
||||||
**Size comparison (approximate):**
|
**Size comparison (approximate):**
|
||||||
- Standard CPU bundle (in the installer): ~200–400 MB
|
- Standard CPU bundle (in the installer): ~200–400 MB
|
||||||
- CUDA server core: ~945 MB (versioned with each Voicebox release)
|
- CUDA server core: ~945 MB (versioned with each TalkBox release)
|
||||||
- CUDA libs (NVIDIA runtime DLLs): ~1.7 GB (versioned independently, cached across upgrades)
|
- CUDA libs (NVIDIA runtime DLLs): ~1.7 GB (versioned independently, cached across upgrades)
|
||||||
|
|
||||||
### Two-archive split
|
### Two-archive split
|
||||||
|
|
||||||
Since v0.4, the CUDA binary is packaged as **two archives** instead of one:
|
Since v0.4, the CUDA binary is packaged as **two archives** instead of one:
|
||||||
|
|
||||||
- **Server core** (`voicebox-server-cuda.tar.gz`) — the Python server + PyTorch code, changes every release.
|
- **Server core** (`talkbox-server-cuda.tar.gz`) — the Python server + PyTorch code, changes every release.
|
||||||
- **CUDA libs** (`cuda-libs-cu128-v1.tar.gz`) — the heavy NVIDIA CUDA/cuDNN DLLs, only re-downloaded when the CUDA toolkit major version changes.
|
- **CUDA libs** (`cuda-libs-cu128-v1.tar.gz`) — the heavy NVIDIA CUDA/cuDNN DLLs, only re-downloaded when the CUDA toolkit major version changes.
|
||||||
|
|
||||||
This means most Voicebox upgrades only re-download the ~945 MB server core, not the full ~2.5 GB bundle.
|
This means most TalkBox upgrades only re-download the ~945 MB server core, not the full ~2.5 GB bundle.
|
||||||
|
|
||||||
### Download Process
|
### Download Process
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ When a user clicks "Install CUDA backend" in Settings → GPU:
|
|||||||
2. **CUDA libs archive** — Downloaded separately (or reused if the installed version still matches).
|
2. **CUDA libs archive** — Downloaded separately (or reused if the installed version still matches).
|
||||||
3. **Verification** — SHA-256 checksum verification for integrity.
|
3. **Verification** — SHA-256 checksum verification for integrity.
|
||||||
4. **Placement** — Extracted into `{data_dir}/backends/cuda/`.
|
4. **Placement** — Extracted into `{data_dir}/backends/cuda/`.
|
||||||
5. **Restart** — The Voicebox server restarts and swaps in the CUDA backend.
|
5. **Restart** — The TalkBox server restarts and swaps in the CUDA backend.
|
||||||
|
|
||||||
### Auto-Update on Startup
|
### Auto-Update on Startup
|
||||||
|
|
||||||
@@ -181,8 +181,8 @@ Downloaded CUDA binaries live in the app's data directory:
|
|||||||
|
|
||||||
```
|
```
|
||||||
{data_dir}/backends/cuda/
|
{data_dir}/backends/cuda/
|
||||||
voicebox-server-cuda.exe # Windows
|
talkbox-server-cuda.exe # Windows
|
||||||
voicebox-server-cuda # macOS/Linux
|
talkbox-server-cuda # macOS/Linux
|
||||||
<NVIDIA CUDA runtime DLLs>
|
<NVIDIA CUDA runtime DLLs>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ Downloads report progress via Server-Sent Events (SSE):
|
|||||||
GET /backend/cuda-progress
|
GET /backend/cuda-progress
|
||||||
|
|
||||||
event: progress
|
event: progress
|
||||||
data: {"current": 52428800, "total": 945000000, "filename": "voicebox-server-cuda.tar.gz", "status": "downloading"}
|
data: {"current": 52428800, "total": 945000000, "filename": "talkbox-server-cuda.tar.gz", "status": "downloading"}
|
||||||
```
|
```
|
||||||
|
|
||||||
The frontend subscribes to this endpoint to show real-time progress, including which archive (server core vs libs) is currently downloading.
|
The frontend subscribes to this endpoint to show real-time progress, including which archive (server core vs libs) is currently downloading.
|
||||||
@@ -212,7 +212,7 @@ The frontend subscribes to this endpoint to show real-time progress, including w
|
|||||||
|
|
||||||
For each CUDA-capable release, these files are uploaded to GitHub:
|
For each CUDA-capable release, these files are uploaded to GitHub:
|
||||||
|
|
||||||
- `voicebox-server-cuda.tar.gz` — server-core archive
|
- `talkbox-server-cuda.tar.gz` — server-core archive
|
||||||
- `voicebox-server-cuda.tar.gz.sha256` — checksum
|
- `talkbox-server-cuda.tar.gz.sha256` — checksum
|
||||||
- `cuda-libs-cu128-v1.tar.gz` — CUDA runtime libs (only when the libs version bumps)
|
- `cuda-libs-cu128-v1.tar.gz` — CUDA runtime libs (only when the libs version bumps)
|
||||||
- `cuda-libs-cu128-v1.tar.gz.sha256` — checksum
|
- `cuda-libs-cu128-v1.tar.gz.sha256` — checksum
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: "Building"
|
title: "Building"
|
||||||
description: "How Voicebox is built for production"
|
description: "How TalkBox is built for production"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Voicebox uses a two-stage build process:
|
TalkBox uses a two-stage build process:
|
||||||
|
|
||||||
1. **Python Server Binary** — PyInstaller bundles the FastAPI backend into a standalone executable
|
1. **Python Server Binary** — PyInstaller bundles the FastAPI backend into a standalone executable
|
||||||
2. **Tauri Desktop App** — Bundles the React frontend, Rust wrapper, and Python server as a sidecar
|
2. **Tauri Desktop App** — Bundles the React frontend, Rust wrapper, and Python server as a sidecar
|
||||||
@@ -33,7 +33,7 @@ cd backend
|
|||||||
python build_binary.py
|
python build_binary.py
|
||||||
|
|
||||||
# Copy to Tauri's binaries directory
|
# Copy to Tauri's binaries directory
|
||||||
cp dist/voicebox-server ../tauri/src-tauri/binaries/voicebox-server-${PLATFORM}
|
cp dist/talkbox-server ../tauri/src-tauri/binaries/talkbox-server-${PLATFORM}
|
||||||
```
|
```
|
||||||
|
|
||||||
### PyInstaller Configuration
|
### PyInstaller Configuration
|
||||||
@@ -119,13 +119,13 @@ The server binary is declared as an external binary in `tauri.conf.json`:
|
|||||||
{
|
{
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"externalBin": ["binaries/voicebox-server"]
|
"externalBin": ["binaries/talkbox-server"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Tauri looks for `voicebox-server-${PLATFORM}` in `src-tauri/binaries/` and bundles it.
|
Tauri looks for `talkbox-server-${PLATFORM}` in `src-tauri/binaries/` and bundles it.
|
||||||
|
|
||||||
## GitHub Actions Release
|
## GitHub Actions Release
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ The `build-cuda-windows` job runs separately:
|
|||||||
1. Install PyTorch with CUDA 12.8
|
1. Install PyTorch with CUDA 12.8
|
||||||
2. Build with `build_binary.py --cuda` (produces `--onedir` output)
|
2. Build with `build_binary.py --cuda` (produces `--onedir` output)
|
||||||
3. Package with `scripts/package_cuda.py` into two archives:
|
3. Package with `scripts/package_cuda.py` into two archives:
|
||||||
- `voicebox-server-cuda.tar.gz` — server core (~945 MB)
|
- `talkbox-server-cuda.tar.gz` — server core (~945 MB)
|
||||||
- `cuda-libs-cu128-v1.tar.gz` — NVIDIA runtime libraries (~1.7 GB, cached independently)
|
- `cuda-libs-cu128-v1.tar.gz` — NVIDIA runtime libraries (~1.7 GB, cached independently)
|
||||||
4. Upload archives as release artifacts
|
4. Upload archives as release artifacts
|
||||||
|
|
||||||
@@ -187,6 +187,6 @@ This binary is downloaded on-demand by users who enable CUDA in settings. The CU
|
|||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Tauri can't find sidecar">
|
<Accordion title="Tauri can't find sidecar">
|
||||||
Ensure binary exists at `tauri/src-tauri/binaries/voicebox-server-${PLATFORM}` before running Tauri build.
|
Ensure binary exists at `tauri/src-tauri/binaries/talkbox-server-${PLATFORM}` before running Tauri build.
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: "Contributing"
|
title: "Contributing"
|
||||||
description: "How to contribute to Voicebox"
|
description: "How to contribute to TalkBox"
|
||||||
---
|
---
|
||||||
|
|
||||||
Thank you for your interest in contributing to Voicebox! This guide will help you get started.
|
Thank you for your interest in contributing to TalkBox! This guide will help you get started.
|
||||||
|
|
||||||
## Code of Conduct
|
## Code of Conduct
|
||||||
|
|
||||||
@@ -16,10 +16,10 @@ Thank you for your interest in contributing to Voicebox! This guide will help yo
|
|||||||
|
|
||||||
Before you start contributing, make sure you have:
|
Before you start contributing, make sure you have:
|
||||||
|
|
||||||
1. **Read the documentation** to understand how Voicebox works
|
1. **Read the documentation** to understand how TalkBox works
|
||||||
2. **Set up your development environment** — see [Development Setup](/developer/setup)
|
2. **Set up your development environment** — see [Development Setup](/developer/setup)
|
||||||
3. **Explored the codebase** to understand the project structure
|
3. **Explored the codebase** to understand the project structure
|
||||||
4. **Checked [`docs/PROJECT_STATUS.md`](https://github.com/jamiepine/voicebox/blob/main/docs/PROJECT_STATUS.md)** — the living engineering roadmap that tracks prioritized tasks (Tier 1 → 3), architectural bottlenecks, and candidate TTS engines under evaluation (including why some are backlogged)
|
4. **Checked [`docs/PROJECT_STATUS.md`](https://github.com/jamiepine/talkbox/blob/main/docs/PROJECT_STATUS.md)** — the living engineering roadmap that tracks prioritized tasks (Tier 1 → 3), architectural bottlenecks, and candidate TTS engines under evaluation (including why some are backlogged)
|
||||||
5. **Checked existing issues** to see if someone else is working on something similar
|
5. **Checked existing issues** to see if someone else is working on something similar
|
||||||
|
|
||||||
## Ways to Contribute
|
## Ways to Contribute
|
||||||
@@ -46,8 +46,8 @@ Before you start contributing, make sure you have:
|
|||||||
```bash
|
```bash
|
||||||
# Fork the repository on GitHub
|
# Fork the repository on GitHub
|
||||||
# Then clone your fork
|
# Then clone your fork
|
||||||
git clone https://github.com/YOUR_USERNAME/voicebox.git
|
git clone https://github.com/YOUR_USERNAME/talkbox.git
|
||||||
cd voicebox
|
cd talkbox
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Create a Branch
|
### 2. Create a Branch
|
||||||
@@ -166,7 +166,7 @@ When creating a pull request:
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
<Files>
|
<Files>
|
||||||
<Folder name="voicebox" defaultOpen>
|
<Folder name="talkbox" defaultOpen>
|
||||||
<Folder name="app/src">
|
<Folder name="app/src">
|
||||||
<File name="components/" />
|
<File name="components/" />
|
||||||
<File name="lib/" />
|
<File name="lib/" />
|
||||||
@@ -197,13 +197,13 @@ When creating a pull request:
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- Check [existing issues](https://github.com/jamiepine/voicebox/issues) for bugs
|
- Check [existing issues](https://github.com/jamiepine/talkbox/issues) for bugs
|
||||||
- Test your fix thoroughly
|
- Test your fix thoroughly
|
||||||
- Add regression tests if possible
|
- Add regression tests if possible
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
- Check [`docs/PROJECT_STATUS.md`](https://github.com/jamiepine/voicebox/blob/main/docs/PROJECT_STATUS.md) and the [roadmap](https://github.com/jamiepine/voicebox#roadmap) before proposing work — the status doc lists prioritized tasks (Tier 1 → 3), known architectural bottlenecks, and candidate TTS engines already under evaluation (including why some have been backlogged)
|
- Check [`docs/PROJECT_STATUS.md`](https://github.com/jamiepine/talkbox/blob/main/docs/PROJECT_STATUS.md) and the [roadmap](https://github.com/jamiepine/talkbox#roadmap) before proposing work — the status doc lists prioritized tasks (Tier 1 → 3), known architectural bottlenecks, and candidate TTS engines already under evaluation (including why some have been backlogged)
|
||||||
- Discuss major features in an issue first
|
- Discuss major features in an issue first
|
||||||
- Keep features focused and well-scoped
|
- Keep features focused and well-scoped
|
||||||
- Adding a new TTS engine? See [TTS Engines](/developer/tts-engines) for the phased workflow
|
- Adding a new TTS engine? See [TTS Engines](/developer/tts-engines) for the phased workflow
|
||||||
@@ -318,7 +318,7 @@ GitHub Actions automatically builds and publishes releases when tags are pushed.
|
|||||||
## Recognition
|
## Recognition
|
||||||
|
|
||||||
Contributors are recognized in:
|
Contributors are recognized in:
|
||||||
- [CHANGELOG.md](https://github.com/jamiepine/voicebox/blob/main/CHANGELOG.md)
|
- [CHANGELOG.md](https://github.com/jamiepine/talkbox/blob/main/CHANGELOG.md)
|
||||||
- GitHub contributor list
|
- GitHub contributor list
|
||||||
- Release notes
|
- Release notes
|
||||||
|
|
||||||
@@ -331,9 +331,9 @@ By contributing, you agree that your contributions will be licensed under the MI
|
|||||||
If you have questions:
|
If you have questions:
|
||||||
|
|
||||||
1. Check the [documentation](/overview/introduction)
|
1. Check the [documentation](/overview/introduction)
|
||||||
2. Read [`docs/PROJECT_STATUS.md`](https://github.com/jamiepine/voicebox/blob/main/docs/PROJECT_STATUS.md) for current engineering priorities
|
2. Read [`docs/PROJECT_STATUS.md`](https://github.com/jamiepine/talkbox/blob/main/docs/PROJECT_STATUS.md) for current engineering priorities
|
||||||
3. Search [existing issues](https://github.com/jamiepine/voicebox/issues)
|
3. Search [existing issues](https://github.com/jamiepine/talkbox/issues)
|
||||||
4. Open a new issue or discussion
|
4. Open a new issue or discussion
|
||||||
5. See [CONTRIBUTING.md](https://github.com/jamiepine/voicebox/blob/main/CONTRIBUTING.md) in the repo
|
5. See [CONTRIBUTING.md](https://github.com/jamiepine/talkbox/blob/main/CONTRIBUTING.md) in the repo
|
||||||
|
|
||||||
Thank you for contributing to Voicebox! 🎉
|
Thank you for contributing to TalkBox! 🎉
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Generation History"
|
title: "Generation History"
|
||||||
description: "How generation history tracking works in Voicebox"
|
description: "How generation history tracking works in TalkBox"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ description: "How model downloading, loading, and status tracking works across a
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Voicebox manages two categories of models:
|
TalkBox manages two categories of models:
|
||||||
|
|
||||||
**TTS Models** — Seven engines covering zero-shot cloning and preset voices. Each engine may have one or more size variants.
|
**TTS Models** — Seven engines covering zero-shot cloning and preset voices. Each engine may have one or more size variants.
|
||||||
|
|
||||||
@@ -51,9 +51,9 @@ Models live in the platform HuggingFace cache:
|
|||||||
| macOS | `~/.cache/huggingface/hub/` |
|
| macOS | `~/.cache/huggingface/hub/` |
|
||||||
| Linux | `~/.cache/huggingface/hub/` |
|
| Linux | `~/.cache/huggingface/hub/` |
|
||||||
| Windows | `%USERPROFILE%\.cache\huggingface\hub\` |
|
| Windows | `%USERPROFILE%\.cache\huggingface\hub\` |
|
||||||
| Docker | `/home/voicebox/.cache/huggingface/hub` (volume-mounted) |
|
| Docker | `/home/talkbox/.cache/huggingface/hub` (volume-mounted) |
|
||||||
|
|
||||||
Set `VOICEBOX_MODELS_DIR` to override.
|
Set `TALKBOX_MODELS_DIR` to override.
|
||||||
|
|
||||||
## Progress Tracking
|
## Progress Tracking
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Development Setup"
|
title: "Development Setup"
|
||||||
description: "Set up your local development environment for Voicebox"
|
description: "Set up your local development environment for TalkBox"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick Setup (Recommended)
|
## Quick Setup (Recommended)
|
||||||
@@ -9,8 +9,8 @@ Get started in two commands:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone and enter the repository
|
# Clone and enter the repository
|
||||||
git clone https://github.com/jamiepine/voicebox.git
|
git clone https://github.com/jamiepine/talkbox.git
|
||||||
cd voicebox
|
cd talkbox
|
||||||
|
|
||||||
# Setup everything (Python venv, JS deps, dev sidecar)
|
# Setup everything (Python venv, JS deps, dev sidecar)
|
||||||
just setup
|
just setup
|
||||||
@@ -86,7 +86,7 @@ Run `just --list` to see all available commands. Highlights:
|
|||||||
| `just build` | CPU server binary + Tauri installer |
|
| `just build` | CPU server binary + Tauri installer |
|
||||||
| `just build-local` | **Windows:** CPU + CUDA server binaries + Tauri installer |
|
| `just build-local` | **Windows:** CPU + CUDA server binaries + Tauri installer |
|
||||||
| `just build-server` | CPU server binary only |
|
| `just build-server` | CPU server binary only |
|
||||||
| `just build-server-cuda` | **Windows:** CUDA server binary only, placed in `%APPDATA%/sh.voicebox.app/backends/cuda` for local testing |
|
| `just build-server-cuda` | **Windows:** CUDA server binary only, placed in `%APPDATA%/sh.talkbox.app/backends/cuda` for local testing |
|
||||||
| `just build-tauri` | Tauri app only |
|
| `just build-tauri` | Tauri app only |
|
||||||
| `just build-web` | Web app only |
|
| `just build-web` | Web app only |
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ Run `just --list` to see all available commands. Highlights:
|
|||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `just generate-api` | Generate TypeScript API client from the backend's OpenAPI schema |
|
| `just generate-api` | Generate TypeScript API client from the backend's OpenAPI schema |
|
||||||
| `just docs` | Open `http://localhost:17493/docs` in your browser |
|
| `just docs` | Open `http://localhost:17494/docs` in your browser |
|
||||||
| `just logs` | Tail backend logs |
|
| `just logs` | Tail backend logs |
|
||||||
| `just clean` | Remove build artifacts |
|
| `just clean` | Remove build artifacts |
|
||||||
| `just clean-python` | Remove the Python venv + `__pycache__` |
|
| `just clean-python` | Remove the Python venv + `__pycache__` |
|
||||||
@@ -121,7 +121,7 @@ Run `just --list` to see all available commands. Highlights:
|
|||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
<Files>
|
<Files>
|
||||||
<Folder name="voicebox" defaultOpen>
|
<Folder name="talkbox" defaultOpen>
|
||||||
<Folder name="app">
|
<Folder name="app">
|
||||||
<Folder name="src">
|
<Folder name="src">
|
||||||
<File name="components/" />
|
<File name="components/" />
|
||||||
@@ -247,7 +247,7 @@ Start the backend:
|
|||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
uvicorn main:app --reload --port 17493
|
uvicorn main:app --reload --port 17494
|
||||||
```
|
```
|
||||||
|
|
||||||
In a new terminal, start the desktop app:
|
In a new terminal, start the desktop app:
|
||||||
@@ -280,7 +280,7 @@ bun run tauri dev
|
|||||||
- Check Python version (must be 3.11+)
|
- Check Python version (must be 3.11+)
|
||||||
- Ensure virtual environment is activated: `source backend/venv/bin/activate`
|
- Ensure virtual environment is activated: `source backend/venv/bin/activate`
|
||||||
- Verify all dependencies are installed: `pip install -r requirements.txt`
|
- Verify all dependencies are installed: `pip install -r requirements.txt`
|
||||||
- Check if port 17493 is available
|
- Check if port 17494 is available
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="Tauri build fails">
|
<Accordion title="Tauri build fails">
|
||||||
@@ -290,9 +290,9 @@ bun run tauri dev
|
|||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
||||||
<Accordion title="OpenAPI client generation fails">
|
<Accordion title="OpenAPI client generation fails">
|
||||||
- Ensure backend is running: `curl http://localhost:17493/openapi.json`
|
- Ensure backend is running: `curl http://localhost:17494/openapi.json`
|
||||||
- Check network connectivity
|
- Check network connectivity
|
||||||
- Verify the backend is accessible at localhost:17493
|
- Verify the backend is accessible at localhost:17494
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Stories & Timeline"
|
title: "Stories & Timeline"
|
||||||
description: "How the multi-voice timeline editor works in Voicebox"
|
description: "How the multi-voice timeline editor works in TalkBox"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
title: "Transcription"
|
title: "Transcription"
|
||||||
description: "How Whisper-based audio transcription works in Voicebox"
|
description: "How Whisper-based audio transcription works in TalkBox"
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Voicebox uses OpenAI's Whisper for automatic speech recognition (ASR). Transcription powers two flows:
|
TalkBox uses OpenAI's Whisper for automatic speech recognition (ASR). Transcription powers two flows:
|
||||||
|
|
||||||
1. **Reference-text auto-fill** — when a user records or uploads a voice sample, the backend transcribes it and populates the `reference_text` field so cloning can use it.
|
1. **Reference-text auto-fill** — when a user records or uploads a voice sample, the backend transcribes it and populates the `reference_text` field so cloning can use it.
|
||||||
2. **On-demand transcription** — a user-facing `/transcribe` endpoint for arbitrary audio.
|
2. **On-demand transcription** — a user-facing `/transcribe` endpoint for arbitrary audio.
|
||||||
@@ -83,7 +83,7 @@ Whisper expects mono 16 kHz audio. The audio utility in `backend/utils/audio.py`
|
|||||||
- **Formats:** WAV, MP3, FLAC, OGG, M4A (via soundfile / librosa)
|
- **Formats:** WAV, MP3, FLAC, OGG, M4A (via soundfile / librosa)
|
||||||
- **Target:** mono, 16 kHz, float32
|
- **Target:** mono, 16 kHz, float32
|
||||||
|
|
||||||
Files longer than Whisper's 30-second window are handled by the underlying library's chunking logic — no explicit splitting in Voicebox code.
|
Files longer than Whisper's 30-second window are handled by the underlying library's chunking logic — no explicit splitting in TalkBox code.
|
||||||
|
|
||||||
## API Endpoints
|
## API Endpoints
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user