Compare commits

..
4 Commits
Author SHA1 Message Date
Jamie Pine ac68052945 create stub resource files when actool output is missing
Older Xcode versions don't produce Assets.car from .icon assets.
Fall back to empty stubs for all platforms so the bundler succeeds.
2026-03-17 01:07:54 -07:00
Jamie Pine e601fd2ca4 generate icon assets at build time instead of tracking them
build.rs now generates voicebox.icns via sips + iconutil alongside the
existing actool Assets.car compilation. On non-macOS, empty stub files
are created so Tauri's resource bundler doesn't fail on missing paths.
2026-03-17 00:51:12 -07:00
Jamie Pine 7b25e0ba0b Bump version: 0.2.3 → 0.3.0 2026-03-17 00:25:56 -07:00
Jamie PineandGitHub a6817cd082 Merge pull request #295 from jamiepine/fix/misc-bugs
fix: batch of bug fixes from issue tracker
2026-03-17 00:08:17 -07:00
12 changed files with 121 additions and 15 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.2.3
current_version = 0.3.0
commit = True
tag = True
tag_name = v{new_version}
+1
View File
@@ -51,6 +51,7 @@ app/openapi.json
tauri/src-tauri/binaries/*
tauri/src-tauri/gen/Assets.car
tauri/src-tauri/gen/voicebox.icns
tauri/src-tauri/gen/partial.plist
# Temporary
tmp/
+29 -2
View File
@@ -7,9 +7,25 @@
## [Unreleased]
This release rewrites the backend into a modular architecture, migrates the documentation site to Fumadocs, and ships a batch of bug fixes and UI polish across the stack.
## [0.3.0] - 2026-03-17
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, model loading status is now visible in the UI, effects presets get a dropdown, and several race conditions and accessibility gaps are closed.
This release rewrites the backend into a modular architecture, overhauls the settings UI into routed sub-pages, fixes audio player freezing, migrates documentation to Fumadocs, and ships a batch of bug fixes targeting the most-reported issues from the tracker.
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))
- Split settings into routed sub-tabs: General, Generation, GPU, Logs, Changelog, About
- Added live server log viewer with auto-scroll
- Added in-app changelog page that parses `CHANGELOG.md` at build time
- Added About page with version info, license, and generation folder quick-open
- Extracted reusable `SettingRow` component for consistent setting layouts
### Audio Player Fix ([#293](https://github.com/jamiepine/voicebox/pull/293))
- Fixed audio player freezing during playback
- Improved playback UX with better state management and listener cleanup
- Fixed restart race condition during regeneration
- Added stable keys for audio element re-rendering
- Improved accessibility across player controls
### Backend Refactor ([#285](https://github.com/jamiepine/voicebox/pull/285))
- Extracted all routes from `main.py` into 13 domain routers under `backend/routes/` — `main.py` dropped from ~3,100 lines to ~10
@@ -40,6 +56,17 @@ The backend's 3,000-line monolith `main.py` has been decomposed into domain rout
- Softened select focus indicator opacity
- Addressed 4 critical and 12 major issues from CodeRabbit review
### Bug Fixes ([#295](https://github.com/jamiepine/voicebox/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 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 error toasts showing `[object Object]` instead of the actual error message ([#290](https://github.com/jamiepine/voicebox/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))
- Upgraded CUDA backend build from cu121 to cu126 for RTX 50-series (Blackwell) GPU support ([#289](https://github.com/jamiepine/voicebox/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))
- Fixed Docker build failure from pip hash mismatch on Qwen3-TTS dependencies ([#286](https://github.com/jamiepine/voicebox/issues/286))
- 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
### Platform Fixes
- Replaced `netstat` with `TcpStream` + PowerShell for Windows port detection ([#277](https://github.com/jamiepine/voicebox/pull/277))
- Fixed Docker frontend build and cleaned up Docker docs
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@voicebox/app",
"version": "0.2.3",
"version": "0.3.0",
"private": true,
"type": "module",
"scripts": {
+1 -1
View File
@@ -1,3 +1,3 @@
# Backend package
__version__ = "0.2.3"
__version__ = "0.3.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@voicebox/landing",
"version": "0.2.3",
"version": "0.3.0",
"description": "Landing page for voicebox.sh",
"scripts": {
"dev": "bun --bun next dev --turbo",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "voicebox",
"version": "0.2.3",
"version": "0.3.0",
"private": true,
"workspaces": [
"app",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@voicebox/tauri",
"private": true,
"version": "0.2.3",
"version": "0.3.0",
"type": "module",
"scripts": {
"dev": "vite",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "voicebox"
version = "0.2.3"
version = "0.3.0"
description = "A production-quality desktop app for Qwen3-TTS voice cloning and generation"
authors = ["you"]
license = ""
+82 -4
View File
@@ -23,15 +23,15 @@ fn main() {
}
}
let project_root = env!("CARGO_MANIFEST_DIR");
let gen_dir = format!("{}/gen", project_root);
std::fs::create_dir_all(&gen_dir).expect("Failed to create gen directory");
// Compile macOS Liquid Glass icon
#[cfg(target_os = "macos")]
{
let project_root = env!("CARGO_MANIFEST_DIR");
// voicebox.icon is in tauri/assets/voicebox.icon (one level up from src-tauri)
let icon_source = format!("{}/../assets/voicebox.icon", project_root);
let gen_dir = format!("{}/gen", project_root);
std::fs::create_dir_all(&gen_dir).expect("Failed to create gen directory");
if std::path::Path::new(&icon_source).exists() {
println!("cargo:rerun-if-changed={}", icon_source);
@@ -76,6 +76,71 @@ fn main() {
panic!("Icon compilation failed");
}
}
// Generate voicebox.icns from the source PNG via sips + iconutil
let icns_path = format!("{}/voicebox.icns", gen_dir);
if !std::path::Path::new(&icns_path).exists() {
let source_png = format!("{}/Assets/Voicebox.png", icon_source);
if std::path::Path::new(&source_png).exists() {
let iconset_dir = format!("{}/voicebox.iconset", gen_dir);
std::fs::create_dir_all(&iconset_dir).ok();
let sizes: &[(u32, &str)] = &[
(16, "icon_16x16.png"),
(32, "[email protected]"),
(32, "icon_32x32.png"),
(64, "[email protected]"),
(128, "icon_128x128.png"),
(256, "[email protected]"),
(256, "icon_256x256.png"),
(512, "[email protected]"),
(512, "icon_512x512.png"),
(1024, "[email protected]"),
];
for (size, name) in sizes {
let dest = format!("{}/{}", iconset_dir, name);
let status = Command::new("sips")
.args([
"-z",
&size.to_string(),
&size.to_string(),
&source_png,
"--out",
&dest,
])
.output();
if let Ok(out) = status {
if !out.status.success() {
eprintln!(
"sips failed for {}: {}",
name,
String::from_utf8_lossy(&out.stderr)
);
}
}
}
let iconutil_output = Command::new("iconutil")
.args(["-c", "icns", "-o", &icns_path, &iconset_dir])
.output();
match iconutil_output {
Ok(out) if out.status.success() => {
println!("Generated voicebox.icns");
}
Ok(out) => {
eprintln!("iconutil failed: {}", String::from_utf8_lossy(&out.stderr));
}
Err(e) => {
eprintln!("Failed to run iconutil: {}", e);
}
}
// Clean up iconset directory
std::fs::remove_dir_all(&iconset_dir).ok();
}
}
} else {
println!(
"cargo:warning=Icon source not found at {}, skipping icon compilation",
@@ -84,5 +149,18 @@ fn main() {
}
}
// Ensure all resource files exist so Tauri's bundler doesn't fail.
// On non-macOS these are always stubs. On macOS, actool may not produce
// Assets.car if the Xcode version doesn't support the .icon format.
{
let required = ["Assets.car", "voicebox.icns", "partial.plist"];
for name in required {
let path = format!("{}/{}", gen_dir, name);
if !std::path::Path::new(&path).exists() {
std::fs::write(&path, b"").ok();
}
}
}
tauri_build::build()
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Voicebox",
"version": "0.2.3",
"version": "0.3.0",
"identifier": "sh.voicebox.app",
"build": {
"beforeDevCommand": "bun run dev",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@voicebox/web",
"private": true,
"version": "0.2.3",
"version": "0.3.0",
"type": "module",
"scripts": {
"dev": "vite",