From 64dd29d35a9b6bf03bf1175ef4c543c9208c14bd Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Fri, 30 Jan 2026 23:32:45 -0800 Subject: [PATCH 01/11] Add initial setup for Fumadocs documentation migration - Created new directory structure for documentation under `/docs2`. - Added `.gitignore` to exclude build artifacts and dependencies. - Introduced `package.json`, `next.config.mjs`, and `postcss.config.mjs` for project configuration. - Implemented MDX components in `mdx-components.tsx` for rendering documentation. - Migrated existing documentation content and created new files for auto-updater and other features. - Established compatibility layer for Mintlify components in `mintlify-compat.tsx`. - Set up OpenAPI documentation in `openapi.json`. - Updated README and migration guide to reflect new structure and usage instructions. - Ensured all components and pages are ready for development and deployment with Fumadocs. --- docs2/.gitignore | 26 + docs2/MIGRATION.md | 87 ++ docs2/README.md | 45 + docs2/app/(home)/layout.tsx | 6 + docs2/app/(home)/page.tsx | 16 + docs2/app/api/search/route.ts | 7 + docs2/app/docs/[[...slug]]/page.tsx | 55 ++ docs2/app/docs/layout.tsx | 11 + docs2/app/global.css | 4 + docs2/app/layout.tsx | 17 + docs2/app/llms-full.txt/route.ts | 10 + docs2/app/og/docs/[...slug]/route.tsx | 36 + docs2/bun.lock | 829 ++++++++++++++++++ docs2/components/api-page.client.tsx | 6 + docs2/components/api-page.tsx | 7 + docs2/components/mintlify-compat.tsx | 124 +++ docs2/content/docs/AUTOUPDATER.md | 195 ++++ docs2/content/docs/AUTOUPDATER_QUICKSTART.md | 119 +++ docs2/content/docs/README.md | 67 ++ docs2/content/docs/TROUBLESHOOTING.md | 311 +++++++ docs2/content/docs/api-reference/meta.json | 4 + ...ple_profiles__profile_id__samples_post.mdx | 16 + .../unknown/create_profile_profiles_post.mdx | 16 + ...eration_history__generation_id__delete.mdx | 16 + ...e_profile_profiles__profile_id__delete.mdx | 16 + ...le_profiles_samples__sample_id__delete.mdx | 16 + .../unknown/generate_speech_generate_post.mdx | 16 + .../get_audio_audio__generation_id__get.mdx | 16 + ...generation_history__generation_id__get.mdx | 16 + ...gress_models_progress__model_name__get.mdx | 16 + .../get_model_status_models_status_get.mdx | 16 + .../get_profile_profiles__profile_id__get.mdx | 16 + ...ples_profiles__profile_id__samples_get.mdx | 16 + .../unknown/get_stats_history_stats_get.mdx | 16 + .../unknown/health_health_get.mdx | 16 + .../unknown/list_history_history_get.mdx | 16 + .../unknown/list_profiles_profiles_get.mdx | 16 + .../unknown/load_model_models_load_post.mdx | 16 + .../docs/api-reference/unknown/meta.json | 27 + .../docs/api-reference/unknown/root__get.mdx | 16 + .../transcribe_audio_transcribe_post.mdx | 16 + ...er_model_download_models_download_post.mdx | 16 + .../unload_model_models_unload_post.mdx | 16 + ...date_profile_profiles__profile_id__put.mdx | 16 + docs2/content/docs/api/authentication.mdx | 55 ++ docs2/content/docs/api/generation.mdx | 119 +++ docs2/content/docs/api/meta.json | 10 + docs2/content/docs/api/overview.mdx | 219 +++++ docs2/content/docs/api/recordings.mdx | 95 ++ docs2/content/docs/api/voice-profiles.mdx | 149 ++++ docs2/content/docs/developer/architecture.mdx | 206 +++++ .../content/docs/developer/audio-channels.mdx | 310 +++++++ docs2/content/docs/developer/autoupdater.mdx | 84 ++ docs2/content/docs/developer/building.mdx | 270 ++++++ docs2/content/docs/developer/contributing.mdx | 326 +++++++ docs2/content/docs/developer/history.mdx | 260 ++++++ docs2/content/docs/developer/meta.json | 17 + .../docs/developer/model-management.mdx | 341 +++++++ docs2/content/docs/developer/setup.mdx | 239 +++++ docs2/content/docs/developer/stories.mdx | 320 +++++++ .../content/docs/developer/transcription.mdx | 299 +++++++ .../content/docs/developer/tts-generation.mdx | 283 ++++++ .../content/docs/developer/voice-profiles.mdx | 202 +++++ docs2/content/docs/index.mdx | 50 ++ docs2/content/docs/meta.json | 10 + .../docs/overview/building-stories.mdx | 37 + .../docs/overview/creating-voice-profiles.mdx | 296 +++++++ .../docs/overview/generating-speech.mdx | 65 ++ .../docs/overview/generation-history.mdx | 88 ++ docs2/content/docs/overview/installation.mdx | 119 +++ docs2/content/docs/overview/introduction.mdx | 58 ++ docs2/content/docs/overview/meta.json | 17 + docs2/content/docs/overview/quick-start.mdx | 154 ++++ .../docs/overview/recording-transcription.mdx | 64 ++ docs2/content/docs/overview/remote-mode.mdx | 138 +++ .../content/docs/overview/stories-editor.mdx | 64 ++ .../content/docs/overview/troubleshooting.mdx | 477 ++++++++++ docs2/content/docs/overview/voice-cloning.mdx | 75 ++ docs2/content/docs/plans/DOCKER_DEPLOYMENT.md | 761 ++++++++++++++++ .../content/docs/plans/EXTERNAL_PROVIDERS.md | 438 +++++++++ docs2/content/docs/plans/MLX_AUDIO.md | 399 +++++++++ docs2/content/docs/plans/OPENAI_SUPPORT.md | 238 +++++ docs2/content/docs/plans/meta.json | 9 + docs2/lib/layout.shared.tsx | 9 + docs2/lib/openapi.ts | 5 + docs2/lib/source.ts | 27 + docs2/mdx-components.tsx | 39 + docs2/next.config.mjs | 17 + docs2/openapi.json | 1 + docs2/package.json | 32 + docs2/postcss.config.mjs | 5 + docs2/public/images/app-screenshot-1.webp | Bin 0 -> 136754 bytes docs2/public/images/app-screenshot-2.webp | Bin 0 -> 131774 bytes docs2/public/images/app-screenshot-3.webp | Bin 0 -> 110446 bytes docs2/public/logo/icon-dark.png | Bin 0 -> 10562 bytes docs2/public/logo/icon-light.png | Bin 0 -> 10562 bytes docs2/scripts/generate-openapi.ts | 10 + docs2/source.config.ts | 27 + docs2/tsconfig.json | 46 + 99 files changed, 9940 insertions(+) create mode 100644 docs2/.gitignore create mode 100644 docs2/MIGRATION.md create mode 100644 docs2/README.md create mode 100644 docs2/app/(home)/layout.tsx create mode 100644 docs2/app/(home)/page.tsx create mode 100644 docs2/app/api/search/route.ts create mode 100644 docs2/app/docs/[[...slug]]/page.tsx create mode 100644 docs2/app/docs/layout.tsx create mode 100644 docs2/app/global.css create mode 100644 docs2/app/layout.tsx create mode 100644 docs2/app/llms-full.txt/route.ts create mode 100644 docs2/app/og/docs/[...slug]/route.tsx create mode 100644 docs2/bun.lock create mode 100644 docs2/components/api-page.client.tsx create mode 100644 docs2/components/api-page.tsx create mode 100644 docs2/components/mintlify-compat.tsx create mode 100644 docs2/content/docs/AUTOUPDATER.md create mode 100644 docs2/content/docs/AUTOUPDATER_QUICKSTART.md create mode 100644 docs2/content/docs/README.md create mode 100644 docs2/content/docs/TROUBLESHOOTING.md create mode 100644 docs2/content/docs/api-reference/meta.json create mode 100644 docs2/content/docs/api-reference/unknown/add_profile_sample_profiles__profile_id__samples_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/create_profile_profiles_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/delete_generation_history__generation_id__delete.mdx create mode 100644 docs2/content/docs/api-reference/unknown/delete_profile_profiles__profile_id__delete.mdx create mode 100644 docs2/content/docs/api-reference/unknown/delete_profile_sample_profiles_samples__sample_id__delete.mdx create mode 100644 docs2/content/docs/api-reference/unknown/generate_speech_generate_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_audio_audio__generation_id__get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_generation_history__generation_id__get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_model_progress_models_progress__model_name__get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_model_status_models_status_get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_profile_profiles__profile_id__get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_profile_samples_profiles__profile_id__samples_get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/get_stats_history_stats_get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/health_health_get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/list_history_history_get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/list_profiles_profiles_get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/load_model_models_load_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/meta.json create mode 100644 docs2/content/docs/api-reference/unknown/root__get.mdx create mode 100644 docs2/content/docs/api-reference/unknown/transcribe_audio_transcribe_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/trigger_model_download_models_download_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/unload_model_models_unload_post.mdx create mode 100644 docs2/content/docs/api-reference/unknown/update_profile_profiles__profile_id__put.mdx create mode 100644 docs2/content/docs/api/authentication.mdx create mode 100644 docs2/content/docs/api/generation.mdx create mode 100644 docs2/content/docs/api/meta.json create mode 100644 docs2/content/docs/api/overview.mdx create mode 100644 docs2/content/docs/api/recordings.mdx create mode 100644 docs2/content/docs/api/voice-profiles.mdx create mode 100644 docs2/content/docs/developer/architecture.mdx create mode 100644 docs2/content/docs/developer/audio-channels.mdx create mode 100644 docs2/content/docs/developer/autoupdater.mdx create mode 100644 docs2/content/docs/developer/building.mdx create mode 100644 docs2/content/docs/developer/contributing.mdx create mode 100644 docs2/content/docs/developer/history.mdx create mode 100644 docs2/content/docs/developer/meta.json create mode 100644 docs2/content/docs/developer/model-management.mdx create mode 100644 docs2/content/docs/developer/setup.mdx create mode 100644 docs2/content/docs/developer/stories.mdx create mode 100644 docs2/content/docs/developer/transcription.mdx create mode 100644 docs2/content/docs/developer/tts-generation.mdx create mode 100644 docs2/content/docs/developer/voice-profiles.mdx create mode 100644 docs2/content/docs/index.mdx create mode 100644 docs2/content/docs/meta.json create mode 100644 docs2/content/docs/overview/building-stories.mdx create mode 100644 docs2/content/docs/overview/creating-voice-profiles.mdx create mode 100644 docs2/content/docs/overview/generating-speech.mdx create mode 100644 docs2/content/docs/overview/generation-history.mdx create mode 100644 docs2/content/docs/overview/installation.mdx create mode 100644 docs2/content/docs/overview/introduction.mdx create mode 100644 docs2/content/docs/overview/meta.json create mode 100644 docs2/content/docs/overview/quick-start.mdx create mode 100644 docs2/content/docs/overview/recording-transcription.mdx create mode 100644 docs2/content/docs/overview/remote-mode.mdx create mode 100644 docs2/content/docs/overview/stories-editor.mdx create mode 100644 docs2/content/docs/overview/troubleshooting.mdx create mode 100644 docs2/content/docs/overview/voice-cloning.mdx create mode 100644 docs2/content/docs/plans/DOCKER_DEPLOYMENT.md create mode 100644 docs2/content/docs/plans/EXTERNAL_PROVIDERS.md create mode 100644 docs2/content/docs/plans/MLX_AUDIO.md create mode 100644 docs2/content/docs/plans/OPENAI_SUPPORT.md create mode 100644 docs2/content/docs/plans/meta.json create mode 100644 docs2/lib/layout.shared.tsx create mode 100644 docs2/lib/openapi.ts create mode 100644 docs2/lib/source.ts create mode 100644 docs2/mdx-components.tsx create mode 100644 docs2/next.config.mjs create mode 100644 docs2/openapi.json create mode 100644 docs2/package.json create mode 100644 docs2/postcss.config.mjs create mode 100644 docs2/public/images/app-screenshot-1.webp create mode 100644 docs2/public/images/app-screenshot-2.webp create mode 100644 docs2/public/images/app-screenshot-3.webp create mode 100644 docs2/public/logo/icon-dark.png create mode 100644 docs2/public/logo/icon-light.png create mode 100644 docs2/scripts/generate-openapi.ts create mode 100644 docs2/source.config.ts create mode 100644 docs2/tsconfig.json diff --git a/docs2/.gitignore b/docs2/.gitignore new file mode 100644 index 00000000..9e429e49 --- /dev/null +++ b/docs2/.gitignore @@ -0,0 +1,26 @@ +# deps +/node_modules + +# generated content +.source + +# test & build +/coverage +/.next/ +/out/ +/build +*.tsbuildinfo + +# misc +.DS_Store +*.pem +/.pnp +.pnp.js +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# others +.env*.local +.vercel +next-env.d.ts \ No newline at end of file diff --git a/docs2/MIGRATION.md b/docs2/MIGRATION.md new file mode 100644 index 00000000..8d633479 --- /dev/null +++ b/docs2/MIGRATION.md @@ -0,0 +1,87 @@ +# Documentation Migration: Mintlify → Fumadocs + +This document summarizes the migration of documentation from `/docs` (Mintlify) to `/docs2` (Fumadocs). + +## What Was Done + +### 1. Files Copied +- ✅ All 29 MDX files from `/docs` folders (overview, api, developer, plans) +- ✅ All 4 root-level markdown files (AUTOUPDATER.md, AUTOUPDATER_QUICKSTART.md, TROUBLESHOOTING.md, README.md) +- ✅ All images (3 webp files) → `public/images/` +- ✅ All logo files (2 png files) → `public/logo/` + +### 2. Component Migration +Created compatibility layer in `components/mintlify-compat.tsx` that maps Mintlify components to Fumadocs equivalents: + +- `` → Simple div wrapper (images are zoomable by default in Fumadocs) +- `` → `` (Fumadocs component) +- `` → `` (with icon string → Lucide icon mapping) +- `` / `` → Direct mapping to Fumadocs components +- ``, ``, `` → `` +- `` → `` +- `` → `` +- `` / `` → HTML `
` / `` elements + +### 3. Navigation Structure +Created `meta.json` files for each folder: +- `content/docs/meta.json` - Root documentation +- `content/docs/overview/meta.json` - Overview pages +- `content/docs/api/meta.json` - API reference +- `content/docs/developer/meta.json` - Developer docs +- `content/docs/plans/meta.json` - Plans/roadmap + +### 4. Link Fixes +- Fixed incorrect `/guides/...` paths → `/overview/...` +- All internal links now use correct paths + +### 5. Branding +- Updated `lib/layout.shared.tsx` to use "Voicebox" as the nav title + +## File Structure + +``` +docs2/ +├── components/ +│ └── mintlify-compat.tsx # Mintlify → Fumadocs component mappings +├── content/docs/ +│ ├── meta.json # Root navigation +│ ├── overview/ # 12 MDX files +│ ├── api/ # 5 MDX files +│ ├── developer/ # 12 MDX files +│ ├── plans/ # 4 MD files +│ └── *.md # 4 root markdown files +├── public/ +│ ├── images/ # 3 webp files +│ └── logo/ # 2 png files +└── mdx-components.tsx # MDX component configuration +``` + +## Icon Mapping + +The following icon strings are mapped to Lucide icons: +- `microphone` → Mic +- `film` → Film +- `code` → Code +- `shield` → Shield +- `download` → Download +- `rocket` → Rocket +- `apple` → Apple +- `windows` → Windows +- `server` → Server +- `user` → User +- `waveform` → Waveform + +## Next Steps + +1. **Test the build**: Run `npm run build` (requires Node.js >= 20.9.0) +2. **Start dev server**: Run `npm run dev` to preview +3. **Customize styling**: Update `app/global.css` if needed +4. **Add more icons**: Extend `iconMap` in `mintlify-compat.tsx` as needed +5. **Review navigation**: Adjust `meta.json` files to customize page order + +## Notes + +- Image paths (`/images/...`) work as-is since Next.js serves from `public/` +- All Mintlify components are now compatible with Fumadocs +- Navigation structure follows Fumadocs conventions +- No breaking changes to content - all MDX files work with compatibility layer diff --git a/docs2/README.md b/docs2/README.md new file mode 100644 index 00000000..68109407 --- /dev/null +++ b/docs2/README.md @@ -0,0 +1,45 @@ +# fumadocs-ui-template + +This is a Next.js application generated with +[Create Fumadocs](https://github.com/fuma-nama/fumadocs). + +Run development server: + +```bash +npm run dev +# or +pnpm dev +# or +yarn dev +``` + +Open http://localhost:3000 with your browser to see the result. + +## Explore + +In the project, you can see: + +- `lib/source.ts`: Code for content source adapter, [`loader()`](https://fumadocs.dev/docs/headless/source-api) provides the interface to access your content. +- `lib/layout.shared.tsx`: Shared options for layouts, optional but preferred to keep. + +| Route | Description | +| ------------------------- | ------------------------------------------------------ | +| `app/(home)` | The route group for your landing page and other pages. | +| `app/docs` | The documentation layout and pages. | +| `app/api/search/route.ts` | The Route Handler for search. | + +### Fumadocs MDX + +A `source.config.ts` config file has been included, you can customise different options like frontmatter schema. + +Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details. + +## Learn More + +To learn more about Next.js and Fumadocs, take a look at the following +resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js + features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +- [Fumadocs](https://fumadocs.dev) - learn about Fumadocs diff --git a/docs2/app/(home)/layout.tsx b/docs2/app/(home)/layout.tsx new file mode 100644 index 00000000..77379fac --- /dev/null +++ b/docs2/app/(home)/layout.tsx @@ -0,0 +1,6 @@ +import { HomeLayout } from 'fumadocs-ui/layouts/home'; +import { baseOptions } from '@/lib/layout.shared'; + +export default function Layout({ children }: LayoutProps<'/'>) { + return {children}; +} diff --git a/docs2/app/(home)/page.tsx b/docs2/app/(home)/page.tsx new file mode 100644 index 00000000..c936084d --- /dev/null +++ b/docs2/app/(home)/page.tsx @@ -0,0 +1,16 @@ +import Link from 'next/link'; + +export default function HomePage() { + return ( +
+

Hello World

+

+ You can open{' '} + + /docs + {' '} + and see the documentation. +

+
+ ); +} diff --git a/docs2/app/api/search/route.ts b/docs2/app/api/search/route.ts new file mode 100644 index 00000000..7ba7e823 --- /dev/null +++ b/docs2/app/api/search/route.ts @@ -0,0 +1,7 @@ +import { source } from '@/lib/source'; +import { createFromSource } from 'fumadocs-core/search/server'; + +export const { GET } = createFromSource(source, { + // https://docs.orama.com/docs/orama-js/supported-languages + language: 'english', +}); diff --git a/docs2/app/docs/[[...slug]]/page.tsx b/docs2/app/docs/[[...slug]]/page.tsx new file mode 100644 index 00000000..1034bb58 --- /dev/null +++ b/docs2/app/docs/[[...slug]]/page.tsx @@ -0,0 +1,55 @@ +import { getPageImage, source } from '@/lib/source'; +import { + DocsBody, + DocsDescription, + DocsPage, + DocsTitle, +} from 'fumadocs-ui/page'; +import { notFound } from 'next/navigation'; +import { getMDXComponents } from '@/mdx-components'; +import type { Metadata } from 'next'; +import { createRelativeLink } from 'fumadocs-ui/mdx'; +import { APIPage } from '@/components/api-page'; + +export default async function Page(props: PageProps<'/docs/[[...slug]]'>) { + const params = await props.params; + const page = source.getPage(params.slug); + if (!page) notFound(); + + const MDX = page.data.body; + + return ( + + {page.data.title} + {page.data.description} + + + + + ); +} + +export async function generateStaticParams() { + return source.generateParams(); +} + +export async function generateMetadata( + props: PageProps<'/docs/[[...slug]]'>, +): Promise { + const params = await props.params; + const page = source.getPage(params.slug); + if (!page) notFound(); + + return { + title: page.data.title, + description: page.data.description, + openGraph: { + images: getPageImage(page).url, + }, + }; +} diff --git a/docs2/app/docs/layout.tsx b/docs2/app/docs/layout.tsx new file mode 100644 index 00000000..299d2e28 --- /dev/null +++ b/docs2/app/docs/layout.tsx @@ -0,0 +1,11 @@ +import { source } from '@/lib/source'; +import { DocsLayout } from 'fumadocs-ui/layouts/docs'; +import { baseOptions } from '@/lib/layout.shared'; + +export default function Layout({ children }: LayoutProps<'/docs'>) { + return ( + + {children} + + ); +} diff --git a/docs2/app/global.css b/docs2/app/global.css new file mode 100644 index 00000000..25072e9d --- /dev/null +++ b/docs2/app/global.css @@ -0,0 +1,4 @@ +@import 'tailwindcss'; +@import 'fumadocs-ui/css/neutral.css'; +@import 'fumadocs-ui/css/preset.css'; +@import 'fumadocs-openapi/css/preset.css'; diff --git a/docs2/app/layout.tsx b/docs2/app/layout.tsx new file mode 100644 index 00000000..22fdca31 --- /dev/null +++ b/docs2/app/layout.tsx @@ -0,0 +1,17 @@ +import { RootProvider } from 'fumadocs-ui/provider/next'; +import './global.css'; +import { Inter } from 'next/font/google'; + +const inter = Inter({ + subsets: ['latin'], +}); + +export default function Layout({ children }: LayoutProps<'/'>) { + return ( + + + {children} + + + ); +} diff --git a/docs2/app/llms-full.txt/route.ts b/docs2/app/llms-full.txt/route.ts new file mode 100644 index 00000000..d494d2cb --- /dev/null +++ b/docs2/app/llms-full.txt/route.ts @@ -0,0 +1,10 @@ +import { getLLMText, source } from '@/lib/source'; + +export const revalidate = false; + +export async function GET() { + const scan = source.getPages().map(getLLMText); + const scanned = await Promise.all(scan); + + return new Response(scanned.join('\n\n')); +} diff --git a/docs2/app/og/docs/[...slug]/route.tsx b/docs2/app/og/docs/[...slug]/route.tsx new file mode 100644 index 00000000..f5df96db --- /dev/null +++ b/docs2/app/og/docs/[...slug]/route.tsx @@ -0,0 +1,36 @@ +import { getPageImage, source } from '@/lib/source'; +import { notFound } from 'next/navigation'; +import { ImageResponse } from 'next/og'; +import { generate as DefaultImage } from 'fumadocs-ui/og'; + +export const revalidate = false; + +export async function GET( + _req: Request, + { params }: RouteContext<'/og/docs/[...slug]'>, +) { + const { slug } = await params; + const page = source.getPage(slug.slice(0, -1)); + if (!page) notFound(); + + return new ImageResponse( + ( + + ), + { + width: 1200, + height: 630, + }, + ); +} + +export function generateStaticParams() { + return source.getPages().map((page) => ({ + lang: page.locale, + slug: getPageImage(page).segments, + })); +} diff --git a/docs2/bun.lock b/docs2/bun.lock new file mode 100644 index 00000000..8f3dc9ee --- /dev/null +++ b/docs2/bun.lock @@ -0,0 +1,829 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "example-next-mdx", + "dependencies": { + "fumadocs-core": "^16.4.11", + "fumadocs-mdx": "13", + "fumadocs-openapi": "^10.2.7", + "fumadocs-ui": "^16.4.11", + "lucide-react": "^0.546.0", + "next": "^16.1.6", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "shiki": "^3.22.0", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.15", + "@types/mdx": "^2.0.13", + "@types/node": "^24.9.1", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.15", + "typescript": "^5.9.3", + }, + }, + }, + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.4", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.5", "", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.7", "", { "dependencies": { "@floating-ui/dom": "^1.7.5" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + + "@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.0", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.8.0", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.0", "tslib": "^2.8.1" } }, "sha512-zgMYWdUlmEZpX2Io+v3LHrfq9xZ6khpQVf9UAw2xYWhGerGgI9XgH1HvL/A34jWiruUJpYlP5pk4g8nIcaDrXQ=="], + + "@fumadocs/ui": ["@fumadocs/ui@16.4.11", "", { "dependencies": { "next-themes": "^0.4.6", "postcss-selector-parser": "^7.1.1", "tailwind-merge": "^3.4.0" }, "peerDependencies": { "@types/react": "*", "fumadocs-core": "16.4.11", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "tailwindcss": "^4.0.0" }, "optionalPeers": ["@types/react", "next", "tailwindcss"] }, "sha512-3APzHr4Rv5P9YQApTKCQW3cXika0dwHuOo8WxYz74y42nONRo/TMDtvoWaNhB145sBrW9N4j0/0xXfiGLihVRQ=="], + + "@fumari/json-schema-to-typescript": ["@fumari/json-schema-to-typescript@2.0.0", "", { "dependencies": { "js-yaml": "^4.1.0" }, "peerDependencies": { "@apidevtools/json-schema-ref-parser": "14.x.x", "prettier": "3.x.x" }, "optionalPeers": ["@apidevtools/json-schema-ref-parser", "prettier"] }, "sha512-X0Wm3QJLj1Rtb1nY2exM6QwMXb9LGyIKLf35+n6xyltDDBLMECOC4R/zPaw3RwgFVmvRLSmLCd+ht4sKabgmNw=="], + + "@fumari/stf": ["@fumari/stf@0.0.1", "", { "peerDependencies": { "@types/react": "*", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@types/react"] }, "sha512-Io3xlYr8xMPZtxWI5GwIRvWEMu1CsfbwXa09ACeXGjbY4QVreMiMjNCvN1YNLmETgG6Ru1S/+2B8qv80OIExyA=="], + + "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + + "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="], + + "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], + + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.14", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.3", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.1", "", {}, "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw=="], + + "@scalar/helpers": ["@scalar/helpers@0.2.10", "", {}, "sha512-VS32setBEAGY9JifuDZKHIq8SUCUWLEfL1V+h3s5V4wcmE8OZVkzaJemsMq/YAM9e7gb9ZbkvJLL4zzEvPSrVg=="], + + "@scalar/json-magic": ["@scalar/json-magic@0.9.5", "", { "dependencies": { "@scalar/helpers": "0.2.10", "yaml": "^2.8.0" } }, "sha512-+IZngReH0P+ima7y9u/f5QJD60AdISG81ezhwEVrYhsp46PiJp7YyOd0z1YLiOgwV0jkPlPo74T/FVBcM2ejuw=="], + + "@scalar/openapi-parser": ["@scalar/openapi-parser@0.24.5", "", { "dependencies": { "@scalar/json-magic": "0.9.4", "@scalar/openapi-types": "0.5.3", "@scalar/openapi-upgrader": "0.1.8", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.8.0" } }, "sha512-pTeKnmhVdSIfG3vysgDm6jsKc7Do1vXdy/4aqp7j8AEzXllf8RZjSgRSUhtvFYFQCr27fDZ117V3WPQUYtgmCw=="], + + "@scalar/openapi-types": ["@scalar/openapi-types@0.5.3", "", { "dependencies": { "zod": "^4.1.11" } }, "sha512-m4n/Su3K01d15dmdWO1LlqecdSPKuNjuokrJLdiQ485kW/hRHbXW1QP6tJL75myhw/XhX5YhYAR+jrwnGjXiMw=="], + + "@scalar/openapi-upgrader": ["@scalar/openapi-upgrader@0.1.8", "", { "dependencies": { "@scalar/openapi-types": "0.5.3" } }, "sha512-2xuYLLs0fBadLIk4I1ObjMiCnOyLPEMPf24A1HtHQvhKGDnGlvT63F2rU2Xw8lxCjgHnzveMPnOJEbwIy64RCg=="], + + "@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA=="], + + "@shikijs/langs": ["@shikijs/langs@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA=="], + + "@shikijs/rehype": ["@shikijs/rehype@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.1", "shiki": "3.22.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0" } }, "sha512-69b2VPc6XBy/VmAJlpBU5By+bJSBdE2nvgRCZXav7zujbrjXuT0F60DIrjKuutjPqNufuizE+E8tIZr2Yn8Z+g=="], + + "@shikijs/themes": ["@shikijs/themes@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g=="], + + "@shikijs/transformers": ["@shikijs/transformers@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/types": "3.22.0" } }, "sha512-E7eRV7mwDBjueLF6852n2oYeJYxBq3NSsDk+uyruYAXONv4U8holGmIrT+mPRJQ1J1SNOH6L8G19KRzmBawrFw=="], + + "@shikijs/types": ["@shikijs/types@3.22.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.18", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", "tailwindcss": "4.1.18" } }, "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g=="], + + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + + "@types/node": ["@types/node@24.10.9", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw=="], + + "@types/react": ["@types/react@19.2.10", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.4", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q=="], + + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], + + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + + "fast-xml-parser": ["fast-xml-parser@4.5.3", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "foreach": ["foreach@2.0.6", "", {}, "sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg=="], + + "fumadocs-core": ["fumadocs-core@16.4.11", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.8.0", "@orama/orama": "^3.1.18", "@shikijs/rehype": "^3.21.0", "@shikijs/transformers": "^3.21.0", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^3.21.0", "tinyglobby": "^0.2.15", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/react": "*", "algoliasearch": "5.x.x", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0", "zod": "4.x.x" }, "optionalPeers": ["@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/react", "algoliasearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-ORjWgYetxDgyHZocuvEghfxt6tuEPWE+Km5KvwNKlXPxcNdBIiSVCED8WEMwiw1n/FZ/ys+W+BOe58ZXxhWg2A=="], + + "fumadocs-mdx": ["fumadocs-mdx@13.0.8", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.0.0", "chokidar": "^4.0.3", "esbuild": "^0.25.12", "estree-util-value-to-estree": "^3.5.0", "js-yaml": "^4.1.0", "lru-cache": "^11.2.2", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "remark-mdx": "^3.1.1", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "zod": "^4.1.12" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "fumadocs-core": "^15.0.0 || ^16.0.0", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-UbUwH0iGvYbytnxhmfd7tWJKFK8L0mrbTAmrQYnpg6Wi/h8afNMJmbHBOzVcaEWJKeFipZ1CGDAsNA2fztwXNg=="], + + "fumadocs-openapi": ["fumadocs-openapi@10.2.7", "", { "dependencies": { "@fumari/json-schema-to-typescript": "^2.0.0", "@fumari/stf": "^0.0.1", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@scalar/json-magic": "^0.9.4", "@scalar/openapi-parser": "0.24.5", "ajv": "^8.17.1", "class-variance-authority": "^0.7.1", "github-slugger": "^2.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "js-yaml": "^4.1.1", "lucide-react": "^0.563.0", "next-themes": "^0.4.6", "openapi-sampler": "^1.6.2", "react-hook-form": "^7.71.1", "remark": "^15.0.1", "remark-rehype": "^11.1.2", "tailwind-merge": "^3.4.0", "xml-js": "^1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "@types/react": "*", "fumadocs-core": "^16.2.0", "fumadocs-ui": "^16.2.0", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@scalar/api-client-react", "@types/react"] }, "sha512-V24iseZFHmUyPdVEH/nyR1205mltOamlHXvAGtJx9FteKj0li0Rf7o7EPkV9Mby202ReG2CIic1cR2oWa+i7Jg=="], + + "fumadocs-ui": ["fumadocs-ui@16.4.11", "", { "dependencies": { "@fumadocs/ui": "16.4.11", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "lucide-react": "^0.563.0", "next-themes": "^0.4.6", "react-medium-image-zoom": "^5.4.0", "scroll-into-view-if-needed": "^3.1.0" }, "peerDependencies": { "@types/react": "*", "fumadocs-core": "16.4.11", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "tailwindcss": "^4.0.0" }, "optionalPeers": ["@types/react", "next", "tailwindcss"] }, "sha512-LFOzdnNFAFkOHzsUtCMi8cyal1pIZqygoQKSET0LO/C5JOk1YQKAZqiut1jf6pv6o0OKXacDk+MY7kfn61309A=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "image-size": ["image-size@2.0.2", "", { "bin": { "image-size": "bin/image-size.js" } }, "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w=="], + + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + + "json-pointer": ["json-pointer@0.6.2", "", { "dependencies": { "foreach": "^2.0.4" } }, "sha512-vLWcKbOaXlO+jvRy4qNd+TI1QUPZzfJj1tpJ3vAXDych5XJf93ftpUKe5pKCrzyIIwgBJcOcCVRUfqQP25afBw=="], + + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "jsonpointer": ["jsonpointer@5.0.1", "", {}, "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ=="], + + "leven": ["leven@4.1.0", "", {}, "sha512-KZ9W9nWDT7rF7Dazg8xyLHGLrmpgq2nVNFUckhqdW3szVP6YhCpp/RAnpmVExA9JvrMynjwSLVrEj3AepHR6ew=="], + + "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], + + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "lru-cache": ["lru-cache@11.2.5", "", {}, "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw=="], + + "lucide-react": ["lucide-react@0.546.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "npm-to-yarn": ["npm-to-yarn@3.0.1", "", {}, "sha512-tt6PvKu4WyzPwWUzy/hvPFqn+uwXO0K1ZHka8az3NnrhWJDmSqI8ncWq0fkL0k/lmmi5tAC11FXwXuh0rFbt1A=="], + + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], + + "openapi-sampler": ["openapi-sampler@1.6.2", "", { "dependencies": { "@types/json-schema": "^7.0.7", "fast-xml-parser": "^4.5.0", "json-pointer": "0.6.2" } }, "sha512-NyKGiFKfSWAZr4srD/5WDhInOWDhfml32h/FKUqLpEwKJt0kG0LGUU0MdyNkKrVGuJnw6DuPWq/sHCwAMpiRxg=="], + + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + + "path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + + "react-hook-form": ["react-hook-form@7.71.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w=="], + + "react-medium-image-zoom": ["react-medium-image-zoom@5.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-BsE+EnFVQzFIlyuuQrZ9iTwyKpKkqdFZV1ImEQN573QPqGrIUuNni7aF+sZwDcxlsuOMayCr6oO/PZR/yJnbRg=="], + + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "shiki": ["shiki@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/engine-javascript": "3.22.0", "@shikijs/engine-oniguruma": "3.22.0", "@shikijs/langs": "3.22.0", "@shikijs/themes": "3.22.0", "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], + + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], + + "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], + + "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": { "xml-js": "./bin/cli.js" } }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="], + + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@scalar/openapi-parser/@scalar/json-magic": ["@scalar/json-magic@0.9.4", "", { "dependencies": { "@scalar/helpers": "0.2.9", "yaml": "^2.8.0" } }, "sha512-PyfyWrH4ZkW0TM1ColiiHj4NRF8hUM61H0UzAkHLhRNnKFxi6hI+oqNrwqPnyk93hrpkpTRHC7Fl5T0BRwuzVg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "fumadocs-openapi/lucide-react": ["lucide-react@0.563.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA=="], + + "fumadocs-ui/lucide-react": ["lucide-react@0.563.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "@scalar/openapi-parser/@scalar/json-magic/@scalar/helpers": ["@scalar/helpers@0.2.9", "", {}, "sha512-Y4ffJF0yELdwZ0BKgonqn3SumIgRn1WKyYCVHD+TDM7qRFChdGRypyt20+efHs26fmJeyBAIIv2laICj5uimiw=="], + } +} diff --git a/docs2/components/api-page.client.tsx b/docs2/components/api-page.client.tsx new file mode 100644 index 00000000..afb84c75 --- /dev/null +++ b/docs2/components/api-page.client.tsx @@ -0,0 +1,6 @@ +'use client'; +import { defineClientConfig } from 'fumadocs-openapi/ui/client'; + +export default defineClientConfig({ + // Client-side configuration for API playground +}); diff --git a/docs2/components/api-page.tsx b/docs2/components/api-page.tsx new file mode 100644 index 00000000..e52c79fd --- /dev/null +++ b/docs2/components/api-page.tsx @@ -0,0 +1,7 @@ +import { openapi } from '@/lib/openapi'; +import { createAPIPage } from 'fumadocs-openapi/ui'; +import client from './api-page.client'; + +export const APIPage = createAPIPage(openapi, { + client, +}); diff --git a/docs2/components/mintlify-compat.tsx b/docs2/components/mintlify-compat.tsx new file mode 100644 index 00000000..2cc6c916 --- /dev/null +++ b/docs2/components/mintlify-compat.tsx @@ -0,0 +1,124 @@ +/** + * Compatibility components for Mintlify -> Fumadocs migration + * Maps Mintlify components to Fumadocs equivalents + */ + +import { Card, Cards } from 'fumadocs-ui/components/card'; +import { Step, Steps } from 'fumadocs-ui/components/steps'; +import { Callout } from 'fumadocs-ui/components/callout'; +import type { ReactNode } from 'react'; +import * as Icons from 'lucide-react'; + +// Icon name to Lucide icon mapping +const iconMap: Record = { + microphone: Icons.Mic, + film: Icons.Film, + code: Icons.Code, + shield: Icons.Shield, + download: Icons.Download, + rocket: Icons.Rocket, + apple: Icons.Apple, + windows: Icons.Monitor, // Windows doesn't exist, using Monitor instead + server: Icons.Server, + user: Icons.User, + waveform: Icons.Waves, // Waveform doesn't exist, using Waves instead +}; + +function getIcon(iconName?: string) { + if (!iconName) return undefined; + const IconComponent = iconMap[iconName.toLowerCase()]; + return IconComponent ? : undefined; +} + +// Frame -> Simple image wrapper (Fumadocs images are zoomable by default) +export function Frame({ children }: { children: ReactNode }) { + return
{children}
; +} + +// CardGroup -> Cards (Fumadocs component) +export function CardGroup({ + cols, + children +}: { + cols?: number; + children: ReactNode; +}) { + return {children}; +} + +// Card -> Card (maps icon string to Lucide icon) +export function MintlifyCard({ + title, + icon, + href, + children +}: { + title?: string; + icon?: string; + href?: string; + children: ReactNode; +}) { + const iconElement = getIcon(icon); + + // Extract text content from children to avoid nested

tags + // fumadocs Card component wraps description in a

, so we need plain content + const description = typeof children === 'string' + ? children + : undefined; + + return ( + + ); +} + +// Steps and Step are direct mappings +export { Steps, Step }; + +// Callout variants - Note: Fumadocs doesn't have "note" type, using "info" +export function Tip({ children }: { children: ReactNode }) { + return {children}; +} + +export function Note({ children }: { children: ReactNode }) { + return {children}; +} + +export function Warning({ children }: { children: ReactNode }) { + return {children}; +} + +export function Info({ children }: { children: ReactNode }) { + return {children}; +} + +export function Danger({ children }: { children: ReactNode }) { + return {children}; +} + +// Accordion -> HTML details/summary (Fumadocs doesn't have built-in accordion) +export function AccordionGroup({ children }: { children: ReactNode }) { + return

