GitHub Action
commited on
Commit
·
d3f86d8
1
Parent(s):
47efcbd
Sync from GitHub: ffdd28283d24c66d2e788d9b4a630d7d9f76b0a1
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +6 -0
- .gitignore.backup +280 -0
- .gitignore.hf +40 -0
- CLAUDE.md +142 -0
- MANIFEST.in +1 -0
- QUICKSTART.md +116 -0
- README.md +75 -1
- README_SPACE.md +86 -0
- TECHNICAL_SPECS.md +337 -0
- app.py +9 -0
- frontend/package-lock.json +0 -0
- frontend/package.json +37 -0
- frontend/postcss.config.js +6 -0
- frontend/src/app.css +93 -0
- frontend/src/app.html +13 -0
- frontend/src/routes/+layout.svelte +185 -0
- frontend/src/routes/+page.svelte +499 -0
- frontend/src/routes/api/auth/token/+server.js +46 -0
- frontend/src/routes/auth/callback/+page.svelte +99 -0
- frontend/static/assets/hf-logo.png +3 -0
- frontend/static/assets/hf-studio-logo.png +3 -0
- frontend/static/samples/harvard.wav +3 -0
- frontend/svelte.config.js +18 -0
- frontend/tailwind.config.js +44 -0
- frontend/vite.config.js +10 -0
- hfstudio/__init__.py +17 -0
- hfstudio/__main__.py +4 -0
- hfstudio/cli.py +32 -0
- hfstudio/server.py +217 -0
- hfstudio/static/_app/env.js +1 -0
- hfstudio/static/_app/immutable/assets/0.DkRYx-5s.css +1 -0
- hfstudio/static/_app/immutable/chunks/9DiovRey.js +1 -0
- hfstudio/static/_app/immutable/chunks/BHHrQo_1.js +1 -0
- hfstudio/static/_app/immutable/chunks/Dh5qNFGZ.js +3 -0
- hfstudio/static/_app/immutable/chunks/IHki7fMi.js +1 -0
- hfstudio/static/_app/immutable/entry/app.DrVe4Aen.js +2 -0
- hfstudio/static/_app/immutable/entry/start.CYVq8xer.js +1 -0
- hfstudio/static/_app/immutable/nodes/0.BWfnr5bB.js +1 -0
- hfstudio/static/_app/immutable/nodes/1.DRlzjau5.js +1 -0
- hfstudio/static/_app/immutable/nodes/2.BSR-L-2e.js +16 -0
- hfstudio/static/_app/immutable/nodes/3.VqKPhUiT.js +1 -0
- hfstudio/static/_app/version.json +1 -0
- hfstudio/static/assets/hf-logo.png +3 -0
- hfstudio/static/assets/hf-studio-logo.png +3 -0
- hfstudio/static/index.html +35 -0
- hfstudio/static/samples/harvard.wav +3 -0
- package-lock.json +6 -0
- pyproject.toml +63 -0
- requirements.txt +8 -0
- requirements_space.txt +8 -0
.gitattributes
CHANGED
|
@@ -7,3 +7,9 @@
|
|
| 7 |
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.webm filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.pdf filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 8 |
*.webm filter=lfs diff=lfs merge=lfs -text
|
| 9 |
*.pdf filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
hfstudio/static/assets/hf-studio-logo.png filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
hfstudio/static/assets/hf-logo.png filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
frontend/static/assets/hf-studio-logo.png filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
frontend/static/assets/hf-logo.png filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
hfstudio/static/samples/harvard.wav filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
frontend/static/samples/harvard.wav filter=lfs diff=lfs merge=lfs -text
|
.gitignore.backup
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
*.so
|
| 6 |
+
.Python
|
| 7 |
+
build/
|
| 8 |
+
develop-eggs/
|
| 9 |
+
dist/
|
| 10 |
+
downloads/
|
| 11 |
+
eggs/
|
| 12 |
+
.eggs/
|
| 13 |
+
lib/
|
| 14 |
+
lib64/
|
| 15 |
+
parts/
|
| 16 |
+
sdist/
|
| 17 |
+
var/
|
| 18 |
+
wheels/
|
| 19 |
+
share/python-wheels/
|
| 20 |
+
*.egg-info/
|
| 21 |
+
.installed.cfg
|
| 22 |
+
*.egg
|
| 23 |
+
MANIFEST
|
| 24 |
+
|
| 25 |
+
# PyInstaller
|
| 26 |
+
*.manifest
|
| 27 |
+
*.spec
|
| 28 |
+
|
| 29 |
+
# Installer logs
|
| 30 |
+
pip-log.txt
|
| 31 |
+
pip-delete-this-directory.txt
|
| 32 |
+
|
| 33 |
+
# Unit test / coverage reports
|
| 34 |
+
htmlcov/
|
| 35 |
+
.tox/
|
| 36 |
+
.nox/
|
| 37 |
+
.coverage
|
| 38 |
+
.coverage.*
|
| 39 |
+
.cache
|
| 40 |
+
nosetests.xml
|
| 41 |
+
coverage.xml
|
| 42 |
+
*.cover
|
| 43 |
+
*.py,cover
|
| 44 |
+
.hypothesis/
|
| 45 |
+
.pytest_cache/
|
| 46 |
+
cover/
|
| 47 |
+
|
| 48 |
+
# Translations
|
| 49 |
+
*.mo
|
| 50 |
+
*.pot
|
| 51 |
+
|
| 52 |
+
# Django stuff:
|
| 53 |
+
*.log
|
| 54 |
+
local_settings.py
|
| 55 |
+
db.sqlite3
|
| 56 |
+
db.sqlite3-journal
|
| 57 |
+
|
| 58 |
+
# Flask stuff:
|
| 59 |
+
instance/
|
| 60 |
+
.webassets-cache
|
| 61 |
+
|
| 62 |
+
# Scrapy stuff:
|
| 63 |
+
.scrapy
|
| 64 |
+
|
| 65 |
+
# Sphinx documentation
|
| 66 |
+
docs/_build/
|
| 67 |
+
|
| 68 |
+
# PyBuilder
|
| 69 |
+
.pybuilder/
|
| 70 |
+
target/
|
| 71 |
+
|
| 72 |
+
# Jupyter Notebook
|
| 73 |
+
.ipynb_checkpoints
|
| 74 |
+
|
| 75 |
+
# IPython
|
| 76 |
+
profile_default/
|
| 77 |
+
ipython_config.py
|
| 78 |
+
|
| 79 |
+
# pyenv
|
| 80 |
+
.python-version
|
| 81 |
+
|
| 82 |
+
# pipenv
|
| 83 |
+
Pipfile.lock
|
| 84 |
+
|
| 85 |
+
# poetry
|
| 86 |
+
poetry.lock
|
| 87 |
+
|
| 88 |
+
# pdm
|
| 89 |
+
.pdm.toml
|
| 90 |
+
|
| 91 |
+
# PEP 582
|
| 92 |
+
__pypackages__/
|
| 93 |
+
|
| 94 |
+
# Celery stuff
|
| 95 |
+
celerybeat-schedule
|
| 96 |
+
celerybeat.pid
|
| 97 |
+
|
| 98 |
+
# SageMath parsed files
|
| 99 |
+
*.sage.py
|
| 100 |
+
|
| 101 |
+
# Environments
|
| 102 |
+
.env
|
| 103 |
+
.venv
|
| 104 |
+
env/
|
| 105 |
+
venv/
|
| 106 |
+
ENV/
|
| 107 |
+
env.bak/
|
| 108 |
+
venv.bak/
|
| 109 |
+
|
| 110 |
+
# Spyder project settings
|
| 111 |
+
.spyderproject
|
| 112 |
+
.spyproject
|
| 113 |
+
|
| 114 |
+
# Rope project settings
|
| 115 |
+
.ropeproject
|
| 116 |
+
|
| 117 |
+
# mkdocs documentation
|
| 118 |
+
/site
|
| 119 |
+
|
| 120 |
+
# mypy
|
| 121 |
+
.mypy_cache/
|
| 122 |
+
.dmypy.json
|
| 123 |
+
dmypy.json
|
| 124 |
+
|
| 125 |
+
# Pyre type checker
|
| 126 |
+
.pyre/
|
| 127 |
+
|
| 128 |
+
# pytype static type analyzer
|
| 129 |
+
.pytype/
|
| 130 |
+
|
| 131 |
+
# Cython debug symbols
|
| 132 |
+
cython_debug/
|
| 133 |
+
|
| 134 |
+
# Node.js
|
| 135 |
+
node_modules/
|
| 136 |
+
npm-debug.log*
|
| 137 |
+
yarn-debug.log*
|
| 138 |
+
yarn-error.log*
|
| 139 |
+
lerna-debug.log*
|
| 140 |
+
.pnpm-debug.log*
|
| 141 |
+
|
| 142 |
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
| 143 |
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
| 144 |
+
|
| 145 |
+
# Runtime data
|
| 146 |
+
pids
|
| 147 |
+
*.pid
|
| 148 |
+
*.seed
|
| 149 |
+
*.pid.lock
|
| 150 |
+
|
| 151 |
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
| 152 |
+
lib-cov
|
| 153 |
+
|
| 154 |
+
# Coverage directory used by tools like istanbul
|
| 155 |
+
coverage
|
| 156 |
+
*.lcov
|
| 157 |
+
|
| 158 |
+
# nyc test coverage
|
| 159 |
+
.nyc_output
|
| 160 |
+
|
| 161 |
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
| 162 |
+
.grunt
|
| 163 |
+
|
| 164 |
+
# Bower dependency directory (https://bower.io/)
|
| 165 |
+
bower_components
|
| 166 |
+
|
| 167 |
+
# node-waf configuration
|
| 168 |
+
.lock-wscript
|
| 169 |
+
|
| 170 |
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
| 171 |
+
build/Release
|
| 172 |
+
|
| 173 |
+
# Dependency directories
|
| 174 |
+
jspm_packages/
|
| 175 |
+
|
| 176 |
+
# Snowpack dependency directory (https://snowpack.dev/)
|
| 177 |
+
web_modules/
|
| 178 |
+
|
| 179 |
+
# TypeScript cache
|
| 180 |
+
*.tsbuildinfo
|
| 181 |
+
|
| 182 |
+
# Optional npm cache directory
|
| 183 |
+
.npm
|
| 184 |
+
|
| 185 |
+
# Optional eslint cache
|
| 186 |
+
.eslintcache
|
| 187 |
+
|
| 188 |
+
# Optional stylelint cache
|
| 189 |
+
.stylelintcache
|
| 190 |
+
|
| 191 |
+
# Microbundle cache
|
| 192 |
+
.rpt2_cache/
|
| 193 |
+
.rts2_cache_cjs/
|
| 194 |
+
.rts2_cache_es/
|
| 195 |
+
.rts2_cache_umd/
|
| 196 |
+
|
| 197 |
+
# Optional REPL history
|
| 198 |
+
.node_repl_history
|
| 199 |
+
|
| 200 |
+
# Output of 'npm pack'
|
| 201 |
+
*.tgz
|
| 202 |
+
|
| 203 |
+
# Yarn Integrity file
|
| 204 |
+
.yarn-integrity
|
| 205 |
+
|
| 206 |
+
# dotenv environment variable files
|
| 207 |
+
.env.development.local
|
| 208 |
+
.env.test.local
|
| 209 |
+
.env.production.local
|
| 210 |
+
.env.local
|
| 211 |
+
|
| 212 |
+
# parcel-bundler cache (https://parceljs.org/)
|
| 213 |
+
.cache
|
| 214 |
+
.parcel-cache
|
| 215 |
+
|
| 216 |
+
# Next.js build output
|
| 217 |
+
.next
|
| 218 |
+
out
|
| 219 |
+
|
| 220 |
+
# Nuxt.js build / generate output
|
| 221 |
+
.nuxt
|
| 222 |
+
dist
|
| 223 |
+
|
| 224 |
+
# Gatsby files
|
| 225 |
+
.cache/
|
| 226 |
+
public
|
| 227 |
+
|
| 228 |
+
# Vuepress build output
|
| 229 |
+
.vuepress/dist
|
| 230 |
+
|
| 231 |
+
# Vuepress v2.x temp and cache directory
|
| 232 |
+
.temp
|
| 233 |
+
.cache
|
| 234 |
+
|
| 235 |
+
# Docusaurus cache and generated files
|
| 236 |
+
.docusaurus
|
| 237 |
+
|
| 238 |
+
# Serverless directories
|
| 239 |
+
.serverless/
|
| 240 |
+
|
| 241 |
+
# FuseBox cache
|
| 242 |
+
.fusebox/
|
| 243 |
+
|
| 244 |
+
# DynamoDB Local files
|
| 245 |
+
.dynamodb/
|
| 246 |
+
|
| 247 |
+
# TernJS port file
|
| 248 |
+
.tern-port
|
| 249 |
+
|
| 250 |
+
# Stores VSCode versions used for testing VSCode extensions
|
| 251 |
+
.vscode-test
|
| 252 |
+
|
| 253 |
+
# yarn v2
|
| 254 |
+
.yarn/cache
|
| 255 |
+
.yarn/unplugged
|
| 256 |
+
.yarn/build-state.yml
|
| 257 |
+
.yarn/install-state.gz
|
| 258 |
+
.pnp.*
|
| 259 |
+
|
| 260 |
+
# SvelteKit
|
| 261 |
+
.svelte-kit
|
| 262 |
+
|
| 263 |
+
# Vite
|
| 264 |
+
.vite
|
| 265 |
+
|
| 266 |
+
# IDE
|
| 267 |
+
.vscode/
|
| 268 |
+
.idea/
|
| 269 |
+
*.swp
|
| 270 |
+
*.swo
|
| 271 |
+
*~
|
| 272 |
+
|
| 273 |
+
# OS
|
| 274 |
+
.DS_Store
|
| 275 |
+
.DS_Store?
|
| 276 |
+
._*
|
| 277 |
+
.Spotlight-V100
|
| 278 |
+
.Trashes
|
| 279 |
+
ehthumbs.db
|
| 280 |
+
Thumbs.db
|
.gitignore.hf
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Node.js dependencies and build artifacts
|
| 2 |
+
frontend/node_modules/
|
| 3 |
+
frontend/.svelte-kit/
|
| 4 |
+
frontend/build/
|
| 5 |
+
|
| 6 |
+
# Python cache
|
| 7 |
+
__pycache__/
|
| 8 |
+
*.pyc
|
| 9 |
+
*.pyo
|
| 10 |
+
*.pyd
|
| 11 |
+
.Python
|
| 12 |
+
env/
|
| 13 |
+
venv/
|
| 14 |
+
.env
|
| 15 |
+
.venv
|
| 16 |
+
pip-log.txt
|
| 17 |
+
pip-delete-this-directory.txt
|
| 18 |
+
|
| 19 |
+
# Build artifacts
|
| 20 |
+
dist/
|
| 21 |
+
build/
|
| 22 |
+
*.egg-info/
|
| 23 |
+
|
| 24 |
+
# IDE and editor files
|
| 25 |
+
.vscode/
|
| 26 |
+
.idea/
|
| 27 |
+
*.swp
|
| 28 |
+
*.swo
|
| 29 |
+
*~
|
| 30 |
+
|
| 31 |
+
# OS files
|
| 32 |
+
.DS_Store
|
| 33 |
+
Thumbs.db
|
| 34 |
+
|
| 35 |
+
# Git
|
| 36 |
+
.git/
|
| 37 |
+
.gitignore
|
| 38 |
+
|
| 39 |
+
# Don't ignore the built static files for the Space
|
| 40 |
+
!hfstudio/static/
|
CLAUDE.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLAUDE.md
|
| 2 |
+
|
| 3 |
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
| 4 |
+
|
| 5 |
+
## Development Commands
|
| 6 |
+
|
| 7 |
+
### Quick Start (Recommended)
|
| 8 |
+
```bash
|
| 9 |
+
./run_dev.sh
|
| 10 |
+
```
|
| 11 |
+
This single script starts both frontend (port 11111) and backend (port 11110) with auto-browser opening.
|
| 12 |
+
|
| 13 |
+
### Manual Development Setup
|
| 14 |
+
|
| 15 |
+
**Frontend (Svelte):**
|
| 16 |
+
```bash
|
| 17 |
+
cd frontend
|
| 18 |
+
npm install
|
| 19 |
+
npm run dev # Development server on :11111
|
| 20 |
+
npm run build # Production build
|
| 21 |
+
npm run check # Type checking
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
**Backend (FastAPI):**
|
| 25 |
+
```bash
|
| 26 |
+
cd backend
|
| 27 |
+
pip install -r requirements.txt
|
| 28 |
+
python -m hfstudio.cli --dev # Development server on :11110
|
| 29 |
+
# OR
|
| 30 |
+
pip install -e .
|
| 31 |
+
hfstudio --dev
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
### Port Configuration
|
| 35 |
+
- Frontend: 11111 (configured in `vite.config.js`)
|
| 36 |
+
- Backend: 7860 (configured in `cli.py`)
|
| 37 |
+
- CORS is configured for these specific ports in `server.py`
|
| 38 |
+
|
| 39 |
+
## Architecture Overview
|
| 40 |
+
|
| 41 |
+
### Frontend Structure (SvelteKit + TailwindCSS)
|
| 42 |
+
- **Single Page App**: Main interface in `src/routes/+page.svelte`
|
| 43 |
+
- **Layout**: Global layout with sidebar in `src/routes/+layout.svelte`
|
| 44 |
+
- **Design System**: HuggingFace brand colors (`#FFD21E`, `#FF9D00`) used throughout
|
| 45 |
+
- **State Management**: Local component state with reactive variables
|
| 46 |
+
- **Audio Handling**: Custom HTML5 audio element with manual progress tracking
|
| 47 |
+
|
| 48 |
+
### Key UI Components
|
| 49 |
+
- **Three-panel layout**: Sidebar (56 units) + Main content + Settings panel (80 units)
|
| 50 |
+
- **Fixed bottom button**: Generate button positioned absolutely at page bottom
|
| 51 |
+
- **Mini audio player**: Compact controls in generated audio card
|
| 52 |
+
- **Full audio player**: Expanded controls with ElevenLabs-style design
|
| 53 |
+
- **Custom pause icon**: CSS-only filled bars instead of outline
|
| 54 |
+
|
| 55 |
+
### Backend Structure (FastAPI)
|
| 56 |
+
- **Main server**: `server.py` with CORS configured for development ports
|
| 57 |
+
- **CLI interface**: `cli.py` with typer for command-line control
|
| 58 |
+
- **Pydantic models**: TTSRequest, TTSResponse, Voice, Model
|
| 59 |
+
- **Current implementation**: Mock TTS generation using placeholder audio
|
| 60 |
+
|
| 61 |
+
### API Endpoints
|
| 62 |
+
```
|
| 63 |
+
GET / - Health check
|
| 64 |
+
GET /api/status - Mode and availability info
|
| 65 |
+
GET /api/voices - Available voice list
|
| 66 |
+
GET /api/models - Available model list
|
| 67 |
+
POST /api/tts/generate - Generate speech from text
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
## Design Patterns & Conventions
|
| 71 |
+
|
| 72 |
+
### Frontend Patterns
|
| 73 |
+
- **HF Brand Integration**: Uses official logo (`/assets/hf-logo.png`) and gradient colors
|
| 74 |
+
- **Responsive Controls**: All sliders use custom `.slider-hf` class with HF colors
|
| 75 |
+
- **Audio State Management**: Manual synchronization between UI state and HTML5 audio element
|
| 76 |
+
- **Progressive Enhancement**: Settings always visible, no hidden toggles
|
| 77 |
+
|
| 78 |
+
### Backend Patterns
|
| 79 |
+
- **Development Mode**: Auto-reload enabled with `--dev` flag
|
| 80 |
+
- **Mock Implementation**: Currently returns `/samples/harvard.wav` for testing
|
| 81 |
+
- **CORS Configuration**: Explicitly configured for development ports
|
| 82 |
+
|
| 83 |
+
### Styling Conventions
|
| 84 |
+
- **TailwindCSS**: Primary styling framework
|
| 85 |
+
- **Custom CSS**: Limited to audio sliders and pause icon in `app.css`
|
| 86 |
+
- **Color Scheme**: Light theme with HF amber/orange accents
|
| 87 |
+
- **Typography**: System fonts with careful spacing
|
| 88 |
+
|
| 89 |
+
## Key Implementation Details
|
| 90 |
+
|
| 91 |
+
### Audio System
|
| 92 |
+
The app uses a hidden HTML5 `<audio>` element controlled by custom UI:
|
| 93 |
+
- Real audio playback through bound `audioElement`
|
| 94 |
+
- Manual progress tracking via `timeupdate` events
|
| 95 |
+
- Auto-play when generation completes
|
| 96 |
+
- Custom pause icon using CSS pseudo-elements
|
| 97 |
+
|
| 98 |
+
### Voice & Model Data
|
| 99 |
+
```javascript
|
| 100 |
+
// Current models (in +page.svelte)
|
| 101 |
+
const models = [
|
| 102 |
+
{ id: 'chatterbox', name: 'Chatterbox', badge: 'recommended' },
|
| 103 |
+
{ id: 'kokoro', name: 'Kokoro', badge: 'faster but lower quality' }
|
| 104 |
+
];
|
| 105 |
+
|
| 106 |
+
// Current voices with descriptions
|
| 107 |
+
const voices = [
|
| 108 |
+
{ id: 'novia', name: 'Novia', description: 'Warm, conversational voice' },
|
| 109 |
+
// ... etc
|
| 110 |
+
];
|
| 111 |
+
```
|
| 112 |
+
|
| 113 |
+
### Configuration Files
|
| 114 |
+
- **Frontend env**: `.env` with `PUBLIC_API_URL=http://localhost:11110`
|
| 115 |
+
- **Vite config**: Custom port (11111) and host settings
|
| 116 |
+
- **TailwindCSS**: Custom colors and slider styling
|
| 117 |
+
- **Backend requirements**: FastAPI, numpy, soundfile for audio processing
|
| 118 |
+
|
| 119 |
+
### Static Assets
|
| 120 |
+
- **Logo**: HuggingFace logo at `/assets/hf-logo.png`
|
| 121 |
+
- **Sample audio**: Harvard sample at `/samples/harvard.wav` for testing
|
| 122 |
+
- **Favicon**: Uses HF logo
|
| 123 |
+
|
| 124 |
+
## Development Workflow
|
| 125 |
+
|
| 126 |
+
### Making UI Changes
|
| 127 |
+
1. Edit components in `frontend/src/routes/+page.svelte`
|
| 128 |
+
2. Layout changes go in `frontend/src/routes/+layout.svelte`
|
| 129 |
+
3. Global styles in `frontend/src/app.css`
|
| 130 |
+
4. Hot reload shows changes instantly
|
| 131 |
+
|
| 132 |
+
### Adding API Features
|
| 133 |
+
1. Add Pydantic models in `server.py`
|
| 134 |
+
2. Create new endpoints following existing patterns
|
| 135 |
+
3. Update CORS origins if needed
|
| 136 |
+
4. Test at `http://localhost:11110/docs`
|
| 137 |
+
|
| 138 |
+
### Troubleshooting
|
| 139 |
+
- **Port conflicts**: Change ports in `vite.config.js` and `cli.py`
|
| 140 |
+
- **CORS issues**: Update `allow_origins` in `server.py`
|
| 141 |
+
- **Audio not playing**: Check audio file exists at `/samples/harvard.wav`
|
| 142 |
+
- **Dependencies**: Run `npm install` in frontend, `pip install -r requirements.txt` in backend
|
MANIFEST.in
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
include frontend/package.json
|
QUICKSTART.md
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# HFStudio - Quick Start Guide
|
| 2 |
+
|
| 3 |
+
## 🚀 Running Locally for Development
|
| 4 |
+
|
| 5 |
+
### Prerequisites
|
| 6 |
+
- Node.js 18+ and npm
|
| 7 |
+
- Python 3.8+
|
| 8 |
+
|
| 9 |
+
### Option 1: Quick Start (Recommended)
|
| 10 |
+
```bash
|
| 11 |
+
# Run both frontend and backend
|
| 12 |
+
./run_dev.sh
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
This will:
|
| 16 |
+
- Start the backend API server on http://localhost:11110
|
| 17 |
+
- Start the frontend dev server on http://localhost:11111
|
| 18 |
+
- Install dependencies automatically
|
| 19 |
+
|
| 20 |
+
### Option 2: Manual Setup
|
| 21 |
+
|
| 22 |
+
#### Terminal 1 - Backend
|
| 23 |
+
```bash
|
| 24 |
+
cd backend
|
| 25 |
+
|
| 26 |
+
# Install dependencies
|
| 27 |
+
pip install -r requirements.txt
|
| 28 |
+
|
| 29 |
+
# Run the server
|
| 30 |
+
python -m hfstudio.cli --dev
|
| 31 |
+
|
| 32 |
+
# Or install as package and run
|
| 33 |
+
pip install -e .
|
| 34 |
+
hfstudio --dev
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
Backend will run on http://localhost:11110
|
| 38 |
+
- API docs: http://localhost:11110/docs
|
| 39 |
+
- ReDoc: http://localhost:11110/redoc
|
| 40 |
+
|
| 41 |
+
#### Terminal 2 - Frontend
|
| 42 |
+
```bash
|
| 43 |
+
cd frontend
|
| 44 |
+
|
| 45 |
+
# Install dependencies
|
| 46 |
+
npm install
|
| 47 |
+
|
| 48 |
+
# Run dev server
|
| 49 |
+
npm run dev
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
+
Frontend will run on http://localhost:11111
|
| 53 |
+
|
| 54 |
+
## 📁 Project Structure
|
| 55 |
+
```
|
| 56 |
+
hfstudio/
|
| 57 |
+
├── frontend/ # Svelte UI (port 11111)
|
| 58 |
+
│ └── src/
|
| 59 |
+
│ ├── routes/ # Page components
|
| 60 |
+
│ └── app.css # Tailwind styles
|
| 61 |
+
├── backend/ # Python API (port 11110)
|
| 62 |
+
│ └── hfstudio/
|
| 63 |
+
│ ├── server.py # FastAPI server
|
| 64 |
+
│ └── cli.py # CLI interface
|
| 65 |
+
└── run_dev.sh # Dev startup script
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
## 🎨 UI Features
|
| 69 |
+
- **Text Input**: Large textarea for text-to-speech input
|
| 70 |
+
- **Voice Selection**: Choose from available voices
|
| 71 |
+
- **Model Selection**: Switch between TTS models
|
| 72 |
+
- **Parameter Controls**: Adjust speed, stability, similarity
|
| 73 |
+
- **Mode Toggle**: Switch between API and Local execution
|
| 74 |
+
- **Audio Player**: Play and download generated speech
|
| 75 |
+
|
| 76 |
+
## 🔧 Development Tips
|
| 77 |
+
|
| 78 |
+
### Frontend Changes
|
| 79 |
+
- Edit files in `frontend/src/`
|
| 80 |
+
- Hot reload is enabled - changes appear instantly
|
| 81 |
+
- Tailwind classes are available
|
| 82 |
+
|
| 83 |
+
### Backend Changes
|
| 84 |
+
- Edit files in `backend/hfstudio/`
|
| 85 |
+
- With `--dev` flag, server auto-reloads on changes
|
| 86 |
+
- Test API endpoints at http://localhost:11110/docs
|
| 87 |
+
|
| 88 |
+
### Adding New Features
|
| 89 |
+
1. Frontend components go in `frontend/src/lib/components/`
|
| 90 |
+
2. API endpoints go in `backend/hfstudio/server.py`
|
| 91 |
+
3. TTS models will go in `backend/hfstudio/models/`
|
| 92 |
+
|
| 93 |
+
## 🐛 Troubleshooting
|
| 94 |
+
|
| 95 |
+
### Port Already in Use
|
| 96 |
+
```bash
|
| 97 |
+
# Change frontend port
|
| 98 |
+
cd frontend
|
| 99 |
+
npm run dev -- --port 11112
|
| 100 |
+
|
| 101 |
+
# Change backend port
|
| 102 |
+
cd backend
|
| 103 |
+
python -m hfstudio.cli --port 11112
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
### Missing Dependencies
|
| 107 |
+
```bash
|
| 108 |
+
# Frontend
|
| 109 |
+
cd frontend && npm install
|
| 110 |
+
|
| 111 |
+
# Backend
|
| 112 |
+
cd backend && pip install -r requirements.txt
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
### CORS Issues
|
| 116 |
+
Make sure both servers are running and check the CORS configuration in `backend/hfstudio/server.py`
|
README.md
CHANGED
|
@@ -9,4 +9,78 @@ app_file: app.py
|
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# HFStudio 🎙️
|
| 13 |
+
|
| 14 |
+
A local and API-based Text-to-Speech studio for creating high-quality speech from text.
|
| 15 |
+
|
| 16 |
+
## Features
|
| 17 |
+
|
| 18 |
+
- 🎯 **Multiple TTS Models**: Support for various TTS engines
|
| 19 |
+
- 🎭 **Voice Selection**: Choose from different voice models
|
| 20 |
+
- ⚡ **Real-time Generation**: Fast speech synthesis
|
| 21 |
+
- 🔧 **Parameter Control**: Fine-tune speed, stability, and similarity
|
| 22 |
+
- 🎵 **Audio Preview**: Built-in audio player with controls
|
| 23 |
+
- 🔐 **HuggingFace Integration**: Sign in with your HF account
|
| 24 |
+
|
| 25 |
+
## Quick Start
|
| 26 |
+
|
| 27 |
+
This Space runs HFStudio automatically. Simply:
|
| 28 |
+
|
| 29 |
+
1. **Open the Space** - The web interface will load automatically
|
| 30 |
+
2. **Sign In** (optional) - Click "Sign In with Hugging Face" for full features
|
| 31 |
+
3. **Enter Text** - Type or paste the text you want to convert to speech
|
| 32 |
+
4. **Choose Voice** - Select from available voice models
|
| 33 |
+
5. **Adjust Settings** - Tune speed, stability, and other parameters
|
| 34 |
+
6. **Generate** - Click "Generate speech" to create audio
|
| 35 |
+
7. **Play & Download** - Use the built-in player to preview your audio
|
| 36 |
+
|
| 37 |
+
## Local Installation
|
| 38 |
+
|
| 39 |
+
Want to run HFStudio locally? Install via pip:
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
pip install hfstudio
|
| 43 |
+
hfstudio
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Then visit `http://localhost:7860` in your browser.
|
| 47 |
+
|
| 48 |
+
## Development
|
| 49 |
+
|
| 50 |
+
This Space is automatically synced from the [GitHub repository](https://github.com/yourusername/hfstudio).
|
| 51 |
+
|
| 52 |
+
To contribute or run locally for development:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
git clone https://github.com/yourusername/hfstudio
|
| 56 |
+
cd hfstudio
|
| 57 |
+
./run_dev.sh # Starts both frontend and backend
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
## API Usage
|
| 61 |
+
|
| 62 |
+
HFStudio also provides a REST API for integration:
|
| 63 |
+
|
| 64 |
+
```python
|
| 65 |
+
import requests
|
| 66 |
+
|
| 67 |
+
# Generate speech
|
| 68 |
+
response = requests.post('https://abidlabs-hfstudio.hf.space/api/tts/generate',
|
| 69 |
+
json={
|
| 70 |
+
"text": "Hello, world!",
|
| 71 |
+
"voice_id": "sarah",
|
| 72 |
+
"model_id": "coqui-tts"
|
| 73 |
+
}
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
audio_data = response.json()
|
| 77 |
+
print(f"Audio URL: {audio_data['audio_url']}")
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
## License
|
| 81 |
+
|
| 82 |
+
MIT License - see [LICENSE](LICENSE) for details.
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
Built with ❤️ using [SvelteKit](https://kit.svelte.dev/) and [FastAPI](https://fastapi.tiangolo.com/)
|
README_SPACE.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Hfstudio
|
| 3 |
+
emoji: 🏆
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.49.1
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# HFStudio 🎙️
|
| 13 |
+
|
| 14 |
+
A local and API-based Text-to-Speech studio for creating high-quality speech from text.
|
| 15 |
+
|
| 16 |
+
## Features
|
| 17 |
+
|
| 18 |
+
- 🎯 **Multiple TTS Models**: Support for various TTS engines
|
| 19 |
+
- 🎭 **Voice Selection**: Choose from different voice models
|
| 20 |
+
- ⚡ **Real-time Generation**: Fast speech synthesis
|
| 21 |
+
- 🔧 **Parameter Control**: Fine-tune speed, stability, and similarity
|
| 22 |
+
- 🎵 **Audio Preview**: Built-in audio player with controls
|
| 23 |
+
- 🔐 **HuggingFace Integration**: Sign in with your HF account
|
| 24 |
+
|
| 25 |
+
## Quick Start
|
| 26 |
+
|
| 27 |
+
This Space runs HFStudio automatically. Simply:
|
| 28 |
+
|
| 29 |
+
1. **Open the Space** - The web interface will load automatically
|
| 30 |
+
2. **Sign In** (optional) - Click "Sign In with Hugging Face" for full features
|
| 31 |
+
3. **Enter Text** - Type or paste the text you want to convert to speech
|
| 32 |
+
4. **Choose Voice** - Select from available voice models
|
| 33 |
+
5. **Adjust Settings** - Tune speed, stability, and other parameters
|
| 34 |
+
6. **Generate** - Click "Generate speech" to create audio
|
| 35 |
+
7. **Play & Download** - Use the built-in player to preview your audio
|
| 36 |
+
|
| 37 |
+
## Local Installation
|
| 38 |
+
|
| 39 |
+
Want to run HFStudio locally? Install via pip:
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
pip install hfstudio
|
| 43 |
+
hfstudio
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Then visit `http://localhost:7860` in your browser.
|
| 47 |
+
|
| 48 |
+
## Development
|
| 49 |
+
|
| 50 |
+
This Space is automatically synced from the [GitHub repository](https://github.com/yourusername/hfstudio).
|
| 51 |
+
|
| 52 |
+
To contribute or run locally for development:
|
| 53 |
+
|
| 54 |
+
```bash
|
| 55 |
+
git clone https://github.com/yourusername/hfstudio
|
| 56 |
+
cd hfstudio
|
| 57 |
+
./run_dev.sh # Starts both frontend and backend
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
## API Usage
|
| 61 |
+
|
| 62 |
+
HFStudio also provides a REST API for integration:
|
| 63 |
+
|
| 64 |
+
```python
|
| 65 |
+
import requests
|
| 66 |
+
|
| 67 |
+
# Generate speech
|
| 68 |
+
response = requests.post('https://abidlabs-hfstudio.hf.space/api/tts/generate',
|
| 69 |
+
json={
|
| 70 |
+
"text": "Hello, world!",
|
| 71 |
+
"voice_id": "sarah",
|
| 72 |
+
"model_id": "coqui-tts"
|
| 73 |
+
}
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
audio_data = response.json()
|
| 77 |
+
print(f"Audio URL: {audio_data['audio_url']}")
|
| 78 |
+
```
|
| 79 |
+
|
| 80 |
+
## License
|
| 81 |
+
|
| 82 |
+
MIT License - see [LICENSE](LICENSE) for details.
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
Built with ❤️ using [SvelteKit](https://kit.svelte.dev/) and [FastAPI](https://fastapi.tiangolo.com/)
|
TECHNICAL_SPECS.md
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# HFStudio Technical Specifications
|
| 2 |
+
|
| 3 |
+
## Project Overview
|
| 4 |
+
HFStudio is a web-based text-to-speech application that provides both local and API-based TTS capabilities, inspired by ElevenLabs Studio but with support for local model execution.
|
| 5 |
+
|
| 6 |
+
## Core Features
|
| 7 |
+
|
| 8 |
+
### 1. Text-to-Speech Engine
|
| 9 |
+
- **Input**: Multi-line text area for user input
|
| 10 |
+
- **Output**: Generated audio playback with download capability
|
| 11 |
+
- **Models**: Support for multiple TTS models (local and API-based)
|
| 12 |
+
- **Voice Selection**: Dropdown/list for available voices
|
| 13 |
+
- **Audio Controls**: Play, pause, download generated audio
|
| 14 |
+
|
| 15 |
+
### 2. Execution Modes
|
| 16 |
+
- **API Mode**: Connect to remote TTS services (HuggingFace, OpenAI, etc.)
|
| 17 |
+
- **Local Mode**: Run TTS models locally using downloaded models
|
| 18 |
+
- **Mode Toggle**: Clear UI toggle between API and Local execution
|
| 19 |
+
- **Local Setup Instructions**: Display installation command when local mode selected
|
| 20 |
+
|
| 21 |
+
### 3. Voice Configuration
|
| 22 |
+
- **Speed Control**: Slider (0.5x - 2.0x speed)
|
| 23 |
+
- **Stability**: Slider for voice consistency (when applicable)
|
| 24 |
+
- **Similarity**: Slider for voice matching (when applicable)
|
| 25 |
+
- **Style/Emotion**: Optional controls for voice style
|
| 26 |
+
|
| 27 |
+
### 4. User Interface Layout
|
| 28 |
+
- **Left Sidebar**: Navigation and feature selection
|
| 29 |
+
- Home/Text-to-Speech (default)
|
| 30 |
+
- Settings
|
| 31 |
+
- History (future feature)
|
| 32 |
+
- **Main Content Area**: Text input and controls
|
| 33 |
+
- **Right Panel**: Voice/model selection and parameters
|
| 34 |
+
|
| 35 |
+
## Technology Stack
|
| 36 |
+
|
| 37 |
+
### Frontend
|
| 38 |
+
- **Framework**: SvelteKit
|
| 39 |
+
- **Styling**: TailwindCSS
|
| 40 |
+
- **Components**:
|
| 41 |
+
- Shadcn-svelte for UI components
|
| 42 |
+
- Audio player: Native HTML5 or Wavesurfer.js
|
| 43 |
+
- **State Management**: Svelte stores
|
| 44 |
+
- **Build Tool**: Vite
|
| 45 |
+
|
| 46 |
+
### Backend (Python Package)
|
| 47 |
+
- **Framework**: FastAPI for API server
|
| 48 |
+
- **TTS Libraries**:
|
| 49 |
+
- Transformers (HuggingFace models)
|
| 50 |
+
- Coqui TTS
|
| 51 |
+
- Optional: Piper, Bark
|
| 52 |
+
- **Audio Processing**: librosa, soundfile
|
| 53 |
+
- **CLI**: Click or Typer for command-line interface
|
| 54 |
+
|
| 55 |
+
### API Integration
|
| 56 |
+
- **HuggingFace Inference API**
|
| 57 |
+
- **OpenAI TTS API** (optional)
|
| 58 |
+
- **Custom model endpoints**
|
| 59 |
+
|
| 60 |
+
## Project Structure
|
| 61 |
+
|
| 62 |
+
```
|
| 63 |
+
hfstudio/
|
| 64 |
+
├── frontend/ # Svelte frontend
|
| 65 |
+
│ ├── src/
|
| 66 |
+
│ │ ├── routes/
|
| 67 |
+
│ │ │ ├── +layout.svelte
|
| 68 |
+
│ │ │ ├── +page.svelte
|
| 69 |
+
│ │ │ └── api/
|
| 70 |
+
│ │ ├── lib/
|
| 71 |
+
│ │ │ ├── components/
|
| 72 |
+
│ │ │ │ ├── Sidebar.svelte
|
| 73 |
+
│ │ │ │ ├── TextInput.svelte
|
| 74 |
+
│ │ │ │ ├── VoiceSelector.svelte
|
| 75 |
+
│ │ │ │ ├── AudioPlayer.svelte
|
| 76 |
+
│ │ │ │ ├── ModeToggle.svelte
|
| 77 |
+
│ │ │ │ └── ParameterControls.svelte
|
| 78 |
+
│ │ │ ├── stores/
|
| 79 |
+
│ │ │ │ ├── app.js
|
| 80 |
+
│ │ │ │ └── audio.js
|
| 81 |
+
│ │ │ └── api/
|
| 82 |
+
│ │ │ └── client.js
|
| 83 |
+
│ │ └── app.html
|
| 84 |
+
│ ├── package.json
|
| 85 |
+
│ ├── vite.config.js
|
| 86 |
+
│ └── tailwind.config.js
|
| 87 |
+
│
|
| 88 |
+
├── backend/ # Python backend
|
| 89 |
+
│ ├── hfstudio/
|
| 90 |
+
│ │ ├── __init__.py
|
| 91 |
+
│ │ ├── __main__.py
|
| 92 |
+
│ │ ├── server.py # FastAPI app
|
| 93 |
+
│ │ ├── cli.py # CLI interface
|
| 94 |
+
│ │ ├── models/
|
| 95 |
+
│ │ │ ├── __init__.py
|
| 96 |
+
│ │ │ ├── base.py
|
| 97 |
+
│ │ │ ├── local.py
|
| 98 |
+
│ │ │ └── api.py
|
| 99 |
+
│ │ ├── voices/
|
| 100 |
+
│ │ │ ├── __init__.py
|
| 101 |
+
│ │ │ └── manager.py
|
| 102 |
+
│ │ └── utils/
|
| 103 |
+
│ │ ├── __init__.py
|
| 104 |
+
│ │ └── audio.py
|
| 105 |
+
│ ├── requirements.txt
|
| 106 |
+
│ └── setup.py
|
| 107 |
+
│
|
| 108 |
+
├── README.md
|
| 109 |
+
└── docker-compose.yml # Optional containerization
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
## API Endpoints
|
| 113 |
+
|
| 114 |
+
### REST API
|
| 115 |
+
```
|
| 116 |
+
POST /api/tts/generate
|
| 117 |
+
Body: {
|
| 118 |
+
text: string,
|
| 119 |
+
voice_id: string,
|
| 120 |
+
model_id: string,
|
| 121 |
+
parameters: {
|
| 122 |
+
speed: float,
|
| 123 |
+
stability: float,
|
| 124 |
+
similarity: float,
|
| 125 |
+
style: string
|
| 126 |
+
},
|
| 127 |
+
mode: "api" | "local"
|
| 128 |
+
}
|
| 129 |
+
Response: {
|
| 130 |
+
audio_url: string,
|
| 131 |
+
duration: float,
|
| 132 |
+
format: string
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
GET /api/voices
|
| 136 |
+
Response: {
|
| 137 |
+
voices: [{
|
| 138 |
+
id: string,
|
| 139 |
+
name: string,
|
| 140 |
+
preview_url: string,
|
| 141 |
+
supported_models: string[]
|
| 142 |
+
}]
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
GET /api/models
|
| 146 |
+
Response: {
|
| 147 |
+
models: [{
|
| 148 |
+
id: string,
|
| 149 |
+
name: string,
|
| 150 |
+
type: "local" | "api",
|
| 151 |
+
status: "available" | "downloadable" | "api-only"
|
| 152 |
+
}]
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
GET /api/status
|
| 156 |
+
Response: {
|
| 157 |
+
mode: "api" | "local",
|
| 158 |
+
local_available: boolean,
|
| 159 |
+
api_configured: boolean
|
| 160 |
+
}
|
| 161 |
+
```
|
| 162 |
+
|
| 163 |
+
## Component Specifications
|
| 164 |
+
|
| 165 |
+
### 1. ModeToggle Component
|
| 166 |
+
```svelte
|
| 167 |
+
Props:
|
| 168 |
+
- mode: "api" | "local"
|
| 169 |
+
- onModeChange: function
|
| 170 |
+
|
| 171 |
+
Features:
|
| 172 |
+
- Visual toggle switch
|
| 173 |
+
- Installation hint for local mode
|
| 174 |
+
- Status indicator (green/yellow/red)
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
### 2. TextInput Component
|
| 178 |
+
```svelte
|
| 179 |
+
Props:
|
| 180 |
+
- value: string
|
| 181 |
+
- maxLength: number (default: 5000)
|
| 182 |
+
- placeholder: string
|
| 183 |
+
|
| 184 |
+
Features:
|
| 185 |
+
- Character counter
|
| 186 |
+
- Auto-resize
|
| 187 |
+
- Clear button
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
### 3. VoiceSelector Component
|
| 191 |
+
```svelte
|
| 192 |
+
Props:
|
| 193 |
+
- voices: Voice[]
|
| 194 |
+
- selectedVoice: string
|
| 195 |
+
- onSelect: function
|
| 196 |
+
|
| 197 |
+
Features:
|
| 198 |
+
- Search/filter
|
| 199 |
+
- Voice preview
|
| 200 |
+
- Favorite voices
|
| 201 |
+
```
|
| 202 |
+
|
| 203 |
+
### 4. AudioPlayer Component
|
| 204 |
+
```svelte
|
| 205 |
+
Props:
|
| 206 |
+
- audioUrl: string
|
| 207 |
+
- duration: number
|
| 208 |
+
|
| 209 |
+
Features:
|
| 210 |
+
- Play/pause
|
| 211 |
+
- Progress bar
|
| 212 |
+
- Volume control
|
| 213 |
+
- Download button
|
| 214 |
+
- Waveform visualization (optional)
|
| 215 |
+
```
|
| 216 |
+
|
| 217 |
+
## Local Package (hfstudio)
|
| 218 |
+
|
| 219 |
+
### Installation
|
| 220 |
+
```bash
|
| 221 |
+
pip install hfstudio
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
### CLI Usage
|
| 225 |
+
```bash
|
| 226 |
+
# Start the server
|
| 227 |
+
hfstudio
|
| 228 |
+
|
| 229 |
+
# Start with custom port
|
| 230 |
+
hfstudio --port 8080
|
| 231 |
+
|
| 232 |
+
# Download models for offline use
|
| 233 |
+
hfstudio download-models
|
| 234 |
+
|
| 235 |
+
# List available models
|
| 236 |
+
hfstudio list-models
|
| 237 |
+
```
|
| 238 |
+
|
| 239 |
+
### Python API
|
| 240 |
+
```python
|
| 241 |
+
from hfstudio import TTSEngine
|
| 242 |
+
|
| 243 |
+
# Initialize engine
|
| 244 |
+
engine = TTSEngine(mode="local")
|
| 245 |
+
|
| 246 |
+
# Generate speech
|
| 247 |
+
audio = engine.generate(
|
| 248 |
+
text="Hello, world!",
|
| 249 |
+
voice="default",
|
| 250 |
+
model="coqui/tts-vits"
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
# Save audio
|
| 254 |
+
audio.save("output.wav")
|
| 255 |
+
```
|
| 256 |
+
|
| 257 |
+
## Configuration
|
| 258 |
+
|
| 259 |
+
### Frontend (.env)
|
| 260 |
+
```env
|
| 261 |
+
PUBLIC_API_URL=http://localhost:8000
|
| 262 |
+
PUBLIC_DEFAULT_MODE=api
|
| 263 |
+
```
|
| 264 |
+
|
| 265 |
+
### Backend (config.yaml)
|
| 266 |
+
```yaml
|
| 267 |
+
server:
|
| 268 |
+
host: 0.0.0.0
|
| 269 |
+
port: 8000
|
| 270 |
+
cors_origins:
|
| 271 |
+
- http://localhost:5173
|
| 272 |
+
- http://localhost:3000
|
| 273 |
+
|
| 274 |
+
models:
|
| 275 |
+
local:
|
| 276 |
+
cache_dir: ~/.hfstudio/models
|
| 277 |
+
default: "coqui/tts-vits"
|
| 278 |
+
api:
|
| 279 |
+
huggingface_token: ${HF_TOKEN}
|
| 280 |
+
openai_key: ${OPENAI_API_KEY}
|
| 281 |
+
|
| 282 |
+
audio:
|
| 283 |
+
output_format: "wav"
|
| 284 |
+
sample_rate: 22050
|
| 285 |
+
bitrate: 128
|
| 286 |
+
```
|
| 287 |
+
|
| 288 |
+
## Development Workflow
|
| 289 |
+
|
| 290 |
+
### Phase 1: MVP
|
| 291 |
+
1. Basic Svelte frontend with text input and generate button
|
| 292 |
+
2. FastAPI backend with single TTS model support
|
| 293 |
+
3. Mode toggle (UI only, local mode shows installation message)
|
| 294 |
+
4. Basic audio playback
|
| 295 |
+
|
| 296 |
+
### Phase 2: Core Features
|
| 297 |
+
1. Multiple voice support
|
| 298 |
+
2. Parameter controls (speed, stability, similarity)
|
| 299 |
+
3. Local model execution
|
| 300 |
+
4. Audio download functionality
|
| 301 |
+
|
| 302 |
+
### Phase 3: Enhanced Features
|
| 303 |
+
1. History/saved generations
|
| 304 |
+
2. Voice cloning (if supported by models)
|
| 305 |
+
3. Batch processing
|
| 306 |
+
4. Audio format options
|
| 307 |
+
|
| 308 |
+
### Phase 4: Polish
|
| 309 |
+
1. Waveform visualization
|
| 310 |
+
2. Real-time generation (streaming)
|
| 311 |
+
3. Voice preview
|
| 312 |
+
4. Keyboard shortcuts
|
| 313 |
+
|
| 314 |
+
## Performance Requirements
|
| 315 |
+
- **API Response Time**: < 2s for typical requests
|
| 316 |
+
- **Local Generation**: < 5s for 100 words
|
| 317 |
+
- **Frontend Load Time**: < 1s
|
| 318 |
+
- **Audio Streaming**: Start playback within 500ms
|
| 319 |
+
|
| 320 |
+
## Security Considerations
|
| 321 |
+
- API key management (environment variables)
|
| 322 |
+
- CORS configuration
|
| 323 |
+
- Rate limiting
|
| 324 |
+
- Input sanitization
|
| 325 |
+
- File size limits for audio generation
|
| 326 |
+
|
| 327 |
+
## Testing Strategy
|
| 328 |
+
- Frontend: Vitest for unit tests, Playwright for E2E
|
| 329 |
+
- Backend: Pytest for unit and integration tests
|
| 330 |
+
- Load testing: Locust or K6
|
| 331 |
+
- Audio quality: Manual testing with various inputs
|
| 332 |
+
|
| 333 |
+
## Deployment Options
|
| 334 |
+
1. **Standalone**: User runs both frontend and backend locally
|
| 335 |
+
2. **Docker**: Containerized deployment
|
| 336 |
+
3. **Cloud**: Separate frontend (Vercel/Netlify) and backend (Railway/Fly.io)
|
| 337 |
+
4. **Desktop**: Electron wrapper (future consideration)
|
app.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
HuggingFace Space entry point for HFStudio
|
| 4 |
+
"""
|
| 5 |
+
import uvicorn
|
| 6 |
+
from hfstudio.server import app
|
| 7 |
+
|
| 8 |
+
if __name__ == "__main__":
|
| 9 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
frontend/package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
frontend/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "hfstudio-frontend",
|
| 3 |
+
"version": "0.1.1",
|
| 4 |
+
"private": true,
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "vite dev",
|
| 7 |
+
"build": "vite build",
|
| 8 |
+
"preview": "vite preview",
|
| 9 |
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
| 10 |
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
| 11 |
+
},
|
| 12 |
+
"devDependencies": {
|
| 13 |
+
"@sveltejs/adapter-auto": "^3.0.0",
|
| 14 |
+
"@sveltejs/kit": "^2.0.0",
|
| 15 |
+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
| 16 |
+
"autoprefixer": "^10.4.16",
|
| 17 |
+
"postcss": "^8.4.32",
|
| 18 |
+
"svelte": "^4.2.7",
|
| 19 |
+
"svelte-check": "^3.6.0",
|
| 20 |
+
"tailwindcss": "^3.3.0",
|
| 21 |
+
"typescript": "^5.0.0",
|
| 22 |
+
"vite": "^5.0.3"
|
| 23 |
+
},
|
| 24 |
+
"type": "module",
|
| 25 |
+
"dependencies": {
|
| 26 |
+
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
| 27 |
+
"@sveltejs/adapter-static": "^3.0.10",
|
| 28 |
+
"bits-ui": "^0.21.0",
|
| 29 |
+
"clsx": "^2.0.0",
|
| 30 |
+
"lucide-svelte": "^0.303.0",
|
| 31 |
+
"mode-watcher": "^0.2.0",
|
| 32 |
+
"svelte-sonner": "^0.3.19",
|
| 33 |
+
"tailwind-merge": "^2.2.0",
|
| 34 |
+
"tailwind-variants": "^0.1.19",
|
| 35 |
+
"wavesurfer.js": "^7.6.0"
|
| 36 |
+
}
|
| 37 |
+
}
|
frontend/postcss.config.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export default {
|
| 2 |
+
plugins: {
|
| 3 |
+
tailwindcss: {},
|
| 4 |
+
autoprefixer: {},
|
| 5 |
+
},
|
| 6 |
+
}
|
frontend/src/app.css
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@tailwind base;
|
| 2 |
+
@tailwind components;
|
| 3 |
+
@tailwind utilities;
|
| 4 |
+
|
| 5 |
+
@layer base {
|
| 6 |
+
:root {
|
| 7 |
+
--background: 0 0% 100%;
|
| 8 |
+
--foreground: 240 10% 3.9%;
|
| 9 |
+
--card: 0 0% 100%;
|
| 10 |
+
--card-foreground: 240 10% 3.9%;
|
| 11 |
+
--popover: 0 0% 100%;
|
| 12 |
+
--popover-foreground: 240 10% 3.9%;
|
| 13 |
+
--primary: 240 5.9% 10%;
|
| 14 |
+
--primary-foreground: 0 0% 98%;
|
| 15 |
+
--secondary: 240 4.8% 95.9%;
|
| 16 |
+
--secondary-foreground: 240 5.9% 10%;
|
| 17 |
+
--muted: 240 4.8% 95.9%;
|
| 18 |
+
--muted-foreground: 240 3.8% 46.1%;
|
| 19 |
+
--accent: 240 4.8% 95.9%;
|
| 20 |
+
--accent-foreground: 240 5.9% 10%;
|
| 21 |
+
--destructive: 0 84.2% 60.2%;
|
| 22 |
+
--destructive-foreground: 0 0% 98%;
|
| 23 |
+
--border: 240 5.9% 90%;
|
| 24 |
+
--input: 240 5.9% 90%;
|
| 25 |
+
--ring: 240 5.9% 10%;
|
| 26 |
+
--radius: 0.5rem;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
.dark {
|
| 30 |
+
--background: 240 10% 3.9%;
|
| 31 |
+
--foreground: 0 0% 98%;
|
| 32 |
+
--card: 240 10% 3.9%;
|
| 33 |
+
--card-foreground: 0 0% 98%;
|
| 34 |
+
--popover: 240 10% 3.9%;
|
| 35 |
+
--popover-foreground: 0 0% 98%;
|
| 36 |
+
--primary: 0 0% 98%;
|
| 37 |
+
--primary-foreground: 240 5.9% 10%;
|
| 38 |
+
--secondary: 240 3.7% 15.9%;
|
| 39 |
+
--secondary-foreground: 0 0% 98%;
|
| 40 |
+
--muted: 240 3.7% 15.9%;
|
| 41 |
+
--muted-foreground: 240 5% 64.9%;
|
| 42 |
+
--accent: 240 3.7% 15.9%;
|
| 43 |
+
--accent-foreground: 0 0% 98%;
|
| 44 |
+
--destructive: 0 62.8% 30.6%;
|
| 45 |
+
--destructive-foreground: 0 0% 98%;
|
| 46 |
+
--border: 240 3.7% 15.9%;
|
| 47 |
+
--input: 240 3.7% 15.9%;
|
| 48 |
+
--ring: 240 4.9% 83.9%;
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
body {
|
| 53 |
+
@apply bg-background text-foreground;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/* Custom slider styles with HF brand colors */
|
| 57 |
+
.slider-hf::-webkit-slider-thumb {
|
| 58 |
+
@apply appearance-none w-4 h-4 rounded-full cursor-pointer;
|
| 59 |
+
background: linear-gradient(45deg, #FFD21E, #FF9D00);
|
| 60 |
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
.slider-hf::-moz-range-thumb {
|
| 64 |
+
@apply w-4 h-4 rounded-full cursor-pointer border-0;
|
| 65 |
+
background: linear-gradient(45deg, #FFD21E, #FF9D00);
|
| 66 |
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/* Custom pause icon with filled bars */
|
| 70 |
+
.pause-filled {
|
| 71 |
+
display: inline-flex;
|
| 72 |
+
align-items: center;
|
| 73 |
+
justify-content: center;
|
| 74 |
+
width: 14px;
|
| 75 |
+
height: 14px;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.pause-filled::before,
|
| 79 |
+
.pause-filled::after {
|
| 80 |
+
content: '';
|
| 81 |
+
width: 2px;
|
| 82 |
+
height: 10px;
|
| 83 |
+
background-color: currentColor;
|
| 84 |
+
border-radius: 1px;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
.pause-filled::before {
|
| 88 |
+
margin-right: 2px;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.pause-filled::after {
|
| 92 |
+
margin-left: 2px;
|
| 93 |
+
}
|
frontend/src/app.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<link rel="icon" href="/assets/hf-logo.png" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
+
<title>HFStudio - Text to Speech</title>
|
| 8 |
+
%sveltekit.head%
|
| 9 |
+
</head>
|
| 10 |
+
<body data-sveltekit-preload-data="hover">
|
| 11 |
+
<div style="display: contents">%sveltekit.body%</div>
|
| 12 |
+
</body>
|
| 13 |
+
</html>
|
frontend/src/routes/+layout.svelte
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script>
|
| 2 |
+
import '../app.css';
|
| 3 |
+
import { Home, Settings, History, Github, Menu } from 'lucide-svelte';
|
| 4 |
+
import { onMount } from 'svelte';
|
| 5 |
+
|
| 6 |
+
let currentPage = 'tts';
|
| 7 |
+
let sidebarOpen = true;
|
| 8 |
+
let isLoggedIn = false;
|
| 9 |
+
let username = '';
|
| 10 |
+
|
| 11 |
+
onMount(() => {
|
| 12 |
+
// Check if user is logged in
|
| 13 |
+
checkLoginStatus();
|
| 14 |
+
|
| 15 |
+
// Re-check login status when page becomes visible (e.g., after OAuth redirect)
|
| 16 |
+
document.addEventListener('visibilitychange', () => {
|
| 17 |
+
if (!document.hidden) {
|
| 18 |
+
checkLoginStatus();
|
| 19 |
+
}
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
+
// Listen for storage changes (e.g., when token is set from OAuth callback)
|
| 23 |
+
window.addEventListener('storage', checkLoginStatus);
|
| 24 |
+
|
| 25 |
+
// Also check periodically to catch cases where localStorage changes in same tab
|
| 26 |
+
const interval = setInterval(checkLoginStatus, 1000);
|
| 27 |
+
|
| 28 |
+
return () => {
|
| 29 |
+
window.removeEventListener('storage', checkLoginStatus);
|
| 30 |
+
clearInterval(interval);
|
| 31 |
+
};
|
| 32 |
+
});
|
| 33 |
+
|
| 34 |
+
function checkLoginStatus() {
|
| 35 |
+
const token = localStorage.getItem('hf_access_token');
|
| 36 |
+
if (token) {
|
| 37 |
+
// Fetch user info from HuggingFace API
|
| 38 |
+
fetchUserInfo(token);
|
| 39 |
+
} else {
|
| 40 |
+
isLoggedIn = false;
|
| 41 |
+
username = '';
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
async function fetchUserInfo(token) {
|
| 46 |
+
try {
|
| 47 |
+
// For OAuth tokens, we need to use the OAuth API endpoint
|
| 48 |
+
const isOAuthToken = token.startsWith('hf_oauth_');
|
| 49 |
+
const apiUrl = isOAuthToken
|
| 50 |
+
? 'https://huggingface.co/oauth/userinfo'
|
| 51 |
+
: 'https://huggingface.co/api/whoami';
|
| 52 |
+
|
| 53 |
+
const response = await fetch(apiUrl, {
|
| 54 |
+
headers: {
|
| 55 |
+
'Authorization': `Bearer ${token}`
|
| 56 |
+
}
|
| 57 |
+
});
|
| 58 |
+
|
| 59 |
+
if (response.ok) {
|
| 60 |
+
const userData = await response.json();
|
| 61 |
+
isLoggedIn = true;
|
| 62 |
+
const fullName = userData.name || userData.login || 'User';
|
| 63 |
+
username = fullName.split(' ')[0]; // Extract first name only
|
| 64 |
+
} else {
|
| 65 |
+
// Token might be invalid, remove it
|
| 66 |
+
localStorage.removeItem('hf_access_token');
|
| 67 |
+
isLoggedIn = false;
|
| 68 |
+
username = '';
|
| 69 |
+
}
|
| 70 |
+
} catch (error) {
|
| 71 |
+
// Token might be invalid, remove it
|
| 72 |
+
localStorage.removeItem('hf_access_token');
|
| 73 |
+
isLoggedIn = false;
|
| 74 |
+
username = '';
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
function handleAuthAction() {
|
| 79 |
+
if (isLoggedIn) {
|
| 80 |
+
// Logout
|
| 81 |
+
localStorage.removeItem('hf_access_token');
|
| 82 |
+
sessionStorage.removeItem('oauth_state');
|
| 83 |
+
isLoggedIn = false;
|
| 84 |
+
username = '';
|
| 85 |
+
} else {
|
| 86 |
+
// Login
|
| 87 |
+
const clientId = 'cdf32a17-e40f-4a84-b683-f66aa1105793';
|
| 88 |
+
const redirectUri = 'http://localhost:11111/auth/callback';
|
| 89 |
+
const scope = 'read-repos';
|
| 90 |
+
const state = Math.random().toString(36).substring(2, 15);
|
| 91 |
+
const authUrl = `https://huggingface.co/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}`;
|
| 92 |
+
|
| 93 |
+
sessionStorage.setItem('oauth_state', state);
|
| 94 |
+
window.location.href = authUrl;
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
</script>
|
| 98 |
+
|
| 99 |
+
<div class="flex h-screen bg-white">
|
| 100 |
+
<!-- Sidebar -->
|
| 101 |
+
<aside class="w-56 border-r border-gray-200 bg-white flex-shrink-0 {sidebarOpen ? '' : 'hidden'}">
|
| 102 |
+
<div class="p-4 border-b border-gray-200">
|
| 103 |
+
<div class="flex items-center gap-3">
|
| 104 |
+
<img src="/assets/hf-studio-logo.png" alt="HF Logo" class="w-8 h-8" />
|
| 105 |
+
<h1 class="text-xl font-semibold">HFStudio<sup class="text-xs text-gray-500 ml-1">BETA</sup></h1>
|
| 106 |
+
</div>
|
| 107 |
+
</div>
|
| 108 |
+
|
| 109 |
+
<nav class="p-2 text-sm">
|
| 110 |
+
<div class="mt-2 mb-1 px-2 text-xs font-medium text-gray-500 uppercase">
|
| 111 |
+
Tasks
|
| 112 |
+
</div>
|
| 113 |
+
|
| 114 |
+
<button
|
| 115 |
+
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 transition-colors text-left
|
| 116 |
+
{currentPage === 'tts' ? 'bg-gray-100' : ''}"
|
| 117 |
+
on:click={() => currentPage = 'tts'}
|
| 118 |
+
>
|
| 119 |
+
<span>🎙️</span>
|
| 120 |
+
<span>Text to Speech</span>
|
| 121 |
+
</button>
|
| 122 |
+
|
| 123 |
+
<button
|
| 124 |
+
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"
|
| 125 |
+
disabled
|
| 126 |
+
>
|
| 127 |
+
<span>🎵</span>
|
| 128 |
+
<span>Voice Cloning</span>
|
| 129 |
+
</button>
|
| 130 |
+
|
| 131 |
+
<button
|
| 132 |
+
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"
|
| 133 |
+
disabled
|
| 134 |
+
>
|
| 135 |
+
<span>🎧</span>
|
| 136 |
+
<span>Speech to Text</span>
|
| 137 |
+
</button>
|
| 138 |
+
|
| 139 |
+
<button
|
| 140 |
+
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"
|
| 141 |
+
disabled
|
| 142 |
+
>
|
| 143 |
+
<span>🎼</span>
|
| 144 |
+
<span>Sound Effects</span>
|
| 145 |
+
</button>
|
| 146 |
+
|
| 147 |
+
<button
|
| 148 |
+
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"
|
| 149 |
+
disabled
|
| 150 |
+
>
|
| 151 |
+
<span>🎸</span>
|
| 152 |
+
<span>Music Generation</span>
|
| 153 |
+
</button>
|
| 154 |
+
|
| 155 |
+
<button
|
| 156 |
+
class="w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"
|
| 157 |
+
disabled
|
| 158 |
+
>
|
| 159 |
+
<span>🔊</span>
|
| 160 |
+
<span>Audio Enhancement</span>
|
| 161 |
+
</button>
|
| 162 |
+
|
| 163 |
+
</nav>
|
| 164 |
+
|
| 165 |
+
<!-- Sign in with Hugging Face at bottom -->
|
| 166 |
+
<div class="absolute bottom-4 left-2 right-2 w-52">
|
| 167 |
+
<button
|
| 168 |
+
on:click={handleAuthAction}
|
| 169 |
+
class="w-full px-6 py-3 bg-black text-white rounded-lg font-medium hover:bg-gray-800 transition-colors shadow-sm flex items-center justify-center gap-2 text-sm"
|
| 170 |
+
>
|
| 171 |
+
<img src="/assets/hf-logo.png" alt="HF Logo" class="w-5 h-5" />
|
| 172 |
+
{#if isLoggedIn}
|
| 173 |
+
<span>Sign Out, {username}</span>
|
| 174 |
+
{:else}
|
| 175 |
+
<span>Sign In</span>
|
| 176 |
+
{/if}
|
| 177 |
+
</button>
|
| 178 |
+
</div>
|
| 179 |
+
</aside>
|
| 180 |
+
|
| 181 |
+
<!-- Main content -->
|
| 182 |
+
<main class="flex-1 overflow-auto">
|
| 183 |
+
<slot />
|
| 184 |
+
</main>
|
| 185 |
+
</div>
|
frontend/src/routes/+page.svelte
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script>
|
| 2 |
+
import { Play, Download, Loader2, AlertCircle, ChevronDown, Copy, RefreshCw, Share, MoreHorizontal, Settings, Sliders, Pause, SkipBack, SkipForward } from 'lucide-svelte';
|
| 3 |
+
|
| 4 |
+
let text = `Adam was playing outside when he heard a tiny meow! He looked around. Meow! There it was again! He tiptoed toward the bushes. Pushing the leaves aside, he gasped. A fluffy, orange kitten with big, curious eyes stared up at him. "Oh no! Where is your home?" Adam asked. The kitten just blinked and let out a tiny mew!
|
| 5 |
+
|
| 6 |
+
"Mama! I found a lost kitten!" Adam called as he carried the little furball inside. The kitten sniffed around, then jumped up—boing!—right onto the couch. "Oh!" Mama laughed. "Let's give it some milk while we find its owner." The kitten licked the milk happily and purred, prrrr! Adam giggled. "I think he likes it here!"
|
| 7 |
+
|
| 8 |
+
That evening, there was a knock at the door. A little girl stood there, eyes wide. "Have you seen my kitten, Biscuit?" she asked. Adam smiled and pointed. The kitten ran to her, mew mew mew! The girl hugged Biscuit tightly. "Thank you!" she said. As she left, Adam waved. "Goodbye, Biscuit!" he whispered. And in the quiet, he could still hear a happy prrrr!`;
|
| 9 |
+
let selectedVoice = 'Novia';
|
| 10 |
+
let selectedModel = 'Chatterbox';
|
| 11 |
+
let mode = 'api'; // 'api' or 'local'
|
| 12 |
+
let modelDropdownOpen = false;
|
| 13 |
+
let isGenerating = false;
|
| 14 |
+
let audioUrl = null;
|
| 15 |
+
let speed = 1.0;
|
| 16 |
+
let stability = 0.5;
|
| 17 |
+
let similarity = 0.75;
|
| 18 |
+
let styleExaggeration = 0;
|
| 19 |
+
let showSettings = true;
|
| 20 |
+
let isPlaying = false;
|
| 21 |
+
let currentTime = 0;
|
| 22 |
+
let duration = 0;
|
| 23 |
+
let audioTitle = '';
|
| 24 |
+
let audioElement = null;
|
| 25 |
+
|
| 26 |
+
const models = [
|
| 27 |
+
{ id: 'chatterbox', name: 'Chatterbox', badge: 'recommended' },
|
| 28 |
+
{ id: 'kokoro', name: 'Kokoro', badge: 'faster but lower quality' },
|
| 29 |
+
];
|
| 30 |
+
|
| 31 |
+
const voices = [
|
| 32 |
+
{ id: 'novia', name: 'Novia', description: 'Warm, conversational voice' },
|
| 33 |
+
{ id: 'sarah', name: 'Sarah', description: 'Clear, professional tone' },
|
| 34 |
+
{ id: 'alex', name: 'Alex', description: 'Friendly, approachable voice' },
|
| 35 |
+
{ id: 'emma', name: 'Emma', description: 'Calm, soothing delivery' },
|
| 36 |
+
];
|
| 37 |
+
|
| 38 |
+
async function generateSpeech() {
|
| 39 |
+
if (!text.trim()) return;
|
| 40 |
+
|
| 41 |
+
isGenerating = true;
|
| 42 |
+
audioUrl = null;
|
| 43 |
+
currentTime = 0;
|
| 44 |
+
isPlaying = false;
|
| 45 |
+
|
| 46 |
+
// Create title from first part of text
|
| 47 |
+
audioTitle = text.length > 30 ? text.substring(0, 30) + '...' : text;
|
| 48 |
+
|
| 49 |
+
// Simulate API call
|
| 50 |
+
setTimeout(() => {
|
| 51 |
+
audioUrl = '/samples/harvard.wav';
|
| 52 |
+
isGenerating = false;
|
| 53 |
+
}, 2000);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
function togglePlayPause() {
|
| 57 |
+
if (audioElement) {
|
| 58 |
+
if (isPlaying) {
|
| 59 |
+
audioElement.pause();
|
| 60 |
+
} else {
|
| 61 |
+
audioElement.play();
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
function handleAudioLoad() {
|
| 67 |
+
if (audioElement) {
|
| 68 |
+
duration = audioElement.duration;
|
| 69 |
+
// Auto-play when audio loads
|
| 70 |
+
audioElement.play();
|
| 71 |
+
}
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
function handleTimeUpdate() {
|
| 75 |
+
if (audioElement) {
|
| 76 |
+
currentTime = audioElement.currentTime;
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
function handlePlay() {
|
| 81 |
+
isPlaying = true;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
function handlePause() {
|
| 85 |
+
isPlaying = false;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
function formatTime(seconds) {
|
| 89 |
+
const mins = Math.floor(seconds / 60);
|
| 90 |
+
const secs = Math.floor(seconds % 60);
|
| 91 |
+
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
function downloadAudio() {
|
| 95 |
+
if (audioUrl) {
|
| 96 |
+
const a = document.createElement('a');
|
| 97 |
+
a.href = audioUrl;
|
| 98 |
+
a.download = 'speech.wav';
|
| 99 |
+
a.click();
|
| 100 |
+
}
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
function shareAudio() {
|
| 104 |
+
// Share functionality would go here
|
| 105 |
+
console.log('Share audio');
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
function handleKeyDown(event) {
|
| 109 |
+
if (event.key === 'Enter' && !event.shiftKey) {
|
| 110 |
+
event.preventDefault();
|
| 111 |
+
generateSpeech();
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
function handleClickOutside(event) {
|
| 116 |
+
if (!event.target.closest('.model-dropdown')) {
|
| 117 |
+
modelDropdownOpen = false;
|
| 118 |
+
}
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
function clearText() {
|
| 122 |
+
text = '';
|
| 123 |
+
audioUrl = null;
|
| 124 |
+
}
|
| 125 |
+
</script>
|
| 126 |
+
|
| 127 |
+
<div class="flex flex-col h-full" on:click={handleClickOutside}>
|
| 128 |
+
<!-- Header -->
|
| 129 |
+
<header class="border-b border-gray-200 bg-white">
|
| 130 |
+
<div class="flex items-center justify-between px-4 py-2">
|
| 131 |
+
<div class="flex items-center gap-3">
|
| 132 |
+
<!-- Mode toggle -->
|
| 133 |
+
<div class="flex items-center bg-gray-100 rounded-md p-0.5">
|
| 134 |
+
<button
|
| 135 |
+
class="px-3 py-1 text-sm font-medium rounded transition-colors {mode === 'api' ? 'bg-white shadow-sm' : 'text-gray-600'}"
|
| 136 |
+
on:click={() => mode = 'api'}
|
| 137 |
+
>
|
| 138 |
+
API
|
| 139 |
+
</button>
|
| 140 |
+
<button
|
| 141 |
+
class="px-3 py-1 text-sm font-medium rounded transition-colors {mode === 'local' ? 'bg-white shadow-sm' : 'text-gray-600'}"
|
| 142 |
+
on:click={() => mode = 'local'}
|
| 143 |
+
>
|
| 144 |
+
Local
|
| 145 |
+
</button>
|
| 146 |
+
</div>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<div class="flex items-center gap-2">
|
| 150 |
+
<button class="p-2 hover:bg-gray-50 rounded-md" title="Settings">
|
| 151 |
+
<Settings size={18} class="text-gray-600" />
|
| 152 |
+
</button>
|
| 153 |
+
<button class="p-2 hover:bg-gray-50 rounded-md" title="Share">
|
| 154 |
+
<Share size={18} class="text-gray-600" />
|
| 155 |
+
</button>
|
| 156 |
+
<button class="p-2 hover:bg-gray-50 rounded-md" title="More">
|
| 157 |
+
<MoreHorizontal size={18} class="text-gray-600" />
|
| 158 |
+
</button>
|
| 159 |
+
</div>
|
| 160 |
+
</div>
|
| 161 |
+
</header>
|
| 162 |
+
|
| 163 |
+
<!-- Main content area -->
|
| 164 |
+
<div class="flex-1 flex">
|
| 165 |
+
<!-- Main content area -->
|
| 166 |
+
<div class="flex-1 flex flex-col p-6">
|
| 167 |
+
{#if mode === 'local'}
|
| 168 |
+
<div class="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg flex items-start gap-2">
|
| 169 |
+
<AlertCircle size={18} class="text-blue-600 mt-0.5 flex-shrink-0" />
|
| 170 |
+
<div class="text-sm">
|
| 171 |
+
<p class="font-medium text-blue-900">To run locally:</p>
|
| 172 |
+
<code class="text-xs bg-blue-100 px-1.5 py-0.5 rounded">pip install hfstudio</code>
|
| 173 |
+
<span class="text-blue-700"> and run </span>
|
| 174 |
+
<code class="text-xs bg-blue-100 px-1.5 py-0.5 rounded">hfstudio</code>
|
| 175 |
+
<span class="text-blue-700"> from your terminal</span>
|
| 176 |
+
</div>
|
| 177 |
+
</div>
|
| 178 |
+
{/if}
|
| 179 |
+
|
| 180 |
+
<!-- Text input area -->
|
| 181 |
+
<div class="flex-1 pb-24">
|
| 182 |
+
<textarea
|
| 183 |
+
bind:value={text}
|
| 184 |
+
class="w-full h-full p-6 bg-white resize-none border-0 focus:outline-none text-gray-900 text-base leading-relaxed"
|
| 185 |
+
placeholder="Enter text to convert to speech..."
|
| 186 |
+
/>
|
| 187 |
+
|
| 188 |
+
{#if text}
|
| 189 |
+
<button
|
| 190 |
+
on:click={clearText}
|
| 191 |
+
class="absolute top-4 right-6 text-sm text-gray-400 hover:text-gray-600"
|
| 192 |
+
>
|
| 193 |
+
Clear
|
| 194 |
+
</button>
|
| 195 |
+
{/if}
|
| 196 |
+
</div>
|
| 197 |
+
|
| 198 |
+
<!-- Fixed bottom generate button -->
|
| 199 |
+
<div class="fixed bottom-0 left-56 right-80 p-4 bg-white border-t border-gray-200">
|
| 200 |
+
<div class="flex items-center justify-between mb-3">
|
| 201 |
+
<span class="text-sm text-gray-500">{text.length} / 5,000 characters</span>
|
| 202 |
+
</div>
|
| 203 |
+
<button
|
| 204 |
+
on:click={generateSpeech}
|
| 205 |
+
disabled={isGenerating || !text.trim()}
|
| 206 |
+
class="w-full px-6 py-3 bg-gradient-to-r from-amber-400 to-orange-500 text-white rounded-lg font-medium hover:from-amber-500 hover:to-orange-600 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 shadow-sm"
|
| 207 |
+
>
|
| 208 |
+
{#if isGenerating}
|
| 209 |
+
<Loader2 size={20} class="animate-spin" />
|
| 210 |
+
Generating...
|
| 211 |
+
{:else}
|
| 212 |
+
<Play size={20} />
|
| 213 |
+
Generate speech
|
| 214 |
+
{/if}
|
| 215 |
+
</button>
|
| 216 |
+
</div>
|
| 217 |
+
|
| 218 |
+
<!-- Generated audio section -->
|
| 219 |
+
{#if audioUrl}
|
| 220 |
+
<div class="p-4 border border-gray-200 rounded-lg bg-white">
|
| 221 |
+
<!-- Audio title and voice info -->
|
| 222 |
+
<div class="flex items-center gap-3 mb-4">
|
| 223 |
+
<div class="w-2 h-2 bg-green-500 rounded-full"></div>
|
| 224 |
+
<div class="flex-1">
|
| 225 |
+
<h3 class="font-medium text-gray-900 text-sm">{audioTitle}</h3>
|
| 226 |
+
<p class="text-xs text-gray-500">{selectedVoice} • Created 1 second ago</p>
|
| 227 |
+
</div>
|
| 228 |
+
<!-- Mini action buttons -->
|
| 229 |
+
<div class="flex items-center gap-2">
|
| 230 |
+
<button
|
| 231 |
+
on:click={shareAudio}
|
| 232 |
+
class="flex items-center gap-1.5 px-3 py-1.5 text-sm border border-gray-200 rounded-md hover:bg-gray-50 transition-colors"
|
| 233 |
+
>
|
| 234 |
+
<Share size={14} class="text-gray-600" />
|
| 235 |
+
<span class="text-gray-700">Share</span>
|
| 236 |
+
</button>
|
| 237 |
+
<button
|
| 238 |
+
on:click={downloadAudio}
|
| 239 |
+
class="flex items-center gap-1.5 px-3 py-1.5 text-sm border border-gray-200 rounded-md hover:bg-gray-50 transition-colors"
|
| 240 |
+
>
|
| 241 |
+
<span class="text-gray-700">Download</span>
|
| 242 |
+
<Download size={14} class="text-gray-600" />
|
| 243 |
+
</button>
|
| 244 |
+
</div>
|
| 245 |
+
</div>
|
| 246 |
+
|
| 247 |
+
<!-- Mini audio controls -->
|
| 248 |
+
<div class="flex items-center gap-3 mb-4">
|
| 249 |
+
<!-- Play/Pause button -->
|
| 250 |
+
<button
|
| 251 |
+
on:click={togglePlayPause}
|
| 252 |
+
class="w-8 h-8 bg-black rounded-full flex items-center justify-center hover:bg-gray-800 transition-colors"
|
| 253 |
+
>
|
| 254 |
+
{#if isPlaying}
|
| 255 |
+
<div class="pause-filled text-white"></div>
|
| 256 |
+
{:else}
|
| 257 |
+
<Play size={14} class="text-white ml-0.5" />
|
| 258 |
+
{/if}
|
| 259 |
+
</button>
|
| 260 |
+
|
| 261 |
+
<!-- Progress bar -->
|
| 262 |
+
<div class="flex-1 flex items-center gap-2">
|
| 263 |
+
<span class="text-xs text-gray-500 font-mono">{formatTime(currentTime)}</span>
|
| 264 |
+
<div class="flex-1 h-1 bg-gray-200 rounded-full cursor-pointer">
|
| 265 |
+
<div
|
| 266 |
+
class="h-full bg-gradient-to-r from-amber-400 to-orange-500 rounded-full transition-all"
|
| 267 |
+
style="width: {(currentTime / duration) * 100}%"
|
| 268 |
+
></div>
|
| 269 |
+
</div>
|
| 270 |
+
<span class="text-xs text-gray-500 font-mono">{formatTime(duration)}</span>
|
| 271 |
+
</div>
|
| 272 |
+
</div>
|
| 273 |
+
|
| 274 |
+
<!-- Full audio player controls -->
|
| 275 |
+
<div class="flex items-center gap-4 mb-4">
|
| 276 |
+
<!-- Skip back button -->
|
| 277 |
+
<button class="p-2 hover:bg-gray-100 rounded-full" title="Skip back">
|
| 278 |
+
<SkipBack size={20} class="text-gray-600" />
|
| 279 |
+
</button>
|
| 280 |
+
|
| 281 |
+
<!-- Play/Pause button -->
|
| 282 |
+
<button
|
| 283 |
+
on:click={togglePlayPause}
|
| 284 |
+
class="w-12 h-12 bg-black rounded-full flex items-center justify-center hover:bg-gray-800 transition-colors"
|
| 285 |
+
>
|
| 286 |
+
{#if isPlaying}
|
| 287 |
+
<div class="pause-filled text-white scale-150"></div>
|
| 288 |
+
{:else}
|
| 289 |
+
<Play size={20} class="text-white ml-0.5" />
|
| 290 |
+
{/if}
|
| 291 |
+
</button>
|
| 292 |
+
|
| 293 |
+
<!-- Skip forward button -->
|
| 294 |
+
<button class="p-2 hover:bg-gray-100 rounded-full" title="Skip forward">
|
| 295 |
+
<SkipForward size={20} class="text-gray-600" />
|
| 296 |
+
</button>
|
| 297 |
+
|
| 298 |
+
<!-- Progress bar -->
|
| 299 |
+
<div class="flex-1 flex items-center gap-3">
|
| 300 |
+
<span class="text-xs text-gray-500 font-mono">{formatTime(currentTime)}</span>
|
| 301 |
+
<div class="flex-1 h-1 bg-gray-200 rounded-full">
|
| 302 |
+
<div
|
| 303 |
+
class="h-full bg-gradient-to-r from-amber-400 to-orange-500 rounded-full transition-all"
|
| 304 |
+
style="width: {(currentTime / duration) * 100}%"
|
| 305 |
+
></div>
|
| 306 |
+
</div>
|
| 307 |
+
<span class="text-xs text-gray-500 font-mono">{formatTime(duration)}</span>
|
| 308 |
+
</div>
|
| 309 |
+
|
| 310 |
+
<!-- Action buttons -->
|
| 311 |
+
<div class="flex items-center gap-2">
|
| 312 |
+
<button
|
| 313 |
+
on:click={shareAudio}
|
| 314 |
+
class="flex items-center gap-2 px-3 py-1.5 text-sm border border-gray-200 rounded-md hover:bg-gray-50"
|
| 315 |
+
>
|
| 316 |
+
<Share size={14} />
|
| 317 |
+
Share
|
| 318 |
+
</button>
|
| 319 |
+
<button
|
| 320 |
+
on:click={downloadAudio}
|
| 321 |
+
class="p-2 hover:bg-gray-100 rounded-md"
|
| 322 |
+
title="Download"
|
| 323 |
+
>
|
| 324 |
+
<Download size={16} class="text-gray-600" />
|
| 325 |
+
</button>
|
| 326 |
+
<button class="p-2 hover:bg-gray-100 rounded-md" title="More options">
|
| 327 |
+
<MoreHorizontal size={16} class="text-gray-600" />
|
| 328 |
+
</button>
|
| 329 |
+
</div>
|
| 330 |
+
</div>
|
| 331 |
+
|
| 332 |
+
<!-- Hidden audio element -->
|
| 333 |
+
{#if audioUrl}
|
| 334 |
+
<audio
|
| 335 |
+
bind:this={audioElement}
|
| 336 |
+
src={audioUrl}
|
| 337 |
+
on:loadedmetadata={handleAudioLoad}
|
| 338 |
+
on:timeupdate={handleTimeUpdate}
|
| 339 |
+
on:play={handlePlay}
|
| 340 |
+
on:pause={handlePause}
|
| 341 |
+
style="display: none;"
|
| 342 |
+
/>
|
| 343 |
+
{/if}
|
| 344 |
+
</div>
|
| 345 |
+
{/if}
|
| 346 |
+
</div>
|
| 347 |
+
|
| 348 |
+
<!-- Right panel -->
|
| 349 |
+
<div class="w-80 border-l border-gray-200 bg-white p-4 overflow-y-auto">
|
| 350 |
+
<!-- Model selector -->
|
| 351 |
+
<div class="mb-6 relative model-dropdown">
|
| 352 |
+
<h3 class="font-medium text-gray-900 mb-3">Model</h3>
|
| 353 |
+
<button
|
| 354 |
+
on:click={() => modelDropdownOpen = !modelDropdownOpen}
|
| 355 |
+
class="w-full p-3 border border-gray-200 rounded-lg bg-white text-sm focus:outline-none focus:ring-2 focus:ring-amber-400 focus:border-transparent appearance-none bg-no-repeat bg-right pr-10 shadow-sm text-left flex items-center justify-between"
|
| 356 |
+
>
|
| 357 |
+
<span>
|
| 358 |
+
{#each models as model}
|
| 359 |
+
{#if model.name === selectedModel}
|
| 360 |
+
{model.name}{#if model.badge} <span class="text-xs text-gray-500">({model.badge})</span>{/if}
|
| 361 |
+
{/if}
|
| 362 |
+
{/each}
|
| 363 |
+
</span>
|
| 364 |
+
<ChevronDown size={16} class="text-gray-500" />
|
| 365 |
+
</button>
|
| 366 |
+
|
| 367 |
+
{#if modelDropdownOpen}
|
| 368 |
+
<div class="absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-10">
|
| 369 |
+
{#each models as model}
|
| 370 |
+
<button
|
| 371 |
+
class="w-full px-3 py-2 text-left hover:bg-gray-50 transition-colors text-sm {model.name === selectedModel ? 'bg-gray-100' : ''}"
|
| 372 |
+
on:click={() => {
|
| 373 |
+
selectedModel = model.name;
|
| 374 |
+
modelDropdownOpen = false;
|
| 375 |
+
}}
|
| 376 |
+
>
|
| 377 |
+
{model.name}{#if model.badge} <span class="text-xs text-gray-500">({model.badge})</span>{/if}
|
| 378 |
+
</button>
|
| 379 |
+
{/each}
|
| 380 |
+
</div>
|
| 381 |
+
{/if}
|
| 382 |
+
</div>
|
| 383 |
+
|
| 384 |
+
<div class="mb-6">
|
| 385 |
+
<div class="mb-3">
|
| 386 |
+
<h3 class="font-medium text-gray-900">Voice</h3>
|
| 387 |
+
</div>
|
| 388 |
+
|
| 389 |
+
<div class="space-y-1">
|
| 390 |
+
{#each voices as voice}
|
| 391 |
+
<button
|
| 392 |
+
class="w-full flex items-center justify-between p-2 rounded-md hover:bg-gray-50 transition-colors text-left
|
| 393 |
+
{voice.name === selectedVoice ? 'bg-gray-100' : ''}"
|
| 394 |
+
on:click={() => selectedVoice = voice.name}
|
| 395 |
+
>
|
| 396 |
+
<div class="flex items-center gap-2">
|
| 397 |
+
<div class="w-8 h-8 bg-gradient-to-br from-amber-400 to-orange-500 rounded-full flex items-center justify-center text-white text-xs font-medium">
|
| 398 |
+
{voice.name[0]}
|
| 399 |
+
</div>
|
| 400 |
+
<span class="text-sm font-medium">{voice.name}</span>
|
| 401 |
+
</div>
|
| 402 |
+
<div class="text-xs text-gray-500">
|
| 403 |
+
{voice.description}
|
| 404 |
+
</div>
|
| 405 |
+
</button>
|
| 406 |
+
{/each}
|
| 407 |
+
|
| 408 |
+
<!-- Clone voice option -->
|
| 409 |
+
<button
|
| 410 |
+
class="w-full flex items-center justify-between p-2 rounded-md opacity-50 cursor-not-allowed text-left"
|
| 411 |
+
disabled
|
| 412 |
+
>
|
| 413 |
+
<div class="flex items-center gap-2">
|
| 414 |
+
<div class="w-8 h-8 bg-gray-400 rounded-full flex items-center justify-center text-white text-xs font-medium">
|
| 415 |
+
+
|
| 416 |
+
</div>
|
| 417 |
+
<span class="text-sm font-medium text-gray-600">Clone your voice</span>
|
| 418 |
+
</div>
|
| 419 |
+
<div class="text-xs text-gray-400">
|
| 420 |
+
(coming soon)
|
| 421 |
+
</div>
|
| 422 |
+
</button>
|
| 423 |
+
</div>
|
| 424 |
+
</div>
|
| 425 |
+
|
| 426 |
+
<div class="space-y-4 pt-4 border-t border-gray-200">
|
| 427 |
+
<!-- Speed control -->
|
| 428 |
+
<div>
|
| 429 |
+
<div class="flex justify-between mb-1">
|
| 430 |
+
<label class="text-sm font-medium text-gray-700">Speed</label>
|
| 431 |
+
<span class="text-sm text-gray-500">{speed.toFixed(1)}x</span>
|
| 432 |
+
</div>
|
| 433 |
+
<input
|
| 434 |
+
type="range"
|
| 435 |
+
bind:value={speed}
|
| 436 |
+
min="0.5"
|
| 437 |
+
max="2"
|
| 438 |
+
step="0.1"
|
| 439 |
+
class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"
|
| 440 |
+
/>
|
| 441 |
+
<div class="flex justify-between text-xs text-gray-400 mt-1">
|
| 442 |
+
<span>0.5x</span>
|
| 443 |
+
<span>2.0x</span>
|
| 444 |
+
</div>
|
| 445 |
+
</div>
|
| 446 |
+
|
| 447 |
+
<!-- Stability control -->
|
| 448 |
+
<div>
|
| 449 |
+
<div class="flex justify-between mb-1">
|
| 450 |
+
<label class="text-sm font-medium text-gray-700">Stability</label>
|
| 451 |
+
<span class="text-sm text-gray-500">{(stability * 100).toFixed(0)}%</span>
|
| 452 |
+
</div>
|
| 453 |
+
<input
|
| 454 |
+
type="range"
|
| 455 |
+
bind:value={stability}
|
| 456 |
+
min="0"
|
| 457 |
+
max="1"
|
| 458 |
+
step="0.01"
|
| 459 |
+
class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"
|
| 460 |
+
/>
|
| 461 |
+
</div>
|
| 462 |
+
|
| 463 |
+
<!-- Similarity control -->
|
| 464 |
+
<div>
|
| 465 |
+
<div class="flex justify-between mb-1">
|
| 466 |
+
<label class="text-sm font-medium text-gray-700">Similarity</label>
|
| 467 |
+
<span class="text-sm text-gray-500">{(similarity * 100).toFixed(0)}%</span>
|
| 468 |
+
</div>
|
| 469 |
+
<input
|
| 470 |
+
type="range"
|
| 471 |
+
bind:value={similarity}
|
| 472 |
+
min="0"
|
| 473 |
+
max="1"
|
| 474 |
+
step="0.01"
|
| 475 |
+
class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"
|
| 476 |
+
/>
|
| 477 |
+
</div>
|
| 478 |
+
|
| 479 |
+
<!-- Style control -->
|
| 480 |
+
<div>
|
| 481 |
+
<div class="flex justify-between mb-1">
|
| 482 |
+
<label class="text-sm font-medium text-gray-700">Style</label>
|
| 483 |
+
<span class="text-sm text-gray-500">
|
| 484 |
+
{styleExaggeration === 0 ? 'None' : 'Exaggerated'}
|
| 485 |
+
</span>
|
| 486 |
+
</div>
|
| 487 |
+
<input
|
| 488 |
+
type="range"
|
| 489 |
+
bind:value={styleExaggeration}
|
| 490 |
+
min="0"
|
| 491 |
+
max="1"
|
| 492 |
+
step="1"
|
| 493 |
+
class="w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"
|
| 494 |
+
/>
|
| 495 |
+
</div>
|
| 496 |
+
</div>
|
| 497 |
+
</div>
|
| 498 |
+
</div>
|
| 499 |
+
</div>
|
frontend/src/routes/api/auth/token/+server.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { json } from '@sveltejs/kit';
|
| 2 |
+
|
| 3 |
+
export async function POST({ request }) {
|
| 4 |
+
try {
|
| 5 |
+
const { code } = await request.json();
|
| 6 |
+
console.log('Received OAuth code:', code);
|
| 7 |
+
|
| 8 |
+
// Forward request to backend
|
| 9 |
+
console.log('Forwarding to backend at http://localhost:7860/api/auth/token');
|
| 10 |
+
const response = await fetch('http://localhost:7860/api/auth/token', {
|
| 11 |
+
method: 'POST',
|
| 12 |
+
headers: {
|
| 13 |
+
'Content-Type': 'application/json',
|
| 14 |
+
},
|
| 15 |
+
body: JSON.stringify({ code }),
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
+
console.log('Backend response status:', response.status);
|
| 19 |
+
|
| 20 |
+
if (!response.ok) {
|
| 21 |
+
const errorText = await response.text();
|
| 22 |
+
console.error('Backend error response:', errorText);
|
| 23 |
+
throw new Error(`Backend error (${response.status}): ${errorText}`);
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
const tokenData = await response.json();
|
| 27 |
+
console.log('Successfully received token data');
|
| 28 |
+
return json(tokenData);
|
| 29 |
+
|
| 30 |
+
} catch (error) {
|
| 31 |
+
console.error('OAuth token exchange error:', error);
|
| 32 |
+
|
| 33 |
+
// Check if it's a network error (backend not running)
|
| 34 |
+
if (error.message.includes('fetch') || error.code === 'ECONNREFUSED') {
|
| 35 |
+
return json(
|
| 36 |
+
{ error: 'Backend server is not running. Please start the backend with: cd backend && python -m hfstudio.cli --dev' },
|
| 37 |
+
{ status: 503 }
|
| 38 |
+
);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
return json(
|
| 42 |
+
{ error: `Failed to exchange authorization code: ${error.message}` },
|
| 43 |
+
{ status: 500 }
|
| 44 |
+
);
|
| 45 |
+
}
|
| 46 |
+
}
|
frontend/src/routes/auth/callback/+page.svelte
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script>
|
| 2 |
+
import { onMount } from 'svelte';
|
| 3 |
+
import { goto } from '$app/navigation';
|
| 4 |
+
import { page } from '$app/stores';
|
| 5 |
+
|
| 6 |
+
let status = 'Processing...';
|
| 7 |
+
let error = null;
|
| 8 |
+
|
| 9 |
+
onMount(async () => {
|
| 10 |
+
try {
|
| 11 |
+
const code = $page.url.searchParams.get('code');
|
| 12 |
+
const state = $page.url.searchParams.get('state');
|
| 13 |
+
const errorParam = $page.url.searchParams.get('error');
|
| 14 |
+
const errorDescription = $page.url.searchParams.get('error_description');
|
| 15 |
+
|
| 16 |
+
if (errorParam) {
|
| 17 |
+
error = errorDescription || errorParam;
|
| 18 |
+
status = 'Authentication failed';
|
| 19 |
+
return;
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
if (!code) {
|
| 23 |
+
error = 'No authorization code received';
|
| 24 |
+
status = 'Authentication failed';
|
| 25 |
+
return;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// Verify state parameter (optional but recommended)
|
| 29 |
+
const storedState = sessionStorage.getItem('oauth_state');
|
| 30 |
+
if (storedState && state !== storedState) {
|
| 31 |
+
error = 'Invalid state parameter';
|
| 32 |
+
status = 'Authentication failed';
|
| 33 |
+
return;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
// Exchange code for access token
|
| 37 |
+
const tokenResponse = await fetch('/api/auth/token', {
|
| 38 |
+
method: 'POST',
|
| 39 |
+
headers: {
|
| 40 |
+
'Content-Type': 'application/json',
|
| 41 |
+
},
|
| 42 |
+
body: JSON.stringify({ code }),
|
| 43 |
+
});
|
| 44 |
+
|
| 45 |
+
if (!tokenResponse.ok) {
|
| 46 |
+
const errorText = await tokenResponse.text();
|
| 47 |
+
console.error('Token exchange failed:', errorText);
|
| 48 |
+
throw new Error(`Failed to exchange code for token: ${errorText}`);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
const tokenData = await tokenResponse.json();
|
| 52 |
+
|
| 53 |
+
// Store access token (in a real app, you'd want secure storage)
|
| 54 |
+
localStorage.setItem('hf_access_token', tokenData.access_token);
|
| 55 |
+
|
| 56 |
+
status = 'Successfully authenticated!';
|
| 57 |
+
|
| 58 |
+
// Redirect back to main app after a short delay
|
| 59 |
+
setTimeout(() => {
|
| 60 |
+
goto('/');
|
| 61 |
+
}, 2000);
|
| 62 |
+
|
| 63 |
+
} catch (err) {
|
| 64 |
+
error = err.message;
|
| 65 |
+
status = 'Authentication failed';
|
| 66 |
+
}
|
| 67 |
+
});
|
| 68 |
+
</script>
|
| 69 |
+
|
| 70 |
+
<svelte:head>
|
| 71 |
+
<title>Authenticating with Hugging Face - HFStudio</title>
|
| 72 |
+
</svelte:head>
|
| 73 |
+
|
| 74 |
+
<div class="min-h-screen flex items-center justify-center bg-gray-50">
|
| 75 |
+
<div class="max-w-md w-full bg-white rounded-lg shadow-md p-6">
|
| 76 |
+
<div class="text-center">
|
| 77 |
+
<img src="/assets/hf-logo.png" alt="HF Logo" class="w-12 h-12 mx-auto mb-4" />
|
| 78 |
+
<h1 class="text-xl font-semibold mb-2">HFStudio Authentication</h1>
|
| 79 |
+
|
| 80 |
+
{#if error}
|
| 81 |
+
<div class="text-red-600 mb-4">
|
| 82 |
+
<p class="font-medium">{status}</p>
|
| 83 |
+
<p class="text-sm mt-1">{error}</p>
|
| 84 |
+
</div>
|
| 85 |
+
<button
|
| 86 |
+
on:click={() => goto('/')}
|
| 87 |
+
class="px-4 py-2 bg-gradient-to-r from-amber-400 to-orange-500 text-white rounded-lg hover:from-amber-500 hover:to-orange-600 transition-colors"
|
| 88 |
+
>
|
| 89 |
+
Return to HFStudio
|
| 90 |
+
</button>
|
| 91 |
+
{:else}
|
| 92 |
+
<div class="text-gray-600 mb-4">
|
| 93 |
+
<div class="animate-spin w-8 h-8 border-2 border-amber-400 border-t-transparent rounded-full mx-auto mb-2"></div>
|
| 94 |
+
<p>{status}</p>
|
| 95 |
+
</div>
|
| 96 |
+
{/if}
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
frontend/static/assets/hf-logo.png
ADDED
|
Git LFS Details
|
frontend/static/assets/hf-studio-logo.png
ADDED
|
Git LFS Details
|
frontend/static/samples/harvard.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:971b4163670445c415c6b0fb6813c38093409ecac2f6b4d429ae3574d24ad470
|
| 3 |
+
size 3249924
|
frontend/svelte.config.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import adapter from '@sveltejs/adapter-static';
|
| 2 |
+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
| 3 |
+
|
| 4 |
+
/** @type {import('@sveltejs/kit').Config} */
|
| 5 |
+
const config = {
|
| 6 |
+
preprocess: vitePreprocess(),
|
| 7 |
+
kit: {
|
| 8 |
+
adapter: adapter({
|
| 9 |
+
pages: 'build',
|
| 10 |
+
assets: 'build',
|
| 11 |
+
fallback: 'index.html',
|
| 12 |
+
precompress: false,
|
| 13 |
+
strict: true
|
| 14 |
+
})
|
| 15 |
+
}
|
| 16 |
+
};
|
| 17 |
+
|
| 18 |
+
export default config;
|
frontend/tailwind.config.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/** @type {import('tailwindcss').Config} */
|
| 2 |
+
export default {
|
| 3 |
+
content: ['./src/**/*.{html,js,svelte,ts}'],
|
| 4 |
+
theme: {
|
| 5 |
+
extend: {
|
| 6 |
+
colors: {
|
| 7 |
+
border: "hsl(var(--border))",
|
| 8 |
+
input: "hsl(var(--input))",
|
| 9 |
+
ring: "hsl(var(--ring))",
|
| 10 |
+
background: "hsl(var(--background))",
|
| 11 |
+
foreground: "hsl(var(--foreground))",
|
| 12 |
+
primary: {
|
| 13 |
+
DEFAULT: "hsl(var(--primary))",
|
| 14 |
+
foreground: "hsl(var(--primary-foreground))",
|
| 15 |
+
},
|
| 16 |
+
secondary: {
|
| 17 |
+
DEFAULT: "hsl(var(--secondary))",
|
| 18 |
+
foreground: "hsl(var(--secondary-foreground))",
|
| 19 |
+
},
|
| 20 |
+
destructive: {
|
| 21 |
+
DEFAULT: "hsl(var(--destructive))",
|
| 22 |
+
foreground: "hsl(var(--destructive-foreground))",
|
| 23 |
+
},
|
| 24 |
+
muted: {
|
| 25 |
+
DEFAULT: "hsl(var(--muted))",
|
| 26 |
+
foreground: "hsl(var(--muted-foreground))",
|
| 27 |
+
},
|
| 28 |
+
accent: {
|
| 29 |
+
DEFAULT: "hsl(var(--accent))",
|
| 30 |
+
foreground: "hsl(var(--accent-foreground))",
|
| 31 |
+
},
|
| 32 |
+
popover: {
|
| 33 |
+
DEFAULT: "hsl(var(--popover))",
|
| 34 |
+
foreground: "hsl(var(--popover-foreground))",
|
| 35 |
+
},
|
| 36 |
+
card: {
|
| 37 |
+
DEFAULT: "hsl(var(--card))",
|
| 38 |
+
foreground: "hsl(var(--card-foreground))",
|
| 39 |
+
},
|
| 40 |
+
},
|
| 41 |
+
},
|
| 42 |
+
},
|
| 43 |
+
plugins: [],
|
| 44 |
+
}
|
frontend/vite.config.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { sveltekit } from '@sveltejs/kit/vite';
|
| 2 |
+
import { defineConfig } from 'vite';
|
| 3 |
+
|
| 4 |
+
export default defineConfig({
|
| 5 |
+
plugins: [sveltekit()],
|
| 6 |
+
server: {
|
| 7 |
+
port: 11111,
|
| 8 |
+
host: true
|
| 9 |
+
}
|
| 10 |
+
});
|
hfstudio/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HFStudio - Local and API-based Text-to-Speech Studio"""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
def _get_version():
|
| 8 |
+
"""Read version from frontend/package.json"""
|
| 9 |
+
# Get the project root (one level up from this package)
|
| 10 |
+
package_root = Path(__file__).parent.parent
|
| 11 |
+
package_json_path = package_root / "frontend" / "package.json"
|
| 12 |
+
|
| 13 |
+
with open(package_json_path, 'r') as f:
|
| 14 |
+
package_data = json.load(f)
|
| 15 |
+
return package_data['version']
|
| 16 |
+
|
| 17 |
+
__version__ = _get_version()
|
hfstudio/__main__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from hfstudio.cli import main
|
| 2 |
+
|
| 3 |
+
if __name__ == "__main__":
|
| 4 |
+
main()
|
hfstudio/cli.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import typer
|
| 2 |
+
import uvicorn
|
| 3 |
+
from rich.console import Console
|
| 4 |
+
from rich.panel import Panel
|
| 5 |
+
|
| 6 |
+
console = Console()
|
| 7 |
+
|
| 8 |
+
def main(
|
| 9 |
+
port: int = typer.Option(7860, "--port", "-p", help="Port to run the server on"),
|
| 10 |
+
host: str = typer.Option("0.0.0.0", "--host", help="Host to run the server on"),
|
| 11 |
+
dev: bool = typer.Option(False, "--dev", help="Run in development mode"),
|
| 12 |
+
):
|
| 13 |
+
"""Start the HFStudio server"""
|
| 14 |
+
|
| 15 |
+
console.print(Panel.fit(
|
| 16 |
+
"[bold green]HFStudio Server[/bold green]\n"
|
| 17 |
+
f"Running on http://{host if host != '0.0.0.0' else 'localhost'}:{port}\n"
|
| 18 |
+
f"API docs: http://localhost:{port}/docs",
|
| 19 |
+
title="🎙️ HFStudio",
|
| 20 |
+
border_style="green"
|
| 21 |
+
))
|
| 22 |
+
|
| 23 |
+
uvicorn.run(
|
| 24 |
+
"hfstudio.server:app",
|
| 25 |
+
host=host,
|
| 26 |
+
port=port,
|
| 27 |
+
reload=dev,
|
| 28 |
+
log_level="info" if not dev else "debug"
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
if __name__ == "__main__":
|
| 32 |
+
typer.run(main)
|
hfstudio/server.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException, Request
|
| 2 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from fastapi.responses import FileResponse, HTMLResponse
|
| 4 |
+
from fastapi.staticfiles import StaticFiles
|
| 5 |
+
from pydantic import BaseModel
|
| 6 |
+
from typing import Optional, Dict, Any
|
| 7 |
+
import base64
|
| 8 |
+
import io
|
| 9 |
+
import numpy as np
|
| 10 |
+
import soundfile as sf
|
| 11 |
+
import httpx
|
| 12 |
+
import os
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
|
| 15 |
+
app = FastAPI(title="HFStudio API", version="0.1.0")
|
| 16 |
+
|
| 17 |
+
# Get the static files directory
|
| 18 |
+
static_dir = Path(__file__).parent / "static"
|
| 19 |
+
|
| 20 |
+
# Mount static files
|
| 21 |
+
if static_dir.exists():
|
| 22 |
+
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
| 23 |
+
app.mount("/_app", StaticFiles(directory=str(static_dir / "_app")), name="app")
|
| 24 |
+
app.mount("/assets", StaticFiles(directory=str(static_dir / "assets")), name="assets")
|
| 25 |
+
app.mount("/samples", StaticFiles(directory=str(static_dir / "samples")), name="samples")
|
| 26 |
+
|
| 27 |
+
# Configure CORS
|
| 28 |
+
app.add_middleware(
|
| 29 |
+
CORSMiddleware,
|
| 30 |
+
allow_origins=["http://localhost:7860", "http://localhost:11111", "http://localhost:3000"],
|
| 31 |
+
allow_credentials=True,
|
| 32 |
+
allow_methods=["*"],
|
| 33 |
+
allow_headers=["*"],
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
# Models
|
| 37 |
+
class TTSRequest(BaseModel):
|
| 38 |
+
text: str
|
| 39 |
+
voice_id: str = "default"
|
| 40 |
+
model_id: str = "coqui-tts"
|
| 41 |
+
parameters: Dict[str, Any] = {}
|
| 42 |
+
mode: str = "api" # "api" or "local"
|
| 43 |
+
|
| 44 |
+
class TTSResponse(BaseModel):
|
| 45 |
+
audio_url: str
|
| 46 |
+
duration: float
|
| 47 |
+
format: str = "wav"
|
| 48 |
+
|
| 49 |
+
class Voice(BaseModel):
|
| 50 |
+
id: str
|
| 51 |
+
name: str
|
| 52 |
+
preview_url: Optional[str] = None
|
| 53 |
+
supported_models: list[str] = []
|
| 54 |
+
|
| 55 |
+
class Model(BaseModel):
|
| 56 |
+
id: str
|
| 57 |
+
name: str
|
| 58 |
+
type: str # "local" or "api"
|
| 59 |
+
status: str # "available", "downloadable", "api-only"
|
| 60 |
+
|
| 61 |
+
class OAuthTokenRequest(BaseModel):
|
| 62 |
+
code: str
|
| 63 |
+
|
| 64 |
+
class OAuthTokenResponse(BaseModel):
|
| 65 |
+
access_token: str
|
| 66 |
+
token_type: str
|
| 67 |
+
scope: str
|
| 68 |
+
|
| 69 |
+
# Routes
|
| 70 |
+
@app.get("/")
|
| 71 |
+
async def root():
|
| 72 |
+
"""Serve the SvelteKit single-page application"""
|
| 73 |
+
index_path = static_dir / "index.html"
|
| 74 |
+
if index_path.exists():
|
| 75 |
+
return FileResponse(str(index_path), media_type="text/html")
|
| 76 |
+
else:
|
| 77 |
+
return {"message": "HFStudio API is running"}
|
| 78 |
+
|
| 79 |
+
@app.get("/api/status")
|
| 80 |
+
async def get_status():
|
| 81 |
+
return {
|
| 82 |
+
"mode": "api",
|
| 83 |
+
"local_available": False,
|
| 84 |
+
"api_configured": True
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
@app.get("/api/voices")
|
| 88 |
+
async def get_voices():
|
| 89 |
+
voices = [
|
| 90 |
+
Voice(id="liam", name="Liam", supported_models=["coqui-tts", "bark"]),
|
| 91 |
+
Voice(id="sarah", name="Sarah", supported_models=["coqui-tts", "bark"]),
|
| 92 |
+
Voice(id="alex", name="Alex", supported_models=["coqui-tts", "bark"]),
|
| 93 |
+
Voice(id="emma", name="Emma", supported_models=["coqui-tts", "bark"]),
|
| 94 |
+
]
|
| 95 |
+
return {"voices": voices}
|
| 96 |
+
|
| 97 |
+
@app.get("/api/models")
|
| 98 |
+
async def get_models():
|
| 99 |
+
models = [
|
| 100 |
+
Model(id="eleven-multilingual-v2", name="Eleven Multilingual v2", type="api", status="api-only"),
|
| 101 |
+
Model(id="coqui-tts", name="Coqui TTS", type="local", status="available"),
|
| 102 |
+
Model(id="bark", name="Bark", type="local", status="downloadable"),
|
| 103 |
+
]
|
| 104 |
+
return {"models": models}
|
| 105 |
+
|
| 106 |
+
@app.post("/api/tts/generate")
|
| 107 |
+
async def generate_tts(request: TTSRequest):
|
| 108 |
+
try:
|
| 109 |
+
# For now, generate a simple sine wave as placeholder audio
|
| 110 |
+
duration = min(len(request.text) * 0.05, 10) # Rough estimate
|
| 111 |
+
sample_rate = 22050
|
| 112 |
+
t = np.linspace(0, duration, int(sample_rate * duration))
|
| 113 |
+
|
| 114 |
+
# Generate a simple tone
|
| 115 |
+
frequency = 440 # A4 note
|
| 116 |
+
audio = np.sin(2 * np.pi * frequency * t) * 0.3
|
| 117 |
+
|
| 118 |
+
# Add some variation
|
| 119 |
+
audio += np.sin(2 * np.pi * frequency * 1.5 * t) * 0.1
|
| 120 |
+
audio += np.sin(2 * np.pi * frequency * 2 * t) * 0.05
|
| 121 |
+
|
| 122 |
+
# Convert to int16
|
| 123 |
+
audio = (audio * 32767).astype(np.int16)
|
| 124 |
+
|
| 125 |
+
# Create WAV in memory
|
| 126 |
+
buffer = io.BytesIO()
|
| 127 |
+
sf.write(buffer, audio, sample_rate, format='WAV')
|
| 128 |
+
buffer.seek(0)
|
| 129 |
+
|
| 130 |
+
# Convert to base64
|
| 131 |
+
audio_base64 = base64.b64encode(buffer.read()).decode('utf-8')
|
| 132 |
+
audio_url = f"data:audio/wav;base64,{audio_base64}"
|
| 133 |
+
|
| 134 |
+
return TTSResponse(
|
| 135 |
+
audio_url=audio_url,
|
| 136 |
+
duration=duration,
|
| 137 |
+
format="wav"
|
| 138 |
+
)
|
| 139 |
+
except Exception as e:
|
| 140 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 141 |
+
|
| 142 |
+
@app.post("/api/auth/token")
|
| 143 |
+
async def exchange_oauth_token(request: OAuthTokenRequest):
|
| 144 |
+
"""Exchange OAuth authorization code for access token"""
|
| 145 |
+
try:
|
| 146 |
+
# HuggingFace OAuth token endpoint
|
| 147 |
+
token_url = "https://huggingface.co/oauth/token"
|
| 148 |
+
|
| 149 |
+
# OAuth app credentials
|
| 150 |
+
client_id = "cdf32a17-e40f-4a84-b683-f66aa1105793"
|
| 151 |
+
client_secret = "f590cb2d-6eac-4cef-a0cb-d0116825295c"
|
| 152 |
+
redirect_uri = "http://localhost:11111/auth/callback"
|
| 153 |
+
|
| 154 |
+
# Prepare token exchange request
|
| 155 |
+
token_data = {
|
| 156 |
+
"client_id": client_id,
|
| 157 |
+
"client_secret": client_secret,
|
| 158 |
+
"code": request.code,
|
| 159 |
+
"grant_type": "authorization_code",
|
| 160 |
+
"redirect_uri": redirect_uri,
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
# Exchange code for token
|
| 164 |
+
async with httpx.AsyncClient() as client:
|
| 165 |
+
response = await client.post(
|
| 166 |
+
token_url,
|
| 167 |
+
data=token_data,
|
| 168 |
+
headers={"Accept": "application/json"}
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
if response.status_code != 200:
|
| 172 |
+
raise HTTPException(
|
| 173 |
+
status_code=400,
|
| 174 |
+
detail=f"Token exchange failed: {response.text}"
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
token_response = response.json()
|
| 178 |
+
|
| 179 |
+
return OAuthTokenResponse(
|
| 180 |
+
access_token=token_response["access_token"],
|
| 181 |
+
token_type=token_response.get("token_type", "Bearer"),
|
| 182 |
+
scope=token_response.get("scope", "")
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
except httpx.RequestError as e:
|
| 186 |
+
raise HTTPException(status_code=500, detail=f"Network error: {str(e)}")
|
| 187 |
+
except Exception as e:
|
| 188 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 189 |
+
|
| 190 |
+
# Catch-all route to serve the SvelteKit app (excluding API routes)
|
| 191 |
+
@app.get("/{path:path}")
|
| 192 |
+
async def serve_spa(path: str):
|
| 193 |
+
"""Serve the SvelteKit single-page application for non-API routes"""
|
| 194 |
+
# Skip API routes
|
| 195 |
+
if path.startswith("api/") or path.startswith("docs") or path.startswith("openapi.json"):
|
| 196 |
+
raise HTTPException(status_code=404, detail="Not found")
|
| 197 |
+
|
| 198 |
+
# For any non-API route, serve the index.html
|
| 199 |
+
index_path = static_dir / "index.html"
|
| 200 |
+
if index_path.exists():
|
| 201 |
+
return FileResponse(str(index_path), media_type="text/html")
|
| 202 |
+
else:
|
| 203 |
+
# Fallback if no built frontend
|
| 204 |
+
return HTMLResponse("""
|
| 205 |
+
<html>
|
| 206 |
+
<head><title>HFStudio</title></head>
|
| 207 |
+
<body>
|
| 208 |
+
<h1>HFStudio Backend</h1>
|
| 209 |
+
<p>The backend is running, but the frontend hasn't been built yet.</p>
|
| 210 |
+
<p>Visit <a href="/docs">/docs</a> for the API documentation.</p>
|
| 211 |
+
</body>
|
| 212 |
+
</html>
|
| 213 |
+
""")
|
| 214 |
+
|
| 215 |
+
if __name__ == "__main__":
|
| 216 |
+
import uvicorn
|
| 217 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
hfstudio/static/_app/env.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export const env={}
|
hfstudio/static/_app/immutable/assets/0.DkRYx-5s.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 100%;--foreground: 240 10% 3.9%;--card: 0 0% 100%;--card-foreground: 240 10% 3.9%;--popover: 0 0% 100%;--popover-foreground: 240 10% 3.9%;--primary: 240 5.9% 10%;--primary-foreground: 0 0% 98%;--secondary: 240 4.8% 95.9%;--secondary-foreground: 240 5.9% 10%;--muted: 240 4.8% 95.9%;--muted-foreground: 240 3.8% 46.1%;--accent: 240 4.8% 95.9%;--accent-foreground: 240 5.9% 10%;--destructive: 0 84.2% 60.2%;--destructive-foreground: 0 0% 98%;--border: 240 5.9% 90%;--input: 240 5.9% 90%;--ring: 240 5.9% 10%;--radius: .5rem}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.left-0{left:0}.left-2{left:.5rem}.left-56{left:14rem}.right-0{right:0}.right-2{right:.5rem}.right-6{right:1.5rem}.right-80{right:20rem}.top-4{top:1rem}.top-full{top:100%}.z-10{z-index:10}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-0\.5{margin-left:.125rem}.ml-1{margin-left:.25rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.flex{display:flex}.contents{display:contents}.hidden{display:none}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-12{height:3rem}.h-2{height:.5rem}.h-5{height:1.25rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-2{width:.5rem}.w-5{width:1.25rem}.w-52{width:13rem}.w-56{width:14rem}.w-8{width:2rem}.w-80{width:20rem}.w-full{width:100%}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.scale-150{--tw-scale-x: 1.5;--tw-scale-y: 1.5;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-amber-400{--tw-border-opacity: 1;border-color:rgb(251 191 36 / var(--tw-border-opacity, 1))}.border-blue-200{--tw-border-opacity: 1;border-color:rgb(191 219 254 / var(--tw-border-opacity, 1))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity, 1))}.border-t-transparent{border-top-color:transparent}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-blue-100{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-50{--tw-bg-opacity: 1;background-color:rgb(239 246 255 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-400{--tw-bg-opacity: 1;background-color:rgb(156 163 175 / var(--tw-bg-opacity, 1))}.bg-gray-50{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-amber-400{--tw-gradient-from: #fbbf24 var(--tw-gradient-from-position);--tw-gradient-to: rgb(251 191 36 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-orange-500{--tw-gradient-to: #f97316 var(--tw-gradient-to-position)}.bg-right{background-position:right}.bg-no-repeat{background-repeat:no-repeat}.p-0\.5{padding:.125rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-24{padding-bottom:6rem}.pr-10{padding-right:2.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-relaxed{line-height:1.625}.text-blue-600{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-blue-700{--tw-text-opacity: 1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-blue-900{--tw-text-opacity: 1;color:rgb(30 58 138 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-red-600{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}body{background-color:hsl(var(--background));color:hsl(var(--foreground))}.slider-hf::-webkit-slider-thumb{height:1rem;width:1rem;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:9999px;background:linear-gradient(45deg,#ffd21e,#ff9d00);box-shadow:0 1px 3px #0000001a}.slider-hf::-moz-range-thumb{height:1rem;width:1rem;cursor:pointer;border-radius:9999px;border-width:0px;background:linear-gradient(45deg,#ffd21e,#ff9d00);box-shadow:0 1px 3px #0000001a}.pause-filled{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px}.pause-filled:before,.pause-filled:after{content:"";width:2px;height:10px;background-color:currentColor;border-radius:1px}.pause-filled:before{margin-right:2px}.pause-filled:after{margin-left:2px}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-50:hover{--tw-bg-opacity: 1;background-color:rgb(249 250 251 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:from-amber-500:hover{--tw-gradient-from: #f59e0b var(--tw-gradient-from-position);--tw-gradient-to: rgb(245 158 11 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.hover\:to-orange-600:hover{--tw-gradient-to: #ea580c var(--tw-gradient-to-position)}.hover\:text-gray-600:hover{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-amber-400:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(251 191 36 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}
|
hfstudio/static/_app/immutable/chunks/9DiovRey.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
var I=Object.defineProperty;var R=(t,n,e)=>n in t?I(t,n,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[n]=e;var N=(t,n,e)=>R(t,typeof n!="symbol"?n+"":n,e);function w(){}function W(t,n){for(const e in n)t[e]=n[e];return t}function O(t){return t()}function T(){return Object.create(null)}function $(t){t.forEach(O)}function P(t){return typeof t=="function"}function ht(t,n){return t!=t?n==n:t!==n||t&&typeof t=="object"||typeof t=="function"}let b;function mt(t,n){return t===n?!0:(b||(b=document.createElement("a")),b.href=n,t===b.href)}function G(t){return Object.keys(t).length===0}function J(t,...n){if(t==null){for(const r of n)r(void 0);return w}const e=t.subscribe(...n);return e.unsubscribe?()=>e.unsubscribe():e}function pt(t,n,e){t.$$.on_destroy.push(J(n,e))}function yt(t,n,e,r){if(t){const i=q(t,n,e,r);return t[0](i)}}function q(t,n,e,r){return t[1]&&r?W(e.ctx.slice(),t[1](r(n))):e.ctx}function gt(t,n,e,r){if(t[2]&&r){const i=t[2](r(e));if(n.dirty===void 0)return i;if(typeof i=="object"){const o=[],c=Math.max(n.dirty.length,i.length);for(let l=0;l<c;l+=1)o[l]=n.dirty[l]|i[l];return o}return n.dirty|i}return n.dirty}function $t(t,n,e,r,i,o){if(i){const c=q(n,e,r,o);t.p(c,i)}}function xt(t){if(t.ctx.length>32){const n=[],e=t.ctx.length/32;for(let r=0;r<e;r++)n[r]=-1;return n}return-1}function bt(t){const n={};for(const e in t)e[0]!=="$"&&(n[e]=t[e]);return n}function vt(t,n){const e={};n=new Set(n);for(const r in t)!n.has(r)&&r[0]!=="$"&&(e[r]=t[r]);return e}let E=!1;function K(){E=!0}function Q(){E=!1}function V(t,n,e,r){for(;t<n;){const i=t+(n-t>>1);e(i)<=r?t=i+1:n=i}return t}function X(t){if(t.hydrate_init)return;t.hydrate_init=!0;let n=t.childNodes;if(t.nodeName==="HEAD"){const u=[];for(let s=0;s<n.length;s++){const f=n[s];f.claim_order!==void 0&&u.push(f)}n=u}const e=new Int32Array(n.length+1),r=new Int32Array(n.length);e[0]=-1;let i=0;for(let u=0;u<n.length;u++){const s=n[u].claim_order,f=(i>0&&n[e[i]].claim_order<=s?i+1:V(1,i,x=>n[e[x]].claim_order,s))-1;r[u]=e[f]+1;const a=f+1;e[a]=u,i=Math.max(a,i)}const o=[],c=[];let l=n.length-1;for(let u=e[i]+1;u!=0;u=r[u-1]){for(o.push(n[u-1]);l>=u;l--)c.push(n[l]);l--}for(;l>=0;l--)c.push(n[l]);o.reverse(),c.sort((u,s)=>u.claim_order-s.claim_order);for(let u=0,s=0;u<c.length;u++){for(;s<o.length&&c[u].claim_order>=o[s].claim_order;)s++;const f=s<o.length?o[s]:null;t.insertBefore(c[u],f)}}function Y(t,n){if(E){for(X(t),(t.actual_end_child===void 0||t.actual_end_child!==null&&t.actual_end_child.parentNode!==t)&&(t.actual_end_child=t.firstChild);t.actual_end_child!==null&&t.actual_end_child.claim_order===void 0;)t.actual_end_child=t.actual_end_child.nextSibling;n!==t.actual_end_child?(n.claim_order!==void 0||n.parentNode!==t)&&t.insertBefore(n,t.actual_end_child):t.actual_end_child=n.nextSibling}else(n.parentNode!==t||n.nextSibling!==null)&&t.appendChild(n)}function wt(t,n,e){E&&!e?Y(t,n):(n.parentNode!==t||n.nextSibling!=e)&&t.insertBefore(n,e||null)}function Z(t){t.parentNode&&t.parentNode.removeChild(t)}function Et(t,n){for(let e=0;e<t.length;e+=1)t[e]&&t[e].d(n)}function tt(t){return document.createElement(t)}function nt(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function j(t){return document.createTextNode(t)}function Nt(){return j(" ")}function At(){return j("")}function Ct(t,n,e,r){return t.addEventListener(n,e,r),()=>t.removeEventListener(n,e,r)}function et(t,n,e){e==null?t.removeAttribute(n):t.getAttribute(n)!==e&&t.setAttribute(n,e)}function St(t,n){for(const e in n)et(t,e,n[e])}function jt(t){return t.dataset.svelteH}function kt(t){return t===""?null:+t}function rt(t){return Array.from(t.childNodes)}function it(t){t.claim_info===void 0&&(t.claim_info={last_index:0,total_claimed:0})}function B(t,n,e,r,i=!1){it(t);const o=(()=>{for(let c=t.claim_info.last_index;c<t.length;c++){const l=t[c];if(n(l)){const u=e(l);return u===void 0?t.splice(c,1):t[c]=u,i||(t.claim_info.last_index=c),l}}for(let c=t.claim_info.last_index-1;c>=0;c--){const l=t[c];if(n(l)){const u=e(l);return u===void 0?t.splice(c,1):t[c]=u,i?u===void 0&&t.claim_info.last_index--:t.claim_info.last_index=c,l}}return r()})();return o.claim_order=t.claim_info.total_claimed,t.claim_info.total_claimed+=1,o}function L(t,n,e,r){return B(t,i=>i.nodeName===n,i=>{const o=[];for(let c=0;c<i.attributes.length;c++){const l=i.attributes[c];e[l.name]||o.push(l.name)}o.forEach(c=>i.removeAttribute(c))},()=>r(n))}function Dt(t,n,e){return L(t,n,e,tt)}function Tt(t,n,e){return L(t,n,e,nt)}function ct(t,n){return B(t,e=>e.nodeType===3,e=>{const r=""+n;if(e.data.startsWith(r)){if(e.data.length!==r.length)return e.splitText(r.length)}else e.data=r},()=>j(n),!0)}function Ht(t){return ct(t," ")}function Mt(t,n){n=""+n,t.data!==n&&(t.data=n)}function Ot(t,n){t.value=n??""}function Pt(t,n,e,r){e==null?t.style.removeProperty(n):t.style.setProperty(n,e,"")}function ut(t,n,{bubbles:e=!1,cancelable:r=!1}={}){return new CustomEvent(t,{detail:n,bubbles:e,cancelable:r})}function qt(t,n){const e=[];let r=0;for(const i of n.childNodes)if(i.nodeType===8){const o=i.textContent.trim();o===`HEAD_${t}_END`?(r-=1,e.push(i)):o===`HEAD_${t}_START`&&(r+=1,e.push(i))}else r>0&&e.push(i);return e}function Bt(t,n){return new t(n)}let g;function y(t){g=t}function _(){if(!g)throw new Error("Function called outside component initialization");return g}function Lt(t){_().$$.before_update.push(t)}function Ut(t){_().$$.on_mount.push(t)}function zt(t){_().$$.after_update.push(t)}function Ft(t){_().$$.on_destroy.push(t)}function It(){const t=_();return(n,e,{cancelable:r=!1}={})=>{const i=t.$$.callbacks[n];if(i){const o=ut(n,e,{cancelable:r});return i.slice().forEach(c=>{c.call(t,o)}),!o.defaultPrevented}return!0}}function Rt(t,n){return _().$$.context.set(t,n),n}function Wt(t){return _().$$.context.get(t)}function Gt(){return _().$$.context}function Jt(t){return _().$$.context.has(t)}const m=[],H=[];let p=[];const M=[],U=Promise.resolve();let C=!1;function z(){C||(C=!0,U.then(F))}function Kt(){return z(),U}function S(t){p.push(t)}const A=new Set;let h=0;function F(){if(h!==0)return;const t=g;do{try{for(;h<m.length;){const n=m[h];h++,y(n),st(n.$$)}}catch(n){throw m.length=0,h=0,n}for(y(null),m.length=0,h=0;H.length;)H.pop()();for(let n=0;n<p.length;n+=1){const e=p[n];A.has(e)||(A.add(e),e())}p.length=0}while(m.length);for(;M.length;)M.pop()();C=!1,A.clear(),y(t)}function st(t){if(t.fragment!==null){t.update(),$(t.before_update);const n=t.dirty;t.dirty=[-1],t.fragment&&t.fragment.p(t.ctx,n),t.after_update.forEach(S)}}function ot(t){const n=[],e=[];p.forEach(r=>t.indexOf(r)===-1?n.push(r):e.push(r)),e.forEach(r=>r()),p=n}const v=new Set;let d;function Qt(){d={r:0,c:[],p:d}}function Vt(){d.r||$(d.c),d=d.p}function lt(t,n){t&&t.i&&(v.delete(t),t.i(n))}function Xt(t,n,e,r){if(t&&t.o){if(v.has(t))return;v.add(t),d.c.push(()=>{v.delete(t),r&&(e&&t.d(1),r())}),t.o(n)}else r&&r()}function Yt(t){t&&t.c()}function Zt(t,n){t&&t.l(n)}function at(t,n,e){const{fragment:r,after_update:i}=t.$$;r&&r.m(n,e),S(()=>{const o=t.$$.on_mount.map(O).filter(P);t.$$.on_destroy?t.$$.on_destroy.push(...o):$(o),t.$$.on_mount=[]}),i.forEach(S)}function ft(t,n){const e=t.$$;e.fragment!==null&&(ot(e.after_update),$(e.on_destroy),e.fragment&&e.fragment.d(n),e.on_destroy=e.fragment=null,e.ctx=[])}function _t(t,n){t.$$.dirty[0]===-1&&(m.push(t),z(),t.$$.dirty.fill(0)),t.$$.dirty[n/31|0]|=1<<n%31}function tn(t,n,e,r,i,o,c=null,l=[-1]){const u=g;y(t);const s=t.$$={fragment:null,ctx:[],props:o,update:w,not_equal:i,bound:T(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(n.context||(u?u.$$.context:[])),callbacks:T(),dirty:l,skip_bound:!1,root:n.target||u.$$.root};c&&c(s.root);let f=!1;if(s.ctx=e?e(t,n.props||{},(a,x,...k)=>{const D=k.length?k[0]:x;return s.ctx&&i(s.ctx[a],s.ctx[a]=D)&&(!s.skip_bound&&s.bound[a]&&s.bound[a](D),f&&_t(t,a)),x}):[],s.update(),f=!0,$(s.before_update),s.fragment=r?r(s.ctx):!1,n.target){if(n.hydrate){K();const a=rt(n.target);s.fragment&&s.fragment.l(a),a.forEach(Z)}else s.fragment&&s.fragment.c();n.intro&<(t.$$.fragment),at(t,n.target,n.anchor),Q(),F()}y(u)}class nn{constructor(){N(this,"$$");N(this,"$$set")}$destroy(){ft(this,1),this.$destroy=w}$on(n,e){if(!P(e))return w;const r=this.$$.callbacks[n]||(this.$$.callbacks[n]=[]);return r.push(e),()=>{const i=r.indexOf(e);i!==-1&&r.splice(i,1)}}$set(n){this.$$set&&!G(n)&&(this.$$.skip_bound=!0,this.$$set(n),this.$$.skip_bound=!1)}}export{Wt as $,xt as A,gt as B,Qt as C,Vt as D,At as E,zt as F,Kt as G,Bt as H,ft as I,Yt as J,at as K,Zt as L,Pt as M,H as N,W as O,Et as P,St as Q,Tt as R,nn as S,nt as T,vt as U,bt as V,Ot as W,kt as X,Lt as Y,It as Z,Gt as _,Mt as a,Jt as a0,Ft as a1,Rt as a2,wt as b,Y as c,Z as d,Dt as e,rt as f,ct as g,Ht as h,tn as i,tt as j,Nt as k,pt as l,mt as m,w as n,et as o,qt as p,jt as q,Ut as r,ht as s,j as t,Ct as u,yt as v,$ as w,Xt as x,lt as y,$t as z};
|
hfstudio/static/_app/immutable/chunks/BHHrQo_1.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{s as e}from"./Dh5qNFGZ.js";const r=()=>{const s=e;return{page:{subscribe:s.page.subscribe},navigating:{subscribe:s.navigating.subscribe},updated:s.updated}},b={subscribe(s){return r().page.subscribe(s)}};export{b as p};
|
hfstudio/static/_app/immutable/chunks/Dh5qNFGZ.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var kt=Object.defineProperty;var Et=(e,t,n)=>t in e?kt(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var R=(e,t,n)=>Et(e,typeof t!="symbol"?t+"":t,n);import{S as At,F as Rt,Y as Tt,Z as Ut,_ as It,$ as Lt,a0 as $t,a1 as xt,r as ve,a2 as Ct,G as be,n as ge,s as Pt}from"./9DiovRey.js";class Qe extends At{constructor(n){if(!n||!n.target&&!n.$$inline)throw new Error("'target' is a required option");super();R(this,"$$prop_def");R(this,"$$events_def");R(this,"$$slot_def")}$destroy(){super.$destroy(),this.$destroy=()=>{console.warn("Component was already destroyed")}}$capture_state(){}$inject_state(){}}class Ot extends Qe{}const Nt=Object.freeze(Object.defineProperty({__proto__:null,SvelteComponent:Qe,SvelteComponentTyped:Ot,afterUpdate:Rt,beforeUpdate:Tt,createEventDispatcher:Ut,getAllContexts:It,getContext:Lt,hasContext:$t,onDestroy:xt,onMount:ve,setContext:Ct,tick:be},Symbol.toStringTag,{value:"Module"}));class ce{constructor(t,n){this.status=t,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${t}`}}toString(){return JSON.stringify(this.body)}}class Re{constructor(t,n){this.status=t,this.location=n}}class Te extends Error{constructor(t,n,r){super(r),this.status=t,this.text=n}}new URL("sveltekit-internal://");function jt(e,t){return e==="/"||t==="ignore"?e:t==="never"?e.endsWith("/")?e.slice(0,-1):e:t==="always"&&!e.endsWith("/")?e+"/":e}function Dt(e){return e.split("%25").map(decodeURI).join("%25")}function qt(e){for(const t in e)e[t]=decodeURIComponent(e[t]);return e}function me({href:e}){return e.split("#")[0]}function Ft(e,t,n,r=!1){const a=new URL(e);Object.defineProperty(a,"searchParams",{value:new Proxy(a.searchParams,{get(i,o){if(o==="get"||o==="getAll"||o==="has")return l=>(n(l),i[o](l));t();const c=Reflect.get(i,o);return typeof c=="function"?c.bind(i):c}}),enumerable:!0,configurable:!0});const s=["href","pathname","search","toString","toJSON"];r&&s.push("hash");for(const i of s)Object.defineProperty(a,i,{get(){return t(),e[i]},enumerable:!0,configurable:!0});return a}function Bt(...e){let t=5381;for(const n of e)if(typeof n=="string"){let r=n.length;for(;r;)t=t*33^n.charCodeAt(--r)}else if(ArrayBuffer.isView(n)){const r=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let a=r.length;for(;a;)t=t*33^r[--a]}else throw new TypeError("value must be a string or TypedArray");return(t>>>0).toString(36)}new TextEncoder;const Mt=new TextDecoder;function Vt(e){const t=atob(e),n=new Uint8Array(t.length);for(let r=0;r<t.length;r++)n[r]=t.charCodeAt(r);return n}const Gt=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:(t==null?void 0:t.method)||"GET")!=="GET"&&Y.delete(Ue(e)),Gt(e,t));const Y=new Map;function Yt(e,t){const n=Ue(e,t),r=document.querySelector(n);if(r!=null&&r.textContent){r.remove();let{body:a,...s}=JSON.parse(r.textContent);const i=r.getAttribute("data-ttl");return i&&Y.set(n,{body:a,init:s,ttl:1e3*Number(i)}),r.getAttribute("data-b64")!==null&&(a=Vt(a)),Promise.resolve(new Response(a,s))}return window.fetch(e,t)}function Ht(e,t,n){if(Y.size>0){const r=Ue(e,n),a=Y.get(r);if(a){if(performance.now()<a.ttl&&["default","force-cache","only-if-cached",void 0].includes(n==null?void 0:n.cache))return new Response(a.body,a.init);Y.delete(r)}}return window.fetch(t,n)}function Ue(e,t){let r=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t!=null&&t.headers||t!=null&&t.body){const a=[];t.headers&&a.push([...new Headers(t.headers)].join(",")),t.body&&(typeof t.body=="string"||ArrayBuffer.isView(t.body))&&a.push(t.body),r+=`[data-hash="${Bt(...a)}"]`}return r}const Kt=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;function zt(e){const t=[];return{pattern:e==="/"?/^\/$/:new RegExp(`^${Jt(e).map(r=>{const a=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(r);if(a)return t.push({name:a[1],matcher:a[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const s=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(r);if(s)return t.push({name:s[1],matcher:s[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!r)return;const i=r.split(/\[(.+?)\](?!\])/);return"/"+i.map((c,l)=>{if(l%2){if(c.startsWith("x+"))return _e(String.fromCharCode(parseInt(c.slice(2),16)));if(c.startsWith("u+"))return _e(String.fromCharCode(...c.slice(2).split("-").map(u=>parseInt(u,16))));const d=Kt.exec(c),[,h,y,f,p]=d;return t.push({name:f,matcher:p,optional:!!h,rest:!!y,chained:y?l===1&&i[0]==="":!1}),y?"([^]*?)":h?"([^/]*)?":"([^/]+?)"}return _e(c)}).join("")}).join("")}/?$`),params:t}}function Wt(e){return e!==""&&!/^\([^)]+\)$/.test(e)}function Jt(e){return e.slice(1).split("/").filter(Wt)}function Xt(e,t,n){const r={},a=e.slice(1),s=a.filter(o=>o!==void 0);let i=0;for(let o=0;o<t.length;o+=1){const c=t[o];let l=a[o-i];if(c.chained&&c.rest&&i&&(l=a.slice(o-i,o+1).filter(d=>d).join("/"),i=0),l===void 0){c.rest&&(r[c.name]="");continue}if(!c.matcher||n[c.matcher](l)){r[c.name]=l;const d=t[o+1],h=a[o+1];d&&!d.rest&&d.optional&&h&&c.chained&&(i=0),!d&&!h&&Object.keys(r).length===s.length&&(i=0);continue}if(c.optional&&c.chained){i++;continue}return}if(!i)return r}function _e(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function Zt({nodes:e,server_loads:t,dictionary:n,matchers:r}){const a=new Set(t);return Object.entries(n).map(([o,[c,l,d]])=>{const{pattern:h,params:y}=zt(o),f={id:o,exec:p=>{const u=h.exec(p);if(u)return Xt(u,y,r)},errors:[1,...d||[]].map(p=>e[p]),layouts:[0,...l||[]].map(i),leaf:s(c)};return f.errors.length=f.layouts.length=Math.max(f.errors.length,f.layouts.length),f});function s(o){const c=o<0;return c&&(o=~o),[c,e[o]]}function i(o){return o===void 0?o:[a.has(o),e[o]]}}function et(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function Be(e,t,n=JSON.stringify){const r=n(t);try{sessionStorage[e]=r}catch{}}const D=[];function Ie(e,t=ge){let n;const r=new Set;function a(o){if(Pt(e,o)&&(e=o,n)){const c=!D.length;for(const l of r)l[1](),D.push(l,e);if(c){for(let l=0;l<D.length;l+=2)D[l][0](D[l+1]);D.length=0}}}function s(o){a(o(e))}function i(o,c=ge){const l=[o,c];return r.add(l),r.size===1&&(n=t(a,s)||ge),o(e),()=>{r.delete(l),r.size===0&&n&&(n(),n=null)}}return{set:a,update:s,subscribe:i}}var Xe;const $=((Xe=globalThis.__sveltekit_pr8qbd)==null?void 0:Xe.base)??"";var Ze;const Qt=((Ze=globalThis.__sveltekit_pr8qbd)==null?void 0:Ze.assets)??$??"",en="1760660574318",tt="sveltekit:snapshot",nt="sveltekit:scroll",at="sveltekit:states",tn="sveltekit:pageurl",F="sveltekit:history",z="sveltekit:navigation",O={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},Z=location.origin;function Le(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){const n=document.getElementsByTagName("base");t=n.length?n[0].href:document.URL}return new URL(e,t)}function le(){return{x:pageXOffset,y:pageYOffset}}function q(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const Me={...O,"":O.hover};function rt(e){let t=e.assignedSlot??e.parentNode;return(t==null?void 0:t.nodeType)===11&&(t=t.host),t}function ot(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=rt(e)}}function Se(e,t,n){let r;try{if(r=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&r.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";r.hash=`#${o}${r.hash}`}}catch{}const a=e instanceof SVGAElement?e.target.baseVal:e.target,s=!r||!!a||fe(r,t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),i=(r==null?void 0:r.origin)===Z&&e.hasAttribute("download");return{url:r,external:s,target:a,download:i}}function ee(e){let t=null,n=null,r=null,a=null,s=null,i=null,o=e;for(;o&&o!==document.documentElement;)r===null&&(r=q(o,"preload-code")),a===null&&(a=q(o,"preload-data")),t===null&&(t=q(o,"keepfocus")),n===null&&(n=q(o,"noscroll")),s===null&&(s=q(o,"reload")),i===null&&(i=q(o,"replacestate")),o=rt(o);function c(l){switch(l){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:Me[r??"off"],preload_data:Me[a??"off"],keepfocus:c(t),noscroll:c(n),reload:c(s),replace_state:c(i)}}function Ve(e){const t=Ie(e);let n=!0;function r(){n=!0,t.update(i=>i)}function a(i){n=!1,t.set(i)}function s(i){let o;return t.subscribe(c=>{(o===void 0||n&&c!==o)&&i(o=c)})}return{notify:r,set:a,subscribe:s}}const st={v:()=>{}};function nn(){const{set:e,subscribe:t}=Ie(!1);let n;async function r(){clearTimeout(n);try{const a=await fetch(`${Qt}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!a.ok)return!1;const i=(await a.json()).version!==en;return i&&(e(!0),st.v(),clearTimeout(n)),i}catch{return!1}}return{subscribe:t,check:r}}function fe(e,t,n){return e.origin!==Z||!e.pathname.startsWith(t)?!0:n?!(e.pathname===t+"/"||e.pathname===t+"/index.html"||e.protocol==="file:"&&e.pathname.replace(/\/[^/]+\.html?$/,"")===t):!1}function Vn(e){}function an(e){const t=on(e),n=new ArrayBuffer(t.length),r=new DataView(n);for(let a=0;a<n.byteLength;a++)r.setUint8(a,t.charCodeAt(a));return n}const rn="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function on(e){e.length%4===0&&(e=e.replace(/==?$/,""));let t="",n=0,r=0;for(let a=0;a<e.length;a++)n<<=6,n|=rn.indexOf(e[a]),r+=6,r===24&&(t+=String.fromCharCode((n&16711680)>>16),t+=String.fromCharCode((n&65280)>>8),t+=String.fromCharCode(n&255),n=r=0);return r===12?(n>>=4,t+=String.fromCharCode(n)):r===18&&(n>>=2,t+=String.fromCharCode((n&65280)>>8),t+=String.fromCharCode(n&255)),t}const sn=-1,cn=-2,ln=-3,fn=-4,un=-5,dn=-6;function hn(e,t){if(typeof e=="number")return a(e,!0);if(!Array.isArray(e)||e.length===0)throw new Error("Invalid input");const n=e,r=Array(n.length);function a(s,i=!1){if(s===sn)return;if(s===ln)return NaN;if(s===fn)return 1/0;if(s===un)return-1/0;if(s===dn)return-0;if(i||typeof s!="number")throw new Error("Invalid input");if(s in r)return r[s];const o=n[s];if(!o||typeof o!="object")r[s]=o;else if(Array.isArray(o))if(typeof o[0]=="string"){const c=o[0],l=t==null?void 0:t[c];if(l)return r[s]=l(a(o[1]));switch(c){case"Date":r[s]=new Date(o[1]);break;case"Set":const d=new Set;r[s]=d;for(let f=1;f<o.length;f+=1)d.add(a(o[f]));break;case"Map":const h=new Map;r[s]=h;for(let f=1;f<o.length;f+=2)h.set(a(o[f]),a(o[f+1]));break;case"RegExp":r[s]=new RegExp(o[1],o[2]);break;case"Object":r[s]=Object(o[1]);break;case"BigInt":r[s]=BigInt(o[1]);break;case"null":const y=Object.create(null);r[s]=y;for(let f=1;f<o.length;f+=2)y[o[f]]=a(o[f+1]);break;case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"BigInt64Array":case"BigUint64Array":{const f=globalThis[c],p=new f(a(o[1]));r[s]=o[2]!==void 0?p.subarray(o[2],o[3]):p;break}case"ArrayBuffer":{const f=o[1],p=an(f);r[s]=p;break}case"Temporal.Duration":case"Temporal.Instant":case"Temporal.PlainDate":case"Temporal.PlainTime":case"Temporal.PlainDateTime":case"Temporal.PlainMonthDay":case"Temporal.PlainYearMonth":case"Temporal.ZonedDateTime":{const f=c.slice(9);r[s]=Temporal[f].from(o[1]);break}case"URL":{const f=new URL(o[1]);r[s]=f;break}case"URLSearchParams":{const f=new URLSearchParams(o[1]);r[s]=f;break}default:throw new Error(`Unknown type ${c}`)}}else{const c=new Array(o.length);r[s]=c;for(let l=0;l<o.length;l+=1){const d=o[l];d!==cn&&(c[l]=a(d))}}else{const c={};r[s]=c;for(const l in o){if(l==="__proto__")throw new Error("Cannot parse an object with a `__proto__` property");const d=o[l];c[l]=a(d)}}return r[s]}return a(0)}const it=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...it];const pn=new Set([...it]);[...pn];function gn(e){return e.filter(t=>t!=null)}const mn="x-sveltekit-invalidated",_n="x-sveltekit-trailing-slash";function te(e){return e instanceof ce||e instanceof Te?e.status:500}function wn(e){return e instanceof Te?e.text:"Internal Error"}let T,W,we;const yn=ve.toString().includes("$$")||/function \w+\(\) \{\}/.test(ve.toString());yn?(T={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL("https://example.com")},W={current:null},we={current:!1}):(T=new class{constructor(){R(this,"data",$state.raw({}));R(this,"form",$state.raw(null));R(this,"error",$state.raw(null));R(this,"params",$state.raw({}));R(this,"route",$state.raw({id:null}));R(this,"state",$state.raw({}));R(this,"status",$state.raw(-1));R(this,"url",$state.raw(new URL("https://example.com")))}},W=new class{constructor(){R(this,"current",$state.raw(null))}},we=new class{constructor(){R(this,"current",$state.raw(!1))}},st.v=()=>we.current=!0);function vn(e){Object.assign(T,e)}const bn="/__data.json",Sn=".html__data.json";function kn(e){return e.endsWith(".html")?e.replace(/\.html$/,Sn):e.replace(/\/$/,"")+bn}const Ge={spanContext(){return En},setAttribute(){return this},setAttributes(){return this},addEvent(){return this},setStatus(){return this},updateName(){return this},end(){return this},isRecording(){return!1},recordException(){return this},addLink(){return this},addLinks(){return this}},En={traceId:"",spanId:"",traceFlags:0},{tick:An}=Nt,Rn=new Set(["icon","shortcut icon","apple-touch-icon"]),j=et(nt)??{},J=et(tt)??{},C={url:Ve({}),page:Ve({}),navigating:Ie(null),updated:nn()};function $e(e){j[e]=le()}function Tn(e,t){let n=e+1;for(;j[n];)delete j[n],n+=1;for(n=t+1;J[n];)delete J[n],n+=1}function V(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(()=>{})}async function ct(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration($||"/");e&&await e.update()}}function Ye(){}let xe,ke,ne,x,Ee,k;const ae=[],re=[];let I=null;const Q=new Map,lt=new Set,Un=new Set,H=new Set;let b={branch:[],error:null,url:null},Ce=!1,oe=!1,He=!0,X=!1,G=!1,ft=!1,Pe=!1,ut,A,L,N;const K=new Set,Ke=new Map;async function Kn(e,t,n){var s,i,o,c,l;(s=globalThis.__sveltekit_pr8qbd)!=null&&s.data&&globalThis.__sveltekit_pr8qbd.data,document.URL!==location.href&&(location.href=location.href),k=e,await((o=(i=e.hooks).init)==null?void 0:o.call(i)),xe=Zt(e),x=document.documentElement,Ee=t,ke=e.nodes[0],ne=e.nodes[1],ke(),ne(),A=(c=history.state)==null?void 0:c[F],L=(l=history.state)==null?void 0:l[z],A||(A=L=Date.now(),history.replaceState({...history.state,[F]:A,[z]:L},""));const r=j[A];function a(){r&&(history.scrollRestoration="manual",scrollTo(r.x,r.y))}n?(a(),await Dn(Ee,n)):(await B({type:"enter",url:Le(k.hash?Fn(new URL(location.href)):location.href),replace_state:!0}),a()),jn()}function In(){ae.length=0,Pe=!1}function dt(e){re.some(t=>t==null?void 0:t.snapshot)&&(J[e]=re.map(t=>{var n;return(n=t==null?void 0:t.snapshot)==null?void 0:n.capture()}))}function ht(e){var t;(t=J[e])==null||t.forEach((n,r)=>{var a,s;(s=(a=re[r])==null?void 0:a.snapshot)==null||s.restore(n)})}function ze(){$e(A),Be(nt,j),dt(L),Be(tt,J)}async function pt(e,t,n,r){let a;t.invalidateAll&&(I=null),await B({type:"goto",url:Le(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:r,accept:()=>{t.invalidateAll&&(Pe=!0,a=[...Ke.keys()]),t.invalidate&&t.invalidate.forEach(Nn)}}),t.invalidateAll&&be().then(be).then(()=>{Ke.forEach(({resource:s},i)=>{var o;a!=null&&a.includes(i)&&((o=s.refresh)==null||o.call(s))})})}async function Ln(e){if(e.id!==(I==null?void 0:I.id)){const t={};K.add(t),I={id:e.id,token:t,promise:_t({...e,preload:t}).then(n=>(K.delete(t),n.type==="loaded"&&n.state.error&&(I=null),n))}}return I.promise}async function ye(e){var n;const t=(n=await de(e,!1))==null?void 0:n.route;t&&await Promise.all([...t.layouts,t.leaf].map(r=>r==null?void 0:r[1]()))}function gt(e,t,n){var a;b=e.state;const r=document.querySelector("style[data-sveltekit]");if(r&&r.remove(),Object.assign(T,e.props.page),ut=new k.root({target:t,props:{...e.props,stores:C,components:re},hydrate:n,sync:!1}),ht(L),n){const s={from:null,to:{params:b.params,route:{id:((a=b.route)==null?void 0:a.id)??null},url:new URL(location.href)},willUnload:!1,type:"enter",complete:Promise.resolve()};H.forEach(i=>i(s))}oe=!0}function se({url:e,params:t,branch:n,status:r,error:a,route:s,form:i}){let o="never";if($&&(e.pathname===$||e.pathname===$+"/"))o="always";else for(const f of n)(f==null?void 0:f.slash)!==void 0&&(o=f.slash);e.pathname=jt(e.pathname,o),e.search=e.search;const c={type:"loaded",state:{url:e,params:t,branch:n,error:a,route:s},props:{constructors:gn(n).map(f=>f.node.component),page:De(T)}};i!==void 0&&(c.props.form=i);let l={},d=!T,h=0;for(let f=0;f<Math.max(n.length,b.branch.length);f+=1){const p=n[f],u=b.branch[f];(p==null?void 0:p.data)!==(u==null?void 0:u.data)&&(d=!0),p&&(l={...l,...p.data},d&&(c.props[`data_${h}`]=l),h+=1)}return(!b.url||e.href!==b.url.href||b.error!==a||i!==void 0&&i!==T.form||d)&&(c.props.page={error:a,params:t,route:{id:(s==null?void 0:s.id)??null},state:{},status:r,url:new URL(e),form:i??null,data:d?l:T.data}),c}async function Oe({loader:e,parent:t,url:n,params:r,route:a,server_data_node:s}){var d,h,y;let i=null,o=!0;const c={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},l=await e();if((d=l.universal)!=null&&d.load){let f=function(...u){for(const g of u){const{href:_}=new URL(g,n);c.dependencies.add(_)}};const p={tracing:{enabled:!1,root:Ge,current:Ge},route:new Proxy(a,{get:(u,g)=>(o&&(c.route=!0),u[g])}),params:new Proxy(r,{get:(u,g)=>(o&&c.params.add(g),u[g])}),data:(s==null?void 0:s.data)??null,url:Ft(n,()=>{o&&(c.url=!0)},u=>{o&&c.search_params.add(u)},k.hash),async fetch(u,g){u instanceof Request&&(g={body:u.method==="GET"||u.method==="HEAD"?void 0:await u.blob(),cache:u.cache,credentials:u.credentials,headers:[...u.headers].length>0?u==null?void 0:u.headers:void 0,integrity:u.integrity,keepalive:u.keepalive,method:u.method,mode:u.mode,redirect:u.redirect,referrer:u.referrer,referrerPolicy:u.referrerPolicy,signal:u.signal,...g});const{resolved:_,promise:U}=mt(u,g,n);return o&&f(_.href),U},setHeaders:()=>{},depends:f,parent(){return o&&(c.parent=!0),t()},untrack(u){o=!1;try{return u()}finally{o=!0}}};i=await l.universal.load.call(null,p)??null}return{node:l,loader:e,server:s,universal:(h=l.universal)!=null&&h.load?{type:"data",data:i,uses:c}:null,data:i??(s==null?void 0:s.data)??null,slash:((y=l.universal)==null?void 0:y.trailingSlash)??(s==null?void 0:s.slash)}}function mt(e,t,n){let r=e instanceof Request?e.url:e;const a=new URL(r,n);a.origin===n.origin&&(r=a.href.slice(n.origin.length));const s=oe?Ht(r,a.href,t):Yt(r,t);return{resolved:a,promise:s}}function We(e,t,n,r,a,s){if(Pe)return!0;if(!a)return!1;if(a.parent&&e||a.route&&t||a.url&&n)return!0;for(const i of a.search_params)if(r.has(i))return!0;for(const i of a.params)if(s[i]!==b.params[i])return!0;for(const i of a.dependencies)if(ae.some(o=>o(new URL(i))))return!0;return!1}function Ne(e,t){return(e==null?void 0:e.type)==="data"?e:(e==null?void 0:e.type)==="skip"?t??null:null}function $n(e,t){if(!e)return new Set(t.searchParams.keys());const n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(const r of n){const a=e.searchParams.getAll(r),s=t.searchParams.getAll(r);a.every(i=>s.includes(i))&&s.every(i=>a.includes(i))&&n.delete(r)}return n}function Je({error:e,url:t,route:n,params:r}){return{type:"loaded",state:{error:e,url:t,route:n,params:r,branch:[]},props:{page:De(T),constructors:[]}}}async function _t({id:e,invalidating:t,url:n,params:r,route:a,preload:s}){if((I==null?void 0:I.id)===e)return K.delete(I.token),I.promise;const{errors:i,layouts:o,leaf:c}=a,l=[...o,c];i.forEach(w=>w==null?void 0:w().catch(()=>{})),l.forEach(w=>w==null?void 0:w[1]().catch(()=>{}));let d=null;const h=b.url?e!==ie(b.url):!1,y=b.route?a.id!==b.route.id:!1,f=$n(b.url,n);let p=!1;const u=l.map((w,m)=>{var P;const v=b.branch[m],S=!!(w!=null&&w[0])&&((v==null?void 0:v.loader)!==w[1]||We(p,y,h,f,(P=v.server)==null?void 0:P.uses,r));return S&&(p=!0),S});if(u.some(Boolean)){try{d=await vt(n,u)}catch(w){const m=await M(w,{url:n,params:r,route:{id:e}});return K.has(s)?Je({error:m,url:n,params:r,route:a}):ue({status:te(w),error:m,url:n,route:a})}if(d.type==="redirect")return d}const g=d==null?void 0:d.nodes;let _=!1;const U=l.map(async(w,m)=>{var he;if(!w)return;const v=b.branch[m],S=g==null?void 0:g[m];if((!S||S.type==="skip")&&w[1]===(v==null?void 0:v.loader)&&!We(_,y,h,f,(he=v.universal)==null?void 0:he.uses,r))return v;if(_=!0,(S==null?void 0:S.type)==="error")throw S;return Oe({loader:w[1],url:n,params:r,route:a,parent:async()=>{var Fe;const qe={};for(let pe=0;pe<m;pe+=1)Object.assign(qe,(Fe=await U[pe])==null?void 0:Fe.data);return qe},server_data_node:Ne(S===void 0&&w[0]?{type:"skip"}:S??null,w[0]?v==null?void 0:v.server:void 0)})});for(const w of U)w.catch(()=>{});const E=[];for(let w=0;w<l.length;w+=1)if(l[w])try{E.push(await U[w])}catch(m){if(m instanceof Re)return{type:"redirect",location:m.location};if(K.has(s))return Je({error:await M(m,{params:r,url:n,route:{id:a.id}}),url:n,params:r,route:a});let v=te(m),S;if(g!=null&&g.includes(m))v=m.status??v,S=m.error;else if(m instanceof ce)S=m.body;else{if(await C.updated.check())return await ct(),await V(n);S=await M(m,{params:r,url:n,route:{id:a.id}})}const P=await xn(w,E,i);return P?se({url:n,params:r,branch:E.slice(0,P.idx).concat(P.node),status:v,error:S,route:a}):await yt(n,{id:a.id},S,v)}else E.push(void 0);return se({url:n,params:r,branch:E,status:200,error:null,route:a,form:t?void 0:null})}async function xn(e,t,n){for(;e--;)if(n[e]){let r=e;for(;!t[r];)r-=1;try{return{idx:r+1,node:{node:await n[e](),loader:n[e],data:{},server:null,universal:null}}}catch{continue}}}async function ue({status:e,error:t,url:n,route:r}){const a={};let s=null;if(k.server_loads[0]===0)try{const o=await vt(n,[!0]);if(o.type!=="data"||o.nodes[0]&&o.nodes[0].type!=="data")throw 0;s=o.nodes[0]??null}catch{(n.origin!==Z||n.pathname!==location.pathname||Ce)&&await V(n)}try{const o=await Oe({loader:ke,url:n,params:a,route:r,parent:()=>Promise.resolve({}),server_data_node:Ne(s)}),c={node:await ne(),loader:ne,universal:null,server:null,data:null};return se({url:n,params:a,branch:[o,c],status:e,error:t,route:null})}catch(o){if(o instanceof Re)return pt(new URL(o.location,location.href),{},0);throw o}}async function Cn(e){const t=e.href;if(Q.has(t))return Q.get(t);let n;try{const r=(async()=>{let a=await k.hooks.reroute({url:new URL(e),fetch:async(s,i)=>mt(s,i,e).promise})??e;if(typeof a=="string"){const s=new URL(e);k.hash?s.hash=a:s.pathname=a,a=s}return a})();Q.set(t,r),n=await r}catch{Q.delete(t);return}return n}async function de(e,t){if(e&&!fe(e,$,k.hash)){const n=await Cn(e);if(!n)return;const r=Pn(n);for(const a of xe){const s=a.exec(r);if(s)return{id:ie(e),invalidating:t,route:a,params:qt(s),url:e}}}}function Pn(e){return Dt(k.hash?e.hash.replace(/^#/,"").replace(/[?#].+/,""):e.pathname.slice($.length))||"/"}function ie(e){return(k.hash?e.hash.replace(/^#/,""):e.pathname)+e.search}function wt({url:e,type:t,intent:n,delta:r,event:a}){let s=!1;const i=je(b,n,e,t);r!==void 0&&(i.navigation.delta=r),a!==void 0&&(i.navigation.event=a);const o={...i.navigation,cancel:()=>{s=!0,i.reject(new Error("navigation cancelled"))}};return X||lt.forEach(c=>c(o)),s?null:i}async function B({type:e,url:t,popped:n,keepfocus:r,noscroll:a,replace_state:s,state:i={},redirect_count:o=0,nav_token:c={},accept:l=Ye,block:d=Ye,event:h}){const y=N;N=c;const f=await de(t,!1),p=e==="enter"?je(b,f,t,e):wt({url:t,type:e,delta:n==null?void 0:n.delta,intent:f,event:h});if(!p){d(),N===c&&(N=y);return}const u=A,g=L;l(),X=!0,oe&&p.navigation.type!=="enter"&&C.navigating.set(W.current=p.navigation);let _=f&&await _t(f);if(!_){if(fe(t,$,k.hash))return await V(t,s);_=await yt(t,{id:null},await M(new Te(404,"Not Found",`Not found: ${t.pathname}`),{url:t,params:{},route:{id:null}}),404,s)}if(t=(f==null?void 0:f.url)||t,N!==c)return p.reject(new Error("navigation aborted")),!1;if(_.type==="redirect"){if(o<20){await B({type:e,url:new URL(_.location,t),popped:n,keepfocus:r,noscroll:a,replace_state:s,state:i,redirect_count:o+1,nav_token:c}),p.fulfil(void 0);return}_=await ue({status:500,error:await M(new Error("Redirect loop"),{url:t,params:{},route:{id:null}}),url:t,route:{id:null}})}else _.props.page.status>=400&&await C.updated.check()&&(await ct(),await V(t,s));if(In(),$e(u),dt(g),_.props.page.url.pathname!==t.pathname&&(t.pathname=_.props.page.url.pathname),i=n?n.state:i,!n){const m=s?0:1,v={[F]:A+=m,[z]:L+=m,[at]:i};(s?history.replaceState:history.pushState).call(history,v,"",t),s||Tn(A,L)}if(I=null,_.props.page.state=i,oe){const m=(await Promise.all(Array.from(Un,v=>v(p.navigation)))).filter(v=>typeof v=="function");if(m.length>0){let v=function(){m.forEach(S=>{H.delete(S)})};m.push(v),m.forEach(S=>{H.add(S)})}b=_.state,_.props.page&&(_.props.page.url=t),ut.$set(_.props),vn(_.props.page),ft=!0}else gt(_,Ee,!1);const{activeElement:U}=document;await An();let E=n?n.scroll:a?le():null;if(He){const m=t.hash&&document.getElementById(St(t));if(E)scrollTo(E.x,E.y);else if(m){m.scrollIntoView();const{top:v,left:S}=m.getBoundingClientRect();E={x:pageXOffset+S,y:pageYOffset+v}}else scrollTo(0,0)}const w=document.activeElement!==U&&document.activeElement!==document.body;!r&&!w&&qn(t,E),He=!0,_.props.page&&Object.assign(T,_.props.page),X=!1,e==="popstate"&&ht(L),p.fulfil(void 0),H.forEach(m=>m(p.navigation)),C.navigating.set(W.current=null)}async function yt(e,t,n,r,a){return e.origin===Z&&e.pathname===location.pathname&&!Ce?await ue({status:r,error:n,url:e,route:t}):await V(e,a)}function On(){let e,t,n;x.addEventListener("mousemove",o=>{const c=o.target;clearTimeout(e),e=setTimeout(()=>{s(c,O.hover)},20)});function r(o){o.defaultPrevented||s(o.composedPath()[0],O.tap)}x.addEventListener("mousedown",r),x.addEventListener("touchstart",r,{passive:!0});const a=new IntersectionObserver(o=>{for(const c of o)c.isIntersecting&&(ye(new URL(c.target.href)),a.unobserve(c.target))},{threshold:0});async function s(o,c){const l=ot(o,x),d=l===t&&c>=n;if(!l||d)return;const{url:h,external:y,download:f}=Se(l,$,k.hash);if(y||f)return;const p=ee(l),u=h&&ie(b.url)===ie(h);if(!(p.reload||u))if(c<=p.preload_data){t=l,n=O.tap;const g=await de(h,!1);if(!g)return;Ln(g)}else c<=p.preload_code&&(t=l,n=c,ye(h))}function i(){a.disconnect();for(const o of x.querySelectorAll("a")){const{url:c,external:l,download:d}=Se(o,$,k.hash);if(l||d)continue;const h=ee(o);h.reload||(h.preload_code===O.viewport&&a.observe(o),h.preload_code===O.eager&&ye(c))}}H.add(i),i()}function M(e,t){if(e instanceof ce)return e.body;const n=te(e),r=wn(e);return k.hooks.handleError({error:e,event:t,status:n,message:r})??{message:r}}function zn(e,t={}){return e=new URL(Le(e)),e.origin!==Z?Promise.reject(new Error("goto: invalid URL")):pt(e,t,0)}function Nn(e){if(typeof e=="function")ae.push(e);else{const{href:t}=new URL(e,location.href);ae.push(n=>n.href===t)}}function jn(){var t;history.scrollRestoration="manual",addEventListener("beforeunload",n=>{let r=!1;if(ze(),!X){const a=je(b,void 0,null,"leave"),s={...a.navigation,cancel:()=>{r=!0,a.reject(new Error("navigation cancelled"))}};lt.forEach(i=>i(s))}r?(n.preventDefault(),n.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&ze()}),(t=navigator.connection)!=null&&t.saveData||On(),x.addEventListener("click",async n=>{if(n.button||n.which!==1||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||n.defaultPrevented)return;const r=ot(n.composedPath()[0],x);if(!r)return;const{url:a,external:s,target:i,download:o}=Se(r,$,k.hash);if(!a)return;if(i==="_parent"||i==="_top"){if(window.parent!==window)return}else if(i&&i!=="_self")return;const c=ee(r);if(!(r instanceof SVGAElement)&&a.protocol!==location.protocol&&!(a.protocol==="https:"||a.protocol==="http:")||o)return;const[d,h]=(k.hash?a.hash.replace(/^#/,""):a.href).split("#"),y=d===me(location);if(s||c.reload&&(!y||!h)){wt({url:a,type:"link",event:n})?X=!0:n.preventDefault();return}if(h!==void 0&&y){const[,f]=b.url.href.split("#");if(f===h){if(n.preventDefault(),h===""||h==="top"&&r.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const p=r.ownerDocument.getElementById(decodeURIComponent(h));p&&(p.scrollIntoView(),p.focus())}return}if(G=!0,$e(A),e(a),!c.replace_state)return;G=!1}n.preventDefault(),await new Promise(f=>{requestAnimationFrame(()=>{setTimeout(f,0)}),setTimeout(f,100)}),await B({type:"link",url:a,keepfocus:c.keepfocus,noscroll:c.noscroll,replace_state:c.replace_state??a.href===location.href,event:n})}),x.addEventListener("submit",n=>{if(n.defaultPrevented)return;const r=HTMLFormElement.prototype.cloneNode.call(n.target),a=n.submitter;if(((a==null?void 0:a.formTarget)||r.target)==="_blank"||((a==null?void 0:a.formMethod)||r.method)!=="get")return;const o=new URL((a==null?void 0:a.hasAttribute("formaction"))&&(a==null?void 0:a.formAction)||r.action);if(fe(o,$,!1))return;const c=n.target,l=ee(c);if(l.reload)return;n.preventDefault(),n.stopPropagation();const d=new FormData(c,a);o.search=new URLSearchParams(d).toString(),B({type:"form",url:o,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??o.href===location.href,event:n})}),addEventListener("popstate",async n=>{var r;if(!Ae){if((r=n.state)!=null&&r[F]){const a=n.state[F];if(N={},a===A)return;const s=j[a],i=n.state[at]??{},o=new URL(n.state[tn]??location.href),c=n.state[z],l=b.url?me(location)===me(b.url):!1;if(c===L&&(ft||l)){i!==T.state&&(T.state=i),e(o),j[A]=le(),s&&scrollTo(s.x,s.y),A=a;return}const h=a-A;await B({type:"popstate",url:o,popped:{state:i,scroll:s,delta:h},accept:()=>{A=a,L=c},block:()=>{history.go(-h)},nav_token:N,event:n})}else if(!G){const a=new URL(location.href);e(a),k.hash&&location.reload()}}}),addEventListener("hashchange",()=>{G&&(G=!1,history.replaceState({...history.state,[F]:++A,[z]:L},"",location.href))});for(const n of document.querySelectorAll("link"))Rn.has(n.rel)&&(n.href=n.href);addEventListener("pageshow",n=>{n.persisted&&C.navigating.set(W.current=null)});function e(n){b.url=T.url=n,C.page.set(De(T)),C.page.notify()}}async function Dn(e,{status:t=200,error:n,node_ids:r,params:a,route:s,server_route:i,data:o,form:c}){Ce=!0;const l=new URL(location.href);let d;({params:a={},route:s={id:null}}=await de(l,!1)||{}),d=xe.find(({id:f})=>f===s.id);let h,y=!0;try{const f=r.map(async(u,g)=>{const _=o[g];return _!=null&&_.uses&&(_.uses=bt(_.uses)),Oe({loader:k.nodes[u],url:l,params:a,route:s,parent:async()=>{const U={};for(let E=0;E<g;E+=1)Object.assign(U,(await f[E]).data);return U},server_data_node:Ne(_)})}),p=await Promise.all(f);if(d){const u=d.layouts;for(let g=0;g<u.length;g++)u[g]||p.splice(g,0,void 0)}h=se({url:l,params:a,branch:p,status:t,error:n,form:c,route:d??null})}catch(f){if(f instanceof Re){await V(new URL(f.location,location.href));return}h=await ue({status:te(f),error:await M(f,{url:l,params:a,route:s}),url:l,route:s}),e.textContent="",y=!1}h.props.page&&(h.props.page.state={}),gt(h,e,y)}async function vt(e,t){var s;const n=new URL(e);n.pathname=kn(e.pathname),e.pathname.endsWith("/")&&n.searchParams.append(_n,"1"),n.searchParams.append(mn,t.map(i=>i?"1":"0").join(""));const r=window.fetch,a=await r(n.href,{});if(!a.ok){let i;throw(s=a.headers.get("content-type"))!=null&&s.includes("application/json")?i=await a.json():a.status===404?i="Not Found":a.status===500&&(i="Internal Error"),new ce(a.status,i)}return new Promise(async i=>{var h;const o=new Map,c=a.body.getReader();function l(y){return hn(y,{...k.decoders,Promise:f=>new Promise((p,u)=>{o.set(f,{fulfil:p,reject:u})})})}let d="";for(;;){const{done:y,value:f}=await c.read();if(y&&!d)break;for(d+=!f&&d?`
|
| 2 |
+
`:Mt.decode(f,{stream:!0});;){const p=d.indexOf(`
|
| 3 |
+
`);if(p===-1)break;const u=JSON.parse(d.slice(0,p));if(d=d.slice(p+1),u.type==="redirect")return i(u);if(u.type==="data")(h=u.nodes)==null||h.forEach(g=>{(g==null?void 0:g.type)==="data"&&(g.uses=bt(g.uses),g.data=l(g.data))}),i(u);else if(u.type==="chunk"){const{id:g,data:_,error:U}=u,E=o.get(g);o.delete(g),U?E.reject(l(U)):E.fulfil(l(_))}}}})}function bt(e){return{dependencies:new Set((e==null?void 0:e.dependencies)??[]),params:new Set((e==null?void 0:e.params)??[]),parent:!!(e!=null&&e.parent),route:!!(e!=null&&e.route),url:!!(e!=null&&e.url),search_params:new Set((e==null?void 0:e.search_params)??[])}}let Ae=!1;function qn(e,t=null){const n=document.querySelector("[autofocus]");if(n)n.focus();else{const r=St(e);if(r&&document.getElementById(r)){const{x:s,y:i}=t??le();setTimeout(()=>{const o=history.state;Ae=!0,location.replace(`#${r}`),k.hash&&location.replace(e.hash),history.replaceState(o,"",e.hash),scrollTo(s,i),Ae=!1})}else{const s=document.body,i=s.getAttribute("tabindex");s.tabIndex=-1,s.focus({preventScroll:!0,focusVisible:!1}),i!==null?s.setAttribute("tabindex",i):s.removeAttribute("tabindex")}const a=getSelection();if(a&&a.type!=="None"){const s=[];for(let i=0;i<a.rangeCount;i+=1)s.push(a.getRangeAt(i));setTimeout(()=>{if(a.rangeCount===s.length){for(let i=0;i<a.rangeCount;i+=1){const o=s[i],c=a.getRangeAt(i);if(o.commonAncestorContainer!==c.commonAncestorContainer||o.startContainer!==c.startContainer||o.endContainer!==c.endContainer||o.startOffset!==c.startOffset||o.endOffset!==c.endOffset)return}a.removeAllRanges()}})}}}function je(e,t,n,r){var c,l;let a,s;const i=new Promise((d,h)=>{a=d,s=h});return i.catch(()=>{}),{navigation:{from:{params:e.params,route:{id:((c=e.route)==null?void 0:c.id)??null},url:e.url},to:n&&{params:(t==null?void 0:t.params)??null,route:{id:((l=t==null?void 0:t.route)==null?void 0:l.id)??null},url:n},willUnload:!t,type:r,complete:i},fulfil:a,reject:s}}function De(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function Fn(e){const t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function St(e){let t;if(k.hash){const[,,n]=e.hash.split("#",3);t=n??""}else t=e.hash.slice(1);return decodeURIComponent(t)}export{Kn as a,zn as g,Vn as l,C as s};
|
hfstudio/static/_app/immutable/chunks/IHki7fMi.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
const e="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(e);
|
hfstudio/static/_app/immutable/entry/app.DrVe4Aen.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.BWfnr5bB.js","../chunks/9DiovRey.js","../chunks/IHki7fMi.js","../assets/0.DkRYx-5s.css","../nodes/1.DRlzjau5.js","../chunks/BHHrQo_1.js","../chunks/Dh5qNFGZ.js","../nodes/2.BSR-L-2e.js","../nodes/3.VqKPhUiT.js"])))=>i.map(i=>d[i]);
|
| 2 |
+
import{S as j,i as q,s as B,d,x as h,y as g,C as O,D as A,b as E,h as U,E as w,k as F,F as G,r as H,G as J,H as y,I as P,J as R,K as L,L as C,o as D,M as p,e as K,f as W,j as z,N as I,a as Q,g as X,t as Y}from"../chunks/9DiovRey.js";import"../chunks/IHki7fMi.js";const Z="modulepreload",M=function(o,e){return new URL(o,e).href},N={},S=function(e,n,i){let r=Promise.resolve();if(n&&n.length>0){const t=document.getElementsByTagName("link"),s=document.querySelector("meta[property=csp-nonce]"),a=(s==null?void 0:s.nonce)||(s==null?void 0:s.getAttribute("nonce"));r=Promise.allSettled(n.map(f=>{if(f=M(f,i),f in N)return;N[f]=!0;const l=f.endsWith(".css"),_=l?'[rel="stylesheet"]':"";if(!!i)for(let k=t.length-1;k>=0;k--){const v=t[k];if(v.href===f&&(!l||v.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${f}"]${_}`))return;const m=document.createElement("link");if(m.rel=l?"stylesheet":Z,l||(m.as="script"),m.crossOrigin="",m.href=f,a&&m.setAttribute("nonce",a),document.head.appendChild(m),l)return new Promise((k,v)=>{m.addEventListener("load",k),m.addEventListener("error",()=>v(new Error(`Unable to preload CSS for ${f}`)))})}))}function u(t){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=t,window.dispatchEvent(s),!s.defaultPrevented)throw t}return r.then(t=>{for(const s of t||[])s.status==="rejected"&&u(s.reason);return e().catch(u)})},ae={};function $(o){let e,n,i;var r=o[2][0];function u(t,s){return{props:{data:t[4],form:t[3],params:t[1].params}}}return r&&(e=y(r,u(o)),o[12](e)),{c(){e&&R(e.$$.fragment),n=w()},l(t){e&&C(e.$$.fragment,t),n=w()},m(t,s){e&&L(e,t,s),E(t,n,s),i=!0},p(t,s){if(s&4&&r!==(r=t[2][0])){if(e){O();const a=e;h(a.$$.fragment,1,0,()=>{P(a,1)}),A()}r?(e=y(r,u(t)),t[12](e),R(e.$$.fragment),g(e.$$.fragment,1),L(e,n.parentNode,n)):e=null}else if(r){const a={};s&16&&(a.data=t[4]),s&8&&(a.form=t[3]),s&2&&(a.params=t[1].params),e.$set(a)}},i(t){i||(e&&g(e.$$.fragment,t),i=!0)},o(t){e&&h(e.$$.fragment,t),i=!1},d(t){t&&d(n),o[12](null),e&&P(e,t)}}}function x(o){let e,n,i;var r=o[2][0];function u(t,s){return{props:{data:t[4],params:t[1].params,$$slots:{default:[ee]},$$scope:{ctx:t}}}}return r&&(e=y(r,u(o)),o[11](e)),{c(){e&&R(e.$$.fragment),n=w()},l(t){e&&C(e.$$.fragment,t),n=w()},m(t,s){e&&L(e,t,s),E(t,n,s),i=!0},p(t,s){if(s&4&&r!==(r=t[2][0])){if(e){O();const a=e;h(a.$$.fragment,1,0,()=>{P(a,1)}),A()}r?(e=y(r,u(t)),t[11](e),R(e.$$.fragment),g(e.$$.fragment,1),L(e,n.parentNode,n)):e=null}else if(r){const a={};s&16&&(a.data=t[4]),s&2&&(a.params=t[1].params),s&8239&&(a.$$scope={dirty:s,ctx:t}),e.$set(a)}},i(t){i||(e&&g(e.$$.fragment,t),i=!0)},o(t){e&&h(e.$$.fragment,t),i=!1},d(t){t&&d(n),o[11](null),e&&P(e,t)}}}function ee(o){let e,n,i;var r=o[2][1];function u(t,s){return{props:{data:t[5],form:t[3],params:t[1].params}}}return r&&(e=y(r,u(o)),o[10](e)),{c(){e&&R(e.$$.fragment),n=w()},l(t){e&&C(e.$$.fragment,t),n=w()},m(t,s){e&&L(e,t,s),E(t,n,s),i=!0},p(t,s){if(s&4&&r!==(r=t[2][1])){if(e){O();const a=e;h(a.$$.fragment,1,0,()=>{P(a,1)}),A()}r?(e=y(r,u(t)),t[10](e),R(e.$$.fragment),g(e.$$.fragment,1),L(e,n.parentNode,n)):e=null}else if(r){const a={};s&32&&(a.data=t[5]),s&8&&(a.form=t[3]),s&2&&(a.params=t[1].params),e.$set(a)}},i(t){i||(e&&g(e.$$.fragment,t),i=!0)},o(t){e&&h(e.$$.fragment,t),i=!1},d(t){t&&d(n),o[10](null),e&&P(e,t)}}}function T(o){let e,n=o[7]&&V(o);return{c(){e=z("div"),n&&n.c(),this.h()},l(i){e=K(i,"DIV",{id:!0,"aria-live":!0,"aria-atomic":!0,style:!0});var r=W(e);n&&n.l(r),r.forEach(d),this.h()},h(){D(e,"id","svelte-announcer"),D(e,"aria-live","assertive"),D(e,"aria-atomic","true"),p(e,"position","absolute"),p(e,"left","0"),p(e,"top","0"),p(e,"clip","rect(0 0 0 0)"),p(e,"clip-path","inset(50%)"),p(e,"overflow","hidden"),p(e,"white-space","nowrap"),p(e,"width","1px"),p(e,"height","1px")},m(i,r){E(i,e,r),n&&n.m(e,null)},p(i,r){i[7]?n?n.p(i,r):(n=V(i),n.c(),n.m(e,null)):n&&(n.d(1),n=null)},d(i){i&&d(e),n&&n.d()}}}function V(o){let e;return{c(){e=Y(o[8])},l(n){e=X(n,o[8])},m(n,i){E(n,e,i)},p(n,i){i&256&&Q(e,n[8])},d(n){n&&d(e)}}}function te(o){let e,n,i,r,u;const t=[x,$],s=[];function a(l,_){return l[2][1]?0:1}e=a(o),n=s[e]=t[e](o);let f=o[6]&&T(o);return{c(){n.c(),i=F(),f&&f.c(),r=w()},l(l){n.l(l),i=U(l),f&&f.l(l),r=w()},m(l,_){s[e].m(l,_),E(l,i,_),f&&f.m(l,_),E(l,r,_),u=!0},p(l,[_]){let b=e;e=a(l),e===b?s[e].p(l,_):(O(),h(s[b],1,1,()=>{s[b]=null}),A(),n=s[e],n?n.p(l,_):(n=s[e]=t[e](l),n.c()),g(n,1),n.m(i.parentNode,i)),l[6]?f?f.p(l,_):(f=T(l),f.c(),f.m(r.parentNode,r)):f&&(f.d(1),f=null)},i(l){u||(g(n),u=!0)},o(l){h(n),u=!1},d(l){l&&(d(i),d(r)),s[e].d(l),f&&f.d(l)}}}function ne(o,e,n){let{stores:i}=e,{page:r}=e,{constructors:u}=e,{components:t=[]}=e,{form:s}=e,{data_0:a=null}=e,{data_1:f=null}=e;G(i.page.notify);let l=!1,_=!1,b=null;H(()=>{const c=i.page.subscribe(()=>{l&&(n(7,_=!0),J().then(()=>{n(8,b=document.title||"untitled page")}))});return n(6,l=!0),c});function m(c){I[c?"unshift":"push"](()=>{t[1]=c,n(0,t)})}function k(c){I[c?"unshift":"push"](()=>{t[0]=c,n(0,t)})}function v(c){I[c?"unshift":"push"](()=>{t[0]=c,n(0,t)})}return o.$$set=c=>{"stores"in c&&n(9,i=c.stores),"page"in c&&n(1,r=c.page),"constructors"in c&&n(2,u=c.constructors),"components"in c&&n(0,t=c.components),"form"in c&&n(3,s=c.form),"data_0"in c&&n(4,a=c.data_0),"data_1"in c&&n(5,f=c.data_1)},o.$$.update=()=>{o.$$.dirty&514&&i.page.set(r)},[t,r,u,s,a,f,l,_,b,i,m,k,v]}class le extends j{constructor(e){super(),q(this,e,ne,te,B,{stores:9,page:1,constructors:2,components:0,form:3,data_0:4,data_1:5})}}const fe=[()=>S(()=>import("../nodes/0.BWfnr5bB.js"),__vite__mapDeps([0,1,2,3]),import.meta.url),()=>S(()=>import("../nodes/1.DRlzjau5.js"),__vite__mapDeps([4,1,2,5,6]),import.meta.url),()=>S(()=>import("../nodes/2.BSR-L-2e.js"),__vite__mapDeps([7,1,2]),import.meta.url),()=>S(()=>import("../nodes/3.VqKPhUiT.js"),__vite__mapDeps([8,1,2,6,5]),import.meta.url)],ce=[],ue={"/":[2],"/auth/callback":[3]},se={handleError:({error:o})=>{console.error(o)},reroute:()=>{},transport:{}},ie=Object.fromEntries(Object.entries(se.transport).map(([o,e])=>[o,e.decode])),_e=!1,me=(o,e)=>ie[o](e);export{me as decode,ie as decoders,ue as dictionary,_e as hash,se as hooks,ae as matchers,fe as nodes,le as root,ce as server_loads};
|
hfstudio/static/_app/immutable/entry/start.CYVq8xer.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{l as o,a as r}from"../chunks/Dh5qNFGZ.js";export{o as load_css,r as start};
|
hfstudio/static/_app/immutable/nodes/0.BWfnr5bB.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{S as mt,i as gt,s as _t,v as xt,d as y,w as bt,x as vt,y as yt,o as c,z as wt,A as Tt,B as kt,b as et,c as s,u as dt,m as St,e as o,f as U,q as k,h as b,j as r,k as v,r as Ct,a as It,g as pt,t as ht,n as Lt}from"../chunks/9DiovRey.js";import"../chunks/IHki7fMi.js";function Mt(i){let e,t="Sign In";return{c(){e=r("span"),e.textContent=t},l(l){e=o(l,"SPAN",{"data-svelte-h":!0}),k(e)!=="svelte-6n3gky"&&(e.textContent=t)},m(l,u){et(l,e,u)},p:Lt,d(l){l&&y(e)}}}function At(i){let e,t,l;return{c(){e=r("span"),t=ht("Sign Out, "),l=ht(i[2])},l(u){e=o(u,"SPAN",{});var d=U(e);t=pt(d,"Sign Out, "),l=pt(d,i[2]),d.forEach(y)},m(u,d){et(u,e,d),s(e,t),s(e,l)},p(u,d){d&4&&It(l,u[2])},d(u){u&&y(e)}}}function Et(i){let e,t,l,u='<div class="flex items-center gap-3"><img src="/assets/hf-studio-logo.png" alt="HF Logo" class="w-8 h-8"/> <h1 class="text-xl font-semibold">HFStudio<sup class="text-xs text-gray-500 ml-1">BETA</sup></h1></div>',d,n,p,C="Tasks",q,g,I,f="🎙️",O,w,L="Text to Speech",S,z,M,st="<span>🎵</span> <span>Voice Cloning</span>",W,A,nt="<span>🎧</span> <span>Speech to Text</span>",J,E,at="<span>🎼</span> <span>Sound Effects</span>",K,H,lt="<span>🎸</span> <span>Music Generation</span>",Q,N,ot="<span>🔊</span> <span>Audio Enhancement</span>",X,D,T,B,rt,Y,Z,V,j,$,ct;function it(a,_){return a[1]?At:Mt}let F=it(i),x=F(i);const tt=i[5].default,m=xt(tt,i,i[4],null);return{c(){e=r("div"),t=r("aside"),l=r("div"),l.innerHTML=u,d=v(),n=r("nav"),p=r("div"),p.textContent=C,q=v(),g=r("button"),I=r("span"),I.textContent=f,O=v(),w=r("span"),w.textContent=L,z=v(),M=r("button"),M.innerHTML=st,W=v(),A=r("button"),A.innerHTML=nt,J=v(),E=r("button"),E.innerHTML=at,K=v(),H=r("button"),H.innerHTML=lt,Q=v(),N=r("button"),N.innerHTML=ot,X=v(),D=r("div"),T=r("button"),B=r("img"),Y=v(),x.c(),Z=v(),V=r("main"),m&&m.c(),this.h()},l(a){e=o(a,"DIV",{class:!0});var _=U(e);t=o(_,"ASIDE",{class:!0});var P=U(t);l=o(P,"DIV",{class:!0,"data-svelte-h":!0}),k(l)!=="svelte-1vahj38"&&(l.innerHTML=u),d=b(P),n=o(P,"NAV",{class:!0});var h=U(n);p=o(h,"DIV",{class:!0,"data-svelte-h":!0}),k(p)!=="svelte-1x5465q"&&(p.textContent=C),q=b(h),g=o(h,"BUTTON",{class:!0});var G=U(g);I=o(G,"SPAN",{"data-svelte-h":!0}),k(I)!=="svelte-1yx42xi"&&(I.textContent=f),O=b(G),w=o(G,"SPAN",{"data-svelte-h":!0}),k(w)!=="svelte-2j89jk"&&(w.textContent=L),G.forEach(y),z=b(h),M=o(h,"BUTTON",{class:!0,"data-svelte-h":!0}),k(M)!=="svelte-10dl8nf"&&(M.innerHTML=st),W=b(h),A=o(h,"BUTTON",{class:!0,"data-svelte-h":!0}),k(A)!=="svelte-wf0x5d"&&(A.innerHTML=nt),J=b(h),E=o(h,"BUTTON",{class:!0,"data-svelte-h":!0}),k(E)!=="svelte-x7bha3"&&(E.innerHTML=at),K=b(h),H=o(h,"BUTTON",{class:!0,"data-svelte-h":!0}),k(H)!=="svelte-1tyblmt"&&(H.innerHTML=lt),Q=b(h),N=o(h,"BUTTON",{class:!0,"data-svelte-h":!0}),k(N)!=="svelte-1emrjb3"&&(N.innerHTML=ot),h.forEach(y),X=b(P),D=o(P,"DIV",{class:!0});var ut=U(D);T=o(ut,"BUTTON",{class:!0});var R=U(T);B=o(R,"IMG",{src:!0,alt:!0,class:!0}),Y=b(R),x.l(R),R.forEach(y),ut.forEach(y),P.forEach(y),Z=b(_),V=o(_,"MAIN",{class:!0});var ft=U(V);m&&m.l(ft),ft.forEach(y),_.forEach(y),this.h()},h(){c(l,"class","p-4 border-b border-gray-200"),c(p,"class","mt-2 mb-1 px-2 text-xs font-medium text-gray-500 uppercase"),c(g,"class",S="w-full flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 transition-colors text-left "+(i[0]==="tts"?"bg-gray-100":"")),c(M,"class","w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"),M.disabled=!0,c(A,"class","w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"),A.disabled=!0,c(E,"class","w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"),E.disabled=!0,c(H,"class","w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"),H.disabled=!0,c(N,"class","w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left opacity-40 cursor-not-allowed"),N.disabled=!0,c(n,"class","p-2 text-sm"),St(B.src,rt="/assets/hf-logo.png")||c(B,"src",rt),c(B,"alt","HF Logo"),c(B,"class","w-5 h-5"),c(T,"class","w-full px-6 py-3 bg-black text-white rounded-lg font-medium hover:bg-gray-800 transition-colors shadow-sm flex items-center justify-center gap-2 text-sm"),c(D,"class","absolute bottom-4 left-2 right-2 w-52"),c(t,"class","w-56 border-r border-gray-200 bg-white flex-shrink-0 "),c(V,"class","flex-1 overflow-auto"),c(e,"class","flex h-screen bg-white")},m(a,_){et(a,e,_),s(e,t),s(t,l),s(t,d),s(t,n),s(n,p),s(n,q),s(n,g),s(g,I),s(g,O),s(g,w),s(n,z),s(n,M),s(n,W),s(n,A),s(n,J),s(n,E),s(n,K),s(n,H),s(n,Q),s(n,N),s(t,X),s(t,D),s(D,T),s(T,B),s(T,Y),x.m(T,null),s(e,Z),s(e,V),m&&m.m(V,null),j=!0,$||(ct=[dt(g,"click",i[6]),dt(T,"click",i[3])],$=!0)},p(a,[_]){(!j||_&1&&S!==(S="w-full flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-gray-100 transition-colors text-left "+(a[0]==="tts"?"bg-gray-100":"")))&&c(g,"class",S),F===(F=it(a))&&x?x.p(a,_):(x.d(1),x=F(a),x&&(x.c(),x.m(T,null))),m&&m.p&&(!j||_&16)&&wt(m,tt,a,a[4],j?kt(tt,a[4],_,null):Tt(a[4]),null)},i(a){j||(yt(m,a),j=!0)},o(a){vt(m,a),j=!1},d(a){a&&y(e),x.d(),m&&m.d(a),$=!1,bt(ct)}}}function Ht(i,e,t){let{$$slots:l={},$$scope:u}=e,d="tts",n=!1,p="";Ct(()=>{C(),document.addEventListener("visibilitychange",()=>{document.hidden||C()}),window.addEventListener("storage",C);const f=setInterval(C,1e3);return()=>{window.removeEventListener("storage",C),clearInterval(f)}});function C(){const f=localStorage.getItem("hf_access_token");f?q(f):(t(1,n=!1),t(2,p=""))}async function q(f){try{const w=f.startsWith("hf_oauth_")?"https://huggingface.co/oauth/userinfo":"https://huggingface.co/api/whoami",L=await fetch(w,{headers:{Authorization:`Bearer ${f}`}});if(L.ok){const S=await L.json();t(1,n=!0);const z=S.name||S.login||"User";t(2,p=z.split(" ")[0])}else localStorage.removeItem("hf_access_token"),t(1,n=!1),t(2,p="")}catch{localStorage.removeItem("hf_access_token"),t(1,n=!1),t(2,p="")}}function g(){if(n)localStorage.removeItem("hf_access_token"),sessionStorage.removeItem("oauth_state"),t(1,n=!1),t(2,p="");else{const f="cdf32a17-e40f-4a84-b683-f66aa1105793",O="http://localhost:11111/auth/callback",w="read-repos",L=Math.random().toString(36).substring(2,15),S=`https://huggingface.co/oauth/authorize?client_id=${f}&redirect_uri=${encodeURIComponent(O)}&scope=${w}&response_type=code&state=${L}`;sessionStorage.setItem("oauth_state",L),window.location.href=S}}const I=()=>t(0,d="tts");return i.$$set=f=>{"$$scope"in f&&t(4,u=f.$$scope)},[d,n,p,g,u,l,I]}class Ot extends mt{constructor(e){super(),gt(this,e,Ht,Et,_t,{})}}export{Ot as component};
|
hfstudio/static/_app/immutable/nodes/1.DRlzjau5.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{S as x,i as S,s as j,n as u,d as c,a as h,b as _,c as d,e as v,f as g,g as b,h as k,j as E,t as $,k as q,l as y}from"../chunks/9DiovRey.js";import"../chunks/IHki7fMi.js";import{p as C}from"../chunks/BHHrQo_1.js";function H(p){var f;let a,s=p[0].status+"",r,n,o,i=((f=p[0].error)==null?void 0:f.message)+"",m;return{c(){a=E("h1"),r=$(s),n=q(),o=E("p"),m=$(i)},l(e){a=v(e,"H1",{});var t=g(a);r=b(t,s),t.forEach(c),n=k(e),o=v(e,"P",{});var l=g(o);m=b(l,i),l.forEach(c)},m(e,t){_(e,a,t),d(a,r),_(e,n,t),_(e,o,t),d(o,m)},p(e,[t]){var l;t&1&&s!==(s=e[0].status+"")&&h(r,s),t&1&&i!==(i=((l=e[0].error)==null?void 0:l.message)+"")&&h(m,i)},i:u,o:u,d(e){e&&(c(a),c(n),c(o))}}}function P(p,a,s){let r;return y(p,C,n=>s(0,r=n)),[r]}class B extends x{constructor(a){super(),S(this,a,P,H,j,{})}}export{B as component};
|
hfstudio/static/_app/immutable/nodes/2.BSR-L-2e.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import{S as Ye,i as Ze,s as Me,v as ft,O as V,d as f,P as Vl,x as w,y as k,z as ct,A as dt,B as mt,Q as Al,b as G,c as o,R as vn,f as m,E as Ut,T as bn,U as Yl,V as R,I as B,K as U,L as O,J as x,w as xl,o as i,D as dl,W as yt,a as ut,u as P,e as c,g as I,h as p,q as Qe,j as d,t as D,k as v,n as Kt,C as ml,M as cl,N as kn,X as Dl,m as Zl}from"../chunks/9DiovRey.js";import"../chunks/IHki7fMi.js";function Bt(a){return(a==null?void 0:a.length)!==void 0?a:Array.from(a)}function et(a,e){const s={},t={},l={$$scope:1};let n=a.length;for(;n--;){const r=a[n],u=e[n];if(u){for(const g in r)g in u||(t[g]=1);for(const g in u)l[g]||(s[g]=u[g],l[g]=1);a[n]=u}else for(const g in r)l[g]=1}for(const r in t)r in s||(s[r]=void 0);return s}function wt(a){return typeof a=="object"&&a!==null?a:{}}/**
|
| 2 |
+
* @license lucide-svelte v0.303.0 - ISC
|
| 3 |
+
|
| 4 |
+
This source code is licensed under the ISC license.
|
| 5 |
+
See the LICENSE file in the root directory of this source tree.
|
| 6 |
+
*/const en={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};function tn(a,e,s){const t=a.slice();return t[10]=e[s][0],t[11]=e[s][1],t}function Ul(a){let e,s=[a[11]],t={};for(let l=0;l<s.length;l+=1)t=V(t,s[l]);return{c(){e=bn(a[10]),this.h()},l(l){e=vn(l,a[10],{}),m(e).forEach(f),this.h()},h(){Al(e,t)},m(l,n){G(l,e,n)},p(l,n){Al(e,t=et(s,[n&32&&l[11]]))},d(l){l&&f(e)}}}function ln(a){let e=a[10],s,t=a[10]&&Ul(a);return{c(){t&&t.c(),s=Ut()},l(l){t&&t.l(l),s=Ut()},m(l,n){t&&t.m(l,n),G(l,s,n)},p(l,n){l[10]?e?Me(e,l[10])?(t.d(1),t=Ul(l),e=l[10],t.c(),t.m(s.parentNode,s)):t.p(l,n):(t=Ul(l),e=l[10],t.c(),t.m(s.parentNode,s)):e&&(t.d(1),t=null,e=l[10])},d(l){l&&f(s),t&&t.d(l)}}}function yn(a){let e,s,t,l,n,r=Bt(a[5]),u=[];for(let _=0;_<r.length;_+=1)u[_]=ln(tn(a,r,_));const g=a[9].default,E=ft(g,a,a[8],null);let N=[en,a[6],{width:a[2]},{height:a[2]},{stroke:a[1]},{"stroke-width":t=a[4]?Number(a[3])*24/Number(a[2]):a[3]},{class:l=`lucide-icon lucide lucide-${a[0]} ${a[7].class??""}`}],T={};for(let _=0;_<N.length;_+=1)T=V(T,N[_]);return{c(){e=bn("svg");for(let _=0;_<u.length;_+=1)u[_].c();s=Ut(),E&&E.c(),this.h()},l(_){e=vn(_,"svg",{width:!0,height:!0,stroke:!0,"stroke-width":!0,class:!0});var b=m(e);for(let $=0;$<u.length;$+=1)u[$].l(b);s=Ut(),E&&E.l(b),b.forEach(f),this.h()},h(){Al(e,T)},m(_,b){G(_,e,b);for(let $=0;$<u.length;$+=1)u[$]&&u[$].m(e,null);o(e,s),E&&E.m(e,null),n=!0},p(_,[b]){if(b&32){r=Bt(_[5]);let $;for($=0;$<r.length;$+=1){const S=tn(_,r,$);u[$]?u[$].p(S,b):(u[$]=ln(S),u[$].c(),u[$].m(e,s))}for(;$<u.length;$+=1)u[$].d(1);u.length=r.length}E&&E.p&&(!n||b&256)&&ct(E,g,_,_[8],n?mt(g,_[8],b,null):dt(_[8]),null),Al(e,T=et(N,[en,b&64&&_[6],(!n||b&4)&&{width:_[2]},(!n||b&4)&&{height:_[2]},(!n||b&2)&&{stroke:_[1]},(!n||b&28&&t!==(t=_[4]?Number(_[3])*24/Number(_[2]):_[3]))&&{"stroke-width":t},(!n||b&129&&l!==(l=`lucide-icon lucide lucide-${_[0]} ${_[7].class??""}`))&&{class:l}]))},i(_){n||(k(E,_),n=!0)},o(_){w(E,_),n=!1},d(_){_&&f(e),Vl(u,_),E&&E.d(_)}}}function wn(a,e,s){const t=["name","color","size","strokeWidth","absoluteStrokeWidth","iconNode"];let l=Yl(e,t),{$$slots:n={},$$scope:r}=e,{name:u}=e,{color:g="currentColor"}=e,{size:E=24}=e,{strokeWidth:N=2}=e,{absoluteStrokeWidth:T=!1}=e,{iconNode:_}=e;return a.$$set=b=>{s(7,e=V(V({},e),R(b))),s(6,l=Yl(e,t)),"name"in b&&s(0,u=b.name),"color"in b&&s(1,g=b.color),"size"in b&&s(2,E=b.size),"strokeWidth"in b&&s(3,N=b.strokeWidth),"absoluteStrokeWidth"in b&&s(4,T=b.absoluteStrokeWidth),"iconNode"in b&&s(5,_=b.iconNode),"$$scope"in b&&s(8,r=b.$$scope)},e=R(e),[u,g,E,N,T,_,l,e,r,n]}class Et extends Ye{constructor(e){super(),Ze(this,e,wn,yn,Me,{name:0,color:1,size:2,strokeWidth:3,absoluteStrokeWidth:4,iconNode:5})}}function En(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Nn(a){let e,s;const t=[{name:"alert-circle"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[En]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function Tn(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["circle",{cx:"12",cy:"12",r:"10"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class In extends Ye{constructor(e){super(),Ze(this,e,Tn,Nn,Me,{})}}function Dn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Vn(a){let e,s;const t=[{name:"chevron-down"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Dn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function An(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["path",{d:"m6 9 6 6 6-6"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class Cn extends Ye{constructor(e){super(),Ze(this,e,An,Vn,Me,{})}}function Sn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Pn(a){let e,s;const t=[{name:"download"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Sn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function zn(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"}],["polyline",{points:"7 10 12 15 17 10"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class nn extends Ye{constructor(e){super(),Ze(this,e,zn,Pn,Me,{})}}function Bn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Un(a){let e,s;const t=[{name:"loader-2"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Bn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function On(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["path",{d:"M21 12a9 9 0 1 1-6.219-8.56"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class xn extends Ye{constructor(e){super(),Ze(this,e,On,Un,Me,{})}}function jn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Mn(a){let e,s;const t=[{name:"more-horizontal"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[jn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function Ln(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["circle",{cx:"12",cy:"12",r:"1"}],["circle",{cx:"19",cy:"12",r:"1"}],["circle",{cx:"5",cy:"12",r:"1"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class $n extends Ye{constructor(e){super(),Ze(this,e,Ln,Mn,Me,{})}}function Hn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Wn(a){let e,s;const t=[{name:"play"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Hn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function qn(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["polygon",{points:"5 3 19 12 5 21 5 3"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class jl extends Ye{constructor(e){super(),Ze(this,e,qn,Wn,Me,{})}}function Fn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Gn(a){let e,s;const t=[{name:"settings"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Fn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function Rn(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["path",{d:"M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"}],["circle",{cx:"12",cy:"12",r:"3"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class Kn extends Ye{constructor(e){super(),Ze(this,e,Rn,Gn,Me,{})}}function Xn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Jn(a){let e,s;const t=[{name:"share"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Xn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function Qn(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["path",{d:"M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"}],["polyline",{points:"16 6 12 2 8 6"}],["line",{x1:"12",x2:"12",y1:"2",y2:"15"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class Ol extends Ye{constructor(e){super(),Ze(this,e,Qn,Jn,Me,{})}}function Yn(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function Zn(a){let e,s;const t=[{name:"skip-back"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[Yn]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function es(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["polygon",{points:"19 20 9 12 19 4 19 20"}],["line",{x1:"5",x2:"5",y1:"19",y2:"5"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class ts extends Ye{constructor(e){super(),Ze(this,e,es,Zn,Me,{})}}function ls(a){let e;const s=a[2].default,t=ft(s,a,a[3],null);return{c(){t&&t.c()},l(l){t&&t.l(l)},m(l,n){t&&t.m(l,n),e=!0},p(l,n){t&&t.p&&(!e||n&8)&&ct(t,s,l,l[3],e?mt(s,l[3],n,null):dt(l[3]),null)},i(l){e||(k(t,l),e=!0)},o(l){w(t,l),e=!1},d(l){t&&t.d(l)}}}function ns(a){let e,s;const t=[{name:"skip-forward"},a[1],{iconNode:a[0]}];let l={$$slots:{default:[ls]},$$scope:{ctx:a}};for(let n=0;n<t.length;n+=1)l=V(l,t[n]);return e=new Et({props:l}),{c(){x(e.$$.fragment)},l(n){O(e.$$.fragment,n)},m(n,r){U(e,n,r),s=!0},p(n,[r]){const u=r&3?et(t,[t[0],r&2&&wt(n[1]),r&1&&{iconNode:n[0]}]):{};r&8&&(u.$$scope={dirty:r,ctx:n}),e.$set(u)},i(n){s||(k(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){B(e,n)}}}function ss(a,e,s){let{$$slots:t={},$$scope:l}=e;const n=[["polygon",{points:"5 4 15 12 5 20 5 4"}],["line",{x1:"19",x2:"19",y1:"5",y2:"19"}]];return a.$$set=r=>{s(1,e=V(V({},e),R(r))),"$$scope"in r&&s(3,l=r.$$scope)},e=R(e),[n,e,t,l]}class as extends Ye{constructor(e){super(),Ze(this,e,ss,ns,Me,{})}}function sn(a,e,s){const t=a.slice();return t[39]=e[s],t}function an(a,e,s){const t=a.slice();return t[42]=e[s],t}function rn(a,e,s){const t=a.slice();return t[42]=e[s],t}function on(a){let e,s,t,l,n='<p class="font-medium text-blue-900">To run locally:</p> <code class="text-xs bg-blue-100 px-1.5 py-0.5 rounded">pip install hfstudio</code> <span class="text-blue-700">and run</span> <code class="text-xs bg-blue-100 px-1.5 py-0.5 rounded">hfstudio</code> <span class="text-blue-700">from your terminal</span>',r;return s=new In({props:{size:18,class:"text-blue-600 mt-0.5 flex-shrink-0"}}),{c(){e=d("div"),x(s.$$.fragment),t=v(),l=d("div"),l.innerHTML=n,this.h()},l(u){e=c(u,"DIV",{class:!0});var g=m(e);O(s.$$.fragment,g),t=p(g),l=c(g,"DIV",{class:!0,"data-svelte-h":!0}),Qe(l)!=="svelte-p3cqs1"&&(l.innerHTML=n),g.forEach(f),this.h()},h(){i(l,"class","text-sm"),i(e,"class","mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg flex items-start gap-2")},m(u,g){G(u,e,g),U(s,e,null),o(e,t),o(e,l),r=!0},i(u){r||(k(s.$$.fragment,u),r=!0)},o(u){w(s.$$.fragment,u),r=!1},d(u){u&&f(e),B(s)}}}function un(a){let e,s="Clear",t,l;return{c(){e=d("button"),e.textContent=s,this.h()},l(n){e=c(n,"BUTTON",{class:!0,"data-svelte-h":!0}),Qe(e)!=="svelte-18e8snq"&&(e.textContent=s),this.h()},h(){i(e,"class","absolute top-4 right-6 text-sm text-gray-400 hover:text-gray-600")},m(n,r){G(n,e,r),t||(l=P(e,"click",a[26]),t=!0)},p:Kt,d(n){n&&f(e),t=!1,l()}}}function rs(a){let e,s,t;return e=new jl({props:{size:20}}),{c(){x(e.$$.fragment),s=D(`
|
| 7 |
+
Generate speech`)},l(l){O(e.$$.fragment,l),s=I(l,`
|
| 8 |
+
Generate speech`)},m(l,n){U(e,l,n),G(l,s,n),t=!0},i(l){t||(k(e.$$.fragment,l),t=!0)},o(l){w(e.$$.fragment,l),t=!1},d(l){l&&f(s),B(e,l)}}}function os(a){let e,s,t;return e=new xn({props:{size:20,class:"animate-spin"}}),{c(){x(e.$$.fragment),s=D(`
|
| 9 |
+
Generating...`)},l(l){O(e.$$.fragment,l),s=I(l,`
|
| 10 |
+
Generating...`)},m(l,n){U(e,l,n),G(l,s,n),t=!0},i(l){t||(k(e.$$.fragment,l),t=!0)},o(l){w(e.$$.fragment,l),t=!1},d(l){l&&f(s),B(e,l)}}}function fn(a){let e,s,t,l,n,r,u,g,E,N,T,_,b,$,S,ue,de,M="Share",K,j,W,Ee="Download",Le,X,Z,Ne,ee,L,me,Nt,J,Te,Ie=zt(a[12])+"",He,ht,We,Ue,pe,te,ae=zt(a[13])+"",q,Ot,fe,ce,re,tt,lt,De,Q,gt,nt,Ve,xt,Se,Ae,Oe=zt(a[12])+"",jt,Mt,ve,qe,Fe,Tt,It=zt(a[13])+"",he,xe,oe,be,_t,Lt,st,Pe,Ge,Ht,at,le,Wt,ne,Xt,Jt;S=new Ol({props:{size:14,class:"text-gray-600"}}),X=new nn({props:{size:14,class:"text-gray-600"}});const pt=[us,is],$e=[];function vt(y,z){return y[11]?0:1}L=vt(a),me=$e[L]=pt[L](a),re=new ts({props:{size:20,class:"text-gray-600"}});const ul=[cs,fs],rt=[];function bt(y,z){return y[11]?0:1}De=bt(a),Q=rt[De]=ul[De](a),Ve=new as({props:{size:20,class:"text-gray-600"}}),_t=new Ol({props:{size:14}}),Ge=new nn({props:{size:16,class:"text-gray-600"}}),le=new $n({props:{size:16,class:"text-gray-600"}});let F=a[6]&&cn(a);return{c(){e=d("div"),s=d("div"),t=d("div"),l=v(),n=d("div"),r=d("h3"),u=D(a[14]),g=v(),E=d("p"),N=D(a[1]),T=D(" • Created 1 second ago"),_=v(),b=d("div"),$=d("button"),x(S.$$.fragment),ue=v(),de=d("span"),de.textContent=M,K=v(),j=d("button"),W=d("span"),W.textContent=Ee,Le=v(),x(X.$$.fragment),Z=v(),Ne=d("div"),ee=d("button"),me.c(),Nt=v(),J=d("div"),Te=d("span"),He=D(Ie),ht=v(),We=d("div"),Ue=d("div"),pe=v(),te=d("span"),q=D(ae),Ot=v(),fe=d("div"),ce=d("button"),x(re.$$.fragment),tt=v(),lt=d("button"),Q.c(),gt=v(),nt=d("button"),x(Ve.$$.fragment),xt=v(),Se=d("div"),Ae=d("span"),jt=D(Oe),Mt=v(),ve=d("div"),qe=d("div"),Fe=v(),Tt=d("span"),he=D(It),xe=v(),oe=d("div"),be=d("button"),x(_t.$$.fragment),Lt=D(`
|
| 11 |
+
Share`),st=v(),Pe=d("button"),x(Ge.$$.fragment),Ht=v(),at=d("button"),x(le.$$.fragment),Wt=v(),F&&F.c(),this.h()},l(y){e=c(y,"DIV",{class:!0});var z=m(e);s=c(z,"DIV",{class:!0});var ze=m(s);t=c(ze,"DIV",{class:!0}),m(t).forEach(f),l=p(ze),n=c(ze,"DIV",{class:!0});var Y=m(n);r=c(Y,"H3",{class:!0});var Qt=m(r);u=I(Qt,a[14]),Qt.forEach(f),g=p(Y),E=c(Y,"P",{class:!0});var ot=m(E);N=I(ot,a[1]),T=I(ot," • Created 1 second ago"),ot.forEach(f),Y.forEach(f),_=p(ze),b=c(ze,"DIV",{class:!0});var je=m(b);$=c(je,"BUTTON",{class:!0});var Re=m($);O(S.$$.fragment,Re),ue=p(Re),de=c(Re,"SPAN",{class:!0,"data-svelte-h":!0}),Qe(de)!=="svelte-hbn8gl"&&(de.textContent=M),Re.forEach(f),K=p(je),j=c(je,"BUTTON",{class:!0});var qt=m(j);W=c(qt,"SPAN",{class:!0,"data-svelte-h":!0}),Qe(W)!=="svelte-h01aeg"&&(W.textContent=Ee),Le=p(qt),O(X.$$.fragment,qt),qt.forEach(f),je.forEach(f),ze.forEach(f),Z=p(z),Ne=c(z,"DIV",{class:!0});var Vt=m(Ne);ee=c(Vt,"BUTTON",{class:!0});var $t=m(ee);me.l($t),$t.forEach(f),Nt=p(Vt),J=c(Vt,"DIV",{class:!0});var Ke=m(J);Te=c(Ke,"SPAN",{class:!0});var Ft=m(Te);He=I(Ft,Ie),Ft.forEach(f),ht=p(Ke),We=c(Ke,"DIV",{class:!0});var Yt=m(We);Ue=c(Yt,"DIV",{class:!0,style:!0}),m(Ue).forEach(f),Yt.forEach(f),pe=p(Ke),te=c(Ke,"SPAN",{class:!0});var Zt=m(te);q=I(Zt,ae),Zt.forEach(f),Ke.forEach(f),Vt.forEach(f),Ot=p(z),fe=c(z,"DIV",{class:!0});var H=m(fe);ce=c(H,"BUTTON",{class:!0,title:!0});var el=m(ce);O(re.$$.fragment,el),el.forEach(f),tt=p(H),lt=c(H,"BUTTON",{class:!0});var kt=m(lt);Q.l(kt),kt.forEach(f),gt=p(H),nt=c(H,"BUTTON",{class:!0,title:!0});var it=m(nt);O(Ve.$$.fragment,it),it.forEach(f),xt=p(H),Se=c(H,"DIV",{class:!0});var Be=m(Se);Ae=c(Be,"SPAN",{class:!0});var fl=m(Ae);jt=I(fl,Oe),fl.forEach(f),Mt=p(Be),ve=c(Be,"DIV",{class:!0});var tl=m(ve);qe=c(tl,"DIV",{class:!0,style:!0}),m(qe).forEach(f),tl.forEach(f),Fe=p(Be),Tt=c(Be,"SPAN",{class:!0});var Dt=m(Tt);he=I(Dt,It),Dt.forEach(f),Be.forEach(f),xe=p(H),oe=c(H,"DIV",{class:!0});var Xe=m(oe);be=c(Xe,"BUTTON",{class:!0});var At=m(be);O(_t.$$.fragment,At),Lt=I(At,`
|
| 12 |
+
Share`),At.forEach(f),st=p(Xe),Pe=c(Xe,"BUTTON",{class:!0,title:!0});var ll=m(Pe);O(Ge.$$.fragment,ll),ll.forEach(f),Ht=p(Xe),at=c(Xe,"BUTTON",{class:!0,title:!0});var Ce=m(at);O(le.$$.fragment,Ce),Ce.forEach(f),Xe.forEach(f),H.forEach(f),Wt=p(z),F&&F.l(z),z.forEach(f),this.h()},h(){i(t,"class","w-2 h-2 bg-green-500 rounded-full"),i(r,"class","font-medium text-gray-900 text-sm"),i(E,"class","text-xs text-gray-500"),i(n,"class","flex-1"),i(de,"class","text-gray-700"),i($,"class","flex items-center gap-1.5 px-3 py-1.5 text-sm border border-gray-200 rounded-md hover:bg-gray-50 transition-colors"),i(W,"class","text-gray-700"),i(j,"class","flex items-center gap-1.5 px-3 py-1.5 text-sm border border-gray-200 rounded-md hover:bg-gray-50 transition-colors"),i(b,"class","flex items-center gap-2"),i(s,"class","flex items-center gap-3 mb-4"),i(ee,"class","w-8 h-8 bg-black rounded-full flex items-center justify-center hover:bg-gray-800 transition-colors"),i(Te,"class","text-xs text-gray-500 font-mono"),i(Ue,"class","h-full bg-gradient-to-r from-amber-400 to-orange-500 rounded-full transition-all"),cl(Ue,"width",a[12]/a[13]*100+"%"),i(We,"class","flex-1 h-1 bg-gray-200 rounded-full cursor-pointer"),i(te,"class","text-xs text-gray-500 font-mono"),i(J,"class","flex-1 flex items-center gap-2"),i(Ne,"class","flex items-center gap-3 mb-4"),i(ce,"class","p-2 hover:bg-gray-100 rounded-full"),i(ce,"title","Skip back"),i(lt,"class","w-12 h-12 bg-black rounded-full flex items-center justify-center hover:bg-gray-800 transition-colors"),i(nt,"class","p-2 hover:bg-gray-100 rounded-full"),i(nt,"title","Skip forward"),i(Ae,"class","text-xs text-gray-500 font-mono"),i(qe,"class","h-full bg-gradient-to-r from-amber-400 to-orange-500 rounded-full transition-all"),cl(qe,"width",a[12]/a[13]*100+"%"),i(ve,"class","flex-1 h-1 bg-gray-200 rounded-full"),i(Tt,"class","text-xs text-gray-500 font-mono"),i(Se,"class","flex-1 flex items-center gap-3"),i(be,"class","flex items-center gap-2 px-3 py-1.5 text-sm border border-gray-200 rounded-md hover:bg-gray-50"),i(Pe,"class","p-2 hover:bg-gray-100 rounded-md"),i(Pe,"title","Download"),i(at,"class","p-2 hover:bg-gray-100 rounded-md"),i(at,"title","More options"),i(oe,"class","flex items-center gap-2"),i(fe,"class","flex items-center gap-4 mb-4"),i(e,"class","p-4 border border-gray-200 rounded-lg bg-white")},m(y,z){G(y,e,z),o(e,s),o(s,t),o(s,l),o(s,n),o(n,r),o(r,u),o(n,g),o(n,E),o(E,N),o(E,T),o(s,_),o(s,b),o(b,$),U(S,$,null),o($,ue),o($,de),o(b,K),o(b,j),o(j,W),o(j,Le),U(X,j,null),o(e,Z),o(e,Ne),o(Ne,ee),$e[L].m(ee,null),o(Ne,Nt),o(Ne,J),o(J,Te),o(Te,He),o(J,ht),o(J,We),o(We,Ue),o(J,pe),o(J,te),o(te,q),o(e,Ot),o(e,fe),o(fe,ce),U(re,ce,null),o(fe,tt),o(fe,lt),rt[De].m(lt,null),o(fe,gt),o(fe,nt),U(Ve,nt,null),o(fe,xt),o(fe,Se),o(Se,Ae),o(Ae,jt),o(Se,Mt),o(Se,ve),o(ve,qe),o(Se,Fe),o(Se,Tt),o(Tt,he),o(fe,xe),o(fe,oe),o(oe,be),U(_t,be,null),o(be,Lt),o(oe,st),o(oe,Pe),U(Ge,Pe,null),o(oe,Ht),o(oe,at),U(le,at,null),o(e,Wt),F&&F.m(e,null),ne=!0,Xt||(Jt=[P($,"click",pn),P(j,"click",a[24]),P(ee,"click",a[19]),P(lt,"click",a[19]),P(be,"click",pn),P(Pe,"click",a[24])],Xt=!0)},p(y,z){(!ne||z[0]&16384)&&ut(u,y[14]),(!ne||z[0]&2)&&ut(N,y[1]);let ze=L;L=vt(y),L!==ze&&(ml(),w($e[ze],1,1,()=>{$e[ze]=null}),dl(),me=$e[L],me||(me=$e[L]=pt[L](y),me.c()),k(me,1),me.m(ee,null)),(!ne||z[0]&4096)&&Ie!==(Ie=zt(y[12])+"")&&ut(He,Ie),(!ne||z[0]&12288)&&cl(Ue,"width",y[12]/y[13]*100+"%"),(!ne||z[0]&8192)&&ae!==(ae=zt(y[13])+"")&&ut(q,ae);let Y=De;De=bt(y),De!==Y&&(ml(),w(rt[Y],1,1,()=>{rt[Y]=null}),dl(),Q=rt[De],Q||(Q=rt[De]=ul[De](y),Q.c()),k(Q,1),Q.m(lt,null)),(!ne||z[0]&4096)&&Oe!==(Oe=zt(y[12])+"")&&ut(jt,Oe),(!ne||z[0]&12288)&&cl(qe,"width",y[12]/y[13]*100+"%"),(!ne||z[0]&8192)&&It!==(It=zt(y[13])+"")&&ut(he,It),y[6]?F?F.p(y,z):(F=cn(y),F.c(),F.m(e,null)):F&&(F.d(1),F=null)},i(y){ne||(k(S.$$.fragment,y),k(X.$$.fragment,y),k(me),k(re.$$.fragment,y),k(Q),k(Ve.$$.fragment,y),k(_t.$$.fragment,y),k(Ge.$$.fragment,y),k(le.$$.fragment,y),ne=!0)},o(y){w(S.$$.fragment,y),w(X.$$.fragment,y),w(me),w(re.$$.fragment,y),w(Q),w(Ve.$$.fragment,y),w(_t.$$.fragment,y),w(Ge.$$.fragment,y),w(le.$$.fragment,y),ne=!1},d(y){y&&f(e),B(S),B(X),$e[L].d(),B(re),rt[De].d(),B(Ve),B(_t),B(Ge),B(le),F&&F.d(),Xt=!1,xl(Jt)}}}function is(a){let e,s;return e=new jl({props:{size:14,class:"text-white ml-0.5"}}),{c(){x(e.$$.fragment)},l(t){O(e.$$.fragment,t)},m(t,l){U(e,t,l),s=!0},i(t){s||(k(e.$$.fragment,t),s=!0)},o(t){w(e.$$.fragment,t),s=!1},d(t){B(e,t)}}}function us(a){let e;return{c(){e=d("div"),this.h()},l(s){e=c(s,"DIV",{class:!0}),m(e).forEach(f),this.h()},h(){i(e,"class","pause-filled text-white")},m(s,t){G(s,e,t)},i:Kt,o:Kt,d(s){s&&f(e)}}}function fs(a){let e,s;return e=new jl({props:{size:20,class:"text-white ml-0.5"}}),{c(){x(e.$$.fragment)},l(t){O(e.$$.fragment,t)},m(t,l){U(e,t,l),s=!0},i(t){s||(k(e.$$.fragment,t),s=!0)},o(t){w(e.$$.fragment,t),s=!1},d(t){B(e,t)}}}function cs(a){let e;return{c(){e=d("div"),this.h()},l(s){e=c(s,"DIV",{class:!0}),m(e).forEach(f),this.h()},h(){i(e,"class","pause-filled text-white scale-150")},m(s,t){G(s,e,t)},i:Kt,o:Kt,d(s){s&&f(e)}}}function cn(a){let e,s,t,l;return{c(){e=d("audio"),this.h()},l(n){e=c(n,"AUDIO",{src:!0,style:!0}),m(e).forEach(f),this.h()},h(){Zl(e.src,s=a[6])||i(e,"src",s),cl(e,"display","none")},m(n,r){G(n,e,r),a[30](e),t||(l=[P(e,"loadedmetadata",a[20]),P(e,"timeupdate",a[21]),P(e,"play",a[22]),P(e,"pause",a[23])],t=!0)},p(n,r){r[0]&64&&!Zl(e.src,s=n[6])&&i(e,"src",s)},d(n){n&&f(e),a[30](null),t=!1,xl(l)}}}function dn(a){let e=a[42].name+"",s,t,l=a[42].badge&&ds(a);return{c(){s=D(e),l&&l.c(),t=Ut()},l(n){s=I(n,e),l&&l.l(n),t=Ut()},m(n,r){G(n,s,r),l&&l.m(n,r),G(n,t,r)},p(n,r){n[42].badge&&l.p(n,r)},d(n){n&&(f(s),f(t)),l&&l.d(n)}}}function ds(a){let e,s,t,l=a[42].badge+"",n,r;return{c(){e=D(" "),s=d("span"),t=D("("),n=D(l),r=D(")"),this.h()},l(u){e=I(u," "),s=c(u,"SPAN",{class:!0});var g=m(s);t=I(g,"("),n=I(g,l),r=I(g,")"),g.forEach(f),this.h()},h(){i(s,"class","text-xs text-gray-500")},m(u,g){G(u,e,g),G(u,s,g),o(s,t),o(s,n),o(s,r)},p:Kt,d(u){u&&(f(e),f(s))}}}function mn(a){let e,s=a[42].name===a[2]&&dn(a);return{c(){s&&s.c(),e=Ut()},l(t){s&&s.l(t),e=Ut()},m(t,l){s&&s.m(t,l),G(t,e,l)},p(t,l){t[42].name===t[2]?s?s.p(t,l):(s=dn(t),s.c(),s.m(e.parentNode,e)):s&&(s.d(1),s=null)},d(t){t&&f(e),s&&s.d(t)}}}function hn(a){let e,s=Bt(a[16]),t=[];for(let l=0;l<s.length;l+=1)t[l]=gn(an(a,s,l));return{c(){e=d("div");for(let l=0;l<t.length;l+=1)t[l].c();this.h()},l(l){e=c(l,"DIV",{class:!0});var n=m(e);for(let r=0;r<t.length;r+=1)t[r].l(n);n.forEach(f),this.h()},h(){i(e,"class","absolute top-full left-0 right-0 mt-1 bg-white border border-gray-200 rounded-lg shadow-lg z-10")},m(l,n){G(l,e,n);for(let r=0;r<t.length;r+=1)t[r]&&t[r].m(e,null)},p(l,n){if(n[0]&65556){s=Bt(l[16]);let r;for(r=0;r<s.length;r+=1){const u=an(l,s,r);t[r]?t[r].p(u,n):(t[r]=gn(u),t[r].c(),t[r].m(e,null))}for(;r<t.length;r+=1)t[r].d(1);t.length=s.length}},d(l){l&&f(e),Vl(t,l)}}}function ms(a){let e,s,t,l=a[42].badge+"",n,r;return{c(){e=D(" "),s=d("span"),t=D("("),n=D(l),r=D(")"),this.h()},l(u){e=I(u," "),s=c(u,"SPAN",{class:!0});var g=m(s);t=I(g,"("),n=I(g,l),r=I(g,")"),g.forEach(f),this.h()},h(){i(s,"class","text-xs text-gray-500")},m(u,g){G(u,e,g),G(u,s,g),o(s,t),o(s,n),o(s,r)},p:Kt,d(u){u&&(f(e),f(s))}}}function gn(a){let e,s=a[42].name+"",t,l,n,r,u,g=a[42].badge&&ms(a);function E(){return a[32](a[42])}return{c(){e=d("button"),t=D(s),g&&g.c(),l=v(),this.h()},l(N){e=c(N,"BUTTON",{class:!0});var T=m(e);t=I(T,s),g&&g.l(T),l=p(T),T.forEach(f),this.h()},h(){i(e,"class",n="w-full px-3 py-2 text-left hover:bg-gray-50 transition-colors text-sm "+(a[42].name===a[2]?"bg-gray-100":""))},m(N,T){G(N,e,T),o(e,t),g&&g.m(e,null),o(e,l),r||(u=P(e,"click",E),r=!0)},p(N,T){a=N,a[42].badge&&g.p(a,T),T[0]&4&&n!==(n="w-full px-3 py-2 text-left hover:bg-gray-50 transition-colors text-sm "+(a[42].name===a[2]?"bg-gray-100":""))&&i(e,"class",n)},d(N){N&&f(e),g&&g.d(),r=!1,u()}}}function _n(a){let e,s,t,l=a[39].name[0]+"",n,r,u,g=a[39].name+"",E,N,T,_=a[39].description+"",b,$,S,ue,de;function M(){return a[33](a[39])}return{c(){e=d("button"),s=d("div"),t=d("div"),n=D(l),r=v(),u=d("span"),E=D(g),N=v(),T=d("div"),b=D(_),$=v(),this.h()},l(K){e=c(K,"BUTTON",{class:!0});var j=m(e);s=c(j,"DIV",{class:!0});var W=m(s);t=c(W,"DIV",{class:!0});var Ee=m(t);n=I(Ee,l),Ee.forEach(f),r=p(W),u=c(W,"SPAN",{class:!0});var Le=m(u);E=I(Le,g),Le.forEach(f),W.forEach(f),N=p(j),T=c(j,"DIV",{class:!0});var X=m(T);b=I(X,_),X.forEach(f),$=p(j),j.forEach(f),this.h()},h(){i(t,"class","w-8 h-8 bg-gradient-to-br from-amber-400 to-orange-500 rounded-full flex items-center justify-center text-white text-xs font-medium"),i(u,"class","text-sm font-medium"),i(s,"class","flex items-center gap-2"),i(T,"class","text-xs text-gray-500"),i(e,"class",S="w-full flex items-center justify-between p-2 rounded-md hover:bg-gray-50 transition-colors text-left "+(a[39].name===a[1]?"bg-gray-100":""))},m(K,j){G(K,e,j),o(e,s),o(s,t),o(t,n),o(s,r),o(s,u),o(u,E),o(e,N),o(e,T),o(T,b),o(e,$),ue||(de=P(e,"click",M),ue=!0)},p(K,j){a=K,j[0]&2&&S!==(S="w-full flex items-center justify-between p-2 rounded-md hover:bg-gray-50 transition-colors text-left "+(a[39].name===a[1]?"bg-gray-100":""))&&i(e,"class",S)},d(K){K&&f(e),ue=!1,de()}}}function hs(a){let e,s,t,l,n,r,u,g,E,N,T,_,b,$,S,ue,de,M,K,j,W,Ee,Le,X,Z,Ne,ee,L,me,Nt,J,Te,Ie,He=a[0].length+"",ht,We,Ue,pe,te,ae,q,Ot,fe,ce,re,tt,lt="Model",De,Q,gt,nt,Ve,xt,Se,Ae,Oe,jt='<h3 class="font-medium text-gray-900">Voice</h3>',Mt,ve,qe,Fe,Tt='<div class="flex items-center gap-2"><div class="w-8 h-8 bg-gray-400 rounded-full flex items-center justify-center text-white text-xs font-medium">+</div> <span class="text-sm font-medium text-gray-600">Clone your voice</span></div> <div class="text-xs text-gray-400">(coming soon)</div>',It,he,xe,oe,be,_t="Speed",Lt,st,Pe=a[7].toFixed(1)+"",Ge,Ht,at,le,Wt,ne,Xt="<span>0.5x</span> <span>2.0x</span>",Jt,pt,$e,vt,ul="Stability",rt,bt,F=(a[8]*100).toFixed(0)+"",y,z,ze,Y,Qt,ot,je,Re,qt="Similarity",Vt,$t,Ke=(a[9]*100).toFixed(0)+"",Ft,Yt,Zt,H,el,kt,it,Be,fl="Style",tl,Dt,Xe=a[10]===0?"None":"Exaggerated",At,ll,Ce,Je,Cl,Ml;ue=new Kn({props:{size:18,class:"text-gray-600"}}),K=new Ol({props:{size:18,class:"text-gray-600"}}),Ee=new $n({props:{size:18,class:"text-gray-600"}});let ie=a[3]==="local"&&on(),ke=a[0]&&un(a);const Ll=[os,rs],Gt=[];function Hl(h,A){return h[5]?0:1}te=Hl(a),ae=Gt[te]=Ll[te](a);let se=a[6]&&fn(a),nl=Bt(a[16]),ge=[];for(let h=0;h<nl.length;h+=1)ge[h]=mn(rn(a,nl,h));Ve=new Cn({props:{size:16,class:"text-gray-500"}});let ye=a[4]&&hn(a),sl=Bt(a[17]),_e=[];for(let h=0;h<sl.length;h+=1)_e[h]=_n(sn(a,sl,h));return{c(){e=d("div"),s=d("header"),t=d("div"),l=d("div"),n=d("div"),r=d("button"),u=D("API"),E=v(),N=d("button"),T=D("Local"),b=v(),$=d("div"),S=d("button"),x(ue.$$.fragment),de=v(),M=d("button"),x(K.$$.fragment),j=v(),W=d("button"),x(Ee.$$.fragment),Le=v(),X=d("div"),Z=d("div"),ie&&ie.c(),Ne=v(),ee=d("div"),L=d("textarea"),me=v(),ke&&ke.c(),Nt=v(),J=d("div"),Te=d("div"),Ie=d("span"),ht=D(He),We=D(" / 5,000 characters"),Ue=v(),pe=d("button"),ae.c(),Ot=v(),se&&se.c(),fe=v(),ce=d("div"),re=d("div"),tt=d("h3"),tt.textContent=lt,De=v(),Q=d("button"),gt=d("span");for(let h=0;h<ge.length;h+=1)ge[h].c();nt=v(),x(Ve.$$.fragment),xt=v(),ye&&ye.c(),Se=v(),Ae=d("div"),Oe=d("div"),Oe.innerHTML=jt,Mt=v(),ve=d("div");for(let h=0;h<_e.length;h+=1)_e[h].c();qe=v(),Fe=d("button"),Fe.innerHTML=Tt,It=v(),he=d("div"),xe=d("div"),oe=d("div"),be=d("label"),be.textContent=_t,Lt=v(),st=d("span"),Ge=D(Pe),Ht=D("x"),at=v(),le=d("input"),Wt=v(),ne=d("div"),ne.innerHTML=Xt,Jt=v(),pt=d("div"),$e=d("div"),vt=d("label"),vt.textContent=ul,rt=v(),bt=d("span"),y=D(F),z=D("%"),ze=v(),Y=d("input"),Qt=v(),ot=d("div"),je=d("div"),Re=d("label"),Re.textContent=qt,Vt=v(),$t=d("span"),Ft=D(Ke),Yt=D("%"),Zt=v(),H=d("input"),el=v(),kt=d("div"),it=d("div"),Be=d("label"),Be.textContent=fl,tl=v(),Dt=d("span"),At=D(Xe),ll=v(),Ce=d("input"),this.h()},l(h){e=c(h,"DIV",{class:!0});var A=m(e);s=c(A,"HEADER",{class:!0});var we=m(s);t=c(we,"DIV",{class:!0});var C=m(t);l=c(C,"DIV",{class:!0});var Ct=m(l);n=c(Ct,"DIV",{class:!0});var hl=m(n);r=c(hl,"BUTTON",{class:!0});var Wl=m(r);u=I(Wl,"API"),Wl.forEach(f),E=p(hl),N=c(hl,"BUTTON",{class:!0});var ql=m(N);T=I(ql,"Local"),ql.forEach(f),hl.forEach(f),Ct.forEach(f),b=p(C),$=c(C,"DIV",{class:!0});var al=m($);S=c(al,"BUTTON",{class:!0,title:!0});var Fl=m(S);O(ue.$$.fragment,Fl),Fl.forEach(f),de=p(al),M=c(al,"BUTTON",{class:!0,title:!0});var Gl=m(M);O(K.$$.fragment,Gl),Gl.forEach(f),j=p(al),W=c(al,"BUTTON",{class:!0,title:!0});var Rl=m(W);O(Ee.$$.fragment,Rl),Rl.forEach(f),al.forEach(f),C.forEach(f),we.forEach(f),Le=p(A),X=c(A,"DIV",{class:!0});var gl=m(X);Z=c(gl,"DIV",{class:!0});var St=m(Z);ie&&ie.l(St),Ne=p(St),ee=c(St,"DIV",{class:!0});var _l=m(ee);L=c(_l,"TEXTAREA",{class:!0,placeholder:!0}),m(L).forEach(f),me=p(_l),ke&&ke.l(_l),_l.forEach(f),Nt=p(St),J=c(St,"DIV",{class:!0});var pl=m(J);Te=c(pl,"DIV",{class:!0});var Kl=m(Te);Ie=c(Kl,"SPAN",{class:!0});var Sl=m(Ie);ht=I(Sl,He),We=I(Sl," / 5,000 characters"),Sl.forEach(f),Kl.forEach(f),Ue=p(pl),pe=c(pl,"BUTTON",{class:!0});var Xl=m(pe);ae.l(Xl),Xl.forEach(f),pl.forEach(f),Ot=p(St),se&&se.l(St),St.forEach(f),fe=p(gl),ce=c(gl,"DIV",{class:!0});var rl=m(ce);re=c(rl,"DIV",{class:!0});var ol=m(re);tt=c(ol,"H3",{class:!0,"data-svelte-h":!0}),Qe(tt)!=="svelte-b3hs3r"&&(tt.textContent=lt),De=p(ol),Q=c(ol,"BUTTON",{class:!0});var vl=m(Q);gt=c(vl,"SPAN",{});var Jl=m(gt);for(let Rt=0;Rt<ge.length;Rt+=1)ge[Rt].l(Jl);Jl.forEach(f),nt=p(vl),O(Ve.$$.fragment,vl),vl.forEach(f),xt=p(ol),ye&&ye.l(ol),ol.forEach(f),Se=p(rl),Ae=c(rl,"DIV",{class:!0});var bl=m(Ae);Oe=c(bl,"DIV",{class:!0,"data-svelte-h":!0}),Qe(Oe)!=="svelte-182x1ki"&&(Oe.innerHTML=jt),Mt=p(bl),ve=c(bl,"DIV",{class:!0});var $l=m(ve);for(let Rt=0;Rt<_e.length;Rt+=1)_e[Rt].l($l);qe=p($l),Fe=c($l,"BUTTON",{class:!0,"data-svelte-h":!0}),Qe(Fe)!=="svelte-20x663"&&(Fe.innerHTML=Tt),$l.forEach(f),bl.forEach(f),It=p(rl),he=c(rl,"DIV",{class:!0});var Pt=m(he);xe=c(Pt,"DIV",{});var il=m(xe);oe=c(il,"DIV",{class:!0});var kl=m(oe);be=c(kl,"LABEL",{class:!0,"data-svelte-h":!0}),Qe(be)!=="svelte-9vcpiu"&&(be.textContent=_t),Lt=p(kl),st=c(kl,"SPAN",{class:!0});var Pl=m(st);Ge=I(Pl,Pe),Ht=I(Pl,"x"),Pl.forEach(f),kl.forEach(f),at=p(il),le=c(il,"INPUT",{type:!0,min:!0,max:!0,step:!0,class:!0}),Wt=p(il),ne=c(il,"DIV",{class:!0,"data-svelte-h":!0}),Qe(ne)!=="svelte-4ydm1m"&&(ne.innerHTML=Xt),il.forEach(f),Jt=p(Pt),pt=c(Pt,"DIV",{});var yl=m(pt);$e=c(yl,"DIV",{class:!0});var wl=m($e);vt=c(wl,"LABEL",{class:!0,"data-svelte-h":!0}),Qe(vt)!=="svelte-1lgjete"&&(vt.textContent=ul),rt=p(wl),bt=c(wl,"SPAN",{class:!0});var zl=m(bt);y=I(zl,F),z=I(zl,"%"),zl.forEach(f),wl.forEach(f),ze=p(yl),Y=c(yl,"INPUT",{type:!0,min:!0,max:!0,step:!0,class:!0}),yl.forEach(f),Qt=p(Pt),ot=c(Pt,"DIV",{});var El=m(ot);je=c(El,"DIV",{class:!0});var Nl=m(je);Re=c(Nl,"LABEL",{class:!0,"data-svelte-h":!0}),Qe(Re)!=="svelte-1ha0vec"&&(Re.textContent=qt),Vt=p(Nl),$t=c(Nl,"SPAN",{class:!0});var Bl=m($t);Ft=I(Bl,Ke),Yt=I(Bl,"%"),Bl.forEach(f),Nl.forEach(f),Zt=p(El),H=c(El,"INPUT",{type:!0,min:!0,max:!0,step:!0,class:!0}),El.forEach(f),el=p(Pt),kt=c(Pt,"DIV",{});var Tl=m(kt);it=c(Tl,"DIV",{class:!0});var Il=m(it);Be=c(Il,"LABEL",{class:!0,"data-svelte-h":!0}),Qe(Be)!=="svelte-1ata0tk"&&(Be.textContent=fl),tl=p(Il),Dt=c(Il,"SPAN",{class:!0});var Ql=m(Dt);At=I(Ql,Xe),Ql.forEach(f),Il.forEach(f),ll=p(Tl),Ce=c(Tl,"INPUT",{type:!0,min:!0,max:!0,step:!0,class:!0}),Tl.forEach(f),Pt.forEach(f),rl.forEach(f),gl.forEach(f),A.forEach(f),this.h()},h(){i(r,"class",g="px-3 py-1 text-sm font-medium rounded transition-colors "+(a[3]==="api"?"bg-white shadow-sm":"text-gray-600")),i(N,"class",_="px-3 py-1 text-sm font-medium rounded transition-colors "+(a[3]==="local"?"bg-white shadow-sm":"text-gray-600")),i(n,"class","flex items-center bg-gray-100 rounded-md p-0.5"),i(l,"class","flex items-center gap-3"),i(S,"class","p-2 hover:bg-gray-50 rounded-md"),i(S,"title","Settings"),i(M,"class","p-2 hover:bg-gray-50 rounded-md"),i(M,"title","Share"),i(W,"class","p-2 hover:bg-gray-50 rounded-md"),i(W,"title","More"),i($,"class","flex items-center gap-2"),i(t,"class","flex items-center justify-between px-4 py-2"),i(s,"class","border-b border-gray-200 bg-white"),i(L,"class","w-full h-full p-6 bg-white resize-none border-0 focus:outline-none text-gray-900 text-base leading-relaxed"),i(L,"placeholder","Enter text to convert to speech..."),i(ee,"class","flex-1 pb-24"),i(Ie,"class","text-sm text-gray-500"),i(Te,"class","flex items-center justify-between mb-3"),pe.disabled=q=a[5]||!a[0].trim(),i(pe,"class","w-full px-6 py-3 bg-gradient-to-r from-amber-400 to-orange-500 text-white rounded-lg font-medium hover:from-amber-500 hover:to-orange-600 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 shadow-sm"),i(J,"class","fixed bottom-0 left-56 right-80 p-4 bg-white border-t border-gray-200"),i(Z,"class","flex-1 flex flex-col p-6"),i(tt,"class","font-medium text-gray-900 mb-3"),i(Q,"class","w-full p-3 border border-gray-200 rounded-lg bg-white text-sm focus:outline-none focus:ring-2 focus:ring-amber-400 focus:border-transparent appearance-none bg-no-repeat bg-right pr-10 shadow-sm text-left flex items-center justify-between"),i(re,"class","mb-6 relative model-dropdown"),i(Oe,"class","mb-3"),i(Fe,"class","w-full flex items-center justify-between p-2 rounded-md opacity-50 cursor-not-allowed text-left"),Fe.disabled=!0,i(ve,"class","space-y-1"),i(Ae,"class","mb-6"),i(be,"class","text-sm font-medium text-gray-700"),i(st,"class","text-sm text-gray-500"),i(oe,"class","flex justify-between mb-1"),i(le,"type","range"),i(le,"min","0.5"),i(le,"max","2"),i(le,"step","0.1"),i(le,"class","w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"),i(ne,"class","flex justify-between text-xs text-gray-400 mt-1"),i(vt,"class","text-sm font-medium text-gray-700"),i(bt,"class","text-sm text-gray-500"),i($e,"class","flex justify-between mb-1"),i(Y,"type","range"),i(Y,"min","0"),i(Y,"max","1"),i(Y,"step","0.01"),i(Y,"class","w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"),i(Re,"class","text-sm font-medium text-gray-700"),i($t,"class","text-sm text-gray-500"),i(je,"class","flex justify-between mb-1"),i(H,"type","range"),i(H,"min","0"),i(H,"max","1"),i(H,"step","0.01"),i(H,"class","w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"),i(Be,"class","text-sm font-medium text-gray-700"),i(Dt,"class","text-sm text-gray-500"),i(it,"class","flex justify-between mb-1"),i(Ce,"type","range"),i(Ce,"min","0"),i(Ce,"max","1"),i(Ce,"step","1"),i(Ce,"class","w-full h-1.5 bg-gray-200 rounded-lg appearance-none cursor-pointer slider-hf"),i(he,"class","space-y-4 pt-4 border-t border-gray-200"),i(ce,"class","w-80 border-l border-gray-200 bg-white p-4 overflow-y-auto"),i(X,"class","flex-1 flex"),i(e,"class","flex flex-col h-full")},m(h,A){G(h,e,A),o(e,s),o(s,t),o(t,l),o(l,n),o(n,r),o(r,u),o(n,E),o(n,N),o(N,T),o(t,b),o(t,$),o($,S),U(ue,S,null),o($,de),o($,M),U(K,M,null),o($,j),o($,W),U(Ee,W,null),o(e,Le),o(e,X),o(X,Z),ie&&ie.m(Z,null),o(Z,Ne),o(Z,ee),o(ee,L),yt(L,a[0]),o(ee,me),ke&&ke.m(ee,null),o(Z,Nt),o(Z,J),o(J,Te),o(Te,Ie),o(Ie,ht),o(Ie,We),o(J,Ue),o(J,pe),Gt[te].m(pe,null),o(Z,Ot),se&&se.m(Z,null),o(X,fe),o(X,ce),o(ce,re),o(re,tt),o(re,De),o(re,Q),o(Q,gt);for(let we=0;we<ge.length;we+=1)ge[we]&&ge[we].m(gt,null);o(Q,nt),U(Ve,Q,null),o(re,xt),ye&&ye.m(re,null),o(ce,Se),o(ce,Ae),o(Ae,Oe),o(Ae,Mt),o(Ae,ve);for(let we=0;we<_e.length;we+=1)_e[we]&&_e[we].m(ve,null);o(ve,qe),o(ve,Fe),o(ce,It),o(ce,he),o(he,xe),o(xe,oe),o(oe,be),o(oe,Lt),o(oe,st),o(st,Ge),o(st,Ht),o(xe,at),o(xe,le),yt(le,a[7]),o(xe,Wt),o(xe,ne),o(he,Jt),o(he,pt),o(pt,$e),o($e,vt),o($e,rt),o($e,bt),o(bt,y),o(bt,z),o(pt,ze),o(pt,Y),yt(Y,a[8]),o(he,Qt),o(he,ot),o(ot,je),o(je,Re),o(je,Vt),o(je,$t),o($t,Ft),o($t,Yt),o(ot,Zt),o(ot,H),yt(H,a[9]),o(he,el),o(he,kt),o(kt,it),o(it,Be),o(it,tl),o(it,Dt),o(Dt,At),o(kt,ll),o(kt,Ce),yt(Ce,a[10]),Je=!0,Cl||(Ml=[P(r,"click",a[27]),P(N,"click",a[28]),P(L,"input",a[29]),P(pe,"click",a[18]),P(Q,"click",a[31]),P(le,"change",a[34]),P(le,"input",a[34]),P(Y,"change",a[35]),P(Y,"input",a[35]),P(H,"change",a[36]),P(H,"input",a[36]),P(Ce,"change",a[37]),P(Ce,"input",a[37]),P(e,"click",a[25])],Cl=!0)},p(h,A){(!Je||A[0]&8&&g!==(g="px-3 py-1 text-sm font-medium rounded transition-colors "+(h[3]==="api"?"bg-white shadow-sm":"text-gray-600")))&&i(r,"class",g),(!Je||A[0]&8&&_!==(_="px-3 py-1 text-sm font-medium rounded transition-colors "+(h[3]==="local"?"bg-white shadow-sm":"text-gray-600")))&&i(N,"class",_),h[3]==="local"?ie?A[0]&8&&k(ie,1):(ie=on(),ie.c(),k(ie,1),ie.m(Z,Ne)):ie&&(ml(),w(ie,1,1,()=>{ie=null}),dl()),A[0]&1&&yt(L,h[0]),h[0]?ke?ke.p(h,A):(ke=un(h),ke.c(),ke.m(ee,null)):ke&&(ke.d(1),ke=null),(!Je||A[0]&1)&&He!==(He=h[0].length+"")&&ut(ht,He);let we=te;if(te=Hl(h),te!==we&&(ml(),w(Gt[we],1,1,()=>{Gt[we]=null}),dl(),ae=Gt[te],ae||(ae=Gt[te]=Ll[te](h),ae.c()),k(ae,1),ae.m(pe,null)),(!Je||A[0]&33&&q!==(q=h[5]||!h[0].trim()))&&(pe.disabled=q),h[6]?se?(se.p(h,A),A[0]&64&&k(se,1)):(se=fn(h),se.c(),k(se,1),se.m(Z,null)):se&&(ml(),w(se,1,1,()=>{se=null}),dl()),A[0]&65540){nl=Bt(h[16]);let C;for(C=0;C<nl.length;C+=1){const Ct=rn(h,nl,C);ge[C]?ge[C].p(Ct,A):(ge[C]=mn(Ct),ge[C].c(),ge[C].m(gt,null))}for(;C<ge.length;C+=1)ge[C].d(1);ge.length=nl.length}if(h[4]?ye?ye.p(h,A):(ye=hn(h),ye.c(),ye.m(re,null)):ye&&(ye.d(1),ye=null),A[0]&131074){sl=Bt(h[17]);let C;for(C=0;C<sl.length;C+=1){const Ct=sn(h,sl,C);_e[C]?_e[C].p(Ct,A):(_e[C]=_n(Ct),_e[C].c(),_e[C].m(ve,qe))}for(;C<_e.length;C+=1)_e[C].d(1);_e.length=sl.length}(!Je||A[0]&128)&&Pe!==(Pe=h[7].toFixed(1)+"")&&ut(Ge,Pe),A[0]&128&&yt(le,h[7]),(!Je||A[0]&256)&&F!==(F=(h[8]*100).toFixed(0)+"")&&ut(y,F),A[0]&256&&yt(Y,h[8]),(!Je||A[0]&512)&&Ke!==(Ke=(h[9]*100).toFixed(0)+"")&&ut(Ft,Ke),A[0]&512&&yt(H,h[9]),(!Je||A[0]&1024)&&Xe!==(Xe=h[10]===0?"None":"Exaggerated")&&ut(At,Xe),A[0]&1024&&yt(Ce,h[10])},i(h){Je||(k(ue.$$.fragment,h),k(K.$$.fragment,h),k(Ee.$$.fragment,h),k(ie),k(ae),k(se),k(Ve.$$.fragment,h),Je=!0)},o(h){w(ue.$$.fragment,h),w(K.$$.fragment,h),w(Ee.$$.fragment,h),w(ie),w(ae),w(se),w(Ve.$$.fragment,h),Je=!1},d(h){h&&f(e),B(ue),B(K),B(Ee),ie&&ie.d(),ke&&ke.d(),Gt[te].d(),se&&se.d(),Vl(ge,h),B(Ve),ye&&ye.d(),Vl(_e,h),Cl=!1,xl(Ml)}}}function zt(a){const e=Math.floor(a/60),s=Math.floor(a%60);return`${e}:${s.toString().padStart(2,"0")}`}function pn(){console.log("Share audio")}function gs(a,e,s){let t=`Adam was playing outside when he heard a tiny meow! He looked around. Meow! There it was again! He tiptoed toward the bushes. Pushing the leaves aside, he gasped. A fluffy, orange kitten with big, curious eyes stared up at him. "Oh no! Where is your home?" Adam asked. The kitten just blinked and let out a tiny mew!
|
| 13 |
+
|
| 14 |
+
"Mama! I found a lost kitten!" Adam called as he carried the little furball inside. The kitten sniffed around, then jumped up—boing!—right onto the couch. "Oh!" Mama laughed. "Let's give it some milk while we find its owner." The kitten licked the milk happily and purred, prrrr! Adam giggled. "I think he likes it here!"
|
| 15 |
+
|
| 16 |
+
That evening, there was a knock at the door. A little girl stood there, eyes wide. "Have you seen my kitten, Biscuit?" she asked. Adam smiled and pointed. The kitten ran to her, mew mew mew! The girl hugged Biscuit tightly. "Thank you!" she said. As she left, Adam waved. "Goodbye, Biscuit!" he whispered. And in the quiet, he could still hear a happy prrrr!`,l="Novia",n="Chatterbox",r="api",u=!1,g=!1,E=null,N=1,T=.5,_=.75,b=0,$=!1,S=0,ue=0,de="",M=null;const K=[{id:"chatterbox",name:"Chatterbox",badge:"recommended"},{id:"kokoro",name:"Kokoro",badge:"faster but lower quality"}],j=[{id:"novia",name:"Novia",description:"Warm, conversational voice"},{id:"sarah",name:"Sarah",description:"Clear, professional tone"},{id:"alex",name:"Alex",description:"Friendly, approachable voice"},{id:"emma",name:"Emma",description:"Calm, soothing delivery"}];async function W(){t.trim()&&(s(5,g=!0),s(6,E=null),s(12,S=0),s(11,$=!1),s(14,de=t.length>30?t.substring(0,30)+"...":t),setTimeout(()=>{s(6,E="/samples/harvard.wav"),s(5,g=!1)},2e3))}function Ee(){M&&($?M.pause():M.play())}function Le(){M&&(s(13,ue=M.duration),M.play())}function X(){M&&s(12,S=M.currentTime)}function Z(){s(11,$=!0)}function Ne(){s(11,$=!1)}function ee(){if(E){const q=document.createElement("a");q.href=E,q.download="speech.wav",q.click()}}function L(q){q.target.closest(".model-dropdown")||s(4,u=!1)}function me(){s(0,t=""),s(6,E=null)}const Nt=()=>s(3,r="api"),J=()=>s(3,r="local");function Te(){t=this.value,s(0,t)}function Ie(q){kn[q?"unshift":"push"](()=>{M=q,s(15,M)})}const He=()=>s(4,u=!u),ht=q=>{s(2,n=q.name),s(4,u=!1)},We=q=>s(1,l=q.name);function Ue(){N=Dl(this.value),s(7,N)}function pe(){T=Dl(this.value),s(8,T)}function te(){_=Dl(this.value),s(9,_)}function ae(){b=Dl(this.value),s(10,b)}return[t,l,n,r,u,g,E,N,T,_,b,$,S,ue,de,M,K,j,W,Ee,Le,X,Z,Ne,ee,L,me,Nt,J,Te,Ie,He,ht,We,Ue,pe,te,ae]}class vs extends Ye{constructor(e){super(),Ze(this,e,gs,hs,Me,{},null,[-1,-1])}}export{vs as component};
|
hfstudio/static/_app/immutable/nodes/3.VqKPhUiT.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
import{S as V,i as j,s as q,n as A,d,b as y,c as h,m as N,o as f,p as O,h as E,e as p,f as b,q as H,k as P,j as _,l as z,r as M,a as S,u as R,g as I,t as C}from"../chunks/9DiovRey.js";import"../chunks/IHki7fMi.js";import{g as F}from"../chunks/Dh5qNFGZ.js";import{p as B}from"../chunks/BHHrQo_1.js";function G(m){let s,t,l,e,r;return{c(){s=_("div"),t=_("div"),l=P(),e=_("p"),r=C(m[0]),this.h()},l(u){s=p(u,"DIV",{class:!0});var o=b(s);t=p(o,"DIV",{class:!0}),b(t).forEach(d),l=E(o),e=p(o,"P",{});var n=b(e);r=I(n,m[0]),n.forEach(d),o.forEach(d),this.h()},h(){f(t,"class","animate-spin w-8 h-8 border-2 border-amber-400 border-t-transparent rounded-full mx-auto mb-2"),f(s,"class","text-gray-600 mb-4")},m(u,o){y(u,s,o),h(s,t),h(s,l),h(s,e),h(e,r)},p(u,o){o&1&&S(r,u[0])},d(u){u&&d(s)}}}function J(m){let s,t,l,e,r,u,o,n,k="Return to HFStudio",g,x;return{c(){s=_("div"),t=_("p"),l=C(m[0]),e=P(),r=_("p"),u=C(m[1]),o=P(),n=_("button"),n.textContent=k,this.h()},l(c){s=p(c,"DIV",{class:!0});var a=b(s);t=p(a,"P",{class:!0});var i=b(t);l=I(i,m[0]),i.forEach(d),e=E(a),r=p(a,"P",{class:!0});var v=b(r);u=I(v,m[1]),v.forEach(d),a.forEach(d),o=E(c),n=p(c,"BUTTON",{class:!0,"data-svelte-h":!0}),H(n)!=="svelte-lo2i5l"&&(n.textContent=k),this.h()},h(){f(t,"class","font-medium"),f(r,"class","text-sm mt-1"),f(s,"class","text-red-600 mb-4"),f(n,"class","px-4 py-2 bg-gradient-to-r from-amber-400 to-orange-500 text-white rounded-lg hover:from-amber-500 hover:to-orange-600 transition-colors")},m(c,a){y(c,s,a),h(s,t),h(t,l),h(s,e),h(s,r),h(r,u),y(c,o,a),y(c,n,a),g||(x=R(n,"click",m[2]),g=!0)},p(c,a){a&1&&S(l,c[0]),a&2&&S(u,c[1])},d(c){c&&(d(s),d(o),d(n)),g=!1,x()}}}function L(m){let s,t,l,e,r,u,o,n,k="HFStudio Authentication",g;function x(i,v){return i[1]?J:G}let c=x(m),a=c(m);return{c(){s=P(),t=_("div"),l=_("div"),e=_("div"),r=_("img"),o=P(),n=_("h1"),n.textContent=k,g=P(),a.c(),this.h()},l(i){O("svelte-17lku3q",document.head).forEach(d),s=E(i),t=p(i,"DIV",{class:!0});var D=b(t);l=p(D,"DIV",{class:!0});var T=b(l);e=p(T,"DIV",{class:!0});var w=b(e);r=p(w,"IMG",{src:!0,alt:!0,class:!0}),o=E(w),n=p(w,"H1",{class:!0,"data-svelte-h":!0}),H(n)!=="svelte-l7zisw"&&(n.textContent=k),g=E(w),a.l(w),w.forEach(d),T.forEach(d),D.forEach(d),this.h()},h(){document.title="Authenticating with Hugging Face - HFStudio",N(r.src,u="/assets/hf-logo.png")||f(r,"src",u),f(r,"alt","HF Logo"),f(r,"class","w-12 h-12 mx-auto mb-4"),f(n,"class","text-xl font-semibold mb-2"),f(e,"class","text-center"),f(l,"class","max-w-md w-full bg-white rounded-lg shadow-md p-6"),f(t,"class","min-h-screen flex items-center justify-center bg-gray-50")},m(i,v){y(i,s,v),y(i,t,v),h(t,l),h(l,e),h(e,r),h(e,o),h(e,n),h(e,g),a.m(e,null)},p(i,[v]){c===(c=x(i))&&a?a.p(i,v):(a.d(1),a=c(i),a&&(a.c(),a.m(e,null)))},i:A,o:A,d(i){i&&(d(s),d(t)),a.d()}}}function U(m,s,t){let l;z(m,B,o=>t(3,l=o));let e="Processing...",r=null;return M(async()=>{try{const o=l.url.searchParams.get("code"),n=l.url.searchParams.get("state"),k=l.url.searchParams.get("error"),g=l.url.searchParams.get("error_description");if(k){t(1,r=g||k),t(0,e="Authentication failed");return}if(!o){t(1,r="No authorization code received"),t(0,e="Authentication failed");return}const x=sessionStorage.getItem("oauth_state");if(x&&n!==x){t(1,r="Invalid state parameter"),t(0,e="Authentication failed");return}const c=await fetch("/api/auth/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:o})});if(!c.ok){const i=await c.text();throw console.error("Token exchange failed:",i),new Error(`Failed to exchange code for token: ${i}`)}const a=await c.json();localStorage.setItem("hf_access_token",a.access_token),t(0,e="Successfully authenticated!"),setTimeout(()=>{F("/")},2e3)}catch(o){t(1,r=o.message),t(0,e="Authentication failed")}}),[e,r,()=>F("/")]}class Y extends V{constructor(s){super(),j(this,s,U,L,q,{})}}export{Y as component};
|
hfstudio/static/_app/version.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"version":"1760660574318"}
|
hfstudio/static/assets/hf-logo.png
ADDED
|
Git LFS Details
|
hfstudio/static/assets/hf-studio-logo.png
ADDED
|
Git LFS Details
|
hfstudio/static/index.html
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<link rel="icon" href="/assets/hf-logo.png" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 7 |
+
<title>HFStudio - Text to Speech</title>
|
| 8 |
+
|
| 9 |
+
<link rel="modulepreload" href="/_app/immutable/entry/start.CYVq8xer.js">
|
| 10 |
+
<link rel="modulepreload" href="/_app/immutable/chunks/Dh5qNFGZ.js">
|
| 11 |
+
<link rel="modulepreload" href="/_app/immutable/chunks/9DiovRey.js">
|
| 12 |
+
<link rel="modulepreload" href="/_app/immutable/entry/app.DrVe4Aen.js">
|
| 13 |
+
<link rel="modulepreload" href="/_app/immutable/chunks/IHki7fMi.js">
|
| 14 |
+
</head>
|
| 15 |
+
<body data-sveltekit-preload-data="hover">
|
| 16 |
+
<div style="display: contents">
|
| 17 |
+
<script>
|
| 18 |
+
{
|
| 19 |
+
__sveltekit_pr8qbd = {
|
| 20 |
+
base: ""
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
const element = document.currentScript.parentElement;
|
| 24 |
+
|
| 25 |
+
Promise.all([
|
| 26 |
+
import("/_app/immutable/entry/start.CYVq8xer.js"),
|
| 27 |
+
import("/_app/immutable/entry/app.DrVe4Aen.js")
|
| 28 |
+
]).then(([kit, app]) => {
|
| 29 |
+
kit.start(app, element);
|
| 30 |
+
});
|
| 31 |
+
}
|
| 32 |
+
</script>
|
| 33 |
+
</div>
|
| 34 |
+
</body>
|
| 35 |
+
</html>
|
hfstudio/static/samples/harvard.wav
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:971b4163670445c415c6b0fb6813c38093409ecac2f6b4d429ae3574d24ad470
|
| 3 |
+
size 3249924
|
package-lock.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "hfstudio",
|
| 3 |
+
"lockfileVersion": 3,
|
| 4 |
+
"requires": true,
|
| 5 |
+
"packages": {}
|
| 6 |
+
}
|
pyproject.toml
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=61.0", "wheel"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "hfstudio"
|
| 7 |
+
dynamic = ["version"]
|
| 8 |
+
description = "Local and API-based Text-to-Speech Studio"
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
requires-python = ">=3.8"
|
| 11 |
+
license = {text = "MIT"}
|
| 12 |
+
authors = [
|
| 13 |
+
{name = "HFStudio Team"}
|
| 14 |
+
]
|
| 15 |
+
classifiers = [
|
| 16 |
+
"Development Status :: 3 - Alpha",
|
| 17 |
+
"Intended Audience :: Developers",
|
| 18 |
+
"Topic :: Multimedia :: Sound/Audio :: Speech",
|
| 19 |
+
"License :: OSI Approved :: MIT License",
|
| 20 |
+
"Programming Language :: Python :: 3",
|
| 21 |
+
"Programming Language :: Python :: 3.8",
|
| 22 |
+
"Programming Language :: Python :: 3.9",
|
| 23 |
+
"Programming Language :: Python :: 3.10",
|
| 24 |
+
"Programming Language :: Python :: 3.11",
|
| 25 |
+
]
|
| 26 |
+
dependencies = [
|
| 27 |
+
"fastapi>=0.100.0",
|
| 28 |
+
"uvicorn[standard]>=0.20.0",
|
| 29 |
+
"typer[all]>=0.9.0",
|
| 30 |
+
"pydantic>=2.0.0",
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
[project.optional-dependencies]
|
| 34 |
+
dev = [
|
| 35 |
+
"pytest>=7.0.0",
|
| 36 |
+
"pytest-asyncio>=0.21.0",
|
| 37 |
+
"black>=23.0.0",
|
| 38 |
+
"ruff>=0.1.0",
|
| 39 |
+
]
|
| 40 |
+
|
| 41 |
+
[project.scripts]
|
| 42 |
+
hfstudio = "hfstudio.cli:main"
|
| 43 |
+
|
| 44 |
+
[project.urls]
|
| 45 |
+
Homepage = "https://github.com/yourusername/hfstudio"
|
| 46 |
+
Repository = "https://github.com/yourusername/hfstudio"
|
| 47 |
+
|
| 48 |
+
[tool.setuptools]
|
| 49 |
+
packages = ["hfstudio"]
|
| 50 |
+
|
| 51 |
+
[tool.setuptools.package-data]
|
| 52 |
+
hfstudio = ["frontend/package.json", "static/**/*"]
|
| 53 |
+
|
| 54 |
+
[tool.setuptools.dynamic]
|
| 55 |
+
version = {attr = "hfstudio.__version__"}
|
| 56 |
+
|
| 57 |
+
[tool.ruff]
|
| 58 |
+
line-length = 88
|
| 59 |
+
target-version = "py38"
|
| 60 |
+
|
| 61 |
+
[tool.black]
|
| 62 |
+
line-length = 88
|
| 63 |
+
target-version = ['py38']
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi>=0.104.0
|
| 2 |
+
uvicorn[standard]>=0.24.0
|
| 3 |
+
pydantic>=2.0.0
|
| 4 |
+
httpx>=0.25.0
|
| 5 |
+
numpy>=1.24.0
|
| 6 |
+
soundfile>=0.12.0
|
| 7 |
+
typer>=0.9.0
|
| 8 |
+
rich>=13.0.0
|
requirements_space.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi>=0.104.0
|
| 2 |
+
uvicorn[standard]>=0.24.0
|
| 3 |
+
pydantic>=2.0.0
|
| 4 |
+
httpx>=0.25.0
|
| 5 |
+
numpy>=1.24.0
|
| 6 |
+
soundfile>=0.12.0
|
| 7 |
+
typer>=0.9.0
|
| 8 |
+
rich>=13.0.0
|