diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9056c239..00bcf160 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,50 @@
## [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.
+
+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.
+
+### 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
+- 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
+- Added `backend/STYLE_GUIDE.md` and `pyproject.toml` with ruff linting config
+- Removed dead code: unused `_get_cuda_dll_excludes`, stale `studio.py`, `example_usage.py`, old `Makefile`
+- Deduplicated shared logic across TTS backends into `backends/base.py`
+- Improved startup logging with version, platform, data directory, and database stats
+- Fixed startup database session leak — sessions now rollback and close in `finally` block
+- Isolated shutdown unload calls so one backend failure doesn't block the others
+- Handled null duration in `story_items` migration
+- Reject model migration when target is a subdirectory of source cache
+
+### Documentation Rewrite ([#288](https://github.com/jamiepine/voicebox/pull/288))
+- Migrated docs site from Mintlify to Fumadocs (Next.js-based)
+- Rewrote introduction and root page with content from README
+- Added "Edit on GitHub" links and last-updated timestamps on all pages
+- Generated OpenAPI spec and auto-generated API reference pages
+- Removed stale planning docs (`CUDA_BACKEND_SWAP`, `EXTERNAL_PROVIDERS`, `MLX_AUDIO`, `TTS_PROVIDER_ARCHITECTURE`, etc.)
+- Sidebar groups now expand by default; root redirects to `/docs`
+- Added OG image metadata and `/og` preview page
+
+### UI & Frontend
+- Added model loading status indicator and effects preset dropdown ([3187344](https://github.com/jamiepine/voicebox/commit/3187344))
+- Fixed take-label race condition during regeneration
+- Added accessible focus styling to select component
+- Softened select focus indicator opacity
+- Addressed 4 critical and 12 major issues from CodeRabbit review
+
+### 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
+- Fixed macOS download links to use `.dmg` instead of `.app.tar.gz`
+- Added dynamic download redirect routes to landing site
+
+### Release Tooling
+- Added `draft-release-notes` and `release-bump` agent skills
+- Wired CI release workflow to extract notes from `CHANGELOG.md` for GitHub Releases
+- Backfilled changelog with all historical releases
+
## [0.2.3] - 2026-03-15
The "it works in dev but not in prod" release. This version fixes a series of PyInstaller bundling issues that prevented model downloading, loading, generation, and progress tracking from working in production builds.
diff --git a/app/plugins/changelog.ts b/app/plugins/changelog.ts
new file mode 100644
index 00000000..d131fe53
--- /dev/null
+++ b/app/plugins/changelog.ts
@@ -0,0 +1,23 @@
+import { readFileSync } from 'node:fs';
+import path from 'node:path';
+import type { Plugin } from 'vite';
+
+/** Vite plugin that exposes CHANGELOG.md as `virtual:changelog`. */
+export function changelogPlugin(repoRoot: string): Plugin {
+ const virtualId = 'virtual:changelog';
+ const resolvedId = '\0' + virtualId;
+ const changelogPath = path.resolve(repoRoot, 'CHANGELOG.md');
+
+ return {
+ name: 'changelog',
+ resolveId(id) {
+ if (id === virtualId) return resolvedId;
+ },
+ load(id) {
+ if (id === resolvedId) {
+ const raw = readFileSync(changelogPath, 'utf-8');
+ return `export default ${JSON.stringify(raw)};`;
+ }
+ },
+ };
+}
diff --git a/app/src/App.tsx b/app/src/App.tsx
index 458686c8..b6964db1 100644
--- a/app/src/App.tsx
+++ b/app/src/App.tsx
@@ -8,6 +8,7 @@ import { TOP_SAFE_AREA_PADDING } from '@/lib/constants/ui';
import { cn } from '@/lib/utils/cn';
import { usePlatform } from '@/platform/PlatformContext';
import { router } from '@/router';
+import { useLogStore } from '@/stores/logStore';
import { useServerStore } from '@/stores/serverStore';
const LOADING_MESSAGES = [
@@ -63,6 +64,14 @@ function App() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [platform.lifecycle]);
+ // Subscribe to server logs
+ useEffect(() => {
+ const unsubscribe = platform.lifecycle.subscribeToServerLogs((entry) => {
+ useLogStore.getState().addEntry(entry);
+ });
+ return unsubscribe;
+ }, [platform.lifecycle]);
+
// Setup window close handler and auto-start server when running in Tauri (production only)
useEffect(() => {
if (!platform.metadata.isTauri) {
diff --git a/app/src/components/History/HistoryTable.tsx b/app/src/components/History/HistoryTable.tsx
index b37817df..e7b87e0c 100644
--- a/app/src/components/History/HistoryTable.tsx
+++ b/app/src/components/History/HistoryTable.tsx
@@ -15,7 +15,7 @@ import {
Wand2,
} from 'lucide-react';
import { useEffect, useRef, useState } from 'react';
-import Loader from 'react-loaders';
+
import { EffectsChainEditor } from '@/components/Effects/EffectsChainEditor';
import { Button } from '@/components/ui/button';
import {
@@ -56,8 +56,35 @@ import { formatDate, formatDuration, formatEngineName } from '@/lib/utils/format
import { useGenerationStore } from '@/stores/generationStore';
import { usePlayerStore } from '@/stores/playerStore';
-// OLD TABLE-BASED COMPONENT - REMOVED (can be found in git history)
-// This is the new alternate history view with fixed height rows
+// ─── Audio Bars ─────────────────────────────────────────────────────────────
+
+function AudioBars({ mode }: { mode: 'idle' | 'generating' | 'playing' }) {
+ const barColor = mode !== 'idle' ? 'bg-accent' : 'bg-muted-foreground/40';
+ return (
+
+ {[0, 1, 2, 3, 4].map((i) => (
+
+ ))}
+
+ );
+}
// NEW ALTERNATE HISTORY VIEW - FIXED HEIGHT ROWS WITH INFINITE SCROLL
export function HistoryTable() {
@@ -446,12 +473,9 @@ export function HistoryTable() {
>
{/* Status icon */}
-
-
-
+
{/* Left side - Meta information */}
diff --git a/app/src/components/ServerTab/AboutPage.tsx b/app/src/components/ServerTab/AboutPage.tsx
new file mode 100644
index 00000000..29c27814
--- /dev/null
+++ b/app/src/components/ServerTab/AboutPage.tsx
@@ -0,0 +1,135 @@
+import { ArrowUpRight } from 'lucide-react';
+import type { CSSProperties, ReactNode } from 'react';
+import { useEffect, useState } from 'react';
+import voiceboxLogo from '@/assets/voicebox-logo.png';
+import { usePlatform } from '@/platform/PlatformContext';
+
+function FadeIn({ delay = 0, children }: { delay?: number; children: ReactNode }) {
+ return (
+
+ The open-source voice synthesis studio. Clone voices, generate speech, apply effects,
+ and build voice-powered apps — all running locally on your machine.
+
+
+
+ {/* CUDA section — only when no native GPU and not already on CUDA */}
+ {!hasNativeGpu && !isCurrentlyCuda && (
+
+ {/* Download progress */}
+ {cudaDownloading && downloadProgress && (
+
+
+ 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 and Linux with
+ NVIDIA GPUs, you can download an optional CUDA backend for hardware-accelerated inference.
+ AMD ROCm, 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.
+