{children}
; +} + +export function Accordion({ + title, + children +}: { + title: string; + children: ReactNode; +}) { + return ( +
+ + + {title} + +
{children}
+
+ ); +} diff --git a/docs2/content/docs/AUTOUPDATER.md b/docs2/content/docs/AUTOUPDATER.md new file mode 100644 index 00000000..62a7e69f --- /dev/null +++ b/docs2/content/docs/AUTOUPDATER.md @@ -0,0 +1,195 @@ +--- +title: "Auto-Updater Documentation" +description: "How Voicebox automatic updates work for users and developers" +--- + +Voicebox includes automatic updates powered by Tauri's updater plugin. This document explains how it works for both users and developers. + +## 1. Generate Signing Keys + +Run this command to generate your signing keypair: + +```bash +cd tauri && bun tauri signer generate -w ~/.tauri/voicebox.key +``` + +This creates: +- **Private key**: `~/.tauri/voicebox.key` (keep this secret!) +- **Public key**: `~/.tauri/voicebox.key.pub` + +## 2. Update Configuration + +Copy the content from `~/.tauri/voicebox.key.pub` and replace the placeholder in `tauri/src-tauri/tauri.conf.json`: + +```json +{ + "plugins": { + "updater": { + "pubkey": "PASTE_PUBLIC_KEY_CONTENT_HERE", + "endpoints": [ + "https://github.com/YOUR_USERNAME/voicebox/releases/latest/download/latest.json" + ] + } + } +} +``` + +Update the endpoint URL with your actual GitHub username/organization. + +## 3. Building with Signatures + +When building releases, set these environment variables: + +**macOS/Linux:** +```bash +export TAURI_SIGNING_PRIVATE_KEY="$(cat ~/.tauri/voicebox.key)" +export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" +bun run build +``` + +**Windows PowerShell:** +```powershell +$env:TAURI_SIGNING_PRIVATE_KEY = Get-Content ~/.tauri/voicebox.key -Raw +$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD = "" +bun run build +``` + +## 4. GitHub Release Setup + +When you create a GitHub release, the build process will generate: +- Installers for each platform +- `.sig` signature files +- `latest.json` update manifest + +### Manual Release Process + +1. Build the app with signing keys set +2. Create a new GitHub release +3. Upload all files from `tauri/src-tauri/target/release/bundle/` +4. Create `latest.json` in your release assets: + +```json +{ + "version": "0.2.0", + "notes": "Bug fixes and improvements", + "pub_date": "2026-01-25T12:00:00Z", + "platforms": { + "darwin-aarch64": { + "signature": "CONTENT_FROM_.app.tar.gz.sig", + "url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_aarch64.dmg" + }, + "darwin-x86_64": { + "signature": "CONTENT_FROM_.app.tar.gz.sig", + "url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64.dmg" + }, + "linux-x86_64": { + "signature": "CONTENT_FROM_.AppImage.sig", + "url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_amd64.AppImage" + }, + "windows-x86_64": { + "signature": "CONTENT_FROM_.msi.sig", + "url": "https://github.com/YOUR_USERNAME/voicebox/releases/download/v0.2.0/voicebox_0.2.0_x64_en-US.msi" + } + } +} +``` + +### Automated GitHub Actions (Recommended) + +Create `.github/workflows/release.yml`: + +```yaml +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + strategy: + matrix: + platform: [macos-latest, ubuntu-22.04, windows-latest] + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install dependencies (Ubuntu) + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + + - name: Install dependencies + run: bun install + + - name: Build + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + run: bun run build + + - name: Upload Release + uses: softprops/action-gh-release@v1 + with: + files: tauri/src-tauri/target/release/bundle/**/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + +Add your private key to GitHub secrets: +- Go to Settings → Secrets and variables → Actions +- Add `TAURI_SIGNING_PRIVATE_KEY` with the content of `~/.tauri/voicebox.key` +- Add `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` (empty string if no password) + +## Frontend Integration + +The frontend integration is complete with automatic update notifications and manual update checks: + +- **Update Notification Banner** - Appears automatically when updates are available +- **Settings Panel** - Manual "Check for Updates" button in Settings tab +- **Update Hook** - React hook handles all update operations + +See `docs/AUTOUPDATER_QUICKSTART.md` for a quick setup guide. + +## Security Notes + +- Never commit your private key to version control +- Store private keys securely (use GitHub secrets for CI/CD) +- The public key in `tauri.conf.json` is safe to commit +- Updates are cryptographically verified before installation +- HTTP endpoints are blocked by default (HTTPS only) + +## Testing Updates + +1. Build version 0.1.0 and install it +2. Update version in `tauri.conf.json` to 0.2.0 +3. Build version 0.2.0 with signatures +4. Create a local server or GitHub release with `latest.json` +5. Run version 0.1.0 and trigger update check +6. Verify update downloads and installs correctly + +## Troubleshooting + +**"Invalid signature" error:** +- Verify public key matches the private key used to sign +- Ensure signature files (.sig) are uploaded correctly + +**"No update available" when one exists:** +- Check endpoint URL is correct +- Verify `latest.json` format matches specification +- Ensure version in latest.json is higher than current version + +**Build fails with signing:** +- Confirm environment variables are set correctly +- Check private key file exists and is readable +- Verify private key format (should start with `dW50cnVzdGVkIGNvbW1lbnQ6`) diff --git a/docs2/content/docs/AUTOUPDATER_QUICKSTART.md b/docs2/content/docs/AUTOUPDATER_QUICKSTART.md new file mode 100644 index 00000000..9a28070b --- /dev/null +++ b/docs2/content/docs/AUTOUPDATER_QUICKSTART.md @@ -0,0 +1,119 @@ +--- +title: "Autoupdater Quick Start" +description: "Quick guide to activate the Tauri v2 autoupdater" +--- + +The Tauri v2 autoupdater has been fully configured and integrated. Follow these steps to activate it. + +## What's Already Done + +✅ Rust plugin installed and initialized +✅ Tauri configuration set up with updater settings +✅ Permissions granted for update operations +✅ GitHub Actions workflow updated with signing support +✅ Frontend components created and integrated +✅ Update notifications on app startup +✅ Manual update check in Settings tab + +## Required Steps (5 minutes) + +### 1. Generate Signing Keys + +```bash +bun run generate:keys +``` + +This creates: +- Private key: `~/.tauri/voicebox.key` (keep secret!) +- Public key: `~/.tauri/voicebox.key.pub` (safe to share) + +### 2. Update Tauri Config + +Open `tauri/src-tauri/tauri.conf.json` and: + +1. Replace `"REPLACE_WITH_YOUR_PUBLIC_KEY"` with the content from `~/.tauri/voicebox.key.pub` +2. Update the endpoint URL with your GitHub username: + ```json + "endpoints": [ + "https://github.com/YOUR_USERNAME/voicebox/releases/latest/download/latest.json" + ] + ``` + +### 3. Add GitHub Secrets + +Go to your repo Settings → Secrets and variables → Actions: + +1. Add `TAURI_SIGNING_PRIVATE_KEY`: + ```bash + cat ~/.tauri/voicebox.key + ``` + Copy the entire output and paste as the secret value + +2. Add `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`: + Leave empty (or add your password if you set one) + +### 4. Test the Setup + +To test locally before creating a release: + +```bash +bun run build:release +``` + +This will verify your keys are set up correctly. + +## How It Works + +### For Users +1. App checks for updates on startup (only in Tauri builds) +2. If an update is available, a banner appears at the top +3. Users can click "Install Now" to download and install +4. App restarts automatically after installation + +### For Developers +1. Create a new git tag: `git tag v0.2.0 && git push --tags` +2. GitHub Actions builds signed releases for all platforms +3. Uploads installers and generates `latest.json` manifest +4. Users running older versions will be notified automatically + +## UI Components + +### Update Notification Banner +- Shows at top of app when update is available +- Appears automatically on startup +- Displays download/install progress + +### Settings Panel +- Located in Settings tab +- Shows current version +- Manual "Check for Updates" button +- Update status and progress + +## Troubleshooting + +**"Public key not configured"** +- Make sure you copied the entire content from `voicebox.key.pub` +- The key should start with `dW50cnVzdGVkIGNvbW1lbnQ6` + +**"Failed to check for updates"** +- Endpoint URL might be incorrect +- No releases published yet (expected for first setup) + +**Build fails with signing error** +- Check that GitHub secrets are set correctly +- Verify private key file exists at `~/.tauri/voicebox.key` + +## Next Release Workflow + +1. Update version in `tauri/src-tauri/tauri.conf.json` +2. Commit changes +3. Create and push tag: `git tag v0.2.0 && git push --tags` +4. GitHub Actions will automatically build and create a draft release +5. Review the release and publish it +6. Users will be notified of the update + +## See Also + +- Full documentation: `docs/AUTOUPDATER.md` +- Build script: `scripts/prepare-release.sh` +- GitHub workflow: `.github/workflows/release.yml` diff --git a/docs2/content/docs/README.md b/docs2/content/docs/README.md new file mode 100644 index 00000000..66f7833d --- /dev/null +++ b/docs2/content/docs/README.md @@ -0,0 +1,67 @@ +--- +title: "Documentation README" +description: "Voicebox documentation development guide" +--- + +This directory contains the documentation for Voicebox, built with [Fumadocs](https://fumadocs.dev). + +## Development + +### Prerequisites + +Install Mintlify globally using bun: + +```bash +bun add -g mintlify +``` + +Or use the helper script: + +```bash +bun run install:mintlify +``` + +### Running Locally + +```bash +bun run dev +``` + +This will start the Mintlify dev server. + +The docs will be available at `http://localhost:3000` + +### Structure + +``` +docs/ +├── mint.json # Mintlify configuration +├── custom.css # Custom styles +├── overview/ # Getting started & feature docs +├── guides/ # User guides +├── api/ # API reference +├── development/ # Developer documentation +├── logo/ # Logo assets +└── public/ # Static assets +``` + +### Writing Docs + +- Use `.mdx` files for all documentation pages +- Follow the existing structure in `mint.json` for navigation +- Use Mintlify components for enhanced formatting (Card, CardGroup, Accordion, etc.) +- Reference the [Mintlify documentation](https://mintlify.com/docs) for available components + +## Deployment + +Docs are automatically deployed when changes are pushed to the main branch. + +To manually deploy: + +```bash +mintlify deploy +``` + +## Contributing + +See [CONTRIBUTING.md](../CONTRIBUTING.md) for contribution guidelines. diff --git a/docs2/content/docs/TROUBLESHOOTING.md b/docs2/content/docs/TROUBLESHOOTING.md new file mode 100644 index 00000000..0fef0c09 --- /dev/null +++ b/docs2/content/docs/TROUBLESHOOTING.md @@ -0,0 +1,311 @@ +--- +title: "Troubleshooting Guide" +description: "Common issues and solutions for Voicebox" +--- + +Common issues and solutions for Voicebox. + +## Installation Issues + +### macOS: "Voicebox cannot be opened because it is from an unidentified developer" + +**Solution:** +1. Right-click the `.dmg` file +2. Select "Open" +3. Click "Open" in the security dialog +4. Alternatively, go to System Settings → Privacy & Security → Allow Voicebox + +### Windows: "Windows protected your PC" + +**Solution:** +1. Click "More info" +2. Click "Run anyway" +3. Windows Defender may flag new software; this is normal for unsigned apps + +### Linux: AppImage won't run + +**Solution:** +```bash +chmod +x voicebox-*.AppImage +./voicebox-*.AppImage +``` + +## Runtime Issues + +### Server won't start + +**Symptoms:** App opens but shows "Server not connected" + +**Solutions:** +1. **Check Python installation** + ```bash + python --version # Should be 3.11+ + ``` + +2. **Check server binary exists** + - Look in `tauri/src-tauri/binaries/` for your platform + - Binary should match your system architecture + +3. **Check permissions** + ```bash + # macOS/Linux + chmod +x tauri/src-tauri/binaries/voicebox-server-* + ``` + +4. **Check logs** + - macOS: Open Console.app and search for "voicebox" + - Linux: Check `~/.local/share/voicebox/` for logs + - Windows: Check Event Viewer + +### "Model download failed" + +**Symptoms:** First generation fails with download error + +**Solutions:** +1. **Check internet connection** + - Models download from HuggingFace Hub (~2-4GB) + - First download may take several minutes + +2. **Check disk space** + - Models are cached in `~/.cache/huggingface/` + - Ensure at least 5GB free space + +3. **Manual download** (if automatic fails) + ```bash + pip install huggingface_hub + huggingface-cli download Qwen/Qwen3-TTS-12Hz-1.7B-Base + ``` + +### "Out of memory" errors + +**Symptoms:** Generation fails with CUDA/VRAM errors + +**Solutions:** +1. **Use smaller model** + - Switch to 0.6B model instead of 1.7B + - Settings → Model Management → Load 0.6B + +2. **Close other applications** + - Free up GPU memory + - Close browser tabs, other ML apps + +3. **Use CPU mode** + - Slower but works without GPU + - Backend automatically falls back to CPU + +### MLX "Failed to load the default metallib" error (Apple Silicon) + +**Symptoms:** Generation fails with "library not found" or "metallib" errors + +**Solutions:** +1. **Rebuild server binary** + ```bash + bun run build:server + ``` + The build script should automatically include MLX Metal shader libraries. + +2. **Check MLX installation** + ```bash + pip install -r backend/requirements-mlx.txt + ``` + +3. **Verify backend detection** + - Check server logs for "Backend: MLX" + - If showing "Backend: PYTORCH", MLX may not be installed correctly + +### Audio playback issues + +**Symptoms:** Generated audio won't play + +**Solutions:** +1. **Check audio format** + - Audio is saved as WAV files + - Ensure your system supports WAV playback + +2. **Try downloading audio** + - Right-click → Download + - Play in external player + +3. **Check browser permissions** (web version) + - Allow audio autoplay in browser settings + +### Slow generation + +**Symptoms:** Generation takes >30 seconds + +**Solutions:** +1. **Check backend type** (Apple Silicon) + - Check Settings → Server Status + - Should show "Backend: MLX" on Apple Silicon + - If showing "Backend: PYTORCH", install MLX: `pip install -r backend/requirements-mlx.txt` + - MLX provides 4-5x faster inference on Apple Silicon + +2. **Use GPU** (if available) + - Check Settings → Server Status + - Should show "GPU available: true" + - Apple Silicon: Should show "Metal (Apple Silicon via MLX)" + - Windows/Linux: Should show "CUDA" if GPU available + +3. **Enable caching** + - Voice prompts are cached automatically + - Second generation with same voice should be faster + +4. **Use smaller model** + - 0.6B model is faster than 1.7B + - Quality difference is minimal for most voices + +5. **Check system resources** + - Close other CPU/GPU intensive apps + - Ensure adequate RAM (8GB+ recommended) + +## API Issues + +### "Connection refused" when using API + +**Solutions:** +1. **Check server is running** + ```bash + curl http://localhost:8000/health + ``` + +2. **Check remote mode** + - If connecting remotely, ensure server is started with `--host 0.0.0.0` + - Check firewall settings + +3. **Check port availability** + - Default port is 8000 + - Ensure no other service is using it + +### CORS errors in browser + +**Solutions:** +1. **Use desktop app** (recommended) + - Desktop app doesn't have CORS restrictions + +2. **Configure CORS** (for web deployment) + - Update `backend/main.py` CORS settings + - Add your domain to allowed origins + +## Update Issues + +### "Update check failed" + +**Solutions:** +1. **Check internet connection** + - Updates are fetched from GitHub releases + +2. **Check GitHub access** + - Ensure `github.com` is accessible + - Check firewall/proxy settings + +3. **Manual update** + - Download latest release from GitHub + - Install manually + +### "Invalid signature" error + +**Solutions:** +1. **Re-download installer** + - Signature may be corrupted + - Download fresh copy from GitHub + +2. **Check release integrity** + - Verify `.sig` file matches installer + - Report issue if signature is invalid + +## Data Issues + +### Profiles disappeared + +**Solutions:** +1. **Check data directory** + - macOS: `~/Library/Application Support/voicebox/` + - Windows: `%APPDATA%/voicebox/` + - Linux: `~/.local/share/voicebox/` + +2. **Check database** + - Database: `data/voicebox.db` + - Ensure file exists and is readable + +3. **Restore from backup** + - Profiles can be exported/imported + - Check for backup files + +### "Database locked" error + +**Solutions:** +1. **Close other instances** + - Ensure only one Voicebox instance is running + +2. **Restart app** + - Close and reopen Voicebox + +3. **Check file permissions** + - Ensure database file is writable + - Check directory permissions + +## Development Issues + +### Build fails + +**Solutions:** +1. **Check Rust installation** + ```bash + rustc --version + rustup update + ``` + +2. **Check Tauri dependencies** + ```bash + cd tauri + bun install + ``` + +3. **Clean build** + ```bash + cd tauri/src-tauri + cargo clean + cd ../.. + bun run build + ``` + +### API client generation fails + +**Solutions:** +1. **Start backend server** + ```bash + bun run dev:server + ``` + +2. **Check OpenAPI endpoint** + ```bash + curl http://localhost:8000/openapi.json + ``` + +3. **Regenerate client** + ```bash + bun run generate:api + ``` + +## Still Having Issues? + +1. **Check existing issues** + - Search GitHub issues for similar problems + - Check closed issues for solutions + +2. **Create new issue** + - Include: + - OS and version + - Voicebox version + - Steps to reproduce + - Error messages/logs + - Screenshots (if applicable) + +3. **Get help** + - Check documentation in `docs/` + - Review `backend/README.md` for API details + - See `CONTRIBUTING.md` for development help + +--- + +For more help, open an issue on [GitHub](https://github.com/jamiepine/voicebox/issues). diff --git a/docs2/content/docs/api-reference/meta.json b/docs2/content/docs/api-reference/meta.json new file mode 100644 index 00000000..0a75d4fb --- /dev/null +++ b/docs2/content/docs/api-reference/meta.json @@ -0,0 +1,4 @@ +{ + "title": "API Reference", + "pages": ["unknown"] +} diff --git a/docs2/content/docs/api-reference/unknown/add_profile_sample_profiles__profile_id__samples_post.mdx b/docs2/content/docs/api-reference/unknown/add_profile_sample_profiles__profile_id__samples_post.mdx new file mode 100644 index 00000000..976dd731 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/add_profile_sample_profiles__profile_id__samples_post.mdx @@ -0,0 +1,16 @@ +--- +title: Add Profile Sample +description: Add a sample to a voice profile. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Add a sample to a voice profile. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/create_profile_profiles_post.mdx b/docs2/content/docs/api-reference/unknown/create_profile_profiles_post.mdx new file mode 100644 index 00000000..7f72d2ad --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/create_profile_profiles_post.mdx @@ -0,0 +1,16 @@ +--- +title: Create Profile +description: Create a new voice profile. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Create a new voice profile. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/delete_generation_history__generation_id__delete.mdx b/docs2/content/docs/api-reference/unknown/delete_generation_history__generation_id__delete.mdx new file mode 100644 index 00000000..3c729906 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/delete_generation_history__generation_id__delete.mdx @@ -0,0 +1,16 @@ +--- +title: Delete Generation +description: Delete a generation. +full: true +_openapi: + method: DELETE + toc: [] + structuredData: + headings: [] + contents: + - content: Delete a generation. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/delete_profile_profiles__profile_id__delete.mdx b/docs2/content/docs/api-reference/unknown/delete_profile_profiles__profile_id__delete.mdx new file mode 100644 index 00000000..fba980fc --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/delete_profile_profiles__profile_id__delete.mdx @@ -0,0 +1,16 @@ +--- +title: Delete Profile +description: Delete a voice profile. +full: true +_openapi: + method: DELETE + toc: [] + structuredData: + headings: [] + contents: + - content: Delete a voice profile. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/delete_profile_sample_profiles_samples__sample_id__delete.mdx b/docs2/content/docs/api-reference/unknown/delete_profile_sample_profiles_samples__sample_id__delete.mdx new file mode 100644 index 00000000..da28f2f3 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/delete_profile_sample_profiles_samples__sample_id__delete.mdx @@ -0,0 +1,16 @@ +--- +title: Delete Profile Sample +description: Delete a profile sample. +full: true +_openapi: + method: DELETE + toc: [] + structuredData: + headings: [] + contents: + - content: Delete a profile sample. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/generate_speech_generate_post.mdx b/docs2/content/docs/api-reference/unknown/generate_speech_generate_post.mdx new file mode 100644 index 00000000..c683704e --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/generate_speech_generate_post.mdx @@ -0,0 +1,16 @@ +--- +title: Generate Speech +description: Generate speech from text using a voice profile. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Generate speech from text using a voice profile. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_audio_audio__generation_id__get.mdx b/docs2/content/docs/api-reference/unknown/get_audio_audio__generation_id__get.mdx new file mode 100644 index 00000000..120e0643 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_audio_audio__generation_id__get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Audio +description: Serve generated audio file. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Serve generated audio file. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_generation_history__generation_id__get.mdx b/docs2/content/docs/api-reference/unknown/get_generation_history__generation_id__get.mdx new file mode 100644 index 00000000..57d06b78 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_generation_history__generation_id__get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Generation +description: Get a generation by ID. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Get a generation by ID. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_model_progress_models_progress__model_name__get.mdx b/docs2/content/docs/api-reference/unknown/get_model_progress_models_progress__model_name__get.mdx new file mode 100644 index 00000000..fbab8c0b --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_model_progress_models_progress__model_name__get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Model Progress +description: Get model download progress via Server-Sent Events. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Get model download progress via Server-Sent Events. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_model_status_models_status_get.mdx b/docs2/content/docs/api-reference/unknown/get_model_status_models_status_get.mdx new file mode 100644 index 00000000..89ae21e5 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_model_status_models_status_get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Model Status +description: Get status of all available models. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Get status of all available models. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_profile_profiles__profile_id__get.mdx b/docs2/content/docs/api-reference/unknown/get_profile_profiles__profile_id__get.mdx new file mode 100644 index 00000000..78292b77 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_profile_profiles__profile_id__get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Profile +description: Get a voice profile by ID. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Get a voice profile by ID. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_profile_samples_profiles__profile_id__samples_get.mdx b/docs2/content/docs/api-reference/unknown/get_profile_samples_profiles__profile_id__samples_get.mdx new file mode 100644 index 00000000..dd17e00b --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_profile_samples_profiles__profile_id__samples_get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Profile Samples +description: Get all samples for a profile. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Get all samples for a profile. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/get_stats_history_stats_get.mdx b/docs2/content/docs/api-reference/unknown/get_stats_history_stats_get.mdx new file mode 100644 index 00000000..cdcdcb77 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/get_stats_history_stats_get.mdx @@ -0,0 +1,16 @@ +--- +title: Get Stats +description: Get generation statistics. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Get generation statistics. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/health_health_get.mdx b/docs2/content/docs/api-reference/unknown/health_health_get.mdx new file mode 100644 index 00000000..36ec81c5 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/health_health_get.mdx @@ -0,0 +1,16 @@ +--- +title: Health +description: Health check endpoint. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Health check endpoint. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/list_history_history_get.mdx b/docs2/content/docs/api-reference/unknown/list_history_history_get.mdx new file mode 100644 index 00000000..74640f9d --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/list_history_history_get.mdx @@ -0,0 +1,16 @@ +--- +title: List History +description: List generation history with optional filters. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: List generation history with optional filters. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/list_profiles_profiles_get.mdx b/docs2/content/docs/api-reference/unknown/list_profiles_profiles_get.mdx new file mode 100644 index 00000000..d125c614 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/list_profiles_profiles_get.mdx @@ -0,0 +1,16 @@ +--- +title: List Profiles +description: List all voice profiles. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: List all voice profiles. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/load_model_models_load_post.mdx b/docs2/content/docs/api-reference/unknown/load_model_models_load_post.mdx new file mode 100644 index 00000000..1f11daba --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/load_model_models_load_post.mdx @@ -0,0 +1,16 @@ +--- +title: Load Model +description: Manually load TTS model. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Manually load TTS model. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/meta.json b/docs2/content/docs/api-reference/unknown/meta.json new file mode 100644 index 00000000..ddac4fd3 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/meta.json @@ -0,0 +1,27 @@ +{ + "title": "Endpoints", + "pages": [ + "root__get", + "health_health_get", + "list_profiles_profiles_get", + "create_profile_profiles_post", + "get_profile_profiles__profile_id__get", + "update_profile_profiles__profile_id__put", + "delete_profile_profiles__profile_id__delete", + "add_profile_sample_profiles__profile_id__samples_post", + "get_profile_samples_profiles__profile_id__samples_get", + "delete_profile_sample_profiles_samples__sample_id__delete", + "generate_speech_generate_post", + "list_history_history_get", + "get_generation_history__generation_id__get", + "delete_generation_history__generation_id__delete", + "get_stats_history_stats_get", + "transcribe_audio_transcribe_post", + "get_audio_audio__generation_id__get", + "load_model_models_load_post", + "unload_model_models_unload_post", + "get_model_progress_models_progress__model_name__get", + "get_model_status_models_status_get", + "trigger_model_download_models_download_post" + ] +} diff --git a/docs2/content/docs/api-reference/unknown/root__get.mdx b/docs2/content/docs/api-reference/unknown/root__get.mdx new file mode 100644 index 00000000..bb2bd1a8 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/root__get.mdx @@ -0,0 +1,16 @@ +--- +title: Root +description: Root endpoint. +full: true +_openapi: + method: GET + toc: [] + structuredData: + headings: [] + contents: + - content: Root endpoint. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/transcribe_audio_transcribe_post.mdx b/docs2/content/docs/api-reference/unknown/transcribe_audio_transcribe_post.mdx new file mode 100644 index 00000000..678e2e90 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/transcribe_audio_transcribe_post.mdx @@ -0,0 +1,16 @@ +--- +title: Transcribe Audio +description: Transcribe audio file to text. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Transcribe audio file to text. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/trigger_model_download_models_download_post.mdx b/docs2/content/docs/api-reference/unknown/trigger_model_download_models_download_post.mdx new file mode 100644 index 00000000..1e7a92bb --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/trigger_model_download_models_download_post.mdx @@ -0,0 +1,16 @@ +--- +title: Trigger Model Download +description: Trigger download of a specific model. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Trigger download of a specific model. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/unload_model_models_unload_post.mdx b/docs2/content/docs/api-reference/unknown/unload_model_models_unload_post.mdx new file mode 100644 index 00000000..29ff7a96 --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/unload_model_models_unload_post.mdx @@ -0,0 +1,16 @@ +--- +title: Unload Model +description: Unload TTS model to free memory. +full: true +_openapi: + method: POST + toc: [] + structuredData: + headings: [] + contents: + - content: Unload TTS model to free memory. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api-reference/unknown/update_profile_profiles__profile_id__put.mdx b/docs2/content/docs/api-reference/unknown/update_profile_profiles__profile_id__put.mdx new file mode 100644 index 00000000..8192d2ba --- /dev/null +++ b/docs2/content/docs/api-reference/unknown/update_profile_profiles__profile_id__put.mdx @@ -0,0 +1,16 @@ +--- +title: Update Profile +description: Update a voice profile. +full: true +_openapi: + method: PUT + toc: [] + structuredData: + headings: [] + contents: + - content: Update a voice profile. +--- + +{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} + + \ No newline at end of file diff --git a/docs2/content/docs/api/authentication.mdx b/docs2/content/docs/api/authentication.mdx new file mode 100644 index 00000000..e4e72808 --- /dev/null +++ b/docs2/content/docs/api/authentication.mdx @@ -0,0 +1,55 @@ +--- +title: "Authentication" +description: "API authentication and security" +--- + +## Current Status + + + Authentication is not currently implemented in Voicebox. The API is intended for local use only. + + +## Local Usage + +For local development and usage: +- API runs on `localhost:17493` +- No authentication required +- Access restricted to local machine + +## Future Implementation + +Authentication will be added in a future release for: +- Remote deployments +- Multi-user access +- Production environments + +Planned authentication methods: +- API keys +- OAuth 2.0 +- JWT tokens + +## Security Best Practices + +Until authentication is implemented: + + + + Use WireGuard or Tailscale for remote access + + + Run behind nginx with basic auth + + + Restrict access to trusted IPs only + + + Don't expose to public internet + + + +## Coming Soon + +- API key management +- User accounts +- Rate limiting +- Access control diff --git a/docs2/content/docs/api/generation.mdx b/docs2/content/docs/api/generation.mdx new file mode 100644 index 00000000..c9fff59c --- /dev/null +++ b/docs2/content/docs/api/generation.mdx @@ -0,0 +1,119 @@ +--- +title: "Generation API" +description: "Generate speech from text" +--- + +## Generate Speech + +```http +POST /generate +``` + +**Request:** +```json +{ + "text": "Hello world", + "profile_id": "abc123", + "language": "en" +} +``` + +**Response:** +```json +{ + "id": "gen123", + "text": "Hello world", + "profile_id": "abc123", + "language": "en", + "audio_url": "/audio/gen123.wav", + "duration": 2.3, + "created_at": "2024-01-29T12:00:00Z" +} +``` + +## List History + +```http +GET /history +``` + +**Query Parameters:** +- `profile_id` (optional) - Filter by voice profile +- `limit` (optional) - Number of results (default: 50) +- `offset` (optional) - Pagination offset + +**Response:** +```json +{ + "generations": [ + { + "id": "gen123", + "text": "Hello world", + "profile_id": "abc123", + "duration": 2.3, + "created_at": "2024-01-29T12:00:00Z" + } + ], + "total": 100 +} +``` + +## Get Generation + +```http +GET /history/{id} +``` + +**Response:** +```json +{ + "id": "gen123", + "text": "Hello world", + "profile_id": "abc123", + "language": "en", + "audio_url": "/audio/gen123.wav", + "duration": 2.3, + "created_at": "2024-01-29T12:00:00Z" +} +``` + +## Delete Generation + +```http +DELETE /history/{id} +``` + +**Response:** +```json +{ + "success": true +} +``` + +## TypeScript Example + +```typescript +import { VoiceboxClient } from '@/lib/api' + +const client = new VoiceboxClient({ + baseUrl: 'http://localhost:17493' +}) + +// Generate speech +const generation = await client.generate({ + text: 'Hello world', + profile_id: 'abc123', + language: 'en' +}) + +// Get audio URL +const audioUrl = generation.audio_url + +// List history +const history = await client.listHistory({ + profile_id: 'abc123', + limit: 20 +}) +``` + +For full API documentation, visit `http://localhost:17493/docs` when the server is running. diff --git a/docs2/content/docs/api/meta.json b/docs2/content/docs/api/meta.json new file mode 100644 index 00000000..154992f5 --- /dev/null +++ b/docs2/content/docs/api/meta.json @@ -0,0 +1,10 @@ +{ + "title": "API Reference", + "pages": [ + "overview", + "authentication", + "voice-profiles", + "generation", + "recordings" + ] +} diff --git a/docs2/content/docs/api/overview.mdx b/docs2/content/docs/api/overview.mdx new file mode 100644 index 00000000..c862a332 --- /dev/null +++ b/docs2/content/docs/api/overview.mdx @@ -0,0 +1,219 @@ +--- +title: "API Overview" +description: "Integrate voice synthesis into your applications with the Voicebox REST API" +--- + +## Introduction + +Voicebox exposes a full REST API that allows you to integrate voice synthesis into your own applications. The API runs on `http://localhost:17493` by default. + + + When Voicebox is running, visit the auto-generated API documentation at `http://localhost:17493/docs` + + +## Base URL + +``` +http://localhost:17493 +``` + +For remote deployments, replace `localhost` with your server's IP or hostname. + +## Authentication + + + Currently, the API does not require authentication for local development. Authentication will be added in a future release for production deployments. + + +## Quick Example + +Here's a simple example of generating speech: + +```bash +# Generate speech +curl -X POST http://localhost:17493/generate \ + -H "Content-Type: application/json" \ + -d '{ + "text": "Hello world", + "profile_id": "abc123", + "language": "en" + }' +``` + +## API Endpoints + +The Voicebox API is organized into several categories: + + + + Create, list, update, and delete voice profiles + + + Generate speech from text using voice profiles + + + Record and transcribe audio + + + Create and manage multi-voice stories (coming soon) + + + +## Core Endpoints + +### Voice Profiles + +```http +GET /profiles # List all profiles +POST /profiles # Create a new profile +GET /profiles/{id} # Get profile details +PUT /profiles/{id} # Update a profile +DELETE /profiles/{id} # Delete a profile +POST /profiles/{id}/samples # Add voice sample +``` + +### Generation + +```http +POST /generate # Generate speech +GET /history # List generation history +GET /history/{id} # Get generation details +DELETE /history/{id} # Delete from history +``` + +### Recordings + +```http +POST /recordings # Start recording +POST /recordings/stop # Stop recording +POST /transcribe # Transcribe audio +``` + +## Response Format + +All API responses follow a consistent JSON format: + +```json +{ + "success": true, + "data": { + // Response data + }, + "error": null +} +``` + +Error responses: + +```json +{ + "success": false, + "data": null, + "error": { + "message": "Error description", + "code": "ERROR_CODE" + } +} +``` + +## Data Models + +### Voice Profile + +```json +{ + "id": "abc123", + "name": "John Smith", + "language": "en", + "description": "Professional narrator voice", + "created_at": "2024-01-29T12:00:00Z", + "samples": [ + { + "id": "sample123", + "audio_path": "/path/to/sample.wav", + "duration": 15.5 + } + ] +} +``` + +### Generation + +```json +{ + "id": "gen123", + "text": "Hello world", + "profile_id": "abc123", + "language": "en", + "audio_path": "/path/to/output.wav", + "duration": 2.3, + "created_at": "2024-01-29T12:00:00Z" +} +``` + +## TypeScript Client + +Voicebox provides an auto-generated TypeScript client with full type safety: + +```typescript +import { VoiceboxClient } from '@/lib/api' + +const client = new VoiceboxClient({ + baseUrl: 'http://localhost:17493' +}) + +// Create a profile +const profile = await client.createProfile({ + name: 'John Smith', + language: 'en' +}) + +// Generate speech +const generation = await client.generate({ + text: 'Hello world', + profile_id: profile.id, + language: 'en' +}) +``` + +The client is automatically generated from the OpenAPI schema. See [Development Setup](/development/setup#generate-openapi-client) for details. + +## Rate Limiting + + + Currently, there are no rate limits for local usage. Rate limiting will be added in a future release for production deployments. + + +## WebSocket Support + + + Real-time streaming generation via WebSockets is planned for a future release. + + +## Use Cases + + + + Generate dynamic dialogue for NPCs and characters + + + Automate voiceovers for videos and podcasts + + + Build text-to-speech tools for visually impaired users + + + Create custom voice interfaces + + + +## Next Steps + + + + Learn how to manage voice profiles + + + Generate speech from text + + diff --git a/docs2/content/docs/api/recordings.mdx b/docs2/content/docs/api/recordings.mdx new file mode 100644 index 00000000..dd0552d2 --- /dev/null +++ b/docs2/content/docs/api/recordings.mdx @@ -0,0 +1,95 @@ +--- +title: "Recordings API" +description: "Record and transcribe audio" +--- + +## Start Recording + +```http +POST /recordings/start +``` + +**Request:** +```json +{ + "source": "microphone" +} +``` + +**Response:** +```json +{ + "recording_id": "rec123", + "status": "recording" +} +``` + +## Stop Recording + +```http +POST /recordings/stop +``` + +**Request:** +```json +{ + "recording_id": "rec123" +} +``` + +**Response:** +```json +{ + "recording_id": "rec123", + "audio_url": "/audio/rec123.wav", + "duration": 15.5 +} +``` + +## Transcribe Audio + +```http +POST /transcribe +``` + +**Request:** (multipart/form-data) +``` +audio: +language: "en" (optional) +``` + +**Response:** +```json +{ + "text": "Transcribed speech text here", + "language": "en", + "duration": 15.5, + "confidence": 0.95 +} +``` + +## TypeScript Example + +```typescript +import { VoiceboxClient } from '@/lib/api' + +const client = new VoiceboxClient({ + baseUrl: 'http://localhost:17493' +}) + +// Start recording +const recording = await client.startRecording({ + source: 'microphone' +}) + +// ... record audio ... + +// Stop recording +const result = await client.stopRecording(recording.id) + +// Transcribe +const transcription = await client.transcribe(audioFile, 'en') +console.log(transcription.text) +``` + +For full API documentation, visit `http://localhost:17493/docs` when the server is running. diff --git a/docs2/content/docs/api/voice-profiles.mdx b/docs2/content/docs/api/voice-profiles.mdx new file mode 100644 index 00000000..3715e44c --- /dev/null +++ b/docs2/content/docs/api/voice-profiles.mdx @@ -0,0 +1,149 @@ +--- +title: "Voice Profiles API" +description: "Manage voice profiles programmatically" +--- + +## Endpoints + +### List Profiles + +```http +GET /profiles +``` + +**Response:** +```json +{ + "profiles": [ + { + "id": "abc123", + "name": "John Smith", + "language": "en", + "description": "Professional narrator", + "created_at": "2024-01-29T12:00:00Z", + "sample_count": 2 + } + ] +} +``` + +### Get Profile + +```http +GET /profiles/{id} +``` + +**Response:** +```json +{ + "id": "abc123", + "name": "John Smith", + "language": "en", + "description": "Professional narrator", + "created_at": "2024-01-29T12:00:00Z", + "samples": [ + { + "id": "sample123", + "duration": 15.5, + "created_at": "2024-01-29T12:00:00Z" + } + ] +} +``` + +### Create Profile + +```http +POST /profiles +``` + +**Request:** +```json +{ + "name": "John Smith", + "language": "en", + "description": "Professional narrator" +} +``` + +**Response:** +```json +{ + "id": "abc123", + "name": "John Smith", + "language": "en", + "description": "Professional narrator", + "created_at": "2024-01-29T12:00:00Z" +} +``` + +### Update Profile + +```http +PUT /profiles/{id} +``` + +**Request:** +```json +{ + "name": "Updated Name", + "description": "Updated description" +} +``` + +### Delete Profile + +```http +DELETE /profiles/{id} +``` + +**Response:** +```json +{ + "success": true +} +``` + +### Add Voice Sample + +```http +POST /profiles/{id}/samples +``` + +**Request:** (multipart/form-data) +``` +audio: +``` + +**Response:** +```json +{ + "sample_id": "sample123", + "duration": 15.5 +} +``` + +## TypeScript Example + +```typescript +import { VoiceboxClient } from '@/lib/api' + +const client = new VoiceboxClient({ + baseUrl: 'http://localhost:17493' +}) + +// Create profile +const profile = await client.createProfile({ + name: 'John Smith', + language: 'en', + description: 'Professional narrator' +}) + +// Add sample +await client.addSample(profile.id, audioFile) + +// List all profiles +const profiles = await client.listProfiles() +``` + +For full API documentation, visit `http://localhost:17493/docs` when the server is running. diff --git a/docs2/content/docs/developer/architecture.mdx b/docs2/content/docs/developer/architecture.mdx new file mode 100644 index 00000000..367d566a --- /dev/null +++ b/docs2/content/docs/developer/architecture.mdx @@ -0,0 +1,206 @@ +--- +title: "Architecture" +description: "Understanding Voicebox's technical architecture" +--- + +## 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: + +**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 provides the REST API, runs the TTS engine (Qwen3-TTS), manages the SQLite database, and handles audio processing. + +These two layers communicate via HTTP, with the frontend making API requests to the backend. + +## Frontend Architecture + +### Tech Stack + +- **Framework**: React 18 with TypeScript +- **State Management**: Zustand stores +- **Data Fetching**: React Query (TanStack Query) +- **Styling**: Tailwind CSS +- **Audio**: WaveSurfer.js +- **Desktop**: Tauri (Rust) + +### Component Structure + +``` +app/src/ +├── components/ # React components +│ ├── profiles/ # Voice profile UI +│ ├── generation/ # Speech generation UI +│ ├── stories/ # Timeline editor +│ └── shared/ # Reusable components +├── lib/ # Utilities +│ ├── api/ # Generated API client +│ └── utils/ # Helper functions +├── hooks/ # React hooks +└── stores/ # Zustand state stores +``` + +### State Management + +```typescript +// Example: Profile store +const useProfileStore = create((set) => ({ + profiles: [], + selectedProfile: null, + setProfiles: (profiles) => set({ profiles }), + selectProfile: (id) => set({ selectedProfile: id }) +})) +``` + +## Backend Architecture + +### Tech Stack + +- **Framework**: FastAPI (Python 3.11+) +- **TTS Model**: Qwen3-TTS +- **Transcription**: Whisper +- **Database**: SQLite +- **Audio**: librosa, soundfile + +### API Structure + +```python +# main.py - API routes +@app.post("/generate") +async def generate_speech(request: GenerateRequest): + # 1. Validate request + # 2. Load voice profile + # 3. Generate audio with TTS + # 4. Save to database + # 5. Return response +``` + +### Data Model + +The database uses three main tables: + +**Profile Table:** Stores voice profiles with fields for id, name, and language. + +**Sample Table:** Stores audio samples linked to profiles via profile_id, with fields for audio_path and duration. + +**Generation Table:** Stores generated audio with fields for id, profile_id, text, and audio_path. + +## Desktop App (Tauri) + +### Rust Backend + +```rust +// Sidecar process management +// File system access +// Native integrations +``` + +### Responsibilities + +- Launch Python backend as sidecar process +- Native file dialogs +- System tray integration +- Auto-updates +- OS-specific features + +## Build Process + +### Development + +```bash +# Frontend (Vite dev server) +cd app && bun run dev + +# Backend (manual start) +cd backend && uvicorn main:app --reload + +# Desktop app (connects to manual backend) +bun run dev +``` + +### Production + +```bash +# Build everything (server binary + Tauri app) +bun run build + +# Or build separately: +# 1. Build server binary (PyInstaller) +bun run build:server + +# 2. Build Tauri app (includes server) +cd tauri && bun run tauri build +``` + +## Data Flow + +### Generation Flow + +When a user generates speech, the data flows through the following stages: + +1. **User Input** - User enters text in a React component +2. **State Update** - Text is stored in Zustand state +3. **API Request** - React Query mutation triggers an API call via fetch +4. **Backend Processing** - FastAPI endpoint receives the request +5. **TTS Generation** - Qwen3-TTS model generates the audio +6. **Storage** - Audio file is saved to disk and a database record is created +7. **Response** - Backend returns the audio URL +8. **Cache Update** - React Query updates its cache with the response +9. **UI Update** - Component re-renders with new data +10. **Playback** - User can play the generated audio + +## Performance Considerations + +### Frontend + +- **Code splitting** - Lazy load routes +- **Memoization** - React.memo for heavy components +- **Virtual scrolling** - For large lists +- **Debouncing** - Search and input handling + +### Backend + +- **Async operations** - All I/O is async +- **Model caching** - Keep TTS model in memory +- **Voice prompt caching** - Reuse embeddings +- **Connection pooling** - Database connections + +## Security + +### Current + +- Local-only by default +- No authentication (localhost trust) +- File system sandboxing via Tauri + +### Planned + +- API key authentication +- User accounts +- Rate limiting +- HTTPS support + +## Deployment Modes + +### Local Mode + +- Backend runs as sidecar +- All data stays on device +- No network required + +### Remote Mode + +- Backend on separate machine +- Frontend connects via HTTP +- Shared infrastructure possible + +## Next Steps + + + + Set up your dev environment + + + Contribute to Voicebox + + diff --git a/docs2/content/docs/developer/audio-channels.mdx b/docs2/content/docs/developer/audio-channels.mdx new file mode 100644 index 00000000..6fce8164 --- /dev/null +++ b/docs2/content/docs/developer/audio-channels.mdx @@ -0,0 +1,310 @@ +--- +title: "Audio Channels" +description: "How audio output routing works in Voicebox" +--- + +## Overview + +Audio channels allow routing voice output to different audio devices. This is useful for multi-output setups where different voices should play through different speakers or applications. + +## Architecture + +**Channel:** A named audio bus that can be assigned to output devices. + +**Device Mapping:** Links channels to OS audio device identifiers. + +**Profile Mapping:** Links voice profiles to channels (many-to-many). + +## Data Model + +### AudioChannel Table + +```python +class AudioChannel(Base): + __tablename__ = "audio_channels" + + id = Column(String, primary_key=True) + name = Column(String, nullable=False) + is_default = Column(Boolean, default=False) + created_at = Column(DateTime) +``` + +### ChannelDeviceMapping Table + +```python +class ChannelDeviceMapping(Base): + __tablename__ = "channel_device_mappings" + + id = Column(String, primary_key=True) + channel_id = Column(String, ForeignKey("audio_channels.id")) + device_id = Column(String) # OS device identifier +``` + +### ProfileChannelMapping Table + +```python +class ProfileChannelMapping(Base): + __tablename__ = "profile_channel_mappings" + + profile_id = Column(String, ForeignKey("profiles.id"), primary_key=True) + channel_id = Column(String, ForeignKey("audio_channels.id"), primary_key=True) +``` + +## Default Channel + +A default channel is created on database initialization: + +```python +def init_db(): + # Create default channel if it doesn't exist + default_channel = db.query(AudioChannel).filter( + AudioChannel.is_default == True + ).first() + + if not default_channel: + default_channel = AudioChannel( + id=str(uuid.uuid4()), + name="Default", + is_default=True + ) + db.add(default_channel) + + # Assign all existing profiles to default channel + profiles = db.query(VoiceProfile).all() + for profile in profiles: + mapping = ProfileChannelMapping( + profile_id=profile.id, + channel_id=default_channel.id + ) + db.add(mapping) +``` + +## Core Operations + +### Creating a Channel + +```python +async def create_channel( + data: AudioChannelCreate, + db: Session, +) -> AudioChannelResponse: + # Check name uniqueness + existing = db.query(DBAudioChannel).filter_by(name=data.name).first() + if existing: + raise ValueError(f"Channel with name '{data.name}' already exists") + + # Create channel + channel = DBAudioChannel( + id=str(uuid.uuid4()), + name=data.name, + is_default=False, + ) + db.add(channel) + + # Add device mappings + for device_id in data.device_ids: + mapping = DBChannelDeviceMapping( + id=str(uuid.uuid4()), + channel_id=channel.id, + device_id=device_id, + ) + db.add(mapping) + + db.commit() +``` + +### Updating a Channel + +```python +async def update_channel( + channel_id: str, + data: AudioChannelUpdate, + db: Session, +) -> AudioChannelResponse: + channel = db.query(DBAudioChannel).filter_by(id=channel_id).first() + + # Cannot modify default channel + if channel.is_default: + raise ValueError("Cannot modify the default channel") + + # Update name + if data.name is not None: + channel.name = data.name + + # Update device mappings + if data.device_ids is not None: + # Delete existing + db.query(DBChannelDeviceMapping).filter_by(channel_id=channel_id).delete() + + # Add new + for device_id in data.device_ids: + mapping = DBChannelDeviceMapping( + channel_id=channel.id, + device_id=device_id, + ) + db.add(mapping) + + db.commit() +``` + +### Deleting a Channel + +```python +async def delete_channel(channel_id: str, db: Session) -> bool: + channel = db.query(DBAudioChannel).filter_by(id=channel_id).first() + + # Cannot delete default channel + if channel.is_default: + raise ValueError("Cannot delete the default channel") + + # Delete device mappings + db.query(DBChannelDeviceMapping).filter_by(channel_id=channel_id).delete() + + # Delete profile-channel mappings + db.query(DBProfileChannelMapping).filter_by(channel_id=channel_id).delete() + + # Delete channel + db.delete(channel) + db.commit() +``` + +## Voice Assignment + +### Assigning Voices to Channel + +```python +async def set_channel_voices( + channel_id: str, + data: ChannelVoiceAssignment, + db: Session, +) -> None: + # Verify channel exists + channel = db.query(DBAudioChannel).filter_by(id=channel_id).first() + if not channel: + raise ValueError(f"Channel {channel_id} not found") + + # Verify all profiles exist + for profile_id in data.profile_ids: + profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first() + if not profile: + raise ValueError(f"Profile {profile_id} not found") + + # Delete existing mappings + db.query(DBProfileChannelMapping).filter_by(channel_id=channel_id).delete() + + # Add new mappings + for profile_id in data.profile_ids: + mapping = DBProfileChannelMapping( + profile_id=profile_id, + channel_id=channel_id, + ) + db.add(mapping) + + db.commit() +``` + +### Assigning Channels to Voice + +```python +async def set_profile_channels( + profile_id: str, + data: ProfileChannelAssignment, + db: Session, +) -> None: + # Verify profile exists + profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first() + if not profile: + raise ValueError(f"Profile {profile_id} not found") + + # Delete existing mappings + db.query(DBProfileChannelMapping).filter_by(profile_id=profile_id).delete() + + # Add new mappings + for channel_id in data.channel_ids: + mapping = DBProfileChannelMapping( + profile_id=profile_id, + channel_id=channel_id, + ) + db.add(mapping) + + db.commit() +``` + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/channels` | List all channels | +| POST | `/channels` | Create a channel | +| GET | `/channels/{id}` | Get channel by ID | +| PUT | `/channels/{id}` | Update channel | +| DELETE | `/channels/{id}` | Delete channel | +| GET | `/channels/{id}/voices` | Get assigned voices | +| PUT | `/channels/{id}/voices` | Set assigned voices | +| GET | `/profiles/{id}/channels` | Get profile's channels | +| PUT | `/profiles/{id}/channels` | Set profile's channels | + +## Request/Response Schemas + +### AudioChannelCreate + +```json +{ + "name": "Speakers", + "device_ids": ["device_uuid_1", "device_uuid_2"] +} +``` + +### AudioChannelResponse + +```json +{ + "id": "channel_uuid", + "name": "Speakers", + "is_default": false, + "device_ids": ["device_uuid_1", "device_uuid_2"], + "created_at": "2024-01-15T10:30:00Z" +} +``` + +### ChannelVoiceAssignment + +```json +{ + "profile_ids": ["profile_1", "profile_2"] +} +``` + +## Use Cases + +### Multi-Output Setup + +**Scenario:** Stream with different voice characters + +1. Create "Stream" channel → OBS virtual audio +2. Create "Monitor" channel → Headphones +3. Assign "Narrator" profile → Both channels +4. Assign "Character 1" profile → Stream only + +### Virtual Audio Cables + +Common device IDs for virtual audio: +- VB-Audio Virtual Cable +- BlackHole (macOS) +- Soundflower (macOS) + +## Frontend Integration + +The frontend needs to: + +1. **Enumerate devices** using Web Audio API or Tauri +2. **Display channel list** with device assignments +3. **Allow profile assignment** via drag/drop or dropdown +4. **Route playback** to correct device based on profile's channel + +## Limitations + +- Device IDs are OS-specific +- Hot-plugging may invalidate device IDs +- Default channel cannot be modified/deleted +- Frontend handles actual audio routing (backend just stores config) diff --git a/docs2/content/docs/developer/autoupdater.mdx b/docs2/content/docs/developer/autoupdater.mdx new file mode 100644 index 00000000..b483f153 --- /dev/null +++ b/docs2/content/docs/developer/autoupdater.mdx @@ -0,0 +1,84 @@ +--- +title: "Auto-Updater" +description: "Configure and use the Tauri auto-updater" +--- + +## Overview + +Voicebox uses Tauri's built-in auto-updater to deliver updates to users automatically. + +## Quick Reference + +For detailed setup instructions, see the existing documentation: + +- [AUTOUPDATER_QUICKSTART.md](https://github.com/jamiepine/voicebox/blob/main/docs/AUTOUPDATER_QUICKSTART.md) +- [AUTOUPDATER.md](https://github.com/jamiepine/voicebox/blob/main/docs/AUTOUPDATER.md) + +## How It Works + +The auto-updater follows a secure update process: + +1. **Check for Updates** - The Voicebox app periodically checks GitHub Releases for new versions +2. **Download Update** - If a new version is found, the update package is downloaded +3. **Verify Signature** - The downloaded package is cryptographically verified using the public key +4. **Install** - After verification, the update is installed +5. **Restart** - The app restarts with the new version + +## Configuration + +Updates are configured in `tauri/src-tauri/tauri.conf.json`: + +```json +{ + "updater": { + "active": true, + "endpoints": [ + "https://github.com/jamiepine/voicebox/releases/latest/download/latest.json" + ], + "dialog": true, + "pubkey": "YOUR_PUBLIC_KEY" + } +} +``` + +## Generating Keys + +```bash +# Generate signing keys +bun run generate:keys + +# Keys saved to ~/.tauri/voicebox.key +``` + + + Keep your private key secure! Never commit it to the repository. + + +## Release Process + +1. **Bump version** using bumpversion +2. **Push tag** to trigger CI/CD +3. **GitHub Actions** builds and signs releases +4. **Users** receive update notification + +## User Experience + +When an update is available: + +1. User sees a notification dialog +2. User clicks "Update" +3. Update downloads in background +4. App restarts with new version + +## For Developers + +See the full documentation files for: + +- Setting up signing keys +- Configuring GitHub releases +- Testing updates locally +- Troubleshooting update failures + + + Access AUTOUPDATER.md and AUTOUPDATER_QUICKSTART.md in the repository + diff --git a/docs2/content/docs/developer/building.mdx b/docs2/content/docs/developer/building.mdx new file mode 100644 index 00000000..237805b9 --- /dev/null +++ b/docs2/content/docs/developer/building.mdx @@ -0,0 +1,270 @@ +--- +title: "Building" +description: "Build Voicebox for production" +--- + +## Overview + +Voicebox uses a multi-step build process to create platform-specific installers. + +## Quick Build + +```bash +# Build for your current platform (automatically builds server binary first) +make build + +# Or manually +bun run build +``` + +This automatically: +1. Builds the Python server binary (`bun run build:server`) +2. Builds the Tauri app (`cd tauri && bun run tauri build`) + +## Build Process + +The build process consists of two steps, but `bun run build` handles both automatically: + +### 1. Server Binary Build (Automatic) + +The Python backend is compiled into a standalone executable using PyInstaller. This happens automatically when you run `bun run build`. + +**Platform-specific binaries:** +- macOS (Apple Silicon): `voicebox-server-aarch64-apple-darwin` (includes MLX backend) +- macOS (Intel): `voicebox-server-x86_64-apple-darwin` (PyTorch backend) +- Windows: `voicebox-server-x86_64-pc-windows-msvc.exe` (PyTorch backend) +- Linux: `voicebox-server-x86_64-unknown-linux-gnu` (PyTorch backend) + + + The build script automatically detects your platform and includes the appropriate backend (MLX for Apple Silicon, PyTorch for others). + + +**Manual build (if needed):** +```bash +bun run build:server +``` + +### 2. Tauri App Build (Automatic) + +The Tauri app build is also handled automatically, which: +1. Builds the React frontend (Vite) +2. Compiles the Rust backend +3. Bundles the server binary as a sidecar +4. Creates platform-specific installers + +**Manual build (if needed):** +```bash +cd tauri && bun run tauri build +``` + +### 3. Output + +Installers are created in `tauri/src-tauri/target/release/bundle/`: + +**macOS:** +- `dmg/` - Disk image installer +- `macos/` - App bundle + +**Windows:** +- `msi/` - MSI installer +- `nsis/` - NSIS installer + +**Linux:** +- `deb/` - Debian package +- `appimage/` - AppImage + +## Advanced Options + +### Building for Specific Platform + +```bash +# Build for macOS (Apple Silicon) +bun run tauri build -- --target aarch64-apple-darwin + +# Build for macOS (Intel) +bun run tauri build -- --target x86_64-apple-darwin + +# Build for Windows +bun run tauri build -- --target x86_64-pc-windows-msvc + +# Build for Linux +bun run tauri build -- --target x86_64-unknown-linux-gnu +``` + +### Using Local Qwen3-TTS + +If you're developing Qwen3-TTS locally: + +```bash +export QWEN_TTS_PATH=~/path/to/Qwen3-TTS +bun run build:server # Build server binary only +# or +bun run build # Build everything +``` + +This makes PyInstaller use your local version instead of the pip package. + +### Debug Build + +```bash +cd tauri +bun run tauri build --debug +``` + +Creates a debug build with symbols and logging. + +## Build Configuration + +### Tauri Config + +Edit `tauri/src-tauri/tauri.conf.json`: + +```json +{ + "bundle": { + "identifier": "com.voicebox.app", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} +``` + +### Sidecar Configuration + +The Python server is bundled as a sidecar: + +```json +{ + "tauri": { + "bundle": { + "externalBin": [ + "binaries/voicebox-server" + ] + } + } +} +``` + +## Code Signing + +### macOS + +To sign the app for distribution: + +```bash +# Set signing identity +export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name" + +# Build with signing +bun run tauri build +``` + +For notarization: + +```bash +# Set credentials +export APPLE_ID="your@email.com" +export APPLE_PASSWORD="app-specific-password" + +# Build and notarize +bun run tauri build +``` + +### Windows + +For Windows code signing: + +```bash +# Set certificate +export WINDOWS_CERTIFICATE_PATH="/path/to/cert.pfx" +export WINDOWS_CERTIFICATE_PASSWORD="password" + +# Build with signing +bun run tauri build +``` + +## Release Process + +The full release process is automated: + +```bash +# 1. Bump version +bumpversion patch # or minor/major + +# 2. Build all platforms (CI/CD handles this) +git push --tags + +# 3. GitHub Actions creates releases +``` + +See [CONTRIBUTING.md](/development/contributing) for the full release workflow. + +## Troubleshooting + + + + **Common issues:** + - Missing Python dependencies: `pip install -r requirements.txt` + - PyInstaller not found: `pip install pyinstaller` + - Qwen3-TTS not installed: `pip install git+https://github.com/QwenLM/Qwen3-TTS.git` + + **Solution:** + ```bash + cd backend + source venv/bin/activate + pip install -r requirements.txt + pip install pyinstaller + ``` + + + + **Common issues:** + - Rust not installed: `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` + - Server binary missing: Usually auto-built, but can run manually: `./scripts/build-server.sh` + - Node modules outdated: `bun install` + + **Solution:** + ```bash + # Clean and rebuild + cd tauri/src-tauri + cargo clean + cd ../.. + bun run build # Automatically builds server binary first + ``` + + + + **Check:** + - Server binary has execute permissions + - All dependencies are bundled + - Check logs in the app's data directory + + **macOS:** + ```bash + tail -f ~/Library/Application\ Support/com.voicebox.app/logs/server.log + ``` + + **Windows:** + ```bash + type %APPDATA%\com.voicebox.app\logs\server.log + ``` + + + +## CI/CD + +GitHub Actions automatically builds releases when tags are pushed: + +```yaml +# .github/workflows/release.yml +on: + push: + tags: + - 'v*' +``` + +See the [repository](https://github.com/jamiepine/voicebox) for the full CI/CD configuration. diff --git a/docs2/content/docs/developer/contributing.mdx b/docs2/content/docs/developer/contributing.mdx new file mode 100644 index 00000000..d67a2f4b --- /dev/null +++ b/docs2/content/docs/developer/contributing.mdx @@ -0,0 +1,326 @@ +--- +title: "Contributing" +description: "How to contribute to Voicebox" +--- + +Thank you for your interest in contributing to Voicebox! This guide will help you get started. + +## Code of Conduct + +- Be respectful and inclusive +- Welcome newcomers and help them learn +- Focus on constructive feedback +- Respect different viewpoints and experiences + +## Getting Started + +Before you start contributing, make sure you have: + +1. **Read the documentation** to understand how Voicebox works +2. **Set up your development environment** - see [Development Setup](/development/setup) +3. **Explored the codebase** to understand the project structure +4. **Checked existing issues** to see if someone else is working on something similar + +## Ways to Contribute + + + + Found a bug? Open an issue with reproduction steps + + + Have an idea? Start a discussion or open an issue + + + Fix typos, add examples, or clarify instructions + + + Fix bugs, add features, or optimize performance + + + +## Development Workflow + +### 1. Fork & Clone + +```bash +# Fork the repository on GitHub +# Then clone your fork +git clone https://github.com/YOUR_USERNAME/voicebox.git +cd voicebox +``` + +### 2. Create a Branch + +Use descriptive branch names: + +```bash +# For features +git checkout -b feature/voice-effects + +# For bug fixes +git checkout -b fix/audio-playback-issue + +# For documentation +git checkout -b docs/api-examples +``` + +### 3. Make Your Changes + +Follow these guidelines: + + + + **TypeScript/React:** + - Use TypeScript strict mode + - Prefer functional components with hooks + - Use named exports + - Format with Biome (runs automatically) + + **Python:** + - Follow PEP 8 + - Use type hints + - Use async/await for I/O + - Document functions with docstrings + + **Rust:** + - Follow Rust conventions + - Use meaningful names + - Handle errors explicitly + - Run `rustfmt` + + + + Write clear, descriptive commit messages: + + ```bash + # Good + git commit -m "Add voice profile export feature" + git commit -m "Fix audio playback stopping after 30 seconds" + + # Avoid + git commit -m "Update code" + git commit -m "Fix bug" + ``` + + Format: + - Use imperative mood ("Add feature" not "Added feature") + - Keep first line under 50 characters + - Add detailed description if needed + + + + - Test your changes manually in the app + - Ensure backend API endpoints work + - Check for TypeScript/Python errors + - Verify UI components render correctly + - Add automated tests when possible + + + +### 4. Push & Create PR + +```bash +# Push your branch +git push origin feature/your-feature-name + +# Then create a pull request on GitHub +``` + +## Pull Request Guidelines + +When creating a pull request: + + + + Examples: + - "Add voice profile export functionality" + - "Fix audio playback stopping after 30 seconds" + - "Improve generation speed with caching" + + + + Include: + - What changes you made + - Why you made them + - How to test them + - Screenshots (for UI changes) + - Reference related issues + + + + - Update relevant docs if behavior changes + - Add API documentation for new endpoints + - Update README if needed + + + + - [ ] Code follows style guidelines + - [ ] Documentation updated + - [ ] Changes tested + - [ ] No breaking changes (or documented) + - [ ] CHANGELOG.md updated + + + +## Project Structure + +Understanding the codebase: + +``` +voicebox/ +├── app/ # Shared React frontend +│ ├── src/ +│ │ ├── components/ # UI components +│ │ ├── lib/ # Utilities and API client +│ │ ├── hooks/ # React hooks +│ │ └── stores/ # Zustand state stores +├── backend/ # Python FastAPI server +│ ├── main.py # API routes +│ ├── tts.py # Voice synthesis logic +│ ├── database.py # SQLite operations +│ └── models.py # Pydantic models +├── tauri/ # Desktop app wrapper +│ └── src-tauri/ # Rust backend +├── web/ # Web deployment +├── landing/ # Marketing website +└── scripts/ # Build & release scripts +``` + +## Areas for Contribution + +### Bug Fixes + +- Check [existing issues](https://github.com/jamiepine/voicebox/issues) for bugs +- Test your fix thoroughly +- Add regression tests if possible + +### New Features + +- Check the [roadmap](https://github.com/jamiepine/voicebox#roadmap) for planned features +- Discuss major features in an issue first +- Keep features focused and well-scoped + +### Documentation + +- Improve clarity and fix typos +- Add code examples +- Create tutorials or guides +- Document API endpoints + +### UI/UX Improvements + +- Improve accessibility +- Enhance visual design +- Optimize performance +- Add animations/transitions + +### Infrastructure + +- Improve build process +- Add CI/CD improvements +- Optimize bundle size +- Add testing infrastructure + +## API Development + +When adding new API endpoints: + + + + In `backend/main.py`: + + ```python + @app.post("/api/new-endpoint") + async def new_endpoint(data: RequestModel) -> ResponseModel: + """Endpoint description.""" + # Implementation + return response + ``` + + + + In `backend/models.py`: + + ```python + class RequestModel(BaseModel): + field: str + + class ResponseModel(BaseModel): + result: str + ``` + + + + ```bash + bun run generate:api + ``` + + This updates the TypeScript client with type-safe bindings. + + + + Add documentation in `/docs/api/` + + + +## Testing + +Currently testing is primarily manual. When adding tests: + +**Backend:** +```bash +cd backend +pytest +``` + +**Frontend:** +```bash +bun run test +``` + +**E2E (future):** +```bash +bun run test:e2e +``` + +## Release Process + +Releases are managed by maintainers using `bumpversion`: + +```bash +# Bump version (patch, minor, or major) +bumpversion patch + +# Push with tags +git push && git push --tags +``` + +GitHub Actions automatically builds and publishes releases when tags are pushed. + +## Community + +- **GitHub Issues:** Bug reports and feature requests +- **GitHub Discussions:** General questions and ideas +- **Discord:** Real-time chat (coming soon) + +## Recognition + +Contributors are recognized in: +- [CHANGELOG.md](https://github.com/jamiepine/voicebox/blob/main/CHANGELOG.md) +- GitHub contributor list +- Release notes + +## License + +By contributing, you agree that your contributions will be licensed under the MIT License. + +## Questions? + +If you have questions: + +1. Check the [documentation](/overview/introduction) +2. Search [existing issues](https://github.com/jamiepine/voicebox/issues) +3. Open a new issue or discussion +4. See [CONTRIBUTING.md](https://github.com/jamiepine/voicebox/blob/main/CONTRIBUTING.md) in the repo + +Thank you for contributing to Voicebox! 🎉 diff --git a/docs2/content/docs/developer/history.mdx b/docs2/content/docs/developer/history.mdx new file mode 100644 index 00000000..28fe7faf --- /dev/null +++ b/docs2/content/docs/developer/history.mdx @@ -0,0 +1,260 @@ +--- +title: "Generation History" +description: "How generation history tracking works in Voicebox" +--- + +## Overview + +The history module tracks all generated audio, providing a searchable record of past generations. Each generation stores the text, settings, and a reference to the audio file. + +## Data Model + +### Generation Table + +```python +class Generation(Base): + __tablename__ = "generations" + + id = Column(String, primary_key=True) + profile_id = Column(String, ForeignKey("profiles.id")) + text = Column(Text, nullable=False) + language = Column(String, default="en") + audio_path = Column(String, nullable=False) + duration = Column(Float, nullable=False) + seed = Column(Integer) + instruct = Column(Text) + created_at = Column(DateTime) +``` + +## File Storage + +Generated audio is stored in: + +``` +data/ +└── generations/ + └── {generation_id}.wav +``` + +## Core Functions + +### Creating a Generation Record + +After TTS generates audio, a history entry is created: + +```python +async def create_generation( + profile_id: str, + text: str, + language: str, + audio_path: str, + duration: float, + seed: Optional[int], + db: Session, + instruct: Optional[str] = None, +) -> GenerationResponse: + db_generation = DBGeneration( + id=str(uuid.uuid4()), + profile_id=profile_id, + text=text, + language=language, + audio_path=audio_path, + duration=duration, + seed=seed, + instruct=instruct, + created_at=datetime.utcnow(), + ) + + db.add(db_generation) + db.commit() + + return GenerationResponse.model_validate(db_generation) +``` + +### Listing Generations + +Supports filtering and pagination: + +```python +async def list_generations( + query: HistoryQuery, + db: Session, +) -> HistoryListResponse: + # Build query with profile name join + q = db.query( + DBGeneration, + DBVoiceProfile.name.label('profile_name') + ).join( + DBVoiceProfile, + DBGeneration.profile_id == DBVoiceProfile.id + ) + + # Apply filters + if query.profile_id: + q = q.filter(DBGeneration.profile_id == query.profile_id) + + if query.search: + q = q.filter(DBGeneration.text.like(f"%{query.search}%")) + + # Order and paginate + total = q.count() + q = q.order_by(DBGeneration.created_at.desc()) + q = q.offset(query.offset).limit(query.limit) + + return HistoryListResponse(items=results, total=total) +``` + +### Getting Statistics + +Aggregate statistics for the dashboard: + +```python +async def get_generation_stats(db: Session) -> dict: + total = db.query(func.count(DBGeneration.id)).scalar() + total_duration = db.query(func.sum(DBGeneration.duration)).scalar() + + by_profile = db.query( + DBGeneration.profile_id, + func.count(DBGeneration.id).label('count') + ).group_by(DBGeneration.profile_id).all() + + return { + "total_generations": total, + "total_duration_seconds": total_duration, + "generations_by_profile": { + profile_id: count for profile_id, count in by_profile + }, + } +``` + +## Deletion + +Deleting a generation removes both the database record and audio file: + +```python +async def delete_generation(generation_id: str, db: Session) -> bool: + generation = db.query(DBGeneration).filter_by(id=generation_id).first() + if not generation: + return False + + # Delete audio file + audio_path = Path(generation.audio_path) + if audio_path.exists(): + audio_path.unlink() + + # Delete database record + db.delete(generation) + db.commit() + + return True +``` + +### Cascade Delete + +When deleting a profile, all its generations are also deleted: + +```python +async def delete_generations_by_profile(profile_id: str, db: Session) -> int: + generations = db.query(DBGeneration).filter_by(profile_id=profile_id).all() + + for generation in generations: + Path(generation.audio_path).unlink(missing_ok=True) + db.delete(generation) + + db.commit() + return len(generations) +``` + +## Export/Import + +### Exporting a Generation + +Generations can be exported as ZIP archives: + +``` +generation_export.zip +├── generation.json # Metadata +└── audio.wav # Audio file +``` + +### Importing a Generation + +The import process: + +1. Extract ZIP archive +2. Validate metadata and audio +3. Create new generation ID +4. Copy audio to generations directory +5. Create database record + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/history` | List generations with filters | +| GET | `/history/stats` | Get aggregate statistics | +| GET | `/history/{id}` | Get generation by ID | +| DELETE | `/history/{id}` | Delete generation | +| GET | `/history/{id}/export` | Export as ZIP | +| GET | `/history/{id}/export-audio` | Export audio only | +| POST | `/history/import` | Import from ZIP | + +### Query Parameters + +``` +GET /history?profile_id=uuid&search=hello&limit=50&offset=0 +``` + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `profile_id` | string | null | Filter by profile | +| `search` | string | null | Search in text | +| `limit` | int | 50 | Results per page | +| `offset` | int | 0 | Pagination offset | + +### Response Schema + +```json +{ + "items": [ + { + "id": "uuid", + "profile_id": "uuid", + "profile_name": "My Voice", + "text": "Hello world", + "language": "en", + "audio_path": "/path/to/audio.wav", + "duration": 1.5, + "seed": 42, + "instruct": null, + "created_at": "2024-01-15T10:30:00Z" + } + ], + "total": 150 +} +``` + +## Usage in Stories + +Generations can be added to stories for multi-voice narratives. The story system references generations by ID: + +```python +class StoryItem(Base): + generation_id = Column(String, ForeignKey("generations.id")) +``` + +This allows the same generation to be reused across multiple stories without duplicating audio files. + +## Storage Considerations + +### Disk Usage + +Each generation creates a WAV file. For a 10-second clip at 24kHz: +- ~480KB per file (mono, 16-bit) + +### Cleanup Strategy + +Consider implementing: +- Automatic cleanup of old generations +- Storage quota per profile +- Compression for archival diff --git a/docs2/content/docs/developer/meta.json b/docs2/content/docs/developer/meta.json new file mode 100644 index 00000000..5c97f225 --- /dev/null +++ b/docs2/content/docs/developer/meta.json @@ -0,0 +1,17 @@ +{ + "title": "Developer", + "pages": [ + "setup", + "architecture", + "contributing", + "building", + "autoupdater", + "voice-profiles", + "tts-generation", + "history", + "stories", + "transcription", + "audio-channels", + "model-management" + ] +} diff --git a/docs2/content/docs/developer/model-management.mdx b/docs2/content/docs/developer/model-management.mdx new file mode 100644 index 00000000..ef9f6c1d --- /dev/null +++ b/docs2/content/docs/developer/model-management.mdx @@ -0,0 +1,341 @@ +--- +title: "Model Management" +description: "How model downloading, loading, and status tracking works in Voicebox" +--- + +## Overview + +Voicebox manages two types of models: + +**TTS Models:** Qwen3-TTS for voice cloning (0.6B and 1.7B variants). + +**ASR Models:** Whisper for transcription (tiny through large). + +Models are downloaded from HuggingFace Hub on first use and cached locally. + +## Available Models + +### TTS Models + +| Model | HuggingFace ID | Size | VRAM | +|-------|----------------|------|------| +| 0.6B | `Qwen/Qwen3-TTS-12Hz-0.6B-Base` | ~1.2GB | ~2GB | +| 1.7B | `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | ~3.4GB | ~6GB | + +### Whisper Models + +| Model | HuggingFace ID | Size | VRAM | +|-------|----------------|------|------| +| tiny | `openai/whisper-tiny` | ~150MB | ~1GB | +| base | `openai/whisper-base` | ~300MB | ~1GB | +| small | `openai/whisper-small` | ~500MB | ~2GB | +| medium | `openai/whisper-medium` | ~1.5GB | ~5GB | +| large | `openai/whisper-large` | ~3GB | ~10GB | + +## Model Storage + +Models are cached in the HuggingFace cache directory: + +``` +~/.cache/huggingface/hub/ +├── models--Qwen--Qwen3-TTS-12Hz-1.7B-Base/ +├── models--Qwen--Qwen3-TTS-12Hz-0.6B-Base/ +├── models--openai--whisper-base/ +└── ... +``` + +## Progress Tracking + +### Progress Manager + +Tracks download progress across all models: + +```python +class ProgressManager: + def __init__(self): + self._progress = {} # model_name -> progress_info + + def update_progress( + self, + model_name: str, + current: int, + total: int, + filename: str, + status: str, + ): + self._progress[model_name] = { + "current": current, + "total": total, + "filename": filename, + "status": status, # downloading, complete, error + "updated_at": datetime.utcnow(), + } + + def get_progress(self, model_name: str) -> Optional[dict]: + return self._progress.get(model_name) +``` + +### HuggingFace Progress Callback + +Hooks into HuggingFace's download system: + +```python +class HFProgressTracker: + def __init__(self, callback): + self.callback = callback + + @contextmanager + def patch_download(self): + """Context manager to intercept HF downloads.""" + original_download = hf_hub_download + + def patched_download(*args, **kwargs): + # Intercept progress + result = original_download(*args, **kwargs) + self.callback(progress_info) + return result + + # Apply patch + with patch('huggingface_hub.hf_hub_download', patched_download): + yield +``` + +### Server-Sent Events (SSE) + +Progress is streamed to the frontend: + +```python +@app.get("/models/progress/{model_name}") +async def get_model_progress(model_name: str): + async def event_generator(): + while True: + progress = progress_manager.get_progress(model_name) + if progress: + yield f"data: {json.dumps(progress)}\n\n" + + if progress and progress["status"] in ["complete", "error"]: + break + + await asyncio.sleep(0.5) + + return StreamingResponse( + event_generator(), + media_type="text/event-stream" + ) +``` + +## Task Manager + +Tracks active downloads and generations: + +```python +class TaskManager: + def __init__(self): + self._active_downloads = {} + self._active_generations = {} + + def start_download(self, model_name: str): + self._active_downloads[model_name] = { + "status": "downloading", + "started_at": datetime.utcnow(), + } + + def complete_download(self, model_name: str): + if model_name in self._active_downloads: + del self._active_downloads[model_name] + + def get_active_tasks(self) -> dict: + return { + "downloads": list(self._active_downloads.values()), + "generations": list(self._active_generations.values()), + } +``` + +## Model Status + +Check which models are downloaded and loaded: + +```python +@app.get("/models/status") +async def get_model_status() -> ModelStatusListResponse: + models = [] + + # Check TTS models + for size, hf_id in [("1.7B", "Qwen/Qwen3-TTS-12Hz-1.7B-Base"), ...]: + downloaded = is_model_downloaded(hf_id) + loaded = tts_model._current_model_size == size + + models.append(ModelStatus( + model_name=f"qwen-tts-{size}", + display_name=f"Qwen3-TTS {size}", + downloaded=downloaded, + size_mb=get_model_size_mb(hf_id), + loaded=loaded, + )) + + # Check Whisper models + for size in ["tiny", "base", "small", "medium", "large"]: + hf_id = f"openai/whisper-{size}" + downloaded = is_model_downloaded(hf_id) + + models.append(ModelStatus( + model_name=f"whisper-{size}", + display_name=f"Whisper {size}", + downloaded=downloaded, + size_mb=get_model_size_mb(hf_id), + loaded=False, # Whisper is loaded on-demand + )) + + return ModelStatusListResponse(models=models) +``` + +## Manual Model Operations + +### Load Model + +```python +@app.post("/models/load") +async def load_model(model_size: str = "1.7B"): + tts_model = get_tts_model() + await tts_model.load_model_async(model_size) + return {"status": "loaded", "model_size": model_size} +``` + +### Unload Model + +```python +@app.post("/models/unload") +async def unload_model(): + tts_model = get_tts_model() + tts_model.unload_model() + return {"status": "unloaded"} +``` + +### Trigger Download + +```python +@app.post("/models/download") +async def trigger_model_download(request: ModelDownloadRequest): + # This triggers the download in background + # Progress is tracked via /models/progress/{model_name} + + if request.model_name.startswith("qwen-tts"): + size = request.model_name.split("-")[-1] + asyncio.create_task(download_tts_model(size)) + elif request.model_name.startswith("whisper"): + size = request.model_name.split("-")[-1] + asyncio.create_task(download_whisper_model(size)) + + return {"status": "downloading"} +``` + +### Delete Model + +```python +@app.delete("/models/{model_name}") +async def delete_model(model_name: str): + # Find and delete from HuggingFace cache + cache_dir = Path.home() / ".cache" / "huggingface" / "hub" + + model_dirs = list(cache_dir.glob(f"models--*--{model_name}*")) + for model_dir in model_dirs: + shutil.rmtree(model_dir) + + return {"status": "deleted"} +``` + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/models/status` | Get status of all models | +| POST | `/models/load` | Load TTS model | +| POST | `/models/unload` | Unload TTS model | +| POST | `/models/download` | Trigger model download | +| GET | `/models/progress/{name}` | Stream download progress (SSE) | +| DELETE | `/models/{name}` | Delete downloaded model | +| GET | `/tasks/active` | Get active downloads/generations | + +## Response Schemas + +### ModelStatus + +```json +{ + "model_name": "qwen-tts-1.7B", + "display_name": "Qwen3-TTS 1.7B", + "downloaded": true, + "size_mb": 3400, + "loaded": true +} +``` + +### ActiveTasksResponse + +```json +{ + "downloads": [ + { + "model_name": "whisper-medium", + "status": "downloading", + "started_at": "2024-01-15T10:30:00Z" + } + ], + "generations": [ + { + "task_id": "uuid", + "profile_id": "uuid", + "text_preview": "Hello world...", + "started_at": "2024-01-15T10:30:00Z" + } + ] +} +``` + +## Frontend Integration + +### Progress Display + +```typescript +// Subscribe to download progress via SSE +const eventSource = new EventSource(`/models/progress/${modelName}`); + +eventSource.onmessage = (event) => { + const progress = JSON.parse(event.data); + updateProgressBar(progress.current / progress.total); + + if (progress.status === 'complete') { + eventSource.close(); + } +}; +``` + +### Model Status UI + +```typescript +// Fetch model status +const { data: models } = useQuery({ + queryKey: ['models', 'status'], + queryFn: () => api.getModelStatus(), +}); + +// Display download/load buttons based on status +models.map(model => ( + triggerDownload(model.model_name)} + onLoad={() => loadModel(model.model_name)} + /> +)); +``` + +## Error Handling + +| Error | Cause | Solution | +|-------|-------|----------| +| Download failed | Network issue | Retry download | +| OOM on load | Model too large | Use smaller model | +| Model not found | Cache corrupted | Re-download | +| Slow download | HF rate limit | Wait and retry | diff --git a/docs2/content/docs/developer/setup.mdx b/docs2/content/docs/developer/setup.mdx new file mode 100644 index 00000000..c2c44f24 --- /dev/null +++ b/docs2/content/docs/developer/setup.mdx @@ -0,0 +1,239 @@ +--- +title: "Development Setup" +description: "Set up your local development environment for Voicebox" +--- + +## Prerequisites + +Before you begin, ensure you have the following installed: + + + + [Download Bun](https://bun.sh) + ```bash + curl -fsSL https://bun.sh/install | bash + ``` + + + [Download Python](https://python.org) + ```bash + python --version + ``` + + + [Install Rust](https://rustup.rs) + ```bash + rustc --version + ``` + + + +## Clone the Repository + +```bash +git clone https://github.com/jamiepine/voicebox.git +cd voicebox +``` + +## Quick Setup (Recommended) + +The easiest way to get started is using the Makefile: + +```bash +# Setup everything +make setup + +# Start development +make dev +``` + + + The Makefile is available on macOS and Linux. Windows users should follow the manual setup below. + + +## Manual Setup + +### 1. Install JavaScript Dependencies + +```bash +bun install +``` + +This installs dependencies for: +- `app/` - Shared React frontend +- `tauri/` - Tauri desktop wrapper +- `web/` - Web deployment wrapper + +### 2. Set Up Python Backend + +```bash +cd backend + +# Create virtual environment +python -m venv venv + +# Activate virtual environment +source venv/bin/activate # macOS/Linux +# or +venv\Scripts\activate # Windows + +# Install Python dependencies +pip install -r requirements.txt + +# Install MLX dependencies (Apple Silicon only - for faster inference) +# On Apple Silicon, this enables native Metal acceleration +if [[ $(uname -m) == "arm64" ]]; then + pip install -r requirements-mlx.txt +fi + +# Install Qwen3-TTS +pip install git+https://github.com/QwenLM/Qwen3-TTS.git +``` + +## Running in Development + +Development requires **two terminals**: one for the Python backend, one for the Tauri app. + + + + Start the Python server first: + + ```bash + cd backend + source venv/bin/activate # Activate venv + bun run dev:server + ``` + + Or manually: + ```bash + uvicorn main:app --reload --port 17493 + ``` + + Backend will be available at `http://localhost:17493` + + + + Then start the Tauri app: + + ```bash + bun run dev + ``` + + This will: + - Create a placeholder sidecar binary + - Start Vite dev server on port 5173 + - Launch Tauri window + - Enable hot reload + + + + + In dev mode, the app connects to your manually-started Python server. The bundled server binary is only used in production builds. + + +### Optional: Web App + +```bash +bun run dev:web +``` + +Web app will be available at `http://localhost:5174` + +## 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 (~2-4GB) + + + First-time usage will be slower due to model downloads, but subsequent runs will use cached models. + + +## Project Structure + +``` +voicebox/ +├── app/ # Shared React frontend +│ └── src/ +│ ├── components/ # UI components +│ ├── lib/ # Utilities and API client +│ └── hooks/ # React hooks +├── backend/ # Python FastAPI server +│ ├── main.py # API routes +│ ├── tts.py # Voice synthesis +│ └── database.py # SQLite operations +├── tauri/ # Desktop app wrapper +│ └── src-tauri/ # Rust backend +├── web/ # Web deployment +├── landing/ # Marketing website +└── scripts/ # Build & release scripts +``` + +## Available Make Commands + +Run `make help` to see all available commands: + +```bash +make setup # Install all dependencies +make dev # Start development servers +make dev-web # Start web development server +make build # Build desktop app +make build-web # Build web app +make clean # Clean build artifacts +make test # Run tests +``` + +## Generate OpenAPI Client + +After starting the backend server, generate the TypeScript API client: + +```bash +./scripts/generate-api.sh +# or +bun run generate:api +``` + +This downloads the OpenAPI schema and generates the TypeScript client in `app/src/lib/api/` + +## Next Steps + + + + Understand the system architecture + + + Read the contribution guidelines + + + Learn how to build production releases + + + Explore the REST API + + + +## Troubleshooting + + + + - Check Python version (must be 3.11+) + - Ensure virtual environment is activated + - Verify all dependencies are installed: `pip install -r requirements.txt` + - Check if port 17493 is available + + + + - Ensure Rust is installed: `rustc --version` + - Clean the build: `cd tauri/src-tauri && cargo clean` + - Try rebuilding: `bun run dev` + + + + - Ensure backend is running: `curl http://localhost:17493/openapi.json` + - Check network connectivity + - Verify the backend is accessible at localhost:17493 + + + +See the full [Troubleshooting Guide](/guides/troubleshooting) for more issues and solutions. diff --git a/docs2/content/docs/developer/stories.mdx b/docs2/content/docs/developer/stories.mdx new file mode 100644 index 00000000..8d812086 --- /dev/null +++ b/docs2/content/docs/developer/stories.mdx @@ -0,0 +1,320 @@ +--- +title: "Stories & Timeline" +description: "How the multi-voice timeline editor works in Voicebox" +--- + +## Overview + +Stories allow users to arrange multiple voice generations on a timeline to create multi-voice narratives. The system supports tracks, trimming, splitting, and audio mixing. + +## Architecture + +**Story:** A container that holds story items with metadata. + +**Story Item:** Links a generation to a story with timeline position, track, and trim data. + +**Export:** Combines all items into a single mixed audio file. + +## Data Model + +### Story Table + +```python +class Story(Base): + __tablename__ = "stories" + + id = Column(String, primary_key=True) + name = Column(String, nullable=False) + description = Column(Text) + created_at = Column(DateTime) + updated_at = Column(DateTime) +``` + +### StoryItem Table + +```python +class StoryItem(Base): + __tablename__ = "story_items" + + id = Column(String, primary_key=True) + story_id = Column(String, ForeignKey("stories.id")) + generation_id = Column(String, ForeignKey("generations.id")) + start_time_ms = Column(Integer, default=0) # Timeline position + track = Column(Integer, default=0) # Track number + trim_start_ms = Column(Integer, default=0) # Trim from start + trim_end_ms = Column(Integer, default=0) # Trim from end + created_at = Column(DateTime) +``` + +## Timeline Concepts + +### Start Time + +`start_time_ms` defines when an item begins on the timeline: + +``` +Timeline (ms): 0----1000----2000----3000----4000 +Item 1: [======] +Item 2: [==========] +Item 3: [====] +``` + +### Tracks + +Multiple tracks allow overlapping audio: + +``` +Track 0: [Item 1] [Item 3] +Track 1: [Item 2] +``` + +### Trimming + +Trim values cut audio from the start or end without destroying the original: + +``` +Original: [=========AUDIO=========] +trim_start: ^^ +trim_end: ^^ +Result: [=====AUDIO=====] +``` + +## Core Operations + +### Adding Items + +When adding a generation to a story: + +```python +async def add_item_to_story( + story_id: str, + data: StoryItemCreate, + db: Session, +) -> StoryItemDetail: + # Calculate start time if not provided + if data.start_time_ms is None: + # Find the end of all existing items + existing_items = get_items_with_durations(story_id, db) + max_end_time_ms = max( + item.start_time_ms + int(gen.duration * 1000) + for item, gen in existing_items + ) + start_time_ms = max_end_time_ms + 200 # 200ms gap + + # Create the item + item = DBStoryItem( + id=str(uuid.uuid4()), + story_id=story_id, + generation_id=data.generation_id, + start_time_ms=start_time_ms, + track=data.track or 0, + ) + db.add(item) + db.commit() +``` + +### Moving Items + +Update position and/or track: + +```python +async def move_story_item( + story_id: str, + item_id: str, + data: StoryItemMove, + db: Session, +) -> StoryItemDetail: + item = get_item(story_id, item_id, db) + + item.start_time_ms = data.start_time_ms + item.track = data.track + + db.commit() +``` + +### Trimming Items + +Non-destructive trimming: + +```python +async def trim_story_item( + story_id: str, + item_id: str, + data: StoryItemTrim, + db: Session, +) -> StoryItemDetail: + item = get_item(story_id, item_id, db) + generation = get_generation(item.generation_id, db) + + # Validate trim doesn't exceed duration + max_duration_ms = int(generation.duration * 1000) + if data.trim_start_ms + data.trim_end_ms >= max_duration_ms: + return None # Invalid trim + + item.trim_start_ms = data.trim_start_ms + item.trim_end_ms = data.trim_end_ms + + db.commit() +``` + +### Splitting Items + +Split one item into two at a specific time: + +```python +async def split_story_item( + story_id: str, + item_id: str, + data: StoryItemSplit, + db: Session, +) -> List[StoryItemDetail]: + item = get_item(story_id, item_id, db) + generation = get_generation(item.generation_id, db) + + # Calculate split point + current_trim_start = item.trim_start_ms + current_trim_end = item.trim_end_ms + original_duration_ms = int(generation.duration * 1000) + absolute_split_ms = current_trim_start + data.split_time_ms + + # Update original: trim from end + item.trim_end_ms = original_duration_ms - absolute_split_ms + + # Create new item: trim from start + new_item = DBStoryItem( + generation_id=item.generation_id, # Same generation + start_time_ms=item.start_time_ms + data.split_time_ms, + track=item.track, + trim_start_ms=absolute_split_ms, + trim_end_ms=current_trim_end, + ) + + db.add(new_item) + db.commit() + + return [item, new_item] +``` + +### Duplicating Items + +Create a copy with all properties: + +```python +async def duplicate_story_item( + story_id: str, + item_id: str, + db: Session, +) -> StoryItemDetail: + original = get_item(story_id, item_id, db) + generation = get_generation(original.generation_id, db) + + # Calculate effective duration for positioning + effective_duration_ms = ( + int(generation.duration * 1000) + - original.trim_start_ms + - original.trim_end_ms + ) + + # Place copy after original with 200ms gap + new_item = DBStoryItem( + generation_id=original.generation_id, + start_time_ms=original.start_time_ms + effective_duration_ms + 200, + track=original.track, + trim_start_ms=original.trim_start_ms, + trim_end_ms=original.trim_end_ms, + ) + + db.add(new_item) + db.commit() +``` + +## Audio Export + +### Mixing Algorithm + +The export function mixes all items into a single audio file: + +```python +async def export_story_audio(story_id: str, db: Session) -> bytes: + items = get_all_items_with_generations(story_id, db) + + # Calculate total duration + max_end_time_ms = max( + data['start_time_ms'] + data['duration_ms'] + for data in audio_data + ) + + # Create output buffer + total_samples = int((max_end_time_ms / 1000.0) * sample_rate) + final_audio = np.zeros(total_samples, dtype=np.float32) + + # Mix each item at its position + for data in audio_data: + audio = data['audio'] + start_sample = int((data['start_time_ms'] / 1000.0) * sample_rate) + + # Apply trim + trimmed_audio = audio[trim_start_sample:len(audio) - trim_end_sample] + + # Add to buffer (overlapping items sum together) + final_audio[start_sample:start_sample + len(trimmed_audio)] += trimmed_audio + + # Normalize to prevent clipping + max_val = np.abs(final_audio).max() + if max_val > 1.0: + final_audio = final_audio / max_val + + return audio_to_bytes(final_audio, sample_rate) +``` + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/stories` | List all stories | +| POST | `/stories` | Create a story | +| GET | `/stories/{id}` | Get story with items | +| PUT | `/stories/{id}` | Update story metadata | +| DELETE | `/stories/{id}` | Delete story | +| POST | `/stories/{id}/items` | Add item to story | +| DELETE | `/stories/{id}/items/{item_id}` | Remove item | +| PUT | `/stories/{id}/items/{item_id}/move` | Move item | +| PUT | `/stories/{id}/items/{item_id}/trim` | Trim item | +| POST | `/stories/{id}/items/{item_id}/split` | Split item | +| POST | `/stories/{id}/items/{item_id}/duplicate` | Duplicate item | +| PUT | `/stories/{id}/items/times` | Batch update times | +| PUT | `/stories/{id}/items/reorder` | Reorder items | +| GET | `/stories/{id}/export-audio` | Export mixed audio | + +## Response Schemas + +### StoryItemDetail + +```json +{ + "id": "item_uuid", + "story_id": "story_uuid", + "generation_id": "generation_uuid", + "start_time_ms": 1500, + "track": 0, + "trim_start_ms": 200, + "trim_end_ms": 100, + "profile_id": "profile_uuid", + "profile_name": "Narrator", + "text": "Hello world", + "audio_path": "/path/to/audio.wav", + "duration": 2.5, + "created_at": "2024-01-15T10:30:00Z" +} +``` + +## Frontend Integration + +The timeline UI needs to: + +1. **Fetch story** with all items +2. **Render waveforms** for each item +3. **Handle drag/drop** to move items +4. **Handle edge drag** for trimming +5. **Sync playhead** across all tracks +6. **Export** when user clicks download diff --git a/docs2/content/docs/developer/transcription.mdx b/docs2/content/docs/developer/transcription.mdx new file mode 100644 index 00000000..4eee2b91 --- /dev/null +++ b/docs2/content/docs/developer/transcription.mdx @@ -0,0 +1,299 @@ +--- +title: "Transcription" +description: "How Whisper-based audio transcription works in Voicebox" +--- + +## Overview + +Voicebox uses OpenAI's Whisper model for automatic speech recognition (ASR). This powers the transcription feature for creating reference text from audio recordings. + +## Architecture + +The transcription system is built around the `WhisperModel` class: + +**Model Loading:** Lazy loading with HuggingFace Hub download. + +**Audio Processing:** Resampling and preprocessing for Whisper. + +**Inference:** Running transcription with optional language hints. + +## WhisperModel Class + +```python +class WhisperModel: + def __init__(self, model_size: str = "base"): + self.model = None + self.processor = None + self.model_size = model_size + self.device = self._get_device() +``` + +### Model Sizes + +| Size | Parameters | VRAM | Speed | Quality | +|------|------------|------|-------|---------| +| tiny | 39M | ~1GB | Fastest | Basic | +| base | 74M | ~1GB | Fast | Good | +| small | 244M | ~2GB | Medium | Better | +| medium | 769M | ~5GB | Slow | High | +| large | 1550M | ~10GB | Slowest | Best | + +Default is `base` for balance of speed and quality. + +## Model Loading + +Models are downloaded from HuggingFace Hub: + +```python +def load_model(self, model_size: Optional[str] = None): + from transformers import WhisperProcessor, WhisperForConditionalGeneration + + model_name = f"openai/whisper-{model_size}" + + # Track download progress + progress_manager = get_progress_manager() + task_manager = get_task_manager() + task_manager.start_download(f"whisper-{model_size}") + + # Load processor and model + with tracker.patch_download(): + self.processor = WhisperProcessor.from_pretrained(model_name) + self.model = WhisperForConditionalGeneration.from_pretrained(model_name) + + self.model.to(self.device) + + # Mark complete + progress_manager.mark_complete(f"whisper-{model_size}") + task_manager.complete_download(f"whisper-{model_size}") +``` + +### Async Loading + +Like TTS, loading runs in a thread pool: + +```python +async def load_model_async(self, model_size: Optional[str] = None): + if self.model is not None and self.model_size == model_size: + return + await asyncio.to_thread(self.load_model, model_size) +``` + +## Transcription + +### Basic Transcription + +```python +async def transcribe( + self, + audio_path: str, + language: Optional[str] = None, +) -> str: + await self.load_model_async() + + def _transcribe_sync(): + # Load and resample to 16kHz (Whisper requirement) + audio, sr = load_audio(audio_path, sample_rate=16000) + + # Process audio + inputs = self.processor( + audio, + sampling_rate=16000, + return_tensors="pt", + ) + inputs = inputs.to(self.device) + + # Set language hint if provided + forced_decoder_ids = None + if language: + forced_decoder_ids = self.processor.get_decoder_prompt_ids( + language=language, + task="transcribe", + ) + + # Generate + with torch.no_grad(): + predicted_ids = self.model.generate( + inputs["input_features"], + forced_decoder_ids=forced_decoder_ids, + ) + + # Decode + transcription = self.processor.batch_decode( + predicted_ids, + skip_special_tokens=True, + )[0] + + return transcription.strip() + + return await asyncio.to_thread(_transcribe_sync) +``` + +### Supported Languages + +Whisper supports 99+ languages. Common ones in Voicebox: + +| Code | Language | +|------|----------| +| en | English | +| zh | Chinese | +| ja | Japanese | +| ko | Korean | +| de | German | +| fr | French | +| ru | Russian | +| pt | Portuguese | +| es | Spanish | +| it | Italian | + +### Language Detection + +When no language is specified, Whisper auto-detects: + +```python +# Without language hint - auto-detect +transcription = await whisper.transcribe(audio_path) + +# With language hint - more accurate for short clips +transcription = await whisper.transcribe(audio_path, language="en") +``` + +## Transcription with Timestamps + +For advanced use cases, word-level timestamps are available: + +```python +async def transcribe_with_timestamps( + self, + audio_path: str, + language: Optional[str] = None, +) -> List[Dict[str, any]]: + await self.load_model_async() + + def _transcribe_timestamps_sync(): + audio, sr = load_audio(audio_path, sample_rate=16000) + inputs = self.processor(audio, sampling_rate=16000, return_tensors="pt") + + with torch.no_grad(): + predicted_ids = self.model.generate( + inputs["input_features"], + return_timestamps=True, + ) + + # Parse timestamps + return [ + { + "text": transcription, + "start": 0.0, + "end": len(audio) / sr, + } + ] + + return await asyncio.to_thread(_transcribe_timestamps_sync) +``` + +## Memory Management + +### Unloading + +Free memory when not needed: + +```python +def unload_model(self): + if self.model is not None: + del self.model + del self.processor + self.model = None + self.processor = None + + if torch.cuda.is_available(): + torch.cuda.empty_cache() +``` + +### Global Instance + +A singleton pattern manages the model: + +```python +_whisper_model: Optional[WhisperModel] = None + +def get_whisper_model() -> WhisperModel: + global _whisper_model + if _whisper_model is None: + _whisper_model = WhisperModel() + return _whisper_model +``` + +## Audio Preprocessing + +### Resampling + +Whisper requires 16kHz audio: + +```python +audio, sr = load_audio(audio_path, sample_rate=16000) +``` + +### Format Support + +The `load_audio` utility handles: +- WAV +- MP3 +- FLAC +- OGG +- M4A + +All formats are converted to mono 16kHz. + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/transcribe` | Transcribe audio file | + +### Request + +Multipart form data: + +``` +POST /transcribe +Content-Type: multipart/form-data + +file: +language: en (optional) +``` + +### Response + +```json +{ + "text": "Hello, this is a test transcription.", + "duration": 3.5 +} +``` + +## Use Cases + +### Reference Text for Voice Cloning + +1. User records audio sample +2. Audio is sent to `/transcribe` +3. Transcription becomes `reference_text` +4. Both are added to voice profile + +### Quality Tips + +- Provide language hint for short audio +- Use clean audio with minimal noise +- Longer audio (>5s) improves accuracy +- Consider `small` or `medium` model for better quality + +## Error Handling + +Common issues: + +| Error | Cause | Solution | +|-------|-------|----------| +| Model not found | First run, download failed | Retry with network | +| OOM | Model too large | Use smaller model | +| Empty result | No speech detected | Check audio has speech | +| Wrong language | Auto-detect failed | Provide language hint | diff --git a/docs2/content/docs/developer/tts-generation.mdx b/docs2/content/docs/developer/tts-generation.mdx new file mode 100644 index 00000000..3636d33b --- /dev/null +++ b/docs2/content/docs/developer/tts-generation.mdx @@ -0,0 +1,283 @@ +--- +title: "TTS Generation" +description: "How text-to-speech generation works in Voicebox" +--- + +## Overview + +Voicebox uses Qwen3-TTS for voice cloning and text-to-speech generation. The TTS module handles model loading, voice prompt creation, and audio synthesis. + +## Architecture + +The TTS system is built around the `TTSModel` class which manages: + +**Model Loading:** Lazy loading with automatic HuggingFace Hub download. + +**Voice Prompts:** Converting reference audio into embeddings. + +**Generation:** Synthesizing speech from text using voice prompts. + +## TTSModel Class + +```python +class TTSModel: + def __init__(self, model_size: str = "1.7B"): + self.model = None + self.model_size = model_size + self.device = self._get_device() # cuda, mps, or cpu +``` + +### Device Selection + +The model automatically selects the best available device: + +```python +def _get_device(self) -> str: + if torch.cuda.is_available(): + return "cuda" + elif hasattr(torch.backends, 'mps') and torch.backends.mps.is_available(): + return "cpu" # MPS can have issues, use CPU for stability + return "cpu" +``` + +## Model Loading + +Models are downloaded from HuggingFace Hub on first use: + +```python +def load_model(self, model_size: Optional[str] = None): + # Model IDs on HuggingFace Hub + hf_model_map = { + "1.7B": "Qwen/Qwen3-TTS-12Hz-1.7B-Base", + "0.6B": "Qwen/Qwen3-TTS-12Hz-0.6B-Base", + } + + # Load with progress tracking + with tracker.patch_download(): + self.model = Qwen3TTSModel.from_pretrained( + model_path, + device_map=self.device, + torch_dtype=torch.bfloat16, # float32 on CPU + ) +``` + +### Async Loading + +Loading runs in a thread pool to avoid blocking the event loop: + +```python +async def load_model_async(self, model_size: Optional[str] = None): + if self.model is not None and self._current_model_size == model_size: + return + await asyncio.to_thread(self.load_model, model_size) +``` + +## Voice Prompt Creation + +Voice prompts are created from reference audio and cached for reuse: + +```python +async def create_voice_prompt( + self, + audio_path: str, + reference_text: str, + use_cache: bool = True, +) -> Tuple[dict, bool]: + await self.load_model_async() + + # Check cache + if use_cache: + cache_key = get_cache_key(audio_path, reference_text) + cached = get_cached_voice_prompt(cache_key) + if cached: + return cached, True + + # Create prompt (blocking, run in thread pool) + voice_prompt = await asyncio.to_thread( + self.model.create_voice_clone_prompt, + ref_audio=audio_path, + ref_text=reference_text, + ) + + # Cache the result + cache_voice_prompt(cache_key, voice_prompt) + return voice_prompt, False +``` + +### Combining Multiple Samples + +When a profile has multiple samples, they're combined: + +```python +async def combine_voice_prompts( + self, + audio_paths: List[str], + reference_texts: List[str], +) -> Tuple[np.ndarray, str]: + combined_audio = [] + + for audio_path in audio_paths: + audio, sr = load_audio(audio_path) + audio = normalize_audio(audio) + combined_audio.append(audio) + + # Concatenate and normalize + mixed = np.concatenate(combined_audio) + mixed = normalize_audio(mixed) + + # Combine texts + combined_text = " ".join(reference_texts) + + return mixed, combined_text +``` + +## Speech Generation + +The core generation function: + +```python +async def generate( + self, + text: str, + voice_prompt: dict, + language: str = "en", + seed: Optional[int] = None, + instruct: Optional[str] = None, +) -> Tuple[np.ndarray, int]: + await self.load_model_async() + + def _generate_sync(): + # Set seed for reproducibility + if seed is not None: + torch.manual_seed(seed) + + # Generate audio + wavs, sample_rate = self.model.generate_voice_clone( + text=text, + voice_clone_prompt=voice_prompt, + instruct=instruct, # Natural language delivery control + ) + return wavs[0], sample_rate + + # Run in thread pool + return await asyncio.to_thread(_generate_sync) +``` + +### Instruct Feature + +The `instruct` parameter allows natural language control over speech delivery: + +```python +# Examples: +instruct = "Speak slowly and clearly" +instruct = "Sound excited and enthusiastic" +instruct = "Whisper softly" +``` + +## Caching Strategy + +Voice prompts are cached to avoid recomputation: + +```python +def get_cache_key(audio_path: str, reference_text: str) -> str: + """Generate cache key from audio hash and text.""" + audio_hash = hashlib.md5(Path(audio_path).read_bytes()).hexdigest() + text_hash = hashlib.md5(reference_text.encode()).hexdigest() + return f"{audio_hash}_{text_hash}" +``` + +Cache is stored in `data/cache/voice_prompts/`. + +## Memory Management + +### Unloading Models + +Free VRAM/RAM when not needed: + +```python +def unload_model(self): + if self.model is not None: + del self.model + self.model = None + + if torch.cuda.is_available(): + torch.cuda.empty_cache() +``` + +### Model Switching + +When switching between model sizes (1.7B ↔ 0.6B): + +```python +# Unload existing model first +if self.model is not None and self._current_model_size != model_size: + self.unload_model() +``` + +## Generation Flow + +1. **Request** → Validate text and profile ID +2. **Profile** → Load profile samples from database +3. **Voice Prompt** → Create or retrieve cached prompt +4. **Generate** → Run TTS inference +5. **Save** → Write audio to generations directory +6. **Record** → Create history entry in database +7. **Response** → Return audio path and metadata + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/generate` | Generate speech from text | +| GET | `/audio/{id}` | Serve generated audio file | + +### Request Schema + +```json +{ + "profile_id": "uuid", + "text": "Text to synthesize", + "language": "en", + "seed": 42, + "model_size": "1.7B", + "instruct": "Speak clearly" +} +``` + +### Response Schema + +```json +{ + "id": "generation_uuid", + "profile_id": "profile_uuid", + "text": "Text to synthesize", + "language": "en", + "audio_path": "/path/to/audio.wav", + "duration": 3.5, + "seed": 42, + "instruct": "Speak clearly", + "created_at": "2024-01-15T10:30:00Z" +} +``` + +## Performance Considerations + +### GPU Acceleration + +- CUDA provides fastest inference +- MPS (Apple Silicon) has stability issues, uses CPU fallback +- CPU inference is slower but always works + +### Batch Size + +Currently generates one utterance at a time. For long texts, consider: +- Splitting into sentences +- Sequential generation +- Concatenating results + +### Memory Usage + +| Model | VRAM/RAM Required | +|-------|-------------------| +| 0.6B | ~2GB | +| 1.7B | ~6GB | diff --git a/docs2/content/docs/developer/voice-profiles.mdx b/docs2/content/docs/developer/voice-profiles.mdx new file mode 100644 index 00000000..6be6aad9 --- /dev/null +++ b/docs2/content/docs/developer/voice-profiles.mdx @@ -0,0 +1,202 @@ +--- +title: "Voice Profiles" +description: "How voice profile management works in Voicebox" +--- + +## Overview + +Voice profiles are the foundation of Voicebox's voice cloning capability. Each profile stores reference audio samples and metadata that the TTS model uses to clone a voice. + +## Architecture + +The voice profile system consists of three main components: + +**Database Layer:** SQLite tables store profile metadata and sample references. + +**File Storage:** Audio samples are stored on disk in a structured directory format. + +**Profile Module:** The `profiles.py` module provides the business logic for CRUD operations. + +## Data Model + +### VoiceProfile Table + +```python +class VoiceProfile(Base): + __tablename__ = "profiles" + + id = Column(String, primary_key=True) + name = Column(String, unique=True, nullable=False) + description = Column(Text) + language = Column(String, default="en") + created_at = Column(DateTime) + updated_at = Column(DateTime) +``` + +### ProfileSample Table + +```python +class ProfileSample(Base): + __tablename__ = "profile_samples" + + id = Column(String, primary_key=True) + profile_id = Column(String, ForeignKey("profiles.id")) + audio_path = Column(String, nullable=False) + reference_text = Column(Text, nullable=False) +``` + +## File Structure + +Profiles are stored in the data directory: + +``` +data/ +└── profiles/ + └── {profile_id}/ + ├── {sample_id_1}.wav + ├── {sample_id_2}.wav + └── ... +``` + +## Core Functions + +### Creating a Profile + +```python +async def create_profile(data: VoiceProfileCreate, db: Session) -> VoiceProfileResponse: + # 1. Create database record + db_profile = DBVoiceProfile( + id=str(uuid.uuid4()), + name=data.name, + description=data.description, + language=data.language, + ) + db.add(db_profile) + db.commit() + + # 2. Create profile directory + profile_dir = profiles_dir / db_profile.id + profile_dir.mkdir(parents=True, exist_ok=True) + + return VoiceProfileResponse.model_validate(db_profile) +``` + +### Adding Samples + +When a sample is added, the audio is validated and copied to the profile directory: + +```python +async def add_profile_sample( + profile_id: str, + audio_path: str, + reference_text: str, + db: Session, +) -> ProfileSampleResponse: + # 1. Validate audio (duration, format, quality) + is_valid, error_msg = validate_reference_audio(audio_path) + if not is_valid: + raise ValueError(f"Invalid reference audio: {error_msg}") + + # 2. Copy to profile directory + sample_id = str(uuid.uuid4()) + dest_path = profile_dir / f"{sample_id}.wav" + audio, sr = load_audio(audio_path) + save_audio(audio, str(dest_path), sr) + + # 3. Create database record + db_sample = DBProfileSample( + id=sample_id, + profile_id=profile_id, + audio_path=str(dest_path), + reference_text=reference_text, + ) + db.add(db_sample) + db.commit() +``` + +### Voice Prompt Creation + +When generating speech, samples are combined into a voice prompt: + +```python +async def create_voice_prompt_for_profile( + profile_id: str, + db: Session, +) -> dict: + samples = db.query(DBProfileSample).filter_by(profile_id=profile_id).all() + + if len(samples) == 1: + # Single sample - use directly + voice_prompt, _ = await tts_model.create_voice_prompt( + sample.audio_path, + sample.reference_text, + ) + else: + # Multiple samples - combine them + combined_audio, combined_text = await tts_model.combine_voice_prompts( + [s.audio_path for s in samples], + [s.reference_text for s in samples], + ) + voice_prompt, _ = await tts_model.create_voice_prompt( + combined_audio_path, + combined_text, + ) + + return voice_prompt +``` + +## Audio Validation + +Reference audio is validated before being accepted: + +- **Duration:** 3-30 seconds recommended +- **Format:** WAV, MP3, FLAC, OGG supported +- **Sample Rate:** Resampled to 24kHz +- **Channels:** Converted to mono if stereo + +## Export/Import + +Profiles can be exported as ZIP archives for sharing: + +``` +profile_export.zip +├── profile.json # Metadata +├── samples/ +│ ├── sample_1.wav +│ └── sample_1.json # Reference text +└── ... +``` + +## API Endpoints + +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/profiles` | List all profiles | +| POST | `/profiles` | Create a profile | +| GET | `/profiles/{id}` | Get profile by ID | +| PUT | `/profiles/{id}` | Update profile | +| DELETE | `/profiles/{id}` | Delete profile | +| GET | `/profiles/{id}/samples` | Get profile samples | +| POST | `/profiles/{id}/samples` | Add sample to profile | +| PUT | `/profiles/samples/{id}` | Update sample text | +| DELETE | `/profiles/samples/{id}` | Delete sample | +| GET | `/profiles/{id}/export` | Export as ZIP | +| POST | `/profiles/import` | Import from ZIP | + +## Best Practices + +### Sample Quality + +- Use clean audio with minimal background noise +- Ensure the reference text exactly matches what is spoken +- Multiple samples (3-5) improve voice cloning quality + +### Language Matching + +- Set the profile language to match the reference audio +- Supported languages: en, zh, ja, ko, de, fr, ru, pt, es, it + +### Naming Conventions + +- Use descriptive names that identify the voice +- Avoid special characters that may cause filesystem issues diff --git a/docs2/content/docs/index.mdx b/docs2/content/docs/index.mdx new file mode 100644 index 00000000..c2b91c56 --- /dev/null +++ b/docs2/content/docs/index.mdx @@ -0,0 +1,50 @@ +--- +title: "Voicebox Documentation" +description: "Welcome to Voicebox - the open-source voice synthesis studio" +--- + +## What is Voicebox? + +Voicebox is a **local-first voice cloning studio** with DAW-like features for professional voice synthesis. Think of it as the **Ollama for voice** — download models, clone voices, and generate speech entirely on your machine. + + + Voicebox App Screenshot + + +Unlike cloud services that lock your voice data behind subscriptions, Voicebox gives you: + +- **Complete privacy** — models and voice data stay on your machine +- **Professional tools** — multi-track timeline editor, audio trimming, conversation mixing +- **Model flexibility** — currently powered by Qwen3-TTS, with support for XTTS, Bark, and other models coming soon +- **API-first** — use the desktop app or integrate voice synthesis into your own projects +- **Native performance** — built with Tauri (Rust), not Electron + +Download a voice model, clone any voice from a few seconds of audio, and compose multi-voice projects with studio-grade editing tools. No Python install required, no cloud dependency, no limits. + +## Key Features + + + + Instant cloning from just a few seconds of audio with Qwen3-TTS + + + Multi-track timeline for creating conversations and narratives + + + REST API for integrating voice synthesis into your apps + + + Everything runs on your machine - complete privacy + + + +## Get Started + + + + Download and install Voicebox on your machine + + + Get up and running in 5 minutes + + diff --git a/docs2/content/docs/meta.json b/docs2/content/docs/meta.json new file mode 100644 index 00000000..11ea0c16 --- /dev/null +++ b/docs2/content/docs/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Voicebox Documentation", + "pages": [ + "overview", + "api", + "api-reference", + "developer", + "plans" + ] +} diff --git a/docs2/content/docs/overview/building-stories.mdx b/docs2/content/docs/overview/building-stories.mdx new file mode 100644 index 00000000..0056f38c --- /dev/null +++ b/docs2/content/docs/overview/building-stories.mdx @@ -0,0 +1,37 @@ +--- +title: "Building Stories" +description: "Create multi-voice narratives with the Stories Editor" +--- + +## Getting Started + +The Stories Editor is perfect for creating podcasts, audiobooks, and multi-speaker content. + + + + **Stories** → **+ New Story** + + + Create tracks for each speaker + + + Generate or drag audio to tracks + + + Position and trim clips on timeline + + + Render final audio + + + +## Use Cases + +- Multi-host podcasts +- Audiobook narration with character voices +- Game dialogue scenes +- Educational content with multiple speakers + +## Coming Soon + +Full timeline editor documentation will be added as features are finalized. diff --git a/docs2/content/docs/overview/creating-voice-profiles.mdx b/docs2/content/docs/overview/creating-voice-profiles.mdx new file mode 100644 index 00000000..9bb5f879 --- /dev/null +++ b/docs2/content/docs/overview/creating-voice-profiles.mdx @@ -0,0 +1,296 @@ +--- +title: "Creating Voice Profiles" +description: "Advanced guide to creating high-quality voice profiles" +--- + +## Overview + +Voice profiles are the foundation of voice cloning in Voicebox. This guide covers best practices for creating professional-quality voice profiles. + +## Quick Start + + + + 10-30 seconds of clear speech + + + **Profiles** → **+ New Profile** + + + Add your audio file + + + Use the profile to generate speech + + + +## Audio Requirements + +### Ideal Sample Characteristics + + + + **10-30 seconds** + + Too short: Poor quality + Too long: Unnecessary + + + **Clear speech** + + No background noise + No music or overlapping voices + + + **High fidelity** + + 44.1kHz or 48kHz sample rate + Minimal compression + + + **Natural speech** + + Conversational tone + Complete sentences + + + +### File Formats + +Supported formats: +- **WAV** (recommended) - Lossless quality +- **MP3** - Acceptable, minimal compression +- **M4A** - Acceptable +- **FLAC** - Lossless alternative + + + Use WAV for best results. Avoid heavily compressed formats. + + +## Recording Tips + +### Environment + + + + - Record in a quiet room + - Turn off fans, AC, appliances + - Close windows to reduce outside noise + - Use soft furnishings to reduce echo + + + + - 6-12 inches from mouth + - Slight angle to reduce plosives (p, b, t) + - Use a pop filter if available + - Maintain consistent distance + + + + - 44.1kHz or 48kHz sample rate + - 16-bit or 24-bit depth + - Mono is fine (stereo will be converted) + - Avoid automatic gain control + + + +### Speaking + +- **Natural pace** - Don't rush or speak too slowly +- **Clear articulation** - Pronounce words clearly +- **Consistent volume** - Maintain steady loudness +- **Normal tone** - Speak as you normally would +- **Complete sentences** - Avoid fragments or "ums" + +## Multiple Samples + +Adding multiple samples can significantly improve quality: + +### Why Multiple Samples? + + + + Model learns a more complete representation + + + Handles different speaking styles better + + + Reduces artifacts and improves naturalness + + + More reliable across different texts + + + +### Sample Variety + +Consider adding samples with: + +1. **Different tones** + - Casual conversation + - Professional/formal + - Excited/enthusiastic + - Calm/serious + +2. **Different content** + - Narratives + - Questions + - Statements + - Emotions (happy, sad, neutral) + +3. **Different recording conditions** + - Studio quality + - Phone call quality (if needed) + - Room acoustics + + + All samples should be from the **same speaker**. Mixing voices will produce poor results. + + +## Processing Existing Audio + +If you have existing audio (podcasts, videos, etc.): + +### Extracting Clean Segments + + + + Look for segments with: + - Just the target speaker + - No background music + - Minimal noise + + + + Tools like Audacity or Adobe Audition: + - Cut out clean 10-30s segments + - Remove silence at start/end + - Normalize volume if needed + + + + Save as high-quality WAV file + + + +### Noise Reduction + +If you have light background noise: + +``` +1. Use noise reduction in Audacity: + - Select noise-only section + - Get Noise Profile + - Select full audio + - Apply noise reduction (gentle settings) + +2. Avoid over-processing: + - Can introduce artifacts + - May reduce voice quality +``` + +## Testing & Iteration + +### Test Your Profile + +After creating a profile: + + + + Generate a simple phrase: + ``` + "Hello, this is a test of my voice profile." + ``` + + + + Listen for: + - Natural tone + - Clear pronunciation + - Proper prosody + - Lack of artifacts + + + + If quality is poor: + - Add more samples + - Try different source audio + - Check sample quality + + + +### Common Issues + + + + **Cause**: Poor quality samples or too short + + **Fix**: Use longer, higher quality samples + + + + **Cause**: Sample tone doesn't match desired output + + **Fix**: Record samples in the style you want to generate + + + + **Cause**: Background noise or audio issues in samples + + **Fix**: Clean up samples or re-record in quieter environment + + + +## Advanced Tips + +### Celebrity/Character Voices + +For cloning public figures or characters: + +1. **Legal considerations** - Ensure you have rights or it's fair use +2. **Source quality** - Find high-quality interview audio or clean clips +3. **Consistency** - Use clips where they speak similarly +4. **Multiple samples** - Very important for recognizable voices + +### Accent & Dialect + +The model will preserve accent and dialect: + +- British English will generate British English +- Southern accent will produce Southern accent +- Regional pronunciations will be maintained + +### Emotion Transfer + +The emotional tone of samples affects generation: + +- Energetic samples → Energetic output +- Calm samples → Calm output +- Mix samples for versatile profile + +## Managing Profiles + +### Organization + +- **Descriptive names** - "John Smith - Professional Narrator" +- **Add descriptions** - Note recording conditions, use cases +- **Language tags** - Mark the primary language +- **Archive unused** - Keep profile list manageable + +### Export/Import + +- **Export** profiles to share or backup +- **Import** from colleagues or teammates +- Profiles include voice embeddings, not original audio + +## Next Steps + + + + Use your profile to generate speech + + + Create multi-voice narratives + + diff --git a/docs2/content/docs/overview/generating-speech.mdx b/docs2/content/docs/overview/generating-speech.mdx new file mode 100644 index 00000000..775b011c --- /dev/null +++ b/docs2/content/docs/overview/generating-speech.mdx @@ -0,0 +1,65 @@ +--- +title: "Generating Speech" +description: "Generate high-quality speech from text" +--- + +## Basic Generation + + + + Choose a voice profile from the dropdown + + + Type or paste your text + + + Click **Generate** and wait a few seconds + + + Preview and download the result + + + +## Text Formatting Tips + +The way you format text affects the output quality. + +### Punctuation + +Use proper punctuation for natural pauses: + +``` +Good: "Hello! How are you today? I'm doing great." +Bad: "Hello how are you today Im doing great" +``` + +### Emphasis + +Use formatting to suggest emphasis: + +``` +- ALL CAPS for louder/emphasized: "That was AMAZING!" +- Italics for subtle emphasis: "I *really* enjoyed that" +- Bold for strong emphasis: "This is **very** important" +``` + + + The model interprets these hints but results may vary. + + +## Advanced Features + +### Batch Generation + +For long-form content, split into smaller chunks for better control and faster processing. + +### Voice Caching + +Voicebox caches voice prompts for faster re-generation with the same profile. + +## Coming Soon + +- Real-time streaming +- Word-level timing control +- Emotion and style controls +- SSML support diff --git a/docs2/content/docs/overview/generation-history.mdx b/docs2/content/docs/overview/generation-history.mdx new file mode 100644 index 00000000..cdee4689 --- /dev/null +++ b/docs2/content/docs/overview/generation-history.mdx @@ -0,0 +1,88 @@ +--- +title: "Generation History" +description: "Track and manage all your generated audio" +--- + +## Overview + +Voicebox keeps a complete history of all generated audio, making it easy to find, reuse, and manage your creations. + +## Features + + + + Every generation is automatically saved + + + Find by text, voice, or date + + + Regenerate any past generation with one click + + + Download individual or batch exports + + + +## Viewing History + +Navigate to the **History** tab to see all your generations. + +Each entry shows: +- Generated text +- Voice profile used +- Timestamp +- Audio duration +- Language + +## Actions + +### Play +Click any generation to play it immediately. + +### Re-generate +Regenerate with the same settings or modify the text/voice. + +### Download +Export as WAV, MP3, or M4A. + +### Delete +Remove unwanted generations to free up space. + +### Add to Story +Drag generations to the Stories Editor timeline. + +## Search & Filter + + + + Search for specific text content + ``` + "Hello world" + ``` + + + Filter by voice profile + ``` + Select from dropdown + ``` + + + Filter by date range + ``` + Last 7 days, Last 30 days, Custom range + ``` + + + +## Storage + +History is stored locally: + +- **macOS**: `~/Library/Application Support/com.voicebox.app/data/` +- **Windows**: `%APPDATA%/com.voicebox.app/data/` +- **Linux**: `~/.config/com.voicebox.app/data/` + + + Deleting the data directory will remove all history. Export important files first. + diff --git a/docs2/content/docs/overview/installation.mdx b/docs2/content/docs/overview/installation.mdx new file mode 100644 index 00000000..04ddc5ba --- /dev/null +++ b/docs2/content/docs/overview/installation.mdx @@ -0,0 +1,119 @@ +--- +title: "Installation" +description: "Download and install Voicebox on macOS, Windows, or Linux" +--- + +## Download + +Voicebox is available for macOS and Windows, with Linux builds coming soon. + + + + Download for Apple Silicon or Intel Macs + + + Download MSI installer or Setup executable + + + +### macOS + + + + Download: [voicebox_aarch64.app.tar.gz](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_aarch64.app.tar.gz) + + ```bash + # Extract the archive + tar -xzf voicebox_aarch64.app.tar.gz + + # Move to Applications + mv Voicebox.app /Applications/ + ``` + + + Download: [voicebox_x64.app.tar.gz](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_x64.app.tar.gz) + + ```bash + # Extract the archive + tar -xzf voicebox_x64.app.tar.gz + + # Move to Applications + mv Voicebox.app /Applications/ + ``` + + + +### Windows + + + + Download: [voicebox_x64_en-US.msi](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_x64_en-US.msi) + + Double-click the MSI file and follow the installation wizard. + + + Download: [voicebox_x64-setup.exe](https://github.com/jamiepine/voicebox/releases/latest/download/voicebox_x64-setup.exe) + + Run the executable and follow the installation wizard. + + + +### Linux + + + Linux builds are coming soon. Currently blocked by GitHub runner disk space limitations. + + +## First Launch + +When you launch Voicebox for the first time: + +1. **Model Download** — Qwen3-TTS model (~2-4GB) will download automatically on first use +2. **Data Directory** — Voice profiles and generated audio are stored in: + - macOS: `~/Library/Application Support/com.voicebox.app/` + - Windows: `%APPDATA%/com.voicebox.app/` + - Linux: `~/.config/com.voicebox.app/` + +3. **Backend Server** — The bundled Python server starts automatically + + + First generation will be slower due to model downloads. Subsequent runs use cached models. + + +## System Requirements + +### Minimum + +- **OS:** macOS 11+, Windows 10+, or Linux +- **RAM:** 8GB +- **Storage:** 5GB free space (for models and data) +- **CPU:** Modern multi-core processor + +### Recommended + +- **RAM:** 16GB+ +- **GPU:** CUDA-capable NVIDIA GPU (for faster generation) +- **Storage:** 10GB+ free space + + + CPU inference is supported but significantly slower than GPU. A CUDA-capable GPU is highly recommended for real-time workflows. + + +## Verification + +After installation, verify everything works: + +1. Launch Voicebox +2. Check the server status indicator in the bottom-left corner (should be green) +3. Navigate to **Profiles** and create a test profile +4. Generate a short audio clip to verify the TTS engine works + + + If you see a green status indicator and can generate audio, you're all set! + + +## Next Steps + + + Create your first voice profile and generate speech + diff --git a/docs2/content/docs/overview/introduction.mdx b/docs2/content/docs/overview/introduction.mdx new file mode 100644 index 00000000..99569a73 --- /dev/null +++ b/docs2/content/docs/overview/introduction.mdx @@ -0,0 +1,58 @@ +--- +title: "Introduction" +description: "Welcome to Voicebox - the open-source voice synthesis studio" +--- + +## What is Voicebox? + +Voicebox is a **local-first voice cloning studio** with DAW-like features for professional voice synthesis. Think of it as the **Ollama for voice** — download models, clone voices, and generate speech entirely on your machine. + + + Voicebox App Screenshot + + +Unlike cloud services that lock your voice data behind subscriptions, Voicebox gives you: + +- **Complete privacy** — models and voice data stay on your machine +- **Professional tools** — multi-track timeline editor, audio trimming, conversation mixing +- **Model flexibility** — currently powered by Qwen3-TTS, with support for XTTS, Bark, and other models coming soon +- **API-first** — use the desktop app or integrate voice synthesis into your own projects +- **Native performance** — built with Tauri (Rust), not Electron + +Download a voice model, clone any voice from a few seconds of audio, and compose multi-voice projects with studio-grade editing tools. No Python install required, no cloud dependency, no limits. + +## Key Features + + + + Instant cloning from just a few seconds of audio with Qwen3-TTS + + + Multi-track timeline for creating conversations and narratives + + + REST API for integrating voice synthesis into your apps + + + Everything runs on your machine - complete privacy + + + +## Use Cases + +- **Game Development** — Generate dynamic dialogue for characters +- **Content Creation** — Produce podcasts and video voiceovers +- **Accessibility** — Build text-to-speech tools +- **Voice Assistants** — Create custom voice interfaces +- **Production Pipelines** — Automate voiceover workflows + +## Next Steps + + + + Download and install Voicebox on your machine + + + Get up and running in 5 minutes + + diff --git a/docs2/content/docs/overview/meta.json b/docs2/content/docs/overview/meta.json new file mode 100644 index 00000000..d3906004 --- /dev/null +++ b/docs2/content/docs/overview/meta.json @@ -0,0 +1,17 @@ +{ + "title": "Overview", + "pages": [ + "introduction", + "installation", + "quick-start", + "voice-cloning", + "stories-editor", + "recording-transcription", + "generation-history", + "remote-mode", + "creating-voice-profiles", + "generating-speech", + "building-stories", + "troubleshooting" + ] +} diff --git a/docs2/content/docs/overview/quick-start.mdx b/docs2/content/docs/overview/quick-start.mdx new file mode 100644 index 00000000..6fee0496 --- /dev/null +++ b/docs2/content/docs/overview/quick-start.mdx @@ -0,0 +1,154 @@ +--- +title: "Quick Start" +description: "Get started with Voicebox in 5 minutes" +--- + +This guide will walk you through creating your first voice profile and generating speech. + +## Prerequisites + +Make sure you have [installed Voicebox](/overview/installation) and launched the app. + +## Step 1: Create a Voice Profile + +Voice profiles are the foundation of Voicebox. Each profile contains voice samples that the AI uses to clone the voice. + + + + Click the **Profiles** tab in the sidebar + + + + Click the **+ New Profile** button + + Fill in the details: + - **Name:** A descriptive name (e.g., "John Smith") + - **Language:** Select the primary language + - **Description:** Optional notes about the voice + + + + You have two options: + + **Option A: Upload Audio** + - Click **Upload Sample** + - Select an audio file (WAV, MP3, or M4A) + - Ideal length: 10-30 seconds of clear speech + + **Option B: Record Live** + - Click **Record Sample** + - Speak clearly for 10-30 seconds + - Click stop when finished + + + + Click **Create Profile** to save + + + + + For best results, use clean audio with minimal background noise and consistent speaking tone. + + +## Step 2: Generate Speech + +Now let's use your new voice profile to generate speech. + + + + Click the **Generate** tab in the sidebar + + + + Choose your newly created profile from the dropdown + + + + Type or paste the text you want to generate: + + ``` + Hello! This is my first voice generation with Voicebox. + ``` + + + + Click **Generate** and wait a few seconds + + + First generation may take longer due to model initialization. Subsequent generations will be faster. + + + + + - Click **Play** to preview the audio + - Click **Download** to save the audio file + - The generation is also saved to your **History** + + + +## Step 3: Build a Story (Optional) + +The Stories Editor lets you create multi-voice narratives with a timeline-based interface. + + + + Navigate to **Stories** and click **+ New Story** + + + + Click **+ Add Track** to create tracks for different speakers + + + + - Drag generated audio from your History + - Or generate new clips directly in the timeline + - Arrange clips on the timeline + + + + - Trim clips by dragging edges + - Adjust timing and spacing + - Click **Export** to render the final audio + + + +## What's Next? + + + + Learn advanced techniques for high-quality voice cloning + + + Integrate Voicebox into your own applications + + + Master the multi-track timeline editor + + + Connect to a GPU server for faster generation + + + +## Tips for Success + + + + - Use 10-30 seconds of clear, consistent speech + - Avoid background noise and echo + - Multiple samples from the same speaker improve quality + - Match the speaking style you want to generate + + + + - Use a CUDA-capable GPU for 5-10x faster generation + - Enable voice prompt caching for repeated generations + - Consider running the backend on a remote GPU server + + + + - **Server won't start:** Check if port 17493 is available + - **Poor audio quality:** Try adding more voice samples + - **Slow generation:** Verify GPU acceleration is enabled + - See the full [Troubleshooting Guide](/overview/troubleshooting) for more + + diff --git a/docs2/content/docs/overview/recording-transcription.mdx b/docs2/content/docs/overview/recording-transcription.mdx new file mode 100644 index 00000000..7f1e4871 --- /dev/null +++ b/docs2/content/docs/overview/recording-transcription.mdx @@ -0,0 +1,64 @@ +--- +title: "Recording & Transcription" +description: "Record audio and transcribe speech with Whisper" +--- + +## Recording + +Voicebox includes built-in recording capabilities for creating voice samples and capturing audio. + +### Features + +- **Microphone input** - Record from any audio input device +- **System audio capture** - Record desktop audio (macOS/Windows) +- **Waveform visualization** - See audio levels in real-time +- **Multiple formats** - Export as WAV, MP3, or M4A + +### How to Record + + + + Choose your microphone or system audio + + + Click the record button and speak clearly + + + Click stop when finished + + + Use as voice sample or export to file + + + +## Transcription + +Automatic speech-to-text powered by OpenAI's Whisper model. + +### Features + +- **High accuracy** - Industry-leading speech recognition +- **Multiple languages** - Supports 50+ languages +- **Automatic detection** - Language auto-detection +- **Timestamps** - Word-level timing information + +### How to Transcribe + + + + Choose a recording or upload an audio file + + + Select language or use auto-detect + + + Click transcribe and wait for processing + + + Review text and export as needed + + + + + Transcription is useful for creating voice samples from existing audio or generating subtitles. + diff --git a/docs2/content/docs/overview/remote-mode.mdx b/docs2/content/docs/overview/remote-mode.mdx new file mode 100644 index 00000000..533147fd --- /dev/null +++ b/docs2/content/docs/overview/remote-mode.mdx @@ -0,0 +1,138 @@ +--- +title: "Remote Mode" +description: "Connect to a GPU server for faster generation" +--- + +## Overview + +Remote Mode allows you to run the Voicebox backend on a separate machine (like a GPU server) while using the desktop app on your local machine. + +## Use Cases + +- **No local GPU** - Use a cloud GPU or remote workstation +- **Faster generation** - Leverage powerful remote hardware +- **Shared infrastructure** - Multiple users connect to one server +- **Laptop workflows** - Keep your laptop cool and battery-efficient + +## Architecture + +In Remote Mode, the Voicebox desktop app (running on your local machine) communicates with the backend server (running on a remote machine) via HTTP. The local app provides only the user interface, while the remote server handles all the heavy processing including the TTS models, API endpoints, and audio generation. + +## Setting Up Remote Mode + +### On the Server + + + + ```bash + # Clone the repo + git clone https://github.com/jamiepine/voicebox.git + cd voicebox/backend + + # Install Python dependencies + pip install -r requirements.txt + pip install git+https://github.com/QwenLM/Qwen3-TTS.git + ``` + + + + ```bash + # Allow external connections + uvicorn main:app --host 0.0.0.0 --port 17493 + ``` + + + This exposes the server to your network. Use a firewall or VPN for security. + + + + + ```bash + # Ubuntu/Debian + sudo ufw allow 17493 + + # Or use your cloud provider's firewall settings + ``` + + + +### On the Client + + + + In Voicebox, go to **Settings → Server** + + + + Toggle **Use Remote Server** + + + + ``` + http://:17493 + ``` + + Replace `` with your server's IP address + + + + Click **Test Connection** to verify + + + +## Cloud Deployment + +### AWS EC2 + +```bash +# Launch a GPU instance (e.g., g4dn.xlarge) +# Install dependencies +# Start server with --host 0.0.0.0 +``` + +### Vast.ai + +```bash +# Rent a GPU instance +# SSH in and clone repo +# Start server +``` + +### RunPod + +```bash +# Deploy a pod with CUDA support +# Install Voicebox backend +# Expose port 17493 +``` + +## Security Considerations + + + The API currently has no authentication. Only use on trusted networks or with a VPN. + + +**Best Practices:** +- Use a VPN (WireGuard, Tailscale) instead of exposing to the internet +- Run behind a reverse proxy with authentication (nginx + basic auth) +- Use HTTPS with SSL certificates +- Firewall rules to limit access to specific IPs + +## Performance + +Expected performance on various GPUs: + +| GPU | Generation Speed | +|-----|------------------| +| RTX 4090 | ~2-3s per 10 words | +| RTX 3090 | ~3-4s per 10 words | +| RTX 3060 | ~5-7s per 10 words | +| CPU (12-core) | ~20-30s per 10 words | + + + A GPU with 8GB+ VRAM is recommended for best performance. + + +## Troubleshooting + +See the [Troubleshooting Guide](/guides/troubleshooting#remote-mode-issues) for common remote mode issues. diff --git a/docs2/content/docs/overview/stories-editor.mdx b/docs2/content/docs/overview/stories-editor.mdx new file mode 100644 index 00000000..7e9629d3 --- /dev/null +++ b/docs2/content/docs/overview/stories-editor.mdx @@ -0,0 +1,64 @@ +--- +title: "Stories Editor" +description: "Create multi-voice narratives with a timeline-based editor" +--- + +## Overview + +The Stories Editor is a DAW-like timeline interface for creating multi-voice narratives, podcasts, and conversations. + +## Features + + + + Arrange multiple voice tracks in parallel + + + Trim and split clips directly in the timeline + + + Preview with synchronized playhead + + + Build conversations with multiple speakers + + + +## Creating a Story + + + + Navigate to **Stories** and click **+ New Story** + + + Create separate tracks for each voice/speaker + + + - Drag from generation history + - Generate new clips inline + - Upload audio files + + + - Position clips on timeline + - Trim clip edges + - Adjust spacing and timing + + + Render the final mixed audio + + + +## Use Cases + +- **Podcasts**: Multi-host conversations +- **Audiobooks**: Narrator + character voices +- **Game Dialogue**: Character interactions +- **Video Voiceovers**: Multiple speakers +- **Audio Drama**: Full voice casts + +## Coming Soon + +- Word-level editing +- Crossfades and transitions +- Audio effects (reverb, EQ) +- Real-time collaboration diff --git a/docs2/content/docs/overview/troubleshooting.mdx b/docs2/content/docs/overview/troubleshooting.mdx new file mode 100644 index 00000000..f57c29f2 --- /dev/null +++ b/docs2/content/docs/overview/troubleshooting.mdx @@ -0,0 +1,477 @@ +--- +title: "Troubleshooting" +description: "Common issues and solutions for Voicebox" +--- + +This guide covers common issues you might encounter when using or developing Voicebox, along with solutions. + +## Installation Issues + +### macOS: "App is damaged and can't be opened" + +This occurs because the app isn't signed with an Apple Developer certificate. + +**Solution:** +```bash +# Remove the quarantine attribute +xattr -cr /Applications/Voicebox.app +``` + +### Windows: SmartScreen Warning + +Windows SmartScreen may warn that the app is unrecognized. + +**Solution:** +- Click "More info" +- Click "Run anyway" + + + This is expected for unsigned applications. We're working on code signing for future releases. + + +## Server Issues + +### Backend Server Won't Start + +**Symptoms:** +- Red status indicator in bottom-left corner +- "Failed to connect to server" error + +**Solutions:** + + + + Check if port 17493 is already in use: + + ```bash + # macOS/Linux + lsof -i :17493 + + # Windows + netstat -ano | findstr :17493 + ``` + + Kill the process using the port: + ```bash + # macOS/Linux + kill -9 + + # Windows + taskkill /PID /F + ``` + + + + The server binary might not have execute permissions: + + ```bash + # macOS/Linux + chmod +x ~/Library/Application\ Support/com.voicebox.app/backend/voicebox-server + ``` + + + + View server logs for errors: + + **macOS:** + ```bash + tail -f ~/Library/Application\ Support/com.voicebox.app/logs/server.log + ``` + + **Windows:** + ```bash + type %APPDATA%\com.voicebox.app\logs\server.log + ``` + + + +### Connection Timeout + +**Symptoms:** +- Long loading times +- "Connection timeout" errors + +**Solution:** +- Restart the app +- Check your firewall settings +- Ensure localhost is accessible + +## Generation Issues + +### First Generation is Very Slow + +**Symptoms:** +- First generation takes 2-5 minutes +- Progress indicator stuck at "Loading model..." + +**Explanation:** +This is expected behavior. The first generation downloads the Qwen3-TTS model (~2-4GB) and initializes it. + +**Solution:** +- Wait for the initial download to complete +- Subsequent generations will be much faster +- Check your internet connection + +### Poor Voice Quality + +**Symptoms:** +- Robotic or unnatural voice +- Missing emotion or prosody +- Pronunciation errors + +**Solutions:** + + + + - Use 10-30 seconds of clear audio + - Avoid background noise + - Ensure consistent speaking tone + - Add multiple samples from the same speaker + + + + The generated voice will mimic the tone and style of your samples. If your sample is monotone, the generation will be too. + + + + - Use proper punctuation + - Add commas for natural pauses + - Capitalize proper nouns + + + +### Generation Fails with "Out of Memory" + +**Symptoms:** +- Generation crashes +- "CUDA out of memory" or "RuntimeError: out of memory" + +**Solutions:** + + + + Close other GPU-intensive applications: + - Games + - Video editors + - Multiple browser tabs with WebGL + + Then restart Voicebox. + + + + If your GPU doesn't have enough VRAM (need 6GB+), use CPU mode: + + Settings → Generation → Use CPU instead of GPU + + + CPU generation is 5-10x slower but uses system RAM instead of VRAM. + + + + + For long text, split it into smaller chunks instead of generating all at once. + + + +## Audio Issues + +### No Audio Playback + +**Symptoms:** +- Generated audio won't play +- Playback button doesn't respond + +**Solutions:** +- Check system audio settings +- Ensure audio output device is connected +- Try exporting and playing in a media player + +### Crackling or Distorted Audio + +**Symptoms:** +- Audio has static or distortion +- Clipping sounds + +**Solutions:** +- Check if your input samples have distortion +- Reduce playback volume +- Re-generate with cleaner voice samples + +## Development Issues + +### Backend Won't Start in Dev Mode + +**Symptoms:** +- `bun run dev:server` fails +- Import errors or module not found + +**Solutions:** + + + + Ensure Python 3.11 or higher: + + ```bash + python --version + ``` + + If not, install Python 3.11+ and recreate the virtual environment. + + + + Ensure venv is activated: + + ```bash + # macOS/Linux + source backend/venv/bin/activate + + # Windows + backend\venv\Scripts\activate + ``` + + You should see `(venv)` in your prompt. + + + + Reinstall dependencies: + + ```bash + cd backend + pip install -r requirements.txt + pip install git+https://github.com/QwenLM/Qwen3-TTS.git + ``` + + + +### Tauri Build Fails + +**Symptoms:** +- `bun run tauri build` fails +- Rust compilation errors + +**Solutions:** + +```bash +# Clean build artifacts +cd tauri/src-tauri +cargo clean + +# Update Rust +rustup update + +# Try building again +cd ../.. +bun run tauri build +``` + +### OpenAPI Client Generation Fails + +**Symptoms:** +- `./scripts/generate-api.sh` fails +- "Failed to fetch schema" error + +**Solutions:** + + + + ```bash + curl http://localhost:17493/openapi.json + ``` + + Should return JSON. If not, start the backend. + + + + Ensure nothing else is using port 17493 + + + + ```bash + cd backend + source venv/bin/activate + uvicorn main:app --reload --port 17493 + + # In another terminal + ./scripts/generate-api.sh + ``` + + + +## Database Issues + +### "Database is locked" Error + +**Symptoms:** +- Profile or generation operations fail +- SQLite lock errors + +**Solutions:** +- Close all Voicebox instances +- Delete the lock file: + ```bash + # macOS + rm ~/Library/Application\ Support/com.voicebox.app/data/voicebox.db-shm + rm ~/Library/Application\ Support/com.voicebox.app/data/voicebox.db-wal + ``` + +### Corrupted Database + +**Symptoms:** +- App crashes on launch +- Data missing or corrupted + +**Solutions:** + + + This will delete all your voice profiles and generation history. Export important profiles first if possible. + + +```bash +# macOS +rm ~/Library/Application\ Support/com.voicebox.app/data/voicebox.db + +# Windows +del %APPDATA%\com.voicebox.app\data\voicebox.db +``` + +Restart the app to create a fresh database. + +## Model Issues + +### Model Download Fails + +**Symptoms:** +- "Failed to download model" error +- Stuck at "Downloading..." + +**Solutions:** +- Check your internet connection +- Check HuggingFace Hub status +- Try using a VPN if HuggingFace is blocked in your region +- Manually download and place in cache directory + +### Wrong Model Version + +**Symptoms:** +- Generation quality suddenly degraded +- Different voice output + +**Solutions:** +Clear the model cache and re-download: + +```bash +# macOS +rm -rf ~/.cache/huggingface/hub/models--Qwen* + +# Windows +rmdir /s %USERPROFILE%\.cache\huggingface\hub\models--Qwen* +``` + +## Performance Issues + +### Slow Generation on GPU + +**Symptoms:** +- Generation slower than expected +- GPU not being utilized + +**Solutions:** + + + + ```bash + nvidia-smi + ``` + + Should show your GPU. If not, install CUDA drivers. + + + + If you have multiple GPUs, ensure Voicebox is using the right one. + + Settings → Generation → GPU Device + + + + Outdated drivers can cause performance issues. Update to the latest NVIDIA drivers. + + + +### High Memory Usage + +**Symptoms:** +- App uses excessive RAM +- System becomes sluggish + +**Solutions:** +- Close unused voice profiles +- Clear generation history +- Restart the app periodically + +## Remote Mode Issues + +### Can't Connect to Remote Server + +**Symptoms:** +- "Connection refused" error +- Remote server not found + +**Solutions:** + + + + Ensure the remote server is running: + + ```bash + curl http://:17493/health + ``` + + + + Ensure port 17493 is open on the remote server: + + ```bash + # Allow port on Ubuntu/Debian + sudo ufw allow 17493 + ``` + + + + - Ensure both machines are on the same network (for local servers) + - Use IP address instead of hostname + - Try pinging the server: `ping ` + + + +## Still Having Issues? + +If you're still experiencing problems: + +1. **Check GitHub Issues:** [github.com/jamiepine/voicebox/issues](https://github.com/jamiepine/voicebox/issues) +2. **Open a New Issue:** Provide: + - Operating system and version + - Voicebox version + - Steps to reproduce + - Error messages or logs +3. **Join Discord:** [discord.gg/voicebox](https://discord.gg/voicebox) (coming soon) + +## Diagnostic Information + +When reporting issues, include this information: + +```bash +# Voicebox version +# Check Help → About in the app + +# Operating system +uname -a # macOS/Linux +systeminfo # Windows + +# Python version (for dev issues) +python --version + +# GPU info (if generation issues) +nvidia-smi # NVIDIA GPUs +``` + +For more detailed troubleshooting, see the [TROUBLESHOOTING.md](https://github.com/jamiepine/voicebox/blob/main/docs/TROUBLESHOOTING.md) file in the repository. diff --git a/docs2/content/docs/overview/voice-cloning.mdx b/docs2/content/docs/overview/voice-cloning.mdx new file mode 100644 index 00000000..efc6caf8 --- /dev/null +++ b/docs2/content/docs/overview/voice-cloning.mdx @@ -0,0 +1,75 @@ +--- +title: "Voice Cloning" +description: "Clone any voice from just a few seconds of audio" +--- + +## Overview + +Voicebox uses **Qwen3-TTS** from Alibaba to achieve near-perfect voice cloning from just a few seconds of audio. The model captures prosody, emotion, and natural cadence. + +## How It Works + + + + Provide 10-30 seconds of clear speech from the target voice + + + Qwen3-TTS analyzes vocal characteristics, tone, and speaking patterns + + + The model generates a voice embedding for synthesis + + + Use the profile to generate any text in the cloned voice + + + +## Best Practices + +### Sample Quality + + + + - Use 10-30 seconds of audio + - Clear, consistent speaking + - Minimal background noise + - Natural speaking pace + + + - Very short clips (< 5 seconds) + - Heavy background noise + - Music or overlapping voices + - Heavily processed audio + + + +### Multiple Samples + +Adding multiple samples from the same speaker can improve quality: + +- Different speaking styles (casual, formal) +- Different emotions (happy, serious) +- Different recording conditions + + + The model will learn a more robust representation from diverse samples. + + +## Supported Languages + +Currently supported: +- English +- Chinese (Mandarin) + +More languages coming soon. + +## Limitations + + + Voice cloning should only be used with consent. Ensure you have permission to clone someone's voice. + + +- Quality depends on sample clarity +- Works best with consistent speaking tone +- May struggle with extreme accents or speech impediments +- Background noise reduces quality diff --git a/docs2/content/docs/plans/DOCKER_DEPLOYMENT.md b/docs2/content/docs/plans/DOCKER_DEPLOYMENT.md new file mode 100644 index 00000000..9db97495 --- /dev/null +++ b/docs2/content/docs/plans/DOCKER_DEPLOYMENT.md @@ -0,0 +1,761 @@ +--- +title: "Docker Deployment Guide" +description: "Docker deployment guide for Voicebox (In Development)" +--- + +**Status:** In Development for v0.2.0 +**Requested By:** Reddit community ([thread](https://reddit.com/r/LocalLLaMA/...)) + +## Overview + +Docker support makes Voicebox easier to deploy, especially for: + +- **Consistent Environments**: Same setup across dev/staging/prod +- **GPU Passthrough**: Easy NVIDIA/AMD GPU access +- **Server Deployments**: Run on headless Linux servers +- **Multi-User Setups**: Isolate instances per user/team +- **Cloud Platforms**: Deploy to AWS, GCP, Azure, DigitalOcean + +## Quick Start + +### Using Pre-Built Images (Recommended) + +```bash +# CPU-only version +docker run -p 8000:8000 -v voicebox-data:/app/data \ + ghcr.io/jamiepine/voicebox:latest + +# NVIDIA GPU version +docker run --gpus all -p 8000:8000 -v voicebox-data:/app/data \ + ghcr.io/jamiepine/voicebox:latest-cuda + +# AMD GPU version (experimental) +docker run --device=/dev/kfd --device=/dev/dri -p 8000:8000 \ + -v voicebox-data:/app/data \ + ghcr.io/jamiepine/voicebox:latest-rocm +``` + +Then open: `http://localhost:8000` + +### Using Docker Compose (Easiest) + +Create `docker-compose.yml`: + +```yaml +version: '3.8' + +services: + voicebox: + image: ghcr.io/jamiepine/voicebox:latest-cuda + ports: + - "8000:8000" + volumes: + - voicebox-data:/app/data + - huggingface-cache:/root/.cache/huggingface + environment: + - GPU_MEMORY_FRACTION=0.8 # Use 80% of GPU memory + - TTS_MODE=local + - WHISPER_MODE=local + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] + +volumes: + voicebox-data: + huggingface-cache: +``` + +Run: +```bash +docker compose up -d +``` + +## Building From Source + +### Basic Dockerfile + +```dockerfile +# Dockerfile +FROM python:3.11-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + git \ + build-essential \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* + +# Copy application +COPY backend/ /app/backend/ +COPY requirements.txt /app/ + +# Install Python dependencies +RUN pip install --no-cache-dir -r requirements.txt +RUN pip install --no-cache-dir git+https://github.com/QwenLM/Qwen3-TTS.git + +# Create data directory +RUN mkdir -p /app/data + +# Expose port +EXPOSE 8000 + +# Run server +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +Build and run: +```bash +docker build -t voicebox . +docker run -p 8000:8000 -v $(pwd)/data:/app/data voicebox +``` + +### Multi-Stage Build (Optimized) + +Smaller image size by separating build and runtime: + +```dockerfile +# Dockerfile.optimized +# Stage 1: Build dependencies +FROM python:3.11-slim AS builder + +WORKDIR /build + +RUN apt-get update && apt-get install -y \ + git build-essential && \ + rm -rf /var/lib/apt/lists/* + +COPY backend/requirements.txt . +RUN pip install --no-cache-dir --target=/build/packages \ + -r requirements.txt + +RUN pip install --no-cache-dir --target=/build/packages \ + git+https://github.com/QwenLM/Qwen3-TTS.git + +# Stage 2: Runtime +FROM python:3.11-slim + +WORKDIR /app + +# Install only runtime dependencies +RUN apt-get update && apt-get install -y \ + ffmpeg \ + && rm -rf /var/lib/apt/lists/* + +# Copy installed packages from builder +COPY --from=builder /build/packages /usr/local/lib/python3.11/site-packages/ + +# Copy application code +COPY backend/ /app/backend/ + +# Create data directory +RUN mkdir -p /app/data + +EXPOSE 8000 + +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +Build: +```bash +docker build -f Dockerfile.optimized -t voicebox:slim . +``` + +## GPU Support + +### NVIDIA GPUs (CUDA) + +**Dockerfile:** +```dockerfile +FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04 + +# Install Python +RUN apt-get update && apt-get install -y \ + python3.11 python3-pip git ffmpeg && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Install PyTorch with CUDA support +COPY backend/requirements.txt . +RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 + +# Install other dependencies +RUN pip3 install -r requirements.txt +RUN pip3 install git+https://github.com/QwenLM/Qwen3-TTS.git + +COPY backend/ /app/backend/ + +EXPOSE 8000 +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +**Run with GPU:** +```bash +docker run --gpus all -p 8000:8000 \ + -v voicebox-data:/app/data \ + voicebox:cuda +``` + +**Docker Compose with GPU:** +```yaml +services: + voicebox: + image: voicebox:cuda + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: all + capabilities: [gpu] +``` + +### AMD GPUs (ROCm) - Experimental + +**Dockerfile:** +```dockerfile +FROM rocm/dev-ubuntu-22.04:6.0 + +# Install Python +RUN apt-get update && apt-get install -y \ + python3.11 python3-pip git ffmpeg && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Install PyTorch with ROCm support +COPY backend/requirements.txt . +RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.0 + +# Install other dependencies +RUN pip3 install -r requirements.txt +RUN pip3 install git+https://github.com/QwenLM/Qwen3-TTS.git + +# Set ROCm environment variables +ENV HSA_OVERRIDE_GFX_VERSION=10.3.0 +ENV ROCM_PATH=/opt/rocm + +COPY backend/ /app/backend/ + +EXPOSE 8000 +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000"] +``` + +**Run with AMD GPU:** +```bash +docker run --device=/dev/kfd --device=/dev/dri \ + --group-add video --ipc=host --cap-add=SYS_PTRACE \ + --security-opt seccomp=unconfined \ + -p 8000:8000 -v voicebox-data:/app/data \ + voicebox:rocm +``` + +**Note:** ROCm support varies by GPU model. Works best on Linux. See [AMD ROCm docs](https://rocm.docs.amd.com) for compatibility. + +## Volume Mounts + +### Essential Volumes + +```bash +docker run -v voicebox-data:/app/data \ # Profiles, generations, history + -v huggingface-cache:/root/.cache/huggingface \ # Downloaded models + -p 8000:8000 voicebox +``` + +### Development Volume Mounts + +For development with hot-reload: + +```bash +docker run -v $(pwd)/backend:/app/backend \ # Live code changes + -v voicebox-data:/app/data \ + -e RELOAD=true \ + -p 8000:8000 voicebox +``` + +### Custom Model Storage + +Use external model directory: + +```bash +docker run -v /path/to/models:/models \ + -e MODELS_DIR=/models \ + -v voicebox-data:/app/data \ + -p 8000:8000 voicebox +``` + +## Environment Variables + +Configure Voicebox via environment variables: + +```bash +docker run -e TTS_MODE=local \ + -e WHISPER_MODE=openai-api \ + -e OPENAI_API_KEY=sk-... \ + -e GPU_MEMORY_FRACTION=0.8 \ + -e LOG_LEVEL=info \ + -p 8000:8000 voicebox +``` + +### Available Variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `TTS_MODE` | `local` | TTS provider: `local`, `remote` | +| `TTS_REMOTE_URL` | - | URL for remote TTS server | +| `WHISPER_MODE` | `local` | Whisper provider: `local`, `openai-api`, `remote` | +| `WHISPER_REMOTE_URL` | - | URL for remote Whisper server | +| `OPENAI_API_KEY` | - | OpenAI API key (if using OpenAI Whisper) | +| `GPU_MEMORY_FRACTION` | `0.9` | Fraction of GPU memory to use (0.0-1.0) | +| `DATA_DIR` | `/app/data` | Directory for profiles/generations | +| `MODELS_DIR` | `/app/models` | Directory for local models | +| `LOG_LEVEL` | `info` | Logging level: `debug`, `info`, `warning`, `error` | +| `RELOAD` | `false` | Enable hot-reload for development | + +## Complete Docker Compose Examples + +### Production Deployment + +```yaml +# docker-compose.prod.yml +version: '3.8' + +services: + voicebox: + image: ghcr.io/jamiepine/voicebox:latest-cuda + container_name: voicebox + restart: unless-stopped + ports: + - "8000:8000" + volumes: + - voicebox-data:/app/data + - huggingface-cache:/root/.cache/huggingface + environment: + - TTS_MODE=local + - WHISPER_MODE=local + - GPU_MEMORY_FRACTION=0.8 + - LOG_LEVEL=info + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + +volumes: + voicebox-data: + driver: local + huggingface-cache: + driver: local +``` + +Run: +```bash +docker compose -f docker-compose.prod.yml up -d +``` + +### Development Setup + +```yaml +# docker-compose.dev.yml +version: '3.8' + +services: + voicebox: + build: + context: . + dockerfile: Dockerfile + ports: + - "8000:8000" + volumes: + - ./backend:/app/backend:ro + - voicebox-data:/app/data + - huggingface-cache:/root/.cache/huggingface + environment: + - RELOAD=true + - LOG_LEVEL=debug + - TTS_MODE=local + command: uvicorn backend.main:app --host 0.0.0.0 --port 8000 --reload + +volumes: + voicebox-data: + huggingface-cache: +``` + +### Multi-Service Stack + +Full stack with reverse proxy and monitoring: + +```yaml +# docker-compose.stack.yml +version: '3.8' + +services: + # Main Voicebox app + voicebox: + image: ghcr.io/jamiepine/voicebox:latest-cuda + restart: unless-stopped + volumes: + - voicebox-data:/app/data + - huggingface-cache:/root/.cache/huggingface + environment: + - TTS_MODE=local + - WHISPER_MODE=local + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] + + # Nginx reverse proxy + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./ssl:/etc/nginx/ssl:ro + depends_on: + - voicebox + + # Prometheus monitoring (optional) + prometheus: + image: prom/prometheus + ports: + - "9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus-data:/prometheus + +volumes: + voicebox-data: + huggingface-cache: + prometheus-data: +``` + +## Cloud Deployment + +### AWS EC2 + +1. **Launch GPU Instance** (g4dn.xlarge or p3.2xlarge) +2. **Install Docker + nvidia-docker:** + ```bash + # Amazon Linux 2 + sudo yum install -y docker + sudo systemctl start docker + distribution=$(. /etc/os-release;echo $ID$VERSION_ID) + curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - + curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \ + sudo tee /etc/apt/sources.list.d/nvidia-docker.list + sudo apt-get update && sudo apt-get install -y nvidia-docker2 + sudo systemctl restart docker + ``` +3. **Deploy:** + ```bash + docker run --gpus all -d -p 80:8000 \ + -v voicebox-data:/app/data \ + --restart unless-stopped \ + ghcr.io/jamiepine/voicebox:latest-cuda + ``` + +### DigitalOcean + +Use GPU Droplet + Docker: + +```bash +# Create droplet via CLI +doctl compute droplet create voicebox \ + --size gpu-h100x1-80gb \ + --image ubuntu-22-04-x64 \ + --region nyc3 + +# SSH and deploy +ssh root@ +curl -fsSL https://get.docker.com -o get-docker.sh +sh get-docker.sh +docker run --gpus all -d -p 80:8000 voicebox:cuda +``` + +### Google Cloud Run (CPU-only) + +```bash +# Build and push +docker build -t gcr.io/your-project/voicebox . +docker push gcr.io/your-project/voicebox + +# Deploy to Cloud Run +gcloud run deploy voicebox \ + --image gcr.io/your-project/voicebox \ + --platform managed \ + --region us-central1 \ + --memory 4Gi \ + --cpu 2 \ + --port 8000 +``` + +### Fly.io + +Create `fly.toml`: +```toml +app = "voicebox" + +[build] + image = "ghcr.io/jamiepine/voicebox:latest" + +[[services]] + http_checks = [] + internal_port = 8000 + protocol = "tcp" + + [[services.ports]] + port = 80 + handlers = ["http"] + + [[services.ports]] + port = 443 + handlers = ["tls", "http"] + +[mounts] + source = "voicebox_data" + destination = "/app/data" +``` + +Deploy: +```bash +fly launch +fly deploy +``` + +## Troubleshooting + +### GPU Not Detected + +**Check NVIDIA Docker:** +```bash +docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi +``` + +If this fails, reinstall nvidia-docker2. + +**Check AMD ROCm:** +```bash +docker run --rm --device=/dev/kfd --device=/dev/dri rocm/dev-ubuntu-22.04:6.0 rocminfo +``` + +### Permission Errors + +Container can't write to volumes: +```bash +# Fix permissions +docker run --user $(id -u):$(id -g) -v $(pwd)/data:/app/data voicebox +``` + +### Out of Memory + +Reduce GPU memory usage: +```bash +docker run -e GPU_MEMORY_FRACTION=0.5 voicebox +``` + +Or use CPU-only: +```bash +docker run -e DEVICE=cpu voicebox +``` + +### Model Download Fails + +Ensure HuggingFace cache is writable: +```bash +docker run -v huggingface-cache:/root/.cache/huggingface voicebox +``` + +Or use host cache: +```bash +docker run -v ~/.cache/huggingface:/root/.cache/huggingface voicebox +``` + +### Port Already in Use + +Change host port: +```bash +docker run -p 8080:8000 voicebox # Use port 8080 instead +``` + +## Security Best Practices + +### 1. Don't Run as Root + +Create non-root user in Dockerfile: +```dockerfile +RUN useradd -m -u 1000 voicebox +USER voicebox +``` + +### 2. Use Secrets for API Keys + +Don't put API keys in docker-compose.yml: + +```bash +# Use Docker secrets +echo "sk-your-key" | docker secret create openai_key - + +docker service create \ + --secret openai_key \ + -e OPENAI_API_KEY_FILE=/run/secrets/openai_key \ + voicebox +``` + +### 3. Network Isolation + +Use internal networks for multi-container setups: + +```yaml +services: + voicebox: + networks: + - internal + nginx: + networks: + - internal + - external + ports: + - "80:80" + +networks: + internal: + internal: true + external: +``` + +### 4. Resource Limits + +Prevent resource exhaustion: + +```yaml +services: + voicebox: + deploy: + resources: + limits: + cpus: '4' + memory: 8G + reservations: + cpus: '2' + memory: 4G +``` + +## Performance Tuning + +### GPU Memory Management + +```bash +# Use 80% of GPU (default 90%) +docker run -e GPU_MEMORY_FRACTION=0.8 voicebox + +# Allow GPU memory growth (prevents OOM) +docker run -e TF_FORCE_GPU_ALLOW_GROWTH=true voicebox +``` + +### Model Caching + +Pre-download models to volume: + +```bash +# Download models first +docker run --rm -v huggingface-cache:/root/.cache/huggingface \ + voicebox python -c " +from transformers import WhisperProcessor, WhisperForConditionalGeneration +WhisperProcessor.from_pretrained('openai/whisper-base') +WhisperForConditionalGeneration.from_pretrained('openai/whisper-base') +" + +# Then run normally +docker run -v huggingface-cache:/root/.cache/huggingface voicebox +``` + +### Multi-Worker Setup + +Use uvicorn workers for better throughput: + +```dockerfile +CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"] +``` + +## Monitoring + +### Health Checks + +Built-in health endpoint: +```bash +curl http://localhost:8000/health +``` + +Docker health check: +```yaml +healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 +``` + +### Prometheus Metrics + +Add metrics exporter: +```python +# backend/main.py +from prometheus_fastapi_instrumentator import Instrumentator + +Instrumentator().instrument(app).expose(app) +``` + +Then scrape `/metrics` with Prometheus. + +### Logs + +View container logs: +```bash +docker logs -f voicebox + +# Or with compose +docker compose logs -f voicebox +``` + +## Next Steps + +- [ ] Publish official images to GitHub Container Registry +- [ ] Add Kubernetes Helm charts +- [ ] Create Docker Desktop extension +- [ ] Add automated vulnerability scanning +- [ ] Support ARM64 builds for Raspberry Pi / Apple Silicon + +## Contributing + +Help improve Docker support: +1. Test on different platforms (AMD GPU, ARM64, etc.) +2. Submit Dockerfile optimizations +3. Share deployment configurations +4. Report issues: [GitHub Issues](https://github.com/jamiepine/voicebox/issues) + +## Resources + +- [Docker Documentation](https://docs.docker.com) +- [NVIDIA Container Toolkit](https://github.com/NVIDIA/nvidia-docker) +- [AMD ROCm Docker](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/docker.html) +- [Docker Compose Reference](https://docs.docker.com/compose/compose-file/) diff --git a/docs2/content/docs/plans/EXTERNAL_PROVIDERS.md b/docs2/content/docs/plans/EXTERNAL_PROVIDERS.md new file mode 100644 index 00000000..a8023bde --- /dev/null +++ b/docs2/content/docs/plans/EXTERNAL_PROVIDERS.md @@ -0,0 +1,438 @@ +--- +title: "External Provider Support" +description: "External provider support for Voicebox (Planned)" +--- + +**Status:** Planned for v0.2.0 +**Discussion:** [Reddit Thread](https://reddit.com/r/LocalLLaMA/...) + +## Overview + +External provider support allows you to connect Voicebox to remotely-hosted TTS and Whisper services instead of running models locally. This is useful for: + +- **Existing GPU Infrastructure**: You already have Qwen3-TTS running on a GPU server +- **AMD GPU Users**: Run models on your AMD hardware, use Voicebox as the UI +- **Cloud Deployments**: Host models on Modal, Replicate, RunPod, etc. +- **Team Sharing**: Multiple users share one GPU server running models +- **Mixed Deployments**: Local Whisper + remote TTS, or vice versa + +## Architecture + +``` +┌─────────────────┐ HTTP/API ┌──────────────────┐ +│ Voicebox UI │ ───────────────────────> │ Your TTS Server │ +│ + Backend │ │ (Qwen3-TTS on │ +│ │ <─────────────────────── │ AMD/NVIDIA GPU)│ +│ - Profiles │ Audio + Metadata └──────────────────┘ +│ - History │ +│ - Audio Edit │ HTTP/API ┌──────────────────┐ +│ - UI │ ───────────────────────> │ Whisper Service │ +└─────────────────┘ │ (OpenAI API or │ + │ self-hosted) │ + └──────────────────┘ +``` + +**What Voicebox Still Handles:** +- Voice profile management +- Generation history +- Audio trimming/editing +- Multi-track story editor +- UI/UX layer + +**What External Providers Handle:** +- Model inference (TTS generation, transcription) +- GPU allocation +- Model loading/caching + +## Configuration + +### Environment Variables + +```bash +# TTS Provider +TTS_MODE=remote # local | remote +TTS_REMOTE_URL=http://192.168.1.100:8000 # Your TTS server URL +TTS_API_KEY=your-api-key # Optional authentication + +# Whisper Provider +WHISPER_MODE=openai-api # local | openai-api | remote +WHISPER_REMOTE_URL=http://localhost:9000 # For self-hosted Whisper +OPENAI_API_KEY=sk-... # For OpenAI Whisper API +``` + +### Voicebox Config UI (Planned) + +Settings page will include: +- Provider selection dropdowns +- URL/API key inputs +- Connection test button +- Latency/status indicators + +## Hosting External Services + +### Option 1: Simple FastAPI Server (Recommended) + +Create a lightweight server to expose your local Qwen3-TTS model: + +```python +# tts_server.py +from fastapi import FastAPI, UploadFile, File +from qwen_tts import Qwen3TTSModel +import numpy as np +import base64 + +app = FastAPI() +model = Qwen3TTSModel.from_pretrained( + "Qwen/Qwen3-TTS-12Hz-1.7B-Base", + device_map="cuda" # or "cpu" for AMD ROCm: use torch+rocm +) + +@app.post("/v1/generate") +async def generate( + text: str, + voice_prompt: dict, + language: str = "en", + seed: int = None +): + """Generate speech from text using voice prompt.""" + audio, sample_rate = model.generate_voice_clone( + text=text, + voice_clone_prompt=voice_prompt, + ) + + # Return as base64 for transport + audio_bytes = audio.tobytes() + return { + "audio": base64.b64encode(audio_bytes).decode(), + "sample_rate": sample_rate, + "dtype": str(audio.dtype) + } + +@app.post("/v1/create_voice_prompt") +async def create_voice_prompt( + audio: UploadFile = File(...), + reference_text: str = "" +): + """Create voice prompt from reference audio.""" + # Save uploaded audio temporarily + audio_path = f"/tmp/{audio.filename}" + with open(audio_path, "wb") as f: + f.write(await audio.read()) + + # Create voice prompt + voice_prompt = model.create_voice_clone_prompt( + ref_audio=audio_path, + ref_text=reference_text, + ) + + return {"voice_prompt": voice_prompt} + +@app.get("/health") +async def health(): + return { + "status": "healthy", + "model": "Qwen3-TTS-12Hz-1.7B-Base", + "device": str(model.device) + } + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +**Run it:** +```bash +# Install dependencies +pip install fastapi uvicorn qwen-tts torch + +# For AMD GPUs, use ROCm PyTorch: +pip install torch --index-url https://download.pytorch.org/whl/rocm6.4 + +# Start server +python tts_server.py +``` + +### Option 2: vLLM (If Supported) + +```bash +vllm serve Qwen/Qwen3-TTS-12Hz-1.7B-Base \ + --host 0.0.0.0 \ + --port 8000 \ + --gpu-memory-utilization 0.9 +``` + +### Option 3: Cloud Platforms + +**Modal.com Example:** +```python +import modal + +app = modal.App("qwen-tts") +image = modal.Image.debian_slim().pip_install("qwen-tts", "torch") + +@app.function(gpu="A10G", image=image) +@modal.web_endpoint(method="POST") +def generate(text: str, voice_prompt: dict): + from qwen_tts import Qwen3TTSModel + model = Qwen3TTSModel.from_pretrained("Qwen/Qwen3-TTS-12Hz-1.7B-Base") + audio, sr = model.generate_voice_clone(text, voice_prompt) + return {"audio": audio.tolist(), "sample_rate": sr} +``` + +Deploy: `modal deploy tts_server.py` +Get URL: `https://yourapp--generate.modal.run` + +## API Specification + +External TTS providers must implement these endpoints: + +### `POST /v1/generate` + +Generate speech from text. + +**Request:** +```json +{ + "text": "Hello, this is a test.", + "voice_prompt": { /* voice prompt object */ }, + "language": "en", + "seed": 12345 +} +``` + +**Response:** +```json +{ + "audio": "base64-encoded-audio-bytes", + "sample_rate": 24000, + "dtype": "float32" +} +``` + +### `POST /v1/create_voice_prompt` + +Create a voice prompt from reference audio. + +**Request:** (multipart/form-data) +- `audio`: Audio file upload +- `reference_text`: Transcript of the audio + +**Response:** +```json +{ + "voice_prompt": { /* voice prompt object */ } +} +``` + +### `GET /health` + +Health check endpoint. + +**Response:** +```json +{ + "status": "healthy", + "model": "Qwen3-TTS-12Hz-1.7B-Base", + "device": "cuda:0" +} +``` + +## Whisper External Providers + +### OpenAI Whisper API + +Simply set: +```bash +WHISPER_MODE=openai-api +OPENAI_API_KEY=sk-... +``` + +Voicebox will use OpenAI's Whisper API automatically. + +### Self-Hosted Whisper + +Run your own Whisper server: + +```python +# whisper_server.py +from fastapi import FastAPI, UploadFile, File +from transformers import WhisperProcessor, WhisperForConditionalGeneration +import librosa + +app = FastAPI() +processor = WhisperProcessor.from_pretrained("openai/whisper-base") +model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-base") + +@app.post("/v1/transcribe") +async def transcribe(audio: UploadFile = File(...), language: str = None): + # Load audio + audio_path = f"/tmp/{audio.filename}" + with open(audio_path, "wb") as f: + f.write(await audio.read()) + + audio_data, sr = librosa.load(audio_path, sr=16000) + + # Process + inputs = processor(audio_data, sampling_rate=16000, return_tensors="pt") + predicted_ids = model.generate(inputs["input_features"]) + transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)[0] + + return {"text": transcription} +``` + +Configure Voicebox: +```bash +WHISPER_MODE=remote +WHISPER_REMOTE_URL=http://localhost:9000 +``` + +## Use Cases + +### 1. AMD GPU User with Existing Setup + +**Scenario:** You have a Radeon 7900 XTX running Qwen3-TTS on Linux. + +**Setup:** +1. Run `tts_server.py` on your AMD box (ROCm PyTorch) +2. Configure Voicebox: `TTS_MODE=remote`, `TTS_REMOTE_URL=http://amd-box:8000` +3. Use Voicebox UI for profiles, generation, editing +4. TTS happens on your AMD GPU + +### 2. Team Deployment + +**Scenario:** 5 team members, 1 GPU server. + +**Setup:** +1. Deploy TTS server on shared GPU box +2. Each person runs Voicebox desktop app locally +3. All point to same `TTS_REMOTE_URL` +4. Profiles and history stay local per user +5. GPU usage is shared + +### 3. Hybrid Local/Remote + +**Scenario:** Fast local Whisper, heavy TTS on cloud. + +**Setup:** +```bash +TTS_MODE=remote +TTS_REMOTE_URL=https://your-modal-app.modal.run + +WHISPER_MODE=local # Fast transcription on your CPU +``` + +### 4. OpenAI Whisper + Self-Hosted TTS + +**Scenario:** Use OpenAI's API for transcription, run TTS locally. + +**Setup:** +```bash +TTS_MODE=local + +WHISPER_MODE=openai-api +OPENAI_API_KEY=sk-... +``` + +## Security Considerations + +### Authentication + +Add API key authentication to your external server: + +```python +from fastapi import Header, HTTPException + +API_KEY = "your-secret-key" + +async def verify_api_key(x_api_key: str = Header(...)): + if x_api_key != API_KEY: + raise HTTPException(status_code=401, detail="Invalid API key") + +@app.post("/v1/generate", dependencies=[Depends(verify_api_key)]) +async def generate(...): + ... +``` + +Configure Voicebox: +```bash +TTS_API_KEY=your-secret-key +``` + +### Network Security + +- **VPN/Tailscale**: Use private network for remote servers +- **HTTPS**: Use reverse proxy (nginx/Caddy) with SSL certificates +- **Firewall**: Restrict access to known IPs + +### Rate Limiting + +Protect your external server: + +```python +from slowapi import Limiter +from slowapi.util import get_remote_address + +limiter = Limiter(key_func=get_remote_address) +app.state.limiter = limiter + +@app.post("/v1/generate") +@limiter.limit("10/minute") +async def generate(...): + ... +``` + +## Performance Considerations + +### Latency + +External providers add network latency: +- **Local network**: ~10-50ms overhead (negligible) +- **Same datacenter**: ~1-5ms overhead +- **Cross-region cloud**: 50-200ms+ overhead + +For real-time applications, keep TTS server on local network or same cloud region. + +### Caching + +Implement response caching on external server: + +```python +from functools import lru_cache + +@lru_cache(maxsize=1000) +def get_cached_generation(text, voice_prompt_hash, language, seed): + return model.generate_voice_clone(text, voice_prompt) +``` + +### Load Balancing + +For high-traffic deployments, run multiple TTS servers behind a load balancer: + +``` +Voicebox ──> Load Balancer ──> TTS Server 1 (GPU 1) + ├──> TTS Server 2 (GPU 2) + └──> TTS Server 3 (GPU 3) +``` + +## Future Enhancements + +- [ ] **Provider Marketplace**: Built-in directory of compatible providers +- [ ] **Automatic Fallback**: If remote fails, fallback to local +- [ ] **Cost Tracking**: Monitor API usage and costs +- [ ] **Performance Metrics**: Latency, throughput dashboards +- [ ] **Multi-Provider**: Use different providers for different voices/languages + +## Contributing + +If you build an external provider, please share: +1. Server implementation +2. Performance benchmarks +3. Deployment guide + +Submit to: [GitHub Discussions](https://github.com/jamiepine/voicebox/discussions) + +## Questions? + +- **Discord**: [Join the community](https://discord.gg/...) +- **GitHub**: [Open an issue](https://github.com/jamiepine/voicebox/issues) +- **Docs**: [Full documentation](https://voicebox.sh/docs) diff --git a/docs2/content/docs/plans/MLX_AUDIO.md b/docs2/content/docs/plans/MLX_AUDIO.md new file mode 100644 index 00000000..97acfa1a --- /dev/null +++ b/docs2/content/docs/plans/MLX_AUDIO.md @@ -0,0 +1,399 @@ +--- +title: "MLX Audio Integration" +description: "MLX Audio integration for Voicebox (Validated)" +--- + +**Status:** Validated ✅ +**Context:** [mlx-audio v0.3.1 release](https://github.com/Blaizzy/mlx-audio) + +## Validation Results + +We validated mlx-audio in an isolated environment (`mlx-test/`). Key findings: + +| Metric | Result | +|--------|--------| +| MLX Version | 0.30.4 | +| Model Load Time | ~1s (after initial download) | +| Generation RTF | **0.5-0.6x** (1.7-2x faster than real-time) | +| Test Hardware | Apple Silicon Mac | + +### Model Mapping + +| voicebox (PyTorch) | mlx-audio (MLX) | +|--------------------|-----------------| +| `Qwen/Qwen3-TTS-12Hz-1.7B-Base` | `mlx-community/Qwen3-TTS-12Hz-1.7B-Base-bf16` | +| `Qwen/Qwen3-TTS-12Hz-0.6B-Base` | (not yet converted) | + +### mlx-audio API + +The API uses a **generator-based streaming pattern**: + +```python +from mlx_audio.tts import load + +model = load("mlx-community/Qwen3-TTS-12Hz-1.7B-Base-bf16") + +# generate() yields GenerationResult objects +for result in model.generate("Hello world"): + audio = result.audio # numpy array of samples + sample_rate = result.sample_rate # 24000 + rtf = result.real_time_factor # e.g., 0.55 +``` + +### Known Warnings (harmless) + +``` +You are using a model of type qwen3_tts to instantiate a model of type . +The tokenizer you are loading... with an incorrect regex pattern... +``` + +These warnings appear but don't affect functionality or output quality. + +### Demo Script + +Run `mlx-test/demo.py` to test: +```bash +cd mlx-test && source venv/bin/activate && python demo.py "Your text here" +``` + +## Problem + +Apple Silicon users are stuck on CPU inference while Windows and Linux users get CUDA acceleration. The current PyTorch MPS backend has stability issues (lines 34-36 in `backend/tts.py` and `backend/transcribe.py`), forcing a CPU fallback that makes voicebox significantly slower on M1/M2/M3 Macs. + +This creates a poor experience for a large portion of users who bought Apple Silicon specifically for ML workloads. + +## Solution + +Integrate [mlx-audio](https://github.com/Blaizzy/mlx-audio) as the inference engine for macOS Apple Silicon builds. MLX is Apple's native ML framework, optimized for Metal and the unified memory architecture. It's fast, stable, and already supports the same Qwen3-TTS models we use. + +**Key wins:** +- Native GPU acceleration on Apple Silicon (no more CPU fallback) +- Streaming TTS support (faster perceived latency) +- Memory optimizations (run larger models on less RAM) +- Fixed 0.6B silence bug that we currently ship +- Same Qwen3-TTS models (zero migration cost for users) + +## Architecture + +### Current Stack +``` +┌─────────────────────────┐ +│ PyTorch + Qwen3-TTS │ +│ (CPU only on macOS) │ +└─────────────────────────┘ +``` + +### Proposed Stack +``` +┌─────────────────────────────────────────┐ +│ Platform Detection at Runtime │ +└─────────────────────────────────────────┘ + │ + ├─── Apple Silicon (aarch64-darwin) + │ ┌─────────────────────────┐ + │ │ MLX Audio Backend │ + │ │ - Qwen3-TTS (mlx) │ + │ │ - Whisper (mlx) │ + │ │ - Streaming support │ + │ └─────────────────────────┘ + │ + └─── Other (x86_64, Windows, Linux) + ┌─────────────────────────┐ + │ PyTorch Backend │ + │ - Qwen3-TTS (pytorch) │ + │ - Whisper (pytorch) │ + │ - CUDA if available │ + └─────────────────────────┘ +``` + +## Implementation Phases + +### Phase 1: Platform Detection & Dependency Management + +Create a backend that switches between PyTorch and MLX based on runtime platform detection. + +**New files:** +- `backend/platform.py` - Detect Apple Silicon, return backend type +- `backend/backends/__init__.py` - Backend factory pattern +- `backend/requirements-mlx.txt` - MLX-specific deps (macOS only) + +**Modified files:** +- `backend/requirements.txt` - Keep PyTorch as default +- `backend/main.py` - Import from backend factory instead of direct imports + +**Platform detection logic:** +```python +def get_backend_type() -> str: + """Detect best backend for current platform.""" + if platform.system() == "Darwin" and platform.machine() == "arm64": + # Apple Silicon detected + try: + import mlx + return "mlx" + except ImportError: + return "pytorch" # Fallback if mlx not installed + return "pytorch" +``` + +### Phase 2: MLX Backend Implementation + +Create parallel implementations of TTS and STT using mlx-audio. + +**New files:** +- `backend/backends/mlx_backend.py` - MLX inference engine +- `backend/backends/pytorch_backend.py` - Refactor current code into backend + +**Interface both backends must implement:** +```python +class TTSBackend(Protocol): + async def load_model(self, model_size: str) -> None: ... + async def create_voice_prompt(self, audio_path: str, reference_text: str) -> dict: ... + async def generate(self, text: str, voice_prompt: dict, **kwargs) -> Tuple[np.ndarray, int]: ... + async def generate_streaming(self, text: str, voice_prompt: dict, **kwargs) -> AsyncIterator[bytes]: ... + def unload_model(self) -> None: ... + +class STTBackend(Protocol): + async def load_model(self, model_size: str) -> None: ... + async def transcribe(self, audio_path: str, language: Optional[str]) -> str: ... + def unload_model(self) -> None: ... +``` + +**MLX backend implementation notes:** + +mlx-audio's `generate()` returns a generator by default (streaming is built-in): + +```python +# MLX backend wrapper +from mlx_audio.tts import load + +class MLXTTSBackend: + def __init__(self): + self.model = None + + async def load_model(self, model_size: str) -> None: + model_map = { + "1.7B": "mlx-community/Qwen3-TTS-12Hz-1.7B-Base-bf16", + # "0.6B": needs conversion to mlx format + } + self.model = load(model_map[model_size]) + + async def generate(self, text: str, voice_prompt: dict, **kwargs) -> Tuple[np.ndarray, int]: + # Collect all chunks from generator + chunks = [] + for result in self.model.generate(text): # TODO: add voice_prompt support + chunks.append(np.array(result.audio)) + return np.concatenate(chunks), 24000 +``` + +**MLX-specific features to expose:** +- Streaming TTS (new endpoint: `/api/generate/stream`) +- Memory-optimized model loading +- Qwen3-ASR for transcription (in addition to Whisper) + +### Phase 3: API Layer Updates + +Update FastAPI endpoints to support new streaming capabilities and maintain backward compatibility. + +**Modified files:** +- `backend/main.py` - Add streaming endpoints +- `backend/tts.py` - Refactor to use backend abstraction +- `backend/transcribe.py` - Refactor to use backend abstraction + +**New endpoints:** +```python +@app.post("/api/generate/stream") +async def generate_stream(...) -> StreamingResponse: + """Stream TTS chunks as they're generated (MLX only).""" + backend = get_backend() + if not hasattr(backend, 'generate_streaming'): + raise HTTPException(501, "Streaming not supported on this backend") + return StreamingResponse(backend.generate_streaming(...), media_type="audio/wav") +``` + +**Backward compatibility:** +- Keep all existing `/api/generate` endpoints unchanged +- PyTorch backend users see no behavior change +- MLX users automatically get faster inference, streaming is opt-in + +### Phase 4: Frontend Integration + +Add UI indicators for backend type and streaming progress. + +**Modified files:** +- `app/src/hooks/useGenerationForm.tsx` - Add streaming support +- `app/src/components/GenerationForm.tsx` - Show backend badge, streaming toggle +- `app/src/lib/api.ts` - Add streaming API client + +**UI additions:** +- Badge showing current backend ("MLX" or "PyTorch") +- Toggle for streaming mode (disabled if PyTorch) +- Real-time streaming playback (WaveSurfer progressive loading) + +### Phase 5: Build & Distribution + +Create separate installers for MLX (Apple Silicon) and PyTorch (Universal). + +**Modified files:** +- `tauri/src-tauri/tauri.conf.json` - Add target-specific builds +- `.github/workflows/release.yml` - Build both variants + +**Build matrix:** +```yaml +- target: aarch64-apple-darwin + backend: mlx + installer: voicebox-macos-silicon-{version}.dmg + +- target: x86_64-apple-darwin + backend: pytorch + installer: voicebox-macos-intel-{version}.dmg + +- target: x86_64-pc-windows-msvc + backend: pytorch + installer: voicebox-windows-{version}.exe +``` + +**Installation flow:** +- Auto-detect architecture, recommend correct installer +- MLX installer includes `mlx-audio` in embedded Python +- PyTorch installer includes `torch` in embedded Python +- Both can coexist (different backend, same profile format) + +### Phase 6: Testing & Validation + +Ensure both backends produce compatible outputs. + +**New files:** +- `backend/tests/test_backend_parity.py` - Verify both backends produce similar audio +- `backend/tests/test_streaming.py` - Streaming-specific tests + +**Test scenarios:** +- Same voice prompt on both backends → similar (not identical) audio output +- Profile created on MLX → loads on PyTorch (and vice versa) +- Streaming chunks assemble into valid WAV file +- Model downloads work on both backends +- Memory usage stays within bounds + +### Phase 7: Documentation + +Update user-facing docs and developer guides. + +**New files:** +- `docs/developer/BACKENDS.md` - Guide for adding new backends +- `docs/overview/performance.md` - Backend comparison benchmarks + +**Modified files:** +- `README.md` - Note Apple Silicon acceleration +- `docs/TROUBLESHOOTING.md` - Add MLX-specific issues + +**Key docs to write:** +- Which installer to download (architecture detection) +- Performance comparison (MLX vs PyTorch on same M2 hardware) +- How streaming mode works +- How to force PyTorch on Apple Silicon (for debugging) + +## Technical Decisions + +### Why Dual Backend Instead of MLX-Only? + +**Pros of dual backend:** +- Windows and Intel Mac users unaffected +- Easier testing (can compare outputs) +- Fallback if MLX has issues + +**Cons of dual backend:** +- More code to maintain +- Two dependency trees +- Build complexity (separate installers) + +**Decision:** Dual backend. The maintenance cost is worth it to avoid breaking existing users and to have a fallback. + +### Why Separate Installers Instead of Runtime Detection? + +**Pros of separate installers:** +- Smaller bundle size (don't ship both PyTorch and MLX) +- Clearer to users which version they have +- Easier to debug (no "which backend am I running?" confusion) +- Can optimize each build for its target + +**Cons:** +- More installers to build and test +- Users might download the wrong one + +**Decision:** Separate installers. Bundle size matters (PyTorch + MLX would be huge), and we can auto-detect architecture on the download page. + +### Streaming vs Batch Generation + +MLX supports streaming, PyTorch doesn't (without significant work). Should streaming be: +1. MLX-only feature (✅ chosen) +2. Implemented for both (lots of work) +3. Not exposed at all (wasted opportunity) + +**Decision:** MLX-only. Expose as opt-in feature with graceful degradation (button disabled on PyTorch backend). + +## Migration Path + +Nothing needs migrating, macos users will just notice a speed-boost in inference + +**Data format compatibility:** +- Profiles (SQLite) → no schema changes needed +- Voice prompts (cached) → backend-agnostic (just numpy arrays) +- Audio files → unchanged + +## Performance Expectations + +### Measured Results (from validation) + +| Metric | MLX (measured) | PyTorch CPU (estimated) | +|--------|----------------|-------------------------| +| **6s audio generation** | ~3-4s | ~10-15s | +| **Real-time factor** | 0.5-0.6x | 2-3x | +| **Model load (cached)** | ~1s | ~3-5s | + +### TTS Generation (1.7B model, ~20s output) +- **PyTorch CPU (M2 Max):** ~45-60s (slower than real-time) +- **MLX (M2 Max):** ~8-12s (faster than real-time) +- **Improvement:** ~4-5x faster + +### Whisper Transcription (10s audio clip) +- **PyTorch CPU:** ~5-8s +- **MLX:** ~1-2s +- **Improvement:** ~3-4x faster + +### Memory Usage (1.7B model) +- **PyTorch:** ~8-10GB (no GPU offload, so CPU RAM) +- **MLX:** ~4-6GB (unified memory, better optimization) +- **Improvement:** ~40% less RAM + +Full benchmarks will be in `docs/overview/performance.md` after Phase 6. + +## Open Questions + +- **Should we support Qwen3-ASR (MLX-only) in addition to Whisper?** Adds another model option but increases complexity. Probably phase 8+. - Sure +- **Should we backport streaming to PyTorch?** Would require chunking and callback-based generation. Probably not worth it given mlx-audio already has it. - No +- **What's the auto-update UX for migrating PyTorch→MLX users?** Needs design. Don't want to force reinstall, but also want to make upgrade obvious. - it just updates, users see nothing +- **Do we expose backend selection in settings or hide it?** Leaning toward auto-detect only, with env var override for power users. + +## Success Metrics + +How we'll know this worked: + +1. **Performance:** Apple Silicon users report generation faster than real-time +2. **Adoption:** >80% of macOS downloads are MLX build within 1 month +3. **Stability:** <5% increase in bug reports (backend abstraction doesn't introduce regressions) +4. **Feedback:** Positive sentiment in Discord/GitHub about macOS performance + +## Related Work + +- [PyTorch MPS tracking issue](https://github.com/pytorch/pytorch/issues/77764) - Why we can't use MPS directly +- [mlx-audio server implementation](https://github.com/Blaizzy/mlx-audio/blob/main/examples/server.py) - Reference for streaming API +- [MLX Whisper benchmarks](https://github.com/ml-explore/mlx-examples/tree/main/whisper) - Performance data + +## Next Steps + +1. ~~Validate mlx-audio can load Qwen3-TTS models (quick test)~~ ✅ Done - see `mlx-test/` +2. Get approval on dual-backend architecture +3. Start Phase 1 (platform detection) + +## Questions? + +Feedback welcome in GitHub discussions or Discord. diff --git a/docs2/content/docs/plans/OPENAI_SUPPORT.md b/docs2/content/docs/plans/OPENAI_SUPPORT.md new file mode 100644 index 00000000..e2efb008 --- /dev/null +++ b/docs2/content/docs/plans/OPENAI_SUPPORT.md @@ -0,0 +1,238 @@ +--- +title: "OpenAI API Compatibility" +description: "OpenAI API compatibility for Voicebox (Planned)" +--- + +**Status:** Planned for v0.2.0 + +**Issue:** [#10 OpenAI API compatibility](https://github.com/jamiepine/voicebox/issues/10) + +## Overview + +This feature exposes OpenAI-compatible endpoints from Voicebox, allowing any tool, library, or application that speaks the OpenAI Audio API to use Voicebox as a drop-in local replacement. + +```mermaid +flowchart LR + subgraph clients [External Clients] + SDK[OpenAI SDK] + Curl[curl / HTTP] + Apps[Third-party Apps] + end + + subgraph voicebox [Voicebox Server] + OpenAI["/v1/audio/* endpoints"] + TTS[TTSModel] + Whisper[WhisperModel] + Profiles[Voice Profiles] + end + + SDK --> OpenAI + Curl --> OpenAI + Apps --> OpenAI + OpenAI --> TTS + OpenAI --> Whisper + OpenAI --> Profiles +``` + +## Use Cases + +- **OpenAI SDK users**: `openai.audio.speech.create()` works with Voicebox +- **LLM frameworks**: LangChain, AutoGen, etc. can use Voicebox for TTS +- **Shell scripts**: `curl` commands copy-pasted from OpenAI docs work +- **Existing integrations**: Any tool expecting OpenAI's API works without code changes + +## Endpoints to Implement + +### 1. `POST /v1/audio/speech` (TTS) + +OpenAI spec: https://platform.openai.com/docs/api-reference/audio/createSpeech + +**Request:** + +```json +{ + "model": "tts-1", + "input": "Hello world!", + "voice": "alloy", + "response_format": "mp3", + "speed": 1.0 +} +``` + +**Response:** Audio file (mp3, wav, opus, aac, flac, pcm) + +**Voice Mapping Strategy:** + +- `voice` parameter maps to Voicebox profile names (case-insensitive) +- If no match, use a configurable default profile +- Support special syntax: `voice: "profile:uuid"` for explicit profile ID + +### 2. `POST /v1/audio/transcriptions` (Whisper) + +OpenAI spec: https://platform.openai.com/docs/api-reference/audio/createTranscription + +**Request:** (multipart/form-data) + +- `file`: Audio file +- `model`: "whisper-1" +- `language`: Optional language hint +- `response_format`: json, text, srt, verbose_json, vtt + +**Response:** + +```json +{ + "text": "Hello world!" +} +``` + +## Implementation Details + +### New File: `backend/openai_compat.py` + +Create a dedicated module with an APIRouter for OpenAI-compatible endpoints: + +```python +from fastapi import APIRouter, UploadFile, File, Form, HTTPException +from fastapi.responses import StreamingResponse +from pydantic import BaseModel +from typing import Literal, Optional + +router = APIRouter(prefix="/v1/audio", tags=["OpenAI Compatible"]) + +class SpeechRequest(BaseModel): + model: str = "tts-1" + input: str + voice: str = "alloy" + response_format: Literal["mp3", "wav", "opus", "aac", "flac", "pcm"] = "mp3" + speed: float = 1.0 + +@router.post("/speech") +async def create_speech(request: SpeechRequest, db: Session = Depends(get_db)): + # 1. Map voice name to profile + # 2. Generate audio using existing TTSModel + # 3. Convert to requested format + # 4. Return audio stream + ... + +@router.post("/transcriptions") +async def create_transcription( + file: UploadFile = File(...), + model: str = Form("whisper-1"), + language: Optional[str] = Form(None), + response_format: str = Form("json"), +): + # 1. Save uploaded file + # 2. Transcribe using existing WhisperModel + # 3. Return in requested format + ... +``` + +### Voice Profile Resolution + +Add helper in [backend/profiles.py](backend/profiles.py): + +```python +async def resolve_voice_for_openai(voice: str, db: Session) -> Optional[VoiceProfile]: + """ + Resolve OpenAI voice parameter to a Voicebox profile. + + Priority: + 1. Exact profile name match (case-insensitive) + 2. Profile ID match (if voice starts with "profile:") + 3. Default profile from config + 4. First available profile + """ + ... +``` + +### Audio Format Conversion + +Add conversion utilities in [backend/utils/audio.py](backend/utils/audio.py): + +```python +def convert_audio_format( + audio: np.ndarray, + sample_rate: int, + target_format: str, # mp3, wav, opus, aac, flac, pcm +) -> bytes: + """Convert audio to target format using ffmpeg or pydub.""" + ... +``` + +### Configuration + +Add to [backend/config.py](backend/config.py): + +```python +# OpenAI API Compatibility +OPENAI_COMPAT_ENABLED = True +OPENAI_COMPAT_DEFAULT_VOICE = None # Profile ID or name for default voice +OPENAI_COMPAT_REQUIRE_AUTH = False # Require API key validation +OPENAI_COMPAT_API_KEY = None # If set, validate against this +``` + +### Integration with main.py + +In [backend/main.py](backend/main.py), include the router: + +```python +from . import openai_compat + +# Add OpenAI-compatible routes +if config.OPENAI_COMPAT_ENABLED: + app.include_router(openai_compat.router) +``` + +## Streaming Support (Future Enhancement) + +Initial implementation returns complete audio. Streaming can be added later: + +```python +@router.post("/speech") +async def create_speech(request: SpeechRequest): + if request.stream: + return StreamingResponse( + generate_audio_chunks(request), + media_type=f"audio/{request.response_format}" + ) + ... +``` + +## Testing + +Example usage after implementation: + +```bash +# TTS with curl +curl http://localhost:8000/v1/audio/speech \ + -H "Content-Type: application/json" \ + -d '{"model": "tts-1", "input": "Hello!", "voice": "MyProfile"}' \ + --output speech.mp3 + +# With OpenAI Python SDK +from openai import OpenAI +client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused") +response = client.audio.speech.create( + model="tts-1", + voice="MyProfile", + input="Hello world!" +) +response.stream_to_file("output.mp3") + +# Transcription +curl http://localhost:8000/v1/audio/transcriptions \ + -F file=@audio.mp3 \ + -F model="whisper-1" +``` + +## Security Considerations + +- Optional API key validation (for shared deployments) +- Rate limiting on endpoints +- Input length limits (same as existing `/generate` endpoint) + +## Dependencies + +- `pydub` or `ffmpeg-python` for audio format conversion (mp3, opus, etc.) +- No changes to existing TTS/Whisper model code \ No newline at end of file diff --git a/docs2/content/docs/plans/meta.json b/docs2/content/docs/plans/meta.json new file mode 100644 index 00000000..04a72e7d --- /dev/null +++ b/docs2/content/docs/plans/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Plans", + "pages": [ + "DOCKER_DEPLOYMENT", + "EXTERNAL_PROVIDERS", + "MLX_AUDIO", + "OPENAI_SUPPORT" + ] +} diff --git a/docs2/lib/layout.shared.tsx b/docs2/lib/layout.shared.tsx new file mode 100644 index 00000000..1d8ed146 --- /dev/null +++ b/docs2/lib/layout.shared.tsx @@ -0,0 +1,9 @@ +import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'; + +export function baseOptions(): BaseLayoutProps { + return { + nav: { + title: 'Voicebox', + }, + }; +} diff --git a/docs2/lib/openapi.ts b/docs2/lib/openapi.ts new file mode 100644 index 00000000..f6ddf3ba --- /dev/null +++ b/docs2/lib/openapi.ts @@ -0,0 +1,5 @@ +import { createOpenAPI } from 'fumadocs-openapi/server'; + +export const openapi = createOpenAPI({ + input: ['./openapi.json'], +}); diff --git a/docs2/lib/source.ts b/docs2/lib/source.ts new file mode 100644 index 00000000..c829e387 --- /dev/null +++ b/docs2/lib/source.ts @@ -0,0 +1,27 @@ +import { docs } from '@/.source'; +import { type InferPageType, loader } from 'fumadocs-core/source'; +import { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons'; + +// See https://fumadocs.dev/docs/headless/source-api for more info +export const source = loader({ + baseUrl: '/docs', + source: docs.toFumadocsSource(), + plugins: [lucideIconsPlugin()], +}); + +export function getPageImage(page: InferPageType) { + const segments = [...page.slugs, 'image.png']; + + return { + segments, + url: `/og/docs/${segments.join('/')}`, + }; +} + +export async function getLLMText(page: InferPageType) { + const processed = await page.data.getText('processed'); + + return `# ${page.data.title} + +${processed}`; +} diff --git a/docs2/mdx-components.tsx b/docs2/mdx-components.tsx new file mode 100644 index 00000000..064c62fd --- /dev/null +++ b/docs2/mdx-components.tsx @@ -0,0 +1,39 @@ +import defaultMdxComponents from 'fumadocs-ui/mdx'; +import type { MDXComponents } from 'mdx/types'; +import { + Frame, + CardGroup, + MintlifyCard, + Steps, + Step, + Tip, + Note, + Warning, + Info, + Danger, + AccordionGroup, + Accordion, +} from '@/components/mintlify-compat'; +import { APIPage } from '@/components/api-page'; + +export function getMDXComponents(components?: MDXComponents): MDXComponents { + return { + ...defaultMdxComponents, + // Mintlify compatibility components + Frame, + CardGroup, + Card: MintlifyCard, + Steps, + Step, + Tip, + Note, + Warning, + Info, + Danger, + AccordionGroup, + Accordion, + // OpenAPI component + APIPage, + ...components, + }; +} diff --git a/docs2/next.config.mjs b/docs2/next.config.mjs new file mode 100644 index 00000000..d4246450 --- /dev/null +++ b/docs2/next.config.mjs @@ -0,0 +1,17 @@ +import { createMDX } from 'fumadocs-mdx/next'; + +const withMDX = createMDX(); + +/** @type {import('next').NextConfig} */ +const config = { + reactStrictMode: true, + webpack: (config) => { + config.experiments = { + ...config.experiments, + topLevelAwait: true, + }; + return config; + }, +}; + +export default withMDX(config); diff --git a/docs2/openapi.json b/docs2/openapi.json new file mode 100644 index 00000000..8f31bf9e --- /dev/null +++ b/docs2/openapi.json @@ -0,0 +1 @@ +{"openapi":"3.1.0","info":{"title":"voicebox API","description":"Production-quality Qwen3-TTS voice cloning API","version":"0.1.0"},"paths":{"/":{"get":{"summary":"Root","description":"Root endpoint.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health","description":"Health check endpoint.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/profiles":{"get":{"summary":"List Profiles","description":"List all voice profiles.","operationId":"list_profiles_profiles_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VoiceProfileResponse"},"type":"array","title":"Response List Profiles Profiles Get"}}}}}},"post":{"summary":"Create Profile","description":"Create a new voice profile.","operationId":"create_profile_profiles_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profiles/{profile_id}":{"get":{"summary":"Get Profile","description":"Get a voice profile by ID.","operationId":"get_profile_profiles__profile_id__get","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Profile","description":"Update a voice profile.","operationId":"update_profile_profiles__profile_id__put","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Profile","description":"Delete a voice profile.","operationId":"delete_profile_profiles__profile_id__delete","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profiles/{profile_id}/samples":{"post":{"summary":"Add Profile Sample","description":"Add a sample to a voice profile.","operationId":"add_profile_sample_profiles__profile_id__samples_post","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_add_profile_sample_profiles__profile_id__samples_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileSampleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Get Profile Samples","description":"Get all samples for a profile.","operationId":"get_profile_samples_profiles__profile_id__samples_get","parameters":[{"name":"profile_id","in":"path","required":true,"schema":{"type":"string","title":"Profile Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ProfileSampleResponse"},"title":"Response Get Profile Samples Profiles Profile Id Samples Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/profiles/samples/{sample_id}":{"delete":{"summary":"Delete Profile Sample","description":"Delete a profile sample.","operationId":"delete_profile_sample_profiles_samples__sample_id__delete","parameters":[{"name":"sample_id","in":"path","required":true,"schema":{"type":"string","title":"Sample Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/generate":{"post":{"summary":"Generate Speech","description":"Generate speech from text using a voice profile.","operationId":"generate_speech_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history":{"get":{"summary":"List History","description":"List generation history with optional filters.","operationId":"list_history_history_get","parameters":[{"name":"profile_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Id"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HistoryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history/{generation_id}":{"get":{"summary":"Get Generation","description":"Get a generation by ID.","operationId":"get_generation_history__generation_id__get","parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","title":"Generation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Generation","description":"Delete a generation.","operationId":"delete_generation_history__generation_id__delete","parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","title":"Generation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/history/stats":{"get":{"summary":"Get Stats","description":"Get generation statistics.","operationId":"get_stats_history_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/transcribe":{"post":{"summary":"Transcribe Audio","description":"Transcribe audio file to text.","operationId":"transcribe_audio_transcribe_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_transcribe_audio_transcribe_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranscriptionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/audio/{generation_id}":{"get":{"summary":"Get Audio","description":"Serve generated audio file.","operationId":"get_audio_audio__generation_id__get","parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string","title":"Generation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models/load":{"post":{"summary":"Load Model","description":"Manually load TTS model.","operationId":"load_model_models_load_post","parameters":[{"name":"model_size","in":"query","required":false,"schema":{"type":"string","default":"1.7B","title":"Model Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models/unload":{"post":{"summary":"Unload Model","description":"Unload TTS model to free memory.","operationId":"unload_model_models_unload_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/models/progress/{model_name}":{"get":{"summary":"Get Model Progress","description":"Get model download progress via Server-Sent Events.","operationId":"get_model_progress_models_progress__model_name__get","parameters":[{"name":"model_name","in":"path","required":true,"schema":{"type":"string","title":"Model Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/models/status":{"get":{"summary":"Get Model Status","description":"Get status of all available models.","operationId":"get_model_status_models_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelStatusListResponse"}}}}}}},"/models/download":{"post":{"summary":"Trigger Model Download","description":"Trigger download of a specific model.","operationId":"trigger_model_download_models_download_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelDownloadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_add_profile_sample_profiles__profile_id__samples_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"reference_text":{"type":"string","title":"Reference Text"}},"type":"object","required":["file","reference_text"],"title":"Body_add_profile_sample_profiles__profile_id__samples_post"},"Body_transcribe_audio_transcribe_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["file"],"title":"Body_transcribe_audio_transcribe_post"},"GenerationRequest":{"properties":{"profile_id":{"type":"string","title":"Profile Id"},"text":{"type":"string","maxLength":5000,"minLength":1,"title":"Text"},"language":{"type":"string","pattern":"^(en|zh)$","title":"Language","default":"en"},"seed":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Seed"},"model_size":{"anyOf":[{"type":"string","pattern":"^(1\\.7B|0\\.6B)$"},{"type":"null"}],"title":"Model Size","default":"1.7B"}},"type":"object","required":["profile_id","text"],"title":"GenerationRequest","description":"Request model for voice generation."},"GenerationResponse":{"properties":{"id":{"type":"string","title":"Id"},"profile_id":{"type":"string","title":"Profile Id"},"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"audio_path":{"type":"string","title":"Audio Path"},"duration":{"type":"number","title":"Duration"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","profile_id","text","language","audio_path","duration","seed","created_at"],"title":"GenerationResponse","description":"Response model for voice generation."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status"},"model_loaded":{"type":"boolean","title":"Model Loaded"},"model_downloaded":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Model Downloaded"},"model_size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Size"},"gpu_available":{"type":"boolean","title":"Gpu Available"},"vram_used_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Vram Used Mb"}},"type":"object","required":["status","model_loaded","gpu_available"],"title":"HealthResponse","description":"Response model for health check."},"HistoryListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/HistoryResponse"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"HistoryListResponse","description":"Response model for history list."},"HistoryResponse":{"properties":{"id":{"type":"string","title":"Id"},"profile_id":{"type":"string","title":"Profile Id"},"profile_name":{"type":"string","title":"Profile Name"},"text":{"type":"string","title":"Text"},"language":{"type":"string","title":"Language"},"audio_path":{"type":"string","title":"Audio Path"},"duration":{"type":"number","title":"Duration"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","profile_id","profile_name","text","language","audio_path","duration","seed","created_at"],"title":"HistoryResponse","description":"Response model for history entry (includes profile name)."},"ModelDownloadRequest":{"properties":{"model_name":{"type":"string","title":"Model Name"}},"type":"object","required":["model_name"],"title":"ModelDownloadRequest","description":"Request model for triggering model download."},"ModelStatus":{"properties":{"model_name":{"type":"string","title":"Model Name"},"display_name":{"type":"string","title":"Display Name"},"downloaded":{"type":"boolean","title":"Downloaded"},"size_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Size Mb"},"loaded":{"type":"boolean","title":"Loaded","default":false}},"type":"object","required":["model_name","display_name","downloaded"],"title":"ModelStatus","description":"Response model for model status."},"ModelStatusListResponse":{"properties":{"models":{"items":{"$ref":"#/components/schemas/ModelStatus"},"type":"array","title":"Models"}},"type":"object","required":["models"],"title":"ModelStatusListResponse","description":"Response model for model status list."},"ProfileSampleResponse":{"properties":{"id":{"type":"string","title":"Id"},"profile_id":{"type":"string","title":"Profile Id"},"audio_path":{"type":"string","title":"Audio Path"},"reference_text":{"type":"string","title":"Reference Text"}},"type":"object","required":["id","profile_id","audio_path","reference_text"],"title":"ProfileSampleResponse","description":"Response model for profile sample."},"TranscriptionResponse":{"properties":{"text":{"type":"string","title":"Text"},"duration":{"type":"number","title":"Duration"}},"type":"object","required":["text","duration"],"title":"TranscriptionResponse","description":"Response model for transcription."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VoiceProfileCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description"},"language":{"type":"string","pattern":"^(en|zh)$","title":"Language","default":"en"}},"type":"object","required":["name"],"title":"VoiceProfileCreate","description":"Request model for creating a voice profile."},"VoiceProfileResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"language":{"type":"string","title":"Language"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","name","description","language","created_at","updated_at"],"title":"VoiceProfileResponse","description":"Response model for voice profile."}}}} \ No newline at end of file diff --git a/docs2/package.json b/docs2/package.json new file mode 100644 index 00000000..1d14cd8f --- /dev/null +++ b/docs2/package.json @@ -0,0 +1,32 @@ +{ + "name": "example-next-mdx", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "fumadocs-mdx && next build", + "dev": "fumadocs-mdx && next dev", + "start": "next start", + "postinstall": "fumadocs-mdx" + }, + "dependencies": { + "fumadocs-core": "^16.4.11", + "fumadocs-mdx": "13", + "fumadocs-openapi": "^10.2.7", + "fumadocs-ui": "^16.4.11", + "lucide-react": "^0.546.0", + "next": "^16.1.6", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "shiki": "^3.22.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.15", + "@types/mdx": "^2.0.13", + "@types/node": "^24.9.1", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.15", + "typescript": "^5.9.3" + } +} diff --git a/docs2/postcss.config.mjs b/docs2/postcss.config.mjs new file mode 100644 index 00000000..a34a3d56 --- /dev/null +++ b/docs2/postcss.config.mjs @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +}; diff --git a/docs2/public/images/app-screenshot-1.webp b/docs2/public/images/app-screenshot-1.webp new file mode 100644 index 0000000000000000000000000000000000000000..343e94bdee7e824850504cf1b8f0b3b3bd923cc2 GIT binary patch literal 136754 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

_bR3#KXhQG8 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 tJ2`wPbr;00Lz(`!QT{|C=Aw*&wH literal 0 HcmV?d00001 diff --git a/docs2/public/images/app-screenshot-2.webp b/docs2/public/images/app-screenshot-2.webp new file mode 100644 index 0000000000000000000000000000000000000000..2d09760771092a8db4a89beafc358e6dc9726853 GIT binary patch literal 131774 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%0D}@66g}jvZ0Fs3`4MqfZ!dy-ALS}~~2qjx8 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 GQ2q}YF@Yuk literal 0 HcmV?d00001 diff --git a/docs2/public/images/app-screenshot-3.webp b/docs2/public/images/app-screenshot-3.webp new file mode 100644 index 0000000000000000000000000000000000000000..067d53657b6a5b9ace879a9d37de2df9c4e2152f GIT binary patch literal 110446 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