commit 01e30656926c1a0bf8989c51f314c263eb4f01d5 Author: Jamie Pine Date: Sun Jan 25 02:19:06 2026 -0800 Initialize voicebox project with backend, frontend, and Tauri setup. Added configuration files, dependencies, and basic structure for components, hooks, and utilities. Included README and setup documentation for guidance. diff --git a/.biomeignore b/.biomeignore new file mode 100644 index 00000000..5e32ca67 --- /dev/null +++ b/.biomeignore @@ -0,0 +1,18 @@ +# Dependencies +node_modules +bun.lockb + +# Build outputs +dist +target +.tauri + +# Generated files +app/src/lib/api + +# Config files (don't lint/format) +*.config.js +*.config.ts + +# Tailwind CSS files (contains @tailwind directives) +**/index.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7cc8fbce --- /dev/null +++ b/.gitignore @@ -0,0 +1,60 @@ +# Dependencies +node_modules/ +bun.lockb +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +venv/ +env/ +ENV/ + +# Build outputs +dist/ +build/ +*.egg-info/ +*.egg +target/ +*.app +*.dmg +*.exe +*.msi +*.deb +*.AppImage + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Data (user-generated) +data/profiles/* +data/generations/* +data/projects/* +data/voicebox.db +!data/.gitkeep + +# Logs +*.log +logs/ + +# Environment +.env +.env.local + +# Generated files +app/src/lib/api/ +app/openapi.json +tauri/src-tauri/binaries/* + +# Temporary +tmp/ +temp/ +*.tmp diff --git a/README.md b/README.md new file mode 100644 index 00000000..eae6b2f0 --- /dev/null +++ b/README.md @@ -0,0 +1,409 @@ +# voicebox + +A production-quality desktop app for Qwen3-TTS voice cloning and generation. + +**Domain:** voicebox.sh + +--- + +## Vision + +Qwen3-TTS is a breakthrough model from Alibaba that achieves near-perfect voice cloning. The existing implementations (Voice-Clone-Studio, mimic, etc.) are either feature-rich but architecturally messy, or well-structured but limited in scope. + +voicebox aims to build the definitive Qwen3-TTS application by combining the best patterns from existing projects while avoiding their architectural mistakes. + +## Design Principles + +1. **Clean architecture from day one** - No monolithic files, proper separation of concerns +2. **Desktop-first experience** - Native feel via Tauri, not a web app in disguise +3. **Production code quality** - Type safety, modularity, maintainability +4. **Performance and UX** - Smart caching, async operations, responsive UI +5. **Extensible design** - Easy to add new models, effects, and features +6. **Flexible deployment** - Run backend locally or connect to remote GPU machine with one click + +## Technology Stack + +### Backend (Python) +- **FastAPI** - Async REST API +- **SQLAlchemy** - Database ORM with migrations +- **Pydantic** - Request/response validation +- **Qwen3-TTS** - Voice cloning model +- **Whisper** - Speech-to-text transcription +- **librosa + soundfile** - Audio processing + +### Frontend (Tauri + TypeScript) +- **Tauri** - Native desktop framework +- **React** - UI framework +- **TypeScript** - Type safety throughout +- **Bun** - Fast package manager and JavaScript runtime +- **React Query** - Server state management and API calls +- **OpenAPI (generated)** - Type-safe API client from FastAPI schema +- **Tailwind CSS** - Styling +- **Zustand** - Client-side state management +- **WaveSurfer.js** - Audio visualization + +### Database +- **SQLite** - Local storage +- **Alembic** - Schema migrations + +## Server/Client Mode + +voicebox supports flexible deployment for users with multiple machines: + +### Local Mode (Default) +- Backend runs locally alongside the Tauri app +- Best for users with GPU on their primary machine + +### Remote Mode (One-Click Setup) +- **Use case:** Your laptop doesn't have a GPU, but your desktop does +- **Server:** Run voicebox on GPU machine, click "Start Server" + - Starts FastAPI backend on local network + - Shows connection URL (e.g., `http://192.168.1.100:8000`) +- **Client:** Run voicebox on laptop, enter server URL + - Connects to remote backend + - Full UI functionality, inference happens on GPU machine +- **Security:** Local network only for now (no internet exposure) + +### How It Works +``` +┌─────────────────┐ ┌─────────────────┐ +│ Laptop │ │ Desktop │ +│ (Client) │ │ (Server) │ +│ │ │ │ +│ Tauri App ────────────────▶ FastAPI │ +│ React UI │ HTTP │ Qwen3-TTS │ +│ │ │ SQLite │ +│ │ │ CUDA/GPU │ +└─────────────────┘ └─────────────────┘ +``` + +**Benefits:** +- Use powerful GPU machine from lightweight laptop +- No complex setup - just click "Start Server" +- All data (history, profiles) lives on server +- Client is just a UI - no local storage needed in remote mode + +## Core Features + +### Phase 1 (MVP) +- Voice profile management +- Single-reference voice cloning +- Generation history with search +- Basic audio playback and preview +- Server/client mode (local network) +- One-click server startup + +### Phase 2 +- Multi-reference voice combination +- Batch variation generation +- Advanced audio normalization +- Export options and formats + +### Phase 3 +- Audio studio with timeline editing +- Word-level timestamps +- Project system (save/load sessions) +- Export options + +### Phase 4 +- Voice design (text-to-voice) +- Preset voices with style control +- Conversation mode (multi-speaker) +- Custom audio effects + +## Key Differentiators + +What makes voicebox better than existing implementations: + +1. **Clean codebase** - Modular architecture, no 2,000+ line files +2. **Type safety end-to-end** - OpenAPI-generated TypeScript client, Pydantic backend, React Query +3. **Smart caching** - Voice prompt caching for instant re-generation +4. **Desktop UX** - Native performance, keyboard shortcuts, native dialogs +5. **Server/client mode** - One-click remote GPU access from any device +6. **Multi-reference** - Combine voice samples for higher quality +7. **Audio studio** - Timeline-based editing with word-level precision +8. **Production patterns** - Cross-platform, graceful degradation, error recovery +9. **Database-backed** - Searchable history, project persistence +10. **Extensible** - Clean plugin system for models and features + +## Architecture Overview + +``` +voicebox/ +├── app/ # Shared React frontend (used by web & desktop) +│ ├── src/ +│ │ ├── components/ # React components +│ │ │ ├── VoiceProfiles/ +│ │ │ ├── Generation/ +│ │ │ ├── AudioStudio/ +│ │ │ ├── History/ +│ │ │ └── ServerSettings/ +│ │ ├── lib/ +│ │ │ ├── api/ # Generated OpenAPI client +│ │ │ ├── hooks/ # React Query hooks +│ │ │ └── utils/ +│ │ ├── types/ +│ │ └── App.tsx +│ ├── package.json +│ └── vite.config.ts +│ +├── tauri/ # Tauri desktop app (thin wrapper) +│ ├── src/ +│ │ └── main.tsx # Entry point, imports from ../app +│ ├── src-tauri/ # Rust backend +│ │ ├── src/ +│ │ │ └── main.rs # Sidecar management, IPC +│ │ ├── binaries/ # Bundled Python server +│ │ │ └── voicebox-server-{platform} +│ │ ├── Cargo.toml +│ │ └── tauri.conf.json +│ └── package.json +│ +├── web/ # Web deployment (thin wrapper) +│ ├── src/ +│ │ └── main.tsx # Entry point, imports from ../app +│ ├── package.json +│ └── vite.config.ts +│ +├── backend/ # Python FastAPI server +│ ├── main.py # FastAPI app + server mode +│ ├── models.py # Pydantic models +│ ├── tts.py # TTS inference +│ ├── transcribe.py # Whisper ASR +│ ├── profiles.py # Voice profiles +│ ├── history.py # Generation history +│ ├── studio.py # Audio editing +│ ├── database.py # SQLite ORM +│ ├── utils/ +│ │ ├── audio.py # Audio processing +│ │ ├── cache.py # Prompt caching +│ │ └── validation.py +│ ├── requirements.txt +│ └── build_binary.py # PyInstaller build script +│ +├── scripts/ +│ ├── build-server.sh # Build Python binary for all platforms +│ └── generate-api.sh # Generate OpenAPI client +│ +├── data/ # User data +│ ├── profiles/ +│ ├── generations/ +│ ├── projects/ +│ └── voicebox.db +│ +├── package.json # Root workspace config +└── docs/ + ├── ANALYSIS.md # Analysis of existing projects + ├── TAURI_PLAN.md # Tauri app structure and bundling strategy + └── ARCHITECTURE.md # Detailed architecture docs +``` + +**Key architectural decisions:** +- **Shared frontend** - `app/` contains all React code, used by both desktop and web +- **Thin wrappers** - `tauri/` and `web/` just configure build tools and entry points +- **Bundled backend** - Python server packaged as sidecar binary with PyInstaller +- **Type-safe API** - OpenAPI schema generated from FastAPI, TypeScript client auto-generated + +See [TAURI_PLAN.md](./docs/TAURI_PLAN.md) for detailed bundling strategy. + +## Lessons from Existing Projects + +voicebox learns from five existing Qwen3-TTS implementations: + +### voice (Rust CLI) +- ✅ Clean Rust/Python IPC pattern +- ✅ M3GAN voice effect +- ✅ Voice profile abstraction +- ❌ No concurrent requests +- ❌ No generation history + +### Voice-Clone-Studio +- ✅ Brilliant voice prompt caching +- ✅ Feature-rich (voice design, presets, conversations) +- ✅ VRAM-efficient model management +- ❌ 2,815-line single file +- ❌ Global state everywhere + +### Qwen3-TTS_server +- ✅ Clean modular structure +- ✅ FastAPI REST API design +- ✅ Health endpoint for monitoring +- ❌ No authentication or rate limiting +- ❌ No caching or streaming +- ❌ No OpenAPI client generation + +### mimic +- ✅ Excellent backend architecture (async, modular) +- ✅ Audio studio with timeline +- ✅ Database-backed history +- ✅ Multi-sample voice profiles +- ❌ 2,794-line app.js frontend +- ❌ Global state in UI + +### qwen3-tts-enhanced +- ✅ Multi-reference combination +- ✅ Cross-platform graceful degradation +- ✅ Audio validation +- ✅ Production error handling +- ❌ Still monolithic (1,892 lines) +- ❌ No API layer + +See [ANALYSIS.md](./docs/ANALYSIS.md) for detailed breakdown of each project. + +## Development Roadmap + +### Week 1: Foundation +- Project structure setup +- Backend skeleton (FastAPI + SQLite) +- OpenAPI schema generation +- Frontend skeleton (Tauri + React) +- TypeScript client generation from OpenAPI +- React Query setup +- Basic voice profile CRUD +- Server mode implementation +- Client connection UI + +### Week 2: Core Features +- TTS integration +- Voice cloning pipeline +- Voice prompt caching +- Generation history + +### Week 3: UX Polish +- Audio playback and preview +- Profile management UI +- History search and filters +- Error handling and validation + +### Week 4: Advanced Features +- Multi-reference combination +- Batch generation +- Audio normalization +- M3GAN effect + +### Week 5+: Studio Features +- Timeline editor +- Word-level timestamps +- Project system +- Export pipeline + +## Technical Decisions + +### Why Tauri over Electron? +- Smaller bundle size (Rust vs. Node.js) +- Better performance (native vs. V8) +- Lower memory usage +- Rust for system-level operations + +### Why FastAPI over Flask? +- Native async/await support +- Automatic OpenAPI schema generation +- Pydantic validation built-in +- Better performance + +### Why OpenAPI + React Query? +- **Type safety end-to-end** - FastAPI generates OpenAPI schema, we generate TypeScript client +- **No manual API code** - Client generated from `openapi.json` using openapi-typescript-codegen +- **Automatic caching** - React Query handles request deduplication and background refetching +- **Optimistic updates** - Update UI immediately, rollback on error +- **DevX** - Full autocomplete and type checking for all API calls + +**Example workflow:** +```bash +# Backend generates OpenAPI schema +python backend/main.py --openapi > openapi.json + +# Frontend generates TypeScript client +bun run generate-client + +# Use type-safe hooks in React +import { useQuery } from '@tanstack/react-query'; +import { ProfilesService } from '@/lib/api'; + +const { data: profiles } = useQuery({ + queryKey: ['profiles'], + queryFn: () => ProfilesService.listProfiles() +}); +``` + +### Why Bun over npm/yarn/pnpm? +- **Speed** - 20-30x faster than npm for install operations +- **Drop-in replacement** - Compatible with npm ecosystem, no migration needed +- **Built-in tooling** - Bundler, test runner, and package manager in one +- **Performance** - Faster script execution than Node.js +- **Developer experience** - Better error messages, workspaces support + +### Why SQLite over file-based storage? +- Full-text search +- Transactions and integrity +- Migrations via Alembic +- Easy to backup/restore + +### Why React over Vue/Svelte? +- Larger ecosystem +- Better TypeScript support +- Familiar to most developers +- Mature tooling + +### Why bundle Python server with PyInstaller? +- **No Python installation required** - Users don't need Python on their system +- **Consistent environment** - Exact dependencies bundled, no version conflicts +- **Single-click install** - One installer includes everything +- **Tauri sidecar pattern** - Rust spawns/manages Python process lifecycle +- **Platform-specific binaries** - PyInstaller creates native executables for each platform + +**Tradeoffs:** +- Larger bundle size (~500MB with models vs ~50MB without backend) +- Need separate build for each platform (macOS Intel/ARM, Windows, Linux) +- First launch slower (model loading time) + +**Alternative considered:** Require users to install Python and run `pip install` - rejected for poor UX + +### Why no Docker initially? +- Desktop app, not server deployment +- Users install locally +- Can add later for server mode + +## Performance Targets + +- **First generation:** < 10 seconds (cold start) +- **Cached generation:** < 2 seconds (warm start) +- **UI responsiveness:** 60 FPS at all times +- **Memory usage:** < 4GB VRAM for small models +- **Startup time:** < 3 seconds to UI +- **Database queries:** < 100ms for history search + +## Quality Standards + +- **No files over 500 lines** (except auto-generated) +- **Type hints on all Python functions** +- **TypeScript strict mode enabled** +- **OpenAPI client auto-generated from schema** +- **ESLint + Prettier for frontend** +- **Black + isort for backend** +- **All user-facing errors have context** +- **No global mutable state** +- **React Query for all server state** + +## Project Status + +**Current phase:** Planning and analysis + +**Documentation:** +- [ANALYSIS.md](./docs/ANALYSIS.md) - Comprehensive analysis of existing implementations +- [TAURI_PLAN.md](./docs/TAURI_PLAN.md) - Tauri app architecture and Python server bundling strategy + +## License + +TBD + +## Credits + +Built by analyzing and learning from: +- voice (Rust CLI) +- Voice-Clone-Studio +- Qwen3-TTS_server +- mimic +- qwen3-tts-enhanced + +Powered by Alibaba's Qwen3-TTS model. diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 00000000..b99b6580 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,207 @@ +# voicebox Setup Guide + +Quick start guide for setting up the voicebox development environment. + +## Prerequisites + +- **Bun** - Fast JavaScript runtime and package manager + ```bash + curl -fsSL https://bun.sh/install | bash + ``` + +- **Python 3.11+** - For backend development + ```bash + python --version # Should be 3.11 or higher + ``` + +- **Rust** - For Tauri desktop app (installed automatically by Tauri CLI) + ```bash + rustc --version # Check if installed + ``` + +- **Node.js 18+** (optional) - Fallback if Bun is not available + +## Initial Setup + +### 1. Install Dependencies + +```bash +# Install all workspace dependencies +bun install +``` + +This will install dependencies for: +- `app/` - Shared React frontend +- `tauri/` - Tauri desktop wrapper +- `web/` - Web deployment wrapper + +### 2. Setup Backend + +```bash +cd backend + +# Create virtual environment +python -m venv venv + +# Activate virtual environment +source venv/bin/activate # On macOS/Linux +# or +venv\Scripts\activate # On Windows + +# Install Python dependencies +pip install -r requirements.txt +``` + +### 3. Initialize Database + +```bash +cd backend +python -c "from database import init_db; init_db()" +``` + +This creates the SQLite database at `data/voicebox.db`. + +### 4. Install Qwen3-TTS (Optional) + +The Qwen3-TTS models are automatically downloaded from HuggingFace Hub on first use. However, you need to install the `qwen_tts` package: + +```bash +pip install git+https://github.com/QwenLM/Qwen3-TTS.git +``` + +**Note:** Models (~2-4GB) will be automatically downloaded on first generation. This may take a few minutes depending on your internet connection. + +## Development + +### Start Backend Server + +```bash +cd backend +source venv/bin/activate # Activate venv if not already active +uvicorn main:app --reload --port 8000 +``` + +Backend will be available at `http://localhost:8000` + +### Start Tauri Desktop App + +```bash +# From project root +bun run dev +``` + +Or manually: +```bash +cd tauri +bun run tauri dev +``` + +This will: +1. Start Vite dev server on port 5173 +2. Launch Tauri window pointing to localhost:5173 +3. Enable hot reload + +### Start Web App + +```bash +# From project root +bun run dev:web +``` + +Or manually: +```bash +cd web +bun run dev +``` + +Web app will be available at `http://localhost:5174` (or next available port) + +## Building + +### Build Python Server Binary + +```bash +./scripts/build-server.sh +``` + +This creates a platform-specific binary in `tauri/src-tauri/binaries/` + +### Build Tauri Desktop App + +```bash +cd tauri +bun run tauri build +``` + +Creates platform-specific installers: +- macOS: `.app`, `.dmg` +- Windows: `.exe`, `.msi` +- Linux: `.deb`, `.AppImage` + +### Build Web App + +```bash +cd web +bun run build +``` + +Output in `web/dist/` + +## Generate OpenAPI Client + +After starting the backend server: + +```bash +./scripts/generate-api.sh +``` + +This will: +1. Download OpenAPI schema from backend +2. Generate TypeScript client in `app/src/lib/api/` + +## Project Structure + +``` +voicebox/ +├── app/ # Shared React frontend +├── tauri/ # Tauri desktop wrapper +├── web/ # Web deployment wrapper +├── backend/ # Python FastAPI server +├── scripts/ # Build and utility scripts +├── data/ # User data (gitignored) +└── docs/ # Documentation +``` + +## Troubleshooting + +### Backend won't start +- Check Python version: `python --version` (needs 3.11+) +- Ensure virtual environment is activated +- Install dependencies: `pip install -r requirements.txt` + +### Tauri build fails +- Ensure Rust is installed: `rustc --version` +- Install Tauri CLI: `bunx @tauri-apps/cli install` +- Check `tauri/src-tauri/Cargo.toml` for correct dependencies + +### OpenAPI client generation fails +- Ensure backend is running on port 8000 +- Check `curl http://localhost:8000/openapi.json` returns valid JSON +- Install openapi-typescript-codegen: `bun add -d openapi-typescript-codegen` + +## Model Downloads + +Models are automatically downloaded from HuggingFace Hub on first use: +- **Whisper** (transcription): Auto-downloads on first transcription +- **Qwen3-TTS** (voice cloning): Auto-downloads on first generation + +First-time usage will be slower due to model downloads, but subsequent runs will use cached models. + +## Next Steps + +1. ✅ TTS model loading implemented in `backend/tts.py` +2. ✅ API routes implemented in `backend/main.py` +3. Build React components in `app/src/components/` +4. Connect frontend to backend via generated API client + +See [README.md](./README.md) for architecture details and [docs/](./docs/) for detailed documentation. diff --git a/app/components.json b/app/components.json new file mode 100644 index 00000000..ba9305d0 --- /dev/null +++ b/app/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/lib/hooks" + } +} diff --git a/app/index.html b/app/index.html new file mode 100644 index 00000000..1ae976ad --- /dev/null +++ b/app/index.html @@ -0,0 +1,13 @@ + + + + + + + voicebox + + +
+ + + diff --git a/app/package.json b/app/package.json new file mode 100644 index 00000000..b18c6ef6 --- /dev/null +++ b/app/package.json @@ -0,0 +1,54 @@ +{ + "name": "@voicebox/app", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "biome lint src", + "lint:fix": "biome lint --write src", + "format": "biome format --write src", + "check": "biome check --write src" + }, + "dependencies": { + "react": "^18.3.0", + "react-dom": "^18.3.0", + "@tanstack/react-query": "^5.0.0", + "@tanstack/react-query-devtools": "^5.0.0", + "zustand": "^4.5.0", + "react-hook-form": "^7.53.0", + "@hookform/resolvers": "^3.9.0", + "zod": "^3.23.8", + "wavesurfer.js": "^7.0.0", + "lucide-react": "^0.454.0", + "date-fns": "^3.6.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "tailwind-merge": "^2.5.4", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.0", + "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-scroll-area": "^1.1.0", + "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-alert-dialog": "^1.1.1" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.18", + "@types/react": "^18.3.0", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.0", + "tailwindcss": "^3.4.0", + "tailwindcss-animate": "^1.0.7", + "typescript": "^5.6.0", + "vite": "^5.4.0" + } +} diff --git a/app/postcss.config.js b/app/postcss.config.js new file mode 100644 index 00000000..2aa7205d --- /dev/null +++ b/app/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/app/src/App.tsx b/app/src/App.tsx new file mode 100644 index 00000000..69bdd277 --- /dev/null +++ b/app/src/App.tsx @@ -0,0 +1,67 @@ +import { History, Mic, Settings, Sparkles } from 'lucide-react'; +import { GenerationForm } from '@/components/Generation/GenerationForm'; +import { HistoryTable } from '@/components/History/HistoryTable'; +import { ConnectionForm } from '@/components/ServerSettings/ConnectionForm'; +import { ServerStatus } from '@/components/ServerSettings/ServerStatus'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Toaster } from '@/components/ui/toaster'; +import { ProfileList } from '@/components/VoiceProfiles/ProfileList'; + +function App() { + return ( +
+
+
+

voicebox

+

+ Production-quality Qwen3-TTS voice cloning and generation +

+
+ + + + + + Profiles + + + + Generate + + + + History + + + + Settings + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+ + +
+ ); +} + +export default App; diff --git a/app/src/components/AudioStudio/.gitkeep b/app/src/components/AudioStudio/.gitkeep new file mode 100644 index 00000000..fbf3ff01 --- /dev/null +++ b/app/src/components/AudioStudio/.gitkeep @@ -0,0 +1 @@ +# Audio studio timeline editing components diff --git a/app/src/components/Generation/.gitkeep b/app/src/components/Generation/.gitkeep new file mode 100644 index 00000000..18bc6d98 --- /dev/null +++ b/app/src/components/Generation/.gitkeep @@ -0,0 +1 @@ +# Voice generation components diff --git a/app/src/components/Generation/GenerationForm.tsx b/app/src/components/Generation/GenerationForm.tsx new file mode 100644 index 00000000..af29dcb9 --- /dev/null +++ b/app/src/components/Generation/GenerationForm.tsx @@ -0,0 +1,190 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { Loader2 } from 'lucide-react'; +import { useForm } from 'react-hook-form'; +import * as z from 'zod'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Textarea } from '@/components/ui/textarea'; +import { useToast } from '@/components/ui/use-toast'; +import { useGeneration } from '@/lib/hooks/useGeneration'; +import { useProfiles } from '@/lib/hooks/useProfiles'; + +const generationSchema = z.object({ + profileId: z.string().min(1, 'Please select a voice profile'), + text: z.string().min(1, 'Text is required').max(5000), + language: z.enum(['en', 'zh']), + seed: z.number().int().optional(), +}); + +type GenerationFormValues = z.infer; + +export function GenerationForm() { + const { data: profiles } = useProfiles(); + const generation = useGeneration(); + const { toast } = useToast(); + + const form = useForm({ + resolver: zodResolver(generationSchema), + defaultValues: { + profileId: '', + text: '', + language: 'en', + seed: undefined, + }, + }); + + async function onSubmit(data: GenerationFormValues) { + try { + const result = await generation.mutateAsync({ + profile_id: data.profileId, + text: data.text, + language: data.language, + seed: data.seed, + }); + + toast({ + title: 'Generation complete!', + description: `Audio generated (${result.duration.toFixed(2)}s)`, + }); + + form.reset(); + } catch (error) { + toast({ + title: 'Generation failed', + description: error instanceof Error ? error.message : 'Failed to generate audio', + variant: 'destructive', + }); + } + } + + return ( + + + Generate Speech + + +
+ + ( + + Voice Profile + + + + )} + /> + + ( + + Text to Speak + +