diff --git a/bedtime-story-generator/.gitignore b/bedtime-story-generator/.gitignore
new file mode 100644
index 00000000..a547bf36
--- /dev/null
+++ b/bedtime-story-generator/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/bedtime-story-generator/README.md b/bedtime-story-generator/README.md
new file mode 100644
index 00000000..9f0b3c82
--- /dev/null
+++ b/bedtime-story-generator/README.md
@@ -0,0 +1,19 @@
+---
+title: Bedtime Story Generator
+emoji: ✨
+colorFrom: yellow
+colorTo: purple
+sdk: static
+pinned: false
+license: apache-2.0
+short_description: Craft magical tales in seconds.
+app_build_command: npm run build
+app_file: dist/index.html
+thumbnail: >-
+ https://cdn-uploads.huggingface.co/production/uploads/61b253b7ac5ecaae3d1efe0c/Y7gInUdGdF_53fEGfWlbJ.png
+header: mini
+models:
+- onnx-community/gemma-3-270m-it-ONNX
+---
+
+Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
\ No newline at end of file
diff --git a/bedtime-story-generator/eslint.config.js b/bedtime-story-generator/eslint.config.js
new file mode 100644
index 00000000..f4616740
--- /dev/null
+++ b/bedtime-story-generator/eslint.config.js
@@ -0,0 +1,23 @@
+import js from "@eslint/js";
+import globals from "globals";
+import reactHooks from "eslint-plugin-react-hooks";
+import reactRefresh from "eslint-plugin-react-refresh";
+import tseslint from "typescript-eslint";
+import { globalIgnores } from "eslint/config";
+
+export default tseslint.config([
+ globalIgnores(["dist"]),
+ {
+ files: ["**/*.{ts,tsx}"],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs["recommended-latest"],
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ },
+]);
diff --git a/bedtime-story-generator/index.html b/bedtime-story-generator/index.html
new file mode 100644
index 00000000..0739f869
--- /dev/null
+++ b/bedtime-story-generator/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+ Bedtime Story Generator
+
+
+
+
+
+
diff --git a/bedtime-story-generator/package-lock.json b/bedtime-story-generator/package-lock.json
new file mode 100644
index 00000000..827013d0
--- /dev/null
+++ b/bedtime-story-generator/package-lock.json
@@ -0,0 +1,2985 @@
+{
+ "name": "bedtime-story-generator",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "bedtime-story-generator",
+ "version": "0.0.0",
+ "dependencies": {
+ "@huggingface/transformers": "^3.7.1",
+ "@tailwindcss/vite": "^4.1.11",
+ "kokoro-js": "^1.2.1",
+ "lucide-react": "^0.539.0",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "tailwindcss": "^4.1.11"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.32.0",
+ "@types/audioworklet": "^0.0.82",
+ "@types/react": "^19.1.9",
+ "@types/react-dom": "^19.1.7",
+ "@vitejs/plugin-react": "^4.7.0",
+ "@webgpu/types": "^0.1.64",
+ "eslint": "^9.32.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.20",
+ "globals": "^16.3.0",
+ "typescript": "~5.8.3",
+ "typescript-eslint": "^8.39.0",
+ "vite": "^7.1.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.0",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.27.3",
+ "@babel/helpers": "^7.27.6",
+ "@babel/parser": "^7.28.0",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.0",
+ "@babel/types": "^7.28.0",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.0",
+ "@babel/types": "^7.28.0",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.27.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.27.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.27.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.0",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.8",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.15.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.33.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.15.2",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@huggingface/jinja": {
+ "version": "0.5.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@huggingface/transformers": {
+ "version": "3.7.1",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@huggingface/jinja": "^0.5.1",
+ "onnxruntime-node": "1.21.0",
+ "onnxruntime-web": "1.22.0-dev.20250409-89f8206ba4",
+ "sharp": "^0.34.1"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.3",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.0",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.12",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.4",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.29",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.27",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.46.2",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.11",
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "enhanced-resolve": "^5.18.1",
+ "jiti": "^2.4.2",
+ "lightningcss": "1.30.1",
+ "magic-string": "^0.30.17",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.11"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.11",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.11",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.11",
+ "@tailwindcss/oxide-darwin-x64": "4.1.11",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.11",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.11",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.11",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.11",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.11",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.11"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.11",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/vite": {
+ "version": "4.1.11",
+ "license": "MIT",
+ "dependencies": {
+ "@tailwindcss/node": "4.1.11",
+ "@tailwindcss/oxide": "4.1.11",
+ "tailwindcss": "4.1.11"
+ },
+ "peerDependencies": {
+ "vite": "^5.2.0 || ^6 || ^7"
+ }
+ },
+ "node_modules/@types/audioworklet": {
+ "version": "0.0.82",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.2.1",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.10.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.7",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.39.0",
+ "@typescript-eslint/type-utils": "8.39.0",
+ "@typescript-eslint/utils": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.39.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.39.0",
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.39.0",
+ "@typescript-eslint/types": "^8.39.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0",
+ "@typescript-eslint/utils": "8.39.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.39.0",
+ "@typescript-eslint/tsconfig-utils": "8.39.0",
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.7.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.39.0",
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.39.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/@webgpu/types": {
+ "version": "0.1.64",
+ "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.64.tgz",
+ "integrity": "sha512-84kRIAGV46LJTlJZWxShiOrNL30A+9KokD7RB3dRCIqODFjodS5tCD5yyiZ8kIReGVZSDfA3XkkwyyOIF6K62A==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/boolean": {
+ "version": "3.2.0",
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.25.2",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001733",
+ "electron-to-chromium": "^1.5.199",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001733",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.199",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.3",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.8",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.8",
+ "@esbuild/android-arm": "0.25.8",
+ "@esbuild/android-arm64": "0.25.8",
+ "@esbuild/android-x64": "0.25.8",
+ "@esbuild/darwin-arm64": "0.25.8",
+ "@esbuild/darwin-x64": "0.25.8",
+ "@esbuild/freebsd-arm64": "0.25.8",
+ "@esbuild/freebsd-x64": "0.25.8",
+ "@esbuild/linux-arm": "0.25.8",
+ "@esbuild/linux-arm64": "0.25.8",
+ "@esbuild/linux-ia32": "0.25.8",
+ "@esbuild/linux-loong64": "0.25.8",
+ "@esbuild/linux-mips64el": "0.25.8",
+ "@esbuild/linux-ppc64": "0.25.8",
+ "@esbuild/linux-riscv64": "0.25.8",
+ "@esbuild/linux-s390x": "0.25.8",
+ "@esbuild/linux-x64": "0.25.8",
+ "@esbuild/netbsd-arm64": "0.25.8",
+ "@esbuild/netbsd-x64": "0.25.8",
+ "@esbuild/openbsd-arm64": "0.25.8",
+ "@esbuild/openbsd-x64": "0.25.8",
+ "@esbuild/openharmony-arm64": "0.25.8",
+ "@esbuild/sunos-x64": "0.25.8",
+ "@esbuild/win32-arm64": "0.25.8",
+ "@esbuild/win32-ia32": "0.25.8",
+ "@esbuild/win32-x64": "0.25.8"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.33.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.3.1",
+ "@eslint/core": "^0.15.2",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.33.0",
+ "@eslint/plugin-kit": "^0.3.5",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-react-hooks": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-react-refresh": {
+ "version": "0.4.20",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=8.40"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatbuffers": {
+ "version": "25.2.10",
+ "license": "Apache-2.0"
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/global-agent": {
+ "version": "3.0.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "es6-error": "^4.1.1",
+ "matcher": "^3.0.0",
+ "roarr": "^2.15.3",
+ "semver": "^7.3.2",
+ "serialize-error": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/global-agent/node_modules/semver": {
+ "version": "7.7.2",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/guid-typescript": {
+ "version": "1.0.9",
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "license": "MIT"
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jiti": {
+ "version": "2.5.1",
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "license": "ISC"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kokoro-js": {
+ "version": "1.2.1",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@huggingface/transformers": "^3.5.1",
+ "phonemizer": "^1.2.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.1",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.30.1",
+ "lightningcss-darwin-x64": "1.30.1",
+ "lightningcss-freebsd-x64": "1.30.1",
+ "lightningcss-linux-arm-gnueabihf": "1.30.1",
+ "lightningcss-linux-arm64-gnu": "1.30.1",
+ "lightningcss-linux-arm64-musl": "1.30.1",
+ "lightningcss-linux-x64-gnu": "1.30.1",
+ "lightningcss-linux-x64-musl": "1.30.1",
+ "lightningcss-win32-arm64-msvc": "1.30.1",
+ "lightningcss-win32-x64-msvc": "1.30.1"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.1",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.539.0",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/matcher": {
+ "version": "3.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/onnxruntime-common": {
+ "version": "1.21.0",
+ "license": "MIT"
+ },
+ "node_modules/onnxruntime-node": {
+ "version": "1.21.0",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "os": [
+ "win32",
+ "darwin",
+ "linux"
+ ],
+ "dependencies": {
+ "global-agent": "^3.0.0",
+ "onnxruntime-common": "1.21.0",
+ "tar": "^7.0.1"
+ }
+ },
+ "node_modules/onnxruntime-web": {
+ "version": "1.22.0-dev.20250409-89f8206ba4",
+ "license": "MIT",
+ "dependencies": {
+ "flatbuffers": "^25.1.24",
+ "guid-typescript": "^1.0.9",
+ "long": "^5.2.3",
+ "onnxruntime-common": "1.22.0-dev.20250409-89f8206ba4",
+ "platform": "^1.3.6",
+ "protobufjs": "^7.2.4"
+ }
+ },
+ "node_modules/onnxruntime-web/node_modules/onnxruntime-common": {
+ "version": "1.22.0-dev.20250409-89f8206ba4",
+ "license": "MIT"
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/phonemizer": {
+ "version": "1.2.1",
+ "license": "Apache-2.0"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/platform": {
+ "version": "1.3.6",
+ "license": "MIT"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/protobufjs": {
+ "version": "7.5.3",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "19.1.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.1",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.1"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/roarr": {
+ "version": "2.15.4",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "boolean": "^3.0.1",
+ "detect-node": "^2.0.4",
+ "globalthis": "^1.0.1",
+ "json-stringify-safe": "^5.0.1",
+ "semver-compare": "^1.0.0",
+ "sprintf-js": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.46.2",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.46.2",
+ "@rollup/rollup-android-arm64": "4.46.2",
+ "@rollup/rollup-darwin-arm64": "4.46.2",
+ "@rollup/rollup-darwin-x64": "4.46.2",
+ "@rollup/rollup-freebsd-arm64": "4.46.2",
+ "@rollup/rollup-freebsd-x64": "4.46.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.46.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.46.2",
+ "@rollup/rollup-linux-arm64-musl": "4.46.2",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.46.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.46.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.46.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.46.2",
+ "@rollup/rollup-linux-x64-gnu": "4.46.2",
+ "@rollup/rollup-linux-x64-musl": "4.46.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.46.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.46.2",
+ "@rollup/rollup-win32-x64-msvc": "4.46.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/semver-compare": {
+ "version": "1.0.0",
+ "license": "MIT"
+ },
+ "node_modules/serialize-error": {
+ "version": "7.0.1",
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.3",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.4",
+ "semver": "^7.7.2"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.3",
+ "@img/sharp-darwin-x64": "0.34.3",
+ "@img/sharp-libvips-darwin-arm64": "1.2.0",
+ "@img/sharp-libvips-darwin-x64": "1.2.0",
+ "@img/sharp-libvips-linux-arm": "1.2.0",
+ "@img/sharp-libvips-linux-arm64": "1.2.0",
+ "@img/sharp-libvips-linux-ppc64": "1.2.0",
+ "@img/sharp-libvips-linux-s390x": "1.2.0",
+ "@img/sharp-libvips-linux-x64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.0",
+ "@img/sharp-linux-arm": "0.34.3",
+ "@img/sharp-linux-arm64": "0.34.3",
+ "@img/sharp-linux-ppc64": "0.34.3",
+ "@img/sharp-linux-s390x": "0.34.3",
+ "@img/sharp-linux-x64": "0.34.3",
+ "@img/sharp-linuxmusl-arm64": "0.34.3",
+ "@img/sharp-linuxmusl-x64": "0.34.3",
+ "@img/sharp-wasm32": "0.34.3",
+ "@img/sharp-win32-arm64": "0.34.3",
+ "@img/sharp-win32-ia32": "0.34.3",
+ "@img/sharp-win32-x64": "0.34.3"
+ }
+ },
+ "node_modules/sharp/node_modules/semver": {
+ "version": "7.7.2",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.11",
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.2.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.4.3",
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "5.0.0",
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.6",
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.3",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.13.1",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.39.0",
+ "@typescript-eslint/parser": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0",
+ "@typescript-eslint/utils": "8.39.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.10.0",
+ "license": "MIT"
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.1.1",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.6",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.14"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/fdir": {
+ "version": "6.4.6",
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.3",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/bedtime-story-generator/package.json b/bedtime-story-generator/package.json
new file mode 100644
index 00000000..7e87f000
--- /dev/null
+++ b/bedtime-story-generator/package.json
@@ -0,0 +1,41 @@
+{
+ "name": "bedtime-story-generator",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@huggingface/transformers": "^3.7.1",
+ "@tailwindcss/vite": "^4.1.11",
+ "kokoro-js": "^1.2.1",
+ "lucide-react": "^0.539.0",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "tailwindcss": "^4.1.11"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.32.0",
+ "@types/audioworklet": "^0.0.82",
+ "@types/react": "^19.1.9",
+ "@types/react-dom": "^19.1.7",
+ "@vitejs/plugin-react": "^4.7.0",
+ "@webgpu/types": "^0.1.64",
+ "eslint": "^9.32.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.20",
+ "globals": "^16.3.0",
+ "typescript": "~5.8.3",
+ "typescript-eslint": "^8.39.0",
+ "vite": "^7.1.0"
+ },
+ "overrides": {
+ "kokoro-js": {
+ "@huggingface/transformers": "^3.7.1"
+ }
+ }
+}
diff --git a/bedtime-story-generator/public/bubble.mp3 b/bedtime-story-generator/public/bubble.mp3
new file mode 100644
index 00000000..51361694
Binary files /dev/null and b/bedtime-story-generator/public/bubble.mp3 differ
diff --git a/bedtime-story-generator/public/hover.mp3 b/bedtime-story-generator/public/hover.mp3
new file mode 100644
index 00000000..d16e1a93
Binary files /dev/null and b/bedtime-story-generator/public/hover.mp3 differ
diff --git a/bedtime-story-generator/public/music.mp3 b/bedtime-story-generator/public/music.mp3
new file mode 100644
index 00000000..c6af57be
Binary files /dev/null and b/bedtime-story-generator/public/music.mp3 differ
diff --git a/bedtime-story-generator/src/App.tsx b/bedtime-story-generator/src/App.tsx
new file mode 100644
index 00000000..44436038
--- /dev/null
+++ b/bedtime-story-generator/src/App.tsx
@@ -0,0 +1,138 @@
+import { useState, useEffect, useRef, useCallback } from "react";
+import { Volume2, VolumeX } from "lucide-react";
+
+import { useLLM } from "./hooks/useLLM";
+import { useTTS } from "./hooks/useTTS";
+import useAudioPlayer from "./hooks/useAudioPlayer";
+
+import LandingScreen from "./components/LandingScreen";
+import ProgressScreen from "./components/ProgressScreen";
+import ErrorScreen from "./components/ErrorScreen";
+
+import WORKLET from "./play-worklet.js?raw";
+import MainApplication from "./components/MainApplication";
+
+export default function App() {
+ const llm = useLLM();
+ const tts = useTTS();
+
+ const { initAudio, playPopSound, playHoverSound, toggleMusic, playMusic, isMusicPlaying, isAudioReady } =
+ useAudioPlayer();
+ const [appState, setAppState] = useState<"landing" | "loading" | "main" | "error">(
+ navigator.gpu ? "landing" : "error",
+ );
+ const [error, setError] = useState(null);
+ const audioWorkletNodeRef = useRef(null);
+ const audioContextRef = useRef(null);
+
+ const audioGlobal = (globalThis as any).__AUDIO__ || {
+ ctx: null as AudioContext | null,
+ node: null as AudioWorkletNode | null,
+ loaded: false as boolean,
+ };
+ (globalThis as any).__AUDIO__ = audioGlobal;
+
+ const allowAutoplayRef = useRef(true);
+ const handleToggleMusic = useCallback(() => {
+ if (isMusicPlaying) allowAutoplayRef.current = false;
+ toggleMusic();
+ }, [isMusicPlaying, toggleMusic]);
+
+ const handleLoadApp = async () => {
+ setAppState("loading");
+ initAudio();
+
+ if (audioGlobal.ctx && audioGlobal.node) {
+ audioContextRef.current = audioGlobal.ctx;
+ audioWorkletNodeRef.current = audioGlobal.node;
+ await audioContextRef.current?.resume();
+ } else {
+ try {
+ const audioContext = new AudioContext({ sampleRate: 24000 });
+ audioContextRef.current = audioContext;
+ await audioContext.resume();
+
+ if (!audioGlobal.loaded) {
+ const blob = new Blob([WORKLET], { type: "application/javascript" });
+ const url = URL.createObjectURL(blob);
+ await audioContext.audioWorklet.addModule(url);
+ URL.revokeObjectURL(url);
+ audioGlobal.loaded = true;
+ }
+
+ const workletNode = new AudioWorkletNode(audioContext, "buffered-audio-worklet-processor");
+ workletNode.connect(audioContext.destination);
+
+ audioWorkletNodeRef.current = workletNode;
+ audioGlobal.ctx = audioContext;
+ audioGlobal.node = workletNode;
+ } catch {}
+ }
+
+ await audioContextRef.current?.resume();
+ llm.load();
+ tts.load();
+ };
+
+ const handleLoadingComplete = useCallback(() => {
+ setAppState("main");
+ if (allowAutoplayRef.current && !isMusicPlaying) playMusic();
+ }, [playMusic, isMusicPlaying]);
+
+ const handleRetry = () => {
+ setError(null);
+ handleLoadApp();
+ };
+
+ useEffect(() => {
+ if (llm.error) {
+ setError(`LLM Error: ${llm.error}`);
+ setAppState("error");
+ } else if (tts.error) {
+ setError(`TTS Error: ${tts.error}`);
+ setAppState("error");
+ } else if (llm.isReady && tts.isReady) {
+ handleLoadingComplete();
+ }
+ }, [llm.isReady, tts.isReady, llm.error, tts.error, handleLoadingComplete]);
+
+ useEffect(() => {
+ if (!navigator.gpu) {
+ setError("WebGPU is not supported in this browser.");
+ setAppState("error");
+ return;
+ }
+ return () => {
+ audioWorkletNodeRef.current?.disconnect();
+ };
+ }, []);
+
+ return (
+ <>
+
+ {isAudioReady && (
+
+ )}
+
+
+
+
+
+ >
+ );
+}
diff --git a/bedtime-story-generator/src/components/ErrorScreen.tsx b/bedtime-story-generator/src/components/ErrorScreen.tsx
new file mode 100644
index 00000000..ecdf19a6
--- /dev/null
+++ b/bedtime-story-generator/src/components/ErrorScreen.tsx
@@ -0,0 +1,37 @@
+import type React from "react";
+import { AlertTriangle } from "lucide-react";
+import NeoButton from "./NeoButton";
+
+interface ErrorScreenProps {
+ isVisible: boolean;
+ error: string | null;
+ onRetry: () => void;
+}
+
+const ErrorScreen: React.FC = ({ isVisible, error, onRetry }) => {
+ const transitionClass = isVisible ? "opacity-100" : "opacity-0 -translate-y-10 pointer-events-none";
+ return (
+
+
+
+
+
+ An Error Occurred
+
+ {error && (
+
+ Details:
+ {error}
+
+ )}
+
+ Try Again
+
+
+
+ );
+};
+
+export default ErrorScreen;
diff --git a/bedtime-story-generator/src/components/LandingScreen.tsx b/bedtime-story-generator/src/components/LandingScreen.tsx
new file mode 100644
index 00000000..b983c977
--- /dev/null
+++ b/bedtime-story-generator/src/components/LandingScreen.tsx
@@ -0,0 +1,49 @@
+import type React from "react";
+import { Sparkles } from "lucide-react";
+import NeoButton from "./NeoButton";
+
+interface LandingScreenProps {
+ isVisible: boolean;
+ onLoad: () => void;
+ playHoverSound?: () => void;
+}
+
+const LandingScreen: React.FC = ({ isVisible, onLoad, playHoverSound }) => {
+ const transitionClass = isVisible ? "opacity-100" : "opacity-0 -translate-y-10 pointer-events-none";
+ return (
+
+
+
+ ✨ Bedtime Story ✨
+ Generator
+
+
Craft magical tales in seconds.
+
+ Start Creating
+
+
+
+
+
+ );
+};
+
+export default LandingScreen;
diff --git a/bedtime-story-generator/src/components/MainApplication.tsx b/bedtime-story-generator/src/components/MainApplication.tsx
new file mode 100644
index 00000000..8c82804f
--- /dev/null
+++ b/bedtime-story-generator/src/components/MainApplication.tsx
@@ -0,0 +1,523 @@
+import { useReducer, useState, useRef, useEffect, useCallback } from "react";
+import type React from "react";
+import { Dices, RefreshCw } from "lucide-react";
+
+import type { generateFn } from "../hooks/useLLM";
+import NeoButton from "./NeoButton";
+import OptionCard from "./OptionCard";
+import { STORY_DATA } from "../constants";
+
+interface MainApplicationProps {
+ isVisible: boolean;
+ playPopSound: () => void;
+ playHoverSound: () => void;
+ generate: generateFn;
+ streamTTS: (onAudioChunk: (chunk: { audio: Float32Array; text?: string }) => void) => {
+ splitter: any;
+ ttsPromise: Promise;
+ };
+ isTTSReady: boolean;
+ audioWorkletNode: AudioWorkletNode | null;
+ toggleMusic: (force?: boolean) => void;
+ isMusicPlaying: boolean;
+}
+
+type StoryState = {
+ character: string;
+ setting: string;
+ item: string;
+ theme: string;
+ length: string;
+ customCharacter: string;
+ customSetting: string;
+ customItem: string;
+};
+
+type StoryAction =
+ | { type: "SET_FIELD"; field: keyof StoryState; value: string }
+ | {
+ type: "SURPRISE_ME";
+ payload: Omit;
+ }
+ | { type: "RESET" };
+
+const initialState: StoryState = {
+ character: STORY_DATA.characters[0],
+ setting: STORY_DATA.settings[0],
+ item: STORY_DATA.items[0],
+ theme: STORY_DATA.themes[0],
+ length: STORY_DATA.length[0],
+ customCharacter: "",
+ customSetting: "",
+ customItem: "",
+};
+
+const storyReducer = (state: StoryState, action: StoryAction): StoryState => {
+ switch (action.type) {
+ case "SET_FIELD":
+ return { ...state, [action.field]: action.value };
+ case "SURPRISE_ME":
+ return { ...initialState, ...action.payload };
+ case "RESET":
+ return initialState;
+ default:
+ return state;
+ }
+};
+
+const MainApplication: React.FC = ({
+ isVisible,
+ playPopSound,
+ playHoverSound,
+ generate,
+ streamTTS,
+ isTTSReady,
+ audioWorkletNode,
+ toggleMusic,
+ isMusicPlaying,
+}) => {
+ const [storyState, dispatch] = useReducer(storyReducer, initialState);
+ const { character, setting, item, theme, length, customCharacter, customSetting, customItem } = storyState;
+
+ const [generatedStory, setGeneratedStory] = useState(null);
+ const [isShuffling, setIsShuffling] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+ const [isSpeaking, setIsSpeaking] = useState(false);
+ const [previewText, setPreviewText] = useState("");
+
+ const storyPanelRef = useRef(null);
+
+ const [ranges, setRanges] = useState>([]);
+ const rangesRef = useRef(ranges);
+ useEffect(() => {
+ rangesRef.current = ranges;
+ }, [ranges]);
+
+ const pendingRef = useRef([]);
+ const rawSearchStartRef = useRef(0);
+ const normDataRef = useRef<{ norm: string; map: number[] } | null>(null);
+
+ const [currentChunkIdx, setCurrentChunkIdx] = useState(-1);
+ const finishedIndexRef = useRef(-1);
+ const textContainerRef = useRef(null);
+ const chunkRefs = useRef<(HTMLSpanElement | null)[]>([]);
+ const resumeMusicOnPlaybackEndRef = useRef(false);
+ const activeSplitterRef = useRef(null);
+
+ const displayedText = previewText || generatedStory || "";
+
+ const buildNorm = (text: string) => {
+ const map: number[] = [];
+ let norm = "";
+ for (let i = 0; i < text.length; i++) {
+ const ch = text[i];
+ if (/\s/.test(ch)) continue;
+ map.push(i);
+ norm += ch;
+ }
+ return { norm, map };
+ };
+
+ const tryResolve = useCallback(() => {
+ const normData = normDataRef.current;
+ if (!normData) return;
+
+ const { norm, map } = normData;
+ const nextRanges = rangesRef.current.slice();
+
+ const normStartFromRaw = () => {
+ let i = 0;
+ while (i < map.length && map[i] < rawSearchStartRef.current) i++;
+ return i;
+ };
+
+ let changed = false;
+ while (pendingRef.current.length) {
+ const nextText = pendingRef.current[0];
+ const needle = nextText.replace(/\s+/g, "");
+ if (!needle) {
+ pendingRef.current.shift();
+ continue;
+ }
+ const startNormIdx = normStartFromRaw();
+ const idx = norm.indexOf(needle, startNormIdx);
+ if (idx === -1) break;
+
+ const startRaw = map[idx];
+ const endRaw = map[idx + needle.length - 1] + 1;
+
+ nextRanges.push({ start: startRaw, end: endRaw });
+ rawSearchStartRef.current = endRaw;
+ pendingRef.current.shift();
+ changed = true;
+ }
+
+ if (changed) {
+ setRanges(nextRanges);
+ setCurrentChunkIdx((prev) =>
+ prev === -1 ? Math.min(finishedIndexRef.current + 1, nextRanges.length - 1) : prev,
+ );
+ }
+ }, []);
+
+ useEffect(() => {
+ normDataRef.current = buildNorm(displayedText);
+ tryResolve();
+ }, [displayedText, tryResolve]);
+
+ useEffect(() => {
+ if (!audioWorkletNode) return;
+ const handler = (event: MessageEvent) => {
+ const data = (event as any).data;
+ if (!data || typeof data !== "object") return;
+ if (data.type === "next_chunk") {
+ finishedIndexRef.current += 1;
+ const nextIdx = finishedIndexRef.current + 1;
+ setCurrentChunkIdx(nextIdx < rangesRef.current.length ? nextIdx : -1);
+ } else if (data.type === "playback_ended") {
+ setIsSpeaking(false);
+ setCurrentChunkIdx(-1);
+ if (resumeMusicOnPlaybackEndRef.current) {
+ toggleMusic();
+ }
+ }
+ };
+ (audioWorkletNode.port as any).onmessage = handler;
+ return () => {
+ if (audioWorkletNode?.port) (audioWorkletNode.port as any).onmessage = null;
+ };
+ }, [audioWorkletNode, toggleMusic]);
+
+ useEffect(() => {
+ if (currentChunkIdx < 0) return;
+ const container = textContainerRef.current;
+ const el = chunkRefs.current[currentChunkIdx];
+ if (!container || !el) return;
+ const containerRect = container.getBoundingClientRect();
+ const elRect = el.getBoundingClientRect();
+ const elTopInContainer = elRect.top - containerRect.top + container.scrollTop;
+ const targetTop = Math.max(0, elTopInContainer - container.clientHeight * 0.3);
+ container.scrollTo({ top: targetTop, behavior: "smooth" });
+ }, [currentChunkIdx]);
+
+ const getRandomItem = (arr: string[]): string => arr[Math.floor(Math.random() * arr.length)];
+
+ const handleSurpriseMe = () => {
+ playPopSound();
+ if (isShuffling) return;
+ setIsShuffling(true);
+ dispatch({ type: "SET_FIELD", field: "customCharacter", value: "" });
+ dispatch({ type: "SET_FIELD", field: "customSetting", value: "" });
+ dispatch({ type: "SET_FIELD", field: "customItem", value: "" });
+
+ let count = 0;
+ const max = 15;
+ const interval = setInterval(() => {
+ dispatch({
+ type: "SURPRISE_ME",
+ payload: {
+ character: getRandomItem(STORY_DATA.characters),
+ setting: getRandomItem(STORY_DATA.settings),
+ item: getRandomItem(STORY_DATA.items),
+ theme: getRandomItem(STORY_DATA.themes),
+ length: getRandomItem(STORY_DATA.length),
+ },
+ });
+ count++;
+ if (count >= max) {
+ clearInterval(interval);
+ setIsShuffling(false);
+ }
+ }, 60);
+ };
+
+ const generateStory = async () => {
+ if (!audioWorkletNode) return;
+ playPopSound();
+ setIsLoading(true);
+ setPreviewText("");
+ setGeneratedStory(null);
+
+ setRanges([]);
+ rangesRef.current = [];
+ pendingRef.current = [];
+ rawSearchStartRef.current = 0;
+ setCurrentChunkIdx(-1);
+ finishedIndexRef.current = -1;
+ chunkRefs.current = [];
+
+ const wasMusicPlaying = isMusicPlaying;
+ resumeMusicOnPlaybackEndRef.current = wasMusicPlaying;
+
+ storyPanelRef.current?.scrollIntoView({
+ behavior: "smooth",
+ block: "start",
+ });
+
+ setIsSpeaking(true);
+ if (wasMusicPlaying) toggleMusic();
+
+ const { splitter, ttsPromise } = streamTTS(({ audio, text }) => {
+ audioWorkletNode.port.postMessage(audio);
+ if (text) {
+ pendingRef.current.push(text);
+ tryResolve();
+ }
+ });
+ activeSplitterRef.current = splitter;
+
+ const selectedCharacter = (customCharacter || character).trim();
+ const selectedSetting = (customSetting || setting).trim();
+ const selectedItem = (customItem || item).trim();
+
+ const lengthMap: Record = {
+ Short: "100-200 word",
+ Medium: "200-300 word",
+ Long: "300-400 word",
+ };
+
+ const userMessage = `Write a ${lengthMap[length]} ${theme.toLowerCase()} story about ${selectedCharacter} ${selectedSetting} that ${selectedItem}.`;
+
+ try {
+ const llmPromise = generate(
+ [{ role: "user", content: userMessage }],
+ (token: string) => setPreviewText((prev) => prev + token),
+ splitter,
+ );
+ await Promise.all([llmPromise, ttsPromise]);
+ } catch {
+ setGeneratedStory(previewText || "Sorry, the story failed to generate.");
+ } finally {
+ activeSplitterRef.current = null;
+ setIsLoading(false);
+ }
+ };
+
+ const handleReset = () => {
+ playPopSound();
+
+ activeSplitterRef.current?.close?.();
+ activeSplitterRef.current = null;
+ audioWorkletNode?.port.postMessage("stop");
+ setIsSpeaking(false);
+
+ if (!isMusicPlaying && resumeMusicOnPlaybackEndRef.current) {
+ toggleMusic();
+ }
+ resumeMusicOnPlaybackEndRef.current = false;
+
+ setRanges([]);
+ rangesRef.current = [];
+ pendingRef.current = [];
+ rawSearchStartRef.current = 0;
+ setCurrentChunkIdx(-1);
+ finishedIndexRef.current = -1;
+ chunkRefs.current = [];
+
+ setGeneratedStory(null);
+ setPreviewText("");
+
+ dispatch({ type: "RESET" });
+ };
+
+ const transitionClass = isVisible ? "opacity-100" : "opacity-0 translate-y-10 pointer-events-none";
+
+ return (
+
+
+
+
+ dispatch({ type: "SET_FIELD", field: "character", value: val })}
+ customValue={customCharacter}
+ onCustomChange={(val) =>
+ dispatch({
+ type: "SET_FIELD",
+ field: "customCharacter",
+ value: val,
+ })
+ }
+ isShuffling={isShuffling}
+ isVisible={isVisible}
+ playPopSound={playPopSound}
+ playHoverSound={playHoverSound}
+ />
+ dispatch({ type: "SET_FIELD", field: "setting", value: val })}
+ customValue={customSetting}
+ onCustomChange={(val) => dispatch({ type: "SET_FIELD", field: "customSetting", value: val })}
+ isShuffling={isShuffling}
+ isVisible={isVisible}
+ playPopSound={playPopSound}
+ playHoverSound={playHoverSound}
+ />
+ dispatch({ type: "SET_FIELD", field: "item", value: val })}
+ customValue={customItem}
+ onCustomChange={(val) => dispatch({ type: "SET_FIELD", field: "customItem", value: val })}
+ isShuffling={isShuffling}
+ isVisible={isVisible}
+ playPopSound={playPopSound}
+ playHoverSound={playHoverSound}
+ />
+
+
4. Select a Theme
+
+ {STORY_DATA.themes.map((t) => (
+
+ ))}
+
+
+
+
5. Story Length
+
+ {STORY_DATA.length.map((l) => (
+
+ ))}
+
+
+
+
+ Make My Story
+
+
+
+ Surprise Me!
+
+
+
+ Reset
+
+
+
+ {/* Story Panel (covers screen height, no separate story page) */}
+
+
Your Story
+
+ {(() => {
+ const displayed = displayedText;
+ if (!displayed) {
+ return "Click “Make My Story” to generate a bedtime story.";
+ }
+ const pieces: React.ReactNode[] = [];
+ let last = 0;
+ for (let i = 0; i < ranges.length; i++) {
+ const { start, end } = ranges[i];
+ if (start > last) {
+ pieces.push({displayed.slice(last, start)});
+ }
+ pieces.push(
+ {
+ chunkRefs.current[i] = el;
+ }}
+ className={i === currentChunkIdx ? "bg-yellow-200" : ""}
+ >
+ {displayed.slice(start, end)}
+ ,
+ );
+ last = end;
+ }
+ if (last < displayed.length) {
+ pieces.push({displayed.slice(last)});
+ }
+ return pieces;
+ })()}
+
+ {isLoading && (
+
+ {isSpeaking ? "Generating and speaking..." : "Generating..."}
+
+ )}
+ {generatedStory && !isLoading && isTTSReady && !isSpeaking && (
+
+ )}
+
+
+
+ );
+};
+
+export default MainApplication;
diff --git a/bedtime-story-generator/src/components/NeoButton.tsx b/bedtime-story-generator/src/components/NeoButton.tsx
new file mode 100644
index 00000000..139d5ed1
--- /dev/null
+++ b/bedtime-story-generator/src/components/NeoButton.tsx
@@ -0,0 +1,47 @@
+import type React from "react";
+import { Loader } from "lucide-react";
+
+interface NeoButtonProps {
+ children: React.ReactNode;
+ onClick?: (e: React.MouseEvent) => void;
+ playPopSound?: () => void;
+ playHoverSound?: () => void;
+ className?: string;
+ variant?: "primary" | "secondary" | "accent";
+ isLoading?: boolean;
+ disabled?: boolean;
+}
+
+const NeoButtonComponent: React.FC = ({
+ children,
+ onClick,
+ playHoverSound,
+ className = "",
+ variant = "primary",
+ isLoading = false,
+ disabled = false,
+}) => {
+ const colorClasses = {
+ primary: "bg-yellow-300 hover:bg-yellow-400 border-black",
+ secondary: "bg-pink-400 hover:bg-pink-500 border-black",
+ accent: "bg-cyan-400 hover:bg-cyan-500 border-black",
+ };
+ const primaryShine = variant === "primary" ? "animate-shine" : "";
+
+ return (
+
+ );
+};
+
+export default NeoButtonComponent;
diff --git a/bedtime-story-generator/src/components/OptionCard.tsx b/bedtime-story-generator/src/components/OptionCard.tsx
new file mode 100644
index 00000000..340ea7e3
--- /dev/null
+++ b/bedtime-story-generator/src/components/OptionCard.tsx
@@ -0,0 +1,88 @@
+import { useState } from "react";
+import type React from "react";
+import { Pencil } from "lucide-react";
+
+interface OptionCardProps {
+ title: string;
+ options: string[];
+ selected: string;
+ onSelect: (value: string) => void;
+ customValue: string;
+ onCustomChange: (value: string) => void;
+ isShuffling: boolean;
+ isVisible: boolean;
+ playPopSound: () => void;
+ playHoverSound: () => void;
+}
+
+const OptionCard: React.FC = ({
+ title,
+ options,
+ selected,
+ onSelect,
+ customValue,
+ onCustomChange,
+ isShuffling,
+ isVisible,
+ playPopSound,
+ playHoverSound,
+}) => {
+ const [isCustom, setIsCustom] = useState(false);
+
+ const handleSelect = (option: string) => {
+ playPopSound();
+ setIsCustom(false);
+ onSelect(option);
+ onCustomChange("");
+ };
+ const handleCustomClick = () => {
+ playPopSound();
+ setIsCustom(true);
+ onSelect("");
+ };
+
+ const shuffleClass = isShuffling ? "animate-shake" : "";
+ const visibilityClass = isVisible ? "animate-slide-in opacity-100" : "opacity-0 pointer-events-none";
+
+ return (
+
+
{title}
+
+ {options.map((option) => (
+
+ ))}
+
+
+ {isCustom && (
+
) => {
+ onCustomChange(e.target.value);
+ onSelect("");
+ }}
+ placeholder="Enter your own option..."
+ className="mt-4 w-full p-3 border-2 border-black rounded-lg focus:outline-none focus:ring-2 focus:ring-yellow-400"
+ />
+ )}
+
+ );
+};
+
+export default OptionCard;
diff --git a/bedtime-story-generator/src/components/ProgressScreen.tsx b/bedtime-story-generator/src/components/ProgressScreen.tsx
new file mode 100644
index 00000000..59c34057
--- /dev/null
+++ b/bedtime-story-generator/src/components/ProgressScreen.tsx
@@ -0,0 +1,25 @@
+import type React from "react";
+
+interface ProgressScreenProps {
+ progress: number;
+ isVisible: boolean;
+}
+
+const ProgressScreen: React.FC = ({ progress, isVisible }) => {
+ const transitionClass = isVisible ? "opacity-100" : "opacity-0 pointer-events-none";
+ return (
+
+
+
Warming up the magic...
+
+
{progress.toFixed(2)}%
+
+
+ );
+};
+
+export default ProgressScreen;
diff --git a/bedtime-story-generator/src/constants.ts b/bedtime-story-generator/src/constants.ts
new file mode 100644
index 00000000..5a1ca0e5
--- /dev/null
+++ b/bedtime-story-generator/src/constants.ts
@@ -0,0 +1,31 @@
+export const STORY_DATA = {
+ characters: [
+ "a brave squirrel",
+ "a shy dragon",
+ "a clever fox",
+ "a sleepy astronaut",
+ "a grumpy gnome",
+ "a cheerful princess",
+ "a tiny robot",
+ "a magical cat",
+ ],
+ settings: [
+ "on the moon",
+ "in an enchanted forest",
+ "under the sea",
+ "in a castle made of clouds",
+ "inside a giant's boot",
+ "at a wizard's school",
+ "on a floating island",
+ ],
+ items: [
+ "discovers a map to hidden treasure",
+ "finds a pair of flying shoes",
+ "stumbles upon an invisible cloak",
+ "befriends a friendly ghost",
+ "uncovers a secret door",
+ "meets a wise old man",
+ ],
+ themes: ["Fairy Tale", "Silly", "Adventurous", "Magical", "Bedtime", "Funny"],
+ length: ["Short", "Medium", "Long"],
+};
diff --git a/bedtime-story-generator/src/hooks/useAudioPlayer.ts b/bedtime-story-generator/src/hooks/useAudioPlayer.ts
new file mode 100644
index 00000000..53e82575
--- /dev/null
+++ b/bedtime-story-generator/src/hooks/useAudioPlayer.ts
@@ -0,0 +1,86 @@
+import { useState, useRef, useCallback } from "react";
+
+type AudioKeys = "pop" | "hover" | "music";
+interface AudioPlayer {
+ initAudio: () => boolean;
+ playPopSound: () => void;
+ playHoverSound: () => void;
+ toggleMusic: () => void;
+ playMusic: () => void;
+ isMusicPlaying: boolean;
+ isAudioReady: boolean;
+}
+
+const useAudioPlayer = (): AudioPlayer => {
+ const audioRefs = useRef>({
+ pop: null,
+ hover: null,
+ music: null,
+ });
+ const [isReady, setIsReady] = useState(false);
+ const [isMusicPlaying, setIsMusicPlaying] = useState(false);
+
+ const initAudio = useCallback(() => {
+ if (isReady) return true;
+ try {
+ audioRefs.current.pop = new Audio("/bubble.mp3");
+ audioRefs.current.hover = new Audio("/hover.mp3");
+ audioRefs.current.music = new Audio("/music.mp3");
+ audioRefs.current.music.loop = true;
+ audioRefs.current.music.volume = 0.1;
+ setIsReady(true);
+ return true;
+ } catch {
+ return false;
+ }
+ }, [isReady]);
+
+ const playMusic = useCallback(() => {
+ if (isReady && !isMusicPlaying) {
+ audioRefs.current.music?.play().catch(() => {});
+ setIsMusicPlaying(true);
+ }
+ }, [isReady, isMusicPlaying]);
+
+ const toggleMusic = useCallback(
+ (force?: boolean) => {
+ if (!isReady || !audioRefs.current.music) return;
+
+ const shouldBePlaying = force === undefined ? !isMusicPlaying : force;
+
+ if (shouldBePlaying === isMusicPlaying) return;
+
+ if (shouldBePlaying) {
+ audioRefs.current.music?.play().catch(() => {});
+ } else {
+ audioRefs.current.music?.pause();
+ }
+ setIsMusicPlaying(shouldBePlaying);
+ },
+ [isReady, isMusicPlaying],
+ );
+
+ const playPopSound = useCallback(() => {
+ if (!isReady || !audioRefs.current.pop) return;
+ audioRefs.current.pop.currentTime = 0;
+ audioRefs.current.pop.play().catch(() => {});
+ }, [isReady]);
+
+ const playHoverSound = useCallback(() => {
+ if (!isReady || !audioRefs.current.hover) return;
+ audioRefs.current.hover.currentTime = 0;
+ audioRefs.current.hover.play().catch(() => {});
+ }, [isReady]);
+
+ return {
+ initAudio,
+ playPopSound,
+ playHoverSound,
+ toggleMusic,
+ playMusic,
+ isMusicPlaying,
+ isAudioReady: isReady,
+ };
+};
+
+export default useAudioPlayer;
diff --git a/bedtime-story-generator/src/hooks/useLLM.ts b/bedtime-story-generator/src/hooks/useLLM.ts
new file mode 100644
index 00000000..7fbff4fe
--- /dev/null
+++ b/bedtime-story-generator/src/hooks/useLLM.ts
@@ -0,0 +1,86 @@
+import { useState, useCallback } from "react";
+import { pipeline, TextStreamer } from "@huggingface/transformers";
+import type { TextSplitterStream } from "kokoro-js";
+
+interface LLMState {
+ isLoading: boolean;
+ isReady: boolean;
+ error: string | null;
+ progress: number;
+}
+
+type LLMGlobal = { generator: any | null };
+const g = globalThis as any;
+let __LLM: LLMGlobal = g.__LLM || { generator: null };
+g.__LLM = __LLM;
+
+export type generateFn = (
+ messages: Array<{ role: string; content: string }>,
+ onToken?: (token: string) => void,
+ splitter?: TextSplitterStream,
+) => Promise;
+
+export const useLLM = () => {
+ const [state, setState] = useState({
+ isLoading: false,
+ isReady: !!__LLM.generator,
+ error: null,
+ progress: __LLM.generator ? 100 : 0,
+ });
+
+ const load = async () => {
+ if (__LLM.generator) return __LLM.generator;
+ setState((p) => ({ ...p, isLoading: true, error: null, progress: 0 }));
+ try {
+ const generator = await pipeline("text-generation", "onnx-community/gemma-3-270m-it-ONNX", {
+ dtype: "fp32",
+ device: "webgpu",
+ progress_callback: (item) => {
+ if (item.status === "progress" && item.file?.endsWith?.("onnx_data")) {
+ setState((p) => ({ ...p, progress: item.progress || 0 }));
+ }
+ },
+ });
+ __LLM.generator = generator;
+ setState((p) => ({
+ ...p,
+ isLoading: false,
+ isReady: true,
+ progress: 100,
+ }));
+ return generator;
+ } catch (error) {
+ setState((p) => ({
+ ...p,
+ isLoading: false,
+ error: error instanceof Error ? error.message : "Failed to load model",
+ }));
+ throw error;
+ }
+ };
+
+ const generate: generateFn = useCallback(async (messages, onToken, splitter) => {
+ const generator = __LLM.generator;
+ if (!generator) throw new Error("Model not loaded. Call load() first.");
+ const streamer = new TextStreamer(generator.tokenizer, {
+ skip_prompt: true,
+ skip_special_tokens: true,
+ callback_function: (token: string) => {
+ onToken?.(token);
+ splitter?.push(token);
+ },
+ });
+ await generator(messages, {
+ max_new_tokens: 1024,
+ do_sample: false,
+ streamer,
+ });
+ splitter?.close();
+ }, []);
+
+ return {
+ ...state,
+ load,
+ generate,
+ };
+};
diff --git a/bedtime-story-generator/src/hooks/useTTS.ts b/bedtime-story-generator/src/hooks/useTTS.ts
new file mode 100644
index 00000000..6181f12f
--- /dev/null
+++ b/bedtime-story-generator/src/hooks/useTTS.ts
@@ -0,0 +1,75 @@
+import { useState, useCallback } from "react";
+import { KokoroTTS, TextSplitterStream } from "kokoro-js";
+
+interface TTSState {
+ isLoading: boolean;
+ isReady: boolean;
+ error: string | null;
+ progress: number;
+}
+
+type TTSGlobal = { model: KokoroTTS | null };
+const g = globalThis as any;
+let __TTS: TTSGlobal = g.__TTS || { model: null };
+g.__TTS = __TTS;
+
+export const useTTS = () => {
+ const [state, setState] = useState({
+ isLoading: false,
+ isReady: !!__TTS.model,
+ error: null,
+ progress: __TTS.model ? 100 : 0,
+ });
+
+ const load = async () => {
+ if (__TTS.model) return __TTS.model;
+ setState((p) => ({ ...p, isLoading: true, error: null, progress: 0 }));
+ try {
+ const tts = await KokoroTTS.from_pretrained("onnx-community/Kokoro-82M-v1.0-ONNX", {
+ dtype: "fp32",
+ device: "webgpu",
+ progress_callback: (item) => {
+ if (item.status === "progress" && item.file?.endsWith?.("onnx")) {
+ setState((p) => ({ ...p, progress: item.progress || 0 }));
+ }
+ },
+ });
+ __TTS.model = tts;
+ setState((p) => ({
+ ...p,
+ isLoading: false,
+ isReady: true,
+ progress: 100,
+ }));
+ return tts;
+ } catch (error) {
+ setState((p) => ({
+ ...p,
+ isLoading: false,
+ error: error instanceof Error ? error.message : "Failed to load TTS model",
+ }));
+ throw error;
+ }
+ };
+
+ const stream = useCallback((onAudioChunk: (chunk: { audio: Float32Array; text?: string }) => void) => {
+ const tts = __TTS.model as KokoroTTS | null;
+ if (!tts) throw new Error("TTS model not loaded. Call load() first.");
+ const splitter = new TextSplitterStream();
+ const ttsStream = tts.stream(splitter);
+ const ttsPromise = (async () => {
+ for await (const chunk of ttsStream) {
+ if (chunk.audio) {
+ onAudioChunk({ audio: chunk.audio.audio, text: chunk.text });
+ }
+ }
+ })();
+ return { splitter, ttsPromise };
+ }, []);
+
+ return {
+ ...state,
+ load,
+ stream,
+ };
+};
diff --git a/bedtime-story-generator/src/index.css b/bedtime-story-generator/src/index.css
new file mode 100644
index 00000000..73475a31
--- /dev/null
+++ b/bedtime-story-generator/src/index.css
@@ -0,0 +1,88 @@
+@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@400;700;900&display=swap");
+@import "tailwindcss";
+
+body {
+ font-family: "Nunito", sans-serif;
+}
+.bg-pattern {
+ background-color: #fef3c7;
+ background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23fbbf24' fill-opacity='0.2'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
+}
+@keyframes shine {
+ 0% {
+ transform: translateX(-100%) skewX(-20deg);
+ }
+ 100% {
+ transform: translateX(200%) skewX(-20deg);
+ }
+}
+.animate-shine {
+ background: linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 0.4) 50%,
+ rgba(255, 255, 255, 0) 100%
+ );
+ animation: shine 4s infinite;
+}
+@keyframes shake {
+ 0%,
+ 100% {
+ transform: translateX(0);
+ }
+ 10%,
+ 30%,
+ 50%,
+ 70%,
+ 90% {
+ transform: translateX(-3px);
+ }
+ 20%,
+ 40%,
+ 60%,
+ 80% {
+ transform: translateX(3px);
+ }
+}
+.animate-shake {
+ animation: shake 0.3s linear infinite;
+}
+@keyframes slide-in {
+ from {
+ transform: translateY(20px);
+ opacity: 0;
+ }
+ to {
+ transform: translateY(0);
+ opacity: 1;
+ }
+}
+.animate-slide-in {
+ animation: slide-in 0.5s ease-out forwards;
+}
+@keyframes pulse-grow {
+ 0%,
+ 100% {
+ transform: scale(1);
+ }
+ 50% {
+ transform: scale(1.05);
+ }
+}
+.animate-pulse-grow {
+ animation: pulse-grow 3s infinite;
+}
+@keyframes sway {
+ 0% {
+ transform: rotate(-1deg);
+ }
+ 50% {
+ transform: rotate(1deg);
+ }
+ 100% {
+ transform: rotate(-1deg);
+ }
+}
+.animate-sway {
+ animation: sway 8s ease-in-out infinite;
+}
diff --git a/bedtime-story-generator/src/main.tsx b/bedtime-story-generator/src/main.tsx
new file mode 100644
index 00000000..eff7ccc6
--- /dev/null
+++ b/bedtime-story-generator/src/main.tsx
@@ -0,0 +1,10 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import "./index.css";
+import App from "./App.tsx";
+
+createRoot(document.getElementById("root")!).render(
+
+
+ ,
+);
diff --git a/bedtime-story-generator/src/play-worklet.js b/bedtime-story-generator/src/play-worklet.js
new file mode 100644
index 00000000..07c64e2b
--- /dev/null
+++ b/bedtime-story-generator/src/play-worklet.js
@@ -0,0 +1,63 @@
+class BufferedAudioWorkletProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.bufferQueue = [];
+ this.currentChunkOffset = 0;
+ this.hadData = false;
+
+ this.port.onmessage = (event) => {
+ const data = event.data;
+ if (data instanceof Float32Array) {
+ this.hadData = true;
+ this.bufferQueue.push(data);
+ } else if (data === "stop") {
+ this.bufferQueue = [];
+ this.currentChunkOffset = 0;
+ }
+ };
+ }
+
+ process(inputs, outputs) {
+ const channel = outputs[0][0];
+ if (!channel) return true;
+
+ const numSamples = channel.length;
+ let outputIndex = 0;
+
+ if (this.hadData && this.bufferQueue.length === 0) {
+ this.port.postMessage({ type: "playback_ended" });
+ this.hadData = false;
+ }
+
+ while (outputIndex < numSamples) {
+ if (this.bufferQueue.length > 0) {
+ const currentChunk = this.bufferQueue[0];
+ const remainingSamples = currentChunk.length - this.currentChunkOffset;
+ const samplesToCopy = Math.min(remainingSamples, numSamples - outputIndex);
+
+ channel.set(
+ currentChunk.subarray(this.currentChunkOffset, this.currentChunkOffset + samplesToCopy),
+ outputIndex,
+ );
+
+ this.currentChunkOffset += samplesToCopy;
+ outputIndex += samplesToCopy;
+
+ // Remove the chunk if fully consumed.
+ if (this.currentChunkOffset >= currentChunk.length) {
+ // current chunk finished; advance and signal UI to move highlight
+ this.bufferQueue.shift();
+ this.currentChunkOffset = 0;
+ this.port.postMessage({ type: "next_chunk" });
+ }
+ } else {
+ // If no data is available, fill the rest of the buffer with silence.
+ channel.fill(0, outputIndex);
+ outputIndex = numSamples;
+ }
+ }
+ return true;
+ }
+}
+
+registerProcessor("buffered-audio-worklet-processor", BufferedAudioWorkletProcessor);
diff --git a/bedtime-story-generator/src/vite-env.d.ts b/bedtime-story-generator/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/bedtime-story-generator/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/bedtime-story-generator/tsconfig.app.json b/bedtime-story-generator/tsconfig.app.json
new file mode 100644
index 00000000..d5ff3f90
--- /dev/null
+++ b/bedtime-story-generator/tsconfig.app.json
@@ -0,0 +1,29 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true,
+
+ "types": ["@webgpu/types"]
+ },
+ "include": ["src"]
+}
diff --git a/bedtime-story-generator/tsconfig.json b/bedtime-story-generator/tsconfig.json
new file mode 100644
index 00000000..d32ff682
--- /dev/null
+++ b/bedtime-story-generator/tsconfig.json
@@ -0,0 +1,4 @@
+{
+ "files": [],
+ "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
+}
diff --git a/bedtime-story-generator/tsconfig.node.json b/bedtime-story-generator/tsconfig.node.json
new file mode 100644
index 00000000..f85a3990
--- /dev/null
+++ b/bedtime-story-generator/tsconfig.node.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/bedtime-story-generator/vite.config.ts b/bedtime-story-generator/vite.config.ts
new file mode 100644
index 00000000..d4a9bf6f
--- /dev/null
+++ b/bedtime-story-generator/vite.config.ts
@@ -0,0 +1,14 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+import tailwindcss from "@tailwindcss/vite";
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react(), tailwindcss()],
+
+ resolve: {
+ // Only bundle a single instance of Transformers.js
+ // (shared by `@huggingface/transformers` and `kokoro-js`)
+ dedupe: ["@huggingface/transformers"],
+ },
+});