@tryhamster/gerbil 1.0.0-rc.1 → 1.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/{index.d.mts → index.d.ts} +354 -3
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/{index.mjs → index.js} +119 -8
- package/dist/browser/index.js.map +1 -0
- package/dist/{chrome-backend-Y9F7W5VQ.mjs → chrome-backend-CORwaIyC.mjs} +1 -1
- package/dist/{chrome-backend-Y9F7W5VQ.mjs.map → chrome-backend-CORwaIyC.mjs.map} +1 -1
- package/dist/{chrome-backend-JEPeM2YE.mjs → chrome-backend-DIKYoWj-.mjs} +1 -1
- package/dist/cli.mjs +14 -15
- package/dist/cli.mjs.map +1 -1
- package/dist/frameworks/express.d.mts +1 -1
- package/dist/frameworks/express.mjs +3 -4
- package/dist/frameworks/express.mjs.map +1 -1
- package/dist/frameworks/fastify.d.mts +1 -1
- package/dist/frameworks/fastify.mjs +2 -3
- package/dist/frameworks/fastify.mjs.map +1 -1
- package/dist/frameworks/hono.d.mts +1 -1
- package/dist/frameworks/hono.mjs +2 -3
- package/dist/frameworks/hono.mjs.map +1 -1
- package/dist/frameworks/next.d.mts +2 -2
- package/dist/frameworks/next.mjs +2 -3
- package/dist/frameworks/next.mjs.map +1 -1
- package/dist/frameworks/react.d.mts +1 -1
- package/dist/frameworks/trpc.d.mts +1 -1
- package/dist/frameworks/trpc.mjs +2 -3
- package/dist/frameworks/trpc.mjs.map +1 -1
- package/dist/gerbil-DJGqq7BX.mjs +4 -0
- package/dist/{gerbil-yoSpRHgv.mjs → gerbil-DoDGHe6Z.mjs} +187 -19
- package/dist/gerbil-DoDGHe6Z.mjs.map +1 -0
- package/dist/{gerbil-POAz8peb.d.mts → gerbil-qOTe1nl2.d.mts} +2 -2
- package/dist/{gerbil-POAz8peb.d.mts.map → gerbil-qOTe1nl2.d.mts.map} +1 -1
- package/dist/index.d.mts +19 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -7
- package/dist/index.mjs.map +1 -1
- package/dist/integrations/ai-sdk.d.mts +1 -1
- package/dist/integrations/ai-sdk.mjs +4 -5
- package/dist/integrations/ai-sdk.mjs.map +1 -1
- package/dist/integrations/langchain.d.mts +1 -1
- package/dist/integrations/langchain.mjs +2 -3
- package/dist/integrations/langchain.mjs.map +1 -1
- package/dist/integrations/llamaindex.d.mts +1 -1
- package/dist/integrations/llamaindex.mjs +2 -3
- package/dist/integrations/llamaindex.mjs.map +1 -1
- package/dist/integrations/mcp-client.mjs +2 -2
- package/dist/integrations/mcp.d.mts +2 -2
- package/dist/integrations/mcp.mjs +5 -6
- package/dist/kokoro-BNTb6egA.mjs +20210 -0
- package/dist/kokoro-BNTb6egA.mjs.map +1 -0
- package/dist/kokoro-CMOGDSgT.js +20212 -0
- package/dist/kokoro-CMOGDSgT.js.map +1 -0
- package/dist/{mcp-Bitg4sjX.mjs → mcp-kzDDWIoS.mjs} +3 -3
- package/dist/{mcp-Bitg4sjX.mjs.map → mcp-kzDDWIoS.mjs.map} +1 -1
- package/dist/{one-liner-B1rmFto6.mjs → one-liner-DxnNs_JK.mjs} +2 -2
- package/dist/{one-liner-B1rmFto6.mjs.map → one-liner-DxnNs_JK.mjs.map} +1 -1
- package/dist/repl-DGUw4fCc.mjs +9 -0
- package/dist/skills/index.d.mts +24 -24
- package/dist/skills/index.d.mts.map +1 -1
- package/dist/skills/index.mjs +4 -5
- package/dist/{skills-5DxAV-rn.mjs → skills-DulrOPeP.mjs} +12 -12
- package/dist/skills-DulrOPeP.mjs.map +1 -0
- package/dist/stt-1WIefHwc.mjs +3 -0
- package/dist/{stt-Bv_dum-R.mjs → stt-CG_7KB_0.mjs} +3 -2
- package/dist/stt-CG_7KB_0.mjs.map +1 -0
- package/dist/stt-Dne6SENv.js +434 -0
- package/dist/stt-Dne6SENv.js.map +1 -0
- package/dist/{tools-IYPrqoek.mjs → tools-Bi1P7Xoy.mjs} +2 -2
- package/dist/{tools-IYPrqoek.mjs.map → tools-Bi1P7Xoy.mjs.map} +1 -1
- package/dist/transformers.web-DiD1gTwk.js +44695 -0
- package/dist/transformers.web-DiD1gTwk.js.map +1 -0
- package/dist/transformers.web-u34VxRFM.js +3 -0
- package/dist/{tts-5yWeP_I0.mjs → tts-B1pZMlDv.mjs} +1 -1
- package/dist/tts-C2FzKuSx.js +725 -0
- package/dist/tts-C2FzKuSx.js.map +1 -0
- package/dist/{tts-DG6denWG.mjs → tts-CyHhcLtN.mjs} +6 -4
- package/dist/tts-CyHhcLtN.mjs.map +1 -0
- package/dist/{types-s6Py2_DL.d.mts → types-CiTc7ez3.d.mts} +1 -1
- package/dist/{types-s6Py2_DL.d.mts.map → types-CiTc7ez3.d.mts.map} +1 -1
- package/dist/{utils-CkB4Roi6.mjs → utils-CZBZ8dgR.mjs} +1 -1
- package/dist/{utils-CkB4Roi6.mjs.map → utils-CZBZ8dgR.mjs.map} +1 -1
- package/package.json +6 -6
- package/dist/browser/index.d.mts.map +0 -1
- package/dist/browser/index.mjs.map +0 -1
- package/dist/gerbil-DeQlX_Mt.mjs +0 -5
- package/dist/gerbil-yoSpRHgv.mjs.map +0 -1
- package/dist/models-BAtL8qsA.mjs +0 -171
- package/dist/models-BAtL8qsA.mjs.map +0 -1
- package/dist/models-CE0fBq0U.d.mts +0 -22
- package/dist/models-CE0fBq0U.d.mts.map +0 -1
- package/dist/repl-D20JO260.mjs +0 -10
- package/dist/skills-5DxAV-rn.mjs.map +0 -1
- package/dist/stt-Bv_dum-R.mjs.map +0 -1
- package/dist/stt-KzSoNvwI.mjs +0 -3
- package/dist/tts-DG6denWG.mjs.map +0 -1
- /package/dist/{auto-update-DsWBBnEk.mjs → auto-update-S9s5-g0C.mjs} +0 -0
- /package/dist/{chunk-Ct1HF2bE.mjs → chunk-CkXuGtQK.mjs} +0 -0
- /package/dist/{microphone-D-6y9aiE.mjs → microphone-DaMZFRuR.mjs} +0 -0
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["models: CachedModelInfo[]","result: { sizeBytes?: number; contextLength?: number }","files: { path: string; size?: number; lfs?: { size?: number } }[]","devices: Record<string, { avgTokPerSec: number; runs: number }>","bests","converted: BenchmarkResult[]","newResult: BenchmarkResult","args: string[]","elements: React.ReactNode[]","codeBlockContent: string[]","parts: React.ReactNode[]","exec","err: any","supportsVision","chunks: string[]","join","tmpdir","copyToClipboard","cmd: string","LANG_EXTENSIONS: Record<string, string>","cmd: string","STEPS","input","EXAMPLES: Record<CapabilityId, Partial<Record<FrameworkId, Example>>>","err: any","metadata: Record<string, { sizeBytes?: number; contextLength?: number }>","info: HFModelDetails","models: HFModel[]","e: any","tabOrder: Array<\"preset\" | \"voice\" | \"huggingface\">","modes: SortMode[]","filters: SizeFilter[]","models: Array<{\n id: string;\n name: string;\n hfId: string;\n size: string;\n contextLength?: number;\n speed?: string;\n isCached: boolean;\n isRecommended: boolean;\n lastUsed?: string;\n benchStats?: { avgTokPerSec: number };\n }>","SKILL_DETAILS: Record<\n string,\n {\n emoji: string;\n what: string;\n example: string;\n cliExample?: string;\n inputHint: string;\n isVision?: boolean;\n }\n>","parsedInput: Record<string, unknown>","result","allTools: ToolInfo[]","toolPath: string | undefined","params: any","ctx: ViewContext","col","pendingCleanup: Promise<void> | null","sigintTimer: NodeJS.Timeout | null","skills.commit","skills.summarize","skills.explain","skills.review","fs","os","path","e: any","Gerbil","opts","models: {\n name: string;\n size: number;\n mtime: Date;\n path: string;\n }[]","orphanPids: number[]","execSync","results: {\n tokPerSec: number;\n firstToken: number;\n tokens: number;\n }[]","checkForUpdate","CURRENT_VERSION","installUpdate"],"sources":["../package.json","../src/cli/repl/auto-update.ts","../src/cli/repl/utils.ts","../src/cli/repl/views/BenchmarkView.tsx","../src/core/microphone.ts","../src/cli/repl/views/ChatView.tsx","../src/cli/repl/views/CodeView.tsx","../src/cli/repl/views/CreateSkillView.tsx","../src/cli/repl/views/CreateToolView.tsx","../src/cli/repl/views/FrameworksView.tsx","../src/cli/repl/views/InfoView.tsx","../src/cli/repl/views/LoadingView.tsx","../src/cli/repl/views/ModelView.tsx","../src/cli/repl/views/ServeView.tsx","../src/cli/repl/views/SkillsView.tsx","../src/cli/repl/views/ToolsView.tsx","../src/cli/repl/App.tsx","../src/cli/repl/index.tsx","../src/cli/index.ts"],"sourcesContent":["{\n \"name\": \"@tryhamster/gerbil\",\n \"version\": \"1.0.0-rc.1\",\n \"description\": \"Local LLM inference for Node.js. GPU-accelerated. Zero config. Works standalone or with Vercel AI SDK.\",\n \"type\": \"module\",\n \"main\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.mts\",\n \"bin\": {\n \"gerbil\": \"./bin/cli.js\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.mts\"\n },\n \"./skills\": {\n \"import\": \"./dist/skills/index.mjs\",\n \"types\": \"./dist/skills/index.d.mts\"\n },\n \"./ai\": {\n \"import\": \"./dist/integrations/ai-sdk.mjs\",\n \"types\": \"./dist/integrations/ai-sdk.d.mts\"\n },\n \"./react\": {\n \"import\": \"./dist/frameworks/react.mjs\",\n \"types\": \"./dist/frameworks/react.d.mts\"\n },\n \"./next\": {\n \"import\": \"./dist/frameworks/next.mjs\",\n \"types\": \"./dist/frameworks/next.d.mts\"\n },\n \"./express\": {\n \"import\": \"./dist/frameworks/express.mjs\",\n \"types\": \"./dist/frameworks/express.d.mts\"\n },\n \"./fastify\": {\n \"import\": \"./dist/frameworks/fastify.mjs\",\n \"types\": \"./dist/frameworks/fastify.d.mts\"\n },\n \"./hono\": {\n \"import\": \"./dist/frameworks/hono.mjs\",\n \"types\": \"./dist/frameworks/hono.d.mts\"\n },\n \"./trpc\": {\n \"import\": \"./dist/frameworks/trpc.mjs\",\n \"types\": \"./dist/frameworks/trpc.d.mts\"\n },\n \"./langchain\": {\n \"import\": \"./dist/integrations/langchain.mjs\",\n \"types\": \"./dist/integrations/langchain.d.mts\"\n },\n \"./llamaindex\": {\n \"import\": \"./dist/integrations/llamaindex.mjs\",\n \"types\": \"./dist/integrations/llamaindex.d.mts\"\n },\n \"./mcp\": {\n \"import\": \"./dist/integrations/mcp.mjs\",\n \"types\": \"./dist/integrations/mcp.d.mts\"\n },\n \"./mcp-client\": {\n \"import\": \"./dist/integrations/mcp-client.mjs\",\n \"types\": \"./dist/integrations/mcp-client.d.mts\"\n },\n \"./browser\": {\n \"import\": \"./dist/browser/index.mjs\",\n \"types\": \"./dist/browser/index.d.mts\"\n }\n },\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"ultracite check\",\n \"fix\": \"ultracite fix\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"pnpm build\",\n \"changeset\": \"changeset\",\n \"version\": \"changeset version\",\n \"release\": \"pnpm publish && changeset tag\",\n \"prepare\": \"lefthook install\"\n },\n \"dependencies\": {\n \"@huggingface/hub\": \"^2.7.1\",\n \"@huggingface/transformers\": \"^3.8.0\",\n \"chalk\": \"^5.3.0\",\n \"cli-progress\": \"^3.12.0\",\n \"commander\": \"^12.1.0\",\n \"kokoro-js\": \"^1.2.1\",\n \"onnxruntime-web\": \"^1.21.0-dev.20250114-228dd16893\",\n \"ora\": \"^8.0.1\",\n \"puppeteer-core\": \"^24.31.0\",\n \"react\": \"^19.0.0\",\n \"webgpu\": \"^0.3.8\",\n \"zod\": \"^3.23.0\"\n },\n \"peerDependencies\": {\n \"@ai-sdk/provider\": \">=2.0.0\",\n \"@modelcontextprotocol/sdk\": \">=0.5.0\",\n \"@trpc/server\": \">=10.0.0\",\n \"ai\": \">=5.0.0\",\n \"express\": \">=4.0.0\",\n \"fastify\": \">=4.0.0\",\n \"hono\": \">=4.0.0\",\n \"langchain\": \">=0.1.0\",\n \"llamaindex\": \">=0.1.0\",\n \"next\": \">=14.0.0\"\n },\n \"peerDependenciesMeta\": {\n \"@ai-sdk/provider\": {\n \"optional\": true\n },\n \"@modelcontextprotocol/sdk\": {\n \"optional\": true\n },\n \"@trpc/server\": {\n \"optional\": true\n },\n \"ai\": {\n \"optional\": true\n },\n \"express\": {\n \"optional\": true\n },\n \"fastify\": {\n \"optional\": true\n },\n \"hono\": {\n \"optional\": true\n },\n \"langchain\": {\n \"optional\": true\n },\n \"llamaindex\": {\n \"optional\": true\n },\n \"next\": {\n \"optional\": true\n },\n \"react\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@ai-sdk/provider\": \"^2.0.0\",\n \"@biomejs/biome\": \"^2.3.8\",\n \"@changesets/changelog-github\": \"^0.5.1\",\n \"@changesets/cli\": \"^2.28.1\",\n \"@types/cli-progress\": \"^3.11.6\",\n \"@types/express\": \"^4.17.21\",\n \"@types/ink-big-text\": \"^1.2.4\",\n \"@types/node\": \"^20.14.0\",\n \"@types/react\": \"^19.0.0\",\n \"ai\": \"^5.0.0\",\n \"express\": \"^4.19.0\",\n \"ink\": \"^6.5.1\",\n \"ink-big-text\": \"^2.0.0\",\n \"ink-gradient\": \"^3.0.0\",\n \"ink-select-input\": \"^6.2.0\",\n \"ink-spinner\": \"^5.0.0\",\n \"ink-text-input\": \"^6.0.0\",\n \"lefthook\": \"^2.0.5\",\n \"tsdown\": \"^0.17.0-beta.3\",\n \"tsx\": \"^4.15.0\",\n \"typescript\": \"^5.4.5\",\n \"ultracite\": \"6.3.8\",\n \"vitest\": \"^1.6.0\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"keywords\": [\n \"llm\",\n \"local\",\n \"gpu\",\n \"webgpu\",\n \"inference\",\n \"ai-sdk\",\n \"transformers\",\n \"qwen\",\n \"thinking\",\n \"mcp\",\n \"langchain\",\n \"nextjs\",\n \"express\",\n \"cli\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Hamster <hello@tryhamster.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/gethamster/gerbil.git\"\n },\n \"homepage\": \"https://github.com/gethamster/gerbil#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/gethamster/gerbil/issues\"\n },\n \"files\": [\n \"dist\",\n \"bin\",\n \"docs\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execAsync = promisify(exec);\n\nconst CURRENT_VERSION = \"1.0.0\";\nconst PACKAGE_NAME = \"@tryhamster/gerbil\";\n\nexport type UpdateCheckResult = {\n updateAvailable: boolean;\n currentVersion: string;\n latestVersion?: string;\n error?: string;\n};\n\nexport type UpdateInstallResult = {\n success: boolean;\n version?: string;\n error?: string;\n};\n\n/**\n * Check if an update is available (non-blocking, no install)\n */\nexport async function checkForUpdate(): Promise<UpdateCheckResult> {\n try {\n // Check latest version from npm\n const { stdout } = await execAsync(`npm view ${PACKAGE_NAME} version`, {\n timeout: 5000,\n });\n const latestVersion = stdout.trim();\n\n // Compare versions\n if (compareVersions(latestVersion, CURRENT_VERSION) > 0) {\n return {\n updateAvailable: true,\n currentVersion: CURRENT_VERSION,\n latestVersion,\n };\n }\n\n return {\n updateAvailable: false,\n currentVersion: CURRENT_VERSION,\n latestVersion,\n };\n } catch (error) {\n // Silent fail - network issues, npm not available, etc.\n return {\n updateAvailable: false,\n currentVersion: CURRENT_VERSION,\n error: `Update check failed: ${error}`,\n };\n }\n}\n\n/**\n * Install the latest version\n */\nexport async function installUpdate(): Promise<UpdateInstallResult> {\n try {\n const { stdout } = await execAsync(`npm install -g ${PACKAGE_NAME}@latest`, {\n timeout: 60_000, // 60s for download + install\n });\n\n // Try to extract version from output\n const versionMatch = stdout.match(/\\+ @tryhamster\\/gerbil@([\\d.]+)/);\n const version = versionMatch ? versionMatch[1] : undefined;\n\n return {\n success: true,\n version,\n };\n } catch (error) {\n return {\n success: false,\n error: `Failed to install update: ${error}`,\n };\n }\n}\n\n/**\n * Compare two semver versions\n * Returns: 1 if a > b, -1 if a < b, 0 if equal\n */\nexport function compareVersions(a: string, b: string): number {\n const aParts = a.split(\".\").map(Number);\n const bParts = b.split(\".\").map(Number);\n\n for (let i = 0; i < 3; i += 1) {\n const aPart = aParts[i] || 0;\n const bPart = bParts[i] || 0;\n if (aPart > bPart) {\n return 1;\n }\n if (aPart < bPart) {\n return -1;\n }\n }\n return 0;\n}\n\nexport { CURRENT_VERSION };\n","/**\n * Shared utilities for the REPL\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { getChromeCachedModels, refreshCachedModelSizes } from \"../../core/chrome-backend.js\";\n\n/**\n * Format bytes to human readable string\n */\nexport function formatBytes(bytes: number): string {\n if (!bytes || bytes === 0) {\n return \"\";\n }\n if (bytes >= 1e9) {\n return `${(bytes / 1e9).toFixed(1)}GB`;\n }\n if (bytes >= 1e6) {\n return `${(bytes / 1e6).toFixed(0)}MB`;\n }\n if (bytes >= 1e3) {\n return `${(bytes / 1e3).toFixed(0)}KB`;\n }\n return `${bytes}B`;\n}\n\n/**\n * Format time ago string\n */\nexport function formatTimeAgo(date: Date): string {\n const seconds = Math.floor((Date.now() - date.getTime()) / 1000);\n if (seconds < 60) {\n return \"just now\";\n }\n if (seconds < 3600) {\n return `${Math.floor(seconds / 60)}m ago`;\n }\n if (seconds < 86_400) {\n return `${Math.floor(seconds / 3600)}h ago`;\n }\n if (seconds < 604_800) {\n return `${Math.floor(seconds / 86_400)}d ago`;\n }\n return `${Math.floor(seconds / 604_800)}w ago`;\n}\n\n/**\n * Format download count\n */\nexport function formatDownloads(n: number): string {\n if (n >= 1_000_000) {\n return `${(n / 1_000_000).toFixed(1)}M dl`;\n }\n if (n >= 1000) {\n return `${(n / 1000).toFixed(1)}K dl`;\n }\n return `${n} dl`;\n}\n\n/**\n * Get Gerbil models directory (local project models)\n */\nexport function getGerbilModelsDir(): string {\n return path.join(process.cwd(), \".gerbil\", \"models\");\n}\n\n/**\n * Get transformers.js cache directory (downloaded HF models)\n */\nexport function getTransformersCacheDir(): string {\n const nodeModulesCache = path.join(\n process.cwd(),\n \"node_modules\",\n \"@huggingface\",\n \"transformers\",\n \".cache\",\n );\n if (fs.existsSync(nodeModulesCache)) {\n return nodeModulesCache;\n }\n return (\n process.env.HF_HOME ||\n process.env.TRANSFORMERS_CACHE ||\n path.join(os.homedir(), \".cache\", \"huggingface\", \"hub\")\n );\n}\n\n/**\n * Get all cache directories that might contain models\n */\nexport function getAllCacheDirs(): string[] {\n return [getGerbilModelsDir(), getTransformersCacheDir()].filter((dir) => fs.existsSync(dir));\n}\n\n/**\n * Check if a model is cached locally (checks gerbil, transformers, and Chrome cache)\n */\nexport function isModelCached(hfId: string): boolean {\n const [org, model] = hfId.split(\"/\");\n if (!(org && model)) {\n return false;\n }\n\n // Check Chrome IndexedDB cache first (tracked in JSON file)\n try {\n const chromeCached = getChromeCachedModels();\n if (chromeCached.some((m) => m.modelId === hfId)) {\n return true;\n }\n } catch {\n // Chrome cache tracking not available\n }\n\n // Check filesystem cache locations\n const cacheDirs = [getGerbilModelsDir(), getTransformersCacheDir()];\n\n for (const cacheDir of cacheDirs) {\n const possiblePaths = [\n path.join(cacheDir, org, model),\n path.join(cacheDir, hfId.replace(\"/\", \"--\")),\n path.join(cacheDir, `models--${org}--${model}`),\n ];\n\n for (const p of possiblePaths) {\n try {\n if (fs.existsSync(p)) {\n const stat = fs.statSync(p);\n if (stat.isDirectory()) {\n const files = fs.readdirSync(p, { recursive: true });\n const hasOnnxModel = files.some((f: any) => {\n const fname = String(f).toLowerCase();\n return (\n fname.endsWith(\".onnx\") ||\n fname.endsWith(\"model.safetensors\") ||\n fname.includes(\"decoder_model\") ||\n fname.includes(\"encoder_model\")\n );\n });\n if (hasOnnxModel) {\n return true;\n }\n }\n }\n } catch {}\n }\n }\n\n return false;\n}\n\n/** Detailed model info from cache */\nexport type CachedModelInfo = {\n name: string;\n modelSize: string; // ONNX model files only\n totalSize: string; // All files (including shaders, config, etc.)\n lastUsed: string;\n location: string;\n contextLength?: number; // Model's max context window\n};\n\n/**\n * Scan a single cache directory for models\n */\nfunction scanCacheDir(cacheDir: string, models: CachedModelInfo[]): number {\n let totalBytes = 0;\n\n try {\n if (!fs.existsSync(cacheDir)) {\n return 0;\n }\n\n const entries = fs.readdirSync(cacheDir);\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry);\n const entryStat = fs.statSync(entryPath);\n\n if (!entryStat.isDirectory()) {\n continue;\n }\n\n let modelName = entry;\n if (entry.startsWith(\"models--\")) {\n modelName = entry.replace(\"models--\", \"\").replace(\"--\", \"/\");\n }\n\n const subEntries = fs.readdirSync(entryPath);\n for (const subEntry of subEntries) {\n const subPath = path.join(entryPath, subEntry);\n try {\n const subStat = fs.statSync(subPath);\n if (subStat.isDirectory()) {\n const fullModelName = `${entry}/${subEntry}`;\n let totalSize = 0;\n let modelFileSize = 0;\n try {\n const files = fs.readdirSync(subPath, { recursive: true });\n for (const file of files) {\n try {\n const filePath = path.join(subPath, String(file));\n const fileStat = fs.statSync(filePath);\n if (fileStat.isFile()) {\n totalSize += fileStat.size;\n const fname = String(file).toLowerCase();\n if (fname.endsWith(\".onnx\") || fname.endsWith(\".safetensors\")) {\n modelFileSize += fileStat.size;\n }\n }\n } catch {}\n }\n } catch {\n totalSize = subStat.size;\n }\n\n if (totalSize > 0) {\n totalBytes += totalSize;\n const lastUsed = formatTimeAgo(subStat.mtime);\n models.push({\n name: fullModelName,\n modelSize: formatBytes(modelFileSize || totalSize),\n totalSize: formatBytes(totalSize),\n lastUsed,\n location: cacheDir,\n });\n }\n }\n } catch {}\n }\n\n const hasModelFiles = subEntries.some((f) => f.endsWith(\".onnx\") || f.endsWith(\".json\"));\n if (hasModelFiles) {\n let totalSize = 0;\n let modelFileSize = 0;\n try {\n const files = fs.readdirSync(entryPath, { recursive: true });\n for (const file of files) {\n try {\n const filePath = path.join(entryPath, String(file));\n const fileStat = fs.statSync(filePath);\n if (fileStat.isFile()) {\n totalSize += fileStat.size;\n const fname = String(file).toLowerCase();\n if (fname.endsWith(\".onnx\") || fname.endsWith(\".safetensors\")) {\n modelFileSize += fileStat.size;\n }\n }\n } catch {}\n }\n } catch {\n totalSize = entryStat.size;\n }\n\n totalBytes += totalSize;\n const lastUsed = formatTimeAgo(entryStat.mtime);\n models.push({\n name: modelName,\n modelSize: formatBytes(modelFileSize || totalSize),\n totalSize: formatBytes(totalSize),\n lastUsed,\n location: cacheDir,\n });\n }\n }\n } catch {}\n\n return totalBytes;\n}\n\n/**\n * Get cache info from all cache locations\n */\nexport function getCacheInfo(): {\n locations: string[];\n totalSize: string;\n models: CachedModelInfo[];\n} {\n const gerbilDir = getGerbilModelsDir();\n const transformersDir = getTransformersCacheDir();\n const models: CachedModelInfo[] = [];\n\n let totalBytes = 0;\n totalBytes += scanCacheDir(gerbilDir, models);\n totalBytes += scanCacheDir(transformersDir, models);\n\n // Add Chrome-cached models (stored in IndexedDB, tracked in JSON)\n let hasMissingSizes = false;\n try {\n const chromeCached = getChromeCachedModels();\n for (const entry of chromeCached) {\n // Check if already in list from filesystem scan\n const exists = models.some(\n (m) =>\n m.name === entry.modelId ||\n m.name.replace(\"--\", \"/\") === entry.modelId ||\n entry.modelId.replace(\"/\", \"--\") === m.name,\n );\n\n if (!exists) {\n const lastUsed = entry.lastUsed ? formatTimeAgo(new Date(entry.lastUsed)) : \"unknown\";\n if (!(entry.sizeBytes && entry.contextLength)) {\n hasMissingSizes = true;\n }\n models.push({\n name: entry.modelId,\n modelSize: entry.sizeBytes ? formatBytes(entry.sizeBytes) : \"~\",\n totalSize: entry.sizeBytes ? formatBytes(entry.sizeBytes) : \"~\",\n lastUsed,\n location: \"Chrome IndexedDB\",\n contextLength: entry.contextLength,\n });\n if (entry.sizeBytes) {\n totalBytes += entry.sizeBytes;\n }\n }\n }\n\n // Trigger background refresh for missing sizes\n if (hasMissingSizes) {\n refreshCachedModelSizes().catch(() => {});\n }\n } catch {\n // Chrome cache tracking not available\n }\n\n // Get active locations that have models\n const activeLocations = [...new Set(models.map((m) => m.location))];\n\n return {\n locations: activeLocations.length > 0 ? activeLocations : [gerbilDir, transformersDir],\n totalSize: formatBytes(totalBytes),\n models: models.sort((a, b) => a.name.localeCompare(b.name)),\n };\n}\n\n/**\n * Get system memory info\n */\nexport function getMemoryInfo(): {\n total: string;\n used: string;\n free: string;\n percentUsed: number;\n} {\n const totalMem = os.totalmem();\n const freeMem = os.freemem();\n const usedMem = totalMem - freeMem;\n const percentUsed = Math.round((usedMem / totalMem) * 100);\n\n return {\n total: formatBytes(totalMem),\n used: formatBytes(usedMem),\n free: formatBytes(freeMem),\n percentUsed,\n };\n}\n\n/**\n * OSC 8 hyperlink helper (clickable links in terminal)\n */\nexport function hyperlink(url: string, text: string): string {\n return `\\x1b]8;;${url}\\x07${text}\\x1b]8;;\\x07`;\n}\n\n/**\n * Clean AI response - remove think tags and trim\n */\nexport function cleanResponse(text: string): string {\n return text\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\")\n .replace(/<\\/?think>/g, \"\")\n .trim();\n}\n\n/**\n * Preset models with known performance\n */\nexport const PRESET_MODELS = [\n {\n id: \"qwen3-0.6b\",\n name: \"Qwen3 0.6B\",\n size: \"~400MB\",\n speed: \"fastest\",\n hfId: \"onnx-community/Qwen3-0.6B-ONNX\",\n },\n {\n id: \"qwen2.5-0.5b\",\n name: \"Qwen2.5 0.5B\",\n size: \"~350MB\",\n speed: \"fast\",\n hfId: \"onnx-community/Qwen2.5-0.5B-Instruct\",\n },\n {\n id: \"qwen2.5-coder-0.5b\",\n name: \"Qwen2.5 Coder\",\n size: \"~400MB\",\n speed: \"fast\",\n hfId: \"onnx-community/Qwen2.5-Coder-0.5B-Instruct\",\n },\n {\n id: \"smollm2-360m\",\n name: \"SmolLM2 360M\",\n size: \"~250MB\",\n speed: \"fastest\",\n hfId: \"HuggingFaceTB/SmolLM2-360M-Instruct\",\n },\n {\n id: \"smollm2-135m\",\n name: \"SmolLM2 135M\",\n size: \"~100MB\",\n speed: \"fastest\",\n hfId: \"HuggingFaceTB/SmolLM2-135M-Instruct\",\n },\n {\n id: \"phi-3-mini\",\n name: \"Phi-3 Mini\",\n size: \"~2.1GB\",\n speed: \"medium\",\n hfId: \"microsoft/Phi-3-mini-4k-instruct-onnx\",\n },\n];\n\n/**\n * Fetch model metadata from HuggingFace (size and context length)\n */\nexport async function fetchModelMetadata(\n modelId: string,\n): Promise<{ sizeBytes?: number; contextLength?: number }> {\n const result: { sizeBytes?: number; contextLength?: number } = {};\n\n // Priority 1: config.json → max_position_embeddings (actual architectural limit)\n try {\n const configRes = await fetch(`https://huggingface.co/${modelId}/raw/main/config.json`);\n if (configRes.ok) {\n const config = await configRes.json();\n // Check root level first, then nested text_config (for multimodal models like Ministral)\n const textConfig = config.text_config || {};\n result.contextLength =\n config.max_position_embeddings ||\n textConfig.max_position_embeddings ||\n config.sliding_window ||\n textConfig.sliding_window ||\n config.max_seq_len ||\n config.max_sequence_length ||\n config.n_ctx ||\n config.n_positions;\n }\n } catch {\n // Continue to fallback\n }\n\n // Priority 2: tokenizer_config.json → model_max_length (fallback only)\n if (!result.contextLength) {\n try {\n const tokRes = await fetch(\n `https://huggingface.co/${modelId}/raw/main/tokenizer_config.json`,\n );\n if (tokRes.ok) {\n const tokConfig = await tokRes.json();\n // Only use if reasonable (< 1M tokens - anything larger is likely placeholder)\n if (tokConfig.model_max_length && tokConfig.model_max_length < 1e6) {\n result.contextLength = tokConfig.model_max_length;\n }\n }\n } catch {\n // Ignore\n }\n }\n\n // Fetch size from tree API (handles LFS files)\n try {\n const treeRes = await fetch(`https://huggingface.co/api/models/${modelId}/tree/main/onnx`);\n if (treeRes.ok) {\n const files: { path: string; size?: number; lfs?: { size?: number } }[] =\n await treeRes.json();\n\n // Get file size (LFS or regular)\n const getSize = (f: { size?: number; lfs?: { size?: number } }) => f.lfs?.size || f.size || 0;\n\n // Prefer q4f16 > q4 > fp16 > any .onnx file\n const q4f16 = files.find((f) => f.path.includes(\"q4f16\") && f.path.endsWith(\".onnx\"));\n const q4 = files.find(\n (f) => f.path.includes(\"q4\") && !f.path.includes(\"f16\") && f.path.endsWith(\".onnx\"),\n );\n const fp16 = files.find((f) => f.path.includes(\"fp16\") && f.path.endsWith(\".onnx\"));\n const anyOnnx = files.find((f) => f.path.endsWith(\".onnx\"));\n const bestFile = q4f16 || q4 || fp16 || anyOnnx;\n\n if (bestFile) {\n const baseName = bestFile.path.replace(\".onnx\", \"\");\n const relatedFiles = files.filter(\n (f) => f.path === bestFile.path || f.path.startsWith(`${baseName}.onnx_data`),\n );\n result.sizeBytes = relatedFiles.reduce((sum, f) => sum + getSize(f), 0);\n }\n }\n } catch {\n // Ignore tree fetch errors\n }\n\n return result;\n}\n\n/**\n * Session stats interface\n */\nexport type SessionStats = {\n prompts: number;\n tokensIn: number;\n tokensOut: number;\n totalTimeMs: number;\n startTime: number;\n benchResults: { model: string; tokPerSec: number }[];\n /** Last message tok/s */\n lastTokPerSec: number;\n /** Average tok/s across all messages */\n avgTokPerSec: number;\n};\n\n// ============================================\n// Benchmark Persistence\n// ============================================\n\nexport type StoredBenchmark = {\n timestamp: string;\n model: string;\n device: \"webgpu\" | \"cpu\" | \"wasm\";\n tokensPerSec: number;\n firstTokenMs: number;\n totalTokens: number;\n totalMs: number;\n};\n\nconst BENCHMARKS_FILE = path.join(os.homedir(), \".gerbil\", \"benchmarks.json\");\n\n/**\n * Get all stored benchmarks\n */\nexport function getStoredBenchmarks(): StoredBenchmark[] {\n try {\n if (!fs.existsSync(BENCHMARKS_FILE)) {\n return [];\n }\n const data = JSON.parse(fs.readFileSync(BENCHMARKS_FILE, \"utf-8\"));\n return data.benchmarks || [];\n } catch {\n return [];\n }\n}\n\n/**\n * Save a benchmark result\n */\nexport function saveBenchmark(result: {\n model: string;\n device: \"webgpu\" | \"cpu\" | \"wasm\";\n tokensPerSec: number;\n firstTokenMs: number;\n totalTokens: number;\n totalMs: number;\n}): void {\n try {\n const dir = path.dirname(BENCHMARKS_FILE);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const benchmarks = getStoredBenchmarks();\n benchmarks.push({\n ...result,\n timestamp: new Date().toISOString(),\n });\n\n // Keep last 1000 benchmarks\n const trimmed = benchmarks.slice(-1000);\n fs.writeFileSync(BENCHMARKS_FILE, JSON.stringify({ benchmarks: trimmed }, null, 2));\n } catch {\n // Ignore save errors\n }\n}\n\n/**\n * Get average benchmark stats for a model\n */\n/**\n * Normalize model ID for matching (extract core name)\n * e.g., \"HuggingFaceTB/SmolLM2-135M-Instruct\" -> \"smollm2-135m\"\n * \"onnx-community/Qwen3-0.6B-ONNX\" -> \"qwen3-0.6b\"\n * \"qwen3-0.6b\" -> \"qwen3-0.6b\"\n */\nfunction normalizeModelId(id: string): string {\n // Get the last part after /\n const name = id.split(\"/\").pop() || id;\n // Remove common suffixes and convert to lowercase\n return name\n .toLowerCase()\n .replace(/-instruct$/i, \"\")\n .replace(/-onnx$/i, \"\")\n .replace(/-chat$/i, \"\")\n .replace(/-it$/i, \"\");\n}\n\n/**\n * Process an image path (URL or local file) and return info for attaching\n * Supports: URLs (http/https), local paths (including ~ expansion)\n * Returns data URI for local files, original URL for remote images\n */\nexport function processImagePath(imagePath: string): {\n success: boolean;\n message: string;\n imageSource?: string;\n filename?: string;\n} {\n const trimmedPath = imagePath.trim();\n if (!trimmedPath) {\n return { success: false, message: \"\" };\n }\n\n try {\n // Check if it's a URL\n if (trimmedPath.startsWith(\"http://\") || trimmedPath.startsWith(\"https://\")) {\n const urlFilename = trimmedPath.split(\"/\").pop()?.split(\"?\")[0] || \"image\";\n return {\n success: true,\n message: `📎 Attached URL: ${urlFilename}`,\n imageSource: trimmedPath,\n filename: urlFilename,\n };\n }\n\n // Local file path - convert to data URI\n const expandedPath = trimmedPath.startsWith(\"~\")\n ? trimmedPath.replace(\"~\", process.env.HOME || \"\")\n : trimmedPath;\n\n if (!fs.existsSync(expandedPath)) {\n return { success: false, message: `❌ File not found: ${trimmedPath}` };\n }\n\n const ext = path.extname(expandedPath).toLowerCase();\n const mimeTypes: Record<string, string> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n };\n const mimeType = mimeTypes[ext] || \"image/png\";\n const imageData = fs.readFileSync(expandedPath);\n const base64 = imageData.toString(\"base64\");\n const dataUri = `data:${mimeType};base64,${base64}`;\n const filename = path.basename(expandedPath);\n\n return {\n success: true,\n message: `📎 Attached: ${filename}`,\n imageSource: dataUri,\n filename,\n };\n } catch (e) {\n return { success: false, message: `❌ Error loading image: ${e}` };\n }\n}\n\nexport function getModelBenchmarkStats(modelId: string): {\n avgTokPerSec: number;\n avgFirstToken: number;\n runs: number;\n devices: Record<string, { avgTokPerSec: number; runs: number }>;\n} | null {\n const benchmarks = getStoredBenchmarks();\n const normalizedId = normalizeModelId(modelId);\n\n const modelBenchmarks = benchmarks.filter((b) => {\n const normalizedBench = normalizeModelId(b.model);\n return (\n normalizedBench === normalizedId ||\n normalizedBench.includes(normalizedId) ||\n normalizedId.includes(normalizedBench)\n );\n });\n\n if (modelBenchmarks.length === 0) {\n return null;\n }\n\n const avgTokPerSec = Math.round(\n modelBenchmarks.reduce((a, b) => a + b.tokensPerSec, 0) / modelBenchmarks.length,\n );\n const avgFirstToken = Math.round(\n modelBenchmarks.reduce((a, b) => a + b.firstTokenMs, 0) / modelBenchmarks.length,\n );\n\n // Group by device\n const devices: Record<string, { avgTokPerSec: number; runs: number }> = {};\n for (const b of modelBenchmarks) {\n if (!devices[b.device]) {\n devices[b.device] = { avgTokPerSec: 0, runs: 0 };\n }\n devices[b.device].runs += 1;\n }\n for (const device of Object.keys(devices)) {\n const deviceBenchmarks = modelBenchmarks.filter((b) => b.device === device);\n devices[device].avgTokPerSec = Math.round(\n deviceBenchmarks.reduce((a, b) => a + b.tokensPerSec, 0) / deviceBenchmarks.length,\n );\n }\n\n return { avgTokPerSec, avgFirstToken, runs: modelBenchmarks.length, devices };\n}\n","import { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport type React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\nimport { getStoredBenchmarks, type StoredBenchmark, saveBenchmark } from \"../utils.js\";\n\nexport type BenchmarkResult = {\n model: string;\n device: \"webgpu\" | \"cpu\" | \"wasm\";\n tokensPerSec: number;\n firstTokenMs: number;\n totalTokens: number;\n totalMs: number;\n};\n\nexport type BenchmarkStats = {\n runCount: number;\n lastResult?: BenchmarkResult;\n averages: Array<{\n model: string;\n device: string;\n avgTokPerSec: number;\n avgFirstToken: number;\n runs: number;\n }>;\n bests?: {\n fastestRun: BenchmarkResult;\n fastestFirstToken: BenchmarkResult;\n bestOverall: {\n model: string;\n device: string;\n avgTokPerSec: number;\n avgFirstToken: number;\n score: number;\n } | null;\n };\n};\n\ntype BenchmarkViewProps = {\n gerbil: Gerbil;\n model: string;\n disabled?: boolean;\n onResult?: (tokPerSec: number) => void;\n onSwitchModel?: () => void;\n onSwitchDevice?: (device: \"webgpu\" | \"cpu\") => void;\n onStatusChange?: (status: \"idle\" | \"running\" | \"done\", stats: BenchmarkStats) => void;\n benchmarkResults: BenchmarkResult[];\n setBenchmarkResults: React.Dispatch<React.SetStateAction<BenchmarkResult[]>>;\n benchmarkModels: string[];\n setBenchmarkModels: React.Dispatch<React.SetStateAction<string[]>>;\n};\n\nexport function BenchmarkView({\n gerbil,\n model,\n disabled = false,\n onResult,\n onSwitchModel,\n onSwitchDevice,\n onStatusChange,\n benchmarkResults,\n setBenchmarkResults,\n benchmarkModels,\n setBenchmarkModels,\n}: BenchmarkViewProps) {\n const [status, setStatus] = useState<\"idle\" | \"running\" | \"done\">(\"idle\");\n const [currentRun, setCurrentRun] = useState(benchmarkResults.length);\n const [showHistory, setShowHistory] = useState(false);\n const [historyData, setHistoryData] = useState<StoredBenchmark[]>([]);\n\n // Notify parent of status changes with full stats\n const updateStatus = (\n newStatus: \"idle\" | \"running\" | \"done\",\n result?: BenchmarkResult,\n allResults?: BenchmarkResult[],\n ) => {\n setStatus(newStatus);\n\n const results = allResults || benchmarkResults;\n\n // Calculate averages\n const resultKeys = [...new Set(results.map((r) => `${r.model}|${r.device}`))];\n const averages = resultKeys.map((key) => {\n const [m, d] = key.split(\"|\");\n const filtered = results.filter((r) => r.model === m && r.device === d);\n return {\n model: m,\n device: d,\n avgTokPerSec: Math.round(\n filtered.reduce((a, r) => a + r.tokensPerSec, 0) / filtered.length,\n ),\n avgFirstToken: Math.round(\n filtered.reduce((a, r) => a + r.firstTokenMs, 0) / filtered.length,\n ),\n runs: filtered.length,\n };\n });\n\n // Calculate bests\n const bests =\n results.length > 0\n ? (() => {\n const fastestRun = results.reduce((best, r) =>\n r.tokensPerSec > best.tokensPerSec ? r : best,\n );\n const fastestFirstToken = results.reduce((best, r) =>\n r.firstTokenMs < best.firstTokenMs ? r : best,\n );\n\n // Best overall: score = tok/s / (1 + firstTokenMs/100)\n const overallScores = averages.map((s) => ({\n ...s,\n score: s.avgTokPerSec / (1 + s.avgFirstToken / 100),\n }));\n const bestOverall =\n overallScores.length > 0\n ? overallScores.reduce((best, s) => (s.score > best.score ? s : best))\n : null;\n\n return { fastestRun, fastestFirstToken, bestOverall };\n })()\n : undefined;\n\n onStatusChange?.(newStatus, {\n runCount: currentRun + (newStatus === \"done\" ? 1 : 0),\n lastResult: result,\n averages,\n bests,\n });\n };\n\n useEffect(() => {\n if (!benchmarkModels.includes(model)) {\n setBenchmarkModels((prev) => [...prev.slice(-2), model]);\n }\n }, [model, benchmarkModels, setBenchmarkModels]);\n\n const currentDevice = gerbil.getDeviceMode();\n\n useInput((input, key) => {\n // Block all actions while disabled (one-liner generating) or running\n const blocked = disabled || status === \"running\";\n\n if (key.return && !blocked) {\n runBenchmark();\n }\n if ((input === \"r\" || input === \"R\") && !blocked) {\n setBenchmarkResults([]);\n setCurrentRun(0);\n setBenchmarkModels([model]);\n }\n if ((input === \"m\" || input === \"M\") && !blocked) {\n onSwitchModel?.();\n }\n // Toggle device mode with 'd'\n if ((input === \"d\" || input === \"D\") && !blocked) {\n const newDevice = currentDevice === \"webgpu\" ? \"cpu\" : \"webgpu\";\n onSwitchDevice?.(newDevice);\n }\n // Toggle history with 'h'\n if ((input === \"h\" || input === \"H\") && !blocked) {\n if (!showHistory) {\n // Load history when opening\n const stored = getStoredBenchmarks();\n setHistoryData(stored);\n }\n setShowHistory(!showHistory);\n }\n // Load history into current session with 'l'\n if ((input === \"l\" || input === \"L\") && !blocked && showHistory) {\n const stored = getStoredBenchmarks();\n const converted: BenchmarkResult[] = stored.map((s) => ({\n model: s.model,\n device: s.device,\n tokensPerSec: s.tokensPerSec,\n firstTokenMs: s.firstTokenMs,\n totalTokens: s.totalTokens,\n totalMs: s.totalMs,\n }));\n setBenchmarkResults(converted);\n setCurrentRun(converted.length);\n // Collect unique models\n const models = [...new Set(converted.map((r) => r.model))];\n setBenchmarkModels(models.length > 0 ? models.slice(-3) : [model]);\n setShowHistory(false);\n }\n });\n\n const runBenchmark = async () => {\n updateStatus(\"running\");\n const prompts = [\n \"Write a haiku about programming.\",\n \"Explain recursion in one sentence.\",\n \"List 3 benefits of TypeScript.\",\n ];\n const prompt = prompts[currentRun % prompts.length];\n\n // Use generate() to get actual token counts from the result\n // This gives us real tokensGenerated and tokensPerSecond from transformers.js\n const startTime = Date.now();\n let firstTokenTime = 0;\n let gotFirstToken = false;\n\n const result = await gerbil.generate(prompt, {\n maxTokens: 100,\n onToken: () => {\n if (!gotFirstToken) {\n firstTokenTime = Date.now() - startTime;\n gotFirstToken = true;\n }\n },\n });\n\n const device = gerbil.getDeviceMode();\n const newResult: BenchmarkResult = {\n model,\n device,\n // Use actual metrics from generation result\n tokensPerSec: Math.round(result.tokensPerSecond),\n firstTokenMs: firstTokenTime || Math.round(result.totalTime * 0.1), // fallback estimate\n totalTokens: result.tokensGenerated,\n totalMs: Math.round(result.totalTime),\n };\n\n // Persist benchmark to ~/.gerbil/benchmarks.json\n saveBenchmark(newResult);\n\n const updatedResults = [...benchmarkResults, newResult];\n setBenchmarkResults(updatedResults);\n setCurrentRun((prev) => prev + 1);\n updateStatus(\"done\", newResult, updatedResults);\n onResult?.(newResult.tokensPerSec);\n };\n\n const currentResults = benchmarkResults.filter(\n (r) => r.model === model && r.device === currentDevice,\n );\n\n // Group results by model+device combination\n const resultKeys = [...new Set(benchmarkResults.map((r) => `${r.model}|${r.device}`))];\n const modelStats = resultKeys\n .map((key) => {\n const [m, d] = key.split(\"|\");\n const results = benchmarkResults.filter((r) => r.model === m && r.device === d);\n if (results.length === 0) {\n return null;\n }\n return {\n model: m,\n device: d as \"webgpu\" | \"cpu\" | \"wasm\",\n runs: results.length,\n avgTokPerSec: Math.round(results.reduce((a, r) => a + r.tokensPerSec, 0) / results.length),\n avgFirstToken: Math.round(results.reduce((a, r) => a + r.firstTokenMs, 0) / results.length),\n };\n })\n .filter(Boolean) as {\n model: string;\n device: string;\n runs: number;\n avgTokPerSec: number;\n avgFirstToken: number;\n }[];\n\n const bestTokPerSec =\n modelStats.length > 0 ? Math.max(...modelStats.map((s) => s.avgTokPerSec)) : 0;\n\n // Calculate \"bests\" across all runs\n // Best overall: score = tok/s / (1 + firstTokenMs/100) - rewards speed, penalizes slow first token\n const bests =\n benchmarkResults.length > 0\n ? (() => {\n const fastestRun = benchmarkResults.reduce((best, r) =>\n r.tokensPerSec > best.tokensPerSec ? r : best,\n );\n const fastestFirstToken = benchmarkResults.reduce((best, r) =>\n r.firstTokenMs < best.firstTokenMs ? r : best,\n );\n\n // Calculate overall score for each model+device combo using averages\n const overallScores = modelStats.map((s) => ({\n ...s,\n // Score: tok/s weighted against first token penalty\n // Higher tok/s is better, lower first token is better\n score: s.avgTokPerSec / (1 + s.avgFirstToken / 100),\n }));\n const bestOverall =\n overallScores.length > 0\n ? overallScores.reduce((best, s) => (s.score > best.score ? s : best))\n : null;\n\n return { fastestRun, fastestFirstToken, bestOverall };\n })()\n : null;\n\n const deviceColor =\n currentDevice === \"webgpu\" ? \"green\" : currentDevice === \"cpu\" ? \"yellow\" : \"gray\";\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box>\n <Text bold>Benchmark: </Text>\n <Text bold color=\"cyan\">\n {model}\n </Text>\n <Text> on </Text>\n <Text bold color={deviceColor}>\n {currentDevice.toUpperCase()}\n </Text>\n {modelStats.length > 1 && <Text dimColor> (comparing {modelStats.length} configs)</Text>}\n </Box>\n <Text dimColor>Run multiple times for accurate averages</Text>\n\n {currentResults.length > 0 && (\n <Box flexDirection=\"column\" marginY={1}>\n <Text bold color=\"cyan\">\n Results for {model} on {currentDevice.toUpperCase()} ({currentResults.length} runs):\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n {currentResults.slice(-6).map((r, i) => (\n <Text dimColor={i < currentResults.length - 1} key={i}>\n Run {i + 1}: <Text color=\"cyan\">{r.tokensPerSec}</Text> tok/s,{\" \"}\n <Text color=\"yellow\">{r.firstTokenMs}ms</Text> first token, {r.totalTokens} tokens\n </Text>\n ))}\n </Box>\n </Box>\n )}\n\n <Box flexDirection=\"row\" marginY={1}>\n {modelStats.length > 0 && (\n <Box\n borderColor=\"green\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n paddingX={1}\n >\n <Text bold color=\"green\">\n {modelStats.length > 1 ? \"Comparison:\" : \"Averages:\"}\n </Text>\n {modelStats.map((s) => {\n const isCurrent = s.model === model && s.device === currentDevice;\n const dColor =\n s.device === \"webgpu\" ? \"green\" : s.device === \"cpu\" ? \"yellow\" : \"gray\";\n return (\n <Box key={`${s.model}-${s.device}`}>\n <Text>\n {isCurrent ? \">\" : \" \"} {s.model}\n </Text>\n <Text color={dColor}> [{s.device}]</Text>\n <Text>: </Text>\n <Text\n bold\n color={\n s.avgTokPerSec === bestTokPerSec && modelStats.length > 1 ? \"green\" : \"cyan\"\n }\n >\n {s.avgTokPerSec} tok/s\n </Text>\n <Text>, </Text>\n <Text color=\"yellow\">{s.avgFirstToken}ms</Text>\n <Text dimColor> ({s.runs} runs)</Text>\n {s.avgTokPerSec === bestTokPerSec && modelStats.length > 1 && (\n <Text color=\"green\"> *fastest*</Text>\n )}\n </Box>\n );\n })}\n </Box>\n )}\n\n {bests && benchmarkResults.length >= 2 && (\n <Box borderColor=\"magenta\" borderStyle=\"single\" flexDirection=\"column\" paddingX={1}>\n <Text bold color=\"magenta\">\n Records:\n </Text>\n <Text>\n <Text color=\"cyan\">» Speed:</Text>{\" \"}\n <Text bold color=\"green\">\n {bests.fastestRun.tokensPerSec}\n </Text>{\" \"}\n tok/s\n <Text dimColor>\n {\" \"}\n ({bests.fastestRun.model} on {bests.fastestRun.device})\n </Text>\n </Text>\n <Text>\n <Text color=\"cyan\">» First:</Text>{\" \"}\n <Text bold color=\"yellow\">\n {bests.fastestFirstToken.firstTokenMs}\n </Text>\n ms\n <Text dimColor>\n {\" \"}\n ({bests.fastestFirstToken.model} on {bests.fastestFirstToken.device})\n </Text>\n </Text>\n {bests.bestOverall && (\n <Text>\n <Text color=\"cyan\">» Best:</Text>{\" \"}\n <Text bold color=\"magenta\">\n {bests.bestOverall.model}\n </Text>\n <Text dimColor>\n {\" \"}\n on {bests.bestOverall.device} ({bests.bestOverall.avgTokPerSec} tok/s,{\" \"}\n {bests.bestOverall.avgFirstToken}ms)\n </Text>\n </Text>\n )}\n </Box>\n )}\n </Box>\n\n {status === \"running\" && (\n <Box marginY={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" /> Running benchmark #{currentRun + 1}...\n </Text>\n </Box>\n )}\n\n {showHistory && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text bold color=\"gray\">\n Benchmark History ({historyData.length} runs)\n </Text>\n {historyData.length === 0 ? (\n <Text dimColor>No saved benchmarks found in ~/.gerbil/benchmarks.json</Text>\n ) : (\n <>\n <Box flexDirection=\"column\" marginY={1}>\n {historyData\n .slice(-10)\n .reverse()\n .map((b, i) => (\n <Text dimColor key={i}>\n <Text color=\"cyan\">{b.tokensPerSec}</Text> tok/s,{\" \"}\n <Text color=\"yellow\">{b.firstTokenMs}</Text>ms first\n <Text dimColor>\n {\" \"}\n - {b.model} [{b.device}] {new Date(b.timestamp).toLocaleDateString()}\n </Text>\n </Text>\n ))}\n {historyData.length > 10 && (\n <Text dimColor>... and {historyData.length - 10} more</Text>\n )}\n </Box>\n <Text dimColor>\n Press <Text color=\"yellow\">l</Text> to load all history into session |{\" \"}\n <Text color=\"yellow\">h</Text> to close\n </Text>\n </>\n )}\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Enter</Text> run | <Text color=\"yellow\">d</Text> switch{\" \"}\n {currentDevice === \"webgpu\" ? \"CPU\" : \"GPU\"} | <Text color=\"yellow\">m</Text> model |{\" \"}\n <Text color=\"yellow\">h</Text> history | <Text color=\"yellow\">r</Text> reset |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Microphone Recording for Node.js\n *\n * Provides microphone input support for speech-to-text in the CLI/REPL.\n * Requires `sox` to be installed on the system:\n * - macOS: `brew install sox`\n * - Ubuntu: `sudo apt install sox`\n * - Windows: Download from https://sox.sourceforge.net/\n *\n * @example\n * ```ts\n * const mic = new Microphone();\n * await mic.start();\n *\n * // Record for 5 seconds\n * await new Promise((r) => setTimeout(r, 5000));\n *\n * const audioData = await mic.stop();\n * // audioData is Float32Array at 16kHz, ready for Whisper\n * ```\n */\n\nimport { type ChildProcess, spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport interface MicrophoneOptions {\n /** Sample rate (default: 16000 for Whisper) */\n sampleRate?: number;\n /** Channels (default: 1 for mono) */\n channels?: number;\n /** Bit depth (default: 16) */\n bitDepth?: number;\n /** Recording device (platform-specific) */\n device?: string;\n /** Silence threshold for auto-stop (0-1, default: 0.01) */\n silenceThreshold?: number;\n /** Duration of silence before auto-stop in seconds (default: 2) */\n silenceDuration?: number;\n}\n\nexport interface StreamingMicrophoneOptions extends MicrophoneOptions {\n /** Callback called with each audio chunk (Float32Array at sampleRate) */\n onAudioChunk?: (audio: Float32Array) => void;\n /** Chunk duration in milliseconds (default: 500) */\n chunkDuration?: number;\n}\n\nexport interface RecordingResult {\n /** Audio data as Float32Array */\n audio: Float32Array;\n /** Sample rate (16000) */\n sampleRate: number;\n /** Recording duration in seconds */\n duration: number;\n}\n\n// Common SoX installation paths\nconst SOX_PATHS = [\n \"sox\", // In PATH\n \"/opt/homebrew/bin/sox\", // macOS Homebrew (ARM)\n \"/usr/local/bin/sox\", // macOS Homebrew (Intel) / Linux\n \"/usr/bin/sox\", // Linux system\n];\n\n/**\n * Find the sox executable path\n */\nexport function findSoxPath(): string | null {\n for (const soxPath of SOX_PATHS) {\n try {\n const result = spawnSync(soxPath, [\"--version\"], {\n encoding: \"utf-8\",\n stdio: \"pipe\",\n timeout: 2000,\n });\n if (result.status === 0) {\n return soxPath;\n }\n } catch {\n // Try next path\n }\n }\n return null;\n}\n\n/**\n * Check if sox is available on the system\n */\nexport function isSoxAvailable(): boolean {\n return findSoxPath() !== null;\n}\n\n/**\n * Microphone recorder using SoX\n */\nexport class Microphone {\n private process: ChildProcess | null = null;\n private tempFile: string | null = null;\n private options: Required<MicrophoneOptions>;\n private isRecording = false;\n\n constructor(options: MicrophoneOptions = {}) {\n this.options = {\n sampleRate: options.sampleRate ?? 16000,\n channels: options.channels ?? 1,\n bitDepth: options.bitDepth ?? 16,\n device: options.device ?? \"\",\n silenceThreshold: options.silenceThreshold ?? 0.01,\n silenceDuration: options.silenceDuration ?? 2,\n };\n }\n\n /**\n * Start recording from microphone\n */\n async start(): Promise<void> {\n if (this.isRecording) {\n throw new Error(\"Already recording\");\n }\n\n const soxPath = findSoxPath();\n if (!soxPath) {\n throw new Error(\n \"SoX is not installed. Install it with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\\n\" +\n \" Windows: https://sox.sourceforge.net/\",\n );\n }\n\n // Create temp file for recording\n this.tempFile = join(tmpdir(), `gerbil-mic-${Date.now()}.wav`);\n\n // Build sox command\n const args: string[] = [];\n\n // Input from default device\n if (process.platform === \"darwin\") {\n args.push(\"-d\"); // Default input device on macOS\n } else if (process.platform === \"linux\") {\n args.push(\"-t\", \"alsa\", \"default\");\n } else if (process.platform === \"win32\") {\n args.push(\"-t\", \"waveaudio\", \"default\");\n } else {\n args.push(\"-d\");\n }\n\n // Output format\n args.push(\n \"-r\",\n String(this.options.sampleRate),\n \"-c\",\n String(this.options.channels),\n \"-b\",\n String(this.options.bitDepth),\n this.tempFile,\n );\n\n this.process = spawn(soxPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n this.isRecording = true;\n\n // Handle errors\n this.process.on(\"error\", (err) => {\n console.error(\"Microphone error:\", err.message);\n this.isRecording = false;\n });\n }\n\n /**\n * Stop recording and return audio data\n */\n async stop(): Promise<RecordingResult> {\n if (!this.isRecording || !this.process) {\n throw new Error(\"Not recording\");\n }\n\n return new Promise((resolve, reject) => {\n this.process!.on(\"close\", () => {\n this.isRecording = false;\n\n try {\n if (!this.tempFile || !existsSync(this.tempFile)) {\n reject(new Error(\"Recording file not found\"));\n return;\n }\n\n // Read the WAV file\n const buffer = readFileSync(this.tempFile);\n\n // Parse WAV and convert to Float32Array\n const audio = this.parseWav(new Uint8Array(buffer));\n\n // Clean up temp file\n try {\n unlinkSync(this.tempFile);\n } catch {\n // Ignore cleanup errors\n }\n this.tempFile = null;\n\n resolve({\n audio,\n sampleRate: this.options.sampleRate,\n duration: audio.length / this.options.sampleRate,\n });\n } catch (err) {\n reject(err);\n }\n });\n\n // Send SIGTERM to stop recording gracefully\n this.process!.kill(\"SIGTERM\");\n });\n }\n\n /**\n * Cancel recording without saving\n */\n cancel(): void {\n if (this.process) {\n // Use SIGTERM for graceful shutdown to avoid mutex errors\n this.process.kill(\"SIGTERM\");\n // Give it a moment then force kill if needed\n const proc = this.process;\n setTimeout(() => {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n // Already dead\n }\n }, 100);\n this.process = null;\n }\n this.isRecording = false;\n\n // Clean up temp file\n if (this.tempFile && existsSync(this.tempFile)) {\n try {\n unlinkSync(this.tempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n this.tempFile = null;\n }\n\n /**\n * Check if currently recording\n */\n recording(): boolean {\n return this.isRecording;\n }\n\n /**\n * Parse WAV file to Float32Array\n */\n private parseWav(buffer: Uint8Array): Float32Array {\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n // Find data chunk\n let dataOffset = 44; // Standard WAV header size\n for (let i = 12; i < buffer.length - 8; i++) {\n if (\n buffer[i] === 0x64 && // 'd'\n buffer[i + 1] === 0x61 && // 'a'\n buffer[i + 2] === 0x74 && // 't'\n buffer[i + 3] === 0x61 // 'a'\n ) {\n dataOffset = i + 8; // Skip \"data\" + size field\n break;\n }\n }\n\n // Calculate number of samples\n const bytesPerSample = this.options.bitDepth / 8;\n const numSamples = Math.floor((buffer.length - dataOffset) / bytesPerSample);\n const audio = new Float32Array(numSamples);\n\n // Convert to float\n const maxValue = 2 ** (this.options.bitDepth - 1);\n for (let i = 0; i < numSamples; i++) {\n const offset = dataOffset + i * bytesPerSample;\n if (offset + bytesPerSample <= buffer.length) {\n const sample =\n this.options.bitDepth === 16 ? view.getInt16(offset, true) : view.getInt32(offset, true);\n audio[i] = sample / maxValue;\n }\n }\n\n return audio;\n }\n}\n\n/**\n * Record audio from microphone for a specified duration\n */\nexport async function recordAudio(\n durationMs: number,\n options: MicrophoneOptions = {},\n): Promise<RecordingResult> {\n const mic = new Microphone(options);\n await mic.start();\n await new Promise((r) => setTimeout(r, durationMs));\n return mic.stop();\n}\n\n/**\n * Record audio from microphone until silence is detected\n * Note: This is a simplified implementation - sox supports this natively\n */\nexport async function recordUntilSilence(\n maxDurationMs: number = 30000,\n options: MicrophoneOptions = {},\n): Promise<RecordingResult> {\n // For now, just record for the duration\n // TODO: Implement VAD (Voice Activity Detection) for auto-stop\n return recordAudio(maxDurationMs, options);\n}\n\n/**\n * Streaming microphone recorder using SoX\n *\n * Outputs audio data in real-time via callback, perfect for streaming transcription.\n *\n * @example\n * ```ts\n * const mic = new StreamingMicrophone({\n * onAudioChunk: (audio) => session.feedAudio(audio),\n * chunkDuration: 500, // 0.5s chunks\n * });\n *\n * await mic.start();\n * // Audio is now streaming to your callback\n *\n * const result = await mic.stop();\n * ```\n */\nexport class StreamingMicrophone {\n private process: ChildProcess | null = null;\n private options: Required<StreamingMicrophoneOptions>;\n private isRecording = false;\n private audioChunks: Float32Array[] = [];\n private rawBuffer: Buffer[] = [];\n private flushInterval: ReturnType<typeof setInterval> | null = null;\n private startTime = 0;\n\n constructor(options: StreamingMicrophoneOptions = {}) {\n this.options = {\n sampleRate: options.sampleRate ?? 16000,\n channels: options.channels ?? 1,\n bitDepth: options.bitDepth ?? 16,\n device: options.device ?? \"\",\n silenceThreshold: options.silenceThreshold ?? 0.01,\n silenceDuration: options.silenceDuration ?? 2,\n onAudioChunk: options.onAudioChunk ?? (() => {}),\n chunkDuration: options.chunkDuration ?? 500,\n };\n }\n\n /**\n * Start streaming audio from microphone\n */\n async start(): Promise<void> {\n if (this.isRecording) {\n throw new Error(\"Already recording\");\n }\n\n const soxPath = findSoxPath();\n if (!soxPath) {\n throw new Error(\n \"SoX is not installed. Install it with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\\n\" +\n \" Windows: https://sox.sourceforge.net/\",\n );\n }\n\n // Build sox command for raw audio output to stdout\n const args: string[] = [];\n\n // Input from default device\n if (process.platform === \"darwin\") {\n args.push(\"-d\");\n } else if (process.platform === \"linux\") {\n args.push(\"-t\", \"alsa\", \"default\");\n } else if (process.platform === \"win32\") {\n args.push(\"-t\", \"waveaudio\", \"default\");\n } else {\n args.push(\"-d\");\n }\n\n // Output format: raw audio to stdout\n args.push(\n \"-t\",\n \"raw\", // Raw PCM output\n \"-r\",\n String(this.options.sampleRate),\n \"-c\",\n String(this.options.channels),\n \"-b\",\n String(this.options.bitDepth),\n \"-e\",\n \"signed-integer\",\n \"-\", // Output to stdout\n );\n\n this.process = spawn(soxPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n this.isRecording = true;\n this.startTime = Date.now();\n this.rawBuffer = [];\n this.audioChunks = [];\n\n // Handle audio data from stdout\n this.process.stdout?.on(\"data\", (chunk: Buffer) => {\n this.rawBuffer.push(chunk);\n });\n\n // Handle errors\n this.process.on(\"error\", (err) => {\n console.error(\"Microphone error:\", err.message);\n this.isRecording = false;\n });\n\n // Set up interval to flush audio chunks\n this.flushInterval = setInterval(() => {\n this.flushBuffer();\n }, this.options.chunkDuration);\n }\n\n /**\n * Flush buffered audio to callback\n */\n private flushBuffer(): void {\n if (this.rawBuffer.length === 0) return;\n\n // Combine all buffered chunks\n const combined = Buffer.concat(this.rawBuffer);\n this.rawBuffer = [];\n\n // Convert to Float32Array\n const audio = this.bufferToFloat32(combined);\n if (audio.length > 0) {\n this.audioChunks.push(audio);\n this.options.onAudioChunk(audio);\n }\n }\n\n /**\n * Convert raw PCM buffer to Float32Array\n */\n private bufferToFloat32(buffer: Buffer): Float32Array {\n const bytesPerSample = this.options.bitDepth / 8;\n const numSamples = Math.floor(buffer.length / bytesPerSample);\n const audio = new Float32Array(numSamples);\n const maxValue = 2 ** (this.options.bitDepth - 1);\n\n for (let i = 0; i < numSamples; i++) {\n const offset = i * bytesPerSample;\n const sample =\n this.options.bitDepth === 16 ? buffer.readInt16LE(offset) : buffer.readInt32LE(offset);\n audio[i] = sample / maxValue;\n }\n\n return audio;\n }\n\n /**\n * Stop recording and return all collected audio\n */\n async stop(): Promise<RecordingResult> {\n if (!this.isRecording || !this.process) {\n throw new Error(\"Not recording\");\n }\n\n // Clear flush interval\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n // Flush any remaining buffer\n this.flushBuffer();\n\n return new Promise((resolve, reject) => {\n this.process!.on(\"close\", () => {\n this.isRecording = false;\n\n try {\n // Combine all audio chunks\n const totalLength = this.audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const audio = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of this.audioChunks) {\n audio.set(chunk, offset);\n offset += chunk.length;\n }\n\n this.audioChunks = [];\n\n resolve({\n audio,\n sampleRate: this.options.sampleRate,\n duration: audio.length / this.options.sampleRate,\n });\n } catch (err) {\n reject(err);\n }\n });\n\n // Send SIGTERM to stop recording gracefully\n this.process!.kill(\"SIGTERM\");\n });\n }\n\n /**\n * Cancel recording without saving\n */\n cancel(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n if (this.process) {\n // Use SIGTERM for graceful shutdown to avoid mutex errors\n this.process.kill(\"SIGTERM\");\n // Give it a moment then force kill if needed\n const proc = this.process;\n setTimeout(() => {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n // Already dead\n }\n }, 100);\n this.process = null;\n }\n\n this.isRecording = false;\n this.rawBuffer = [];\n this.audioChunks = [];\n }\n\n /**\n * Check if currently recording\n */\n recording(): boolean {\n return this.isRecording;\n }\n\n /**\n * Get elapsed recording time in seconds\n */\n getElapsed(): number {\n if (!this.isRecording) return 0;\n return (Date.now() - this.startTime) / 1000;\n }\n}\n\nexport default Microphone;\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport SelectInput from \"ink-select-input\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { getChromeCachedModels } from \"../../../core/chrome-backend.js\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\nimport { StreamingMicrophone } from \"../../../core/microphone.js\";\nimport {\n executeToolCall,\n formatToolsForPrompt,\n getToolDefinitions,\n parseToolCall,\n} from \"../../../core/tools.js\";\nimport type { StreamingTranscriptionSession } from \"../../../core/types.js\";\nimport { processImagePath } from \"../utils.js\";\n\ntype Message = {\n role: \"user\" | \"assistant\" | \"thinking\" | \"system\";\n content: string;\n images?: string[]; // Attached images (URLs or data URIs)\n};\n\ntype ChatViewProps = {\n gerbil: Gerbil;\n thinkingMode: boolean;\n agentMode: boolean;\n voiceMode: boolean;\n onToggleThinking?: () => void;\n onToggleAgent?: () => void;\n onToggleVoice?: () => void;\n onCreateTool?: () => void;\n onGenerationComplete?: (stats: { tokensOut: number; timeMs: number; tokPerSec: number }) => void;\n /** Last generation tok/s */\n lastTokPerSec?: number;\n /** Average tok/s across session */\n avgTokPerSec?: number;\n};\n\n// Chat modes with system prompts\nconst CHAT_MODES = {\n assistant: {\n name: \"Assistant\",\n symbol: \">\",\n description: \"Helpful and concise\",\n system: `You are Gerbil, a helpful AI assistant running locally. Be concise and direct.\n- Answer questions clearly without unnecessary preamble\n- If you don't know something, say so\n- Use markdown formatting when helpful\n- Keep responses focused and actionable`,\n },\n coder: {\n name: \"Coder\",\n symbol: \"$\",\n description: \"Code-focused, minimal talk\",\n system: `You are Gerbil, a local coding assistant. Be extremely concise.\n- Output code first, explanations only if asked\n- Use proper syntax highlighting hints\n- Prefer working examples over theory\n- When fixing bugs, show the fix directly\n- No pleasantries, just solutions`,\n },\n teacher: {\n name: \"Teacher\",\n symbol: \"?\",\n description: \"Explains concepts clearly\",\n system: `You are Gerbil, a patient teacher who explains concepts clearly.\n- Break down complex topics into simple parts\n- Use analogies and examples\n- Check understanding with questions\n- Build from fundamentals\n- Encourage curiosity`,\n },\n creative: {\n name: \"Creative\",\n symbol: \"*\",\n description: \"Expressive and imaginative\",\n system: `You are Gerbil, a creative collaborator with an expressive style.\n- Embrace unconventional ideas\n- Use vivid language and metaphors\n- Explore possibilities freely\n- Be playful when appropriate\n- Think outside the box`,\n },\n minimal: {\n name: \"Minimal\",\n symbol: \".\",\n description: \"Ultra-short responses\",\n system: \"You are Gerbil. Respond in as few words as possible. No fluff. Direct answers only.\",\n },\n};\n\ntype ChatMode = keyof typeof CHAT_MODES;\n\n// Rough token estimation (4 chars per token average)\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n// Strip all think tags from content\nfunction stripThinkTags(content: string): string {\n return content\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\")\n .replace(/<\\/?think>/g, \"\")\n .trim();\n}\n\n// Simple terminal markdown renderer\nfunction renderMarkdown(content: string): React.ReactNode[] {\n const lines = content.split(\"\\n\");\n const elements: React.ReactNode[] = [];\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n let codeBlockLang = \"\";\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n\n // Code blocks\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // End code block\n elements.push(\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n key={`code-${i}`}\n marginY={1}\n paddingX={1}\n >\n {codeBlockLang && <Text dimColor>{codeBlockLang}</Text>}\n <Text color=\"green\">{codeBlockContent.join(\"\\n\")}</Text>\n </Box>,\n );\n codeBlockContent = [];\n codeBlockLang = \"\";\n inCodeBlock = false;\n } else {\n inCodeBlock = true;\n codeBlockLang = line.slice(3).trim();\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // Headers\n if (line.startsWith(\"### \")) {\n elements.push(\n <Text bold color=\"cyan\" key={i}>\n {line.slice(4)}\n </Text>,\n );\n continue;\n }\n if (line.startsWith(\"## \")) {\n elements.push(\n <Text bold color=\"yellow\" key={i}>\n {line.slice(3)}\n </Text>,\n );\n continue;\n }\n if (line.startsWith(\"# \")) {\n elements.push(\n <Text bold color=\"magenta\" key={i}>\n {line.slice(2)}\n </Text>,\n );\n continue;\n }\n\n // Bullet points\n if (line.match(/^[-*]\\s/)) {\n const bulletContent = line.slice(2);\n elements.push(\n <Text key={i}>\n <Text color=\"cyan\"> * </Text>\n {renderInlineMarkdown(bulletContent)}\n </Text>,\n );\n continue;\n }\n\n // Numbered lists\n const numMatch = line.match(/^(\\d+)\\.\\s(.*)$/);\n if (numMatch) {\n elements.push(\n <Text key={i}>\n <Text color=\"cyan\"> {numMatch[1]}. </Text>\n {renderInlineMarkdown(numMatch[2])}\n </Text>,\n );\n continue;\n }\n\n // Regular text with inline formatting\n elements.push(<Text key={i}>{renderInlineMarkdown(line)}</Text>);\n }\n\n return elements;\n}\n\n// Render inline markdown (bold, italic, code, links)\nfunction renderInlineMarkdown(text: string): React.ReactNode {\n // Split by inline code, bold, and italic markers\n const parts: React.ReactNode[] = [];\n let remaining = text;\n let key = 0;\n\n while (remaining.length > 0) {\n // Inline code `code`\n const codeMatch = remaining.match(/^(.*?)`([^`]+)`(.*)$/);\n if (codeMatch) {\n if (codeMatch[1]) {\n parts.push(<Text key={(key += 1)}>{codeMatch[1]}</Text>);\n }\n parts.push(\n <Text backgroundColor=\"black\" color=\"green\" key={(key += 1)}>\n {\" \"}\n {codeMatch[2]}{\" \"}\n </Text>,\n );\n remaining = codeMatch[3];\n continue;\n }\n\n // Bold **text**\n const boldMatch = remaining.match(/^(.*?)\\*\\*([^*]+)\\*\\*(.*)$/);\n if (boldMatch) {\n if (boldMatch[1]) {\n parts.push(<Text key={(key += 1)}>{boldMatch[1]}</Text>);\n }\n parts.push(\n <Text bold key={(key += 1)}>\n {boldMatch[2]}\n </Text>,\n );\n remaining = boldMatch[3];\n continue;\n }\n\n // Italic *text*\n const italicMatch = remaining.match(/^(.*?)\\*([^*]+)\\*(.*)$/);\n if (italicMatch) {\n if (italicMatch[1]) {\n parts.push(<Text key={(key += 1)}>{italicMatch[1]}</Text>);\n }\n parts.push(\n <Text italic key={(key += 1)}>\n {italicMatch[2]}\n </Text>,\n );\n remaining = italicMatch[3];\n continue;\n }\n\n // No more formatting found\n parts.push(<Text key={(key += 1)}>{remaining}</Text>);\n break;\n }\n\n return <>{parts}</>;\n}\n\n// Parse streaming content to separate thinking from response\nfunction parseStreamContent(\n content: string,\n showThinking: boolean,\n): { thinking: string; response: string; isThinking: boolean } {\n if (!showThinking) {\n return { thinking: \"\", response: stripThinkTags(content), isThinking: false };\n }\n\n // Check for opening <think> tag (may be incomplete during streaming)\n const thinkStart = content.indexOf(\"<think>\");\n const thinkEnd = content.indexOf(\"</think>\");\n\n // Currently inside a think block (has opening but no closing)\n if (thinkStart !== -1 && thinkEnd === -1) {\n const thinking = content.slice(thinkStart + 7);\n const response = content.slice(0, thinkStart).trim();\n return { thinking, response, isThinking: true };\n }\n\n // Has complete think block\n if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {\n const thinking = content.slice(thinkStart + 7, thinkEnd).trim();\n const response = stripThinkTags(content);\n return { thinking, response, isThinking: false };\n }\n\n // No think tags at all - strip any partial/malformed tags just in case\n return { thinking: \"\", response: stripThinkTags(content), isThinking: false };\n}\n\nfunction StreamingResponse({\n content,\n symbol,\n showThinking,\n}: {\n content: string;\n symbol: string;\n showThinking: boolean;\n}) {\n const { thinking, response, isThinking } = parseStreamContent(content, showThinking);\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} paddingX={1}>\n {showThinking && thinking && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color=\"gray\" dimColor italic>\n {\"<think>\"}\n </Text>\n <Text dimColor italic>\n {stripThinkTags(thinking)}\n {isThinking && <Spinner type=\"dots\" />}\n </Text>\n </Box>\n )}\n {response && (\n <Box>\n <Text bold color=\"cyan\">\n {symbol}{\" \"}\n </Text>\n <Text bold color=\"white\">\n {response}\n </Text>\n {!isThinking && (\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n )}\n </Box>\n )}\n {!(thinking || response) && (\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n )}\n </Box>\n );\n}\n\n// Build conversation history for context\nfunction buildConversationContext(messages: Message[], mode: ChatMode): string {\n const systemPrompt = CHAT_MODES[mode].system;\n let context = `<|im_start|>system\\n${systemPrompt}<|im_end|>\\n`;\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n context += `<|im_start|>user\\n${msg.content}<|im_end|>\\n`;\n } else if (msg.role === \"assistant\") {\n context += `<|im_start|>assistant\\n${msg.content}<|im_end|>\\n`;\n }\n // Skip thinking and system messages for context\n }\n\n return context;\n}\n\nexport function ChatView({\n gerbil,\n thinkingMode,\n agentMode,\n voiceMode,\n onToggleThinking,\n onToggleAgent,\n onToggleVoice,\n onCreateTool,\n onGenerationComplete,\n lastTokPerSec,\n avgTokPerSec,\n}: ChatViewProps) {\n const [messages, setMessages] = useState<Message[]>([]);\n const [input, setInput] = useState(\"\");\n const [generating, setGenerating] = useState(false);\n const [streamedContent, setStreamedContent] = useState(\"\");\n const [mode, setMode] = useState<ChatMode>(\"assistant\");\n const [showModeSelector, setShowModeSelector] = useState(false);\n const [summarizing, setSummarizing] = useState(false);\n const [speaking, setSpeaking] = useState(false);\n const [recording, setRecording] = useState(false);\n const [recordingStatus, setRecordingStatus] = useState(\"\");\n const [recordingElapsed, setRecordingElapsed] = useState(0);\n const [liveTranscript, setLiveTranscript] = useState(\"\");\n const micRef = useRef<StreamingMicrophone | null>(null);\n const streamingSessionRef = useRef<StreamingTranscriptionSession | null>(null);\n const recordingStartTime = useRef<number>(0);\n const holdTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const HOLD_RELEASE_DELAY = 200; // ms to detect key release\n\n // Action bar navigation\n const [actionBarFocused, setActionBarFocused] = useState(false);\n const [selectedAction, setSelectedAction] = useState(0);\n\n // Image attachments (for vision models)\n const [attachedImages, setAttachedImages] = useState<string[]>([]);\n const [showImagePicker, setShowImagePicker] = useState(false);\n const [imagePathInput, setImagePathInput] = useState(\"\");\n const [imagePickerKey, setImagePickerKey] = useState(0); // Force remount TextInput\n\n // Check if model supports vision\n const supportsVision = gerbil.supportsVision?.() ?? false;\n\n // Command history\n const [history, setHistory] = useState<string[]>([]);\n const [historyIndex, setHistoryIndex] = useState(-1);\n const [savedInput, setSavedInput] = useState(\"\");\n\n // Track previous mode values to detect changes\n const [prevThinkingMode, setPrevThinkingMode] = useState(thinkingMode);\n const [prevAgentMode, setPrevAgentMode] = useState(agentMode);\n\n // Show feedback when modes change\n useEffect(() => {\n if (thinkingMode !== prevThinkingMode) {\n setPrevThinkingMode(thinkingMode);\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: thinkingMode ? \"🧠 Thinking mode enabled\" : \"🧠 Thinking mode disabled\",\n },\n ]);\n }\n }, [thinkingMode, prevThinkingMode]);\n\n useEffect(() => {\n if (agentMode !== prevAgentMode) {\n setPrevAgentMode(agentMode);\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: agentMode ? \"🔧 Agent mode enabled (tools available)\" : \"🔧 Agent mode disabled\",\n },\n ]);\n }\n }, [agentMode, prevAgentMode]);\n\n // Cleanup microphone, streaming session, and timers on unmount\n useEffect(() => {\n return () => {\n // Abort streaming session immediately (no final transcription)\n if (streamingSessionRef.current) {\n try {\n streamingSessionRef.current.abort();\n } catch {\n // Ignore cleanup errors\n }\n streamingSessionRef.current = null;\n }\n // Then cancel microphone\n if (micRef.current) {\n try {\n micRef.current.cancel();\n } catch {\n // Ignore cleanup errors\n }\n micRef.current = null;\n }\n if (holdTimeoutRef.current) {\n clearTimeout(holdTimeoutRef.current);\n holdTimeoutRef.current = null;\n }\n };\n }, []);\n\n const modes = Object.keys(CHAT_MODES) as ChatMode[];\n\n // Get model info for context limits\n // Check Chrome cache for accurate context length (may have been fetched from HuggingFace)\n const modelInfo = gerbil.getModelInfo();\n const modelId = modelInfo?.repo;\n const cachedModels = modelId ? getChromeCachedModels() : [];\n const cachedModel = modelId ? cachedModels.find((m) => m.modelId === modelId) : null;\n const maxContext = cachedModel?.contextLength || modelInfo?.contextLength || 32_768;\n\n // Calculate current context usage\n const contextText = buildConversationContext(messages, mode);\n const currentTokens = estimateTokens(contextText);\n const contextPercent = Math.round((currentTokens / maxContext) * 100);\n\n // Number of actions in the action bar\n const ACTION_COUNT = supportsVision ? 5 : 4; // Think, Agent, Voice, [Image], Record\n\n // Handle action bar toggle/trigger\n const handleActionSelect = () => {\n const recordIndex = supportsVision ? 4 : 3;\n const imageIndex = supportsVision ? 3 : -1;\n\n switch (selectedAction) {\n case 0: // Thinking\n onToggleThinking?.();\n break;\n case 1: // Agent\n onToggleAgent?.();\n break;\n case 2: // Voice\n onToggleVoice?.();\n break;\n default:\n if (selectedAction === imageIndex) {\n // Image - open image picker\n setShowImagePicker(true);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n } else if (selectedAction === recordIndex) {\n // Record - just shows hint, use Cmd+M to record\n }\n break;\n }\n };\n\n // Handle Tab for action bar, Up/Down for history, and Cmd shortcuts\n useInput((char, key) => {\n // Tab toggles action bar focus\n if (key.tab && !generating && !showImagePicker) {\n if (actionBarFocused) {\n // If already focused, unfocus\n setActionBarFocused(false);\n } else {\n // Focus action bar\n setActionBarFocused(true);\n setShowModeSelector(false);\n }\n return;\n }\n\n // When action bar is focused, handle navigation\n if (actionBarFocused && !generating) {\n // Left/Right to navigate actions\n if (key.leftArrow) {\n setSelectedAction((s) => (s - 1 + ACTION_COUNT) % ACTION_COUNT);\n return;\n }\n if (key.rightArrow) {\n setSelectedAction((s) => (s + 1) % ACTION_COUNT);\n return;\n }\n // Enter to toggle/trigger selected action\n if (key.return) {\n handleActionSelect();\n // Keep action bar focused for multiple toggles\n return;\n }\n // Escape to exit action bar\n if (key.escape) {\n setActionBarFocused(false);\n return;\n }\n // Number keys for quick select\n if (char >= \"1\" && char <= \"4\") {\n const idx = Number.parseInt(char, 10) - 1;\n setSelectedAction(idx);\n handleActionSelect();\n return;\n }\n }\n\n // Ctrl+O to open image picker (for vision models)\n if (char === \"o\" && key.ctrl && supportsVision && !generating && !showModeSelector) {\n setShowImagePicker(true);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n }\n\n // Cmd+C to copy chat\n if (char === \"c\" && key.meta) {\n copyChat();\n }\n\n // Cmd+S to save chat\n if (char === \"s\" && key.meta) {\n saveChat();\n }\n\n // Backtick (`): Toggle recording - press once to start, press again to stop and send\n // Simple push-to-talk that works in all terminals\n if (char === \"`\" && !generating && !showModeSelector && !showImagePicker) {\n if (recording) {\n // Stop and send\n stopRecording(true);\n } else {\n // Start recording\n startRecording();\n }\n }\n\n // Escape to close image picker or action bar\n if (key.escape) {\n if (showImagePicker) {\n setShowImagePicker(false);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n } else if (actionBarFocused) {\n setActionBarFocused(false);\n }\n }\n\n // Navigate history with Up/Down arrows (only when action bar not focused)\n if (!(generating || showModeSelector || showImagePicker || actionBarFocused)) {\n if (key.upArrow && history.length > 0) {\n if (historyIndex === -1) {\n // Save current input before browsing history\n setSavedInput(input);\n setHistoryIndex(history.length - 1);\n setInput(history.at(-1) ?? \"\");\n } else if (historyIndex > 0) {\n setHistoryIndex(historyIndex - 1);\n setInput(history[historyIndex - 1]);\n }\n }\n if (key.downArrow && historyIndex !== -1) {\n if (historyIndex < history.length - 1) {\n setHistoryIndex(historyIndex + 1);\n setInput(history[historyIndex + 1]);\n } else {\n // Return to saved input\n setHistoryIndex(-1);\n setInput(savedInput);\n }\n }\n }\n });\n\n // Summarize conversation and reset context\n const summarizeConversation = async () => {\n if (messages.length < 4) {\n setMessages((m) => [...m, { role: \"system\", content: \"Not enough messages to summarize.\" }]);\n return;\n }\n\n setSummarizing(true);\n setMessages((m) => [...m, { role: \"system\", content: \"Summarizing conversation...\" }]);\n\n try {\n const conversationText = messages\n .filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .map((m) => `${m.role === \"user\" ? \"User\" : \"Assistant\"}: ${m.content}`)\n .join(\"\\n\");\n\n let summary = \"\";\n for await (const chunk of gerbil.stream(\n `Summarize this conversation in 2-3 sentences, capturing the key topics and conclusions:\\n\\n${conversationText}`,\n { maxTokens: 200, system: \"You are a summarizer. Be concise.\" },\n )) {\n summary += chunk;\n }\n\n summary = stripThinkTags(summary);\n\n // Reset messages with just the summary\n setMessages([\n { role: \"system\", content: `Previous conversation summary: ${summary}` },\n { role: \"system\", content: \"Context compressed. Continuing from summary above.\" },\n ]);\n } catch (e) {\n setMessages((m) => [...m, { role: \"system\", content: `Error summarizing: ${e}` }]);\n }\n\n setSummarizing(false);\n };\n\n // Format chat as text\n const formatChatContent = () => {\n let content = `# Gerbil Chat - ${new Date().toLocaleString()}\\n\\n`;\n content += `**Mode:** ${CHAT_MODES[mode].name}\\n`;\n content += `**Model:** ${modelInfo?.id || \"unknown\"}\\n\\n`;\n content += \"---\\n\\n\";\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n content += `**You:** ${msg.content}\\n\\n`;\n } else if (msg.role === \"assistant\") {\n content += `**Gerbil:** ${msg.content}\\n\\n`;\n } else if (msg.role === \"system\") {\n content += `*${msg.content}*\\n\\n`;\n }\n }\n return content;\n };\n\n // Copy chat to clipboard\n const copyChat = () => {\n const content = formatChatContent();\n const { exec } = require(\"node:child_process\");\n const platform = process.platform;\n\n const cmd =\n platform === \"darwin\"\n ? \"pbcopy\"\n : platform === \"win32\"\n ? \"clip\"\n : \"xclip -selection clipboard\";\n const proc = exec(cmd, (err: any) => {\n if (err) {\n setMessages((m) => [...m, { role: \"system\", content: `Copy failed: ${err}` }]);\n } else {\n setMessages((m) => [...m, { role: \"system\", content: \"Chat copied to clipboard\" }]);\n }\n });\n proc.stdin?.write(content);\n proc.stdin?.end();\n };\n\n // Save chat to file\n const saveChat = (filename?: string) => {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19);\n const fname = filename || `gerbil-chat-${timestamp}.md`;\n const fpath = path.join(process.cwd(), fname);\n\n const content = formatChatContent();\n\n try {\n fs.writeFileSync(fpath, content);\n setMessages((m) => [...m, { role: \"system\", content: `Chat saved to ${fname}` }]);\n } catch (e) {\n setMessages((m) => [...m, { role: \"system\", content: `Error saving: ${e}` }]);\n }\n };\n\n // Start recording with streaming transcription\n const startRecording = async () => {\n if (recording || generating) return;\n\n try {\n const micAvailable = await gerbil.isMicrophoneAvailable();\n if (!micAvailable) {\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content:\n \"Microphone requires SoX. Install with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\",\n },\n ]);\n return;\n }\n\n // Ensure STT is loaded before starting\n await gerbil.loadSTT();\n\n // Create streaming transcription session\n const session = await gerbil.createStreamingTranscription({\n chunkDuration: 2500, // Transcribe every 2.5 seconds\n onChunk: (text, idx) => {\n // Update live transcript as each chunk is transcribed\n setLiveTranscript((prev) => (prev ? `${prev} ${text}` : text));\n },\n onTranscript: (fullText) => {\n // Update the recording message with live transcript\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n const elapsed = Math.floor((Date.now() - recordingStartTime.current) / 1000);\n newMessages[lastIdx] = {\n role: \"system\",\n content: fullText\n ? `Recording... ${elapsed}s\\n> ${fullText}`\n : `Recording... ${elapsed}s`,\n };\n }\n return newMessages;\n });\n },\n onError: (err) => {\n console.error(\"Transcription error:\", err);\n },\n });\n\n streamingSessionRef.current = session;\n session.start();\n\n // Create streaming microphone that feeds audio to the session\n micRef.current = new StreamingMicrophone({\n onAudioChunk: (audio) => {\n session.feedAudio(audio);\n },\n chunkDuration: 500, // 0.5s audio chunks\n });\n\n await micRef.current.start();\n\n recordingStartTime.current = Date.now();\n setRecording(true);\n setRecordingElapsed(0);\n setRecordingStatus(\"Recording...\");\n setLiveTranscript(\"\");\n\n // Add recording message\n setMessages((m) => [...m, { role: \"system\", content: \"Recording... (release to send)\" }]);\n\n // Update elapsed time every 500ms\n const timerInterval = setInterval(() => {\n if (!micRef.current?.recording()) {\n clearInterval(timerInterval);\n return;\n }\n const elapsed = Math.floor((Date.now() - recordingStartTime.current) / 1000);\n setRecordingElapsed(elapsed);\n\n // Only update status if no transcript yet\n const currentTranscript = streamingSessionRef.current?.getTranscript() || \"\";\n if (!currentTranscript) {\n setRecordingStatus(`Recording... ${elapsed}s`);\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Recording... ${elapsed}s`,\n };\n }\n return newMessages;\n });\n } else {\n setRecordingStatus(`Recording... ${elapsed}s (transcribing)`);\n }\n }, 500);\n\n // Store interval ID for cleanup\n (micRef.current as any)._timerInterval = timerInterval;\n } catch (err: any) {\n setMessages((m) => [...m, { role: \"system\", content: `Recording error: ${err.message}` }]);\n setRecording(false);\n micRef.current = null;\n streamingSessionRef.current = null;\n }\n };\n\n // Stop recording and finalize streaming transcription\n // autoSubmit: if true, automatically submit the transcribed text as a message\n const stopRecording = async (autoSubmit = false) => {\n if (!recording || !micRef.current) return;\n\n const mic = micRef.current;\n const session = streamingSessionRef.current;\n micRef.current = null;\n streamingSessionRef.current = null;\n\n // Clear hold timeout\n if (holdTimeoutRef.current) {\n clearTimeout(holdTimeoutRef.current);\n holdTimeoutRef.current = null;\n }\n\n // Clear timer interval\n if ((mic as any)._timerInterval) {\n clearInterval((mic as any)._timerInterval);\n }\n\n setRecordingStatus(\"Finalizing...\");\n\n try {\n // Stop microphone first\n const result = await mic.stop();\n const durationSec = result.duration.toFixed(1);\n\n // Update message\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Recorded ${durationSec}s, finalizing transcription...`,\n };\n }\n return newMessages;\n });\n\n // Stop the streaming session and get final transcript\n let text = \"\";\n if (session) {\n text = await session.stop();\n text = text.trim();\n }\n\n // If streaming didn't capture anything, fall back to full transcription\n if (!text && result.audio.length > 0) {\n setRecordingStatus(\"Transcribing...\");\n const transcription = await gerbil.transcribe(result.audio);\n text = transcription.text.trim();\n }\n\n if (text) {\n if (autoSubmit) {\n // Remove the system message and auto-submit\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages.pop(); // Remove \"Recording...\" message\n }\n return newMessages;\n });\n // Reset recording state first\n setRecording(false);\n setRecordingStatus(\"\");\n setRecordingElapsed(0);\n setLiveTranscript(\"\");\n // Submit the transcribed text\n await handleSubmit(text);\n return;\n }\n // Put transcribed text in input field (manual mode)\n setInput(text);\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Transcribed: \"${text}\"`,\n };\n }\n return newMessages;\n });\n } else {\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: \"No speech detected. Try speaking louder or closer to mic.\",\n };\n }\n return newMessages;\n });\n }\n } catch (err: any) {\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Transcription error: ${err.message}`,\n };\n }\n return newMessages;\n });\n }\n\n setRecording(false);\n setRecordingStatus(\"\");\n setRecordingElapsed(0);\n setLiveTranscript(\"\");\n };\n\n // Toggle recording (called when Record action is triggered)\n const toggleRecording = async () => {\n if (recording) {\n await stopRecording();\n } else {\n await startRecording();\n }\n };\n\n const handleSubmit = async (value: string) => {\n if (!value.trim() || generating || summarizing) {\n return;\n }\n\n // Handle slash commands\n if (value.startsWith(\"/\")) {\n const parts = value.slice(1).split(\" \");\n const cmd = parts[0].toLowerCase().trim();\n const arg = parts.slice(1).join(\" \");\n\n if (cmd === \"clear\" || cmd === \"new\") {\n setMessages([]);\n setInput(\"\");\n return;\n }\n if (cmd === \"mode\") {\n setShowModeSelector(true);\n setInput(\"\");\n return;\n }\n if (cmd === \"summarize\" || cmd === \"compress\") {\n setInput(\"\");\n await summarizeConversation();\n return;\n }\n if (cmd === \"reset-cache\" || cmd === \"reset\") {\n setInput(\"\");\n try {\n await gerbil.clearCache();\n const mem = await gerbil.getMemoryUsage();\n const memInfo = mem ? ` (was ${mem.usedGB.toFixed(1)}GB)` : \"\";\n setMessages((m) => [\n ...m,\n { role: \"system\", content: `🧹 Cache cleared${memInfo}. Conversation context reset.` },\n ]);\n } catch (err) {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: `❌ Failed to clear cache: ${err}` },\n ]);\n }\n return;\n }\n if (cmd === \"memory\" || cmd === \"mem\") {\n setInput(\"\");\n try {\n const mem = await gerbil.getMemoryUsage();\n if (mem) {\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `💾 Memory: ${mem.usedGB.toFixed(1)}GB / ${mem.totalGB.toFixed(1)}GB (${mem.usedPercent.toFixed(1)}%)`,\n },\n ]);\n } else {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: \"Memory monitoring not available (CPU mode)\" },\n ]);\n }\n } catch (err) {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: `❌ Failed to get memory: ${err}` },\n ]);\n }\n return;\n }\n if (cmd === \"save\") {\n setInput(\"\");\n saveChat(arg || undefined);\n return;\n }\n if (cmd === \"help\") {\n const supportsVision = gerbil.supportsVision?.() ?? false;\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `Commands:\n /new, /clear - Start new chat\n /docs <topic> - Search Gerbil docs (tools, skills, ai-sdk, next, etc.)\n /summarize - Compress conversation\n /reset-cache - Clear KV cache (frees memory)\n /memory - Show memory usage\n /mode - Change chat mode\n /image <path> - Attach image ${supportsVision ? \"✓\" : \"(requires vision model)\"}\n /images - Show attached images\n /clear-images - Remove attached images\n\nShortcuts:\n Tab - Focus action bar (🧠 🔧 🔊 🎤)\n ←→ - Navigate actions (when focused)\n Enter - Toggle/trigger action\n Esc - Exit action bar\n \n ⌘C - Copy chat to clipboard\n ⌘S - Save chat to file\n ^R - Quick record (5s)\n ^O - Open image ${supportsVision ? \"✓\" : \"(requires vision model)\"}`,\n },\n ]);\n setInput(\"\");\n return;\n }\n\n // /image command - attach image for vision models (supports URLs and local paths)\n if (cmd === \"image\" && arg) {\n const result = processImagePath(arg);\n if (result.success && result.imageSource) {\n setAttachedImages((imgs) => [...imgs, result.imageSource!]);\n const count = attachedImages.length + 1;\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `${result.message} (${count} image${count > 1 ? \"s\" : \"\"} total)`,\n },\n ]);\n } else if (result.message) {\n setMessages((m) => [...m, { role: \"system\", content: result.message }]);\n }\n setInput(\"\");\n return;\n }\n\n // /images - show attached images\n if (cmd === \"images\") {\n if (attachedImages.length === 0) {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: \"No images attached. Use /image <path> to attach.\" },\n ]);\n } else {\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `📎 ${attachedImages.length} image(s) attached\\nUse /clear-images to remove.`,\n },\n ]);\n }\n setInput(\"\");\n return;\n }\n\n // /clear-images - remove attached images\n if (cmd === \"clear-images\") {\n const count = attachedImages.length;\n setAttachedImages([]);\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: count > 0 ? `🗑️ Cleared ${count} attached image(s)` : \"No images to clear\",\n },\n ]);\n setInput(\"\");\n return;\n }\n\n // /docs command - direct docs search\n if (cmd === \"docs\" && arg) {\n setInput(\"\");\n setGenerating(true);\n try {\n const result = await executeToolCall(\"gerbil_docs\", { query: arg });\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `📄 docs(${arg}):\\n${result.slice(0, 600)}${result.length > 600 ? \"...\" : \"\"}`,\n },\n ]);\n } catch (e) {\n setMessages((m) => [...m, { role: \"system\", content: `Error: ${e}` }]);\n }\n setGenerating(false);\n return;\n }\n }\n\n const userMessage = value.trim();\n setInput(\"\");\n\n // Add to history (avoid duplicates)\n if (history.at(-1) !== userMessage) {\n setHistory((h) => [...h.slice(-50), userMessage]); // Keep last 50\n }\n setHistoryIndex(-1);\n setSavedInput(\"\");\n\n // Include attached images with user message\n const currentImages = [...attachedImages];\n\n setMessages((m) => [\n ...m,\n {\n role: \"user\",\n content: userMessage,\n images: currentImages.length > 0 ? currentImages : undefined,\n },\n ]);\n setGenerating(true);\n setStreamedContent(\"\");\n\n // Clear images after attaching to message\n if (currentImages.length > 0) {\n setAttachedImages([]);\n }\n\n try {\n let fullResponse = \"\";\n const currentMode = CHAT_MODES[mode];\n const startTime = performance.now();\n let tokenCount = 0;\n\n // Build system prompt - replace with agent prompt if agent mode is enabled\n let systemPrompt = currentMode.system;\n\n if (agentMode) {\n const tools = getToolDefinitions();\n // Agent mode uses its own prompt entirely (not combined with mode)\n systemPrompt = formatToolsForPrompt(tools);\n }\n\n for await (const chunk of gerbil.stream(userMessage, {\n thinking: thinkingMode,\n maxTokens: 500,\n system: systemPrompt,\n // Include images for vision models\n images:\n currentImages.length > 0 ? currentImages.map((src) => ({ source: src })) : undefined,\n })) {\n fullResponse += chunk;\n tokenCount += 1;\n setStreamedContent(fullResponse);\n }\n\n // Calculate and report tok/s\n const endTime = performance.now();\n const timeMs = endTime - startTime;\n const tokPerSec = timeMs > 0 ? (tokenCount / timeMs) * 1000 : 0;\n onGenerationComplete?.({ tokensOut: tokenCount, timeMs, tokPerSec });\n\n let response = stripThinkTags(fullResponse);\n\n // Check for tool calls in agent mode\n if (agentMode) {\n const toolCall = parseToolCall(response);\n if (toolCall) {\n // Show that we're calling a tool\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `🔧 Calling: ${toolCall.tool}(${JSON.stringify(toolCall.params)})`,\n },\n ]);\n setStreamedContent(\"\");\n\n // Execute the tool\n const toolResult = await executeToolCall(toolCall.tool, toolCall.params);\n\n // Add tool result (truncate if too long)\n const truncatedResult =\n toolResult.length > 500 ? `${toolResult.slice(0, 500)}\\n... (truncated)` : toolResult;\n setMessages((m) => [...m, { role: \"system\", content: `📄 Result:\\n${truncatedResult}` }]);\n\n // Generate follow-up response with tool result\n let followUp = \"\";\n for await (const chunk of gerbil.stream(\n `Here's documentation:\\n${toolResult}\\n\\nSummarize this for the user who asked: \"${userMessage}\"`,\n { maxTokens: 300, system: \"You are helpful. Summarize the documentation briefly.\" },\n )) {\n followUp += chunk;\n setStreamedContent(followUp);\n }\n response = stripThinkTags(followUp);\n\n // If follow-up is empty, just show the tool worked\n if (!response.trim()) {\n response = \"See the documentation above.\";\n }\n }\n }\n\n const thinkMatch = fullResponse.match(/<think>([\\s\\S]*?)<\\/think>/);\n let thinking = \"\";\n\n if (thinkMatch && thinkingMode) {\n // Extract content and strip any remaining tags\n thinking = stripThinkTags(thinkMatch[1]).trim();\n }\n\n setMessages((m) => {\n const newMessages = [...m];\n if (thinking && thinkingMode) {\n newMessages.push({ role: \"thinking\", content: thinking });\n }\n newMessages.push({ role: \"assistant\", content: response });\n return newMessages;\n });\n\n // Speak response if voice mode is enabled\n if (voiceMode && response.trim()) {\n speakResponse(response);\n }\n } catch (error) {\n setMessages((m) => [...m, { role: \"assistant\", content: `Error: ${error}` }]);\n } finally {\n setGenerating(false);\n setStreamedContent(\"\");\n }\n };\n\n // Speak response using TTS (sentence-by-sentence for responsiveness)\n // Check if text looks like gibberish (common on first response due to model warmup)\n const isGibberish = (text: string): boolean => {\n const cleaned = text.trim();\n if (cleaned.length < 3) return true;\n\n // Check for high ratio of non-ASCII or weird characters\n const weirdChars = cleaned.match(/[^\\x20-\\x7E\\n]/g) || [];\n if (weirdChars.length / cleaned.length > 0.3) return true;\n\n // Check for repetitive patterns (e.g., \"aaaa\" or \"abababab\")\n if (/(.)\\1{4,}/.test(cleaned)) return true;\n if (/(.{1,3})\\1{3,}/.test(cleaned)) return true;\n\n // Check if mostly punctuation or symbols\n const alphaNum = cleaned.match(/[a-zA-Z0-9]/g) || [];\n if (alphaNum.length / cleaned.length < 0.5) return true;\n\n // Check for common gibberish patterns\n if (/^[^a-zA-Z]*$/.test(cleaned)) return true;\n\n return false;\n };\n\n const speakResponse = async (text: string) => {\n // Skip speaking gibberish\n if (isGibberish(text)) {\n return;\n }\n\n setSpeaking(true);\n try {\n const { execSync } = await import(\"child_process\");\n const { writeFileSync, unlinkSync } = await import(\"fs\");\n const { tmpdir } = await import(\"os\");\n const { join } = await import(\"path\");\n\n // Split into paragraphs for efficient streaming (sentences are too slow)\n // If text is short (<500 chars), speak all at once\n // Otherwise split by double newlines or single newlines\n let chunks: string[];\n const cleanText = text.trim();\n\n if (cleanText.length < 500) {\n chunks = [cleanText];\n } else if (cleanText.includes(\"\\n\\n\")) {\n chunks = cleanText.split(/\\n\\n+/).filter((s) => s.trim() && !isGibberish(s));\n } else if (cleanText.includes(\"\\n\")) {\n chunks = cleanText.split(/\\n+/).filter((s) => s.trim() && !isGibberish(s));\n } else {\n // Long single paragraph - speak in ~300 char chunks at sentence boundaries\n chunks = [];\n let current = \"\";\n for (const sentence of cleanText.split(/(?<=[.!?])\\s+/)) {\n if (current.length + sentence.length > 300 && current.length > 0) {\n chunks.push(current.trim());\n current = sentence;\n } else {\n current += (current ? \" \" : \"\") + sentence;\n }\n }\n if (current.trim()) chunks.push(current.trim());\n chunks = chunks.filter((s) => !isGibberish(s));\n }\n\n for (const chunk of chunks) {\n if (!chunk.trim()) continue;\n\n const result = await gerbil.speak(chunk, { voice: \"af_heart\", speed: 1.0 });\n\n // Save to temp WAV and play\n const tempFile = join(tmpdir(), `gerbil-voice-${Date.now()}.wav`);\n\n // Write WAV file\n const buffer = Buffer.alloc(44 + result.audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + result.audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(result.sampleRate, 24);\n buffer.writeUInt32LE(result.sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(result.audio.length * 2, 40);\n\n for (let i = 0; i < result.audio.length; i++) {\n const s = Math.max(-1, Math.min(1, result.audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n writeFileSync(tempFile, buffer);\n\n // Play audio\n try {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${tempFile}\"`, { stdio: \"ignore\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${tempFile}\"`, { stdio: \"ignore\" });\n } catch {\n execSync(`paplay \"${tempFile}\"`, { stdio: \"ignore\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${tempFile}').PlaySync()\"`, {\n stdio: \"ignore\",\n });\n }\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n } catch (err) {\n // Silently fail TTS - don't interrupt chat\n } finally {\n setSpeaking(false);\n }\n };\n\n const modeItems = modes.map((key) => ({\n key,\n label: `[${CHAT_MODES[key].symbol}] ${CHAT_MODES[key].name}`,\n value: key,\n }));\n\n return (\n <Box flexDirection=\"column\" flexGrow={1}>\n {/* Mode selector popup */}\n {showModeSelector && (\n <Box\n borderColor=\"cyan\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginX={1}\n paddingX={2}\n paddingY={1}\n >\n <Text bold color=\"cyan\">\n Select Mode\n </Text>\n <Box marginY={1}>\n <SelectInput\n initialIndex={modes.indexOf(mode)}\n itemComponent={({ isSelected, label }) => {\n const modeKey = modeItems.find((m) => m.label === label)?.value as ChatMode;\n const modeInfo = modeKey ? CHAT_MODES[modeKey] : null;\n return (\n <Box>\n <Text bold={isSelected} color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n {label}\n </Text>\n {isSelected && modeInfo && <Text dimColor> - {modeInfo.description}</Text>}\n </Box>\n );\n }}\n items={modeItems}\n onSelect={(item) => {\n setMode(item.value as ChatMode);\n setShowModeSelector(false);\n }}\n />\n </Box>\n <Text dimColor>Tab to close</Text>\n </Box>\n )}\n\n {/* Image picker modal */}\n {showImagePicker && (\n <Box\n borderColor=\"yellow\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginX={1}\n paddingX={2}\n paddingY={1}\n width={80}\n >\n <Text bold color=\"yellow\">\n 📷 Attach Image\n </Text>\n <Box flexDirection=\"column\" marginY={1}>\n <Text dimColor>Enter URL or local path (supports ~ and https://):</Text>\n <Box marginTop={1} width={70}>\n <Text color=\"yellow\">> </Text>\n <TextInput\n key={imagePickerKey}\n onChange={setImagePathInput}\n onSubmit={(imagePath) => {\n if (!imagePath.trim()) {\n setShowImagePicker(false);\n setImagePickerKey((k) => k + 1);\n return;\n }\n\n const result = processImagePath(imagePath);\n if (result.success && result.imageSource) {\n setAttachedImages((imgs) => [...imgs, result.imageSource!]);\n setMessages((m) => [...m, { role: \"system\", content: result.message }]);\n } else if (result.message) {\n setMessages((m) => [...m, { role: \"system\", content: result.message }]);\n }\n\n setShowImagePicker(false);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n }}\n placeholder=\"https://... or ~/path/to/image.png\"\n value={imagePathInput}\n />\n </Box>\n {imagePathInput && (\n <Box marginTop={1}>\n <Text dimColor wrap=\"truncate-end\">\n Path: {imagePathInput}\n </Text>\n </Box>\n )}\n {attachedImages.length > 0 && (\n <Box marginTop={1}>\n <Text color=\"green\">✓ {attachedImages.length} image(s) attached</Text>\n </Box>\n )}\n </Box>\n <Text dimColor>Enter to attach • Esc to cancel • Empty to close</Text>\n </Box>\n )}\n\n {/* Header with mode and context info */}\n {!(showModeSelector || showImagePicker) && (\n <Box justifyContent=\"space-between\" paddingX={1}>\n <Text color=\"gray\">\n [{CHAT_MODES[mode].symbol}] {CHAT_MODES[mode].name}\n {agentMode && <Text color=\"cyan\"> +tools</Text>}\n <Text dimColor> | </Text>\n {/* Action bar - Tab to focus, arrows to navigate, Enter to toggle */}\n {actionBarFocused ? (\n <>\n <Text\n backgroundColor={selectedAction === 0 ? \"magenta\" : undefined}\n bold={selectedAction === 0}\n color={selectedAction === 0 ? \"white\" : thinkingMode ? \"magenta\" : \"gray\"}\n >\n Think:{thinkingMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n <Text\n backgroundColor={selectedAction === 1 ? \"green\" : undefined}\n bold={selectedAction === 1}\n color={selectedAction === 1 ? \"white\" : agentMode ? \"green\" : \"gray\"}\n >\n Agent:{agentMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n <Text\n backgroundColor={selectedAction === 2 ? \"cyan\" : undefined}\n bold={selectedAction === 2}\n color={selectedAction === 2 ? \"white\" : voiceMode ? \"cyan\" : \"gray\"}\n >\n Voice:{voiceMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n {supportsVision && (\n <>\n <Text\n backgroundColor={selectedAction === 3 ? \"blue\" : undefined}\n bold={selectedAction === 3}\n color={\n selectedAction === 3 ? \"white\" : attachedImages.length > 0 ? \"blue\" : \"gray\"\n }\n >\n Image{attachedImages.length > 0 ? `:${attachedImages.length}` : \"\"}\n </Text>\n <Text dimColor> </Text>\n </>\n )}\n <Text\n backgroundColor={selectedAction === (supportsVision ? 4 : 3) ? \"red\" : undefined}\n bold={selectedAction === (supportsVision ? 4 : 3)}\n color={\n selectedAction === (supportsVision ? 4 : 3)\n ? \"white\"\n : recording\n ? \"red\"\n : \"gray\"\n }\n >\n {recording ? `Recording:${recordingElapsed}s` : \"`Record\"}\n </Text>\n <Text dimColor> (arrows, Enter)</Text>\n </>\n ) : (\n <>\n <Text color={thinkingMode ? \"magenta\" : \"gray\"}>\n Think:{thinkingMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n <Text color={agentMode ? \"green\" : \"gray\"}>Agent:{agentMode ? \"on\" : \"off\"}</Text>\n <Text dimColor> </Text>\n <Text color={voiceMode ? \"cyan\" : \"gray\"}>Voice:{voiceMode ? \"on\" : \"off\"}</Text>\n <Text dimColor> </Text>\n {supportsVision && (\n <>\n <Text color={attachedImages.length > 0 ? \"blue\" : \"gray\"}>\n Image{attachedImages.length > 0 ? `:${attachedImages.length}` : \"\"}\n </Text>\n <Text dimColor> </Text>\n </>\n )}\n <Text color={recording ? \"red\" : \"gray\"}>\n {recording ? `Recording:${recordingElapsed}s` : \"`Record\"}\n </Text>\n </>\n )}\n <Text dimColor> | /help</Text>\n </Text>\n <Box>\n {lastTokPerSec !== undefined && lastTokPerSec > 0 && (\n <>\n <Text color=\"yellow\">{lastTokPerSec.toFixed(1)}</Text>\n <Text dimColor> tok/s</Text>\n {avgTokPerSec !== undefined && avgTokPerSec > 0 && (\n <>\n <Text dimColor> (avg </Text>\n <Text color=\"cyan\">{avgTokPerSec.toFixed(1)}</Text>\n <Text dimColor>)</Text>\n </>\n )}\n <Text dimColor> </Text>\n </>\n )}\n <Text color={contextPercent > 80 ? \"red\" : contextPercent > 50 ? \"yellow\" : \"green\"}>\n {currentTokens.toLocaleString()}/{maxContext.toLocaleString()} tokens (\n {contextPercent}%)\n </Text>\n </Box>\n </Box>\n )}\n\n {/* Messages */}\n <Box flexDirection=\"column\" flexGrow={1} overflowY=\"hidden\">\n {messages.length === 0 && !generating && (\n <Box padding={1}>\n <Text color=\"gray\">Start a conversation. Type a message and press Enter.</Text>\n </Box>\n )}\n\n {messages.map((msg, i) => (\n <Box key={i} marginBottom={1} paddingX={1}>\n {msg.role === \"user\" && (\n <Box>\n <Text bold color=\"cyan\">\n You:{\" \"}\n </Text>\n {msg.images && msg.images.length > 0 && (\n <Text color=\"yellow\">\n 📷{msg.images.length > 1 ? `×${msg.images.length}` : \"\"}{\" \"}\n </Text>\n )}\n <Text>{msg.content}</Text>\n </Box>\n )}\n {msg.role === \"thinking\" && (\n <Box flexDirection=\"column\">\n <Text color=\"gray\" dimColor italic>\n {\"<think>\"}\n </Text>\n <Text dimColor italic>\n {msg.content}\n </Text>\n </Box>\n )}\n {msg.role === \"assistant\" && (\n <Box>\n <Text bold color=\"cyan\">\n {CHAT_MODES[mode].symbol}{\" \"}\n </Text>\n <Box flexDirection=\"column\" flexGrow={1}>\n {renderMarkdown(msg.content)}\n </Box>\n </Box>\n )}\n {msg.role === \"system\" &&\n (msg.content.startsWith(\"🔧 Calling\") ? (\n <Text color=\"cyan\" dimColor>\n ◆{\" \"}\n {msg.content\n .replace(\"🔧 Calling: \", \"\")\n .replace(\"gerbil_docs\", \"docs\")\n .replace(\"run_skill\", \"skill\")}\n </Text>\n ) : msg.content.startsWith(\"📄\") ? (\n <Box borderColor=\"gray\" borderStyle=\"single\" marginBottom={1} paddingX={1}>\n <Text color=\"gray\">\n {msg.content.replace(\"📄 Result:\\n\", \"\").slice(0, 300)}\n {msg.content.replace(\"📄 Result:\\n\", \"\").length > 300 ? \"...\" : \"\"}\n </Text>\n </Box>\n ) : msg.content.startsWith(\"🧠\") ? (\n <Text color=\"magenta\" dimColor>\n ◆ {msg.content}\n </Text>\n ) : msg.content.startsWith(\"🔧 Agent\") ? (\n <Text color=\"green\" dimColor>\n ◆ {msg.content}\n </Text>\n ) : (\n <Text color=\"yellow\" dimColor>\n {msg.content}\n </Text>\n ))}\n </Box>\n ))}\n\n {/* Streaming response */}\n {generating && streamedContent && (\n <StreamingResponse\n content={streamedContent}\n showThinking={thinkingMode}\n symbol={CHAT_MODES[mode].symbol}\n />\n )}\n\n {generating && !streamedContent && (\n <Box paddingX={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text color=\"gray\"> Thinking...</Text>\n </Box>\n )}\n\n {summarizing && (\n <Box paddingX={1}>\n <Text color=\"yellow\">\n <Spinner type=\"dots\" />\n </Text>\n <Text color=\"gray\"> Summarizing...</Text>\n </Box>\n )}\n </Box>\n\n {/* Context warning */}\n {contextPercent > 80 && (\n <Box paddingX={1}>\n <Text color=\"red\">Context {contextPercent}% full. Use /summarize to compress.</Text>\n </Box>\n )}\n\n {/* Input (hidden when image picker is open) */}\n {!showImagePicker && (\n <Box\n borderColor={generating || summarizing ? \"gray\" : \"cyan\"}\n borderStyle=\"single\"\n paddingX={1}\n >\n {attachedImages.length > 0 && (\n <Text color=\"yellow\">\n 📷{attachedImages.length > 1 ? `×${attachedImages.length}` : \"\"}{\" \"}\n </Text>\n )}\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder={\n recording\n ? `🎤 ${recordingStatus || \"recording...\"}`\n : generating\n ? \"generating...\"\n : summarizing\n ? \"summarizing...\"\n : attachedImages.length > 0\n ? \"describe the image...\"\n : actionBarFocused\n ? \"←→ navigate, Enter to toggle, Esc to exit\"\n : \"type a message... (Tab for actions)\"\n }\n value={input}\n />\n </Box>\n )}\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\n// Copy to clipboard utility\nfunction copyToClipboard(text: string): Promise<boolean> {\n return new Promise((resolve) => {\n const platform = os.platform();\n let cmd: string;\n\n if (platform === \"darwin\") {\n cmd = \"pbcopy\";\n } else if (platform === \"linux\") {\n cmd = \"xclip -selection clipboard\";\n } else if (platform === \"win32\") {\n cmd = \"clip\";\n } else {\n resolve(false);\n return;\n }\n\n const proc = exec(cmd, (err) => {\n resolve(!err);\n });\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n\n// File extension map for languages\nconst LANG_EXTENSIONS: Record<string, string> = {\n typescript: \"ts\",\n javascript: \"js\",\n python: \"py\",\n rust: \"rs\",\n go: \"go\",\n java: \"java\",\n cpp: \"cpp\",\n c: \"c\",\n ruby: \"rb\",\n php: \"php\",\n swift: \"swift\",\n kotlin: \"kt\",\n sql: \"sql\",\n bash: \"sh\",\n shell: \"sh\",\n html: \"html\",\n css: \"css\",\n json: \"json\",\n yaml: \"yaml\",\n markdown: \"md\",\n};\n\ntype CodeViewProps = {\n gerbil: Gerbil;\n};\n\ntype Phase = \"input\" | \"generating\" | \"result\";\n\nexport function CodeView({ gerbil }: CodeViewProps) {\n const [phase, setPhase] = useState<Phase>(\"input\");\n const [prompt, setPrompt] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [language, setLanguage] = useState(\"typescript\");\n const [error, setError] = useState<string | null>(null);\n const [saveStatus, setSaveStatus] = useState<string | null>(null);\n const [copyStatus, setCopyStatus] = useState<string | null>(null);\n\n const handleSave = async () => {\n if (!code || phase !== \"result\" || error) {\n return;\n }\n\n // Generate filename from prompt (sanitized)\n const baseName =\n prompt\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 30) || \"generated\";\n\n const ext = LANG_EXTENSIONS[language] || \"txt\";\n const fileName = `${baseName}.${ext}`;\n const filePath = path.join(process.cwd(), fileName);\n\n try {\n fs.writeFileSync(filePath, code, \"utf-8\");\n setSaveStatus(`Saved to ${fileName}`);\n setTimeout(() => setSaveStatus(null), 3000);\n } catch (err) {\n setSaveStatus(`Error: ${err}`);\n setTimeout(() => setSaveStatus(null), 3000);\n }\n };\n\n const handleCopy = async () => {\n if (!code || phase !== \"result\" || error) {\n return;\n }\n\n const success = await copyToClipboard(code);\n setCopyStatus(success ? \"Copied to clipboard!\" : \"Failed to copy\");\n setTimeout(() => setCopyStatus(null), 2000);\n };\n\n const handleSubmit = async (value: string) => {\n if (!value.trim()) {\n return;\n }\n\n setPrompt(value);\n setPhase(\"generating\");\n setError(null);\n\n try {\n const result = await gerbil.generate(value, {\n maxTokens: 1000,\n temperature: 0.3,\n system: `You are an expert programmer. Generate clean, working code.\nWhen asked to generate code:\n1. Output ONLY the code, no explanations before or after\n2. Use proper formatting and comments within the code\n3. Make the code complete and runnable\n4. Detect the appropriate language from the request`,\n });\n\n // Try to detect language and clean up code\n let generatedCode = result.text;\n\n // Extract language from code fence if present\n const langMatch = generatedCode.match(/```(\\w+)\\n/);\n if (langMatch) {\n setLanguage(langMatch[1]);\n }\n\n // Remove code fences and think tags\n generatedCode = generatedCode\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\") // Remove think blocks\n .replace(/```\\w*\\n?/g, \"\")\n .replace(/```\\n?/g, \"\")\n .trim();\n\n setCode(generatedCode);\n setPhase(\"result\");\n } catch (err) {\n setError(String(err));\n setPhase(\"result\");\n }\n };\n\n const handleReset = () => {\n setPhase(\"input\");\n setPrompt(\"\");\n setCode(\"\");\n setError(null);\n };\n\n useInput((input, key) => {\n if (key.escape && phase === \"result\") {\n handleReset();\n }\n\n // Save and copy shortcuts when code is generated\n if (phase === \"result\" && !error) {\n if (input === \"s\" || input === \"S\") {\n handleSave();\n }\n if (input === \"c\" || input === \"C\") {\n handleCopy();\n }\n }\n });\n\n if (phase === \"input\") {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>💻 Generate Code</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"gray\">Describe what code you want to generate:</Text>\n </Box>\n <Box borderColor=\"cyan\" borderStyle=\"single\" paddingX={1}>\n <Text color=\"cyan\">❯ </Text>\n <TextInput\n onChange={setPrompt}\n onSubmit={handleSubmit}\n placeholder=\"e.g., A function that calculates fibonacci numbers\"\n value={prompt}\n />\n </Box>\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Examples:\n </Text>\n </Box>\n <Text color=\"gray\" dimColor>\n • A React hook for debouncing\n </Text>\n <Text color=\"gray\" dimColor>\n • Python script to parse CSV files\n </Text>\n <Text color=\"gray\" dimColor>\n • SQL query to find duplicate records\n </Text>\n </Box>\n );\n }\n\n if (phase === \"generating\") {\n return (\n <Box padding={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Generating code...</Text>\n </Box>\n );\n }\n\n // Result phase\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color={error ? \"red\" : \"green\"}>\n {error ? \"[x] Error\" : \"[done] Generated Code\"}\n </Text>\n {!error && <Text color=\"gray\"> -- {language}</Text>}\n </Box>\n\n {!error && (\n <Box marginBottom={1}>\n <Text color=\"gray\" dimColor>\n Prompt: {prompt}\n </Text>\n </Box>\n )}\n\n <Box\n borderColor={error ? \"red\" : \"cyan\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n padding={1}\n >\n <Text color={error ? \"red\" : \"white\"} wrap=\"wrap\">\n {error || code}\n </Text>\n </Box>\n\n {/* Status messages */}\n {saveStatus && (\n <Box marginTop={1}>\n <Text color={saveStatus.startsWith(\"Error\") ? \"red\" : \"green\"}>{saveStatus}</Text>\n </Box>\n )}\n {copyStatus && (\n <Box marginTop={1}>\n <Text color={copyStatus.includes(\"Failed\") ? \"red\" : \"green\"}>{copyStatus}</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n {error ? (\n <Text dimColor>Esc to try again</Text>\n ) : (\n <Text dimColor>\n <Text color=\"yellow\">s</Text> save to file | <Text color=\"yellow\">c</Text> copy to\n clipboard | <Text color=\"gray\">Esc</Text> generate more\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\n// Copy to clipboard utility\nfunction copyToClipboard(text: string): Promise<boolean> {\n return new Promise((resolve) => {\n const platform = os.platform();\n let cmd: string;\n\n if (platform === \"darwin\") {\n cmd = \"pbcopy\";\n } else if (platform === \"linux\") {\n cmd = \"xclip -selection clipboard\";\n } else if (platform === \"win32\") {\n cmd = \"clip\";\n } else {\n resolve(false);\n return;\n }\n\n const proc = exec(cmd, (err) => {\n resolve(!err);\n });\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n\ntype CreateSkillViewProps = {\n gerbil: Gerbil;\n onDone: () => void;\n};\n\ntype Step = \"name\" | \"description\" | \"behavior\" | \"generating\" | \"done\";\n\ntype SkillDraft = {\n name: string;\n description: string;\n behavior: string;\n};\n\n// Step definitions for visual progress\nconst STEPS = [\n { key: \"name\", num: 1, title: \"Name\", hint: \"Give your skill a unique identifier\" },\n { key: \"description\", num: 2, title: \"Purpose\", hint: \"What does this skill do?\" },\n { key: \"behavior\", num: 3, title: \"Logic\", hint: \"How should it work?\" },\n] as const;\n\nexport function CreateSkillView({ gerbil, onDone }: CreateSkillViewProps) {\n const [step, setStep] = useState<Step>(\"name\");\n const [draft, setDraft] = useState<SkillDraft>({\n name: \"\",\n description: \"\",\n behavior: \"\",\n });\n const [input, setInput] = useState(\"\");\n const [generatedCode, setGeneratedCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [saveStatus, setSaveStatus] = useState<string | null>(null);\n const [copyStatus, setCopyStatus] = useState<string | null>(null);\n\n const currentStepNum =\n step === \"name\" ? 1 : step === \"description\" ? 2 : step === \"behavior\" ? 3 : 4;\n\n const handleSubmit = async (value: string) => {\n if (!value.trim()) {\n return;\n }\n\n if (step === \"name\") {\n // Validate kebab-case\n const kebabCase = value\n .toLowerCase()\n .replace(/\\s+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"\");\n setDraft((d) => ({ ...d, name: kebabCase }));\n setInput(\"\");\n setStep(\"description\");\n } else if (step === \"description\") {\n setDraft((d) => ({ ...d, description: value }));\n setInput(\"\");\n setStep(\"behavior\");\n } else if (step === \"behavior\") {\n setDraft((d) => ({ ...d, behavior: value }));\n setInput(\"\");\n setStep(\"generating\");\n await generateSkill({ ...draft, behavior: value });\n }\n };\n\n // Render the step progress indicator\n const renderProgress = () => (\n <Box flexDirection=\"column\" marginBottom={1}>\n {/* Progress bar */}\n <Box marginBottom={1}>\n {STEPS.map((s, i) => {\n const isComplete = currentStepNum > s.num;\n const isCurrent = step === s.key;\n const _isPending = currentStepNum < s.num;\n\n return (\n <Box key={s.key}>\n {/* Step circle */}\n <Text bold={isCurrent} color={isComplete ? \"green\" : isCurrent ? \"cyan\" : \"gray\"}>\n {isComplete ? \"[x]\" : isCurrent ? \"[>]\" : \"[ ]\"}\n </Text>\n <Text bold={isCurrent} color={isComplete ? \"green\" : isCurrent ? \"cyan\" : \"gray\"}>\n {\" \"}\n {s.title}\n </Text>\n {/* Connector line */}\n {i < STEPS.length - 1 && <Text color={isComplete ? \"green\" : \"gray\"}> --- </Text>}\n </Box>\n );\n })}\n </Box>\n\n {/* Step details summary */}\n {(draft.name || draft.description) && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginBottom={1}\n paddingX={1}\n >\n {draft.name && (\n <Text>\n <Text dimColor>Name: </Text>\n <Text color=\"white\">{draft.name}</Text>\n </Text>\n )}\n {draft.description && (\n <Text>\n <Text dimColor>Purpose: </Text>\n <Text color=\"white\">{draft.description}</Text>\n </Text>\n )}\n </Box>\n )}\n </Box>\n );\n\n const generateSkill = async (skillDraft: SkillDraft) => {\n try {\n const prompt = `Generate a Gerbil skill file for:\nName: ${skillDraft.name}\nDescription: ${skillDraft.description}\nBehavior: ${skillDraft.behavior}\n\nGenerate TypeScript code that:\n1. Imports { defineSkill } from \"gerbil/skills\" and { z } from \"zod\"\n2. Defines an input schema with Zod\n3. Exports a skill using defineSkill()\n4. Implements the run function that uses gerbil.generate() or gerbil.json()\n\nOnly output the code, no explanations. The file should be complete and runnable.`;\n\n const result = await gerbil.generate(prompt, {\n maxTokens: 800,\n temperature: 0.3,\n system: \"You are an expert TypeScript developer. Generate clean, working code.\",\n });\n\n // Clean up the code - remove code fences and think tags\n let code = result.text;\n code = code\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\") // Remove think blocks\n .replace(/```typescript\\n?/g, \"\")\n .replace(/```\\n?/g, \"\")\n .trim();\n\n setGeneratedCode(code);\n setStep(\"done\");\n } catch (err) {\n setError(String(err));\n setStep(\"done\");\n }\n };\n\n const handleSave = async () => {\n if (!generatedCode || step !== \"done\") {\n return;\n }\n\n // Save to .gerbil/skills/ directory in cwd\n const gerbilDir = path.join(process.cwd(), \".gerbil\");\n const skillsDir = path.join(gerbilDir, \"skills\");\n const filePath = path.join(skillsDir, `${draft.name}.skill.ts`);\n\n try {\n // Create .gerbil/skills directory if it doesn't exist\n if (!fs.existsSync(skillsDir)) {\n fs.mkdirSync(skillsDir, { recursive: true });\n }\n\n fs.writeFileSync(filePath, generatedCode, \"utf-8\");\n setSaveStatus(`Saved to .gerbil/skills/${draft.name}.skill.ts`);\n\n // Clear status after 3 seconds\n setTimeout(() => setSaveStatus(null), 3000);\n } catch (err) {\n setSaveStatus(`Error: ${err}`);\n setTimeout(() => setSaveStatus(null), 3000);\n }\n };\n\n const handleCopy = async () => {\n if (!generatedCode || step !== \"done\") {\n return;\n }\n\n const success = await copyToClipboard(generatedCode);\n setCopyStatus(success ? \"Copied to clipboard!\" : \"Failed to copy\");\n setTimeout(() => setCopyStatus(null), 2000);\n };\n\n useInput((input, key) => {\n if (key.escape) {\n onDone();\n }\n\n // Save and copy shortcuts when skill is generated\n if (step === \"done\" && !error) {\n if (input === \"s\" || input === \"S\") {\n handleSave();\n }\n if (input === \"c\" || input === \"C\") {\n handleCopy();\n }\n }\n });\n\n const renderCurrentInput = () => {\n const currentStep = STEPS.find((s) => s.key === step);\n if (!currentStep) {\n return null;\n }\n\n const placeholders: Record<string, string> = {\n name: \"sentiment-analyzer, code-reviewer, email-writer...\",\n description: \"Analyzes text sentiment, Reviews code for issues...\",\n behavior:\n \"Input: text string. Output: { sentiment, confidence }. Use gerbil.json() for structured output...\",\n };\n\n const helpText: Record<string, string> = {\n name: \"Use lowercase letters, numbers, and hyphens only\",\n description: \"A clear, one-line description of what the skill does\",\n behavior: \"Describe inputs, outputs, and logic. Be specific about data types and structure.\",\n };\n\n return (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n {currentStep.hint}\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>{helpText[step]}</Text>\n </Box>\n <Box borderColor=\"cyan\" borderStyle=\"single\" paddingX={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder={placeholders[step]}\n value={input}\n />\n </Box>\n </Box>\n );\n };\n\n // Generating state\n if (step === \"generating\") {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>{renderProgress()}</Box>\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Generating skill code with AI...</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>This may take a few seconds</Text>\n </Box>\n </Box>\n );\n }\n\n // Done state\n if (step === \"done\") {\n if (error) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>\n <Text bold color=\"red\">\n Error generating skill\n </Text>\n </Box>\n <Text color=\"red\">{error}</Text>\n <Box marginTop={1}>\n <Text dimColor>Esc go back</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>\n <Text bold color=\"green\">\n [done] Skill Generated: {draft.name}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text dimColor>\n Save this code as: <Text color=\"white\">.gerbil/skills/{draft.name}.skill.ts</Text>\n </Text>\n </Box>\n <Box borderColor=\"green\" borderStyle=\"single\" flexDirection=\"column\" padding={1}>\n <Text color=\"white\" wrap=\"wrap\">\n {generatedCode}\n </Text>\n </Box>\n\n {/* Status messages */}\n {saveStatus && (\n <Box marginTop={1}>\n <Text color={saveStatus.startsWith(\"Error\") ? \"red\" : \"green\"}>{saveStatus}</Text>\n </Box>\n )}\n {copyStatus && (\n <Box marginTop={1}>\n <Text color={copyStatus.includes(\"Failed\") ? \"red\" : \"green\"}>{copyStatus}</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">s</Text> save to .gerbil/skills/ | <Text color=\"yellow\">c</Text>{\" \"}\n copy to clipboard | <Text color=\"gray\">Esc</Text> go back\n </Text>\n </Box>\n </Box>\n );\n }\n\n // Input steps (name, description, behavior)\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>{renderProgress()}</Box>\n {renderCurrentInput()}\n <Box marginTop={1}>\n <Text dimColor>Enter continue | Esc cancel</Text>\n </Box>\n </Box>\n );\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\ntype CreateToolViewProps = {\n gerbil: Gerbil;\n onDone: () => void;\n};\n\ntype Step = \"name\" | \"description\" | \"params\" | \"execute\" | \"generating\" | \"done\";\n\ntype ToolDraft = {\n name: string;\n description: string;\n params: string;\n execute: string;\n};\n\nconst STEPS = [\n { key: \"name\", num: 1, title: \"Name\", hint: \"Tool name (e.g., get_weather)\" },\n { key: \"description\", num: 2, title: \"Description\", hint: \"What does this tool do?\" },\n { key: \"params\", num: 3, title: \"Parameters\", hint: \"What inputs does it need?\" },\n { key: \"execute\", num: 4, title: \"Logic\", hint: \"What should it return?\" },\n] as const;\n\n// Template for tool files - simple format, no imports needed\nfunction buildToolCode(draft: ToolDraft, executeBody: string): string {\n return `// Gerbil Tool: ${draft.name}\n// No imports needed - just export a config object\n\nexport default {\n name: \"${draft.name}\",\n description: \"${draft.description}\",\n execute: async (params) => {\n${executeBody}\n },\n};\n`;\n}\n\nexport function CreateToolView({ gerbil, onDone }: CreateToolViewProps) {\n const [step, setStep] = useState<Step>(\"name\");\n const [draft, setDraft] = useState<ToolDraft>({\n name: \"\",\n description: \"\",\n params: \"\",\n execute: \"\",\n });\n const [input, setInput] = useState(\"\");\n const [generatedCode, setGeneratedCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [saveStatus, setSaveStatus] = useState<string | null>(null);\n\n const currentStepNum =\n step === \"name\"\n ? 1\n : step === \"description\"\n ? 2\n : step === \"params\"\n ? 3\n : step === \"execute\"\n ? 4\n : 5;\n\n useInput((char, key) => {\n if (key.escape && step === \"done\") {\n onDone();\n }\n if ((char === \"s\" || char === \"S\") && step === \"done\" && !error) {\n saveToFile();\n }\n });\n\n const handleSubmit = async (value: string) => {\n if (step === \"name\") {\n if (!value.trim()) {\n return; // Name is required\n }\n const snakeCase = value\n .toLowerCase()\n .replace(/\\s+/g, \"_\")\n .replace(/[^a-z0-9_]/g, \"\");\n setDraft((d) => ({ ...d, name: snakeCase }));\n setInput(\"\");\n setStep(\"description\");\n } else if (step === \"description\") {\n if (!value.trim()) {\n return; // Description is required\n }\n setDraft((d) => ({ ...d, description: value }));\n setInput(\"\");\n setStep(\"params\");\n } else if (step === \"params\") {\n // Params are optional - can press enter to skip\n setDraft((d) => ({ ...d, params: value.trim() || \"(none)\" }));\n setInput(\"\");\n setStep(\"execute\");\n } else if (step === \"execute\") {\n if (!value.trim()) {\n return; // Logic is required\n }\n setDraft((d) => ({ ...d, execute: value }));\n setInput(\"\");\n setStep(\"generating\");\n await generateTool({ ...draft, execute: value });\n }\n };\n\n const cleanCode = (text: string): string => {\n return text\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\") // Remove think blocks\n .replace(/<\\/?think>/g, \"\") // Remove unclosed think tags\n .replace(/^```[\\w]*\\n?/gm, \"\") // Remove code fence starts\n .replace(/```$/gm, \"\") // Remove code fence ends\n .replace(/\\*\\*/g, \"\") // Remove markdown bold **\n .replace(/`([^`]+)`/g, \"$1\") // Remove inline code backticks\n .trim();\n };\n\n const generateTool = async (finalDraft: ToolDraft) => {\n try {\n // Generate just the execute function body\n const hasParams = finalDraft.params !== \"(none)\";\n const executePrompt = `Write a simple function body for: ${finalDraft.execute}\n${hasParams ? `params has: ${finalDraft.params}` : \"\"}\nUse params?.fieldName with defaults. Return a string.\n\nExample:\n const value = params?.name || \"default\";\n return \\`Result: \\${value}\\`;`;\n\n let executeBody = \"\";\n for await (const chunk of gerbil.stream(executePrompt, {\n maxTokens: 200,\n system: \"Output only function body code. No markdown. No backticks. Must return a string.\",\n })) {\n executeBody += chunk;\n }\n executeBody = cleanCode(executeBody);\n\n // Ensure proper indentation\n if (!executeBody.startsWith(\" \")) {\n executeBody = executeBody\n .split(\"\\n\")\n .map((line) => ` ${line.trim()}`)\n .join(\"\\n\");\n }\n\n // Build final code from template\n const code = buildToolCode(finalDraft, executeBody);\n setGeneratedCode(code);\n setStep(\"done\");\n } catch (e) {\n setError(`${e}`);\n setStep(\"done\");\n }\n };\n\n const saveToFile = () => {\n try {\n const toolsDir = path.join(process.cwd(), \".gerbil\", \"tools\");\n if (!fs.existsSync(toolsDir)) {\n fs.mkdirSync(toolsDir, { recursive: true });\n }\n const filePath = path.join(toolsDir, `${draft.name}.tool.ts`);\n fs.writeFileSync(filePath, generatedCode);\n setSaveStatus(`Saved to .gerbil/tools/${draft.name}.tool.ts`);\n } catch (e) {\n setSaveStatus(`Error: ${e}`);\n }\n };\n\n const renderProgress = () => (\n <Box marginBottom={1}>\n {STEPS.map((s, i) => {\n const isComplete = currentStepNum > s.num;\n const isCurrent = step === s.key;\n return (\n <Box key={s.key} marginRight={1}>\n <Text color={isComplete ? \"green\" : isCurrent ? \"cyan\" : \"gray\"}>\n {isComplete ? \"[✓]\" : isCurrent ? \"[>]\" : \"[ ]\"} {s.title}\n </Text>\n {i < STEPS.length - 1 && <Text dimColor> --- </Text>}\n </Box>\n );\n })}\n </Box>\n );\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n {renderProgress()}\n\n {/* Current draft */}\n <Box\n borderColor=\"gray\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginBottom={1}\n paddingX={1}\n >\n <Text>\n <Text color=\"cyan\">Name:</Text> {draft.name || \"...\"}\n </Text>\n <Text>\n <Text color=\"cyan\">Description:</Text> {draft.description || \"...\"}\n </Text>\n <Text>\n <Text color=\"cyan\">Parameters:</Text> {draft.params || \"...\"}\n </Text>\n <Text>\n <Text color=\"cyan\">Logic:</Text> {draft.execute || \"...\"}\n </Text>\n </Box>\n\n {/* Step content */}\n {step === \"name\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Tool Name\n </Text>\n <Text dimColor>Use snake_case (e.g., get_weather, search_docs)</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"my_tool\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"description\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Description\n </Text>\n <Text dimColor>What does this tool do? (one sentence)</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"Gets the current weather for a city\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"params\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Parameters\n </Text>\n <Text dimColor>What inputs does this tool need? (press Enter to skip)</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"theme as a string (optional)\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"execute\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Logic\n </Text>\n <Text dimColor>What should this tool do and return?</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"fetch weather from API and return temperature\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"generating\" && (\n <Box flexDirection=\"column\">\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Generating execute function...</Text>\n </Box>\n <Text dimColor>Using template with your inputs</Text>\n </Box>\n )}\n\n {step === \"done\" && (\n <Box flexDirection=\"column\">\n {error ? (\n <Text color=\"red\">Error: {error}</Text>\n ) : (\n <>\n <Text bold color=\"green\">\n ✓ Tool Generated\n </Text>\n <Box\n borderColor=\"green\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text color=\"gray\">\n {generatedCode.slice(0, 500)}\n {generatedCode.length > 500 ? \"...\" : \"\"}\n </Text>\n </Box>\n\n <Box>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\">s</Text>\n <Text dimColor> to save to .gerbil/tools/ | </Text>\n <Text color=\"gray\">Esc</Text>\n <Text dimColor> to go back</Text>\n </Box>\n\n {saveStatus && (\n <Box marginTop={1}>\n <Text color={saveStatus.startsWith(\"Error\") ? \"red\" : \"green\"}>{saveStatus}</Text>\n </Box>\n )}\n </>\n )}\n </Box>\n )}\n </Box>\n );\n}\n","import { Box, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\nimport { hyperlink } from \"../utils.js\";\n\n// ============================================\n// Capability Tabs\n// ============================================\n\nconst CAPABILITIES = [\n { id: \"text\", name: \"Text\" },\n { id: \"vision\", name: \"Vision\" },\n { id: \"tts\", name: \"Text to Speech\" },\n { id: \"stt\", name: \"Transcription\" },\n { id: \"embeddings\", name: \"Embeddings\" },\n] as const;\n\ntype CapabilityId = (typeof CAPABILITIES)[number][\"id\"];\n\n// ============================================\n// Frameworks\n// ============================================\n\nconst FRAMEWORKS = [\n { id: \"standalone\", name: \"Standalone\" },\n { id: \"ai-sdk\", name: \"AI SDK v5\" },\n { id: \"browser\", name: \"React Hooks\" },\n { id: \"nextjs\", name: \"Next.js\" },\n { id: \"express\", name: \"Express\" },\n { id: \"langchain\", name: \"LangChain\" },\n] as const;\n\ntype FrameworkId = (typeof FRAMEWORKS)[number][\"id\"];\n\n// ============================================\n// Examples Data\n// ============================================\n\ninterface Example {\n install: string;\n code: string;\n description: string;\n}\n\nconst EXAMPLES: Record<CapabilityId, Partial<Record<FrameworkId, Example>>> = {\n // ============================================\n // TEXT\n // ============================================\n text: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Direct Gerbil usage for text generation with streaming.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"qwen3-0.6b\");\n\n// Generate text\nconst result = await g.generate(\"Write a haiku\", {\n maxTokens: 100,\n temperature: 0.7,\n});\nconsole.log(result.text);\n\n// Stream\nfor await (const chunk of g.stream(\"Tell me a story\")) {\n process.stdout.write(chunk);\n}\n\n// With thinking mode\nconst math = await g.generate(\"What is 127 × 43?\", { thinking: true });\nconsole.log(math.thinking); // Shows reasoning\nconsole.log(math.text); // \"5461\"`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"Vercel AI SDK v5 with LanguageModelV2 interface.\",\n code: `import { generateText, streamText } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\n\n// Generate\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Explain quantum computing\",\n});\n\n// Stream\nconst { textStream } = streamText({\n model: gerbil(\"qwen3-0.6b\"),\n system: \"You are a helpful assistant.\",\n messages: [{ role: \"user\", content: \"Hello!\" }],\n});\n\nfor await (const chunk of textStream) {\n process.stdout.write(chunk);\n}`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hooks for browser-based inference with WebGPU.\",\n code: `import { useChat, useCompletion } from \"@tryhamster/gerbil/browser\";\n\n// useChat - Full chat with message history\nfunction Chat() {\n const { messages, input, setInput, handleSubmit, isLoading } = useChat({\n model: \"qwen3-0.6b\",\n thinking: true,\n });\n if (isLoading) return <div>Loading...</div>;\n return (\n <form onSubmit={handleSubmit}>\n {messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)}\n <input value={input} onChange={e => setInput(e.target.value)} />\n </form>\n );\n}\n\n// useCompletion - One-off generation\nfunction Generator() {\n const { complete, completion, isLoading } = useCompletion();\n if (isLoading) return <div>Loading...</div>;\n return <button onClick={() => complete(\"Write a haiku\")}>{completion}</button>;\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js App Router with streaming chat endpoint.\",\n code: `// app/api/chat/route.ts\nimport { gerbil } from \"@tryhamster/gerbil/next\";\n\nexport const POST = gerbil.handler({ \n model: \"qwen3-0.6b\",\n system: \"You are a helpful assistant.\",\n});\n\n// Client usage with AI SDK React:\n// import { useChat } from \"@ai-sdk/react\";\n// const { messages, input, handleSubmit } = useChat();`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express middleware with generate and stream endpoints.\",\n code: `import express from \"express\";\nimport { gerbil } from \"@tryhamster/gerbil/express\";\n\nconst app = express();\napp.use(express.json());\napp.use(\"/api/ai\", gerbil({ model: \"qwen3-0.6b\" })());\n\n// POST /api/ai/generate { prompt, options }\n// POST /api/ai/stream { prompt, options } (SSE)\n// GET /api/ai/info\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain LLM wrapper for chains and agents.\",\n code: `import { GerbilLLM } from \"@tryhamster/gerbil/langchain\";\nimport { PromptTemplate } from \"langchain/prompts\";\nimport { LLMChain } from \"langchain/chains\";\n\nconst llm = new GerbilLLM({ \n model: \"qwen3-0.6b\",\n temperature: 0.7,\n});\n\nconst prompt = PromptTemplate.fromTemplate(\"Summarize: {text}\");\nconst chain = new LLMChain({ llm, prompt });\nconst result = await chain.call({ text: \"...\" });`,\n },\n },\n\n // ============================================\n // VISION\n // ============================================\n vision: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Vision models for image understanding and description.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"ministral-3b\"); // Vision-capable model\n\n// Describe an image\nconst result = await g.generate(\"What's in this image?\", {\n images: [{ source: \"https://example.com/photo.jpg\" }]\n});\nconsole.log(result.text);\n\n// Compare images\nconst diff = await g.generate(\"What's different?\", {\n images: [\n { source: \"before.jpg\" },\n { source: \"after.jpg\" }\n ]\n});\n\n// Check if model supports vision\nconsole.log(g.supportsVision()); // true`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK with image content parts.\",\n code: `import { generateText } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\n\nconst { text } = await generateText({\n model: gerbil(\"ministral-3b\"),\n messages: [{\n role: \"user\",\n content: [\n { type: \"image\", image: new URL(\"https://example.com/photo.jpg\") },\n { type: \"text\", text: \"Describe this image in detail\" },\n ],\n }],\n});\n\n// Also accepts:\n// - Base64 strings: { type: \"image\", image: \"data:image/png;base64,...\" }\n// - Uint8Array: { type: \"image\", image: bytes, mimeType: \"image/png\" }`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hooks with image attachment support.\",\n code: `import { useChat } from \"@tryhamster/gerbil/browser\";\n\nfunction VisionChat() {\n const { messages, input, setInput, handleSubmit, attachImage, attachedImages } = useChat({\n model: \"ministral-3b\"\n });\n\n const handleFile = (e) => {\n const file = e.target.files?.[0];\n if (file) {\n const reader = new FileReader();\n reader.onload = () => attachImage(reader.result);\n reader.readAsDataURL(file);\n }\n };\n\n return (\n <div>\n {messages.map(m => <div key={m.id}>{m.content}</div>)}\n <input type=\"file\" accept=\"image/*\" onChange={handleFile} />\n {attachedImages.length > 0 && <span>📎 {attachedImages.length} attached</span>}\n <form onSubmit={handleSubmit}>\n <input value={input} onChange={e => setInput(e.target.value)} />\n </form>\n </div>\n );\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js vision endpoint with image handling.\",\n code: `// app/api/vision/route.ts\nimport { gerbil } from \"@tryhamster/gerbil/next\";\n\nexport const POST = gerbil.handler({ model: \"ministral-3b\" });\n\n// Client fetch:\n// await fetch(\"/api/vision\", {\n// method: \"POST\",\n// body: JSON.stringify({\n// prompt: \"What's in this image?\",\n// images: [{ source: dataUri }]\n// })\n// });`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express vision endpoint with image input.\",\n code: `import express from \"express\";\nimport { gerbil } from \"@tryhamster/gerbil/express\";\n\nconst app = express();\napp.use(express.json({ limit: \"10mb\" })); // For large images\napp.use(\"/api/vision\", gerbil({ model: \"ministral-3b\" })());\n\n// POST /api/vision/generate\n// Body: { prompt: \"Describe this\", images: [{ source: \"...\" }] }\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain LLM with vision support.\",\n code: `import { GerbilLLM } from \"@tryhamster/gerbil/langchain\";\n\nconst llm = new GerbilLLM({ model: \"ministral-3b\" });\n\n// Check vision support\nconst hasVision = await llm.supportsVision();\n\n// Generate with images\nconst result = await llm.invokeWithImages(\n \"Describe what you see in this image\",\n [{ source: \"https://example.com/photo.jpg\" }]\n);\n\nconsole.log(result);`,\n },\n },\n\n // ============================================\n // TTS (Text-to-Speech)\n // ============================================\n tts: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Kokoro TTS with 28 natural voices.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\n// Generate speech\nconst result = await g.speak(\"Hello, I'm Gerbil!\", {\n voice: \"af_heart\", // American female (best quality)\n speed: 1.0\n});\n\n// result.audio = Float32Array (PCM samples)\n// result.sampleRate = 24000\n// result.duration = seconds\n\n// Stream long text\nfor await (const chunk of g.speakStream(\"Long paragraph...\")) {\n // Play each chunk as it's generated\n console.log(\\`Chunk: \\${chunk.samples.length} samples\\`);\n}\n\n// List voices\nconst voices = g.listVoices();\n// af_heart, bf_emma, am_fenrir, bm_george, ...`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK SpeechModelV2 interface.\",\n code: `import { experimental_generateSpeech as generateSpeech } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\n\nconst result = await generateSpeech({\n model: gerbil.speech(), // kokoro-82m\n text: \"Hello from Gerbil!\",\n voice: \"bf_emma\", // British female\n});\n\n// result.audio = Uint8Array (WAV format)\nawait writeFile(\"output.wav\", result.audio);\n\n// List available voices\nconst voices = gerbil.listVoices();\n// [{ id: \"af_heart\", name: \"Heart\", gender: \"female\", language: \"en-US\" }, ...]`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hook for browser TTS with playback.\",\n code: `import { useSpeech } from \"@tryhamster/gerbil/browser\";\n\nfunction SpeechDemo() {\n const { speak, stop, isSpeaking, isLoading, listVoices, setVoice } = useSpeech();\n\n if (isLoading) return <div>Loading TTS model...</div>;\n\n return (\n <div>\n <select onChange={e => setVoice(e.target.value)}>\n {listVoices().map(v => (\n <option key={v.id} value={v.id}>{v.name} ({v.language})</option>\n ))}\n </select>\n <button onClick={() => speak(\"Hello world!\")}>\n {isSpeaking ? \"Speaking...\" : \"Speak\"}\n </button>\n {isSpeaking && <button onClick={stop}>Stop</button>}\n </div>\n );\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js TTS endpoint returning audio.\",\n code: `// app/api/tts/route.ts\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\nexport async function POST(req: Request) {\n const { text, voice = \"af_heart\" } = await req.json();\n const result = await g.speak(text, { voice });\n \n // Convert Float32Array to WAV buffer\n const wavBuffer = float32ToWav(result.audio, result.sampleRate);\n \n return new Response(wavBuffer, {\n headers: { \"Content-Type\": \"audio/wav\" }\n });\n}`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express TTS endpoint with voice selection.\",\n code: `import express from \"express\";\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst app = express();\nconst g = new Gerbil();\n\napp.post(\"/api/tts\", express.json(), async (req, res) => {\n const { text, voice = \"af_heart\", speed = 1.0 } = req.body;\n const result = await g.speak(text, { voice, speed });\n \n // Send as WAV\n res.type(\"audio/wav\");\n res.send(float32ToWav(result.audio, result.sampleRate));\n});\n\napp.get(\"/api/tts/voices\", (req, res) => {\n res.json(g.listVoices());\n});\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain TTS utility for pipelines.\",\n code: `import { GerbilTTS } from \"@tryhamster/gerbil/langchain\";\n\nconst tts = new GerbilTTS({ voice: \"af_heart\", speed: 1.0 });\n\n// Generate speech\nconst result = await tts.speak(\"Hello from LangChain!\");\n// result.audio = Float32Array, result.sampleRate = 24000\n\n// Stream long text\nfor await (const chunk of tts.speakStream(\"Long text...\")) {\n console.log(\\`Chunk \\${chunk.index}: \\${chunk.samples.length} samples\\`);\n}\n\n// List available voices\nconst voices = await tts.listVoices();`,\n },\n },\n\n // ============================================\n // STT (Speech-to-Text / Transcription)\n // ============================================\n stt: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Whisper STT with timestamps and language detection.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\nimport { readFileSync } from \"fs\";\n\nconst g = new Gerbil();\n\n// Transcribe audio file\nconst audio = new Uint8Array(readFileSync(\"audio.wav\"));\nconst result = await g.transcribe(audio);\nconsole.log(result.text);\n\n// With timestamps\nconst result = await g.transcribe(audio, { timestamps: true });\nfor (const seg of result.segments) {\n console.log(\\`[\\${seg.start}s] \\${seg.text}\\`);\n}\n\n// Record from microphone (5 seconds)\nconst live = await g.listen(5000);\nconsole.log(live.text);\n\n// Available models: whisper-tiny.en, whisper-base.en, whisper-small, etc.`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK TranscriptionModelV2 interface.\",\n code: `import { experimental_transcribe as transcribe } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\nimport { readFile } from \"fs/promises\";\n\nconst result = await transcribe({\n model: gerbil.transcription(), // whisper-tiny.en\n audio: await readFile(\"audio.wav\"),\n});\n\nconsole.log(result.text); // \"Hello world\"\nconsole.log(result.language); // \"en\"\nconsole.log(result.durationInSeconds); // 2.5\nconsole.log(result.segments); // Timestamped segments\n\n// Use larger model for better accuracy\nconst result2 = await transcribe({\n model: gerbil.transcription(\"whisper-base\"),\n audio: audioBuffer,\n});\n\n// List available models\nconst models = gerbil.listTranscriptionModels();`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hooks for recording and transcription.\",\n code: `import { useVoiceInput, useVoiceChat } from \"@tryhamster/gerbil/browser\";\n\n// Record and transcribe\nfunction VoiceInput() {\n const { startRecording, stopRecording, isRecording, transcript } = useVoiceInput({\n model: \"whisper-tiny.en\",\n onTranscript: (text) => console.log(\"User said:\", text),\n });\n\n return (\n <button onClick={isRecording ? stopRecording : startRecording}>\n {isRecording ? \"🔴 Stop\" : \"🎤 Record\"}\n </button>\n );\n}\n\n// Full voice conversation: STT → LLM → TTS\nfunction VoiceAssistant() {\n const { startListening, stopListening, stage, messages } = useVoiceChat({\n llmModel: \"qwen3-0.6b\",\n sttModel: \"whisper-tiny.en\",\n voice: \"af_bella\",\n });\n\n return (\n <button onMouseDown={startListening} onMouseUp={stopListening}>\n {stage === \"idle\" ? \"🎤 Hold to Speak\" : stage}\n </button>\n );\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js transcription endpoint.\",\n code: `// app/api/transcribe/route.ts\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\nexport async function POST(req: Request) {\n const formData = await req.formData();\n const audioFile = formData.get(\"audio\") as File;\n const audioBuffer = new Uint8Array(await audioFile.arrayBuffer());\n \n const result = await g.transcribe(audioBuffer, {\n timestamps: true,\n });\n \n return Response.json({\n text: result.text,\n segments: result.segments,\n duration: result.duration,\n });\n}`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express multer\",\n description: \"Express transcription endpoint with file upload.\",\n code: `import express from \"express\";\nimport multer from \"multer\";\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst app = express();\nconst upload = multer();\nconst g = new Gerbil();\n\napp.post(\"/api/transcribe\", upload.single(\"audio\"), async (req, res) => {\n const audioBuffer = new Uint8Array(req.file.buffer);\n const result = await g.transcribe(audioBuffer, {\n timestamps: req.query.timestamps === \"true\",\n language: req.query.language,\n });\n \n res.json({\n text: result.text,\n segments: result.segments,\n duration: result.duration,\n });\n});\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain STT utility for pipelines.\",\n code: `import { GerbilSTT } from \"@tryhamster/gerbil/langchain\";\nimport { readFileSync } from \"fs\";\n\nconst stt = new GerbilSTT({ model: \"whisper-tiny.en\" });\n\n// Transcribe audio\nconst audio = new Uint8Array(readFileSync(\"audio.wav\"));\nconst result = await stt.transcribe(audio);\nconsole.log(result.text);\n\n// With timestamps\nconst result = await stt.transcribe(audio, { timestamps: true });\nfor (const seg of result.segments) {\n console.log(\\`[\\${seg.start}s] \\${seg.text}\\`);\n}\n\n// List available models\nconst models = await stt.listModels();`,\n },\n },\n\n // ============================================\n // EMBEDDINGS\n // ============================================\n embeddings: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Generate embeddings for semantic search and RAG.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\n// Single embedding\nconst result = await g.embed(\"Hello world\");\nconsole.log(result.vector); // number[] (384 dimensions)\nconsole.log(result.dimensions); // 384\n\n// Batch embeddings\nconst results = await g.embedBatch([\n \"First document\",\n \"Second document\",\n \"Third document\",\n]);\n\n// Cosine similarity for search\nfunction cosineSim(a: number[], b: number[]): number {\n const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);\n const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));\n const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));\n return dot / (magA * magB);\n}`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK embeddings (use standalone for now).\",\n code: `// Embeddings are best used directly with Gerbil\n// AI SDK embedding integration coming soon\n\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\nconst result = await g.embed(\"Your text here\");\n\n// Use with AI SDK's embed() when available:\n// import { embed } from \"ai\";\n// import { gerbil } from \"@tryhamster/gerbil/ai\";\n// const { embedding } = await embed({\n// model: gerbil.embedding(),\n// value: \"Hello world\",\n// });`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Browser-based embeddings with WebGPU.\",\n code: `import { createGerbilWorker } from \"@tryhamster/gerbil/browser\";\n\n// Create worker for embeddings\nconst worker = await createGerbilWorker({ modelId: \"qwen3-0.6b\" });\n\n// Note: Browser embeddings use the main model\n// For dedicated embedding model, use Gerbil on server\n\n// Server-side approach (recommended):\n// 1. Create API endpoint with Gerbil\n// 2. Call from browser\n\n// app/api/embed/route.ts\n// const g = new Gerbil();\n// export async function POST(req) {\n// const { text } = await req.json();\n// const result = await g.embed(text);\n// return Response.json(result);\n// }`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js embedding endpoint for RAG.\",\n code: `// app/api/embed/route.ts\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\nexport async function POST(req: Request) {\n const { text, texts } = await req.json();\n \n if (texts) {\n // Batch embedding\n const results = await g.embedBatch(texts);\n return Response.json({ embeddings: results.map(r => r.vector) });\n }\n \n // Single embedding\n const result = await g.embed(text);\n return Response.json({\n vector: result.vector,\n dimensions: result.dimensions,\n });\n}`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express embedding endpoint.\",\n code: `import express from \"express\";\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst app = express();\napp.use(express.json());\nconst g = new Gerbil();\n\napp.post(\"/api/embed\", async (req, res) => {\n const { text, texts } = req.body;\n \n if (texts) {\n const results = await g.embedBatch(texts);\n res.json({ embeddings: results.map(r => r.vector) });\n } else {\n const result = await g.embed(text);\n res.json({ vector: result.vector, dimensions: result.dimensions });\n }\n});\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain embeddings for RAG pipelines.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\nimport { Document } from \"langchain/document\";\nimport { MemoryVectorStore } from \"langchain/vectorstores/memory\";\n\nconst g = new Gerbil();\n\n// Custom embeddings class for LangChain\nclass GerbilEmbeddings {\n async embedDocuments(texts: string[]) {\n const results = await g.embedBatch(texts);\n return results.map(r => r.vector);\n }\n async embedQuery(text: string) {\n const result = await g.embed(text);\n return result.vector;\n }\n}\n\n// Use in vector store\nconst embeddings = new GerbilEmbeddings();\nconst vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);\nconst results = await vectorStore.similaritySearch(\"query\", 5);`,\n },\n },\n};\n\n// ============================================\n// Component\n// ============================================\n\nexport function FrameworksView() {\n const [capabilityIndex, setCapabilityIndex] = useState(0);\n // Track the selected framework by ID to maintain position across tabs\n const [selectedFrameworkId, setSelectedFrameworkId] = useState<FrameworkId>(\"standalone\");\n\n const currentCapability = CAPABILITIES[capabilityIndex];\n const capabilityExamples = EXAMPLES[currentCapability.id];\n const availableFrameworks = FRAMEWORKS.filter((f) => capabilityExamples[f.id]);\n\n // Find index of selected framework in available list, or fall back to closest position\n let frameworkIndex = availableFrameworks.findIndex((f) => f.id === selectedFrameworkId);\n if (frameworkIndex === -1) {\n // Framework not available in this tab - find by original position in FRAMEWORKS\n const originalIndex = FRAMEWORKS.findIndex((f) => f.id === selectedFrameworkId);\n // Clamp to available range\n frameworkIndex = Math.min(originalIndex, availableFrameworks.length - 1);\n frameworkIndex = Math.max(0, frameworkIndex);\n }\n\n const currentFramework = availableFrameworks[frameworkIndex] || availableFrameworks[0];\n const example = currentFramework ? capabilityExamples[currentFramework.id] : null;\n\n useInput((_input, key) => {\n if (key.upArrow) {\n const newIndex = Math.max(0, frameworkIndex - 1);\n setSelectedFrameworkId(availableFrameworks[newIndex].id);\n }\n if (key.downArrow) {\n const newIndex = Math.min(availableFrameworks.length - 1, frameworkIndex + 1);\n setSelectedFrameworkId(availableFrameworks[newIndex].id);\n }\n if (key.leftArrow) {\n setCapabilityIndex((i) => Math.max(0, i - 1));\n // Keep selectedFrameworkId - will auto-adjust in next render\n }\n if (key.rightArrow) {\n setCapabilityIndex((i) => Math.min(CAPABILITIES.length - 1, i + 1));\n // Keep selectedFrameworkId - will auto-adjust in next render\n }\n if (key.tab) {\n // Tab cycles through capabilities\n setCapabilityIndex((i) => (i + 1) % CAPABILITIES.length);\n // Keep selectedFrameworkId - will auto-adjust in next render\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold>Gerbil Integrations</Text>\n <Text dimColor>Select a capability and framework to see examples</Text>\n\n {/* Capability Tabs */}\n <Box marginY={1} gap={1}>\n {CAPABILITIES.map((cap, i) => (\n <Box key={cap.id}>\n <Text\n bold={i === capabilityIndex}\n color={i === capabilityIndex ? \"cyan\" : \"gray\"}\n inverse={i === capabilityIndex}\n >\n {\" \"}\n {cap.name}{\" \"}\n </Text>\n </Box>\n ))}\n </Box>\n\n <Box flexDirection=\"row\">\n {/* Framework List */}\n <Box flexDirection=\"column\" marginRight={2} minWidth={16}>\n {availableFrameworks.map((f, i) => (\n <Text\n bold={i === frameworkIndex}\n color={i === frameworkIndex ? \"cyan\" : \"gray\"}\n key={f.id}\n >\n {i === frameworkIndex ? \"> \" : \" \"}\n {f.name}\n </Text>\n ))}\n </Box>\n\n {/* Code Panel */}\n {example && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n flexGrow={1}\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n {currentCapability.name} + {currentFramework.name}\n </Text>\n <Text dimColor>{example.description}</Text>\n\n <Box marginTop={1}>\n <Text color=\"yellow\">$ </Text>\n <Text>{example.install}</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Example:</Text>\n <Text color=\"green\">{example.code}</Text>\n </Box>\n </Box>\n )}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">←/→</Text> or <Text color=\"yellow\">Tab</Text> switch capability |{\" \"}\n <Text color=\"yellow\">↑/↓</Text> select framework | See{\" \"}\n {hyperlink(\"https://github.com/gethamster/gerbil/tree/main/docs\", \"docs\")} |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","import os from \"node:os\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { Gerbil } from \"../../../core/gerbil.js\";\nimport { getCacheInfo, getMemoryInfo, hyperlink, type SessionStats } from \"../utils.js\";\n\n/** WebGPU process info from Gerbil.getWebGPUProcesses() */\ntype WebGPUInfo = {\n browser: {\n running: boolean;\n pid: number | null;\n port: number;\n activePagesCount: number;\n maxPages: number;\n };\n backends: Array<{\n modelId: string;\n isVision: boolean;\n isReady: boolean;\n memory: { usedGB: number; totalGB: number; usedPercent: number } | null;\n }>;\n};\n\n/** Cross-process Chrome page info */\ntype ChromePageInfo = {\n url: string;\n title: string;\n isOurs: boolean;\n modelId: string | null;\n memory: { usedGB: number; totalGB: number } | null;\n};\n\n/** TTS/STT model info */\ntype VoiceModelInfo = {\n id: string;\n loaded: boolean;\n device?: \"webgpu\" | \"cpu\";\n};\n\ntype InfoViewProps = {\n gerbil: Gerbil;\n model: string;\n modelFamily: string;\n stats: SessionStats;\n onGoToCache?: () => void;\n};\n\nfunction getDeviceLabel(deviceMode: \"webgpu\" | \"cpu\" | \"wasm\"): string {\n const platform = os.platform();\n const arch = os.arch();\n\n if (deviceMode === \"webgpu\") {\n if (platform === \"darwin\" && arch === \"arm64\") {\n return \"WebGPU (Chrome → Metal)\";\n }\n if (platform === \"linux\") {\n return \"WebGPU (Chrome → Vulkan)\";\n }\n if (platform === \"win32\") {\n return \"WebGPU (Chrome → D3D12)\";\n }\n return \"WebGPU (Chrome)\";\n }\n\n if (platform === \"darwin\" && arch === \"arm64\") {\n return `Apple Silicon (${deviceMode.toUpperCase()})`;\n }\n if (platform === \"darwin\" && arch === \"x64\") {\n return `Intel Mac (${deviceMode.toUpperCase()})`;\n }\n\n return deviceMode.toUpperCase();\n}\n\nexport function InfoView({ gerbil, model, modelFamily, stats, onGoToCache }: InfoViewProps) {\n const deviceMode = gerbil.getDeviceMode();\n const dtype = gerbil.getDtype();\n const chromeStatus = gerbil.getChromeStatus();\n const deviceLabel = getDeviceLabel(deviceMode);\n\n const cacheInfo = getCacheInfo();\n const memInfo = getMemoryInfo();\n\n // WebGPU process monitoring state\n const [webgpuInfo, setWebgpuInfo] = useState<WebGPUInfo | null>(null);\n const [allPages, setAllPages] = useState<ChromePageInfo[]>([]);\n const [totalPageCount, setTotalPageCount] = useState(0);\n const [selectedIndex, setSelectedIndex] = useState<number>(-1); // -1 = none, 0+ = page index\n const [killing, setKilling] = useState(false);\n const [killResult, setKillResult] = useState<string | null>(null);\n\n // TTS/STT model info - in state so they update when voice mode is enabled\n const [ttsInfo, setTtsInfo] = useState<VoiceModelInfo | null>(gerbil.getTTSModelInfo());\n const [sttInfo, setSttInfo] = useState<VoiceModelInfo | null>(gerbil.getSTTModelInfo());\n\n // Fetch WebGPU info (cross-process)\n const fetchInfo = useCallback(async () => {\n const [info, pages, totalPages] = await Promise.all([\n Gerbil.getWebGPUProcesses(),\n Gerbil.getAllChromePagesInfo(),\n Gerbil.getTotalChromePageCount(),\n ]);\n setWebgpuInfo(info);\n setAllPages(pages || []);\n setTotalPageCount(totalPages);\n\n // Update TTS/STT info (these can change when voice mode is toggled)\n setTtsInfo(gerbil.getTTSModelInfo());\n setSttInfo(gerbil.getSTTModelInfo());\n\n // Reset selection if it's out of bounds\n const pageCount = pages?.length ?? 0;\n if (selectedIndex >= pageCount) {\n setSelectedIndex(pageCount > 0 ? 0 : -1);\n }\n }, [selectedIndex, gerbil]);\n\n // Fetch on mount and periodically (every 2s for more responsive updates)\n useEffect(() => {\n fetchInfo();\n const interval = setInterval(fetchInfo, 2000);\n return () => clearInterval(interval);\n }, [fetchInfo]);\n\n useInput(async (input, key) => {\n if (input === \"c\" || input === \"C\") {\n onGoToCache?.();\n return;\n }\n\n // Refresh with 'r'\n if (input === \"r\" || input === \"R\") {\n await fetchInfo();\n return;\n }\n\n // Navigate selection with arrow keys or j/k\n const pageCount = allPages.length;\n if (pageCount > 0) {\n if (key.downArrow || input === \"j\") {\n setSelectedIndex((prev) => Math.min(prev + 1, pageCount - 1));\n return;\n }\n if (key.upArrow || input === \"k\") {\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\n return;\n }\n // Number keys to select page (1-9)\n const num = Number.parseInt(input, 10);\n if (num >= 1 && num <= 9 && num <= pageCount) {\n setSelectedIndex(num - 1);\n return;\n }\n }\n\n // Kill selected page with 'x' (cross-process)\n if (input === \"x\" && !killing && selectedIndex >= 0 && allPages[selectedIndex]) {\n setKilling(true);\n setKillResult(null);\n try {\n const page = allPages[selectedIndex];\n const success = await Gerbil.killChromePage(selectedIndex);\n if (success) {\n const shortName = page.modelId?.split(\"/\").pop() || \"page\";\n const suffix = page.isOurs ? \"\" : \" (other session)\";\n setKillResult(`Killed: ${shortName}${suffix}`);\n } else {\n setKillResult(\"Failed to kill page\");\n }\n await fetchInfo();\n } catch (err: any) {\n setKillResult(`Error: ${err.message}`);\n }\n setKilling(false);\n setTimeout(() => setKillResult(null), 3000);\n return;\n }\n\n // Kill ALL with 'K' (shift+k)\n if (input === \"K\" && !killing && webgpuInfo?.browser.running) {\n setKilling(true);\n setKillResult(null);\n try {\n const result = await Gerbil.killAllWebGPU();\n if (result) {\n setKillResult(\n `Killed ${result.pagesKilled} page(s)${result.browserKilled ? \", browser closed\" : \"\"}`,\n );\n } else {\n setKillResult(\"No WebGPU processes to kill\");\n }\n await fetchInfo();\n setSelectedIndex(-1);\n } catch (err: any) {\n setKillResult(`Error: ${err.message}`);\n }\n setKilling(false);\n setTimeout(() => setKillResult(null), 3000);\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold>System Information</Text>\n\n <Box borderColor=\"gray\" borderStyle=\"single\" flexDirection=\"column\" marginY={1} paddingX={1}>\n <Text bold color=\"cyan\">\n LLM Model\n </Text>\n <Text>\n Current:{\" \"}\n <Text bold color=\"cyan\">\n {model}\n </Text>\n </Text>\n <Text>\n Family: <Text color=\"gray\">{modelFamily}</Text>\n </Text>\n <Text>\n Device: <Text color={deviceMode === \"webgpu\" ? \"green\" : \"yellow\"}>{deviceLabel}</Text>\n </Text>\n <Text>\n Dtype: <Text color={dtype === \"q4f16\" ? \"green\" : \"gray\"}>{dtype}</Text>\n </Text>\n </Box>\n\n {/* TTS/STT Voice Models Section */}\n <Box flexDirection=\"row\" marginBottom={1}>\n <Box\n borderColor={ttsInfo?.loaded ? \"green\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n minWidth={30}\n paddingX={1}\n >\n <Text bold color={ttsInfo?.loaded ? \"cyan\" : \"gray\"}>\n 🔊 TTS (Text-to-Speech)\n </Text>\n {ttsInfo ? (\n <>\n <Text>\n Model:{\" \"}\n <Text bold color={ttsInfo.loaded ? \"cyan\" : \"yellow\"}>\n {ttsInfo.id}\n </Text>\n </Text>\n <Text>\n Status:{\" \"}\n <Text color={ttsInfo.loaded ? \"green\" : \"yellow\"}>\n {ttsInfo.loaded ? \"● Loaded\" : \"○ Not loaded\"}\n </Text>\n </Text>\n {ttsInfo.device && (\n <Text>\n Device:{\" \"}\n <Text color={ttsInfo.device === \"webgpu\" ? \"green\" : \"gray\"}>\n {ttsInfo.device.toUpperCase()}\n </Text>\n </Text>\n )}\n </>\n ) : (\n <Text color=\"gray\" dimColor>\n Not initialized\n </Text>\n )}\n </Box>\n\n <Box\n borderColor={sttInfo?.loaded ? \"green\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n minWidth={30}\n paddingX={1}\n >\n <Text bold color={sttInfo?.loaded ? \"cyan\" : \"gray\"}>\n 🎤 STT (Speech-to-Text)\n </Text>\n {sttInfo ? (\n <>\n <Text>\n Model:{\" \"}\n <Text bold color={sttInfo.loaded ? \"cyan\" : \"yellow\"}>\n {sttInfo.id}\n </Text>\n </Text>\n <Text>\n Status:{\" \"}\n <Text color={sttInfo.loaded ? \"green\" : \"yellow\"}>\n {sttInfo.loaded ? \"● Loaded\" : \"○ Not loaded\"}\n </Text>\n </Text>\n {sttInfo.device && (\n <Text>\n Device:{\" \"}\n <Text color={sttInfo.device === \"webgpu\" ? \"green\" : \"gray\"}>\n {sttInfo.device.toUpperCase()}\n </Text>\n </Text>\n )}\n </>\n ) : (\n <Text color=\"gray\" dimColor>\n Not initialized\n </Text>\n )}\n </Box>\n </Box>\n\n <Box flexDirection=\"row\" marginBottom={1}>\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n minWidth={30}\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n Memory\n </Text>\n <Text>\n Total: <Text color=\"gray\">{memInfo.total}</Text>\n </Text>\n <Text>\n Used:{\" \"}\n <Text color={memInfo.percentUsed > 80 ? \"red\" : \"yellow\"}>\n {memInfo.used} ({memInfo.percentUsed}%)\n </Text>\n </Text>\n <Text>\n Free: <Text color=\"green\">{memInfo.free}</Text>\n </Text>\n </Box>\n\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n minWidth={30}\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n Session Stats\n </Text>\n <Text>\n Prompts: <Text color=\"gray\">{stats.prompts}</Text>\n </Text>\n <Text>\n Tokens In: <Text color=\"gray\">{stats.tokensIn}</Text>\n </Text>\n <Text>\n Tokens Out: <Text color=\"gray\">{stats.tokensOut}</Text>\n </Text>\n <Text>\n Total Time: <Text color=\"gray\">{Math.round(stats.totalTimeMs / 1000)}s</Text>\n </Text>\n {stats.lastTokPerSec > 0 && (\n <Text>\n Last tok/s: <Text color=\"yellow\">{stats.lastTokPerSec.toFixed(1)}</Text>\n </Text>\n )}\n {stats.avgTokPerSec > 0 && (\n <Text>\n Avg tok/s: <Text color=\"cyan\">{stats.avgTokPerSec.toFixed(1)}</Text>\n </Text>\n )}\n {stats.benchResults.length > 0 && (\n <Text>\n Best Bench:{\" \"}\n <Text color=\"green\">\n {Math.max(...stats.benchResults.map((b) => b.tokPerSec))} tok/s\n </Text>\n </Text>\n )}\n </Box>\n\n {chromeStatus && (\n <Box\n borderColor=\"green\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n minWidth={38}\n paddingX={1}\n >\n <Text bold color=\"green\">\n WebGPU Backend\n </Text>\n <Text>\n Status: <Text color=\"green\">● Running</Text>\n </Text>\n <Text>\n PID:{\" \"}\n <Text color={chromeStatus.pid ? \"gray\" : \"yellow\"}>\n {chromeStatus.pid ?? \"connected (no PID)\"}\n </Text>\n </Text>\n <Text>\n Port: <Text color=\"gray\">{chromeStatus.port}</Text>\n </Text>\n <Text>\n Model:{\" \"}\n <Text color=\"gray\">\n {chromeStatus.modelId.length > 28\n ? `...${chromeStatus.modelId.slice(-25)}`\n : chromeStatus.modelId}\n </Text>\n </Text>\n </Box>\n )}\n </Box>\n\n {/* WebGPU Processes Section */}\n {webgpuInfo && (\n <Box\n borderColor={webgpuInfo.browser.running ? \"green\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginBottom={1}\n paddingX={1}\n >\n <Text bold color={webgpuInfo.browser.running ? \"green\" : \"gray\"}>\n WebGPU Processes <Text dimColor>(all sessions)</Text>\n </Text>\n <Text>\n Chrome:{\" \"}\n <Text color={webgpuInfo.browser.running ? \"green\" : \"gray\"}>\n {webgpuInfo.browser.running ? \"● Running\" : \"○ Not running\"}\n </Text>\n </Text>\n {webgpuInfo.browser.running && (\n <>\n <Text>\n {\" \"}\n PID: <Text color=\"gray\">{webgpuInfo.browser.pid ?? \"unknown\"}</Text>\n </Text>\n <Text>\n {\" \"}\n Port: <Text color=\"gray\">{webgpuInfo.browser.port}</Text>\n </Text>\n <Text>\n {\" \"}\n Total Pages:{\" \"}\n <Text color={totalPageCount > 0 ? \"yellow\" : \"gray\"}>{totalPageCount}</Text>{\" \"}\n <Text dimColor>({webgpuInfo.browser.activePagesCount} ours)</Text>\n </Text>\n </>\n )}\n\n {allPages.length > 0 && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>\n Active Pages <Text color=\"gray\">(↑↓ select, x kill, K kill all)</Text>:\n </Text>\n {allPages.map((page, i) => {\n const isSelected = i === selectedIndex;\n const modelName = page.modelId || \"loading...\";\n const shortName = modelName.length > 30 ? `...${modelName.slice(-27)}` : modelName;\n\n return (\n <Box flexDirection=\"column\" key={i} marginLeft={1}>\n <Text>\n <Text color=\"gray\">{i + 1}.</Text>{\" \"}\n <Text color={isSelected ? \"cyan\" : page.isOurs ? \"green\" : \"yellow\"}>\n {isSelected ? \"▶\" : page.isOurs ? \"●\" : \"○\"}\n </Text>{\" \"}\n <Text bold={isSelected} inverse={isSelected}>\n {shortName}\n </Text>\n {page.isOurs ? (\n <Text color=\"green\" dimColor>\n {\" \"}\n (this session)\n </Text>\n ) : (\n <Text color=\"yellow\" dimColor>\n {\" \"}\n (other session)\n </Text>\n )}\n </Text>\n {page.memory && (\n <Text dimColor>\n {\" \"}Heap: {page.memory.usedGB.toFixed(2)}GB used of{\" \"}\n {page.memory.totalGB.toFixed(2)}GB\n </Text>\n )}\n </Box>\n );\n })}\n </Box>\n )}\n\n {totalPageCount === 0 && webgpuInfo.browser.running && (\n <Text color=\"yellow\" dimColor>\n ⚠ No active pages (zombie browser?) - press K to kill\n </Text>\n )}\n\n {killing && (\n <Box marginTop={1}>\n <Text color=\"cyan\">Killing...</Text>\n </Box>\n )}\n {killResult && (\n <Box marginTop={1}>\n <Text color=\"cyan\">{killResult}</Text>\n </Box>\n )}\n </Box>\n )}\n\n <Box borderColor=\"gray\" borderStyle=\"single\" flexDirection=\"column\" paddingX={1}>\n <Text bold color=\"cyan\">\n Cache\n </Text>\n <Text>\n Location:{\" \"}\n <Text dimColor>\n {(cacheInfo.locations[0] || \"\").length > 50\n ? `...${(cacheInfo.locations[0] || \"\").slice(-47)}`\n : cacheInfo.locations[0] || \"none\"}\n </Text>\n </Text>\n <Text>\n Total Size: <Text color=\"yellow\">{cacheInfo.totalSize || \"empty\"}</Text>\n </Text>\n <Text>\n Models: <Text color=\"gray\">{cacheInfo.models.length}</Text>\n </Text>\n <Text dimColor>\n Press <Text color=\"cyan\">c</Text> to manage cached models\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>\n Runtime: <Text color=\"gray\">Node.js {process.version}</Text> | Gerbil:{\" \"}\n <Text color=\"gray\">0.1.x</Text> |\n {hyperlink(\"https://github.com/gethamster/gerbil\", \"GitHub\")}\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"cyan\">c</Text> cache | <Text color=\"cyan\">r</Text> refresh\n {allPages.length > 0 && (\n <>\n {\" \"}\n | <Text color=\"red\">x</Text> kill selected | <Text color=\"red\">K</Text> kill all\n </>\n )}\n {webgpuInfo?.browser.running && allPages.length === 0 && (\n <>\n {\" \"}\n | <Text color=\"red\">K</Text> kill browser\n </>\n )}\n {\" \"}| <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport Spinner from \"ink-spinner\";\n\ntype LoadingViewProps = {\n message: string;\n};\n\nexport function LoadingView({ message }: LoadingViewProps) {\n return (\n <Box\n alignItems=\"center\"\n flexDirection=\"column\"\n height=\"100%\"\n justifyContent=\"center\"\n padding={2}\n >\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n 🐹 Gerbil\n </Text>\n </Box>\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> {message}</Text>\n </Box>\n </Box>\n );\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { refreshCachedModelSizes } from \"../../../core/chrome-backend.js\";\nimport {\n type CachedModelInfo,\n fetchModelMetadata,\n formatBytes,\n getCacheInfo,\n getModelBenchmarkStats,\n isModelCached,\n PRESET_MODELS,\n} from \"../utils.js\";\n\ntype HFModel = {\n id: string;\n downloads: number;\n likes: number;\n createdAt?: string;\n // These are fetched separately via individual model API\n sizeBytes?: number;\n contextLength?: number;\n params?: number;\n loading?: boolean;\n};\n\ntype HFApiModel = {\n id: string;\n modelId: string;\n downloads: number;\n likes: number;\n createdAt?: string;\n pipeline_tag?: string;\n library_name?: string;\n tags?: string[];\n};\n\ntype HFModelDetails = {\n siblings?: { rfilename: string; size?: number }[];\n safetensors?: { parameters?: Record<string, number>; total?: number };\n usedStorage?: number;\n};\n\n// Extract param count from model name (e.g., \"Qwen3-0.6B\" -> 0.6B, \"llama-7b\" -> 7B)\nfunction extractParamsFromName(modelId: string): number | null {\n // Match patterns like: 0.6B, 7B, 1.7B, 70B, 360M, etc.\n const match = modelId.match(/[-_](\\d+(?:\\.\\d+)?)(B|M|K)(?:[-_]|$)/i);\n if (match) {\n const num = Number.parseFloat(match[1]);\n const unit = match[2].toUpperCase();\n if (unit === \"B\") {\n return num * 1e9;\n }\n if (unit === \"M\") {\n return num * 1e6;\n }\n if (unit === \"K\") {\n return num * 1e3;\n }\n }\n return null;\n}\n\nfunction formatParams(params: number | undefined, modelId?: string): string {\n // Try the provided params first\n if (params && params > 0) {\n if (params >= 1e9) {\n return `${(params / 1e9).toFixed(1)}B`;\n }\n if (params >= 1e6) {\n return `${Math.round(params / 1e6)}M`;\n }\n if (params >= 1e3) {\n return `${Math.round(params / 1e3)}K`;\n }\n return `${params}`;\n }\n\n // Fallback: try to extract from model name\n if (modelId) {\n const extracted = extractParamsFromName(modelId);\n if (extracted) {\n if (extracted >= 1e9) {\n return `${(extracted / 1e9).toFixed(1)}B`;\n }\n if (extracted >= 1e6) {\n return `${Math.round(extracted / 1e6)}M`;\n }\n if (extracted >= 1e3) {\n return `${Math.round(extracted / 1e3)}K`;\n }\n }\n }\n\n return \"-\";\n}\n\nfunction formatDownloads(n: number): string {\n if (n >= 1e6) {\n return `${(n / 1e6).toFixed(1)}M`;\n }\n if (n >= 1e3) {\n return `${(n / 1e3).toFixed(1)}K`;\n }\n return `${n}`;\n}\n\nfunction formatDate(dateStr: string | undefined): string {\n if (!dateStr) {\n return \"-\";\n }\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays === 0) {\n return \"today\";\n }\n if (diffDays === 1) {\n return \"1d ago\";\n }\n if (diffDays < 7) {\n return `${diffDays}d ago`;\n }\n if (diffDays < 30) {\n return `${Math.floor(diffDays / 7)}w ago`;\n }\n if (diffDays < 365) {\n return `${Math.floor(diffDays / 30)}mo ago`;\n }\n return `${Math.floor(diffDays / 365)}y ago`;\n}\n\nfunction formatContext(contextLength: number | undefined): string {\n if (!contextLength) {\n return \"-\";\n }\n if (contextLength >= 1e6) {\n return `${(contextLength / 1e6).toFixed(0)}M`;\n }\n if (contextLength >= 1e3) {\n return `${(contextLength / 1e3).toFixed(0)}K`;\n }\n return `${contextLength}`;\n}\n\ntype ModelViewProps = {\n currentModel: string;\n onSelect: (model: string) => void;\n onDownloadOnly?: (model: string, hfId: string) => Promise<boolean>;\n onTabChange?: (tab: \"preset\" | \"cached\" | \"huggingface\" | \"voice\") => void;\n onSearchChange?: (query: string) => void;\n /** Callback when TTS model is selected */\n onSelectTTS?: (modelId: string) => void;\n /** Callback when STT model is selected */\n onSelectSTT?: (modelId: string) => void;\n /** Current TTS model ID */\n currentTTSModel?: string;\n /** Current STT model ID */\n currentSTTModel?: string;\n};\n\n// TTS models available\nconst TTS_MODELS = [\n {\n id: \"kokoro-82m\",\n name: \"Kokoro 82M\",\n size: \"~200MB\",\n voices: 10,\n quality: \"High\",\n speed: \"Fast\",\n },\n {\n id: \"supertonic-66m\",\n name: \"Supertonic 66M\",\n size: \"~150MB\",\n voices: 4,\n quality: \"Good\",\n speed: \"Faster\",\n },\n];\n\n// STT models available\nconst STT_MODELS = [\n {\n id: \"whisper-tiny.en\",\n name: \"Whisper Tiny (English)\",\n size: \"~75MB\",\n quality: \"Basic\",\n speed: \"Fastest\",\n },\n {\n id: \"whisper-base.en\",\n name: \"Whisper Base (English)\",\n size: \"~150MB\",\n quality: \"Good\",\n speed: \"Fast\",\n },\n {\n id: \"whisper-small.en\",\n name: \"Whisper Small (English)\",\n size: \"~500MB\",\n quality: \"Better\",\n speed: \"Medium\",\n },\n {\n id: \"whisper-large-v3-turbo\",\n name: \"Whisper Large v3 Turbo\",\n size: \"~1.5GB\",\n quality: \"Best\",\n speed: \"Slower\",\n },\n];\n\n// Secondary sort applied on top of downloads (primary sort)\ntype SortMode = \"none\" | \"likes\" | \"updatedAt\";\ntype SizeFilter = \"all\" | \"small\" | \"medium\" | \"large\";\n\nconst PAGE_SIZE = 15;\n\nexport function ModelView({\n currentModel,\n onSelect,\n onDownloadOnly,\n onTabChange,\n onSearchChange,\n onSelectTTS,\n onSelectSTT,\n currentTTSModel = \"kokoro-82m\",\n currentSTTModel = \"whisper-tiny.en\",\n}: ModelViewProps) {\n const [tab, setTab] = useState<\"preset\" | \"cached\" | \"huggingface\" | \"voice\">(\"preset\");\n const [selected, setSelected] = useState(\n PRESET_MODELS.findIndex((m) => m.id === currentModel) || 0,\n );\n const [hfModels, setHfModels] = useState<HFModel[]>([]);\n const [hfSelected, setHfSelected] = useState(0);\n const [hfLoading, setHfLoading] = useState(false);\n const [hfSearchInput, setHfSearchInput] = useState(\"\");\n const [hfSearchFocused, setHfSearchFocused] = useState(false);\n const [hfError, setHfError] = useState(\"\");\n const [hfHasMore, setHfHasMore] = useState(true);\n const [sortMode, setSortMode] = useState<SortMode>(\"none\");\n const [sizeFilter, setSizeFilter] = useState<SizeFilter>(\"all\");\n const [cachedModels, setCachedModels] = useState<Set<string>>(new Set());\n const [localModels, setLocalModels] = useState<CachedModelInfo[]>([]);\n const [localSelected, setLocalSelected] = useState(0);\n const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);\n\n // Voice tab state\n const [voiceSection, setVoiceSection] = useState<\"tts\" | \"stt\">(\"tts\");\n const [ttsSelected, setTtsSelected] = useState(\n TTS_MODELS.findIndex((m) => m.id === currentTTSModel) || 0,\n );\n const [sttSelected, setSttSelected] = useState(\n STT_MODELS.findIndex((m) => m.id === currentSTTModel) || 0,\n );\n\n // Track actual metadata for preset models (fetched from HF)\n const [presetMetadata, setPresetMetadata] = useState<\n Record<string, { sizeBytes?: number; contextLength?: number }>\n >({});\n\n // Track which models we've fetched details for\n const fetchedDetailsRef = useRef<Set<string>>(new Set());\n const abortControllerRef = useRef<AbortController | null>(null);\n const hasFetchedOnMountRef = useRef(false);\n\n const refreshCachedModels = async () => {\n // Refresh sizes for models with missing or suspiciously small sizes\n await refreshCachedModelSizes().catch(() => {});\n\n const cached = new Set<string>();\n for (const model of PRESET_MODELS) {\n if (isModelCached(model.hfId)) {\n cached.add(model.id);\n }\n }\n setCachedModels(cached);\n const cacheInfo = getCacheInfo();\n setLocalModels(cacheInfo.models);\n\n // Fetch missing metadata for cached models (context, size)\n fetchCachedModelMetadata(cacheInfo.models);\n };\n\n // Fetch metadata for cached models that are missing context or have wrong size\n const fetchCachedModelMetadata = async (models: CachedModelInfo[]) => {\n const MIN_SIZE = 1_000_000; // 1MB - anything smaller is likely wrong\n const needsFetch = models.filter(\n (m) =>\n !(m.contextLength && m.modelSize) ||\n m.modelSize === \"~\" ||\n (m.modelSize.endsWith(\"B\") && Number.parseInt(m.modelSize, 10) < MIN_SIZE),\n );\n if (needsFetch.length === 0) {\n return;\n }\n\n const batchSize = 3;\n const updatedModels = [...models];\n\n for (let i = 0; i < needsFetch.length; i += batchSize) {\n const batch = needsFetch.slice(i, i + batchSize);\n await Promise.all(\n batch.map(async (model) => {\n try {\n // Convert model name to HF ID format\n const hfId = model.name.replace(\"--\", \"/\");\n const meta = await fetchModelMetadata(hfId);\n\n // Update in the array\n const idx = updatedModels.findIndex((m) => m.name === model.name);\n if (idx >= 0 && meta) {\n if (meta.sizeBytes) {\n updatedModels[idx].modelSize = formatBytes(meta.sizeBytes);\n updatedModels[idx].totalSize = formatBytes(meta.sizeBytes);\n }\n if (meta.contextLength) {\n updatedModels[idx].contextLength = meta.contextLength;\n }\n }\n } catch {\n // Ignore fetch errors\n }\n }),\n );\n }\n // Update state only once at the end\n setLocalModels([...updatedModels]);\n };\n\n // Fetch actual metadata (size, context) for preset models from HuggingFace\n const fetchPresetModelMetadata = async () => {\n const metadata: Record<string, { sizeBytes?: number; contextLength?: number }> = {};\n\n // Fetch in parallel with small batches\n const batchSize = 3;\n for (let i = 0; i < PRESET_MODELS.length; i += batchSize) {\n const batch = PRESET_MODELS.slice(i, i + batchSize);\n await Promise.all(\n batch.map(async (model) => {\n try {\n const result = await fetchModelMetadata(model.hfId);\n if (result.sizeBytes || result.contextLength) {\n metadata[model.id] = result;\n }\n } catch {\n // Ignore fetch errors\n }\n }),\n );\n }\n // Update state only once at the end\n setPresetMetadata({ ...metadata });\n };\n\n useEffect(() => {\n if (hasFetchedOnMountRef.current) return;\n hasFetchedOnMountRef.current = true;\n refreshCachedModels();\n fetchPresetModelMetadata();\n }, []);\n\n // Fetch model details (size, params, context) for a single model\n const fetchModelDetails = async (\n modelId: string,\n ): Promise<{ sizeBytes: number; params: number; contextLength?: number } | null> => {\n try {\n // First get model info for params\n const res = await fetch(`https://huggingface.co/api/models/${modelId}`);\n if (!res.ok) {\n return null;\n }\n const info: HFModelDetails = await res.json();\n\n // Try to get params from safetensors info\n let params = 0;\n if (info.safetensors?.parameters) {\n const p = info.safetensors.parameters;\n params = p.BF16 || p.F16 || p.F32 || info.safetensors.total || 0;\n }\n\n // Fetch size and context using our utility\n const metadata = await fetchModelMetadata(modelId);\n\n // Fallback to usedStorage if we couldn't get ONNX size\n const sizeBytes = metadata.sizeBytes || info.usedStorage || 0;\n\n return { sizeBytes, params, contextLength: metadata.contextLength };\n } catch {\n return null;\n }\n };\n\n // Fetch all model details in parallel after getting the list\n const fetchAllModelDetails = async (models: HFModel[]) => {\n // Fetch all details in parallel (batch of 5 at a time to avoid rate limiting)\n const batchSize = 5;\n const updatedModels = [...models];\n\n for (let i = 0; i < models.length; i += batchSize) {\n const batch = models.slice(i, i + batchSize);\n const results = await Promise.all(batch.map((m) => fetchModelDetails(m.id)));\n\n results.forEach((result, idx) => {\n const modelIdx = i + idx;\n if (result && updatedModels[modelIdx]) {\n updatedModels[modelIdx] = {\n ...updatedModels[modelIdx],\n sizeBytes: result.sizeBytes,\n params: result.params,\n contextLength: result.contextLength,\n loading: false,\n };\n } else if (updatedModels[modelIdx]) {\n updatedModels[modelIdx] = { ...updatedModels[modelIdx], loading: false };\n }\n });\n\n // Update state after each batch so user sees progress\n setHfModels([...updatedModels]);\n }\n };\n\n const fetchHFModels = async (query?: string, append = false, sort: SortMode = sortMode) => {\n if (hfLoading) {\n return;\n }\n\n // Cancel any pending request\n abortControllerRef.current?.abort();\n abortControllerRef.current = new AbortController();\n\n setHfLoading(true);\n setHfError(\"\");\n\n try {\n const offset = append ? hfModels.length : 0;\n\n // Build URL with query params - fetch extra for preloading\n // Note: HF API's library=onnx filter isn't reliable, so we use filter=onnx and search\n // Always sort by downloads first (primary), secondary sort applied client-side\n const params = new URLSearchParams({\n filter: \"onnx\",\n sort: \"downloads\",\n direction: \"-1\",\n limit: String(PAGE_SIZE * 2), // Fetch double for preloading next page\n });\n\n // Handle compound search (comma-separated terms like \"qwen3, onnx\")\n // Combine all terms with spaces for the search API\n if (query) {\n const searchTerms = query\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n // Always include \"onnx\" in search to help filter results\n if (!searchTerms.some((t) => t.toLowerCase().includes(\"onnx\"))) {\n searchTerms.push(\"onnx\");\n }\n params.set(\"search\", searchTerms.join(\" \"));\n } else {\n // Default search includes onnx\n params.set(\"search\", \"onnx\");\n }\n\n if (offset > 0) {\n params.set(\"skip\", String(offset));\n }\n\n const res = await fetch(`https://huggingface.co/api/models?${params}`, {\n signal: abortControllerRef.current.signal,\n });\n\n if (!res.ok) {\n throw new Error(\"API error\");\n }\n const data: HFApiModel[] = await res.json();\n\n // Apply secondary sort on top of downloads (primary sort from API)\n const sortedData = [...data];\n if (sort === \"likes\") {\n // Sort by likes within download tiers\n sortedData.sort((a, b) => (b.likes || 0) - (a.likes || 0));\n } else if (sort === \"updatedAt\") {\n // Sort by recency within download tiers\n sortedData.sort((a, b) => {\n const aDate = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const bDate = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return bDate - aDate;\n });\n }\n\n // Then boost models with q4f16/q4 quantization or from onnx-community\n sortedData.sort((a, b) => {\n const aHasQuant =\n a.id.includes(\"q4f16\") || a.id.includes(\"q4\") || a.id.startsWith(\"onnx-community/\");\n const bHasQuant =\n b.id.includes(\"q4f16\") || b.id.includes(\"q4\") || b.id.startsWith(\"onnx-community/\");\n if (aHasQuant && !bHasQuant) {\n return -1;\n }\n if (!aHasQuant && bHasQuant) {\n return 1;\n }\n return 0; // Keep original order for equal priority\n });\n\n const models: HFModel[] = sortedData.map((m) => ({\n id: m.id,\n downloads: m.downloads || 0,\n likes: m.likes || 0,\n createdAt: m.createdAt,\n loading: true,\n }));\n\n setHfHasMore(models.length >= PAGE_SIZE);\n\n if (append) {\n const newModels = [...hfModels, ...models];\n setHfModels(newModels);\n // Fetch details for new models in background\n fetchAllModelDetails(models);\n } else {\n fetchedDetailsRef.current.clear();\n setHfModels(models);\n setHfSelected(0);\n // Fetch all details immediately\n fetchAllModelDetails(models);\n }\n } catch (e: any) {\n if (e.name !== \"AbortError\") {\n setHfError(\"Failed to fetch models. Check your connection.\");\n if (!append) {\n setHfModels([]);\n }\n }\n }\n setHfLoading(false);\n };\n\n useEffect(() => {\n if (tab === \"huggingface\" && hfModels.length === 0 && !hfLoading) {\n fetchHFModels();\n }\n }, [tab, fetchHFModels, hfLoading, hfModels.length]);\n\n const loadMoreModels = () => {\n if (!hfLoading && hfHasMore) {\n fetchHFModels(hfSearchInput || undefined, true);\n }\n };\n\n // Filter models by size (only works for models with fetched params)\n const getFilteredModels = () => {\n if (sizeFilter === \"all\") {\n return hfModels;\n }\n return hfModels.filter((m) => {\n if (!m.params) {\n return true; // Show models without params info\n }\n if (sizeFilter === \"small\") {\n return m.params < 500e6;\n }\n if (sizeFilter === \"medium\") {\n return m.params >= 500e6 && m.params < 3e9;\n }\n if (sizeFilter === \"large\") {\n return m.params >= 3e9;\n }\n return true;\n });\n };\n\n const filteredModels = getFilteredModels();\n\n const deleteModel = (model: CachedModelInfo) => {\n try {\n const possiblePaths = [\n path.join(model.location, model.name),\n path.join(model.location, model.name.replace(\"/\", \"--\")),\n path.join(model.location, `models--${model.name.replace(\"/\", \"--\")}`),\n ];\n\n for (const p of possiblePaths) {\n if (fs.existsSync(p)) {\n fs.rmSync(p, { recursive: true, force: true });\n break;\n }\n }\n\n refreshCachedModels();\n setDeleteConfirm(null);\n\n if (localSelected >= localModels.length - 1) {\n setLocalSelected(Math.max(0, localModels.length - 2));\n }\n } catch {\n setDeleteConfirm(null);\n refreshCachedModels();\n }\n };\n\n useInput((input, key) => {\n if (deleteConfirm !== null) {\n if (input === \"y\" || input === \"Y\") {\n // Find model in localModels by matching either name or hfId format\n const model = localModels.find(\n (m) =>\n m.name === deleteConfirm ||\n m.name.replace(\"--\", \"/\") === deleteConfirm ||\n m.name === deleteConfirm.replace(\"/\", \"--\"),\n );\n if (model) {\n deleteModel(model);\n }\n } else {\n setDeleteConfirm(null);\n }\n return;\n }\n\n if (tab === \"huggingface\" && hfSearchFocused) {\n if (key.escape) {\n setHfSearchFocused(false);\n return;\n }\n return;\n }\n\n if (key.tab) {\n // Cycle through tabs: preset -> voice -> huggingface -> preset\n const tabOrder: Array<\"preset\" | \"voice\" | \"huggingface\"> = [\n \"preset\",\n \"voice\",\n \"huggingface\",\n ];\n const currentIndex = tabOrder.indexOf(tab as \"preset\" | \"voice\" | \"huggingface\");\n const newTab = tabOrder[(currentIndex + 1) % tabOrder.length];\n setTab(newTab);\n setHfSearchFocused(false);\n onTabChange?.(newTab);\n return;\n }\n\n if (tab === \"preset\" || tab === \"cached\") {\n // Unified Models tab\n if (key.upArrow) {\n setSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n setSelected((s) => Math.min(unifiedModels.length - 1, s + 1));\n }\n\n if (key.return && unifiedModels.length > 0) {\n const model = unifiedModels[selected];\n if (model.isCached) {\n onSelect(model.isRecommended ? model.id : model.hfId);\n } else {\n // Download if not cached\n onDownloadOnly?.(model.id, model.hfId).then((success) => {\n if (success) {\n refreshCachedModels();\n }\n });\n }\n }\n\n if (input === \"d\" || input === \"D\") {\n const model = unifiedModels[selected];\n if (!model.isCached) {\n onDownloadOnly?.(model.id, model.hfId).then((success) => {\n if (success) {\n refreshCachedModels();\n }\n });\n }\n }\n\n if ((input === \"x\" || input === \"X\") && unifiedModels.length > 0) {\n const model = unifiedModels[selected];\n if (model.isCached) {\n setDeleteConfirm(model.hfId);\n }\n }\n } else if (tab === \"huggingface\") {\n if (input === \"s\" || input === \"S\") {\n setHfSearchFocused(true);\n return;\n }\n if (input === \"o\" || input === \"O\") {\n // Cycle through secondary sort options (downloads is always primary)\n const modes: SortMode[] = [\"none\", \"likes\", \"updatedAt\"];\n const idx = modes.indexOf(sortMode);\n const newSort = modes[(idx + 1) % modes.length];\n setSortMode(newSort);\n fetchedDetailsRef.current.clear();\n fetchHFModels(hfSearchInput || undefined, false, newSort);\n return;\n }\n if (input === \"f\" || input === \"F\") {\n const filters: SizeFilter[] = [\"all\", \"small\", \"medium\", \"large\"];\n const idx = filters.indexOf(sizeFilter);\n setSizeFilter(filters[(idx + 1) % filters.length]);\n setHfSelected(0);\n return;\n }\n if (key.upArrow) {\n setHfSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n const newSelected = Math.min(filteredModels.length - 1, hfSelected + 1);\n setHfSelected(newSelected);\n if (newSelected >= filteredModels.length - 3 && hfHasMore && !hfLoading) {\n loadMoreModels();\n }\n }\n if (input === \"n\" || input === \"N\") {\n loadMoreModels();\n }\n if (key.return && filteredModels.length > 0) {\n const selectedModel = filteredModels[hfSelected];\n // Only allow Enter if model is cached\n if (selectedModel && isModelCached(selectedModel.id)) {\n onSelect(selectedModel.id);\n }\n }\n if ((input === \"d\" || input === \"D\") && filteredModels.length > 0) {\n const model = filteredModels[hfSelected];\n if (model) {\n onDownloadOnly?.(model.id, model.id).then((success) => {\n if (success) {\n refreshCachedModels();\n }\n });\n }\n }\n }\n\n // Voice tab input handling\n if (tab === \"voice\") {\n // Switch between TTS and STT sections with left/right arrows\n if (key.leftArrow || key.rightArrow) {\n setVoiceSection((s) => (s === \"tts\" ? \"stt\" : \"tts\"));\n return;\n }\n\n if (voiceSection === \"tts\") {\n if (key.upArrow) {\n setTtsSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n setTtsSelected((s) => Math.min(TTS_MODELS.length - 1, s + 1));\n }\n if (key.return) {\n const model = TTS_MODELS[ttsSelected];\n onSelectTTS?.(model.id);\n }\n } else {\n if (key.upArrow) {\n setSttSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n setSttSelected((s) => Math.min(STT_MODELS.length - 1, s + 1));\n }\n if (key.return) {\n const model = STT_MODELS[sttSelected];\n onSelectSTT?.(model.id);\n }\n }\n }\n });\n\n const handleSearch = (value: string) => {\n setHfSearchInput(value);\n };\n\n const handleSearchSubmit = () => {\n setHfHasMore(true);\n fetchedDetailsRef.current.clear();\n fetchHFModels(hfSearchInput || undefined, false);\n setHfSearchFocused(false);\n onSearchChange?.(hfSearchInput);\n };\n\n // Helper to render colored shortcuts\n const renderHelpText = () => {\n if (deleteConfirm !== null) {\n return (\n <Text dimColor>\n Press <Text color=\"red\">y</Text> to confirm delete, any other key to cancel\n </Text>\n );\n }\n if (tab === \"huggingface\") {\n if (hfSearchFocused) {\n return (\n <Text dimColor>\n Type to search | <Text color=\"yellow\">Enter</Text> submit |{\" \"}\n <Text color=\"gray\">Esc</Text> cancel\n </Text>\n );\n }\n const selectedModel = filteredModels[hfSelected];\n const isCached = selectedModel ? isModelCached(selectedModel.id) : false;\n return (\n <Text dimColor>\n <Text color=\"gray\">↑↓</Text> nav |{\" \"}\n {isCached ? (\n <>\n <Text color=\"yellow\">Enter</Text> use |{\" \"}\n </>\n ) : null}\n <Text color=\"cyan\">s</Text> search | <Text color=\"cyan\">o</Text> sort |{\" \"}\n <Text color=\"cyan\">f</Text> filter | <Text color=\"cyan\">n</Text> more |{\" \"}\n <Text color=\"green\">d</Text> download | <Text color=\"cyan\">Tab</Text> |{\" \"}\n <Text color=\"gray\">Esc</Text>\n </Text>\n );\n }\n // Voice tab\n if (tab === \"voice\") {\n return (\n <Text dimColor>\n <Text color=\"gray\">←→</Text> switch TTS/STT | <Text color=\"gray\">↑↓</Text> select |{\" \"}\n <Text color=\"yellow\">Enter</Text> use | <Text color=\"cyan\">Tab</Text> switch |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </Text>\n );\n }\n // Unified Models tab\n const selectedUnified = unifiedModels[selected];\n const showDownload = selectedUnified && !selectedUnified.isCached;\n const showDelete = selectedUnified?.isCached;\n return (\n <Text dimColor>\n <Text color=\"gray\">↑↓</Text> select | <Text color=\"yellow\">Enter</Text> use\n {showDownload ? (\n <>\n {\" \"}\n | <Text color=\"green\">d</Text> download\n </>\n ) : null}\n {showDelete ? (\n <>\n {\" \"}\n | <Text color=\"red\">x</Text> delete\n </>\n ) : null}{\" \"}\n | <Text color=\"cyan\">Tab</Text> switch | <Text color=\"gray\">Esc</Text> back\n </Text>\n );\n };\n\n const getSortLabel = () => {\n switch (sortMode) {\n case \"none\":\n return \"downloads\";\n case \"updatedAt\":\n return \"recent\";\n case \"likes\":\n return \"likes\";\n }\n };\n\n const getFilterLabel = () => {\n switch (sizeFilter) {\n case \"small\":\n return \"<500M\";\n case \"medium\":\n return \"500M-3B\";\n case \"large\":\n return \"3B+\";\n default:\n return \"all\";\n }\n };\n\n // Build unified model list: recommended first, then other cached models\n const unifiedModels = React.useMemo(() => {\n const models: Array<{\n id: string;\n name: string;\n hfId: string;\n size: string;\n contextLength?: number;\n speed?: string;\n isCached: boolean;\n isRecommended: boolean;\n lastUsed?: string;\n benchStats?: { avgTokPerSec: number };\n }> = [];\n\n // Add recommended models first\n for (const preset of PRESET_MODELS) {\n const isCached = cachedModels.has(preset.id) || isModelCached(preset.hfId);\n const meta = presetMetadata[preset.id];\n const cachedInfo = localModels.find(\n (m) => m.name === preset.hfId || m.name.replace(\"--\", \"/\") === preset.hfId,\n );\n const benchStats = getModelBenchmarkStats(preset.hfId);\n models.push({\n id: preset.id,\n name: preset.name,\n hfId: preset.hfId,\n size: meta?.sizeBytes ? formatBytes(meta.sizeBytes) : preset.size,\n contextLength: meta?.contextLength || cachedInfo?.contextLength,\n speed: preset.speed,\n isCached,\n isRecommended: true,\n lastUsed: cachedInfo?.lastUsed,\n benchStats: benchStats ? { avgTokPerSec: benchStats.avgTokPerSec } : undefined,\n });\n }\n\n // Add other cached models (not in recommended)\n for (const cached of localModels) {\n const isRecommended = PRESET_MODELS.some(\n (p) => p.hfId === cached.name || p.hfId === cached.name.replace(\"--\", \"/\"),\n );\n if (!isRecommended) {\n const benchStats = getModelBenchmarkStats(cached.name);\n models.push({\n id: cached.name,\n name: cached.name.length > 35 ? `${cached.name.slice(0, 32)}...` : cached.name,\n hfId: cached.name.replace(\"--\", \"/\"),\n size:\n cached.modelSize !== cached.totalSize\n ? `${cached.modelSize} (${cached.totalSize})`\n : cached.modelSize,\n contextLength: cached.contextLength,\n isCached: true,\n isRecommended: false,\n lastUsed: cached.lastUsed,\n benchStats: benchStats ? { avgTokPerSec: benchStats.avgTokPerSec } : undefined,\n });\n }\n }\n\n return models;\n }, [cachedModels, presetMetadata, localModels]);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text\n bold={tab === \"preset\" || tab === \"cached\"}\n color={tab === \"preset\" || tab === \"cached\" ? \"cyan\" : \"gray\"}\n >\n [Models]\n </Text>\n <Text color=\"gray\"> </Text>\n <Text bold={tab === \"voice\"} color={tab === \"voice\" ? \"magenta\" : \"gray\"}>\n [Voice]\n </Text>\n <Text color=\"gray\"> </Text>\n <Text bold={tab === \"huggingface\"} color={tab === \"huggingface\" ? \"cyan\" : \"gray\"}>\n [HuggingFace]\n </Text>\n <Text dimColor> (Tab to switch)</Text>\n </Box>\n\n {(tab === \"preset\" || tab === \"cached\") && (\n <>\n <Text dimColor>\n Your models {localModels.length > 0 ? `(${localModels.length} cached)` : \"\"} •{\" \"}\n <Text color=\"yellow\">★</Text> = recommended\n </Text>\n <Box marginTop={1}>\n <Text dimColor>\n {\"Model\".padEnd(44)}\n {\"Size\".padEnd(14)}\n {\"Context\".padEnd(10)}\n {\"Speed\".padEnd(12)}\n {\"Benchmark\".padEnd(14)}\n {\"Last Used\"}\n </Text>\n </Box>\n <Box flexDirection=\"column\" marginBottom={1}>\n {unifiedModels.length === 0 ? (\n <Text color=\"gray\">No models yet. Press Tab to browse HuggingFace.</Text>\n ) : (\n unifiedModels.map((model, i) => {\n const isCurrent = currentModel === model.id || currentModel === model.hfId;\n const isPendingDelete = deleteConfirm === model.hfId;\n const ctxDisplay = formatContext(model.contextLength);\n return (\n <Box key={model.id}>\n <Text color={isPendingDelete ? \"red\" : model.isCached ? \"green\" : \"gray\"}>\n {isPendingDelete ? \"✕\" : model.isCached ? \"●\" : \"○\"}{\" \"}\n </Text>\n <Text color={model.isRecommended ? \"yellow\" : \"gray\"}>\n {model.isRecommended ? \"★ \" : \" \"}\n </Text>\n <Text color={isPendingDelete ? \"red\" : i === selected ? \"cyan\" : \"white\"}>\n {i === selected ? \"> \" : \" \"}\n {model.name.length > 38\n ? `${model.name.slice(0, 35)}...`\n : model.name.padEnd(38)}\n </Text>\n <Text dimColor>{model.size.padEnd(14)}</Text>\n <Text color=\"magenta\">{ctxDisplay.padEnd(10)}</Text>\n <Text\n color={\n model.speed === \"fastest\"\n ? \"green\"\n : model.speed === \"fast\"\n ? \"yellow\"\n : \"gray\"\n }\n >\n {(model.speed || \"-\").padEnd(12)}\n </Text>\n <Text color=\"cyan\">\n {(model.benchStats ? `${model.benchStats.avgTokPerSec} tok/s` : \"-\").padEnd(\n 14,\n )}\n </Text>\n <Text dimColor>{model.lastUsed || \"-\"}</Text>\n {isCurrent && <Text color=\"cyan\"> [current]</Text>}\n {isPendingDelete && <Text color=\"red\"> DELETE? (y/n)</Text>}\n </Box>\n );\n })\n )}\n </Box>\n {deleteConfirm && (\n <Text color=\"red\">⚠ This will permanently delete the model from disk</Text>\n )}\n </>\n )}\n\n {tab === \"huggingface\" && (\n <>\n <Box alignItems=\"center\" flexDirection=\"row\" marginBottom={1}>\n <Text color=\"gray\">Search:</Text>\n <Box\n borderColor={hfSearchFocused ? \"cyan\" : \"gray\"}\n borderStyle=\"single\"\n marginLeft={1}\n paddingX={1}\n width={30}\n >\n {hfSearchFocused ? (\n <TextInput\n focus={true}\n onChange={handleSearch}\n onSubmit={handleSearchSubmit}\n placeholder=\"qwen, llama, whisper...\"\n value={hfSearchInput}\n />\n ) : (\n <Text color=\"gray\">{hfSearchInput || \"'s' to search\"}</Text>\n )}\n </Box>\n <Box marginLeft={2}>\n <Text color=\"gray\">Sort: </Text>\n <Text color=\"yellow\">{getSortLabel()}</Text>\n <Text dimColor> (o)</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"gray\">Size: </Text>\n <Text color={sizeFilter !== \"all\" ? \"yellow\" : \"gray\"}>{getFilterLabel()}</Text>\n <Text dimColor> (f)</Text>\n </Box>\n </Box>\n\n {hfLoading && hfModels.length === 0 && (\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />{\" \"}\n </Text>\n <Text dimColor>Fetching ONNX models...</Text>\n </Box>\n )}\n\n {hfError && <Text color=\"red\">{hfError}</Text>}\n\n {filteredModels.length > 0 && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box>\n <Text dimColor>{\" \"}</Text>\n <Text bold dimColor>\n {\"Model\".padEnd(44)}\n </Text>\n <Text bold dimColor>\n {\"Params\".padStart(8)}\n </Text>\n <Text bold dimColor>\n {\"Size\".padStart(10)}\n </Text>\n <Text bold dimColor>\n {\"Context\".padStart(8)}\n </Text>\n <Text bold dimColor>\n {\"Updated\".padStart(10)}\n </Text>\n <Text bold dimColor>\n {\"Downloads\".padStart(10)}\n </Text>\n </Box>\n {filteredModels.map((model, i) => {\n const isCached = isModelCached(model.id);\n return (\n <Box key={`${model.id}-${i}`}>\n <Text color={isCached ? \"green\" : \"gray\"}>{isCached ? \"●\" : \"○\"} </Text>\n <Text color={i === hfSelected ? \"cyan\" : \"white\"}>\n {i === hfSelected ? \"> \" : \" \"}\n {model.id.length > 42 ? `${model.id.slice(0, 39)}...` : model.id.padEnd(42)}\n </Text>\n <Text color=\"yellow\">{formatParams(model.params, model.id).padStart(8)}</Text>\n <Text color=\"magenta\">\n {(model.sizeBytes ? formatBytes(model.sizeBytes) : \"-\").padStart(10)}\n </Text>\n <Text color=\"cyan\">{formatContext(model.contextLength).padStart(8)}</Text>\n <Text dimColor>{formatDate(model.createdAt).padStart(10)}</Text>\n <Text color=\"gray\">{formatDownloads(model.downloads).padStart(10)}</Text>\n {model.id === currentModel && <Text color=\"cyan\"> [cur]</Text>}\n </Box>\n );\n })}\n {hfLoading && (\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />{\" \"}\n </Text>\n <Text dimColor>Loading more...</Text>\n </Box>\n )}\n {!hfLoading && hfHasMore && (\n <Text dimColor> 'n' for more ({filteredModels.length} shown)</Text>\n )}\n {!hfHasMore && <Text dimColor> End of results ({filteredModels.length} total)</Text>}\n </Box>\n )}\n\n {!hfLoading && filteredModels.length === 0 && hfModels.length > 0 && (\n <Text dimColor>No models match size filter. Press 'f' to change.</Text>\n )}\n\n {!hfLoading && hfModels.length === 0 && !hfError && (\n <Text dimColor>Loading ONNX models...</Text>\n )}\n </>\n )}\n\n {/* Voice Tab */}\n {tab === \"voice\" && (\n <Box flexDirection=\"row\">\n {/* TTS Section */}\n <Box\n borderColor={voiceSection === \"tts\" ? \"magenta\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={2}\n minWidth={45}\n paddingX={1}\n >\n <Text bold color={voiceSection === \"tts\" ? \"magenta\" : \"gray\"}>\n Text-to-Speech (TTS)\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>Voices: Kokoro or Supertonic</Text>\n </Box>\n {TTS_MODELS.map((model, i) => {\n const isCurrent = model.id === currentTTSModel;\n const isSelected = voiceSection === \"tts\" && i === ttsSelected;\n return (\n <Box key={model.id}>\n <Text color={isCurrent ? \"green\" : \"gray\"}>{isCurrent ? \"●\" : \"○\"} </Text>\n <Text bold={isSelected} color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n {model.name}\n </Text>\n <Text dimColor> ({model.size})</Text>\n {isCurrent && <Text color=\"cyan\"> [current]</Text>}\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text dimColor>\n Voices: <Text color=\"yellow\">{TTS_MODELS[ttsSelected]?.voices}</Text> | Quality:{\" \"}\n <Text color=\"green\">{TTS_MODELS[ttsSelected]?.quality}</Text>\n </Text>\n </Box>\n </Box>\n\n {/* STT Section */}\n <Box\n borderColor={voiceSection === \"stt\" ? \"magenta\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n minWidth={45}\n paddingX={1}\n >\n <Text bold color={voiceSection === \"stt\" ? \"magenta\" : \"gray\"}>\n Speech-to-Text (STT)\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>Whisper models for transcription</Text>\n </Box>\n {STT_MODELS.map((model, i) => {\n const isCurrent = model.id === currentSTTModel;\n const isSelected = voiceSection === \"stt\" && i === sttSelected;\n return (\n <Box key={model.id}>\n <Text color={isCurrent ? \"green\" : \"gray\"}>{isCurrent ? \"●\" : \"○\"} </Text>\n <Text bold={isSelected} color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n {model.name}\n </Text>\n <Text dimColor> ({model.size})</Text>\n {isCurrent && <Text color=\"cyan\"> [current]</Text>}\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text dimColor>\n Quality: <Text color=\"green\">{STT_MODELS[sttSelected]?.quality}</Text> | Speed:{\" \"}\n <Text color=\"yellow\">{STT_MODELS[sttSelected]?.speed}</Text>\n </Text>\n </Box>\n </Box>\n </Box>\n )}\n\n <Box marginTop={1}>{renderHelpText()}</Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport http from \"node:http\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\ntype ServeViewProps = {\n gerbil: Gerbil;\n model: string;\n};\n\nexport function ServeView({ gerbil, model }: ServeViewProps) {\n const [mode, setMode] = useState<\"http\" | \"mcp\">(\"http\");\n const [port, setPort] = useState(3000);\n const [serverStatus, setServerStatus] = useState<\"idle\" | \"starting\" | \"running\" | \"error\">(\n \"idle\",\n );\n const [logs, setLogs] = useState<string[]>([]);\n const [copied, setCopied] = useState(false);\n const [requestCount, setRequestCount] = useState(0);\n const serverRef = useRef<http.Server | null>(null);\n\n // Cleanup on unmount\n useEffect(\n () => () => {\n if (serverRef.current) {\n serverRef.current.close();\n serverRef.current = null;\n }\n },\n [],\n );\n\n useInput((input, key) => {\n if (key.tab) {\n setMode((m) => (m === \"http\" ? \"mcp\" : \"http\"));\n }\n if (key.return && serverStatus === \"idle\" && mode === \"http\") {\n startServer();\n }\n if ((input === \"s\" || input === \"S\") && serverStatus === \"running\") {\n stopServer();\n }\n if (key.upArrow && serverStatus === \"idle\" && mode === \"http\") {\n setPort((p) => Math.min(65_535, p + 1));\n }\n if (key.downArrow && serverStatus === \"idle\" && mode === \"http\") {\n setPort((p) => Math.max(1024, p - 1));\n }\n if ((input === \"c\" || input === \"C\") && mode === \"mcp\") {\n copyMcpConfig();\n }\n });\n\n const addLog = (msg: string) => {\n setLogs((prev) => [...prev.slice(-8), `${new Date().toLocaleTimeString()} ${msg}`]);\n };\n\n const startServer = () => {\n setServerStatus(\"starting\");\n setLogs([]);\n setRequestCount(0);\n\n const server = http.createServer(async (req, res) => {\n // CORS headers\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = req.url || \"/\";\n\n if (req.method === \"POST\" && url === \"/generate\") {\n let body = \"\";\n req.on(\"data\", (chunk) => (body += chunk));\n req.on(\"end\", async () => {\n try {\n const { prompt, ...opts } = JSON.parse(body);\n addLog(`POST /generate: \"${prompt.slice(0, 30)}...\"`);\n setRequestCount((c) => c + 1);\n\n const result = await gerbil.generate(prompt, opts);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (e) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: String(e) }));\n }\n });\n } else if (req.method === \"POST\" && url === \"/json\") {\n let body = \"\";\n req.on(\"data\", (chunk) => (body += chunk));\n req.on(\"end\", async () => {\n try {\n const { prompt, schema, ...opts } = JSON.parse(body);\n addLog(`POST /json: \"${prompt.slice(0, 30)}...\"`);\n setRequestCount((c) => c + 1);\n\n // Simple JSON generation without Zod validation in HTTP API\n const result = await gerbil.generate(`${prompt}\\n\\nRespond with valid JSON only.`, {\n ...opts,\n temperature: opts.temperature ?? 0.3,\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(result.text);\n } catch (e) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: String(e) }));\n }\n });\n } else if (req.method === \"GET\" && url === \"/info\") {\n addLog(\"GET /info\");\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(gerbil.getInfo()));\n } else if (req.method === \"GET\" && url === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", model }));\n } else {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"Not found\",\n endpoints: [\"POST /generate\", \"POST /json\", \"GET /info\", \"GET /health\"],\n }),\n );\n }\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n addLog(`Port ${port} in use, trying ${port + 1}...`);\n setPort((p) => p + 1);\n setTimeout(() => startServer(), 100);\n } else {\n addLog(`Error: ${err.message}`);\n setServerStatus(\"error\");\n }\n });\n\n server.listen(port, () => {\n addLog(`Server listening on http://localhost:${port}`);\n setServerStatus(\"running\");\n });\n\n serverRef.current = server;\n };\n\n const stopServer = () => {\n if (serverRef.current) {\n serverRef.current.close(() => {\n addLog(\"Server stopped\");\n });\n serverRef.current = null;\n setServerStatus(\"idle\");\n }\n };\n\n const copyMcpConfig = () => {\n const config = JSON.stringify(\n {\n mcpServers: {\n gerbil: {\n command: \"npx\",\n args: [\"-y\", \"@tryhamster/gerbil\", \"serve\", \"--mcp\"],\n },\n },\n },\n null,\n 2,\n );\n\n const platform = process.platform;\n const cmd =\n platform === \"darwin\"\n ? \"pbcopy\"\n : platform === \"win32\"\n ? \"clip\"\n : \"xclip -selection clipboard\";\n const proc = exec(cmd);\n proc.stdin?.write(config);\n proc.stdin?.end();\n\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold>Server Mode</Text>\n <Text dimColor>Expose Gerbil as an API server or MCP server</Text>\n\n <Box marginY={1}>\n <Text bold={mode === \"http\"} color={mode === \"http\" ? \"cyan\" : \"gray\"}>\n [HTTP API]\n </Text>\n <Text color=\"gray\"> </Text>\n <Text bold={mode === \"mcp\"} color={mode === \"mcp\" ? \"cyan\" : \"gray\"}>\n [MCP Server]\n </Text>\n <Text dimColor> (Tab to switch)</Text>\n </Box>\n\n <Box borderColor=\"gray\" borderStyle=\"single\" flexDirection=\"column\" paddingX={1}>\n <Text bold color=\"cyan\">\n {mode === \"http\" ? \"HTTP REST API\" : \"Model Context Protocol\"}\n </Text>\n <Text>\n Model: <Text color=\"cyan\">{model}</Text>\n </Text>\n\n {mode === \"http\" ? (\n <>\n <Text>\n Port: <Text color=\"yellow\">{port}</Text> <Text dimColor>(Up/Down to change)</Text>\n </Text>\n <Text dimColor>Endpoints: /generate, /json, /info, /health</Text>\n </>\n ) : (\n <>\n <Text dimColor>For Claude Desktop and Cursor AI integration</Text>\n <Text dimColor>Tools: generate, stream, json, embed, plus all skills</Text>\n </>\n )}\n </Box>\n\n {mode === \"http\" && serverStatus !== \"idle\" && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box>\n {serverStatus === \"starting\" && (\n <Text color=\"yellow\">\n <Spinner type=\"dots\" /> Starting server...\n </Text>\n )}\n {serverStatus === \"running\" && (\n <Text color=\"green\">\n ● Running on http://localhost:{port} ({requestCount} requests)\n </Text>\n )}\n {serverStatus === \"error\" && <Text color=\"red\">✗ Server error</Text>}\n </Box>\n\n {logs.length > 0 && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginTop={1}\n paddingX={1}\n >\n {logs.slice(-5).map((log, i) => (\n <Text dimColor key={i}>\n {log}\n </Text>\n ))}\n </Box>\n )}\n </Box>\n )}\n\n {mode === \"http\" && serverStatus === \"running\" && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Test with:</Text>\n <Text color=\"green\">curl -X POST http://localhost:{port}/generate \\</Text>\n <Text color=\"green\"> -H \"Content-Type: application/json\" \\</Text>\n <Text color=\"green\"> -d '{`{\"prompt\": \"Hello!\"}`}'</Text>\n </Box>\n )}\n\n {mode === \"mcp\" && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box marginBottom={1}>\n <Text color=\"yellow\">⚠ </Text>\n <Text>MCP servers use stdio - run from terminal, not REPL</Text>\n </Box>\n\n <Text dimColor>Terminal command:</Text>\n <Text color=\"cyan\">npx @tryhamster/gerbil serve --mcp</Text>\n\n <Box marginTop={1}>\n <Text dimColor>Add to MCP config (e.g. .cursor/mcp.json):</Text>\n </Box>\n <Text color=\"green\">{`{\n \"mcpServers\": {\n \"gerbil\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@tryhamster/gerbil\", \"serve\", \"--mcp\"]\n }\n }\n}`}</Text>\n\n {copied && (\n <Box marginTop={1}>\n <Text color=\"green\">✓ Config copied to clipboard!</Text>\n </Box>\n )}\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n {mode === \"mcp\" ? (\n <>\n <Text color=\"yellow\">c</Text> copy config | <Text color=\"yellow\">Tab</Text> HTTP mode\n | <Text color=\"gray\">Esc</Text> back\n </>\n ) : serverStatus === \"idle\" ? (\n <>\n <Text color=\"yellow\">Enter</Text> start | <Text color=\"yellow\">Tab</Text> MCP mode |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </>\n ) : (\n <>\n <Text color=\"yellow\">s</Text> stop | <Text color=\"gray\">Esc</Text> back\n </>\n )}\n </Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useEffect, useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\nimport { getSkillInfo, listSkills, loadProjectSkills, useSkill } from \"../../../skills/index.js\";\nimport { type CachedModelInfo, getCacheInfo, processImagePath } from \"../utils.js\";\n\ntype SkillsViewProps = {\n gerbil: Gerbil;\n onCreateNew?: () => void;\n onSwitchModel?: (modelId: string) => void;\n};\n\ntype Phase = \"select\" | \"input\" | \"running\" | \"result\" | \"model-select\";\n\n// Vision skills that require image input\nconst VISION_SKILLS = new Set([\n \"describe-image\",\n \"analyze-screenshot\",\n \"extract-from-image\",\n \"compare-images\",\n \"caption-image\",\n]);\n\n// Detailed skill information with examples\nconst SKILL_DETAILS: Record<\n string,\n {\n emoji: string;\n what: string;\n example: string;\n cliExample?: string;\n inputHint: string;\n isVision?: boolean;\n }\n> = {\n commit: {\n emoji: \"->\",\n what: \"Generate git commit messages from staged changes\",\n example: \"feat(auth): add OAuth2 login flow\",\n cliExample: \"gerbil commit --type conventional\",\n inputHint: \"Leave empty to use staged changes, or paste a diff\",\n },\n summarize: {\n emoji: \"=\",\n what: \"Condense documents, articles, or code into key points\",\n example: \"• Key insight 1\\\\n• Key insight 2\\\\n• Conclusion\",\n cliExample: \"gerbil summarize README.md --format bullets\",\n inputHint: \"Paste text, article, or document to summarize\",\n },\n explain: {\n emoji: \"?\",\n what: \"Break down code or concepts for any skill level\",\n example: \"This function uses recursion to...\",\n cliExample: \"gerbil explain src/utils.ts --level beginner\",\n inputHint: \"Paste code or describe a concept to explain\",\n },\n review: {\n emoji: \"!\",\n what: \"Find bugs, suggest improvements, check security\",\n example: \"⚠️ Line 5: potential null reference\\\\n✓ Good use of...\",\n cliExample: \"gerbil review src/api.ts --focus security,performance\",\n inputHint: \"Paste code to get a detailed review\",\n },\n extract: {\n emoji: \"<>\",\n what: \"Pull structured data from messy text (names, dates, etc)\",\n example: '{ \"name\": \"John\", \"email\": \"john@...\" }',\n cliExample: 'echo \"John Doe, john@email.com\" | gerbil extract',\n inputHint: \"Paste unstructured text to extract data from\",\n },\n title: {\n emoji: \"#\",\n what: \"Generate catchy titles, headlines, or subject lines\",\n example: \"10 Tips for Better Code Reviews\",\n cliExample: \"cat article.md | gerbil title --style seo\",\n inputHint: \"Paste content to generate a title for\",\n },\n test: {\n emoji: \"T\",\n what: \"Auto-generate unit tests for functions and classes\",\n example: \"describe('add', () => { it('adds numbers'... })\",\n cliExample: \"gerbil test src/utils.ts --framework vitest\",\n inputHint: \"Paste a function or class to generate tests for\",\n },\n translate: {\n emoji: \"~\",\n what: \"Translate text to any language while preserving tone\",\n example: \"Hola mundo (Spanish)\",\n cliExample: 'gerbil translate \"Hello world\" --to spanish',\n inputHint: 'Type: \"your text\" to [language]',\n },\n // Vision skills\n \"describe-image\": {\n emoji: \"👁\",\n what: \"Describe an image using vision AI - identify objects, text, and scenes\",\n example: \"A sunset over mountains with orange and purple hues...\",\n cliExample: \"gerbil describe-image ~/photo.png --focus details\",\n inputHint: \"Enter image URL or local path (supports ~)\",\n isVision: true,\n },\n \"analyze-screenshot\": {\n emoji: \"📱\",\n what: \"Analyze a UI screenshot for design, accessibility, or QA issues\",\n example: \"UI Review: Good visual hierarchy, but contrast ratio on buttons...\",\n cliExample: \"gerbil analyze-screenshot ~/app.png --type accessibility\",\n inputHint: \"Enter screenshot URL or local path\",\n isVision: true,\n },\n \"extract-from-image\": {\n emoji: \"📋\",\n what: \"Extract text, code, tables, or structured data from images\",\n example: '{ \"title\": \"Invoice #123\", \"total\": \"$50.00\" }',\n cliExample: \"gerbil extract-from-image ~/receipt.jpg --type text\",\n inputHint: \"Enter image URL or local path\",\n isVision: true,\n },\n \"compare-images\": {\n emoji: \"🔄\",\n what: \"Compare two images and describe differences\",\n example: \"Differences: 1. Button color changed from blue to green...\",\n cliExample: \"gerbil compare-images --image1 old.png --image2 new.png\",\n inputHint: \"Enter first image, then second (comma-separated)\",\n isVision: true,\n },\n \"caption-image\": {\n emoji: \"img\",\n what: \"Generate alt text, captions, or titles for images\",\n example: \"A developer working late at night, illuminated by monitor glow\",\n cliExample: \"gerbil caption-image ~/photo.jpg --style alt-text\",\n inputHint: \"Enter image URL or local path\",\n isVision: true,\n },\n // TTS skills\n speak: {\n emoji: \"spk\",\n what: \"Convert text to speech using on-device TTS (Kokoro-82M)\",\n example: \"[Audio plays through speakers]\",\n cliExample: \"gerbil speak 'Hello world' --voice af_bella -o hello.wav\",\n inputHint: \"Enter text to speak aloud\",\n },\n announce: {\n emoji: \"ann\",\n what: \"Generate and speak an AI-crafted announcement with style\",\n example: \"[Casual] 'Hey, great news - the build passed!'\",\n cliExample: \"gerbil announce 'Build complete' --style excited\",\n inputHint: \"Enter message to announce (AI will rephrase it)\",\n },\n \"read-aloud\": {\n emoji: \"rd\",\n what: \"Read text or file content aloud using TTS\",\n example: \"[Audio plays paragraph by paragraph]\",\n cliExample: \"gerbil read-aloud ./README.md --voice bf_emma\",\n inputHint: \"Enter file path or text to read aloud\",\n },\n // STT skills\n transcribe: {\n emoji: \"stt\",\n what: \"Transcribe audio to text using on-device STT (Whisper)\",\n example: \"And so, my fellow Americans, ask not what your country can do for you...\",\n cliExample: \"gerbil transcribe audio.wav --timestamps\",\n inputHint: \"Enter audio file path (WAV, MP3, etc.)\",\n },\n};\n\nexport function SkillsView({ gerbil, onCreateNew, onSwitchModel }: SkillsViewProps) {\n const [phase, setPhase] = useState<Phase>(\"select\");\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [selectedSkill, setSelectedSkill] = useState<string | null>(null);\n const [inputValue, setInputValue] = useState(\"\");\n const [result, setResult] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [skillsLoaded, setSkillsLoaded] = useState(false);\n const [cachedModels, setCachedModels] = useState<CachedModelInfo[]>([]);\n const [modelSelectIndex, setModelSelectIndex] = useState(0);\n const [currentModel, setCurrentModel] = useState<string>(\n gerbil.getModelInfo()?.id || \"qwen3-0.6b\",\n );\n\n // Image picker state (for vision skills)\n const [attachedImage, setAttachedImage] = useState<string | null>(null);\n const [attachedImageName, setAttachedImageName] = useState<string | null>(null);\n const [imageError, setImageError] = useState<string | null>(null);\n\n // Load project skills from .gerbil/skills/ on mount\n useEffect(() => {\n if (!skillsLoaded) {\n loadProjectSkills()\n .then(() => {\n setSkillsLoaded(true);\n })\n .catch(() => {\n setSkillsLoaded(true);\n });\n }\n // Load cached models\n const cacheInfo = getCacheInfo();\n setCachedModels(cacheInfo.models);\n }, [skillsLoaded]);\n\n const skills = listSkills();\n\n // Create skill list with \"Create new\" at the end\n const allItems = [\n ...skills.map((name) => {\n const info = getSkillInfo(name);\n const details = SKILL_DETAILS[name];\n const isVision = details?.isVision || VISION_SKILLS.has(name);\n return {\n name,\n description: info?.description || \"\",\n builtin: info?.builtin,\n emoji: details?.emoji || (isVision ? \"👁\" : \"*\"),\n what: details?.what || info?.description || \"\",\n example: details?.example || \"\",\n cliExample: details?.cliExample || \"\",\n inputHint:\n details?.inputHint || (isVision ? \"Enter image URL or local path\" : \"Enter input...\"),\n isVision,\n };\n }),\n {\n name: \"__create__\",\n description: \"Build a custom AI skill with the wizard\",\n builtin: false,\n emoji: \"+\",\n what: \"Create your own skill using AI-assisted code generation\",\n example: \"sentiment-analyzer, email-writer, data-formatter...\",\n cliExample: \"\",\n inputHint: \"\",\n isVision: false,\n },\n ];\n\n useInput((input, key) => {\n if (phase === \"model-select\") {\n if (key.upArrow) {\n setModelSelectIndex((i) => Math.max(0, i - 1));\n }\n if (key.downArrow) {\n setModelSelectIndex((i) => Math.min(cachedModels.length - 1, i + 1));\n }\n if (key.return && cachedModels.length > 0) {\n const model = cachedModels[modelSelectIndex];\n const modelId = model.name.includes(\"/\") ? model.name : model.name.replace(\"--\", \"/\");\n setCurrentModel(modelId);\n onSwitchModel?.(modelId);\n setPhase(\"select\");\n }\n if (key.escape) {\n setPhase(\"select\");\n }\n return;\n }\n\n if (phase === \"select\") {\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1));\n }\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(allItems.length - 1, i + 1));\n }\n if (key.return) {\n const item = allItems[selectedIndex];\n if (item.name === \"__create__\") {\n onCreateNew?.();\n } else {\n setSelectedSkill(item.name);\n setPhase(\"input\");\n }\n }\n // C to create new skill\n if (input === \"c\" || input === \"C\") {\n onCreateNew?.();\n }\n // M to select model\n if ((input === \"m\" || input === \"M\") && cachedModels.length > 0) {\n setPhase(\"model-select\");\n }\n }\n if (key.escape && (phase === \"result\" || phase === \"input\")) {\n handleReset();\n }\n });\n\n const handleRunSkill = async (input: string) => {\n if (!selectedSkill) {\n return;\n }\n\n // Check if this is a vision skill\n const isVision = VISION_SKILLS.has(selectedSkill) || SKILL_DETAILS[selectedSkill]?.isVision;\n\n setPhase(\"running\");\n setError(null);\n\n try {\n const skill = useSkill(selectedSkill);\n\n // Parse input as JSON or use as content/text\n let parsedInput: Record<string, unknown>;\n try {\n parsedInput = JSON.parse(input);\n } catch {\n // Smart input parsing based on skill type\n if (isVision) {\n // For vision skills, process the image path\n let imageSource = attachedImage;\n\n // If no attached image but input provided, try to process it as path\n if (!imageSource && input.trim()) {\n const result = processImagePath(input);\n if (result.success && result.imageSource) {\n imageSource = result.imageSource;\n } else if (result.message) {\n throw new Error(result.message);\n }\n }\n\n if (!imageSource) {\n throw new Error(\"No image provided. Enter an image URL or local path.\");\n }\n\n // Build input based on skill\n if (selectedSkill === \"compare-images\") {\n // compare-images needs two images - for now just use one\n parsedInput = { image1: imageSource, image2: imageSource };\n } else {\n parsedInput = { image: imageSource };\n }\n } else if (selectedSkill === \"commit\") {\n parsedInput = { diff: input, type: \"conventional\" };\n } else if (selectedSkill === \"translate\") {\n const toMatch = input.match(/(.+?)\\s+to\\s+(\\w+)$/i);\n if (toMatch) {\n parsedInput = { text: toMatch[1].trim(), to: toMatch[2].trim() };\n } else {\n parsedInput = { text: input, to: \"spanish\" };\n }\n } else if (selectedSkill === \"summarize\") {\n parsedInput = { content: input, format: \"bullet\" };\n } else if (selectedSkill === \"explain\") {\n parsedInput = { content: input, level: \"beginner\" };\n } else if (selectedSkill === \"review\") {\n parsedInput = { code: input };\n } else if (selectedSkill === \"test\") {\n parsedInput = { code: input, framework: \"jest\" };\n } else if (selectedSkill === \"speak\") {\n // TTS: speak skill expects 'text'\n parsedInput = { text: input };\n } else if (selectedSkill === \"announce\") {\n // TTS: announce skill expects 'message'\n parsedInput = { message: input };\n } else if (selectedSkill === \"read-aloud\") {\n // TTS: read-aloud skill expects 'content' (file path or text)\n parsedInput = { content: input };\n } else if (selectedSkill === \"transcribe\") {\n // STT: transcribe skill expects 'audio' (file path)\n parsedInput = { audio: input };\n } else {\n parsedInput = { content: input, text: input, code: input, message: input };\n }\n }\n\n const output = await skill.run(parsedInput, gerbil);\n\n // Clean up think tags from output\n let cleanOutput = typeof output === \"string\" ? output : JSON.stringify(output, null, 2);\n cleanOutput = cleanOutput.replace(/<think>[\\s\\S]*?<\\/think>/g, \"\").trim();\n\n setResult(cleanOutput);\n setPhase(\"result\");\n } catch (err) {\n setError(String(err));\n setPhase(\"result\");\n } finally {\n // Clear attached image after running\n setAttachedImage(null);\n setAttachedImageName(null);\n }\n };\n\n const handleReset = () => {\n setPhase(\"select\");\n setSelectedSkill(null);\n setInputValue(\"\");\n setResult(null);\n setError(null);\n setAttachedImage(null);\n setAttachedImageName(null);\n setImageError(null);\n };\n\n // Handle image path input for vision skills\n const handleImageInput = (imagePath: string) => {\n setImageError(null);\n const result = processImagePath(imagePath);\n if (result.success && result.imageSource) {\n setAttachedImage(result.imageSource);\n setAttachedImageName(result.filename || \"image\");\n setInputValue(imagePath); // Keep path in input for display\n } else if (result.message) {\n setImageError(result.message);\n }\n };\n\n // Selection phase\n if (phase === \"select\") {\n const currentItem = allItems[selectedIndex];\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n {/* Header explanation */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold color=\"cyan\">\n What are Skills?\n </Text>\n <Text dimColor>\n Skills are pre-built AI tasks that solve specific problems. Each skill has optimized\n prompts and structured outputs.\n </Text>\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">CLI Usage:</Text> gerbil {\"<skill>\"} [input] [--options]\n </Text>\n </Box>\n <Text dimColor>\n Examples: <Text color=\"gray\">gerbil commit</Text> ·{\" \"}\n <Text color=\"gray\">gerbil summarize README.md</Text> ·{\" \"}\n <Text color=\"gray\">gerbil explain src/utils.ts</Text>\n </Text>\n </Box>\n\n {/* Skill list */}\n <Box flexDirection=\"row\">\n {/* Left: Skill list */}\n <Box flexDirection=\"column\" marginRight={2} minWidth={28}>\n <Box marginBottom={1}>\n <Text bold>Available Skills:</Text>\n </Box>\n {allItems.map((item, i) => {\n const isSelected = i === selectedIndex;\n const isCreate = item.name === \"__create__\";\n return (\n <Box key={item.name}>\n <Text\n bold={isSelected}\n color={\n isSelected ? (isCreate ? \"green\" : item.isVision ? \"yellow\" : \"cyan\") : \"gray\"\n }\n >\n {isSelected ? \"> \" : \" \"}[{item.emoji}]{\" \"}\n {isCreate ? \"Create new...\" : item.name}\n </Text>\n {item.isVision && !isSelected && <Text dimColor> 📷</Text>}\n </Box>\n );\n })}\n </Box>\n\n {/* Right: Selected skill details */}\n <Box\n borderColor={\n currentItem.name === \"__create__\" ? \"green\" : currentItem.isVision ? \"yellow\" : \"cyan\"\n }\n borderStyle=\"single\"\n flexDirection=\"column\"\n flexGrow={1}\n paddingX={2}\n paddingY={1}\n >\n <Box>\n <Text\n bold\n color={\n currentItem.name === \"__create__\"\n ? \"green\"\n : currentItem.isVision\n ? \"yellow\"\n : \"cyan\"\n }\n >\n {currentItem.name === \"__create__\" ? \"Create New Skill\" : currentItem.name}\n </Text>\n {currentItem.isVision && <Text color=\"yellow\"> 📷 vision</Text>}\n </Box>\n\n {currentItem.builtin && <Text dimColor>[built-in]</Text>}\n\n <Box marginTop={1}>\n <Text wrap=\"wrap\">{currentItem.what}</Text>\n </Box>\n\n {currentItem.example && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Output example:</Text>\n <Text color=\"green\">\n {currentItem.example.slice(0, 80)}\n {currentItem.example.length > 80 ? \"...\" : \"\"}\n </Text>\n </Box>\n )}\n\n {currentItem.cliExample && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>CLI usage:</Text>\n <Text color=\"yellow\">{currentItem.cliExample}</Text>\n </Box>\n )}\n\n {currentItem.isVision && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Input:</Text>\n <Text color=\"gray\">Image URL or local path (~/path/to/image.png)</Text>\n </Box>\n )}\n\n {currentItem.name !== \"__create__\" && (\n <Box marginTop={1}>\n <Text dimColor>\n Press <Text color=\"cyan\">Enter</Text> to run this skill\n </Text>\n </Box>\n )}\n </Box>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text dimColor>Model: </Text>\n <Text color=\"cyan\">{currentModel}</Text>\n {cachedModels.length > 0 && <Text dimColor> (press </Text>}\n {cachedModels.length > 0 && <Text color=\"yellow\">m</Text>}\n {cachedModels.length > 0 && <Text dimColor> to change)</Text>}\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Up/Down</Text> navigate | <Text color=\"yellow\">Enter</Text>{\" \"}\n select | <Text color=\"yellow\">m</Text> model | <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n </Box>\n );\n }\n\n // Model selection phase\n if (phase === \"model-select\") {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n Select Model for Skills\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>Choose a cached model to run skills with</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginY={1}>\n {cachedModels.length === 0 ? (\n <Text color=\"gray\">No cached models found. Download models from Model view.</Text>\n ) : (\n cachedModels.map((model, i) => {\n const isSelected = i === modelSelectIndex;\n const isCurrent =\n model.name === currentModel || model.name.replace(\"--\", \"/\") === currentModel;\n return (\n <Box key={model.name}>\n <Text\n bold={isSelected}\n color={isSelected ? \"cyan\" : isCurrent ? \"green\" : \"gray\"}\n >\n {isSelected ? \"> \" : \" \"}\n {model.name.length > 50 ? `${model.name.slice(0, 47)}...` : model.name}\n </Text>\n <Text color=\"yellow\"> {model.modelSize}</Text>\n {isCurrent && <Text color=\"green\"> [current]</Text>}\n </Box>\n );\n })\n )}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Up/Down</Text> navigate | <Text color=\"yellow\">Enter</Text> select\n | <Text color=\"gray\">Esc</Text> cancel\n </Text>\n </Box>\n </Box>\n );\n }\n\n // Input phase\n if (phase === \"input\") {\n const info = getSkillInfo(selectedSkill!);\n const details = SKILL_DETAILS[selectedSkill!];\n const isVision = VISION_SKILLS.has(selectedSkill!) || details?.isVision;\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text bold color={isVision ? \"yellow\" : \"cyan\"}>\n [{details?.emoji || \"*\"}] {selectedSkill}\n </Text>\n {isVision && (\n <Text color=\"yellow\" dimColor>\n {\" \"}\n (vision)\n </Text>\n )}\n </Box>\n <Text dimColor>{details?.what || info?.description}</Text>\n </Box>\n\n {details?.example && (\n <Box marginBottom={1}>\n <Text dimColor>Example output: </Text>\n <Text color=\"green\">\n {details.example.slice(0, 60)}\n {details.example.length > 60 ? \"...\" : \"\"}\n </Text>\n </Box>\n )}\n\n {/* Vision skill - show image-specific UI */}\n {isVision && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color=\"yellow\">📷 </Text>\n <Text color=\"white\">{details?.inputHint || \"Enter image URL or local path:\"}</Text>\n </Box>\n <Text dimColor>Supports: https://..., ~/path/to/image.png, ./local.jpg</Text>\n </Box>\n )}\n\n {/* Non-vision skill - show regular hint */}\n {!isVision && (\n <Box marginBottom={1}>\n <Text color=\"white\">{details?.inputHint || \"Enter input:\"}</Text>\n </Box>\n )}\n\n {/* Attached image indicator */}\n {attachedImage && attachedImageName && (\n <Box marginBottom={1}>\n <Text color=\"green\">✓ Image ready: {attachedImageName}</Text>\n </Box>\n )}\n\n {/* Image error */}\n {imageError && (\n <Box marginBottom={1}>\n <Text color=\"red\">{imageError}</Text>\n </Box>\n )}\n\n <Box borderColor={isVision ? \"yellow\" : \"cyan\"} borderStyle=\"single\" paddingX={1}>\n <Text color={isVision ? \"yellow\" : \"cyan\"}>> </Text>\n <TextInput\n onChange={(val) => {\n setInputValue(val);\n // For vision skills, validate image path on change\n if (isVision && val.trim()) {\n handleImageInput(val);\n }\n }}\n onSubmit={handleRunSkill}\n placeholder={isVision ? \"https://... or ~/path/to/image.png\" : \"Type here...\"}\n value={inputValue}\n />\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Enter</Text> run | <Text color=\"gray\">Esc</Text> cancel\n {isVision && attachedImage && <Text color=\"green\"> | ✓ image ready</Text>}\n </Text>\n </Box>\n </Box>\n );\n }\n\n // Running phase\n if (phase === \"running\") {\n const details = SKILL_DETAILS[selectedSkill!];\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n [{details?.emoji || \"*\"}] {selectedSkill}\n </Text>\n </Box>\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />{\" \"}\n </Text>\n <Text>Processing...</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>This may take a few seconds</Text>\n </Box>\n </Box>\n );\n }\n\n // Result phase\n const _details = SKILL_DETAILS[selectedSkill!];\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color={error ? \"red\" : \"green\"}>\n {error ? \"[x] Error\" : \"[done]\"} {selectedSkill}\n </Text>\n </Box>\n\n <Box\n borderColor={error ? \"red\" : \"green\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n padding={1}\n >\n <Text color={error ? \"red\" : \"white\"} wrap=\"wrap\">\n {error || result}\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"gray\">Esc</Text> back to skills\n </Text>\n </Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useEffect, useState } from \"react\";\nimport { getTool, getToolDefinitions, loadProjectTools } from \"../../../core/tools.js\";\n\ntype ToolsViewProps = {\n onCreateTool: () => void;\n};\n\ntype ToolInfo = {\n name: string;\n description: string;\n source: \"builtin\" | \"project\" | \"error\";\n path?: string;\n error?: string;\n parameters?: object;\n};\n\ntype ExecuteState = \"idle\" | \"input\" | \"running\" | \"done\";\n\nexport function ToolsView({ onCreateTool }: ToolsViewProps) {\n const [tools, setTools] = useState<ToolInfo[]>([]);\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [expandedTool, setExpandedTool] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n\n // Execution state\n const [executeState, setExecuteState] = useState<ExecuteState>(\"idle\");\n const [executeInput, setExecuteInput] = useState(\"\");\n const [executeResult, setExecuteResult] = useState<string | null>(null);\n const [executeError, setExecuteError] = useState<string | null>(null);\n\n useEffect(() => {\n const loadAllTools = async () => {\n const allTools: ToolInfo[] = [];\n\n // First load project tools from .gerbil/tools/\n const projectResults = await loadProjectTools();\n\n // Add failed tools to list with error status\n for (const result of projectResults) {\n if (!result.loaded) {\n allTools.push({\n name: result.name,\n description: result.error || \"Failed to load\",\n source: \"error\",\n path: result.path,\n error: result.error,\n });\n }\n }\n\n // Then get all successfully registered tool definitions\n const defs = getToolDefinitions();\n const toolsDir = path.join(process.cwd(), \".gerbil\", \"tools\");\n\n for (const t of defs) {\n const isBuiltin = t.name.startsWith(\"gerbil_\");\n let toolPath: string | undefined;\n\n if (!isBuiltin) {\n // Check if tool file exists\n const tsPath = path.join(toolsDir, `${t.name}.tool.ts`);\n const jsPath = path.join(toolsDir, `${t.name}.tool.js`);\n if (fs.existsSync(tsPath)) {\n toolPath = tsPath;\n } else if (fs.existsSync(jsPath)) {\n toolPath = jsPath;\n }\n }\n\n allTools.push({\n name: t.name,\n description: t.description,\n source: isBuiltin ? \"builtin\" : \"project\",\n path: toolPath,\n parameters: t.parameters ? {} : undefined,\n });\n }\n\n setTools(allTools);\n setLoading(false);\n };\n\n loadAllTools();\n }, []);\n\n const selectedTool = selectedIndex > 0 ? tools[selectedIndex - 1] : null;\n\n const executeTool = async (toolName: string, inputStr: string) => {\n setExecuteState(\"running\");\n setExecuteError(null);\n setExecuteResult(null);\n\n try {\n const tool = getTool(toolName);\n if (!tool) {\n throw new Error(`Tool \"${toolName}\" not found`);\n }\n\n // Simple param handling:\n // - If JSON object, use as-is\n // - Otherwise, set all common param names to the input value\n let params: any = {};\n const trimmed = (inputStr || \"\").trim();\n\n if (trimmed) {\n if (trimmed.startsWith(\"{\")) {\n params = JSON.parse(trimmed);\n } else {\n // Set multiple common param names so the tool gets the value\n params = {\n query: trimmed,\n input: trimmed,\n text: trimmed,\n theme: trimmed,\n content: trimmed,\n value: trimmed,\n };\n }\n }\n\n const result = await tool(params);\n setExecuteResult(result);\n setExecuteState(\"done\");\n } catch (e) {\n setExecuteError(e instanceof Error ? e.message : String(e));\n setExecuteState(\"done\");\n }\n };\n\n const handleExecuteSubmit = (value: string) => {\n if (selectedTool && selectedTool.source !== \"error\") {\n executeTool(selectedTool.name, value);\n }\n };\n\n useInput((char, key) => {\n // Handle escape to exit execute mode\n if (key.escape && executeState !== \"idle\") {\n setExecuteState(\"idle\");\n setExecuteInput(\"\");\n setExecuteResult(null);\n setExecuteError(null);\n return;\n }\n\n // Don't process other keys during input/running\n if (executeState === \"input\" || executeState === \"running\") {\n return;\n }\n\n // Clear result on any key when done\n if (executeState === \"done\") {\n setExecuteState(\"idle\");\n setExecuteResult(null);\n setExecuteError(null);\n return;\n }\n\n if (key.upArrow && selectedIndex > 0) {\n setSelectedIndex(selectedIndex - 1);\n setExpandedTool(null);\n }\n if (key.downArrow && selectedIndex < tools.length) {\n setSelectedIndex(selectedIndex + 1);\n setExpandedTool(null);\n }\n if (key.return) {\n if (selectedIndex === 0) {\n onCreateTool();\n } else if (selectedTool) {\n // Toggle expanded view\n setExpandedTool(expandedTool === selectedTool.name ? null : selectedTool.name);\n }\n }\n if (char === \"c\" || char === \"C\") {\n onCreateTool();\n }\n // Open in editor (works for both project and error tools)\n if ((char === \"o\" || char === \"O\") && selectedTool?.path) {\n exec(`code \"${selectedTool.path}\"`);\n }\n // Execute tool\n if ((char === \"x\" || char === \"X\") && selectedTool && selectedTool.source !== \"error\") {\n setExecuteInput(\"\");\n setExecuteState(\"input\");\n }\n });\n\n if (loading) {\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Text dimColor>Loading tools...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Box marginBottom={1}>\n <Text bold color=\"magenta\">\n Tools\n </Text>\n <Text dimColor> — {tools.length} registered</Text>\n </Box>\n\n {/* Create new option */}\n <Box marginBottom={1}>\n <Text color={selectedIndex === 0 ? \"cyan\" : \"gray\"}>\n {selectedIndex === 0 ? \"> \" : \" \"}\n <Text bold color=\"green\">\n + Create new tool\n </Text>\n </Text>\n </Box>\n\n {/* Tool list */}\n <Box flexDirection=\"column\">\n {tools.length === 0 ? (\n <Text dimColor>No tools registered yet</Text>\n ) : (\n tools.map((tool, i) => {\n const isSelected = selectedIndex === i + 1;\n const isExpanded = expandedTool === tool.name;\n\n return (\n <Box flexDirection=\"column\" key={tool.name}>\n <Box>\n <Text color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n <Text\n bold\n color={isSelected ? \"cyan\" : tool.source === \"error\" ? \"red\" : \"white\"}\n >\n {tool.name}\n </Text>\n </Text>\n <Text\n color={tool.source === \"error\" ? \"red\" : undefined}\n dimColor={tool.source !== \"error\"}\n >\n {\" \"}\n — {tool.description.slice(0, 40)}\n {tool.description.length > 40 ? \"...\" : \"\"}\n </Text>\n <Text\n color={\n tool.source === \"builtin\"\n ? \"yellow\"\n : tool.source === \"error\"\n ? \"red\"\n : \"green\"\n }\n dimColor={tool.source !== \"error\"}\n >\n {\" \"}\n [{tool.source}]\n </Text>\n </Box>\n\n {/* Expanded details */}\n {isExpanded && (\n <Box\n borderColor={tool.source === \"error\" ? \"red\" : \"cyan\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginLeft={4}\n marginY={1}\n paddingX={1}\n >\n {tool.source === \"error\" ? (\n <>\n <Text>\n <Text color=\"red\">Error:</Text> {tool.error}\n </Text>\n <Text>\n <Text color=\"gray\">File:</Text> {tool.path}\n </Text>\n <Text dimColor>Press 'o' to open and fix in editor</Text>\n </>\n ) : (\n <>\n <Text>\n <Text color=\"gray\">Description:</Text> {tool.description}\n </Text>\n <Text>\n <Text color=\"gray\">Source:</Text>{\" \"}\n {tool.source === \"builtin\"\n ? \"Built-in (src/core/tools.ts)\"\n : tool.path || \".gerbil/tools/\"}\n </Text>\n {tool.source === \"project\" && tool.path && (\n <Text dimColor>Press 'o' to open in editor</Text>\n )}\n </>\n )}\n </Box>\n )}\n </Box>\n );\n })\n )}\n </Box>\n\n {/* Execute UI */}\n {executeState === \"input\" && selectedTool && (\n <Box\n borderColor=\"yellow\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text bold color=\"yellow\">\n Execute: {selectedTool.name}\n </Text>\n <Text dimColor>Enter params (JSON or single value, empty for none):</Text>\n <Box marginTop={1}>\n <Text color=\"yellow\">> </Text>\n <TextInput\n onChange={setExecuteInput}\n onSubmit={handleExecuteSubmit}\n placeholder='{\"param\": \"value\"} or just: value'\n value={executeInput}\n />\n </Box>\n </Box>\n )}\n\n {executeState === \"running\" && (\n <Box marginY={1} paddingX={1}>\n <Text color=\"yellow\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Running {selectedTool?.name}...</Text>\n </Box>\n )}\n\n {executeState === \"done\" && (\n <Box\n borderColor={executeError ? \"red\" : \"green\"}\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text bold color={executeError ? \"red\" : \"green\"}>\n {executeError ? \"✗ Error\" : \"✓ Result\"}\n </Text>\n <Text color={executeError ? \"red\" : \"white\"}>\n {executeError || executeResult || \"(no output)\"}\n </Text>\n <Text dimColor>Press any key to dismiss</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">x</Text> execute |<Text color=\"green\">c</Text> create |\n <Text color=\"cyan\">Enter</Text> expand |<Text color=\"gray\">↑↓</Text> navigate |\n <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport { Box, Text, useApp, useInput } from \"ink\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Gerbil } from \"../../core/gerbil.js\";\nimport { setToolContext } from \"../../core/tools.js\";\nimport {\n CURRENT_VERSION,\n checkForUpdate,\n installUpdate,\n type UpdateCheckResult,\n} from \"./auto-update.js\";\nimport { cleanResponse, getCacheInfo, hyperlink, type SessionStats } from \"./utils.js\";\nimport { type BenchmarkResult, type BenchmarkStats, BenchmarkView } from \"./views/BenchmarkView.js\";\nimport { ChatView } from \"./views/ChatView.js\";\nimport { CodeView } from \"./views/CodeView.js\";\nimport { CreateSkillView } from \"./views/CreateSkillView.js\";\nimport { CreateToolView } from \"./views/CreateToolView.js\";\nimport { FrameworksView } from \"./views/FrameworksView.js\";\nimport { InfoView } from \"./views/InfoView.js\";\nimport { LoadingView } from \"./views/LoadingView.js\";\nimport { ModelView } from \"./views/ModelView.js\";\nimport { ServeView } from \"./views/ServeView.js\";\nimport { SkillsView } from \"./views/SkillsView\";\nimport { ToolsView } from \"./views/ToolsView.js\";\n\nexport type View =\n | \"menu\"\n | \"chat\"\n | \"tools\"\n | \"skills\"\n | \"create-skill\"\n | \"create-tool\"\n | \"code\"\n | \"model\"\n | \"frameworks\"\n | \"benchmark\"\n | \"info\"\n | \"serve\";\n\nexport type AppState = {\n view: View;\n gerbil: Gerbil | null;\n model: string;\n loading: boolean;\n loadingMessage: string;\n thinkingMode: boolean;\n agentMode: boolean;\n voiceMode: boolean;\n downloadStatus: string;\n deviceMode: \"webgpu\" | \"cpu\";\n ttsModel: string;\n sttModel: string;\n};\n\nconst MENU_ITEMS = [\n {\n key: \"1\",\n view: \"chat\" as View,\n label: \"Chat\",\n desc: \"Talk with Gerbil\",\n color: \"cyan\",\n },\n {\n key: \"2\",\n view: \"skills\" as View,\n label: \"Skills\",\n desc: \"Run AI skills\",\n color: \"yellow\",\n },\n {\n key: \"3\",\n view: \"tools\" as View,\n label: \"Tools\",\n desc: \"Manage tools\",\n color: \"magenta\",\n },\n {\n key: \"4\",\n view: \"model\" as View,\n label: \"Model\",\n desc: \"Change model\",\n color: \"blue\",\n },\n {\n key: \"5\",\n view: \"frameworks\" as View,\n label: \"Integrate\",\n desc: \"Framework setup\",\n color: \"cyan\",\n },\n {\n key: \"6\",\n view: \"benchmark\" as View,\n label: \"Benchmark\",\n desc: \"Speed tests\",\n color: \"red\",\n },\n {\n key: \"7\",\n view: \"info\" as View,\n label: \"Info\",\n desc: \"System info\",\n color: \"white\",\n },\n {\n key: \"8\",\n view: \"serve\" as View,\n label: \"Serve\",\n desc: \"Start server\",\n color: \"green\",\n },\n];\n\nfunction openBrowser(url: string) {\n const cmd =\n process.platform === \"darwin\"\n ? `open \"${url}\"`\n : process.platform === \"win32\"\n ? `start \"${url}\"`\n : `xdg-open \"${url}\"`;\n exec(cmd);\n}\n\nconst WELCOME_PROMPT =\n \"You are Gerbil, a helpful AI assistant. Write a warm, friendly greeting in 1 short sentence (under 12 words). Be natural and welcoming. No markdown, no emojis, no XML tags, no asterisks, no quotes, no punctuation at the end.\";\n\n// Context-aware view prompts - can include dynamic state\ntype ViewContext = {\n model?: string;\n deviceMode?: \"cpu\" | \"webgpu\";\n thinkingMode?: boolean;\n agentMode?: boolean;\n cachedModelsCount?: number;\n modelTab?: \"preset\" | \"cached\" | \"huggingface\" | \"voice\";\n hfSearchQuery?: string;\n benchmarkStatus?: \"idle\" | \"running\" | \"done\";\n benchmarkStats?: BenchmarkStats;\n};\n\nfunction getViewPrompt(view: View, ctx: ViewContext = {}): string {\n // System instruction to prevent conversational responses\n const system =\n \"Complete the sentence. Do NOT start with 'Sure', 'I', 'Here', 'Of course', or any greeting. Just continue the sentence naturally. Max 20 words.\";\n\n switch (view) {\n case \"chat\":\n return `${system} \"Ready to chat about...\"`;\n case \"skills\":\n return `${system} \"Skills let you...\"`;\n case \"model\": {\n if (ctx.modelTab === \"preset\") {\n return `${system} \"These tested models...\"`;\n }\n if (ctx.modelTab === \"cached\") {\n const count = ctx.cachedModelsCount || 0;\n return `${system} \"${\n count > 0 ? `Your ${count} downloaded models` : \"Downloaded models\"\n }...\"`;\n }\n // HuggingFace tab - mention search if active\n if (ctx.hfSearchQuery) {\n return `${system} \"Searching '${ctx.hfSearchQuery}' to find...\"`;\n }\n return `${system} \"Search HuggingFace to...\"`;\n }\n case \"create-skill\":\n return `${system} \"Create a skill to...\"`;\n case \"code\":\n return `${system} \"Describe what code you need and...\"`;\n case \"benchmark\": {\n const stats = ctx.benchmarkStats;\n if (!stats || stats.runCount === 0) {\n return `${system} \"Run a benchmark to...\"`;\n }\n\n const last = stats.lastResult;\n const bests = stats.bests;\n const avgs = stats.averages;\n\n // Build a rich context for the AI\n let context = `Benchmark results: ${stats.runCount} total runs. `;\n\n if (last) {\n context += `Last run: ${last.tokensPerSec} tok/s, ${last.firstTokenMs}ms first token on ${last.device}. `;\n }\n\n if (bests) {\n context += `Records: fastest ${bests.fastestRun.tokensPerSec} tok/s (${bests.fastestRun.model}/${bests.fastestRun.device}), quickest first token ${bests.fastestFirstToken.firstTokenMs}ms. `;\n }\n\n if (avgs.length > 0) {\n const avgStrs = avgs.map(\n (a) => `${a.model} on ${a.device}: avg ${a.avgTokPerSec} tok/s over ${a.runs} runs`,\n );\n context += `Averages: ${avgStrs.join(\"; \")}. `;\n }\n\n // Compare last to best\n if (last && bests && last.tokensPerSec === bests.fastestRun.tokensPerSec) {\n context += \"This last run set a NEW RECORD! \";\n } else if (last && bests) {\n const diff = bests.fastestRun.tokensPerSec - last.tokensPerSec;\n if (diff > 0) {\n context += `Last run was ${diff} tok/s below the record. `;\n }\n }\n\n return `Based on this benchmark data, give an encouraging and insightful comment. Be specific about the numbers. ${context} Output ONLY the comment, 1-2 sentences. No greetings.`;\n }\n case \"info\":\n return `${system} \"System info shows...\"`;\n case \"serve\":\n return `${system} \"Start a server to...\"`;\n case \"frameworks\":\n return `${system} \"Add Gerbil to your app with...\"`;\n default:\n return \"\";\n }\n}\n\n// Keep static version for backwards compatibility\nconst _VIEW_PROMPTS: Record<View, string> = {\n menu: \"\",\n chat: getViewPrompt(\"chat\"),\n skills: getViewPrompt(\"skills\"),\n tools: \"\",\n \"create-skill\": getViewPrompt(\"create-skill\"),\n \"create-tool\": \"\",\n code: getViewPrompt(\"code\"),\n model: \"\",\n frameworks: \"\",\n benchmark: \"\",\n info: \"\",\n serve: \"\",\n};\n\nexport type DeviceMode = \"cpu\" | \"webgpu\";\n\nexport type AppProps = {\n /** Initial view to show after loading (default: \"menu\") */\n initialView?: View;\n /** Force a specific device mode (cpu or webgpu) */\n forcedDevice?: DeviceMode;\n};\n\nexport function App({ initialView = \"menu\", forcedDevice }: AppProps = {}) {\n const { exit } = useApp();\n const gerbilRef = useRef<Gerbil | null>(null); // Ref for cleanup\n // Determine initial device mode\n const initialDevice = forcedDevice ?? (process.env.GERBIL_CPU === \"1\" ? \"cpu\" : \"webgpu\");\n\n const [state, setState] = useState<AppState>({\n view: \"menu\", // Always start at menu during loading\n gerbil: null,\n model: \"qwen3-0.6b\",\n loading: true,\n loadingMessage: \"Loading model...\",\n thinkingMode: false,\n agentMode: true, // On by default - using proper Qwen tool format\n voiceMode: false,\n downloadStatus: \"\",\n deviceMode: initialDevice,\n ttsModel: \"kokoro-82m\",\n sttModel: \"whisper-tiny.en\",\n });\n const [pendingView, setPendingView] = useState<View | null>(\n initialView !== \"menu\" ? initialView : null,\n );\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [prevSelectedIndex, setPrevSelectedIndex] = useState(0);\n const [selectionPulse, setSelectionPulse] = useState(false);\n const [viewTransition, setViewTransition] = useState<\"in\" | \"out\" | null>(null);\n const [welcomeMessage, setWelcomeMessage] = useState(\"\");\n const [viewTip, setViewTip] = useState(\"\");\n const [generatingTip, setGeneratingTip] = useState(false);\n const generatingTipRef = useRef(false); // Ref to check without causing re-renders\n const [ctrlCPressed, setCtrlCPressed] = useState(false);\n const [modelTab, setModelTab] = useState<\"preset\" | \"cached\" | \"huggingface\" | \"voice\">(\"preset\");\n const [hfSearchQuery, setHfSearchQuery] = useState(\"\");\n const [benchmarkStatus, setBenchmarkStatus] = useState<\"idle\" | \"running\" | \"done\">(\"idle\");\n const [benchmarkStatsState, setBenchmarkStatsState] = useState<BenchmarkStats | undefined>();\n\n // Konami code easter egg\n const [konamiSequence, setKonamiSequence] = useState<string[]>([]);\n const [konamiActivated, setKonamiActivated] = useState(false);\n const [konamiMessage, setKonamiMessage] = useState(\"\");\n const KONAMI_CODE = [\"up\", \"up\", \"down\", \"down\", \"left\", \"right\", \"left\", \"right\", \"b\", \"a\"];\n\n const [sessionStats, setSessionStats] = useState<SessionStats>({\n prompts: 0,\n tokensIn: 0,\n tokensOut: 0,\n totalTimeMs: 0,\n startTime: Date.now(),\n benchResults: [],\n lastTokPerSec: 0,\n avgTokPerSec: 0,\n });\n\n // Benchmark comparison state (persists across model switches)\n const [benchmarkResults, setBenchmarkResults] = useState<BenchmarkResult[]>([]);\n const [benchmarkModels, setBenchmarkModels] = useState<string[]>([]);\n const [returnToBenchmark, setReturnToBenchmark] = useState(false);\n\n // Update state\n const [updateCheck, setUpdateCheck] = useState<UpdateCheckResult | null>(null);\n const [isUpdating, setIsUpdating] = useState(false);\n const [updateSuccess, setUpdateSuccess] = useState(false);\n\n // Animation: pulse effect when selection changes\n useEffect(() => {\n if (selectedIndex !== prevSelectedIndex) {\n setPrevSelectedIndex(selectedIndex);\n setSelectionPulse(true);\n const timer = setTimeout(() => setSelectionPulse(false), 150);\n return () => clearTimeout(timer);\n }\n }, [selectedIndex, prevSelectedIndex]);\n\n // Animation: view transition\n const transitionToView = (newView: View) => {\n setViewTransition(\"out\");\n setTimeout(() => {\n setState((s) => ({ ...s, view: newView }));\n setViewTransition(\"in\");\n setTimeout(() => setViewTransition(null), 200);\n }, 100);\n };\n\n // Generate welcome message (stable ref - only depends on setter)\n const generateWelcome = useCallback(async (g: Gerbil) => {\n try {\n setWelcomeMessage(\"\");\n for await (const chunk of g.stream(WELCOME_PROMPT, { maxTokens: 20 })) {\n setWelcomeMessage((m) => m + chunk);\n }\n } catch {\n // Ignore welcome generation errors\n setWelcomeMessage(\"Ready to help!\");\n }\n }, []);\n\n // Track if we've done the initial model load\n const hasLoadedInitialModelRef = useRef(false);\n\n // Load model on start - only runs ONCE on mount\n // Model switching is handled by handleModelSelect, not this effect\n useEffect(() => {\n // Skip if we've already loaded (prevents double-load on dependency changes)\n if (hasLoadedInitialModelRef.current) {\n return;\n }\n hasLoadedInitialModelRef.current = true;\n\n let mounted = true; // Guard against strict mode double-render\n\n const loadModel = async () => {\n const g = new Gerbil();\n try {\n // Use the device mode from state (set from CLI flag or env var)\n const device = state.deviceMode;\n\n await g.loadModel(state.model, {\n device,\n onProgress: (p) => {\n if (!mounted) {\n return;\n }\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n loadingMessage: p.file ? `${p.file} ${p.progress || 0}%` : p.status,\n }));\n },\n });\n\n if (!mounted) {\n // Component unmounted during load - clean up\n g.dispose();\n return;\n }\n\n const modelInfo = g.getModelInfo();\n const supportsThinking = modelInfo?.supportsThinking ?? false;\n\n // Set tool context so tools can use gerbil.generate()\n setToolContext({\n generate: async (prompt: string) => {\n const result = await g.generate(prompt, { maxTokens: 200 });\n return result.text;\n },\n });\n\n gerbilRef.current = g; // Store in ref for cleanup\n setState((s) => ({\n ...s,\n gerbil: g,\n loading: false,\n thinkingMode: supportsThinking,\n // If we have a pending view, go directly there\n view: pendingView || \"menu\",\n }));\n\n // Clear pending view\n if (pendingView) {\n setPendingView(null);\n }\n\n // Generate welcome in background\n // For WebGPU, add a delay to let Chrome GPU stabilize\n const isWebGPU = g.getDeviceMode() === \"webgpu\";\n if (isWebGPU) {\n setTimeout(() => generateWelcome(g).catch(() => {}), 1500);\n } else {\n generateWelcome(g).catch(() => {});\n }\n } catch (error) {\n if (!mounted) {\n return;\n }\n setState((s) => ({\n ...s,\n loading: false,\n loadingMessage: `Error: ${error}`,\n }));\n }\n };\n loadModel();\n\n return () => {\n mounted = false;\n // Only dispose if we actually have a gerbil instance\n if (gerbilRef.current) {\n // Set cleanup promise so index.tsx can await it before process.exit\n import(\"./index.js\").then(({ setCleanupPromise }) => {\n const cleanupPromise = gerbilRef.current?.dispose(true) ?? Promise.resolve();\n setCleanupPromise(cleanupPromise);\n });\n gerbilRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []); // Empty deps - only run on mount, model switching handled by handleModelSelect\n\n // Check for updates on mount (non-blocking)\n useEffect(() => {\n const performUpdateCheck = async () => {\n const result = await checkForUpdate();\n if (result.updateAvailable) {\n setUpdateCheck(result);\n }\n };\n\n // Run in background, don't block\n performUpdateCheck().catch(() => {\n // Silent fail - don't interrupt user experience\n });\n }, []);\n\n // Handle update installation\n const handleUpdate = async () => {\n setIsUpdating(true);\n const result = await installUpdate();\n setIsUpdating(false);\n\n if (result.success) {\n setUpdateSuccess(true);\n setUpdateCheck(null);\n // Clear success message after 10s\n setTimeout(() => setUpdateSuccess(false), 10_000);\n } else {\n // Show error briefly\n setUpdateCheck({\n ...updateCheck!,\n error: result.error,\n });\n setTimeout(() => {\n if (updateCheck) {\n setUpdateCheck({ ...updateCheck, error: undefined });\n }\n }, 5000);\n }\n };\n\n // Generate view tip when changing views - with context awareness\n useEffect(() => {\n if (!state.gerbil || state.view === \"menu\") {\n return;\n }\n\n // Skip regenerating tip while benchmark is running - this would cause concurrent\n // generation which crashes transformers.js (tensor disposed error)\n if (state.view === \"benchmark\" && benchmarkStatus === \"running\") {\n return;\n }\n\n // Get cached models count for model view\n const cacheInfo = state.view === \"model\" ? getCacheInfo() : null;\n\n // Build context for the prompt\n const ctx: ViewContext = {\n model: state.model,\n deviceMode: state.deviceMode,\n thinkingMode: state.thinkingMode,\n agentMode: state.agentMode,\n modelTab: state.view === \"model\" ? modelTab : undefined,\n hfSearchQuery:\n state.view === \"model\" && modelTab === \"huggingface\" ? hfSearchQuery : undefined,\n cachedModelsCount: cacheInfo?.models.length,\n benchmarkStatus: state.view === \"benchmark\" ? benchmarkStatus : undefined,\n benchmarkStats: state.view === \"benchmark\" ? benchmarkStatsState : undefined,\n };\n\n // Get context-aware prompt\n const prompt = getViewPrompt(state.view, ctx);\n if (!prompt) {\n return;\n }\n\n // Use a ref to track if we should abort (view changed during generation)\n let cancelled = false;\n\n const generateTip = async () => {\n // Don't start if already generating - prevents concurrent generation crash\n if (generatingTipRef.current) {\n return;\n }\n\n setViewTip(\"\");\n setGeneratingTip(true);\n generatingTipRef.current = true;\n try {\n const stream = state.gerbil?.stream(prompt, { maxTokens: 50 });\n if (stream) {\n for await (const chunk of stream) {\n if (cancelled) {\n break;\n }\n setViewTip((t) => t + chunk);\n }\n }\n } catch {\n // Ignore tip generation errors (including interrupts)\n }\n generatingTipRef.current = false;\n if (!cancelled) {\n setGeneratingTip(false);\n }\n };\n generateTip();\n\n // Cleanup: mark as cancelled if effect re-runs before completion\n return () => {\n cancelled = true;\n };\n }, [\n state.view,\n state.gerbil,\n state.model,\n state.deviceMode,\n state.thinkingMode,\n state.agentMode,\n modelTab,\n hfSearchQuery,\n benchmarkStatus,\n benchmarkStatsState,\n ]);\n\n // Reset Ctrl+C warning after a delay\n useEffect(() => {\n if (ctrlCPressed) {\n const timer = setTimeout(() => setCtrlCPressed(false), 3000);\n return () => clearTimeout(timer);\n }\n }, [ctrlCPressed]);\n\n // Reload with a specific device mode (for menu 'm' key and benchmark)\n const handleDeviceSwitch = async (newDevice: \"webgpu\" | \"cpu\") => {\n if (newDevice === state.deviceMode) {\n return;\n }\n\n setState((s) => ({\n ...s,\n deviceMode: newDevice,\n loading: true,\n loadingMessage: `Switching to ${newDevice.toUpperCase()}...`,\n }));\n\n // Dispose old gerbil - must await to prevent zombie pages\n if (gerbilRef.current) {\n await gerbilRef.current.dispose();\n gerbilRef.current = null;\n }\n\n const g = new Gerbil();\n try {\n await g.loadModel(state.model, {\n device: newDevice,\n onProgress: (p) => {\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n loadingMessage: p.file ? `${p.file} ${p.progress || 0}%` : p.status,\n }));\n },\n });\n const modelInfo = g.getModelInfo();\n const supportsThinking = modelInfo?.supportsThinking ?? false;\n\n setToolContext({\n generate: async (prompt: string) => {\n const result = await g.generate(prompt, { maxTokens: 200 });\n return result.text;\n },\n });\n\n gerbilRef.current = g;\n setState((s) => ({\n ...s,\n gerbil: g,\n loading: false,\n thinkingMode: supportsThinking,\n }));\n } catch (error) {\n setState((s) => ({\n ...s,\n loading: false,\n loadingMessage: `Error: ${error}`,\n }));\n }\n };\n\n // Global key handlers\n useInput((input, key) => {\n // Ctrl+C handling\n if (key.ctrl && input === \"c\") {\n if (ctrlCPressed) {\n // Exit immediately - cleanup happens in useEffect unmount\n exit();\n } else {\n setCtrlCPressed(true);\n }\n return;\n }\n\n if (ctrlCPressed) {\n setCtrlCPressed(false);\n }\n\n // Konami code detection (only on menu)\n if (state.view === \"menu\" && !konamiActivated) {\n let keyPressed = \"\";\n if (key.upArrow) {\n keyPressed = \"up\";\n } else if (key.downArrow) {\n keyPressed = \"down\";\n } else if (key.leftArrow) {\n keyPressed = \"left\";\n } else if (key.rightArrow) {\n keyPressed = \"right\";\n } else if (input === \"b\" || input === \"B\") {\n keyPressed = \"b\";\n } else if (input === \"a\" || input === \"A\") {\n keyPressed = \"a\";\n }\n\n if (keyPressed) {\n const newSequence = [...konamiSequence, keyPressed].slice(-10);\n setKonamiSequence(newSequence);\n\n if (newSequence.length === 10 && newSequence.every((k, i) => k === KONAMI_CODE[i])) {\n setKonamiActivated(true);\n setKonamiSequence([]);\n\n if (state.gerbil) {\n setKonamiMessage(\"*~* KONAMI CODE ACTIVATED *~*\");\n state.gerbil\n .generate(\n \"You just discovered a secret easter egg! Generate a single fun, quirky one-liner message (max 15 words) celebrating the user's achievement. Be playful and geeky. No quotes.\",\n { maxTokens: 50, temperature: 0.9 },\n )\n .then((result) => {\n const msg = result.text.replace(/<\\/?think>/g, \"\").trim();\n setKonamiMessage(`*~* ${msg} *~*`);\n setTimeout(() => {\n setKonamiActivated(false);\n setKonamiMessage(\"\");\n }, 5000);\n })\n .catch(() => {\n setKonamiMessage(\"*~* +30 LIVES! Just kidding, this is an LLM. *~*\");\n setTimeout(() => {\n setKonamiActivated(false);\n setKonamiMessage(\"\");\n }, 5000);\n });\n }\n }\n }\n }\n\n // Q to quit from menu\n if ((input === \"q\" || input === \"Q\") && state.view === \"menu\") {\n // Exit immediately - cleanup happens in useEffect unmount\n exit();\n return;\n }\n\n // Ctrl+Q to quit from anywhere\n if (input === \"q\" && key.ctrl) {\n // Exit immediately - cleanup happens in useEffect unmount\n exit();\n return;\n }\n\n // U to update (when update available)\n if ((input === \"u\" || input === \"U\") && updateCheck && !isUpdating) {\n handleUpdate();\n return;\n }\n\n // Menu navigation (grid: row 0 has 4 items, row 1 has 4 items)\n const ROW1_SIZE = 4;\n\n if (state.view === \"menu\") {\n if (key.leftArrow) {\n setSelectedIndex((i) => {\n if (i === 0) {\n return ROW1_SIZE - 1;\n }\n if (i === ROW1_SIZE) {\n return MENU_ITEMS.length - 1;\n }\n return i - 1;\n });\n }\n if (key.rightArrow) {\n setSelectedIndex((i) => {\n if (i === ROW1_SIZE - 1) {\n return 0;\n }\n if (i === MENU_ITEMS.length - 1) {\n return ROW1_SIZE;\n }\n return i + 1;\n });\n }\n if (key.upArrow) {\n setSelectedIndex((i) => {\n if (i < ROW1_SIZE) {\n const col = i;\n return Math.min(ROW1_SIZE + col, MENU_ITEMS.length - 1);\n }\n const col = i - ROW1_SIZE;\n return Math.min(col, ROW1_SIZE - 1);\n });\n }\n if (key.downArrow) {\n setSelectedIndex((i) => {\n if (i < ROW1_SIZE) {\n return Math.min(ROW1_SIZE + i, MENU_ITEMS.length - 1);\n }\n const col = i - ROW1_SIZE;\n return Math.min(col, ROW1_SIZE - 1);\n });\n }\n if (key.return) {\n transitionToView(MENU_ITEMS[selectedIndex].view);\n }\n // Number keys for quick select (0-9)\n if (/^[0-9]$/.test(input)) {\n const item = MENU_ITEMS.find((m) => m.key === input);\n if (item) {\n const idx = MENU_ITEMS.indexOf(item);\n setSelectedIndex(idx);\n transitionToView(item.view);\n }\n }\n // D for docs\n if (input === \"d\" || input === \"D\") {\n openBrowser(\"https://github.com/gethamster/gerbil#readme\");\n }\n // G for github\n if (input === \"g\" || input === \"G\") {\n openBrowser(\"https://github.com/gethamster/gerbil\");\n }\n } else {\n // Escape goes back to menu\n if (key.escape) {\n transitionToView(\"menu\");\n }\n }\n\n // Ctrl+T to toggle thinking mode (anywhere)\n if (input === \"t\" && key.ctrl) {\n const modelInfo = state.gerbil?.getModelInfo();\n if (modelInfo?.supportsThinking) {\n setState((s) => ({ ...s, thinkingMode: !s.thinkingMode }));\n }\n } else if ((input === \"t\" || input === \"T\") && state.view === \"menu\") {\n const modelInfo = state.gerbil?.getModelInfo();\n if (modelInfo?.supportsThinking) {\n setState((s) => ({ ...s, thinkingMode: !s.thinkingMode }));\n }\n }\n\n // Ctrl+A to toggle agent mode (anywhere)\n if (input === \"a\" && key.ctrl) {\n setState((s) => ({ ...s, agentMode: !s.agentMode }));\n } else if ((input === \"a\" || input === \"A\") && state.view === \"menu\") {\n setState((s) => ({ ...s, agentMode: !s.agentMode }));\n }\n\n // Ctrl+V to toggle voice mode (anywhere) - speaks responses aloud\n if (input === \"v\" && key.ctrl) {\n setState((s) => ({ ...s, voiceMode: !s.voiceMode }));\n } else if ((input === \"v\" || input === \"V\") && state.view === \"menu\") {\n setState((s) => ({ ...s, voiceMode: !s.voiceMode }));\n }\n\n // M to toggle device mode (menu only)\n if ((input === \"m\" || input === \"M\") && state.view === \"menu\" && !state.loading) {\n const newDevice = state.deviceMode === \"webgpu\" ? \"cpu\" : \"webgpu\";\n handleDeviceSwitch(newDevice);\n }\n });\n\n if (state.loading) {\n return <LoadingView message={state.loadingMessage} />;\n }\n\n // Menu view\n if (state.view === \"menu\") {\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n <Box alignItems=\"center\" flexDirection=\"column\">\n <Text bold color=\"cyan\">\n 🐹 Gerbil\n </Text>\n <Text dimColor>Local LLM inference for Node.js</Text>\n <Text dimColor>by {hyperlink(\"https://tryhamster.com\", \"tryhamster.com\")}</Text>\n </Box>\n\n {welcomeMessage ? (\n <Box alignItems=\"center\" justifyContent=\"center\" marginY={1}>\n <Text color=\"gray\" italic>\n {cleanResponse(welcomeMessage)}\n </Text>\n </Box>\n ) : null}\n\n <Box justifyContent=\"center\">\n <Text dimColor>model: </Text>\n <Text color=\"white\">{state.model}</Text>\n <Text dimColor> · </Text>\n <Text bold color={state.gerbil?.getDeviceMode() === \"webgpu\" ? \"green\" : \"yellow\"}>\n {state.gerbil?.getDeviceMode()?.toUpperCase() || \"CPU\"}\n </Text>\n {state.gerbil?.getModelInfo()?.supportsThinking ? (\n <>\n <Text dimColor> · thinking: </Text>\n <Text bold={state.thinkingMode} color={state.thinkingMode ? \"magenta\" : \"gray\"}>\n {state.thinkingMode ? \"on\" : \"off\"}\n </Text>\n </>\n ) : null}\n <Text dimColor> · agent: </Text>\n <Text bold={state.agentMode} color={state.agentMode ? \"green\" : \"gray\"}>\n {state.agentMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> · voice: </Text>\n <Text bold={state.voiceMode} color={state.voiceMode ? \"cyan\" : \"gray\"}>\n {state.voiceMode ? \"on\" : \"off\"}\n </Text>\n {state.gerbil?.isTTSLoaded() && (\n <>\n <Text dimColor> · tts: </Text>\n <Text color=\"cyan\">{state.gerbil.getTTSModelInfo()?.id || \"kokoro\"}</Text>\n </>\n )}\n {state.gerbil?.isSTTLoaded() && (\n <>\n <Text dimColor> · stt: </Text>\n <Text color=\"magenta\">{state.gerbil.getSTTModelInfo()?.id || \"whisper\"}</Text>\n </>\n )}\n </Box>\n\n {konamiActivated && konamiMessage ? (\n <Box justifyContent=\"center\" marginY={1}>\n <Text backgroundColor=\"black\" bold color=\"magenta\">\n {konamiMessage}\n </Text>\n </Box>\n ) : null}\n\n <Box alignItems=\"center\" flexDirection=\"column\" marginY={1}>\n <Box justifyContent=\"center\">\n {MENU_ITEMS.slice(0, 4).map((item, idx) => (\n <MenuCard\n isTopRow={true}\n item={item}\n key={item.key}\n pulse={selectionPulse && idx === selectedIndex}\n selected={idx === selectedIndex}\n />\n ))}\n </Box>\n <Box justifyContent=\"center\">\n {MENU_ITEMS.slice(4).map((item, idx) => (\n <MenuCard\n isTopRow={false}\n item={item}\n key={item.key}\n pulse={selectionPulse && idx + 4 === selectedIndex}\n selected={idx + 4 === selectedIndex}\n />\n ))}\n </Box>\n </Box>\n\n <Box justifyContent=\"center\">\n <Text dimColor>arrows · enter · </Text>\n <Text color=\"yellow\">1-8</Text>\n {state.gerbil?.getModelInfo()?.supportsThinking && (\n <>\n <Text dimColor> · </Text>\n <Text color=\"magenta\">t</Text>\n <Text dimColor>hink</Text>\n </>\n )}\n <Text dimColor> · </Text>\n <Text color=\"green\">a</Text>\n <Text dimColor>gent · </Text>\n <Text color=\"cyan\">v</Text>\n <Text dimColor>oice · </Text>\n <Text color=\"cyan\">m</Text>\n <Text dimColor>ode · </Text>\n <Text color=\"blue\">d</Text>\n <Text dimColor>ocs · </Text>\n <Text color=\"blue\">g</Text>\n <Text dimColor>ithub · </Text>\n <Text color=\"red\">q</Text>\n <Text dimColor>uit</Text>\n </Box>\n\n {ctrlCPressed ? (\n <Box justifyContent=\"center\" marginTop={1}>\n <Text color=\"yellow\">Press Ctrl+C again to quit</Text>\n </Box>\n ) : null}\n </Box>\n );\n }\n\n // Handle model switching\n const handleModelSelect = async (model: string, deviceOverride?: \"webgpu\" | \"cpu\") => {\n const returnView = returnToBenchmark ? \"benchmark\" : \"menu\";\n const newDevice = deviceOverride ?? state.deviceMode;\n setState((s) => ({\n ...s,\n model,\n deviceMode: newDevice,\n view: returnView,\n loading: true,\n loadingMessage: \"Switching model...\",\n }));\n setReturnToBenchmark(false);\n\n // Dispose old gerbil using ref - must await to prevent zombie pages\n if (gerbilRef.current) {\n await gerbilRef.current.dispose();\n gerbilRef.current = null;\n }\n\n const g = new Gerbil();\n try {\n await g.loadModel(model, {\n device: newDevice,\n onProgress: (p) => {\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n loadingMessage: p.file ? `${p.file} ${p.progress || 0}%` : p.status,\n }));\n },\n });\n const modelInfo = g.getModelInfo();\n const supportsThinking = modelInfo?.supportsThinking ?? false;\n\n // Update tool context for new model\n setToolContext({\n generate: async (prompt: string) => {\n const result = await g.generate(prompt, { maxTokens: 200 });\n return result.text;\n },\n });\n\n gerbilRef.current = g; // Update ref\n setState((s) => ({\n ...s,\n gerbil: g,\n loading: false,\n thinkingMode: supportsThinking,\n }));\n generateWelcome(g);\n } catch (error) {\n setState((s) => ({\n ...s,\n loading: false,\n loadingMessage: `Error: ${error}`,\n }));\n }\n };\n\n const handleDownloadOnly = async (modelId: string, _hfId: string): Promise<boolean> => {\n setState((s) => ({ ...s, downloadStatus: `Downloading ${modelId}...` }));\n const tempGerbil = new Gerbil();\n try {\n await tempGerbil.loadModel(modelId, {\n onProgress: (p) => {\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n downloadStatus: p.file ? `${p.file} ${p.progress || 0}%` : p.status || \"\",\n }));\n },\n });\n await tempGerbil.dispose();\n setState((s) => ({\n ...s,\n downloadStatus: `[done] Downloaded ${modelId}`,\n }));\n setTimeout(() => setState((s) => ({ ...s, downloadStatus: \"\" })), 2500);\n return true; // Success\n } catch (error) {\n setState((s) => ({ ...s, downloadStatus: `[error] ${error}` }));\n setTimeout(() => setState((s) => ({ ...s, downloadStatus: \"\" })), 3000);\n return false; // Failed\n }\n };\n\n // Active view with minimal header\n return (\n <Box flexDirection=\"column\" height=\"100%\">\n <Box\n borderBottom={false}\n borderColor=\"gray\"\n borderLeft={false}\n borderRight={false}\n borderStyle=\"single\"\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n gerbil\n </Text>\n <Text color=\"gray\"> </Text>\n <Text dimColor>{CURRENT_VERSION}</Text>\n <Text color=\"gray\"> / </Text>\n <Text color=\"white\">\n {MENU_ITEMS.find((m) => m.view === state.view)?.label || state.view}\n </Text>\n {state.thinkingMode && (\n <>\n <Text color=\"gray\"> / </Text>\n <Text color=\"magenta\">thinking</Text>\n </>\n )}\n {state.agentMode && (\n <>\n <Text color=\"gray\"> / </Text>\n <Text color=\"green\">agent</Text>\n </>\n )}\n {state.downloadStatus && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"yellow\">{state.downloadStatus}</Text>\n </>\n )}\n {isUpdating && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"yellow\">⬇ Updating...</Text>\n </>\n )}\n {updateSuccess && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"green\">✅ Updated! Restart to use v{updateCheck?.latestVersion}</Text>\n </>\n )}\n {updateCheck && !isUpdating && !updateSuccess && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"cyan\">Update v{updateCheck.latestVersion} → Press </Text>\n <Text bold color=\"cyan\">\n u\n </Text>\n <Text color=\"cyan\"> to update</Text>\n </>\n )}\n <Text color=\"gray\"> | </Text>\n <Text dimColor>esc back | ^C quit</Text>\n {ctrlCPressed && <Text color=\"yellow\"> (again to quit)</Text>}\n </Box>\n\n {viewTip && (\n <Box paddingX={2} paddingY={1}>\n <Text dimColor italic>\n {generatingTip ? `${cleanResponse(viewTip)}▋` : cleanResponse(viewTip)}\n </Text>\n </Box>\n )}\n\n <Box flexDirection=\"column\" flexGrow={1}>\n <ViewTransition transition={viewTransition}>\n {state.view === \"chat\" && state.gerbil && (\n <ChatView\n agentMode={state.agentMode}\n avgTokPerSec={sessionStats.avgTokPerSec}\n gerbil={state.gerbil}\n lastTokPerSec={sessionStats.lastTokPerSec}\n onCreateTool={() => transitionToView(\"create-tool\")}\n onGenerationComplete={(stats) => {\n setSessionStats((s) => {\n const newPrompts = s.prompts + 1;\n const newTotalTokens = s.tokensOut + stats.tokensOut;\n const newTotalTime = s.totalTimeMs + stats.timeMs;\n return {\n ...s,\n prompts: newPrompts,\n tokensOut: newTotalTokens,\n totalTimeMs: newTotalTime,\n lastTokPerSec: stats.tokPerSec,\n avgTokPerSec: newTotalTime > 0 ? (newTotalTokens / newTotalTime) * 1000 : 0,\n };\n });\n }}\n onToggleAgent={() => setState((s) => ({ ...s, agentMode: !s.agentMode }))}\n onToggleThinking={() => setState((s) => ({ ...s, thinkingMode: !s.thinkingMode }))}\n onToggleVoice={() => setState((s) => ({ ...s, voiceMode: !s.voiceMode }))}\n thinkingMode={state.thinkingMode}\n voiceMode={state.voiceMode}\n />\n )}\n {state.view === \"skills\" && state.gerbil && (\n <SkillsView\n gerbil={state.gerbil}\n onCreateNew={() => transitionToView(\"create-skill\")}\n onSwitchModel={async (modelId) => {\n // Switch to the selected model\n setState((s) => ({\n ...s,\n loading: true,\n loadingMessage: `Loading ${modelId}...`,\n }));\n try {\n await state.gerbil?.loadModel(modelId);\n setState((s) => ({ ...s, model: modelId, loading: false }));\n } catch (_err) {\n setState((s) => ({ ...s, loading: false }));\n }\n }}\n />\n )}\n {state.view === \"create-skill\" && state.gerbil && (\n <CreateSkillView gerbil={state.gerbil} onDone={() => transitionToView(\"menu\")} />\n )}\n {state.view === \"tools\" && (\n <ToolsView onCreateTool={() => transitionToView(\"create-tool\")} />\n )}\n {state.view === \"create-tool\" && state.gerbil && (\n <CreateToolView gerbil={state.gerbil} onDone={() => transitionToView(\"tools\")} />\n )}\n {state.view === \"code\" && state.gerbil && <CodeView gerbil={state.gerbil} />}\n {state.view === \"model\" && (\n <ModelView\n currentModel={state.model}\n currentSTTModel={state.sttModel}\n currentTTSModel={state.ttsModel}\n onDownloadOnly={handleDownloadOnly}\n onSearchChange={setHfSearchQuery}\n onSelect={handleModelSelect}\n onSelectTTS={async (modelId) => {\n if (state.gerbil) {\n await state.gerbil.loadTTS({ model: modelId });\n setState((s) => ({ ...s, ttsModel: modelId }));\n }\n }}\n onSelectSTT={async (modelId) => {\n if (state.gerbil) {\n await state.gerbil.loadSTT(modelId);\n setState((s) => ({ ...s, sttModel: modelId }));\n }\n }}\n onTabChange={setModelTab}\n />\n )}\n {state.view === \"frameworks\" && <FrameworksView />}\n {state.view === \"benchmark\" && state.gerbil && (\n <BenchmarkView\n benchmarkModels={benchmarkModels}\n benchmarkResults={benchmarkResults}\n disabled={generatingTip}\n gerbil={state.gerbil}\n model={state.model}\n onResult={(tokPerSec) => {\n setSessionStats((s) => ({\n ...s,\n benchResults: [...s.benchResults, { model: state.model, tokPerSec }],\n }));\n }}\n onStatusChange={(status, stats) => {\n setBenchmarkStatus(status);\n setBenchmarkStatsState(stats);\n }}\n onSwitchDevice={handleDeviceSwitch}\n onSwitchModel={() => {\n setReturnToBenchmark(true);\n transitionToView(\"model\");\n }}\n setBenchmarkModels={setBenchmarkModels}\n setBenchmarkResults={setBenchmarkResults}\n />\n )}\n {state.view === \"info\" && state.gerbil && (\n <InfoView\n gerbil={state.gerbil}\n model={state.model}\n modelFamily={state.gerbil.getModelInfo()?.family || \"other\"}\n onGoToCache={() => {\n // Go to Model view with Cached tab selected\n transitionToView(\"model\");\n // ModelView will need to handle starting on cached tab\n }}\n stats={sessionStats}\n />\n )}\n {state.view === \"serve\" && state.gerbil && (\n <ServeView gerbil={state.gerbil} model={state.model} />\n )}\n </ViewTransition>\n </Box>\n </Box>\n );\n}\n\nfunction ViewTransition({\n transition,\n children,\n}: {\n transition: \"in\" | \"out\" | null;\n children: React.ReactNode;\n}) {\n if (transition === \"out\") {\n return (\n <Box alignItems=\"center\" flexDirection=\"column\" height={3} justifyContent=\"center\">\n <Text dimColor>...</Text>\n </Box>\n );\n }\n return <>{children}</>;\n}\n\nfunction MenuCard({\n item,\n selected,\n pulse,\n}: {\n item: (typeof MENU_ITEMS)[0];\n selected: boolean;\n pulse: boolean;\n isTopRow?: boolean;\n}) {\n const borderStyle = selected ? (pulse ? \"round\" : \"double\") : \"single\";\n const pointer = selected ? (pulse ? \">\" : \"*\") : \" \";\n\n return (\n <Box\n alignItems=\"center\"\n borderColor={selected ? (item.color as any) : \"gray\"}\n borderStyle={borderStyle}\n flexDirection=\"column\"\n justifyContent=\"center\"\n paddingX={1}\n paddingY={1}\n width={20}\n >\n <Box justifyContent=\"center\">\n <Text bold={selected} color={selected ? (item.color as any) : \"gray\"}>\n {pointer}[{item.key}]\n </Text>\n <Text\n bold={selected}\n color={selected ? (item.color as any) : \"white\"}\n inverse={pulse && selected}\n >\n {\" \"}\n {item.label}\n </Text>\n </Box>\n <Text color={selected ? (item.color as any) : undefined} dimColor={!selected}>\n {item.desc}\n </Text>\n </Box>\n );\n}\n","import { render } from \"ink\";\nimport { App, type View } from \"./App.js\";\n\nexport type DeviceMode = \"cpu\" | \"webgpu\";\n\nexport type ReplOptions = {\n /** Initial view to show (default: \"menu\") */\n initialView?: View;\n /** Force a specific device mode (cpu or webgpu) */\n forcedDevice?: DeviceMode;\n};\n\n// Shared cleanup promise that App can set\nlet pendingCleanup: Promise<void> | null = null;\nexport function setCleanupPromise(promise: Promise<void>) {\n pendingCleanup = promise;\n}\n\nexport async function startRepl(options?: ReplOptions) {\n // Track SIGINT count for force-exit on repeated signals\n let sigintCount = 0;\n let sigintTimer: NodeJS.Timeout | null = null;\n\n const handleSignal = () => {\n sigintCount += 1;\n\n // Reset count after 2 seconds\n if (sigintTimer) {\n clearTimeout(sigintTimer);\n }\n sigintTimer = setTimeout(() => {\n sigintCount = 0;\n }, 2000);\n\n if (sigintCount >= 2) {\n // Force exit on second SIGINT (user really wants out)\n process.exit(0);\n }\n // First SIGINT is handled by ink's useInput - don't exit yet\n };\n\n process.on(\"SIGINT\", handleSignal);\n process.on(\"SIGTERM\", () => process.exit(0));\n\n try {\n const { waitUntilExit } = render(\n <App forcedDevice={options?.forcedDevice} initialView={options?.initialView} />,\n );\n await waitUntilExit();\n\n // Wait for cleanup to complete (set by App component)\n if (pendingCleanup) {\n try {\n await Promise.race([\n pendingCleanup,\n new Promise((r) => setTimeout(r, 500)), // Max 500ms wait\n ]);\n } catch {\n // Ignore cleanup errors\n }\n }\n\n // Additional delay for Chrome's native cleanup\n await new Promise((r) => setTimeout(r, 200));\n\n // Clean exit after ink finishes\n process.exit(0);\n } finally {\n process.off(\"SIGINT\", handleSignal);\n if (sigintTimer) {\n clearTimeout(sigintTimer);\n }\n }\n}\n","#!/usr/bin/env node\n\n/**\n * Gerbil CLI\n *\n * @example\n * ```bash\n * gerbil \"Write a haiku\"\n * gerbil generate \"Hello world\" --thinking\n * gerbil commit\n * gerbil chat\n * gerbil serve --mcp\n * gerbil models\n * gerbil info\n * ```\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { version } from \"../../package.json\" with { type: \"json\" };\nimport { Gerbil } from \"../core/gerbil.js\";\nimport { BUILTIN_MODELS } from \"../core/models.js\";\nimport { startMCPServer } from \"../integrations/mcp.js\";\nimport * as skills from \"../skills/index.js\";\nimport { startRepl } from \"./repl/index.js\";\n\nconst program = new Command();\n\nprogram.name(\"gerbil\").description(\"🐹 Local LLM inference for Node.js\").version(version);\n\n// ============================================\n// Default command: generate\n// ============================================\n\nprogram\n .argument(\"[prompt...]\", \"Prompt to generate from (opens REPL if empty)\")\n .option(\"-m, --model <id>\", \"Model to use\", \"qwen3-0.6b\")\n .option(\"-n, --max-tokens <n>\", \"Max tokens\", \"256\")\n .option(\"-t, --temperature <t>\", \"Temperature\", \"0.7\")\n .option(\"-s, --system <text>\", \"System prompt\")\n .option(\"--thinking\", \"Enable thinking mode\")\n .option(\"--stream\", \"Stream output\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (promptParts, opts) => {\n // If no prompt provided, launch REPL\n if (promptParts.length === 0) {\n await startRepl({});\n return;\n }\n\n const prompt = promptParts.join(\" \");\n await runGenerate(prompt, opts);\n });\n\n// ============================================\n// Generate command\n// ============================================\n\nprogram\n .command(\"generate <prompt...>\")\n .alias(\"g\")\n .description(\"Generate text\")\n .option(\"-m, --model <id>\", \"Model to use\", \"qwen3-0.6b\")\n .option(\"-n, --max-tokens <n>\", \"Max tokens\", \"256\")\n .option(\"-t, --temperature <t>\", \"Temperature\", \"0.7\")\n .option(\"-s, --system <text>\", \"System prompt\")\n .option(\"--thinking\", \"Enable thinking mode\")\n .option(\"--stream\", \"Stream output\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (promptParts, opts) => {\n const prompt = promptParts.join(\" \");\n await runGenerate(prompt, opts);\n });\n\nasync function runGenerate(prompt: string, opts: any) {\n const g = new Gerbil();\n const spinner = ora(`Loading ${chalk.cyan(opts.model)}...`).start();\n\n try {\n await g.loadModel(opts.model, {\n onProgress: (p) => {\n spinner.text = p.progress ? `${p.status} (${p.progress}%)` : p.status;\n },\n });\n spinner.succeed(\"Model loaded\");\n\n if (opts.thinking) {\n }\n\n if (opts.stream) {\n process.stdout.write(chalk.green(\"Response: \"));\n for await (const chunk of g.stream(prompt, {\n maxTokens: Number.parseInt(opts.maxTokens, 10),\n temperature: Number.parseFloat(opts.temperature),\n system: opts.system,\n thinking: opts.thinking,\n })) {\n process.stdout.write(chunk);\n }\n } else {\n const genSpinner = ora(\"Generating...\").start();\n const result = await g.generate(prompt, {\n maxTokens: Number.parseInt(opts.maxTokens, 10),\n temperature: Number.parseFloat(opts.temperature),\n system: opts.system,\n thinking: opts.thinking,\n });\n genSpinner.stop();\n\n if (opts.json) {\n } else if (result.thinking) {\n }\n }\n\n await g.dispose();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n}\n\n// ============================================\n// REPL command (TUI)\n// ============================================\n\nprogram\n .command(\"repl\")\n .alias(\"r\")\n .description(\"Interactive TUI with chat, skills, and more\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ forcedDevice });\n });\n\n// ============================================\n// Chat command (opens REPL in chat view)\n// ============================================\n\nprogram\n .command(\"chat\")\n .alias(\"c\")\n .description(\"Interactive chat (opens REPL chat view)\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"chat\", forcedDevice });\n });\n\n// ============================================\n// REPL View Shortcuts (no args = open view)\n// ============================================\n\nprogram\n .command(\"skills\")\n .description(\"Open REPL skills view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"skills\", forcedDevice });\n });\n\nprogram\n .command(\"tools\")\n .description(\"Open REPL tools view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"tools\", forcedDevice });\n });\n\nprogram\n .command(\"model\")\n .description(\"Open REPL model view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"model\", forcedDevice });\n });\n\nprogram\n .command(\"integrate\")\n .description(\"Open REPL framework integration view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"frameworks\", forcedDevice });\n });\n\nprogram\n .command(\"benchmark\")\n .description(\"Open REPL benchmark view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"benchmark\", forcedDevice });\n });\n\n// ============================================\n// Commit command\n// ============================================\n\nprogram\n .command(\"commit\")\n .description(\"Generate commit message from staged changes\")\n .option(\"--type <type>\", \"Commit style: conventional, simple, detailed\", \"conventional\")\n .option(\"--write\", \"Write message to .git/COMMIT_EDITMSG\")\n .action(async (opts) => {\n const spinner = ora(\"Generating commit message...\").start();\n\n try {\n const message = await skills.commit({ type: opts.type });\n spinner.stop();\n\n if (opts.write) {\n const fs = await import(\"node:fs\");\n fs.writeFileSync(\".git/COMMIT_EDITMSG\", message);\n }\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Summarize command\n// ============================================\n\nprogram\n .command(\"summarize <file>\")\n .description(\"Summarize a file\")\n .option(\"-l, --length <length>\", \"Length: short, medium, long\", \"medium\")\n .option(\"-f, --format <format>\", \"Format: paragraph, bullets\", \"paragraph\")\n .action(async (file, opts) => {\n const fs = await import(\"node:fs\");\n const content = fs.readFileSync(file, \"utf-8\");\n\n const spinner = ora(\"Summarizing...\").start();\n\n try {\n const _summary = await skills.summarize({\n content,\n length: opts.length,\n format: opts.format,\n });\n spinner.stop();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Explain command\n// ============================================\n\nprogram\n .command(\"explain <file>\")\n .description(\"Explain code\")\n .option(\"-l, --level <level>\", \"Level: beginner, intermediate, expert\", \"intermediate\")\n .action(async (file, opts) => {\n const fs = await import(\"node:fs\");\n const content = fs.readFileSync(file, \"utf-8\");\n\n const spinner = ora(\"Explaining...\").start();\n\n try {\n const _explanation = await skills.explain({ content, level: opts.level });\n spinner.stop();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Review command\n// ============================================\n\nprogram\n .command(\"review <file>\")\n .description(\"Review code\")\n .option(\"-f, --focus <areas>\", \"Focus areas (comma-separated)\", \"all\")\n .action(async (file, opts) => {\n const fs = await import(\"node:fs\");\n const content = fs.readFileSync(file, \"utf-8\");\n\n const spinner = ora(\"Reviewing...\").start();\n\n try {\n const focus = opts.focus === \"all\" ? [\"all\"] : opts.focus.split(\",\");\n const _reviewResult = await skills.review({\n code: content,\n focus: focus as any,\n });\n spinner.stop();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Speak command (TTS)\n// ============================================\n\nprogram\n .command(\"speak [text...]\")\n .description(\"Convert text to speech using local TTS\")\n .option(\"-v, --voice <voice>\", \"Voice ID (e.g., af_bella, am_adam)\", \"af_bella\")\n .option(\"-s, --speed <speed>\", \"Speech speed (0.5-2.0)\", \"1.0\")\n .option(\"-o, --output <file>\", \"Save to WAV file (use - for stdout)\")\n .option(\"-m, --model <model>\", \"TTS model (kokoro-82m, supertonic-66m)\", \"kokoro-82m\")\n .option(\"--list-voices\", \"List available voices\")\n .option(\"--list-models\", \"List available TTS models\")\n .action(async (textParts, opts) => {\n const g = new Gerbil();\n\n // List models\n if (opts.listModels) {\n console.log(chalk.cyan(\"\\nAvailable TTS models:\\n\"));\n const models = await g.listTTSModels();\n for (const model of models) {\n console.log(\n ` ${chalk.green(model.id.padEnd(18))} ${model.description} (${model.voiceCount} voices, ${model.sampleRate}Hz)`,\n );\n }\n console.log();\n return;\n }\n\n // List voices\n if (opts.listVoices) {\n console.log(chalk.cyan(\"\\nAvailable voices:\\n\"));\n const voices = g.listVoices();\n for (const voice of voices) {\n console.log(\n ` ${chalk.green(voice.id.padEnd(15))} ${voice.name} (${voice.gender}, ${voice.language})`,\n );\n }\n console.log();\n return;\n }\n\n // Get text from args or stdin\n let text = textParts.join(\" \").trim();\n\n // If no text provided, try reading from stdin (for piping)\n if (!text) {\n const fs = await import(\"node:fs\");\n try {\n // Check if stdin has data (non-TTY means piped input)\n if (!process.stdin.isTTY) {\n text = fs.readFileSync(0, \"utf-8\").trim(); // fd 0 = stdin\n }\n } catch {\n // No stdin data\n }\n }\n\n if (!text) {\n console.log(chalk.yellow(\"Usage: gerbil speak <text> [--voice <id>] [--output file.wav]\"));\n console.log(chalk.gray(\" gerbil speak --list-voices\"));\n console.log(chalk.gray(\" gerbil speak --list-models\"));\n console.log(chalk.gray(\" echo 'Hello' | gerbil speak -o output.wav\"));\n console.log(chalk.gray(\" gerbil speak 'Hello' -o - > output.wav # stdout\"));\n return;\n }\n\n // For stdout output, suppress spinner\n const isStdout = opts.output === \"-\";\n const spinner = isStdout ? null : ora(\"Loading TTS model...\").start();\n\n try {\n await g.loadTTS({\n model: opts.model,\n onProgress: (p) => {\n if (spinner) spinner.text = p.status;\n },\n });\n if (spinner) spinner.text = `Generating speech (${opts.voice})...`;\n\n const result = await g.speak(text, {\n voice: opts.voice,\n speed: Number.parseFloat(opts.speed),\n });\n\n if (spinner) spinner.succeed(`Generated ${result.duration.toFixed(1)}s audio`);\n\n if (opts.output === \"-\") {\n // Output raw WAV to stdout (for piping)\n const wavBuffer = audioToWav(result.audio, result.sampleRate);\n process.stdout.write(wavBuffer);\n } else if (opts.output) {\n // Save to WAV file\n const wavBuffer = audioToWav(result.audio, result.sampleRate);\n const fs = await import(\"node:fs\");\n fs.writeFileSync(opts.output, wavBuffer);\n console.log(chalk.green(`\\nSaved to: ${opts.output}`));\n console.log(chalk.gray(` Play with: afplay ${opts.output}`));\n } else {\n // Save to temp file and play with afplay (macOS)\n const os = await import(\"node:os\");\n const path = await import(\"node:path\");\n const fs = await import(\"node:fs\");\n\n const tempFile = path.join(os.tmpdir(), `gerbil-tts-${Date.now()}.wav`);\n const wavBuffer = audioToWav(result.audio, result.sampleRate);\n fs.writeFileSync(tempFile, wavBuffer);\n\n // Play audio (macOS: afplay, Linux: aplay, Windows: not supported yet)\n const { exec } = await import(\"node:child_process\");\n const platform = os.platform();\n\n if (platform === \"darwin\") {\n exec(`afplay \"${tempFile}\"`, (err) => {\n if (err) console.error(chalk.red(\"Error playing audio:\", err.message));\n fs.unlinkSync(tempFile); // Cleanup\n });\n } else if (platform === \"linux\") {\n exec(`aplay \"${tempFile}\"`, (err) => {\n if (err) console.error(chalk.red(\"Error playing audio:\", err.message));\n fs.unlinkSync(tempFile);\n });\n } else {\n console.log(chalk.yellow(`\\nAudio saved to: ${tempFile}`));\n console.log(chalk.gray(\"Playback not supported on this platform. Use --output to save.\"));\n }\n }\n\n await g.dispose();\n } catch (e: any) {\n if (spinner) spinner.fail(\"Error\");\n console.error(chalk.red(e.message));\n process.exit(1);\n }\n });\n\n// ============================================\n// Transcribe command (STT)\n// ============================================\n\n// ============================================\n// Voice command (STT → LLM → TTS)\n// ============================================\n\nprogram\n .command(\"voice [audio]\")\n .description(\"Voice conversation: transcribe audio, generate response, speak it back\")\n .option(\"-m, --model <model>\", \"LLM model to use\", \"qwen3-0.6b\")\n .option(\"-s, --stt-model <model>\", \"STT model ID\", \"whisper-tiny.en\")\n .option(\"-v, --voice <voice>\", \"TTS voice ID\", \"af_heart\")\n .option(\n \"--system <prompt>\",\n \"System prompt\",\n \"You are a helpful voice assistant. Keep responses brief and conversational.\",\n )\n .option(\"--thinking\", \"Enable thinking mode\")\n .action(async (audioFile, opts) => {\n if (!audioFile) {\n console.log(chalk.cyan(\"\\n🎙️ Gerbil Voice Chat\\n\"));\n console.log(chalk.yellow(\"Usage: gerbil voice <audio.wav> [options]\\n\"));\n console.log(\"Options:\");\n console.log(\" -m, --model <model> LLM model (default: qwen3-0.6b)\");\n console.log(\" -s, --stt-model <model> STT model (default: whisper-tiny.en)\");\n console.log(\" -v, --voice <voice> TTS voice (default: af_heart)\");\n console.log(\" --system <prompt> System prompt\");\n console.log(\" --thinking Enable thinking mode\");\n console.log(\"\\nExample:\");\n console.log(chalk.gray(\" gerbil voice question.wav --voice bf_emma\"));\n console.log(chalk.gray(' gerbil voice \"what time is it.wav\" --model qwen3-1.7b\\n'));\n return;\n }\n\n const g = new Gerbil();\n const spinner = ora(\"Initializing...\").start();\n\n try {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n const os = await import(\"node:os\");\n const { exec } = await import(\"node:child_process\");\n\n // Read audio file\n const filePath = path.resolve(audioFile);\n if (!fs.existsSync(filePath)) {\n spinner.fail(`File not found: ${filePath}`);\n process.exit(1);\n }\n\n const audioData = new Uint8Array(fs.readFileSync(filePath));\n\n // Step 1: Transcribe\n spinner.text = \"Loading STT model...\";\n await g.loadSTT(opts.sttModel, {\n onProgress: (p: any) => {\n spinner.text = p.status || \"Loading STT...\";\n },\n });\n\n spinner.text = \"Transcribing...\";\n const transcribeResult = await g.transcribe(audioData);\n const userText = transcribeResult.text.trim();\n\n spinner.succeed(`You said: \"${userText}\"`);\n\n if (!userText) {\n console.log(chalk.yellow(\"No speech detected in audio.\"));\n await g.dispose();\n return;\n }\n\n // Step 2: Generate response\n spinner.start(\"Loading LLM...\");\n await g.loadModel(opts.model, {\n onProgress: (p: any) => {\n spinner.text = p.status || \"Loading LLM...\";\n },\n });\n\n spinner.text = \"Thinking...\";\n let response = \"\";\n for await (const chunk of g.stream(userText, {\n system: opts.system,\n thinking: opts.thinking,\n })) {\n response += chunk;\n }\n\n spinner.succeed(\"Generated response\");\n console.log(chalk.cyan(\"\\nAssistant: \") + response.trim());\n\n // Step 3: Speak response\n spinner.start(\"Loading TTS...\");\n await g.loadTTS({\n onProgress: (p: any) => {\n spinner.text = p.status || \"Loading TTS...\";\n },\n });\n\n spinner.text = \"Speaking...\";\n const speakResult = await g.speak(response.trim(), {\n voice: opts.voice,\n speed: 1.0,\n });\n\n // Save and play audio\n const tempFile = path.join(os.tmpdir(), `gerbil-voice-${Date.now()}.wav`);\n const wavBuffer = audioToWav(speakResult.audio, speakResult.sampleRate);\n fs.writeFileSync(tempFile, wavBuffer);\n\n spinner.succeed(`Speaking (${speakResult.duration.toFixed(1)}s)`);\n\n // Play audio\n const platform = os.platform();\n if (platform === \"darwin\") {\n exec(`afplay \"${tempFile}\"`, () => fs.unlinkSync(tempFile));\n } else if (platform === \"linux\") {\n exec(`aplay \"${tempFile}\"`, () => fs.unlinkSync(tempFile));\n } else {\n console.log(chalk.gray(`Audio saved to: ${tempFile}`));\n }\n\n await g.dispose();\n } catch (e: any) {\n spinner.fail(\"Error\");\n console.error(chalk.red(e.message));\n process.exit(1);\n }\n });\n\n// ============================================\n// Transcribe command (STT)\n// ============================================\n\nprogram\n .command(\"transcribe [file]\")\n .description(\"Transcribe audio to text using local STT (Whisper)\")\n .option(\"-m, --model <model>\", \"STT model ID\", \"whisper-tiny.en\")\n .option(\"-l, --language <lang>\", \"Language hint (for multilingual models)\")\n .option(\"-t, --timestamps\", \"Show timestamps for each segment\")\n .option(\"-o, --output <file>\", \"Save transcription to file\")\n .option(\"--list-models\", \"List available STT models\")\n .action(async (file, opts) => {\n const g = new Gerbil();\n\n // List models\n if (opts.listModels) {\n console.log(chalk.cyan(\"\\n🎤 Available STT models:\\n\"));\n const models = await g.listSTTModels();\n for (const model of models) {\n const langs = model.multilingual ? \"multi\" : model.languages.join(\",\");\n console.log(\n ` ${chalk.green(model.id.padEnd(18))} ${model.size.padEnd(6)} ${model.description} (${langs})`,\n );\n }\n console.log();\n return;\n }\n\n // Require file input\n if (!file) {\n console.log(\n chalk.yellow(\"Usage: gerbil transcribe <audio.wav> [--model <id>] [--timestamps]\"),\n );\n console.log(chalk.gray(\" gerbil transcribe --list-models\"));\n return;\n }\n\n const spinner = ora(`Loading STT model (${opts.model})...`).start();\n\n try {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n\n // Read audio file\n const filePath = path.resolve(file);\n if (!fs.existsSync(filePath)) {\n spinner.fail(`File not found: ${filePath}`);\n process.exit(1);\n }\n\n const audioData = new Uint8Array(fs.readFileSync(filePath));\n spinner.text = `Transcribing ${path.basename(filePath)}...`;\n\n // Transcribe\n const result = await g.transcribe(audioData, {\n language: opts.language,\n timestamps: opts.timestamps,\n onProgress: (p) => {\n spinner.text = p.status;\n },\n });\n\n spinner.succeed(\n `Transcribed ${result.duration.toFixed(1)}s audio in ${result.totalTime.toFixed(0)}ms`,\n );\n\n // Output\n console.log();\n if (opts.timestamps && result.segments) {\n for (const seg of result.segments) {\n console.log(chalk.gray(`[${seg.start.toFixed(1)}s - ${seg.end.toFixed(1)}s]`), seg.text);\n }\n } else {\n console.log(result.text);\n }\n\n // Save to file\n if (opts.output) {\n let content = result.text;\n if (opts.timestamps && result.segments) {\n content = result.segments\n .map((seg) => `[${seg.start.toFixed(1)}s - ${seg.end.toFixed(1)}s] ${seg.text}`)\n .join(\"\\n\");\n }\n fs.writeFileSync(opts.output, content);\n console.log(chalk.green(`\\n💾 Saved to: ${opts.output}`));\n }\n\n await g.dispose();\n } catch (e: any) {\n spinner.fail(\"Error\");\n console.error(chalk.red(e.message));\n process.exit(1);\n }\n });\n\n/**\n * Convert Float32Array audio to WAV buffer\n */\nfunction audioToWav(audio: Float32Array, sampleRate: number): Buffer {\n const numChannels = 1;\n const bitsPerSample = 16;\n const byteRate = (sampleRate * numChannels * bitsPerSample) / 8;\n const blockAlign = (numChannels * bitsPerSample) / 8;\n const dataSize = audio.length * 2;\n const fileSize = 36 + dataSize;\n\n const buffer = Buffer.alloc(44 + dataSize);\n let offset = 0;\n\n buffer.write(\"RIFF\", offset);\n offset += 4;\n buffer.writeUInt32LE(fileSize, offset);\n offset += 4;\n buffer.write(\"WAVE\", offset);\n offset += 4;\n buffer.write(\"fmt \", offset);\n offset += 4;\n buffer.writeUInt32LE(16, offset);\n offset += 4;\n buffer.writeUInt16LE(1, offset);\n offset += 2;\n buffer.writeUInt16LE(numChannels, offset);\n offset += 2;\n buffer.writeUInt32LE(sampleRate, offset);\n offset += 4;\n buffer.writeUInt32LE(byteRate, offset);\n offset += 4;\n buffer.writeUInt16LE(blockAlign, offset);\n offset += 2;\n buffer.writeUInt16LE(bitsPerSample, offset);\n offset += 2;\n buffer.write(\"data\", offset);\n offset += 4;\n buffer.writeUInt32LE(dataSize, offset);\n offset += 4;\n\n for (let i = 0; i < audio.length; i++) {\n const sample = Math.max(-1, Math.min(1, audio[i]));\n const int16 = sample < 0 ? sample * 32768 : sample * 32767;\n buffer.writeInt16LE(Math.round(int16), offset);\n offset += 2;\n }\n\n return buffer;\n}\n\n// ============================================\n// Serve command\n// ============================================\n\nprogram\n .command(\"serve\")\n .description(\"Start Gerbil server (use --mcp or --http for CLI, otherwise opens REPL)\")\n .option(\"-m, --model <id>\", \"Model to use\", \"qwen3-0.6b\")\n .option(\"-p, --port <port>\", \"HTTP port\", \"3000\")\n .option(\"--mcp\", \"Start MCP server (stdio)\")\n .option(\"--http\", \"Start HTTP server (CLI mode)\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n // If no specific server flag, open REPL serve view\n if (!(opts.mcp || opts.http)) {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"serve\", forcedDevice });\n return;\n }\n if (opts.mcp) {\n await startMCPServer({ model: opts.model });\n } else {\n // HTTP server\n const { Gerbil } = await import(\"../core/gerbil.js\");\n const g = new Gerbil();\n\n const spinner = ora(\"Loading model...\").start();\n await g.loadModel(opts.model);\n spinner.succeed(\"Model loaded\");\n\n // Simple HTTP server\n const http = await import(\"node:http\");\n\n const server = http.createServer(async (req, res) => {\n if (req.method === \"POST\" && req.url === \"/generate\") {\n let body = \"\";\n req.on(\"data\", (chunk) => (body += chunk));\n req.on(\"end\", async () => {\n try {\n const { prompt, ...opts } = JSON.parse(body);\n const result = await g.generate(prompt, opts);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (e) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: String(e) }));\n }\n });\n } else if (req.method === \"GET\" && req.url === \"/info\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(g.getInfo()));\n } else {\n res.writeHead(404);\n res.end(\"Not found\");\n }\n });\n\n server.listen(Number.parseInt(opts.port, 10), () => {});\n }\n });\n\n// ============================================\n// Models command\n// ============================================\n\nprogram\n .command(\"models\")\n .description(\"List available models\")\n .option(\"--search <query>\", \"Search models\")\n .action(async (opts) => {\n let models = Object.values(BUILTIN_MODELS);\n\n if (opts.search) {\n const q = opts.search.toLowerCase();\n models = models.filter(\n (m) => m.id.toLowerCase().includes(q) || m.description.toLowerCase().includes(q),\n );\n }\n\n for (const m of models) {\n if (m.supportsThinking) {\n }\n }\n });\n\n// ============================================\n// Info command\n// ============================================\n\nprogram\n .command(\"info\")\n .description(\"Show system and model info (use --cli for text output, otherwise opens REPL)\")\n .option(\"-m, --model <id>\", \"Model to load and show info for\", \"qwen3-0.6b\")\n .option(\"--cli\", \"Output text info instead of opening REPL\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n // If no --cli flag, open REPL info view\n if (!opts.cli) {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"info\", forcedDevice });\n return;\n }\n\n const g = new Gerbil();\n const spinner = ora(`Loading ${chalk.cyan(opts.model)}...`).start();\n\n try {\n await g.loadModel(opts.model, {\n onProgress: (p) => {\n spinner.text = p.progress ? `${p.status} (${p.progress}%)` : p.status;\n },\n });\n spinner.stop();\n\n const _info = g.getInfo();\n\n await g.dispose();\n } catch (_e) {\n spinner.fail(\"Error loading model\");\n }\n });\n\n// ============================================\n// Cache command\n// ============================================\n\nprogram\n .command(\"cache\")\n .description(\"Show or manage model cache\")\n .option(\"--clean\", \"Remove all cached models\")\n .option(\"--older-than <days>\", \"Remove models older than N days\")\n .action(async (opts) => {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n const os = await import(\"node:os\");\n\n // Check transformers.js cache first (where Gerbil actually stores models)\n const nodeModulesCache = path.join(\n process.cwd(),\n \"node_modules\",\n \"@huggingface\",\n \"transformers\",\n \".cache\",\n );\n const cacheDir = fs.existsSync(nodeModulesCache)\n ? nodeModulesCache\n : process.env.HF_HOME ||\n process.env.TRANSFORMERS_CACHE ||\n path.join(os.homedir(), \".cache\", \"huggingface\", \"hub\");\n\n try {\n if (!fs.existsSync(cacheDir)) {\n return;\n }\n\n const entries = fs.readdirSync(cacheDir);\n const models: {\n name: string;\n size: number;\n mtime: Date;\n path: string;\n }[] = [];\n\n // Helper to get folder size\n const getSize = (dir: string): number => {\n let total = 0;\n try {\n const items = fs.readdirSync(dir);\n for (const item of items) {\n const itemPath = path.join(dir, item);\n const itemStat = fs.statSync(itemPath);\n if (itemStat.isDirectory()) {\n total += getSize(itemPath);\n } else {\n total += itemStat.size;\n }\n }\n } catch {}\n return total;\n };\n\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry);\n try {\n const entryStat = fs.statSync(entryPath);\n if (!entryStat.isDirectory()) {\n continue;\n }\n\n // Handle HuggingFace hub format (models--org--model)\n if (entry.startsWith(\"models--\")) {\n const modelName = entry.replace(\"models--\", \"\").replace(\"--\", \"/\");\n const size = getSize(entryPath);\n models.push({\n name: modelName,\n size,\n mtime: entryStat.mtime,\n path: entryPath,\n });\n } else {\n // Handle transformers.js format (org/model directories)\n const subEntries = fs.readdirSync(entryPath);\n for (const subEntry of subEntries) {\n const subPath = path.join(entryPath, subEntry);\n try {\n const subStat = fs.statSync(subPath);\n if (subStat.isDirectory()) {\n const modelName = `${entry}/${subEntry}`;\n const size = getSize(subPath);\n if (size > 0) {\n models.push({\n name: modelName,\n size,\n mtime: subStat.mtime,\n path: subPath,\n });\n }\n }\n } catch {}\n }\n }\n } catch {}\n }\n\n if (models.length === 0) {\n return;\n }\n\n // Handle clean options\n if (opts.clean || opts.olderThan) {\n const daysThreshold = opts.olderThan ? Number.parseInt(opts.olderThan, 10) : 0;\n const cutoffDate = new Date(Date.now() - daysThreshold * 24 * 60 * 60 * 1000);\n\n let removed = 0;\n let _removedSize = 0;\n\n for (const model of models) {\n if (opts.clean || model.mtime < cutoffDate) {\n try {\n fs.rmSync(model.path, { recursive: true, force: true });\n removed += 1;\n _removedSize += model.size;\n } catch (_e) {}\n }\n }\n\n if (removed > 0) {\n } else {\n }\n return;\n }\n let _totalSize = 0;\n\n for (const model of models.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())) {\n const _timeAgo = formatTimeAgo(model.mtime);\n const _sizeStr = formatSize(model.size).padStart(8);\n _totalSize += model.size;\n }\n } catch (_e) {}\n });\n\n// Import shared utilities from repl/utils\nimport { formatBytes, formatTimeAgo } from \"./repl/utils.js\";\n\n// Alias for CLI usage\nconst formatSize = formatBytes;\n\n// ============================================\n// Cleanup command (kill zombie Chrome pages)\n// ============================================\n\nprogram\n .command(\"cleanup\")\n .description(\"Clean up zombie Chrome pages and free memory\")\n .option(\"--kill-browser\", \"Also kill the shared Chrome browser (forces restart on next use)\")\n .option(\"--force\", \"Force kill all Gerbil Chrome processes (use if --kill-browser fails)\")\n .action(async (opts) => {\n const { ChromeGPUBackend } = await import(\"../core/chrome-backend.js\");\n const { execSync } = await import(\"node:child_process\");\n\n console.log(chalk.cyan(\"\\nChecking Chrome backend status...\\n\"));\n\n // Check for orphan processes using ps (works even without WS connection)\n let orphanPids: number[] = [];\n try {\n const psOutput = execSync('ps aux | grep \"gerbil/chrome-cache\" | grep -v grep', {\n encoding: \"utf-8\",\n });\n const lines = psOutput.trim().split(\"\\n\").filter(Boolean);\n orphanPids = lines.map((line) => {\n const parts = line.trim().split(/\\s+/);\n return Number.parseInt(parts[1], 10);\n });\n } catch {\n // No orphan processes\n }\n\n if (opts.force && orphanPids.length > 0) {\n const spinner = ora(`Force killing ${orphanPids.length} Gerbil Chrome processes...`).start();\n try {\n execSync('pkill -f \"gerbil/chrome-cache\"', { stdio: \"ignore\" });\n spinner.succeed(`Force killed ${orphanPids.length} process(es)`);\n } catch {\n spinner.fail(\"Failed to kill processes\");\n }\n console.log();\n return;\n }\n\n const status = ChromeGPUBackend.getGlobalBrowserStatus();\n\n if (!status.running) {\n if (orphanPids.length > 0) {\n console.log(\n chalk.yellow(\n `Found ${orphanPids.length} orphan Chrome process(es) but no WS connection.`,\n ),\n );\n console.log(chalk.gray(\"Use --force to kill them: gerbil cleanup --force\"));\n } else {\n console.log(chalk.gray(\"No Chrome backend running.\"));\n }\n return;\n }\n\n console.log(`Chrome PID: ${chalk.yellow(status.pid)}`);\n console.log(`Server port: ${chalk.yellow(status.port)}`);\n console.log(`Active pages: ${chalk.yellow(status.activePagesCount)}/${status.maxPages}`);\n\n // Get page info\n const pageInfo = await ChromeGPUBackend.getAllChromePages();\n if (pageInfo.length > 0) {\n console.log(chalk.cyan(\"\\nGerbil pages:\"));\n for (let i = 0; i < pageInfo.length; i++) {\n const p = pageInfo[i];\n const memStr = p.memory ? `${p.memory.usedGB.toFixed(2)}GB` : \"?\";\n const owner = p.isOurs ? chalk.green(\"(this process)\") : chalk.yellow(\"(orphan)\");\n console.log(` [${i}] ${p.modelId || \"unknown\"} - ${memStr} ${owner}`);\n }\n }\n\n if (opts.killBrowser) {\n const spinner = ora(\"Killing Chrome browser...\").start();\n const result = await ChromeGPUBackend.killAllBackends();\n spinner.succeed(\n `Killed ${result.pagesKilled} page(s)${result.browserKilled ? \" and browser\" : \"\"}`,\n );\n } else {\n // Just clean up orphan pages\n const spinner = ora(\"Cleaning up orphan pages...\").start();\n let cleaned = 0;\n for (let i = pageInfo.length - 1; i >= 0; i--) {\n if (!pageInfo[i].isOurs) {\n const killed = await ChromeGPUBackend.killPageByIndex(i);\n if (killed) cleaned++;\n }\n }\n if (cleaned > 0) {\n spinner.succeed(`Cleaned up ${cleaned} orphan page(s)`);\n } else {\n spinner.info(\"No orphan pages to clean up\");\n }\n }\n\n console.log();\n });\n\n// ============================================\n// Bench command\n// ============================================\n\nprogram\n .command(\"bench\")\n .description(\"Benchmark model performance\")\n .option(\"-m, --model <id>\", \"Model to benchmark\", \"qwen3-0.6b\")\n .option(\"-n, --runs <n>\", \"Number of runs\", \"3\")\n .action(async (opts) => {\n const g = new Gerbil();\n const spinner = ora(`Loading ${chalk.cyan(opts.model)}...`).start();\n\n try {\n await g.loadModel(opts.model, {\n onProgress: (p) => {\n spinner.text = p.progress ? `${p.status} (${p.progress}%)` : p.status;\n },\n });\n spinner.succeed(\"Model loaded\");\n\n const runs = Number.parseInt(opts.runs, 10);\n const results: {\n tokPerSec: number;\n firstToken: number;\n tokens: number;\n }[] = [];\n const prompts = [\n \"Write a haiku about programming.\",\n \"Explain recursion briefly.\",\n \"List 3 benefits of TypeScript.\",\n ];\n\n for (let i = 0; i < runs; i += 1) {\n const prompt = prompts[i % prompts.length];\n const runSpinner = ora(`Run ${i + 1}/${runs}...`).start();\n\n const startTime = Date.now();\n let firstTokenTime = 0;\n let tokenCount = 0;\n\n for await (const _chunk of g.stream(prompt, { maxTokens: 100 })) {\n if (tokenCount === 0) {\n firstTokenTime = Date.now() - startTime;\n }\n tokenCount += 1;\n }\n\n const totalTime = Date.now() - startTime;\n const tokPerSec = Math.round((tokenCount / totalTime) * 1000);\n\n results.push({\n tokPerSec,\n firstToken: firstTokenTime,\n tokens: tokenCount,\n });\n runSpinner.succeed(\n `Run ${i + 1}: ${chalk.cyan(tokPerSec)} tok/s, ${chalk.yellow(\n `${firstTokenTime}ms`,\n )} first token`,\n );\n }\n\n // Calculate averages\n const _avgTokPerSec = Math.round(\n results.reduce((a, r) => a + r.tokPerSec, 0) / results.length,\n );\n const _avgFirstToken = Math.round(\n results.reduce((a, r) => a + r.firstToken, 0) / results.length,\n );\n\n await g.dispose();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Update command\n// ============================================\n\nprogram\n .command(\"update\")\n .description(\"Update Gerbil to the latest version\")\n .action(async () => {\n const { checkForUpdate, installUpdate, CURRENT_VERSION } = await import(\n \"./repl/auto-update.js\"\n );\n\n const spinner = ora(\"Checking for updates...\").start();\n\n try {\n const check = await checkForUpdate();\n\n if (!check.updateAvailable) {\n spinner.succeed(chalk.green(`Already on latest version (v${CURRENT_VERSION})`));\n return;\n }\n\n spinner.info(chalk.cyan(`Update available: v${CURRENT_VERSION} → v${check.latestVersion}`));\n\n const updateSpinner = ora(`Installing v${check.latestVersion}...`).start();\n const result = await installUpdate();\n\n if (result.success) {\n updateSpinner.succeed(\n chalk.green(`✅ Updated to v${result.version || check.latestVersion}`),\n );\n } else {\n updateSpinner.fail(chalk.red(\"Update failed\"));\n process.exit(1);\n }\n } catch (_e) {\n spinner.fail(\"Update check failed\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Parse and run\n// ============================================\n\n// Check for updates after command completes (non-blocking)\nconst originalParse = program.parse.bind(program);\nprogram.parse = (...args) => {\n const result = originalParse(...args);\n\n // Check for updates in background (don't block)\n checkForUpdateCLI().catch(() => {\n // Silent fail\n });\n\n return result;\n};\n\nasync function checkForUpdateCLI() {\n const { checkForUpdate, CURRENT_VERSION } = await import(\"./repl/auto-update.js\");\n const check = await checkForUpdate();\n\n if (check.updateAvailable) {\n }\n}\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["models: CachedModelInfo[]","result: { sizeBytes?: number; contextLength?: number }","files: { path: string; size?: number; lfs?: { size?: number } }[]","devices: Record<string, { avgTokPerSec: number; runs: number }>","bests","converted: BenchmarkResult[]","newResult: BenchmarkResult","args: string[]","elements: React.ReactNode[]","codeBlockContent: string[]","parts: React.ReactNode[]","exec","err: any","supportsVision","chunks: string[]","join","tmpdir","copyToClipboard","cmd: string","LANG_EXTENSIONS: Record<string, string>","cmd: string","STEPS","input","EXAMPLES: Record<CapabilityId, Partial<Record<FrameworkId, Example>>>","err: any","metadata: Record<string, { sizeBytes?: number; contextLength?: number }>","info: HFModelDetails","models: HFModel[]","e: any","tabOrder: Array<\"preset\" | \"voice\" | \"huggingface\">","modes: SortMode[]","filters: SizeFilter[]","models: Array<{\n id: string;\n name: string;\n hfId: string;\n size: string;\n contextLength?: number;\n speed?: string;\n isCached: boolean;\n isRecommended: boolean;\n lastUsed?: string;\n benchStats?: { avgTokPerSec: number };\n }>","SKILL_DETAILS: Record<\n string,\n {\n emoji: string;\n what: string;\n example: string;\n cliExample?: string;\n inputHint: string;\n isVision?: boolean;\n }\n>","parsedInput: Record<string, unknown>","result","allTools: ToolInfo[]","toolPath: string | undefined","params: any","ctx: ViewContext","col","pendingCleanup: Promise<void> | null","sigintTimer: NodeJS.Timeout | null","skills.commit","skills.summarize","skills.explain","skills.review","fs","os","path","e: any","Gerbil","opts","models: {\n name: string;\n size: number;\n mtime: Date;\n path: string;\n }[]","orphanPids: number[]","execSync","results: {\n tokPerSec: number;\n firstToken: number;\n tokens: number;\n }[]","checkForUpdate","CURRENT_VERSION","installUpdate"],"sources":["../package.json","../src/cli/repl/auto-update.ts","../src/cli/repl/utils.ts","../src/cli/repl/views/BenchmarkView.tsx","../src/core/microphone.ts","../src/cli/repl/views/ChatView.tsx","../src/cli/repl/views/CodeView.tsx","../src/cli/repl/views/CreateSkillView.tsx","../src/cli/repl/views/CreateToolView.tsx","../src/cli/repl/views/FrameworksView.tsx","../src/cli/repl/views/InfoView.tsx","../src/cli/repl/views/LoadingView.tsx","../src/cli/repl/views/ModelView.tsx","../src/cli/repl/views/ServeView.tsx","../src/cli/repl/views/SkillsView.tsx","../src/cli/repl/views/ToolsView.tsx","../src/cli/repl/App.tsx","../src/cli/repl/index.tsx","../src/cli/index.ts"],"sourcesContent":["{\n \"name\": \"@tryhamster/gerbil\",\n \"version\": \"1.0.0-rc.10\",\n \"description\": \"Local LLM inference for Node.js. GPU-accelerated. Zero config. Works standalone or with Vercel AI SDK.\",\n \"type\": \"module\",\n \"main\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.mts\",\n \"bin\": {\n \"gerbil\": \"./bin/cli.js\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.mjs\",\n \"types\": \"./dist/index.d.mts\"\n },\n \"./skills\": {\n \"import\": \"./dist/skills/index.mjs\",\n \"types\": \"./dist/skills/index.d.mts\"\n },\n \"./ai\": {\n \"import\": \"./dist/integrations/ai-sdk.mjs\",\n \"types\": \"./dist/integrations/ai-sdk.d.mts\"\n },\n \"./react\": {\n \"import\": \"./dist/frameworks/react.mjs\",\n \"types\": \"./dist/frameworks/react.d.mts\"\n },\n \"./next\": {\n \"import\": \"./dist/frameworks/next.mjs\",\n \"types\": \"./dist/frameworks/next.d.mts\"\n },\n \"./express\": {\n \"import\": \"./dist/frameworks/express.mjs\",\n \"types\": \"./dist/frameworks/express.d.mts\"\n },\n \"./fastify\": {\n \"import\": \"./dist/frameworks/fastify.mjs\",\n \"types\": \"./dist/frameworks/fastify.d.mts\"\n },\n \"./hono\": {\n \"import\": \"./dist/frameworks/hono.mjs\",\n \"types\": \"./dist/frameworks/hono.d.mts\"\n },\n \"./trpc\": {\n \"import\": \"./dist/frameworks/trpc.mjs\",\n \"types\": \"./dist/frameworks/trpc.d.mts\"\n },\n \"./langchain\": {\n \"import\": \"./dist/integrations/langchain.mjs\",\n \"types\": \"./dist/integrations/langchain.d.mts\"\n },\n \"./llamaindex\": {\n \"import\": \"./dist/integrations/llamaindex.mjs\",\n \"types\": \"./dist/integrations/llamaindex.d.mts\"\n },\n \"./mcp\": {\n \"import\": \"./dist/integrations/mcp.mjs\",\n \"types\": \"./dist/integrations/mcp.d.mts\"\n },\n \"./mcp-client\": {\n \"import\": \"./dist/integrations/mcp-client.mjs\",\n \"types\": \"./dist/integrations/mcp-client.d.mts\"\n },\n \"./browser\": {\n \"import\": \"./dist/browser/index.js\",\n \"types\": \"./dist/browser/index.d.ts\"\n }\n },\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsx src/cli/index.ts\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"ultracite check\",\n \"fix\": \"ultracite fix\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"prepublishOnly\": \"pnpm build\",\n \"changeset\": \"changeset\",\n \"version\": \"changeset version\",\n \"release\": \"pnpm publish && changeset tag\",\n \"prepare\": \"lefthook install\"\n },\n \"dependencies\": {\n \"@huggingface/hub\": \"^2.7.1\",\n \"chalk\": \"^5.3.0\",\n \"cli-progress\": \"^3.12.0\",\n \"commander\": \"^12.1.0\",\n \"ora\": \"^8.0.1\",\n \"puppeteer-core\": \"^24.31.0\",\n \"react\": \"^19.0.0\",\n \"webgpu\": \"^0.3.8\",\n \"zod\": \"^3.23.0\"\n },\n \"peerDependencies\": {\n \"@ai-sdk/provider\": \">=2.0.0\",\n \"@modelcontextprotocol/sdk\": \">=0.5.0\",\n \"@trpc/server\": \">=10.0.0\",\n \"ai\": \">=5.0.0\",\n \"express\": \">=4.0.0\",\n \"fastify\": \">=4.0.0\",\n \"hono\": \">=4.0.0\",\n \"langchain\": \">=0.1.0\",\n \"llamaindex\": \">=0.1.0\",\n \"next\": \">=14.0.0\"\n },\n \"peerDependenciesMeta\": {\n \"@ai-sdk/provider\": {\n \"optional\": true\n },\n \"@modelcontextprotocol/sdk\": {\n \"optional\": true\n },\n \"@trpc/server\": {\n \"optional\": true\n },\n \"ai\": {\n \"optional\": true\n },\n \"express\": {\n \"optional\": true\n },\n \"fastify\": {\n \"optional\": true\n },\n \"hono\": {\n \"optional\": true\n },\n \"langchain\": {\n \"optional\": true\n },\n \"llamaindex\": {\n \"optional\": true\n },\n \"next\": {\n \"optional\": true\n },\n \"react\": {\n \"optional\": true\n }\n },\n \"devDependencies\": {\n \"@ai-sdk/provider\": \"^2.0.0\",\n \"@biomejs/biome\": \"^2.3.8\",\n \"@huggingface/transformers\": \"^3.8.0\",\n \"kokoro-js\": \"^1.2.1\",\n \"onnxruntime-web\": \"^1.21.0-dev.20250114-228dd16893\",\n \"@changesets/changelog-github\": \"^0.5.1\",\n \"@changesets/cli\": \"^2.28.1\",\n \"@types/cli-progress\": \"^3.11.6\",\n \"@types/express\": \"^4.17.21\",\n \"@types/ink-big-text\": \"^1.2.4\",\n \"@types/node\": \"^20.14.0\",\n \"@types/react\": \"^19.0.0\",\n \"ai\": \"^5.0.0\",\n \"express\": \"^4.19.0\",\n \"ink\": \"^6.5.1\",\n \"ink-big-text\": \"^2.0.0\",\n \"ink-gradient\": \"^3.0.0\",\n \"ink-select-input\": \"^6.2.0\",\n \"ink-spinner\": \"^5.0.0\",\n \"ink-text-input\": \"^6.0.0\",\n \"lefthook\": \"^2.0.5\",\n \"tsdown\": \"^0.17.0-beta.3\",\n \"tsx\": \"^4.15.0\",\n \"typescript\": \"^5.4.5\",\n \"ultracite\": \"6.3.8\",\n \"vitest\": \"^1.6.0\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"keywords\": [\n \"llm\",\n \"local\",\n \"gpu\",\n \"webgpu\",\n \"inference\",\n \"ai-sdk\",\n \"transformers\",\n \"qwen\",\n \"thinking\",\n \"mcp\",\n \"langchain\",\n \"nextjs\",\n \"express\",\n \"cli\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Hamster <hello@tryhamster.com>\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/gethamster/gerbil.git\"\n },\n \"homepage\": \"https://github.com/gethamster/gerbil#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/gethamster/gerbil/issues\"\n },\n \"files\": [\n \"dist\",\n \"bin\",\n \"docs\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst execAsync = promisify(exec);\n\nconst CURRENT_VERSION = \"1.0.0\";\nconst PACKAGE_NAME = \"@tryhamster/gerbil\";\n\nexport type UpdateCheckResult = {\n updateAvailable: boolean;\n currentVersion: string;\n latestVersion?: string;\n error?: string;\n};\n\nexport type UpdateInstallResult = {\n success: boolean;\n version?: string;\n error?: string;\n};\n\n/**\n * Check if an update is available (non-blocking, no install)\n */\nexport async function checkForUpdate(): Promise<UpdateCheckResult> {\n try {\n // Check latest version from npm\n const { stdout } = await execAsync(`npm view ${PACKAGE_NAME} version`, {\n timeout: 5000,\n });\n const latestVersion = stdout.trim();\n\n // Compare versions\n if (compareVersions(latestVersion, CURRENT_VERSION) > 0) {\n return {\n updateAvailable: true,\n currentVersion: CURRENT_VERSION,\n latestVersion,\n };\n }\n\n return {\n updateAvailable: false,\n currentVersion: CURRENT_VERSION,\n latestVersion,\n };\n } catch (error) {\n // Silent fail - network issues, npm not available, etc.\n return {\n updateAvailable: false,\n currentVersion: CURRENT_VERSION,\n error: `Update check failed: ${error}`,\n };\n }\n}\n\n/**\n * Install the latest version\n */\nexport async function installUpdate(): Promise<UpdateInstallResult> {\n try {\n const { stdout } = await execAsync(`npm install -g ${PACKAGE_NAME}@latest`, {\n timeout: 60_000, // 60s for download + install\n });\n\n // Try to extract version from output\n const versionMatch = stdout.match(/\\+ @tryhamster\\/gerbil@([\\d.]+)/);\n const version = versionMatch ? versionMatch[1] : undefined;\n\n return {\n success: true,\n version,\n };\n } catch (error) {\n return {\n success: false,\n error: `Failed to install update: ${error}`,\n };\n }\n}\n\n/**\n * Compare two semver versions\n * Returns: 1 if a > b, -1 if a < b, 0 if equal\n */\nexport function compareVersions(a: string, b: string): number {\n const aParts = a.split(\".\").map(Number);\n const bParts = b.split(\".\").map(Number);\n\n for (let i = 0; i < 3; i += 1) {\n const aPart = aParts[i] || 0;\n const bPart = bParts[i] || 0;\n if (aPart > bPart) {\n return 1;\n }\n if (aPart < bPart) {\n return -1;\n }\n }\n return 0;\n}\n\nexport { CURRENT_VERSION };\n","/**\n * Shared utilities for the REPL\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { getChromeCachedModels, refreshCachedModelSizes } from \"../../core/chrome-backend.js\";\n\n/**\n * Format bytes to human readable string\n */\nexport function formatBytes(bytes: number): string {\n if (!bytes || bytes === 0) {\n return \"\";\n }\n if (bytes >= 1e9) {\n return `${(bytes / 1e9).toFixed(1)}GB`;\n }\n if (bytes >= 1e6) {\n return `${(bytes / 1e6).toFixed(0)}MB`;\n }\n if (bytes >= 1e3) {\n return `${(bytes / 1e3).toFixed(0)}KB`;\n }\n return `${bytes}B`;\n}\n\n/**\n * Format time ago string\n */\nexport function formatTimeAgo(date: Date): string {\n const seconds = Math.floor((Date.now() - date.getTime()) / 1000);\n if (seconds < 60) {\n return \"just now\";\n }\n if (seconds < 3600) {\n return `${Math.floor(seconds / 60)}m ago`;\n }\n if (seconds < 86_400) {\n return `${Math.floor(seconds / 3600)}h ago`;\n }\n if (seconds < 604_800) {\n return `${Math.floor(seconds / 86_400)}d ago`;\n }\n return `${Math.floor(seconds / 604_800)}w ago`;\n}\n\n/**\n * Format download count\n */\nexport function formatDownloads(n: number): string {\n if (n >= 1_000_000) {\n return `${(n / 1_000_000).toFixed(1)}M dl`;\n }\n if (n >= 1000) {\n return `${(n / 1000).toFixed(1)}K dl`;\n }\n return `${n} dl`;\n}\n\n/**\n * Get Gerbil models directory (local project models)\n */\nexport function getGerbilModelsDir(): string {\n return path.join(process.cwd(), \".gerbil\", \"models\");\n}\n\n/**\n * Get transformers.js cache directory (downloaded HF models)\n */\nexport function getTransformersCacheDir(): string {\n const nodeModulesCache = path.join(\n process.cwd(),\n \"node_modules\",\n \"@huggingface\",\n \"transformers\",\n \".cache\",\n );\n if (fs.existsSync(nodeModulesCache)) {\n return nodeModulesCache;\n }\n return (\n process.env.HF_HOME ||\n process.env.TRANSFORMERS_CACHE ||\n path.join(os.homedir(), \".cache\", \"huggingface\", \"hub\")\n );\n}\n\n/**\n * Get all cache directories that might contain models\n */\nexport function getAllCacheDirs(): string[] {\n return [getGerbilModelsDir(), getTransformersCacheDir()].filter((dir) => fs.existsSync(dir));\n}\n\n/**\n * Check if a model is cached locally (checks gerbil, transformers, and Chrome cache)\n */\nexport function isModelCached(hfId: string): boolean {\n const [org, model] = hfId.split(\"/\");\n if (!(org && model)) {\n return false;\n }\n\n // Check Chrome IndexedDB cache first (tracked in JSON file)\n try {\n const chromeCached = getChromeCachedModels();\n if (chromeCached.some((m) => m.modelId === hfId)) {\n return true;\n }\n } catch {\n // Chrome cache tracking not available\n }\n\n // Check filesystem cache locations\n const cacheDirs = [getGerbilModelsDir(), getTransformersCacheDir()];\n\n for (const cacheDir of cacheDirs) {\n const possiblePaths = [\n path.join(cacheDir, org, model),\n path.join(cacheDir, hfId.replace(\"/\", \"--\")),\n path.join(cacheDir, `models--${org}--${model}`),\n ];\n\n for (const p of possiblePaths) {\n try {\n if (fs.existsSync(p)) {\n const stat = fs.statSync(p);\n if (stat.isDirectory()) {\n const files = fs.readdirSync(p, { recursive: true });\n const hasOnnxModel = files.some((f: any) => {\n const fname = String(f).toLowerCase();\n return (\n fname.endsWith(\".onnx\") ||\n fname.endsWith(\"model.safetensors\") ||\n fname.includes(\"decoder_model\") ||\n fname.includes(\"encoder_model\")\n );\n });\n if (hasOnnxModel) {\n return true;\n }\n }\n }\n } catch {}\n }\n }\n\n return false;\n}\n\n/** Detailed model info from cache */\nexport type CachedModelInfo = {\n name: string;\n modelSize: string; // ONNX model files only\n totalSize: string; // All files (including shaders, config, etc.)\n lastUsed: string;\n location: string;\n contextLength?: number; // Model's max context window\n};\n\n/**\n * Scan a single cache directory for models\n */\nfunction scanCacheDir(cacheDir: string, models: CachedModelInfo[]): number {\n let totalBytes = 0;\n\n try {\n if (!fs.existsSync(cacheDir)) {\n return 0;\n }\n\n const entries = fs.readdirSync(cacheDir);\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry);\n const entryStat = fs.statSync(entryPath);\n\n if (!entryStat.isDirectory()) {\n continue;\n }\n\n let modelName = entry;\n if (entry.startsWith(\"models--\")) {\n modelName = entry.replace(\"models--\", \"\").replace(\"--\", \"/\");\n }\n\n const subEntries = fs.readdirSync(entryPath);\n for (const subEntry of subEntries) {\n const subPath = path.join(entryPath, subEntry);\n try {\n const subStat = fs.statSync(subPath);\n if (subStat.isDirectory()) {\n const fullModelName = `${entry}/${subEntry}`;\n let totalSize = 0;\n let modelFileSize = 0;\n try {\n const files = fs.readdirSync(subPath, { recursive: true });\n for (const file of files) {\n try {\n const filePath = path.join(subPath, String(file));\n const fileStat = fs.statSync(filePath);\n if (fileStat.isFile()) {\n totalSize += fileStat.size;\n const fname = String(file).toLowerCase();\n if (fname.endsWith(\".onnx\") || fname.endsWith(\".safetensors\")) {\n modelFileSize += fileStat.size;\n }\n }\n } catch {}\n }\n } catch {\n totalSize = subStat.size;\n }\n\n if (totalSize > 0) {\n totalBytes += totalSize;\n const lastUsed = formatTimeAgo(subStat.mtime);\n models.push({\n name: fullModelName,\n modelSize: formatBytes(modelFileSize || totalSize),\n totalSize: formatBytes(totalSize),\n lastUsed,\n location: cacheDir,\n });\n }\n }\n } catch {}\n }\n\n const hasModelFiles = subEntries.some((f) => f.endsWith(\".onnx\") || f.endsWith(\".json\"));\n if (hasModelFiles) {\n let totalSize = 0;\n let modelFileSize = 0;\n try {\n const files = fs.readdirSync(entryPath, { recursive: true });\n for (const file of files) {\n try {\n const filePath = path.join(entryPath, String(file));\n const fileStat = fs.statSync(filePath);\n if (fileStat.isFile()) {\n totalSize += fileStat.size;\n const fname = String(file).toLowerCase();\n if (fname.endsWith(\".onnx\") || fname.endsWith(\".safetensors\")) {\n modelFileSize += fileStat.size;\n }\n }\n } catch {}\n }\n } catch {\n totalSize = entryStat.size;\n }\n\n totalBytes += totalSize;\n const lastUsed = formatTimeAgo(entryStat.mtime);\n models.push({\n name: modelName,\n modelSize: formatBytes(modelFileSize || totalSize),\n totalSize: formatBytes(totalSize),\n lastUsed,\n location: cacheDir,\n });\n }\n }\n } catch {}\n\n return totalBytes;\n}\n\n/**\n * Get cache info from all cache locations\n */\nexport function getCacheInfo(): {\n locations: string[];\n totalSize: string;\n models: CachedModelInfo[];\n} {\n const gerbilDir = getGerbilModelsDir();\n const transformersDir = getTransformersCacheDir();\n const models: CachedModelInfo[] = [];\n\n let totalBytes = 0;\n totalBytes += scanCacheDir(gerbilDir, models);\n totalBytes += scanCacheDir(transformersDir, models);\n\n // Add Chrome-cached models (stored in IndexedDB, tracked in JSON)\n let hasMissingSizes = false;\n try {\n const chromeCached = getChromeCachedModels();\n for (const entry of chromeCached) {\n // Check if already in list from filesystem scan\n const exists = models.some(\n (m) =>\n m.name === entry.modelId ||\n m.name.replace(\"--\", \"/\") === entry.modelId ||\n entry.modelId.replace(\"/\", \"--\") === m.name,\n );\n\n if (!exists) {\n const lastUsed = entry.lastUsed ? formatTimeAgo(new Date(entry.lastUsed)) : \"unknown\";\n if (!(entry.sizeBytes && entry.contextLength)) {\n hasMissingSizes = true;\n }\n models.push({\n name: entry.modelId,\n modelSize: entry.sizeBytes ? formatBytes(entry.sizeBytes) : \"~\",\n totalSize: entry.sizeBytes ? formatBytes(entry.sizeBytes) : \"~\",\n lastUsed,\n location: \"Chrome IndexedDB\",\n contextLength: entry.contextLength,\n });\n if (entry.sizeBytes) {\n totalBytes += entry.sizeBytes;\n }\n }\n }\n\n // Trigger background refresh for missing sizes\n if (hasMissingSizes) {\n refreshCachedModelSizes().catch(() => {});\n }\n } catch {\n // Chrome cache tracking not available\n }\n\n // Get active locations that have models\n const activeLocations = [...new Set(models.map((m) => m.location))];\n\n return {\n locations: activeLocations.length > 0 ? activeLocations : [gerbilDir, transformersDir],\n totalSize: formatBytes(totalBytes),\n models: models.sort((a, b) => a.name.localeCompare(b.name)),\n };\n}\n\n/**\n * Get system memory info\n */\nexport function getMemoryInfo(): {\n total: string;\n used: string;\n free: string;\n percentUsed: number;\n} {\n const totalMem = os.totalmem();\n const freeMem = os.freemem();\n const usedMem = totalMem - freeMem;\n const percentUsed = Math.round((usedMem / totalMem) * 100);\n\n return {\n total: formatBytes(totalMem),\n used: formatBytes(usedMem),\n free: formatBytes(freeMem),\n percentUsed,\n };\n}\n\n/**\n * OSC 8 hyperlink helper (clickable links in terminal)\n */\nexport function hyperlink(url: string, text: string): string {\n return `\\x1b]8;;${url}\\x07${text}\\x1b]8;;\\x07`;\n}\n\n/**\n * Clean AI response - remove think tags and trim\n */\nexport function cleanResponse(text: string): string {\n return text\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\")\n .replace(/<\\/?think>/g, \"\")\n .trim();\n}\n\n/**\n * Preset models with known performance\n */\nexport const PRESET_MODELS = [\n {\n id: \"qwen3-0.6b\",\n name: \"Qwen3 0.6B\",\n size: \"~400MB\",\n speed: \"fastest\",\n hfId: \"onnx-community/Qwen3-0.6B-ONNX\",\n },\n {\n id: \"qwen2.5-0.5b\",\n name: \"Qwen2.5 0.5B\",\n size: \"~350MB\",\n speed: \"fast\",\n hfId: \"onnx-community/Qwen2.5-0.5B-Instruct\",\n },\n {\n id: \"qwen2.5-coder-0.5b\",\n name: \"Qwen2.5 Coder\",\n size: \"~400MB\",\n speed: \"fast\",\n hfId: \"onnx-community/Qwen2.5-Coder-0.5B-Instruct\",\n },\n {\n id: \"smollm2-360m\",\n name: \"SmolLM2 360M\",\n size: \"~250MB\",\n speed: \"fastest\",\n hfId: \"HuggingFaceTB/SmolLM2-360M-Instruct\",\n },\n {\n id: \"smollm2-135m\",\n name: \"SmolLM2 135M\",\n size: \"~100MB\",\n speed: \"fastest\",\n hfId: \"HuggingFaceTB/SmolLM2-135M-Instruct\",\n },\n {\n id: \"phi-3-mini\",\n name: \"Phi-3 Mini\",\n size: \"~2.1GB\",\n speed: \"medium\",\n hfId: \"microsoft/Phi-3-mini-4k-instruct-onnx\",\n },\n];\n\n/**\n * Fetch model metadata from HuggingFace (size and context length)\n */\nexport async function fetchModelMetadata(\n modelId: string,\n): Promise<{ sizeBytes?: number; contextLength?: number }> {\n const result: { sizeBytes?: number; contextLength?: number } = {};\n\n // Priority 1: config.json → max_position_embeddings (actual architectural limit)\n try {\n const configRes = await fetch(`https://huggingface.co/${modelId}/raw/main/config.json`);\n if (configRes.ok) {\n const config = await configRes.json();\n // Check root level first, then nested text_config (for multimodal models like Ministral)\n const textConfig = config.text_config || {};\n result.contextLength =\n config.max_position_embeddings ||\n textConfig.max_position_embeddings ||\n config.sliding_window ||\n textConfig.sliding_window ||\n config.max_seq_len ||\n config.max_sequence_length ||\n config.n_ctx ||\n config.n_positions;\n }\n } catch {\n // Continue to fallback\n }\n\n // Priority 2: tokenizer_config.json → model_max_length (fallback only)\n if (!result.contextLength) {\n try {\n const tokRes = await fetch(\n `https://huggingface.co/${modelId}/raw/main/tokenizer_config.json`,\n );\n if (tokRes.ok) {\n const tokConfig = await tokRes.json();\n // Only use if reasonable (< 1M tokens - anything larger is likely placeholder)\n if (tokConfig.model_max_length && tokConfig.model_max_length < 1e6) {\n result.contextLength = tokConfig.model_max_length;\n }\n }\n } catch {\n // Ignore\n }\n }\n\n // Fetch size from tree API (handles LFS files)\n try {\n const treeRes = await fetch(`https://huggingface.co/api/models/${modelId}/tree/main/onnx`);\n if (treeRes.ok) {\n const files: { path: string; size?: number; lfs?: { size?: number } }[] =\n await treeRes.json();\n\n // Get file size (LFS or regular)\n const getSize = (f: { size?: number; lfs?: { size?: number } }) => f.lfs?.size || f.size || 0;\n\n // Prefer q4f16 > q4 > fp16 > any .onnx file\n const q4f16 = files.find((f) => f.path.includes(\"q4f16\") && f.path.endsWith(\".onnx\"));\n const q4 = files.find(\n (f) => f.path.includes(\"q4\") && !f.path.includes(\"f16\") && f.path.endsWith(\".onnx\"),\n );\n const fp16 = files.find((f) => f.path.includes(\"fp16\") && f.path.endsWith(\".onnx\"));\n const anyOnnx = files.find((f) => f.path.endsWith(\".onnx\"));\n const bestFile = q4f16 || q4 || fp16 || anyOnnx;\n\n if (bestFile) {\n const baseName = bestFile.path.replace(\".onnx\", \"\");\n const relatedFiles = files.filter(\n (f) => f.path === bestFile.path || f.path.startsWith(`${baseName}.onnx_data`),\n );\n result.sizeBytes = relatedFiles.reduce((sum, f) => sum + getSize(f), 0);\n }\n }\n } catch {\n // Ignore tree fetch errors\n }\n\n return result;\n}\n\n/**\n * Session stats interface\n */\nexport type SessionStats = {\n prompts: number;\n tokensIn: number;\n tokensOut: number;\n totalTimeMs: number;\n startTime: number;\n benchResults: { model: string; tokPerSec: number }[];\n /** Last message tok/s */\n lastTokPerSec: number;\n /** Average tok/s across all messages */\n avgTokPerSec: number;\n};\n\n// ============================================\n// Benchmark Persistence\n// ============================================\n\nexport type StoredBenchmark = {\n timestamp: string;\n model: string;\n device: \"webgpu\" | \"cpu\" | \"wasm\";\n tokensPerSec: number;\n firstTokenMs: number;\n totalTokens: number;\n totalMs: number;\n};\n\nconst BENCHMARKS_FILE = path.join(os.homedir(), \".gerbil\", \"benchmarks.json\");\n\n/**\n * Get all stored benchmarks\n */\nexport function getStoredBenchmarks(): StoredBenchmark[] {\n try {\n if (!fs.existsSync(BENCHMARKS_FILE)) {\n return [];\n }\n const data = JSON.parse(fs.readFileSync(BENCHMARKS_FILE, \"utf-8\"));\n return data.benchmarks || [];\n } catch {\n return [];\n }\n}\n\n/**\n * Save a benchmark result\n */\nexport function saveBenchmark(result: {\n model: string;\n device: \"webgpu\" | \"cpu\" | \"wasm\";\n tokensPerSec: number;\n firstTokenMs: number;\n totalTokens: number;\n totalMs: number;\n}): void {\n try {\n const dir = path.dirname(BENCHMARKS_FILE);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const benchmarks = getStoredBenchmarks();\n benchmarks.push({\n ...result,\n timestamp: new Date().toISOString(),\n });\n\n // Keep last 1000 benchmarks\n const trimmed = benchmarks.slice(-1000);\n fs.writeFileSync(BENCHMARKS_FILE, JSON.stringify({ benchmarks: trimmed }, null, 2));\n } catch {\n // Ignore save errors\n }\n}\n\n/**\n * Get average benchmark stats for a model\n */\n/**\n * Normalize model ID for matching (extract core name)\n * e.g., \"HuggingFaceTB/SmolLM2-135M-Instruct\" -> \"smollm2-135m\"\n * \"onnx-community/Qwen3-0.6B-ONNX\" -> \"qwen3-0.6b\"\n * \"qwen3-0.6b\" -> \"qwen3-0.6b\"\n */\nfunction normalizeModelId(id: string): string {\n // Get the last part after /\n const name = id.split(\"/\").pop() || id;\n // Remove common suffixes and convert to lowercase\n return name\n .toLowerCase()\n .replace(/-instruct$/i, \"\")\n .replace(/-onnx$/i, \"\")\n .replace(/-chat$/i, \"\")\n .replace(/-it$/i, \"\");\n}\n\n/**\n * Process an image path (URL or local file) and return info for attaching\n * Supports: URLs (http/https), local paths (including ~ expansion)\n * Returns data URI for local files, original URL for remote images\n */\nexport function processImagePath(imagePath: string): {\n success: boolean;\n message: string;\n imageSource?: string;\n filename?: string;\n} {\n const trimmedPath = imagePath.trim();\n if (!trimmedPath) {\n return { success: false, message: \"\" };\n }\n\n try {\n // Check if it's a URL\n if (trimmedPath.startsWith(\"http://\") || trimmedPath.startsWith(\"https://\")) {\n const urlFilename = trimmedPath.split(\"/\").pop()?.split(\"?\")[0] || \"image\";\n return {\n success: true,\n message: `📎 Attached URL: ${urlFilename}`,\n imageSource: trimmedPath,\n filename: urlFilename,\n };\n }\n\n // Local file path - convert to data URI\n const expandedPath = trimmedPath.startsWith(\"~\")\n ? trimmedPath.replace(\"~\", process.env.HOME || \"\")\n : trimmedPath;\n\n if (!fs.existsSync(expandedPath)) {\n return { success: false, message: `❌ File not found: ${trimmedPath}` };\n }\n\n const ext = path.extname(expandedPath).toLowerCase();\n const mimeTypes: Record<string, string> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n };\n const mimeType = mimeTypes[ext] || \"image/png\";\n const imageData = fs.readFileSync(expandedPath);\n const base64 = imageData.toString(\"base64\");\n const dataUri = `data:${mimeType};base64,${base64}`;\n const filename = path.basename(expandedPath);\n\n return {\n success: true,\n message: `📎 Attached: ${filename}`,\n imageSource: dataUri,\n filename,\n };\n } catch (e) {\n return { success: false, message: `❌ Error loading image: ${e}` };\n }\n}\n\nexport function getModelBenchmarkStats(modelId: string): {\n avgTokPerSec: number;\n avgFirstToken: number;\n runs: number;\n devices: Record<string, { avgTokPerSec: number; runs: number }>;\n} | null {\n const benchmarks = getStoredBenchmarks();\n const normalizedId = normalizeModelId(modelId);\n\n const modelBenchmarks = benchmarks.filter((b) => {\n const normalizedBench = normalizeModelId(b.model);\n return (\n normalizedBench === normalizedId ||\n normalizedBench.includes(normalizedId) ||\n normalizedId.includes(normalizedBench)\n );\n });\n\n if (modelBenchmarks.length === 0) {\n return null;\n }\n\n const avgTokPerSec = Math.round(\n modelBenchmarks.reduce((a, b) => a + b.tokensPerSec, 0) / modelBenchmarks.length,\n );\n const avgFirstToken = Math.round(\n modelBenchmarks.reduce((a, b) => a + b.firstTokenMs, 0) / modelBenchmarks.length,\n );\n\n // Group by device\n const devices: Record<string, { avgTokPerSec: number; runs: number }> = {};\n for (const b of modelBenchmarks) {\n if (!devices[b.device]) {\n devices[b.device] = { avgTokPerSec: 0, runs: 0 };\n }\n devices[b.device].runs += 1;\n }\n for (const device of Object.keys(devices)) {\n const deviceBenchmarks = modelBenchmarks.filter((b) => b.device === device);\n devices[device].avgTokPerSec = Math.round(\n deviceBenchmarks.reduce((a, b) => a + b.tokensPerSec, 0) / deviceBenchmarks.length,\n );\n }\n\n return { avgTokPerSec, avgFirstToken, runs: modelBenchmarks.length, devices };\n}\n","import { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport type React from \"react\";\nimport { useEffect, useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\nimport { getStoredBenchmarks, type StoredBenchmark, saveBenchmark } from \"../utils.js\";\n\nexport type BenchmarkResult = {\n model: string;\n device: \"webgpu\" | \"cpu\" | \"wasm\";\n tokensPerSec: number;\n firstTokenMs: number;\n totalTokens: number;\n totalMs: number;\n};\n\nexport type BenchmarkStats = {\n runCount: number;\n lastResult?: BenchmarkResult;\n averages: Array<{\n model: string;\n device: string;\n avgTokPerSec: number;\n avgFirstToken: number;\n runs: number;\n }>;\n bests?: {\n fastestRun: BenchmarkResult;\n fastestFirstToken: BenchmarkResult;\n bestOverall: {\n model: string;\n device: string;\n avgTokPerSec: number;\n avgFirstToken: number;\n score: number;\n } | null;\n };\n};\n\ntype BenchmarkViewProps = {\n gerbil: Gerbil;\n model: string;\n disabled?: boolean;\n onResult?: (tokPerSec: number) => void;\n onSwitchModel?: () => void;\n onSwitchDevice?: (device: \"webgpu\" | \"cpu\") => void;\n onStatusChange?: (status: \"idle\" | \"running\" | \"done\", stats: BenchmarkStats) => void;\n benchmarkResults: BenchmarkResult[];\n setBenchmarkResults: React.Dispatch<React.SetStateAction<BenchmarkResult[]>>;\n benchmarkModels: string[];\n setBenchmarkModels: React.Dispatch<React.SetStateAction<string[]>>;\n};\n\nexport function BenchmarkView({\n gerbil,\n model,\n disabled = false,\n onResult,\n onSwitchModel,\n onSwitchDevice,\n onStatusChange,\n benchmarkResults,\n setBenchmarkResults,\n benchmarkModels,\n setBenchmarkModels,\n}: BenchmarkViewProps) {\n const [status, setStatus] = useState<\"idle\" | \"running\" | \"done\">(\"idle\");\n const [currentRun, setCurrentRun] = useState(benchmarkResults.length);\n const [showHistory, setShowHistory] = useState(false);\n const [historyData, setHistoryData] = useState<StoredBenchmark[]>([]);\n\n // Notify parent of status changes with full stats\n const updateStatus = (\n newStatus: \"idle\" | \"running\" | \"done\",\n result?: BenchmarkResult,\n allResults?: BenchmarkResult[],\n ) => {\n setStatus(newStatus);\n\n const results = allResults || benchmarkResults;\n\n // Calculate averages\n const resultKeys = [...new Set(results.map((r) => `${r.model}|${r.device}`))];\n const averages = resultKeys.map((key) => {\n const [m, d] = key.split(\"|\");\n const filtered = results.filter((r) => r.model === m && r.device === d);\n return {\n model: m,\n device: d,\n avgTokPerSec: Math.round(\n filtered.reduce((a, r) => a + r.tokensPerSec, 0) / filtered.length,\n ),\n avgFirstToken: Math.round(\n filtered.reduce((a, r) => a + r.firstTokenMs, 0) / filtered.length,\n ),\n runs: filtered.length,\n };\n });\n\n // Calculate bests\n const bests =\n results.length > 0\n ? (() => {\n const fastestRun = results.reduce((best, r) =>\n r.tokensPerSec > best.tokensPerSec ? r : best,\n );\n const fastestFirstToken = results.reduce((best, r) =>\n r.firstTokenMs < best.firstTokenMs ? r : best,\n );\n\n // Best overall: score = tok/s / (1 + firstTokenMs/100)\n const overallScores = averages.map((s) => ({\n ...s,\n score: s.avgTokPerSec / (1 + s.avgFirstToken / 100),\n }));\n const bestOverall =\n overallScores.length > 0\n ? overallScores.reduce((best, s) => (s.score > best.score ? s : best))\n : null;\n\n return { fastestRun, fastestFirstToken, bestOverall };\n })()\n : undefined;\n\n onStatusChange?.(newStatus, {\n runCount: currentRun + (newStatus === \"done\" ? 1 : 0),\n lastResult: result,\n averages,\n bests,\n });\n };\n\n useEffect(() => {\n if (!benchmarkModels.includes(model)) {\n setBenchmarkModels((prev) => [...prev.slice(-2), model]);\n }\n }, [model, benchmarkModels, setBenchmarkModels]);\n\n const currentDevice = gerbil.getDeviceMode();\n\n useInput((input, key) => {\n // Block all actions while disabled (one-liner generating) or running\n const blocked = disabled || status === \"running\";\n\n if (key.return && !blocked) {\n runBenchmark();\n }\n if ((input === \"r\" || input === \"R\") && !blocked) {\n setBenchmarkResults([]);\n setCurrentRun(0);\n setBenchmarkModels([model]);\n }\n if ((input === \"m\" || input === \"M\") && !blocked) {\n onSwitchModel?.();\n }\n // Toggle device mode with 'd'\n if ((input === \"d\" || input === \"D\") && !blocked) {\n const newDevice = currentDevice === \"webgpu\" ? \"cpu\" : \"webgpu\";\n onSwitchDevice?.(newDevice);\n }\n // Toggle history with 'h'\n if ((input === \"h\" || input === \"H\") && !blocked) {\n if (!showHistory) {\n // Load history when opening\n const stored = getStoredBenchmarks();\n setHistoryData(stored);\n }\n setShowHistory(!showHistory);\n }\n // Load history into current session with 'l'\n if ((input === \"l\" || input === \"L\") && !blocked && showHistory) {\n const stored = getStoredBenchmarks();\n const converted: BenchmarkResult[] = stored.map((s) => ({\n model: s.model,\n device: s.device,\n tokensPerSec: s.tokensPerSec,\n firstTokenMs: s.firstTokenMs,\n totalTokens: s.totalTokens,\n totalMs: s.totalMs,\n }));\n setBenchmarkResults(converted);\n setCurrentRun(converted.length);\n // Collect unique models\n const models = [...new Set(converted.map((r) => r.model))];\n setBenchmarkModels(models.length > 0 ? models.slice(-3) : [model]);\n setShowHistory(false);\n }\n });\n\n const runBenchmark = async () => {\n updateStatus(\"running\");\n const prompts = [\n \"Write a haiku about programming.\",\n \"Explain recursion in one sentence.\",\n \"List 3 benefits of TypeScript.\",\n ];\n const prompt = prompts[currentRun % prompts.length];\n\n // Use generate() to get actual token counts from the result\n // This gives us real tokensGenerated and tokensPerSecond from transformers.js\n const startTime = Date.now();\n let firstTokenTime = 0;\n let gotFirstToken = false;\n\n const result = await gerbil.generate(prompt, {\n maxTokens: 100,\n onToken: () => {\n if (!gotFirstToken) {\n firstTokenTime = Date.now() - startTime;\n gotFirstToken = true;\n }\n },\n });\n\n const device = gerbil.getDeviceMode();\n const newResult: BenchmarkResult = {\n model,\n device,\n // Use actual metrics from generation result\n tokensPerSec: Math.round(result.tokensPerSecond),\n firstTokenMs: firstTokenTime || Math.round(result.totalTime * 0.1), // fallback estimate\n totalTokens: result.tokensGenerated,\n totalMs: Math.round(result.totalTime),\n };\n\n // Persist benchmark to ~/.gerbil/benchmarks.json\n saveBenchmark(newResult);\n\n const updatedResults = [...benchmarkResults, newResult];\n setBenchmarkResults(updatedResults);\n setCurrentRun((prev) => prev + 1);\n updateStatus(\"done\", newResult, updatedResults);\n onResult?.(newResult.tokensPerSec);\n };\n\n const currentResults = benchmarkResults.filter(\n (r) => r.model === model && r.device === currentDevice,\n );\n\n // Group results by model+device combination\n const resultKeys = [...new Set(benchmarkResults.map((r) => `${r.model}|${r.device}`))];\n const modelStats = resultKeys\n .map((key) => {\n const [m, d] = key.split(\"|\");\n const results = benchmarkResults.filter((r) => r.model === m && r.device === d);\n if (results.length === 0) {\n return null;\n }\n return {\n model: m,\n device: d as \"webgpu\" | \"cpu\" | \"wasm\",\n runs: results.length,\n avgTokPerSec: Math.round(results.reduce((a, r) => a + r.tokensPerSec, 0) / results.length),\n avgFirstToken: Math.round(results.reduce((a, r) => a + r.firstTokenMs, 0) / results.length),\n };\n })\n .filter(Boolean) as {\n model: string;\n device: string;\n runs: number;\n avgTokPerSec: number;\n avgFirstToken: number;\n }[];\n\n const bestTokPerSec =\n modelStats.length > 0 ? Math.max(...modelStats.map((s) => s.avgTokPerSec)) : 0;\n\n // Calculate \"bests\" across all runs\n // Best overall: score = tok/s / (1 + firstTokenMs/100) - rewards speed, penalizes slow first token\n const bests =\n benchmarkResults.length > 0\n ? (() => {\n const fastestRun = benchmarkResults.reduce((best, r) =>\n r.tokensPerSec > best.tokensPerSec ? r : best,\n );\n const fastestFirstToken = benchmarkResults.reduce((best, r) =>\n r.firstTokenMs < best.firstTokenMs ? r : best,\n );\n\n // Calculate overall score for each model+device combo using averages\n const overallScores = modelStats.map((s) => ({\n ...s,\n // Score: tok/s weighted against first token penalty\n // Higher tok/s is better, lower first token is better\n score: s.avgTokPerSec / (1 + s.avgFirstToken / 100),\n }));\n const bestOverall =\n overallScores.length > 0\n ? overallScores.reduce((best, s) => (s.score > best.score ? s : best))\n : null;\n\n return { fastestRun, fastestFirstToken, bestOverall };\n })()\n : null;\n\n const deviceColor =\n currentDevice === \"webgpu\" ? \"green\" : currentDevice === \"cpu\" ? \"yellow\" : \"gray\";\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box>\n <Text bold>Benchmark: </Text>\n <Text bold color=\"cyan\">\n {model}\n </Text>\n <Text> on </Text>\n <Text bold color={deviceColor}>\n {currentDevice.toUpperCase()}\n </Text>\n {modelStats.length > 1 && <Text dimColor> (comparing {modelStats.length} configs)</Text>}\n </Box>\n <Text dimColor>Run multiple times for accurate averages</Text>\n\n {currentResults.length > 0 && (\n <Box flexDirection=\"column\" marginY={1}>\n <Text bold color=\"cyan\">\n Results for {model} on {currentDevice.toUpperCase()} ({currentResults.length} runs):\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n {currentResults.slice(-6).map((r, i) => (\n <Text dimColor={i < currentResults.length - 1} key={i}>\n Run {i + 1}: <Text color=\"cyan\">{r.tokensPerSec}</Text> tok/s,{\" \"}\n <Text color=\"yellow\">{r.firstTokenMs}ms</Text> first token, {r.totalTokens} tokens\n </Text>\n ))}\n </Box>\n </Box>\n )}\n\n <Box flexDirection=\"row\" marginY={1}>\n {modelStats.length > 0 && (\n <Box\n borderColor=\"green\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n paddingX={1}\n >\n <Text bold color=\"green\">\n {modelStats.length > 1 ? \"Comparison:\" : \"Averages:\"}\n </Text>\n {modelStats.map((s) => {\n const isCurrent = s.model === model && s.device === currentDevice;\n const dColor =\n s.device === \"webgpu\" ? \"green\" : s.device === \"cpu\" ? \"yellow\" : \"gray\";\n return (\n <Box key={`${s.model}-${s.device}`}>\n <Text>\n {isCurrent ? \">\" : \" \"} {s.model}\n </Text>\n <Text color={dColor}> [{s.device}]</Text>\n <Text>: </Text>\n <Text\n bold\n color={\n s.avgTokPerSec === bestTokPerSec && modelStats.length > 1 ? \"green\" : \"cyan\"\n }\n >\n {s.avgTokPerSec} tok/s\n </Text>\n <Text>, </Text>\n <Text color=\"yellow\">{s.avgFirstToken}ms</Text>\n <Text dimColor> ({s.runs} runs)</Text>\n {s.avgTokPerSec === bestTokPerSec && modelStats.length > 1 && (\n <Text color=\"green\"> *fastest*</Text>\n )}\n </Box>\n );\n })}\n </Box>\n )}\n\n {bests && benchmarkResults.length >= 2 && (\n <Box borderColor=\"magenta\" borderStyle=\"single\" flexDirection=\"column\" paddingX={1}>\n <Text bold color=\"magenta\">\n Records:\n </Text>\n <Text>\n <Text color=\"cyan\">» Speed:</Text>{\" \"}\n <Text bold color=\"green\">\n {bests.fastestRun.tokensPerSec}\n </Text>{\" \"}\n tok/s\n <Text dimColor>\n {\" \"}\n ({bests.fastestRun.model} on {bests.fastestRun.device})\n </Text>\n </Text>\n <Text>\n <Text color=\"cyan\">» First:</Text>{\" \"}\n <Text bold color=\"yellow\">\n {bests.fastestFirstToken.firstTokenMs}\n </Text>\n ms\n <Text dimColor>\n {\" \"}\n ({bests.fastestFirstToken.model} on {bests.fastestFirstToken.device})\n </Text>\n </Text>\n {bests.bestOverall && (\n <Text>\n <Text color=\"cyan\">» Best:</Text>{\" \"}\n <Text bold color=\"magenta\">\n {bests.bestOverall.model}\n </Text>\n <Text dimColor>\n {\" \"}\n on {bests.bestOverall.device} ({bests.bestOverall.avgTokPerSec} tok/s,{\" \"}\n {bests.bestOverall.avgFirstToken}ms)\n </Text>\n </Text>\n )}\n </Box>\n )}\n </Box>\n\n {status === \"running\" && (\n <Box marginY={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" /> Running benchmark #{currentRun + 1}...\n </Text>\n </Box>\n )}\n\n {showHistory && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text bold color=\"gray\">\n Benchmark History ({historyData.length} runs)\n </Text>\n {historyData.length === 0 ? (\n <Text dimColor>No saved benchmarks found in ~/.gerbil/benchmarks.json</Text>\n ) : (\n <>\n <Box flexDirection=\"column\" marginY={1}>\n {historyData\n .slice(-10)\n .reverse()\n .map((b, i) => (\n <Text dimColor key={i}>\n <Text color=\"cyan\">{b.tokensPerSec}</Text> tok/s,{\" \"}\n <Text color=\"yellow\">{b.firstTokenMs}</Text>ms first\n <Text dimColor>\n {\" \"}\n - {b.model} [{b.device}] {new Date(b.timestamp).toLocaleDateString()}\n </Text>\n </Text>\n ))}\n {historyData.length > 10 && (\n <Text dimColor>... and {historyData.length - 10} more</Text>\n )}\n </Box>\n <Text dimColor>\n Press <Text color=\"yellow\">l</Text> to load all history into session |{\" \"}\n <Text color=\"yellow\">h</Text> to close\n </Text>\n </>\n )}\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Enter</Text> run | <Text color=\"yellow\">d</Text> switch{\" \"}\n {currentDevice === \"webgpu\" ? \"CPU\" : \"GPU\"} | <Text color=\"yellow\">m</Text> model |{\" \"}\n <Text color=\"yellow\">h</Text> history | <Text color=\"yellow\">r</Text> reset |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","/**\n * Microphone Recording for Node.js\n *\n * Provides microphone input support for speech-to-text in the CLI/REPL.\n * Requires `sox` to be installed on the system:\n * - macOS: `brew install sox`\n * - Ubuntu: `sudo apt install sox`\n * - Windows: Download from https://sox.sourceforge.net/\n *\n * @example\n * ```ts\n * const mic = new Microphone();\n * await mic.start();\n *\n * // Record for 5 seconds\n * await new Promise((r) => setTimeout(r, 5000));\n *\n * const audioData = await mic.stop();\n * // audioData is Float32Array at 16kHz, ready for Whisper\n * ```\n */\n\nimport { type ChildProcess, spawn, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport interface MicrophoneOptions {\n /** Sample rate (default: 16000 for Whisper) */\n sampleRate?: number;\n /** Channels (default: 1 for mono) */\n channels?: number;\n /** Bit depth (default: 16) */\n bitDepth?: number;\n /** Recording device (platform-specific) */\n device?: string;\n /** Silence threshold for auto-stop (0-1, default: 0.01) */\n silenceThreshold?: number;\n /** Duration of silence before auto-stop in seconds (default: 2) */\n silenceDuration?: number;\n}\n\nexport interface StreamingMicrophoneOptions extends MicrophoneOptions {\n /** Callback called with each audio chunk (Float32Array at sampleRate) */\n onAudioChunk?: (audio: Float32Array) => void;\n /** Chunk duration in milliseconds (default: 500) */\n chunkDuration?: number;\n}\n\nexport interface RecordingResult {\n /** Audio data as Float32Array */\n audio: Float32Array;\n /** Sample rate (16000) */\n sampleRate: number;\n /** Recording duration in seconds */\n duration: number;\n}\n\n// Common SoX installation paths\nconst SOX_PATHS = [\n \"sox\", // In PATH\n \"/opt/homebrew/bin/sox\", // macOS Homebrew (ARM)\n \"/usr/local/bin/sox\", // macOS Homebrew (Intel) / Linux\n \"/usr/bin/sox\", // Linux system\n];\n\n/**\n * Find the sox executable path\n */\nexport function findSoxPath(): string | null {\n for (const soxPath of SOX_PATHS) {\n try {\n const result = spawnSync(soxPath, [\"--version\"], {\n encoding: \"utf-8\",\n stdio: \"pipe\",\n timeout: 2000,\n });\n if (result.status === 0) {\n return soxPath;\n }\n } catch {\n // Try next path\n }\n }\n return null;\n}\n\n/**\n * Check if sox is available on the system\n */\nexport function isSoxAvailable(): boolean {\n return findSoxPath() !== null;\n}\n\n/**\n * Microphone recorder using SoX\n */\nexport class Microphone {\n private process: ChildProcess | null = null;\n private tempFile: string | null = null;\n private options: Required<MicrophoneOptions>;\n private isRecording = false;\n\n constructor(options: MicrophoneOptions = {}) {\n this.options = {\n sampleRate: options.sampleRate ?? 16000,\n channels: options.channels ?? 1,\n bitDepth: options.bitDepth ?? 16,\n device: options.device ?? \"\",\n silenceThreshold: options.silenceThreshold ?? 0.01,\n silenceDuration: options.silenceDuration ?? 2,\n };\n }\n\n /**\n * Start recording from microphone\n */\n async start(): Promise<void> {\n if (this.isRecording) {\n throw new Error(\"Already recording\");\n }\n\n const soxPath = findSoxPath();\n if (!soxPath) {\n throw new Error(\n \"SoX is not installed. Install it with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\\n\" +\n \" Windows: https://sox.sourceforge.net/\",\n );\n }\n\n // Create temp file for recording\n this.tempFile = join(tmpdir(), `gerbil-mic-${Date.now()}.wav`);\n\n // Build sox command\n const args: string[] = [];\n\n // Input from default device\n if (process.platform === \"darwin\") {\n args.push(\"-d\"); // Default input device on macOS\n } else if (process.platform === \"linux\") {\n args.push(\"-t\", \"alsa\", \"default\");\n } else if (process.platform === \"win32\") {\n args.push(\"-t\", \"waveaudio\", \"default\");\n } else {\n args.push(\"-d\");\n }\n\n // Output format\n args.push(\n \"-r\",\n String(this.options.sampleRate),\n \"-c\",\n String(this.options.channels),\n \"-b\",\n String(this.options.bitDepth),\n this.tempFile,\n );\n\n this.process = spawn(soxPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n this.isRecording = true;\n\n // Handle errors\n this.process.on(\"error\", (err) => {\n console.error(\"Microphone error:\", err.message);\n this.isRecording = false;\n });\n }\n\n /**\n * Stop recording and return audio data\n */\n async stop(): Promise<RecordingResult> {\n if (!this.isRecording || !this.process) {\n throw new Error(\"Not recording\");\n }\n\n return new Promise((resolve, reject) => {\n this.process!.on(\"close\", () => {\n this.isRecording = false;\n\n try {\n if (!this.tempFile || !existsSync(this.tempFile)) {\n reject(new Error(\"Recording file not found\"));\n return;\n }\n\n // Read the WAV file\n const buffer = readFileSync(this.tempFile);\n\n // Parse WAV and convert to Float32Array\n const audio = this.parseWav(new Uint8Array(buffer));\n\n // Clean up temp file\n try {\n unlinkSync(this.tempFile);\n } catch {\n // Ignore cleanup errors\n }\n this.tempFile = null;\n\n resolve({\n audio,\n sampleRate: this.options.sampleRate,\n duration: audio.length / this.options.sampleRate,\n });\n } catch (err) {\n reject(err);\n }\n });\n\n // Send SIGTERM to stop recording gracefully\n this.process!.kill(\"SIGTERM\");\n });\n }\n\n /**\n * Cancel recording without saving\n */\n cancel(): void {\n if (this.process) {\n // Use SIGTERM for graceful shutdown to avoid mutex errors\n this.process.kill(\"SIGTERM\");\n // Give it a moment then force kill if needed\n const proc = this.process;\n setTimeout(() => {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n // Already dead\n }\n }, 100);\n this.process = null;\n }\n this.isRecording = false;\n\n // Clean up temp file\n if (this.tempFile && existsSync(this.tempFile)) {\n try {\n unlinkSync(this.tempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n this.tempFile = null;\n }\n\n /**\n * Check if currently recording\n */\n recording(): boolean {\n return this.isRecording;\n }\n\n /**\n * Parse WAV file to Float32Array\n */\n private parseWav(buffer: Uint8Array): Float32Array {\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n // Find data chunk\n let dataOffset = 44; // Standard WAV header size\n for (let i = 12; i < buffer.length - 8; i++) {\n if (\n buffer[i] === 0x64 && // 'd'\n buffer[i + 1] === 0x61 && // 'a'\n buffer[i + 2] === 0x74 && // 't'\n buffer[i + 3] === 0x61 // 'a'\n ) {\n dataOffset = i + 8; // Skip \"data\" + size field\n break;\n }\n }\n\n // Calculate number of samples\n const bytesPerSample = this.options.bitDepth / 8;\n const numSamples = Math.floor((buffer.length - dataOffset) / bytesPerSample);\n const audio = new Float32Array(numSamples);\n\n // Convert to float\n const maxValue = 2 ** (this.options.bitDepth - 1);\n for (let i = 0; i < numSamples; i++) {\n const offset = dataOffset + i * bytesPerSample;\n if (offset + bytesPerSample <= buffer.length) {\n const sample =\n this.options.bitDepth === 16 ? view.getInt16(offset, true) : view.getInt32(offset, true);\n audio[i] = sample / maxValue;\n }\n }\n\n return audio;\n }\n}\n\n/**\n * Record audio from microphone for a specified duration\n */\nexport async function recordAudio(\n durationMs: number,\n options: MicrophoneOptions = {},\n): Promise<RecordingResult> {\n const mic = new Microphone(options);\n await mic.start();\n await new Promise((r) => setTimeout(r, durationMs));\n return mic.stop();\n}\n\n/**\n * Record audio from microphone until silence is detected\n * Note: This is a simplified implementation - sox supports this natively\n */\nexport async function recordUntilSilence(\n maxDurationMs: number = 30000,\n options: MicrophoneOptions = {},\n): Promise<RecordingResult> {\n // For now, just record for the duration\n // TODO: Implement VAD (Voice Activity Detection) for auto-stop\n return recordAudio(maxDurationMs, options);\n}\n\n/**\n * Streaming microphone recorder using SoX\n *\n * Outputs audio data in real-time via callback, perfect for streaming transcription.\n *\n * @example\n * ```ts\n * const mic = new StreamingMicrophone({\n * onAudioChunk: (audio) => session.feedAudio(audio),\n * chunkDuration: 500, // 0.5s chunks\n * });\n *\n * await mic.start();\n * // Audio is now streaming to your callback\n *\n * const result = await mic.stop();\n * ```\n */\nexport class StreamingMicrophone {\n private process: ChildProcess | null = null;\n private options: Required<StreamingMicrophoneOptions>;\n private isRecording = false;\n private audioChunks: Float32Array[] = [];\n private rawBuffer: Buffer[] = [];\n private flushInterval: ReturnType<typeof setInterval> | null = null;\n private startTime = 0;\n\n constructor(options: StreamingMicrophoneOptions = {}) {\n this.options = {\n sampleRate: options.sampleRate ?? 16000,\n channels: options.channels ?? 1,\n bitDepth: options.bitDepth ?? 16,\n device: options.device ?? \"\",\n silenceThreshold: options.silenceThreshold ?? 0.01,\n silenceDuration: options.silenceDuration ?? 2,\n onAudioChunk: options.onAudioChunk ?? (() => {}),\n chunkDuration: options.chunkDuration ?? 500,\n };\n }\n\n /**\n * Start streaming audio from microphone\n */\n async start(): Promise<void> {\n if (this.isRecording) {\n throw new Error(\"Already recording\");\n }\n\n const soxPath = findSoxPath();\n if (!soxPath) {\n throw new Error(\n \"SoX is not installed. Install it with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\\n\" +\n \" Windows: https://sox.sourceforge.net/\",\n );\n }\n\n // Build sox command for raw audio output to stdout\n const args: string[] = [];\n\n // Input from default device\n if (process.platform === \"darwin\") {\n args.push(\"-d\");\n } else if (process.platform === \"linux\") {\n args.push(\"-t\", \"alsa\", \"default\");\n } else if (process.platform === \"win32\") {\n args.push(\"-t\", \"waveaudio\", \"default\");\n } else {\n args.push(\"-d\");\n }\n\n // Output format: raw audio to stdout\n args.push(\n \"-t\",\n \"raw\", // Raw PCM output\n \"-r\",\n String(this.options.sampleRate),\n \"-c\",\n String(this.options.channels),\n \"-b\",\n String(this.options.bitDepth),\n \"-e\",\n \"signed-integer\",\n \"-\", // Output to stdout\n );\n\n this.process = spawn(soxPath, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n this.isRecording = true;\n this.startTime = Date.now();\n this.rawBuffer = [];\n this.audioChunks = [];\n\n // Handle audio data from stdout\n this.process.stdout?.on(\"data\", (chunk: Buffer) => {\n this.rawBuffer.push(chunk);\n });\n\n // Handle errors\n this.process.on(\"error\", (err) => {\n console.error(\"Microphone error:\", err.message);\n this.isRecording = false;\n });\n\n // Set up interval to flush audio chunks\n this.flushInterval = setInterval(() => {\n this.flushBuffer();\n }, this.options.chunkDuration);\n }\n\n /**\n * Flush buffered audio to callback\n */\n private flushBuffer(): void {\n if (this.rawBuffer.length === 0) return;\n\n // Combine all buffered chunks\n const combined = Buffer.concat(this.rawBuffer);\n this.rawBuffer = [];\n\n // Convert to Float32Array\n const audio = this.bufferToFloat32(combined);\n if (audio.length > 0) {\n this.audioChunks.push(audio);\n this.options.onAudioChunk(audio);\n }\n }\n\n /**\n * Convert raw PCM buffer to Float32Array\n */\n private bufferToFloat32(buffer: Buffer): Float32Array {\n const bytesPerSample = this.options.bitDepth / 8;\n const numSamples = Math.floor(buffer.length / bytesPerSample);\n const audio = new Float32Array(numSamples);\n const maxValue = 2 ** (this.options.bitDepth - 1);\n\n for (let i = 0; i < numSamples; i++) {\n const offset = i * bytesPerSample;\n const sample =\n this.options.bitDepth === 16 ? buffer.readInt16LE(offset) : buffer.readInt32LE(offset);\n audio[i] = sample / maxValue;\n }\n\n return audio;\n }\n\n /**\n * Stop recording and return all collected audio\n */\n async stop(): Promise<RecordingResult> {\n if (!this.isRecording || !this.process) {\n throw new Error(\"Not recording\");\n }\n\n // Clear flush interval\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n // Flush any remaining buffer\n this.flushBuffer();\n\n return new Promise((resolve, reject) => {\n this.process!.on(\"close\", () => {\n this.isRecording = false;\n\n try {\n // Combine all audio chunks\n const totalLength = this.audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const audio = new Float32Array(totalLength);\n let offset = 0;\n for (const chunk of this.audioChunks) {\n audio.set(chunk, offset);\n offset += chunk.length;\n }\n\n this.audioChunks = [];\n\n resolve({\n audio,\n sampleRate: this.options.sampleRate,\n duration: audio.length / this.options.sampleRate,\n });\n } catch (err) {\n reject(err);\n }\n });\n\n // Send SIGTERM to stop recording gracefully\n this.process!.kill(\"SIGTERM\");\n });\n }\n\n /**\n * Cancel recording without saving\n */\n cancel(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n if (this.process) {\n // Use SIGTERM for graceful shutdown to avoid mutex errors\n this.process.kill(\"SIGTERM\");\n // Give it a moment then force kill if needed\n const proc = this.process;\n setTimeout(() => {\n try {\n proc.kill(\"SIGKILL\");\n } catch {\n // Already dead\n }\n }, 100);\n this.process = null;\n }\n\n this.isRecording = false;\n this.rawBuffer = [];\n this.audioChunks = [];\n }\n\n /**\n * Check if currently recording\n */\n recording(): boolean {\n return this.isRecording;\n }\n\n /**\n * Get elapsed recording time in seconds\n */\n getElapsed(): number {\n if (!this.isRecording) return 0;\n return (Date.now() - this.startTime) / 1000;\n }\n}\n\nexport default Microphone;\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport SelectInput from \"ink-select-input\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { getChromeCachedModels } from \"../../../core/chrome-backend.js\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\nimport { StreamingMicrophone } from \"../../../core/microphone.js\";\nimport {\n executeToolCall,\n formatToolsForPrompt,\n getToolDefinitions,\n parseToolCall,\n} from \"../../../core/tools.js\";\nimport type { StreamingTranscriptionSession } from \"../../../core/types.js\";\nimport { processImagePath } from \"../utils.js\";\n\ntype Message = {\n role: \"user\" | \"assistant\" | \"thinking\" | \"system\";\n content: string;\n images?: string[]; // Attached images (URLs or data URIs)\n};\n\ntype ChatViewProps = {\n gerbil: Gerbil;\n thinkingMode: boolean;\n agentMode: boolean;\n voiceMode: boolean;\n onToggleThinking?: () => void;\n onToggleAgent?: () => void;\n onToggleVoice?: () => void;\n onCreateTool?: () => void;\n onGenerationComplete?: (stats: { tokensOut: number; timeMs: number; tokPerSec: number }) => void;\n /** Last generation tok/s */\n lastTokPerSec?: number;\n /** Average tok/s across session */\n avgTokPerSec?: number;\n};\n\n// Chat modes with system prompts\nconst CHAT_MODES = {\n assistant: {\n name: \"Assistant\",\n symbol: \">\",\n description: \"Helpful and concise\",\n system: `You are Gerbil, a helpful AI assistant running locally. Be concise and direct.\n- Answer questions clearly without unnecessary preamble\n- If you don't know something, say so\n- Use markdown formatting when helpful\n- Keep responses focused and actionable`,\n },\n coder: {\n name: \"Coder\",\n symbol: \"$\",\n description: \"Code-focused, minimal talk\",\n system: `You are Gerbil, a local coding assistant. Be extremely concise.\n- Output code first, explanations only if asked\n- Use proper syntax highlighting hints\n- Prefer working examples over theory\n- When fixing bugs, show the fix directly\n- No pleasantries, just solutions`,\n },\n teacher: {\n name: \"Teacher\",\n symbol: \"?\",\n description: \"Explains concepts clearly\",\n system: `You are Gerbil, a patient teacher who explains concepts clearly.\n- Break down complex topics into simple parts\n- Use analogies and examples\n- Check understanding with questions\n- Build from fundamentals\n- Encourage curiosity`,\n },\n creative: {\n name: \"Creative\",\n symbol: \"*\",\n description: \"Expressive and imaginative\",\n system: `You are Gerbil, a creative collaborator with an expressive style.\n- Embrace unconventional ideas\n- Use vivid language and metaphors\n- Explore possibilities freely\n- Be playful when appropriate\n- Think outside the box`,\n },\n minimal: {\n name: \"Minimal\",\n symbol: \".\",\n description: \"Ultra-short responses\",\n system: \"You are Gerbil. Respond in as few words as possible. No fluff. Direct answers only.\",\n },\n};\n\ntype ChatMode = keyof typeof CHAT_MODES;\n\n// Rough token estimation (4 chars per token average)\nfunction estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\n// Strip all think tags from content\nfunction stripThinkTags(content: string): string {\n return content\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\")\n .replace(/<\\/?think>/g, \"\")\n .trim();\n}\n\n// Simple terminal markdown renderer\nfunction renderMarkdown(content: string): React.ReactNode[] {\n const lines = content.split(\"\\n\");\n const elements: React.ReactNode[] = [];\n let inCodeBlock = false;\n let codeBlockContent: string[] = [];\n let codeBlockLang = \"\";\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n\n // Code blocks\n if (line.startsWith(\"```\")) {\n if (inCodeBlock) {\n // End code block\n elements.push(\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n key={`code-${i}`}\n marginY={1}\n paddingX={1}\n >\n {codeBlockLang && <Text dimColor>{codeBlockLang}</Text>}\n <Text color=\"green\">{codeBlockContent.join(\"\\n\")}</Text>\n </Box>,\n );\n codeBlockContent = [];\n codeBlockLang = \"\";\n inCodeBlock = false;\n } else {\n inCodeBlock = true;\n codeBlockLang = line.slice(3).trim();\n }\n continue;\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line);\n continue;\n }\n\n // Headers\n if (line.startsWith(\"### \")) {\n elements.push(\n <Text bold color=\"cyan\" key={i}>\n {line.slice(4)}\n </Text>,\n );\n continue;\n }\n if (line.startsWith(\"## \")) {\n elements.push(\n <Text bold color=\"yellow\" key={i}>\n {line.slice(3)}\n </Text>,\n );\n continue;\n }\n if (line.startsWith(\"# \")) {\n elements.push(\n <Text bold color=\"magenta\" key={i}>\n {line.slice(2)}\n </Text>,\n );\n continue;\n }\n\n // Bullet points\n if (line.match(/^[-*]\\s/)) {\n const bulletContent = line.slice(2);\n elements.push(\n <Text key={i}>\n <Text color=\"cyan\"> * </Text>\n {renderInlineMarkdown(bulletContent)}\n </Text>,\n );\n continue;\n }\n\n // Numbered lists\n const numMatch = line.match(/^(\\d+)\\.\\s(.*)$/);\n if (numMatch) {\n elements.push(\n <Text key={i}>\n <Text color=\"cyan\"> {numMatch[1]}. </Text>\n {renderInlineMarkdown(numMatch[2])}\n </Text>,\n );\n continue;\n }\n\n // Regular text with inline formatting\n elements.push(<Text key={i}>{renderInlineMarkdown(line)}</Text>);\n }\n\n return elements;\n}\n\n// Render inline markdown (bold, italic, code, links)\nfunction renderInlineMarkdown(text: string): React.ReactNode {\n // Split by inline code, bold, and italic markers\n const parts: React.ReactNode[] = [];\n let remaining = text;\n let key = 0;\n\n while (remaining.length > 0) {\n // Inline code `code`\n const codeMatch = remaining.match(/^(.*?)`([^`]+)`(.*)$/);\n if (codeMatch) {\n if (codeMatch[1]) {\n parts.push(<Text key={(key += 1)}>{codeMatch[1]}</Text>);\n }\n parts.push(\n <Text backgroundColor=\"black\" color=\"green\" key={(key += 1)}>\n {\" \"}\n {codeMatch[2]}{\" \"}\n </Text>,\n );\n remaining = codeMatch[3];\n continue;\n }\n\n // Bold **text**\n const boldMatch = remaining.match(/^(.*?)\\*\\*([^*]+)\\*\\*(.*)$/);\n if (boldMatch) {\n if (boldMatch[1]) {\n parts.push(<Text key={(key += 1)}>{boldMatch[1]}</Text>);\n }\n parts.push(\n <Text bold key={(key += 1)}>\n {boldMatch[2]}\n </Text>,\n );\n remaining = boldMatch[3];\n continue;\n }\n\n // Italic *text*\n const italicMatch = remaining.match(/^(.*?)\\*([^*]+)\\*(.*)$/);\n if (italicMatch) {\n if (italicMatch[1]) {\n parts.push(<Text key={(key += 1)}>{italicMatch[1]}</Text>);\n }\n parts.push(\n <Text italic key={(key += 1)}>\n {italicMatch[2]}\n </Text>,\n );\n remaining = italicMatch[3];\n continue;\n }\n\n // No more formatting found\n parts.push(<Text key={(key += 1)}>{remaining}</Text>);\n break;\n }\n\n return <>{parts}</>;\n}\n\n// Parse streaming content to separate thinking from response\nfunction parseStreamContent(\n content: string,\n showThinking: boolean,\n): { thinking: string; response: string; isThinking: boolean } {\n if (!showThinking) {\n return { thinking: \"\", response: stripThinkTags(content), isThinking: false };\n }\n\n // Check for opening <think> tag (may be incomplete during streaming)\n const thinkStart = content.indexOf(\"<think>\");\n const thinkEnd = content.indexOf(\"</think>\");\n\n // Currently inside a think block (has opening but no closing)\n if (thinkStart !== -1 && thinkEnd === -1) {\n const thinking = content.slice(thinkStart + 7);\n const response = content.slice(0, thinkStart).trim();\n return { thinking, response, isThinking: true };\n }\n\n // Has complete think block\n if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) {\n const thinking = content.slice(thinkStart + 7, thinkEnd).trim();\n const response = stripThinkTags(content);\n return { thinking, response, isThinking: false };\n }\n\n // No think tags at all - strip any partial/malformed tags just in case\n return { thinking: \"\", response: stripThinkTags(content), isThinking: false };\n}\n\nfunction StreamingResponse({\n content,\n symbol,\n showThinking,\n}: {\n content: string;\n symbol: string;\n showThinking: boolean;\n}) {\n const { thinking, response, isThinking } = parseStreamContent(content, showThinking);\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} paddingX={1}>\n {showThinking && thinking && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text color=\"gray\" dimColor italic>\n {\"<think>\"}\n </Text>\n <Text dimColor italic>\n {stripThinkTags(thinking)}\n {isThinking && <Spinner type=\"dots\" />}\n </Text>\n </Box>\n )}\n {response && (\n <Box>\n <Text bold color=\"cyan\">\n {symbol}{\" \"}\n </Text>\n <Text bold color=\"white\">\n {response}\n </Text>\n {!isThinking && (\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n )}\n </Box>\n )}\n {!(thinking || response) && (\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n )}\n </Box>\n );\n}\n\n// Build conversation history for context\nfunction buildConversationContext(messages: Message[], mode: ChatMode): string {\n const systemPrompt = CHAT_MODES[mode].system;\n let context = `<|im_start|>system\\n${systemPrompt}<|im_end|>\\n`;\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n context += `<|im_start|>user\\n${msg.content}<|im_end|>\\n`;\n } else if (msg.role === \"assistant\") {\n context += `<|im_start|>assistant\\n${msg.content}<|im_end|>\\n`;\n }\n // Skip thinking and system messages for context\n }\n\n return context;\n}\n\nexport function ChatView({\n gerbil,\n thinkingMode,\n agentMode,\n voiceMode,\n onToggleThinking,\n onToggleAgent,\n onToggleVoice,\n onCreateTool,\n onGenerationComplete,\n lastTokPerSec,\n avgTokPerSec,\n}: ChatViewProps) {\n const [messages, setMessages] = useState<Message[]>([]);\n const [input, setInput] = useState(\"\");\n const [generating, setGenerating] = useState(false);\n const [streamedContent, setStreamedContent] = useState(\"\");\n const [mode, setMode] = useState<ChatMode>(\"assistant\");\n const [showModeSelector, setShowModeSelector] = useState(false);\n const [summarizing, setSummarizing] = useState(false);\n const [speaking, setSpeaking] = useState(false);\n const [recording, setRecording] = useState(false);\n const [recordingStatus, setRecordingStatus] = useState(\"\");\n const [recordingElapsed, setRecordingElapsed] = useState(0);\n const [liveTranscript, setLiveTranscript] = useState(\"\");\n const micRef = useRef<StreamingMicrophone | null>(null);\n const streamingSessionRef = useRef<StreamingTranscriptionSession | null>(null);\n const recordingStartTime = useRef<number>(0);\n const holdTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const HOLD_RELEASE_DELAY = 200; // ms to detect key release\n\n // Action bar navigation\n const [actionBarFocused, setActionBarFocused] = useState(false);\n const [selectedAction, setSelectedAction] = useState(0);\n\n // Image attachments (for vision models)\n const [attachedImages, setAttachedImages] = useState<string[]>([]);\n const [showImagePicker, setShowImagePicker] = useState(false);\n const [imagePathInput, setImagePathInput] = useState(\"\");\n const [imagePickerKey, setImagePickerKey] = useState(0); // Force remount TextInput\n\n // Check if model supports vision\n const supportsVision = gerbil.supportsVision?.() ?? false;\n\n // Command history\n const [history, setHistory] = useState<string[]>([]);\n const [historyIndex, setHistoryIndex] = useState(-1);\n const [savedInput, setSavedInput] = useState(\"\");\n\n // Track previous mode values to detect changes\n const [prevThinkingMode, setPrevThinkingMode] = useState(thinkingMode);\n const [prevAgentMode, setPrevAgentMode] = useState(agentMode);\n\n // Show feedback when modes change\n useEffect(() => {\n if (thinkingMode !== prevThinkingMode) {\n setPrevThinkingMode(thinkingMode);\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: thinkingMode ? \"🧠 Thinking mode enabled\" : \"🧠 Thinking mode disabled\",\n },\n ]);\n }\n }, [thinkingMode, prevThinkingMode]);\n\n useEffect(() => {\n if (agentMode !== prevAgentMode) {\n setPrevAgentMode(agentMode);\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: agentMode ? \"🔧 Agent mode enabled (tools available)\" : \"🔧 Agent mode disabled\",\n },\n ]);\n }\n }, [agentMode, prevAgentMode]);\n\n // Cleanup microphone, streaming session, and timers on unmount\n useEffect(() => {\n return () => {\n // Abort streaming session immediately (no final transcription)\n if (streamingSessionRef.current) {\n try {\n streamingSessionRef.current.abort();\n } catch {\n // Ignore cleanup errors\n }\n streamingSessionRef.current = null;\n }\n // Then cancel microphone\n if (micRef.current) {\n try {\n micRef.current.cancel();\n } catch {\n // Ignore cleanup errors\n }\n micRef.current = null;\n }\n if (holdTimeoutRef.current) {\n clearTimeout(holdTimeoutRef.current);\n holdTimeoutRef.current = null;\n }\n };\n }, []);\n\n const modes = Object.keys(CHAT_MODES) as ChatMode[];\n\n // Get model info for context limits\n // Check Chrome cache for accurate context length (may have been fetched from HuggingFace)\n const modelInfo = gerbil.getModelInfo();\n const modelId = modelInfo?.repo;\n const cachedModels = modelId ? getChromeCachedModels() : [];\n const cachedModel = modelId ? cachedModels.find((m) => m.modelId === modelId) : null;\n const maxContext = cachedModel?.contextLength || modelInfo?.contextLength || 32_768;\n\n // Calculate current context usage\n const contextText = buildConversationContext(messages, mode);\n const currentTokens = estimateTokens(contextText);\n const contextPercent = Math.round((currentTokens / maxContext) * 100);\n\n // Number of actions in the action bar\n const ACTION_COUNT = supportsVision ? 5 : 4; // Think, Agent, Voice, [Image], Record\n\n // Handle action bar toggle/trigger\n const handleActionSelect = () => {\n const recordIndex = supportsVision ? 4 : 3;\n const imageIndex = supportsVision ? 3 : -1;\n\n switch (selectedAction) {\n case 0: // Thinking\n onToggleThinking?.();\n break;\n case 1: // Agent\n onToggleAgent?.();\n break;\n case 2: // Voice\n onToggleVoice?.();\n break;\n default:\n if (selectedAction === imageIndex) {\n // Image - open image picker\n setShowImagePicker(true);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n } else if (selectedAction === recordIndex) {\n // Record - just shows hint, use Cmd+M to record\n }\n break;\n }\n };\n\n // Handle Tab for action bar, Up/Down for history, and Cmd shortcuts\n useInput((char, key) => {\n // Tab toggles action bar focus\n if (key.tab && !generating && !showImagePicker) {\n if (actionBarFocused) {\n // If already focused, unfocus\n setActionBarFocused(false);\n } else {\n // Focus action bar\n setActionBarFocused(true);\n setShowModeSelector(false);\n }\n return;\n }\n\n // When action bar is focused, handle navigation\n if (actionBarFocused && !generating) {\n // Left/Right to navigate actions\n if (key.leftArrow) {\n setSelectedAction((s) => (s - 1 + ACTION_COUNT) % ACTION_COUNT);\n return;\n }\n if (key.rightArrow) {\n setSelectedAction((s) => (s + 1) % ACTION_COUNT);\n return;\n }\n // Enter to toggle/trigger selected action\n if (key.return) {\n handleActionSelect();\n // Keep action bar focused for multiple toggles\n return;\n }\n // Escape to exit action bar\n if (key.escape) {\n setActionBarFocused(false);\n return;\n }\n // Number keys for quick select\n if (char >= \"1\" && char <= \"4\") {\n const idx = Number.parseInt(char, 10) - 1;\n setSelectedAction(idx);\n handleActionSelect();\n return;\n }\n }\n\n // Ctrl+O to open image picker (for vision models)\n if (char === \"o\" && key.ctrl && supportsVision && !generating && !showModeSelector) {\n setShowImagePicker(true);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n }\n\n // Cmd+C to copy chat\n if (char === \"c\" && key.meta) {\n copyChat();\n }\n\n // Cmd+S to save chat\n if (char === \"s\" && key.meta) {\n saveChat();\n }\n\n // Backtick (`): Toggle recording - press once to start, press again to stop and send\n // Simple push-to-talk that works in all terminals\n if (char === \"`\" && !generating && !showModeSelector && !showImagePicker) {\n if (recording) {\n // Stop and send\n stopRecording(true);\n } else {\n // Start recording\n startRecording();\n }\n }\n\n // Escape to close image picker or action bar\n if (key.escape) {\n if (showImagePicker) {\n setShowImagePicker(false);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n } else if (actionBarFocused) {\n setActionBarFocused(false);\n }\n }\n\n // Navigate history with Up/Down arrows (only when action bar not focused)\n if (!(generating || showModeSelector || showImagePicker || actionBarFocused)) {\n if (key.upArrow && history.length > 0) {\n if (historyIndex === -1) {\n // Save current input before browsing history\n setSavedInput(input);\n setHistoryIndex(history.length - 1);\n setInput(history.at(-1) ?? \"\");\n } else if (historyIndex > 0) {\n setHistoryIndex(historyIndex - 1);\n setInput(history[historyIndex - 1]);\n }\n }\n if (key.downArrow && historyIndex !== -1) {\n if (historyIndex < history.length - 1) {\n setHistoryIndex(historyIndex + 1);\n setInput(history[historyIndex + 1]);\n } else {\n // Return to saved input\n setHistoryIndex(-1);\n setInput(savedInput);\n }\n }\n }\n });\n\n // Summarize conversation and reset context\n const summarizeConversation = async () => {\n if (messages.length < 4) {\n setMessages((m) => [...m, { role: \"system\", content: \"Not enough messages to summarize.\" }]);\n return;\n }\n\n setSummarizing(true);\n setMessages((m) => [...m, { role: \"system\", content: \"Summarizing conversation...\" }]);\n\n try {\n const conversationText = messages\n .filter((m) => m.role === \"user\" || m.role === \"assistant\")\n .map((m) => `${m.role === \"user\" ? \"User\" : \"Assistant\"}: ${m.content}`)\n .join(\"\\n\");\n\n let summary = \"\";\n for await (const chunk of gerbil.stream(\n `Summarize this conversation in 2-3 sentences, capturing the key topics and conclusions:\\n\\n${conversationText}`,\n { maxTokens: 200, system: \"You are a summarizer. Be concise.\" },\n )) {\n summary += chunk;\n }\n\n summary = stripThinkTags(summary);\n\n // Reset messages with just the summary\n setMessages([\n { role: \"system\", content: `Previous conversation summary: ${summary}` },\n { role: \"system\", content: \"Context compressed. Continuing from summary above.\" },\n ]);\n } catch (e) {\n setMessages((m) => [...m, { role: \"system\", content: `Error summarizing: ${e}` }]);\n }\n\n setSummarizing(false);\n };\n\n // Format chat as text\n const formatChatContent = () => {\n let content = `# Gerbil Chat - ${new Date().toLocaleString()}\\n\\n`;\n content += `**Mode:** ${CHAT_MODES[mode].name}\\n`;\n content += `**Model:** ${modelInfo?.id || \"unknown\"}\\n\\n`;\n content += \"---\\n\\n\";\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n content += `**You:** ${msg.content}\\n\\n`;\n } else if (msg.role === \"assistant\") {\n content += `**Gerbil:** ${msg.content}\\n\\n`;\n } else if (msg.role === \"system\") {\n content += `*${msg.content}*\\n\\n`;\n }\n }\n return content;\n };\n\n // Copy chat to clipboard\n const copyChat = () => {\n const content = formatChatContent();\n const { exec } = require(\"node:child_process\");\n const platform = process.platform;\n\n const cmd =\n platform === \"darwin\"\n ? \"pbcopy\"\n : platform === \"win32\"\n ? \"clip\"\n : \"xclip -selection clipboard\";\n const proc = exec(cmd, (err: any) => {\n if (err) {\n setMessages((m) => [...m, { role: \"system\", content: `Copy failed: ${err}` }]);\n } else {\n setMessages((m) => [...m, { role: \"system\", content: \"Chat copied to clipboard\" }]);\n }\n });\n proc.stdin?.write(content);\n proc.stdin?.end();\n };\n\n // Save chat to file\n const saveChat = (filename?: string) => {\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19);\n const fname = filename || `gerbil-chat-${timestamp}.md`;\n const fpath = path.join(process.cwd(), fname);\n\n const content = formatChatContent();\n\n try {\n fs.writeFileSync(fpath, content);\n setMessages((m) => [...m, { role: \"system\", content: `Chat saved to ${fname}` }]);\n } catch (e) {\n setMessages((m) => [...m, { role: \"system\", content: `Error saving: ${e}` }]);\n }\n };\n\n // Start recording with streaming transcription\n const startRecording = async () => {\n if (recording || generating) return;\n\n try {\n const micAvailable = await gerbil.isMicrophoneAvailable();\n if (!micAvailable) {\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content:\n \"Microphone requires SoX. Install with:\\n\" +\n \" macOS: brew install sox\\n\" +\n \" Ubuntu: sudo apt install sox\",\n },\n ]);\n return;\n }\n\n // Ensure STT is loaded before starting\n await gerbil.loadSTT();\n\n // Create streaming transcription session\n const session = await gerbil.createStreamingTranscription({\n chunkDuration: 2500, // Transcribe every 2.5 seconds\n onChunk: (text, idx) => {\n // Update live transcript as each chunk is transcribed\n setLiveTranscript((prev) => (prev ? `${prev} ${text}` : text));\n },\n onTranscript: (fullText) => {\n // Update the recording message with live transcript\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n const elapsed = Math.floor((Date.now() - recordingStartTime.current) / 1000);\n newMessages[lastIdx] = {\n role: \"system\",\n content: fullText\n ? `Recording... ${elapsed}s\\n> ${fullText}`\n : `Recording... ${elapsed}s`,\n };\n }\n return newMessages;\n });\n },\n onError: (err) => {\n console.error(\"Transcription error:\", err);\n },\n });\n\n streamingSessionRef.current = session;\n session.start();\n\n // Create streaming microphone that feeds audio to the session\n micRef.current = new StreamingMicrophone({\n onAudioChunk: (audio) => {\n session.feedAudio(audio);\n },\n chunkDuration: 500, // 0.5s audio chunks\n });\n\n await micRef.current.start();\n\n recordingStartTime.current = Date.now();\n setRecording(true);\n setRecordingElapsed(0);\n setRecordingStatus(\"Recording...\");\n setLiveTranscript(\"\");\n\n // Add recording message\n setMessages((m) => [...m, { role: \"system\", content: \"Recording... (release to send)\" }]);\n\n // Update elapsed time every 500ms\n const timerInterval = setInterval(() => {\n if (!micRef.current?.recording()) {\n clearInterval(timerInterval);\n return;\n }\n const elapsed = Math.floor((Date.now() - recordingStartTime.current) / 1000);\n setRecordingElapsed(elapsed);\n\n // Only update status if no transcript yet\n const currentTranscript = streamingSessionRef.current?.getTranscript() || \"\";\n if (!currentTranscript) {\n setRecordingStatus(`Recording... ${elapsed}s`);\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Recording... ${elapsed}s`,\n };\n }\n return newMessages;\n });\n } else {\n setRecordingStatus(`Recording... ${elapsed}s (transcribing)`);\n }\n }, 500);\n\n // Store interval ID for cleanup\n (micRef.current as any)._timerInterval = timerInterval;\n } catch (err: any) {\n setMessages((m) => [...m, { role: \"system\", content: `Recording error: ${err.message}` }]);\n setRecording(false);\n micRef.current = null;\n streamingSessionRef.current = null;\n }\n };\n\n // Stop recording and finalize streaming transcription\n // autoSubmit: if true, automatically submit the transcribed text as a message\n const stopRecording = async (autoSubmit = false) => {\n if (!recording || !micRef.current) return;\n\n const mic = micRef.current;\n const session = streamingSessionRef.current;\n micRef.current = null;\n streamingSessionRef.current = null;\n\n // Clear hold timeout\n if (holdTimeoutRef.current) {\n clearTimeout(holdTimeoutRef.current);\n holdTimeoutRef.current = null;\n }\n\n // Clear timer interval\n if ((mic as any)._timerInterval) {\n clearInterval((mic as any)._timerInterval);\n }\n\n setRecordingStatus(\"Finalizing...\");\n\n try {\n // Stop microphone first\n const result = await mic.stop();\n const durationSec = result.duration.toFixed(1);\n\n // Update message\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Recorded ${durationSec}s, finalizing transcription...`,\n };\n }\n return newMessages;\n });\n\n // Stop the streaming session and get final transcript\n let text = \"\";\n if (session) {\n text = await session.stop();\n text = text.trim();\n }\n\n // If streaming didn't capture anything, fall back to full transcription\n if (!text && result.audio.length > 0) {\n setRecordingStatus(\"Transcribing...\");\n const transcription = await gerbil.transcribe(result.audio);\n text = transcription.text.trim();\n }\n\n if (text) {\n if (autoSubmit) {\n // Remove the system message and auto-submit\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages.pop(); // Remove \"Recording...\" message\n }\n return newMessages;\n });\n // Reset recording state first\n setRecording(false);\n setRecordingStatus(\"\");\n setRecordingElapsed(0);\n setLiveTranscript(\"\");\n // Submit the transcribed text\n await handleSubmit(text);\n return;\n }\n // Put transcribed text in input field (manual mode)\n setInput(text);\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Transcribed: \"${text}\"`,\n };\n }\n return newMessages;\n });\n } else {\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: \"No speech detected. Try speaking louder or closer to mic.\",\n };\n }\n return newMessages;\n });\n }\n } catch (err: any) {\n setMessages((m) => {\n const newMessages = [...m];\n const lastIdx = newMessages.length - 1;\n if (lastIdx >= 0 && newMessages[lastIdx].role === \"system\") {\n newMessages[lastIdx] = {\n role: \"system\",\n content: `Transcription error: ${err.message}`,\n };\n }\n return newMessages;\n });\n }\n\n setRecording(false);\n setRecordingStatus(\"\");\n setRecordingElapsed(0);\n setLiveTranscript(\"\");\n };\n\n // Toggle recording (called when Record action is triggered)\n const toggleRecording = async () => {\n if (recording) {\n await stopRecording();\n } else {\n await startRecording();\n }\n };\n\n const handleSubmit = async (value: string) => {\n if (!value.trim() || generating || summarizing) {\n return;\n }\n\n // Handle slash commands\n if (value.startsWith(\"/\")) {\n const parts = value.slice(1).split(\" \");\n const cmd = parts[0].toLowerCase().trim();\n const arg = parts.slice(1).join(\" \");\n\n if (cmd === \"clear\" || cmd === \"new\") {\n setMessages([]);\n setInput(\"\");\n return;\n }\n if (cmd === \"mode\") {\n setShowModeSelector(true);\n setInput(\"\");\n return;\n }\n if (cmd === \"summarize\" || cmd === \"compress\") {\n setInput(\"\");\n await summarizeConversation();\n return;\n }\n if (cmd === \"reset-cache\" || cmd === \"reset\") {\n setInput(\"\");\n try {\n await gerbil.clearCache();\n const mem = await gerbil.getMemoryUsage();\n const memInfo = mem ? ` (was ${mem.usedGB.toFixed(1)}GB)` : \"\";\n setMessages((m) => [\n ...m,\n { role: \"system\", content: `🧹 Cache cleared${memInfo}. Conversation context reset.` },\n ]);\n } catch (err) {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: `❌ Failed to clear cache: ${err}` },\n ]);\n }\n return;\n }\n if (cmd === \"memory\" || cmd === \"mem\") {\n setInput(\"\");\n try {\n const mem = await gerbil.getMemoryUsage();\n if (mem) {\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `💾 Memory: ${mem.usedGB.toFixed(1)}GB / ${mem.totalGB.toFixed(1)}GB (${mem.usedPercent.toFixed(1)}%)`,\n },\n ]);\n } else {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: \"Memory monitoring not available (CPU mode)\" },\n ]);\n }\n } catch (err) {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: `❌ Failed to get memory: ${err}` },\n ]);\n }\n return;\n }\n if (cmd === \"save\") {\n setInput(\"\");\n saveChat(arg || undefined);\n return;\n }\n if (cmd === \"help\") {\n const supportsVision = gerbil.supportsVision?.() ?? false;\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `Commands:\n /new, /clear - Start new chat\n /docs <topic> - Search Gerbil docs (tools, skills, ai-sdk, next, etc.)\n /summarize - Compress conversation\n /reset-cache - Clear KV cache (frees memory)\n /memory - Show memory usage\n /mode - Change chat mode\n /image <path> - Attach image ${supportsVision ? \"✓\" : \"(requires vision model)\"}\n /images - Show attached images\n /clear-images - Remove attached images\n\nShortcuts:\n Tab - Focus action bar (🧠 🔧 🔊 🎤)\n ←→ - Navigate actions (when focused)\n Enter - Toggle/trigger action\n Esc - Exit action bar\n \n ⌘C - Copy chat to clipboard\n ⌘S - Save chat to file\n ^R - Quick record (5s)\n ^O - Open image ${supportsVision ? \"✓\" : \"(requires vision model)\"}`,\n },\n ]);\n setInput(\"\");\n return;\n }\n\n // /image command - attach image for vision models (supports URLs and local paths)\n if (cmd === \"image\" && arg) {\n const result = processImagePath(arg);\n if (result.success && result.imageSource) {\n setAttachedImages((imgs) => [...imgs, result.imageSource!]);\n const count = attachedImages.length + 1;\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `${result.message} (${count} image${count > 1 ? \"s\" : \"\"} total)`,\n },\n ]);\n } else if (result.message) {\n setMessages((m) => [...m, { role: \"system\", content: result.message }]);\n }\n setInput(\"\");\n return;\n }\n\n // /images - show attached images\n if (cmd === \"images\") {\n if (attachedImages.length === 0) {\n setMessages((m) => [\n ...m,\n { role: \"system\", content: \"No images attached. Use /image <path> to attach.\" },\n ]);\n } else {\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `📎 ${attachedImages.length} image(s) attached\\nUse /clear-images to remove.`,\n },\n ]);\n }\n setInput(\"\");\n return;\n }\n\n // /clear-images - remove attached images\n if (cmd === \"clear-images\") {\n const count = attachedImages.length;\n setAttachedImages([]);\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: count > 0 ? `🗑️ Cleared ${count} attached image(s)` : \"No images to clear\",\n },\n ]);\n setInput(\"\");\n return;\n }\n\n // /docs command - direct docs search\n if (cmd === \"docs\" && arg) {\n setInput(\"\");\n setGenerating(true);\n try {\n const result = await executeToolCall(\"gerbil_docs\", { query: arg });\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `📄 docs(${arg}):\\n${result.slice(0, 600)}${result.length > 600 ? \"...\" : \"\"}`,\n },\n ]);\n } catch (e) {\n setMessages((m) => [...m, { role: \"system\", content: `Error: ${e}` }]);\n }\n setGenerating(false);\n return;\n }\n }\n\n const userMessage = value.trim();\n setInput(\"\");\n\n // Add to history (avoid duplicates)\n if (history.at(-1) !== userMessage) {\n setHistory((h) => [...h.slice(-50), userMessage]); // Keep last 50\n }\n setHistoryIndex(-1);\n setSavedInput(\"\");\n\n // Include attached images with user message\n const currentImages = [...attachedImages];\n\n setMessages((m) => [\n ...m,\n {\n role: \"user\",\n content: userMessage,\n images: currentImages.length > 0 ? currentImages : undefined,\n },\n ]);\n setGenerating(true);\n setStreamedContent(\"\");\n\n // Clear images after attaching to message\n if (currentImages.length > 0) {\n setAttachedImages([]);\n }\n\n try {\n let fullResponse = \"\";\n const currentMode = CHAT_MODES[mode];\n const startTime = performance.now();\n let tokenCount = 0;\n\n // Build system prompt - replace with agent prompt if agent mode is enabled\n let systemPrompt = currentMode.system;\n\n if (agentMode) {\n const tools = getToolDefinitions();\n // Agent mode uses its own prompt entirely (not combined with mode)\n systemPrompt = formatToolsForPrompt(tools);\n }\n\n for await (const chunk of gerbil.stream(userMessage, {\n thinking: thinkingMode,\n maxTokens: 500,\n system: systemPrompt,\n // Include images for vision models\n images:\n currentImages.length > 0 ? currentImages.map((src) => ({ source: src })) : undefined,\n })) {\n fullResponse += chunk;\n tokenCount += 1;\n setStreamedContent(fullResponse);\n }\n\n // Calculate and report tok/s\n const endTime = performance.now();\n const timeMs = endTime - startTime;\n const tokPerSec = timeMs > 0 ? (tokenCount / timeMs) * 1000 : 0;\n onGenerationComplete?.({ tokensOut: tokenCount, timeMs, tokPerSec });\n\n let response = stripThinkTags(fullResponse);\n\n // Check for tool calls in agent mode\n if (agentMode) {\n const toolCall = parseToolCall(response);\n if (toolCall) {\n // Show that we're calling a tool\n setMessages((m) => [\n ...m,\n {\n role: \"system\",\n content: `🔧 Calling: ${toolCall.tool}(${JSON.stringify(toolCall.params)})`,\n },\n ]);\n setStreamedContent(\"\");\n\n // Execute the tool\n const toolResult = await executeToolCall(toolCall.tool, toolCall.params);\n\n // Add tool result (truncate if too long)\n const truncatedResult =\n toolResult.length > 500 ? `${toolResult.slice(0, 500)}\\n... (truncated)` : toolResult;\n setMessages((m) => [...m, { role: \"system\", content: `📄 Result:\\n${truncatedResult}` }]);\n\n // Generate follow-up response with tool result\n let followUp = \"\";\n for await (const chunk of gerbil.stream(\n `Here's documentation:\\n${toolResult}\\n\\nSummarize this for the user who asked: \"${userMessage}\"`,\n { maxTokens: 300, system: \"You are helpful. Summarize the documentation briefly.\" },\n )) {\n followUp += chunk;\n setStreamedContent(followUp);\n }\n response = stripThinkTags(followUp);\n\n // If follow-up is empty, just show the tool worked\n if (!response.trim()) {\n response = \"See the documentation above.\";\n }\n }\n }\n\n const thinkMatch = fullResponse.match(/<think>([\\s\\S]*?)<\\/think>/);\n let thinking = \"\";\n\n if (thinkMatch && thinkingMode) {\n // Extract content and strip any remaining tags\n thinking = stripThinkTags(thinkMatch[1]).trim();\n }\n\n setMessages((m) => {\n const newMessages = [...m];\n if (thinking && thinkingMode) {\n newMessages.push({ role: \"thinking\", content: thinking });\n }\n newMessages.push({ role: \"assistant\", content: response });\n return newMessages;\n });\n\n // Speak response if voice mode is enabled\n if (voiceMode && response.trim()) {\n speakResponse(response);\n }\n } catch (error) {\n setMessages((m) => [...m, { role: \"assistant\", content: `Error: ${error}` }]);\n } finally {\n setGenerating(false);\n setStreamedContent(\"\");\n }\n };\n\n // Speak response using TTS (sentence-by-sentence for responsiveness)\n // Check if text looks like gibberish (common on first response due to model warmup)\n const isGibberish = (text: string): boolean => {\n const cleaned = text.trim();\n if (cleaned.length < 3) return true;\n\n // Check for high ratio of non-ASCII or weird characters\n const weirdChars = cleaned.match(/[^\\x20-\\x7E\\n]/g) || [];\n if (weirdChars.length / cleaned.length > 0.3) return true;\n\n // Check for repetitive patterns (e.g., \"aaaa\" or \"abababab\")\n if (/(.)\\1{4,}/.test(cleaned)) return true;\n if (/(.{1,3})\\1{3,}/.test(cleaned)) return true;\n\n // Check if mostly punctuation or symbols\n const alphaNum = cleaned.match(/[a-zA-Z0-9]/g) || [];\n if (alphaNum.length / cleaned.length < 0.5) return true;\n\n // Check for common gibberish patterns\n if (/^[^a-zA-Z]*$/.test(cleaned)) return true;\n\n return false;\n };\n\n const speakResponse = async (text: string) => {\n // Skip speaking gibberish\n if (isGibberish(text)) {\n return;\n }\n\n setSpeaking(true);\n try {\n const { execSync } = await import(\"child_process\");\n const { writeFileSync, unlinkSync } = await import(\"fs\");\n const { tmpdir } = await import(\"os\");\n const { join } = await import(\"path\");\n\n // Split into paragraphs for efficient streaming (sentences are too slow)\n // If text is short (<500 chars), speak all at once\n // Otherwise split by double newlines or single newlines\n let chunks: string[];\n const cleanText = text.trim();\n\n if (cleanText.length < 500) {\n chunks = [cleanText];\n } else if (cleanText.includes(\"\\n\\n\")) {\n chunks = cleanText.split(/\\n\\n+/).filter((s) => s.trim() && !isGibberish(s));\n } else if (cleanText.includes(\"\\n\")) {\n chunks = cleanText.split(/\\n+/).filter((s) => s.trim() && !isGibberish(s));\n } else {\n // Long single paragraph - speak in ~300 char chunks at sentence boundaries\n chunks = [];\n let current = \"\";\n for (const sentence of cleanText.split(/(?<=[.!?])\\s+/)) {\n if (current.length + sentence.length > 300 && current.length > 0) {\n chunks.push(current.trim());\n current = sentence;\n } else {\n current += (current ? \" \" : \"\") + sentence;\n }\n }\n if (current.trim()) chunks.push(current.trim());\n chunks = chunks.filter((s) => !isGibberish(s));\n }\n\n for (const chunk of chunks) {\n if (!chunk.trim()) continue;\n\n const result = await gerbil.speak(chunk, { voice: \"af_heart\", speed: 1.0 });\n\n // Save to temp WAV and play\n const tempFile = join(tmpdir(), `gerbil-voice-${Date.now()}.wav`);\n\n // Write WAV file\n const buffer = Buffer.alloc(44 + result.audio.length * 2);\n buffer.write(\"RIFF\", 0);\n buffer.writeUInt32LE(36 + result.audio.length * 2, 4);\n buffer.write(\"WAVE\", 8);\n buffer.write(\"fmt \", 12);\n buffer.writeUInt32LE(16, 16);\n buffer.writeUInt16LE(1, 20);\n buffer.writeUInt16LE(1, 22);\n buffer.writeUInt32LE(result.sampleRate, 24);\n buffer.writeUInt32LE(result.sampleRate * 2, 28);\n buffer.writeUInt16LE(2, 32);\n buffer.writeUInt16LE(16, 34);\n buffer.write(\"data\", 36);\n buffer.writeUInt32LE(result.audio.length * 2, 40);\n\n for (let i = 0; i < result.audio.length; i++) {\n const s = Math.max(-1, Math.min(1, result.audio[i]));\n buffer.writeInt16LE(Math.round(s * 32767), 44 + i * 2);\n }\n writeFileSync(tempFile, buffer);\n\n // Play audio\n try {\n const platform = process.platform;\n if (platform === \"darwin\") {\n execSync(`afplay \"${tempFile}\"`, { stdio: \"ignore\" });\n } else if (platform === \"linux\") {\n try {\n execSync(`aplay \"${tempFile}\"`, { stdio: \"ignore\" });\n } catch {\n execSync(`paplay \"${tempFile}\"`, { stdio: \"ignore\" });\n }\n } else if (platform === \"win32\") {\n execSync(`powershell -c \"(New-Object Media.SoundPlayer '${tempFile}').PlaySync()\"`, {\n stdio: \"ignore\",\n });\n }\n } finally {\n try {\n unlinkSync(tempFile);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n } catch (err) {\n // Silently fail TTS - don't interrupt chat\n } finally {\n setSpeaking(false);\n }\n };\n\n const modeItems = modes.map((key) => ({\n key,\n label: `[${CHAT_MODES[key].symbol}] ${CHAT_MODES[key].name}`,\n value: key,\n }));\n\n return (\n <Box flexDirection=\"column\" flexGrow={1}>\n {/* Mode selector popup */}\n {showModeSelector && (\n <Box\n borderColor=\"cyan\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginX={1}\n paddingX={2}\n paddingY={1}\n >\n <Text bold color=\"cyan\">\n Select Mode\n </Text>\n <Box marginY={1}>\n <SelectInput\n initialIndex={modes.indexOf(mode)}\n itemComponent={({ isSelected, label }) => {\n const modeKey = modeItems.find((m) => m.label === label)?.value as ChatMode;\n const modeInfo = modeKey ? CHAT_MODES[modeKey] : null;\n return (\n <Box>\n <Text bold={isSelected} color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n {label}\n </Text>\n {isSelected && modeInfo && <Text dimColor> - {modeInfo.description}</Text>}\n </Box>\n );\n }}\n items={modeItems}\n onSelect={(item) => {\n setMode(item.value as ChatMode);\n setShowModeSelector(false);\n }}\n />\n </Box>\n <Text dimColor>Tab to close</Text>\n </Box>\n )}\n\n {/* Image picker modal */}\n {showImagePicker && (\n <Box\n borderColor=\"yellow\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginX={1}\n paddingX={2}\n paddingY={1}\n width={80}\n >\n <Text bold color=\"yellow\">\n 📷 Attach Image\n </Text>\n <Box flexDirection=\"column\" marginY={1}>\n <Text dimColor>Enter URL or local path (supports ~ and https://):</Text>\n <Box marginTop={1} width={70}>\n <Text color=\"yellow\">> </Text>\n <TextInput\n key={imagePickerKey}\n onChange={setImagePathInput}\n onSubmit={(imagePath) => {\n if (!imagePath.trim()) {\n setShowImagePicker(false);\n setImagePickerKey((k) => k + 1);\n return;\n }\n\n const result = processImagePath(imagePath);\n if (result.success && result.imageSource) {\n setAttachedImages((imgs) => [...imgs, result.imageSource!]);\n setMessages((m) => [...m, { role: \"system\", content: result.message }]);\n } else if (result.message) {\n setMessages((m) => [...m, { role: \"system\", content: result.message }]);\n }\n\n setShowImagePicker(false);\n setImagePathInput(\"\");\n setImagePickerKey((k) => k + 1);\n }}\n placeholder=\"https://... or ~/path/to/image.png\"\n value={imagePathInput}\n />\n </Box>\n {imagePathInput && (\n <Box marginTop={1}>\n <Text dimColor wrap=\"truncate-end\">\n Path: {imagePathInput}\n </Text>\n </Box>\n )}\n {attachedImages.length > 0 && (\n <Box marginTop={1}>\n <Text color=\"green\">✓ {attachedImages.length} image(s) attached</Text>\n </Box>\n )}\n </Box>\n <Text dimColor>Enter to attach • Esc to cancel • Empty to close</Text>\n </Box>\n )}\n\n {/* Header with mode and context info */}\n {!(showModeSelector || showImagePicker) && (\n <Box justifyContent=\"space-between\" paddingX={1}>\n <Text color=\"gray\">\n [{CHAT_MODES[mode].symbol}] {CHAT_MODES[mode].name}\n {agentMode && <Text color=\"cyan\"> +tools</Text>}\n <Text dimColor> | </Text>\n {/* Action bar - Tab to focus, arrows to navigate, Enter to toggle */}\n {actionBarFocused ? (\n <>\n <Text\n backgroundColor={selectedAction === 0 ? \"magenta\" : undefined}\n bold={selectedAction === 0}\n color={selectedAction === 0 ? \"white\" : thinkingMode ? \"magenta\" : \"gray\"}\n >\n Think:{thinkingMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n <Text\n backgroundColor={selectedAction === 1 ? \"green\" : undefined}\n bold={selectedAction === 1}\n color={selectedAction === 1 ? \"white\" : agentMode ? \"green\" : \"gray\"}\n >\n Agent:{agentMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n <Text\n backgroundColor={selectedAction === 2 ? \"cyan\" : undefined}\n bold={selectedAction === 2}\n color={selectedAction === 2 ? \"white\" : voiceMode ? \"cyan\" : \"gray\"}\n >\n Voice:{voiceMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n {supportsVision && (\n <>\n <Text\n backgroundColor={selectedAction === 3 ? \"blue\" : undefined}\n bold={selectedAction === 3}\n color={\n selectedAction === 3 ? \"white\" : attachedImages.length > 0 ? \"blue\" : \"gray\"\n }\n >\n Image{attachedImages.length > 0 ? `:${attachedImages.length}` : \"\"}\n </Text>\n <Text dimColor> </Text>\n </>\n )}\n <Text\n backgroundColor={selectedAction === (supportsVision ? 4 : 3) ? \"red\" : undefined}\n bold={selectedAction === (supportsVision ? 4 : 3)}\n color={\n selectedAction === (supportsVision ? 4 : 3)\n ? \"white\"\n : recording\n ? \"red\"\n : \"gray\"\n }\n >\n {recording ? `Recording:${recordingElapsed}s` : \"`Record\"}\n </Text>\n <Text dimColor> (arrows, Enter)</Text>\n </>\n ) : (\n <>\n <Text color={thinkingMode ? \"magenta\" : \"gray\"}>\n Think:{thinkingMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> </Text>\n <Text color={agentMode ? \"green\" : \"gray\"}>Agent:{agentMode ? \"on\" : \"off\"}</Text>\n <Text dimColor> </Text>\n <Text color={voiceMode ? \"cyan\" : \"gray\"}>Voice:{voiceMode ? \"on\" : \"off\"}</Text>\n <Text dimColor> </Text>\n {supportsVision && (\n <>\n <Text color={attachedImages.length > 0 ? \"blue\" : \"gray\"}>\n Image{attachedImages.length > 0 ? `:${attachedImages.length}` : \"\"}\n </Text>\n <Text dimColor> </Text>\n </>\n )}\n <Text color={recording ? \"red\" : \"gray\"}>\n {recording ? `Recording:${recordingElapsed}s` : \"`Record\"}\n </Text>\n </>\n )}\n <Text dimColor> | /help</Text>\n </Text>\n <Box>\n {lastTokPerSec !== undefined && lastTokPerSec > 0 && (\n <>\n <Text color=\"yellow\">{lastTokPerSec.toFixed(1)}</Text>\n <Text dimColor> tok/s</Text>\n {avgTokPerSec !== undefined && avgTokPerSec > 0 && (\n <>\n <Text dimColor> (avg </Text>\n <Text color=\"cyan\">{avgTokPerSec.toFixed(1)}</Text>\n <Text dimColor>)</Text>\n </>\n )}\n <Text dimColor> </Text>\n </>\n )}\n <Text color={contextPercent > 80 ? \"red\" : contextPercent > 50 ? \"yellow\" : \"green\"}>\n {currentTokens.toLocaleString()}/{maxContext.toLocaleString()} tokens (\n {contextPercent}%)\n </Text>\n </Box>\n </Box>\n )}\n\n {/* Messages */}\n <Box flexDirection=\"column\" flexGrow={1} overflowY=\"hidden\">\n {messages.length === 0 && !generating && (\n <Box padding={1}>\n <Text color=\"gray\">Start a conversation. Type a message and press Enter.</Text>\n </Box>\n )}\n\n {messages.map((msg, i) => (\n <Box key={i} marginBottom={1} paddingX={1}>\n {msg.role === \"user\" && (\n <Box>\n <Text bold color=\"cyan\">\n You:{\" \"}\n </Text>\n {msg.images && msg.images.length > 0 && (\n <Text color=\"yellow\">\n 📷{msg.images.length > 1 ? `×${msg.images.length}` : \"\"}{\" \"}\n </Text>\n )}\n <Text>{msg.content}</Text>\n </Box>\n )}\n {msg.role === \"thinking\" && (\n <Box flexDirection=\"column\">\n <Text color=\"gray\" dimColor italic>\n {\"<think>\"}\n </Text>\n <Text dimColor italic>\n {msg.content}\n </Text>\n </Box>\n )}\n {msg.role === \"assistant\" && (\n <Box>\n <Text bold color=\"cyan\">\n {CHAT_MODES[mode].symbol}{\" \"}\n </Text>\n <Box flexDirection=\"column\" flexGrow={1}>\n {renderMarkdown(msg.content)}\n </Box>\n </Box>\n )}\n {msg.role === \"system\" &&\n (msg.content.startsWith(\"🔧 Calling\") ? (\n <Text color=\"cyan\" dimColor>\n ◆{\" \"}\n {msg.content\n .replace(\"🔧 Calling: \", \"\")\n .replace(\"gerbil_docs\", \"docs\")\n .replace(\"run_skill\", \"skill\")}\n </Text>\n ) : msg.content.startsWith(\"📄\") ? (\n <Box borderColor=\"gray\" borderStyle=\"single\" marginBottom={1} paddingX={1}>\n <Text color=\"gray\">\n {msg.content.replace(\"📄 Result:\\n\", \"\").slice(0, 300)}\n {msg.content.replace(\"📄 Result:\\n\", \"\").length > 300 ? \"...\" : \"\"}\n </Text>\n </Box>\n ) : msg.content.startsWith(\"🧠\") ? (\n <Text color=\"magenta\" dimColor>\n ◆ {msg.content}\n </Text>\n ) : msg.content.startsWith(\"🔧 Agent\") ? (\n <Text color=\"green\" dimColor>\n ◆ {msg.content}\n </Text>\n ) : (\n <Text color=\"yellow\" dimColor>\n {msg.content}\n </Text>\n ))}\n </Box>\n ))}\n\n {/* Streaming response */}\n {generating && streamedContent && (\n <StreamingResponse\n content={streamedContent}\n showThinking={thinkingMode}\n symbol={CHAT_MODES[mode].symbol}\n />\n )}\n\n {generating && !streamedContent && (\n <Box paddingX={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text color=\"gray\"> Thinking...</Text>\n </Box>\n )}\n\n {summarizing && (\n <Box paddingX={1}>\n <Text color=\"yellow\">\n <Spinner type=\"dots\" />\n </Text>\n <Text color=\"gray\"> Summarizing...</Text>\n </Box>\n )}\n </Box>\n\n {/* Context warning */}\n {contextPercent > 80 && (\n <Box paddingX={1}>\n <Text color=\"red\">Context {contextPercent}% full. Use /summarize to compress.</Text>\n </Box>\n )}\n\n {/* Input (hidden when image picker is open) */}\n {!showImagePicker && (\n <Box\n borderColor={generating || summarizing ? \"gray\" : \"cyan\"}\n borderStyle=\"single\"\n paddingX={1}\n >\n {attachedImages.length > 0 && (\n <Text color=\"yellow\">\n 📷{attachedImages.length > 1 ? `×${attachedImages.length}` : \"\"}{\" \"}\n </Text>\n )}\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder={\n recording\n ? `🎤 ${recordingStatus || \"recording...\"}`\n : generating\n ? \"generating...\"\n : summarizing\n ? \"summarizing...\"\n : attachedImages.length > 0\n ? \"describe the image...\"\n : actionBarFocused\n ? \"←→ navigate, Enter to toggle, Esc to exit\"\n : \"type a message... (Tab for actions)\"\n }\n value={input}\n />\n </Box>\n )}\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\n// Copy to clipboard utility\nfunction copyToClipboard(text: string): Promise<boolean> {\n return new Promise((resolve) => {\n const platform = os.platform();\n let cmd: string;\n\n if (platform === \"darwin\") {\n cmd = \"pbcopy\";\n } else if (platform === \"linux\") {\n cmd = \"xclip -selection clipboard\";\n } else if (platform === \"win32\") {\n cmd = \"clip\";\n } else {\n resolve(false);\n return;\n }\n\n const proc = exec(cmd, (err) => {\n resolve(!err);\n });\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n\n// File extension map for languages\nconst LANG_EXTENSIONS: Record<string, string> = {\n typescript: \"ts\",\n javascript: \"js\",\n python: \"py\",\n rust: \"rs\",\n go: \"go\",\n java: \"java\",\n cpp: \"cpp\",\n c: \"c\",\n ruby: \"rb\",\n php: \"php\",\n swift: \"swift\",\n kotlin: \"kt\",\n sql: \"sql\",\n bash: \"sh\",\n shell: \"sh\",\n html: \"html\",\n css: \"css\",\n json: \"json\",\n yaml: \"yaml\",\n markdown: \"md\",\n};\n\ntype CodeViewProps = {\n gerbil: Gerbil;\n};\n\ntype Phase = \"input\" | \"generating\" | \"result\";\n\nexport function CodeView({ gerbil }: CodeViewProps) {\n const [phase, setPhase] = useState<Phase>(\"input\");\n const [prompt, setPrompt] = useState(\"\");\n const [code, setCode] = useState(\"\");\n const [language, setLanguage] = useState(\"typescript\");\n const [error, setError] = useState<string | null>(null);\n const [saveStatus, setSaveStatus] = useState<string | null>(null);\n const [copyStatus, setCopyStatus] = useState<string | null>(null);\n\n const handleSave = async () => {\n if (!code || phase !== \"result\" || error) {\n return;\n }\n\n // Generate filename from prompt (sanitized)\n const baseName =\n prompt\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 30) || \"generated\";\n\n const ext = LANG_EXTENSIONS[language] || \"txt\";\n const fileName = `${baseName}.${ext}`;\n const filePath = path.join(process.cwd(), fileName);\n\n try {\n fs.writeFileSync(filePath, code, \"utf-8\");\n setSaveStatus(`Saved to ${fileName}`);\n setTimeout(() => setSaveStatus(null), 3000);\n } catch (err) {\n setSaveStatus(`Error: ${err}`);\n setTimeout(() => setSaveStatus(null), 3000);\n }\n };\n\n const handleCopy = async () => {\n if (!code || phase !== \"result\" || error) {\n return;\n }\n\n const success = await copyToClipboard(code);\n setCopyStatus(success ? \"Copied to clipboard!\" : \"Failed to copy\");\n setTimeout(() => setCopyStatus(null), 2000);\n };\n\n const handleSubmit = async (value: string) => {\n if (!value.trim()) {\n return;\n }\n\n setPrompt(value);\n setPhase(\"generating\");\n setError(null);\n\n try {\n const result = await gerbil.generate(value, {\n maxTokens: 1000,\n temperature: 0.3,\n system: `You are an expert programmer. Generate clean, working code.\nWhen asked to generate code:\n1. Output ONLY the code, no explanations before or after\n2. Use proper formatting and comments within the code\n3. Make the code complete and runnable\n4. Detect the appropriate language from the request`,\n });\n\n // Try to detect language and clean up code\n let generatedCode = result.text;\n\n // Extract language from code fence if present\n const langMatch = generatedCode.match(/```(\\w+)\\n/);\n if (langMatch) {\n setLanguage(langMatch[1]);\n }\n\n // Remove code fences and think tags\n generatedCode = generatedCode\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\") // Remove think blocks\n .replace(/```\\w*\\n?/g, \"\")\n .replace(/```\\n?/g, \"\")\n .trim();\n\n setCode(generatedCode);\n setPhase(\"result\");\n } catch (err) {\n setError(String(err));\n setPhase(\"result\");\n }\n };\n\n const handleReset = () => {\n setPhase(\"input\");\n setPrompt(\"\");\n setCode(\"\");\n setError(null);\n };\n\n useInput((input, key) => {\n if (key.escape && phase === \"result\") {\n handleReset();\n }\n\n // Save and copy shortcuts when code is generated\n if (phase === \"result\" && !error) {\n if (input === \"s\" || input === \"S\") {\n handleSave();\n }\n if (input === \"c\" || input === \"C\") {\n handleCopy();\n }\n }\n });\n\n if (phase === \"input\") {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold>💻 Generate Code</Text>\n </Box>\n <Box marginBottom={1}>\n <Text color=\"gray\">Describe what code you want to generate:</Text>\n </Box>\n <Box borderColor=\"cyan\" borderStyle=\"single\" paddingX={1}>\n <Text color=\"cyan\">❯ </Text>\n <TextInput\n onChange={setPrompt}\n onSubmit={handleSubmit}\n placeholder=\"e.g., A function that calculates fibonacci numbers\"\n value={prompt}\n />\n </Box>\n <Box marginTop={1}>\n <Text color=\"gray\" dimColor>\n Examples:\n </Text>\n </Box>\n <Text color=\"gray\" dimColor>\n • A React hook for debouncing\n </Text>\n <Text color=\"gray\" dimColor>\n • Python script to parse CSV files\n </Text>\n <Text color=\"gray\" dimColor>\n • SQL query to find duplicate records\n </Text>\n </Box>\n );\n }\n\n if (phase === \"generating\") {\n return (\n <Box padding={1}>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Generating code...</Text>\n </Box>\n );\n }\n\n // Result phase\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color={error ? \"red\" : \"green\"}>\n {error ? \"[x] Error\" : \"[done] Generated Code\"}\n </Text>\n {!error && <Text color=\"gray\"> -- {language}</Text>}\n </Box>\n\n {!error && (\n <Box marginBottom={1}>\n <Text color=\"gray\" dimColor>\n Prompt: {prompt}\n </Text>\n </Box>\n )}\n\n <Box\n borderColor={error ? \"red\" : \"cyan\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n padding={1}\n >\n <Text color={error ? \"red\" : \"white\"} wrap=\"wrap\">\n {error || code}\n </Text>\n </Box>\n\n {/* Status messages */}\n {saveStatus && (\n <Box marginTop={1}>\n <Text color={saveStatus.startsWith(\"Error\") ? \"red\" : \"green\"}>{saveStatus}</Text>\n </Box>\n )}\n {copyStatus && (\n <Box marginTop={1}>\n <Text color={copyStatus.includes(\"Failed\") ? \"red\" : \"green\"}>{copyStatus}</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n {error ? (\n <Text dimColor>Esc to try again</Text>\n ) : (\n <Text dimColor>\n <Text color=\"yellow\">s</Text> save to file | <Text color=\"yellow\">c</Text> copy to\n clipboard | <Text color=\"gray\">Esc</Text> generate more\n </Text>\n )}\n </Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\n// Copy to clipboard utility\nfunction copyToClipboard(text: string): Promise<boolean> {\n return new Promise((resolve) => {\n const platform = os.platform();\n let cmd: string;\n\n if (platform === \"darwin\") {\n cmd = \"pbcopy\";\n } else if (platform === \"linux\") {\n cmd = \"xclip -selection clipboard\";\n } else if (platform === \"win32\") {\n cmd = \"clip\";\n } else {\n resolve(false);\n return;\n }\n\n const proc = exec(cmd, (err) => {\n resolve(!err);\n });\n proc.stdin?.write(text);\n proc.stdin?.end();\n });\n}\n\ntype CreateSkillViewProps = {\n gerbil: Gerbil;\n onDone: () => void;\n};\n\ntype Step = \"name\" | \"description\" | \"behavior\" | \"generating\" | \"done\";\n\ntype SkillDraft = {\n name: string;\n description: string;\n behavior: string;\n};\n\n// Step definitions for visual progress\nconst STEPS = [\n { key: \"name\", num: 1, title: \"Name\", hint: \"Give your skill a unique identifier\" },\n { key: \"description\", num: 2, title: \"Purpose\", hint: \"What does this skill do?\" },\n { key: \"behavior\", num: 3, title: \"Logic\", hint: \"How should it work?\" },\n] as const;\n\nexport function CreateSkillView({ gerbil, onDone }: CreateSkillViewProps) {\n const [step, setStep] = useState<Step>(\"name\");\n const [draft, setDraft] = useState<SkillDraft>({\n name: \"\",\n description: \"\",\n behavior: \"\",\n });\n const [input, setInput] = useState(\"\");\n const [generatedCode, setGeneratedCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [saveStatus, setSaveStatus] = useState<string | null>(null);\n const [copyStatus, setCopyStatus] = useState<string | null>(null);\n\n const currentStepNum =\n step === \"name\" ? 1 : step === \"description\" ? 2 : step === \"behavior\" ? 3 : 4;\n\n const handleSubmit = async (value: string) => {\n if (!value.trim()) {\n return;\n }\n\n if (step === \"name\") {\n // Validate kebab-case\n const kebabCase = value\n .toLowerCase()\n .replace(/\\s+/g, \"-\")\n .replace(/[^a-z0-9-]/g, \"\");\n setDraft((d) => ({ ...d, name: kebabCase }));\n setInput(\"\");\n setStep(\"description\");\n } else if (step === \"description\") {\n setDraft((d) => ({ ...d, description: value }));\n setInput(\"\");\n setStep(\"behavior\");\n } else if (step === \"behavior\") {\n setDraft((d) => ({ ...d, behavior: value }));\n setInput(\"\");\n setStep(\"generating\");\n await generateSkill({ ...draft, behavior: value });\n }\n };\n\n // Render the step progress indicator\n const renderProgress = () => (\n <Box flexDirection=\"column\" marginBottom={1}>\n {/* Progress bar */}\n <Box marginBottom={1}>\n {STEPS.map((s, i) => {\n const isComplete = currentStepNum > s.num;\n const isCurrent = step === s.key;\n const _isPending = currentStepNum < s.num;\n\n return (\n <Box key={s.key}>\n {/* Step circle */}\n <Text bold={isCurrent} color={isComplete ? \"green\" : isCurrent ? \"cyan\" : \"gray\"}>\n {isComplete ? \"[x]\" : isCurrent ? \"[>]\" : \"[ ]\"}\n </Text>\n <Text bold={isCurrent} color={isComplete ? \"green\" : isCurrent ? \"cyan\" : \"gray\"}>\n {\" \"}\n {s.title}\n </Text>\n {/* Connector line */}\n {i < STEPS.length - 1 && <Text color={isComplete ? \"green\" : \"gray\"}> --- </Text>}\n </Box>\n );\n })}\n </Box>\n\n {/* Step details summary */}\n {(draft.name || draft.description) && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginBottom={1}\n paddingX={1}\n >\n {draft.name && (\n <Text>\n <Text dimColor>Name: </Text>\n <Text color=\"white\">{draft.name}</Text>\n </Text>\n )}\n {draft.description && (\n <Text>\n <Text dimColor>Purpose: </Text>\n <Text color=\"white\">{draft.description}</Text>\n </Text>\n )}\n </Box>\n )}\n </Box>\n );\n\n const generateSkill = async (skillDraft: SkillDraft) => {\n try {\n const prompt = `Generate a Gerbil skill file for:\nName: ${skillDraft.name}\nDescription: ${skillDraft.description}\nBehavior: ${skillDraft.behavior}\n\nGenerate TypeScript code that:\n1. Imports { defineSkill } from \"gerbil/skills\" and { z } from \"zod\"\n2. Defines an input schema with Zod\n3. Exports a skill using defineSkill()\n4. Implements the run function that uses gerbil.generate() or gerbil.json()\n\nOnly output the code, no explanations. The file should be complete and runnable.`;\n\n const result = await gerbil.generate(prompt, {\n maxTokens: 800,\n temperature: 0.3,\n system: \"You are an expert TypeScript developer. Generate clean, working code.\",\n });\n\n // Clean up the code - remove code fences and think tags\n let code = result.text;\n code = code\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\") // Remove think blocks\n .replace(/```typescript\\n?/g, \"\")\n .replace(/```\\n?/g, \"\")\n .trim();\n\n setGeneratedCode(code);\n setStep(\"done\");\n } catch (err) {\n setError(String(err));\n setStep(\"done\");\n }\n };\n\n const handleSave = async () => {\n if (!generatedCode || step !== \"done\") {\n return;\n }\n\n // Save to .gerbil/skills/ directory in cwd\n const gerbilDir = path.join(process.cwd(), \".gerbil\");\n const skillsDir = path.join(gerbilDir, \"skills\");\n const filePath = path.join(skillsDir, `${draft.name}.skill.ts`);\n\n try {\n // Create .gerbil/skills directory if it doesn't exist\n if (!fs.existsSync(skillsDir)) {\n fs.mkdirSync(skillsDir, { recursive: true });\n }\n\n fs.writeFileSync(filePath, generatedCode, \"utf-8\");\n setSaveStatus(`Saved to .gerbil/skills/${draft.name}.skill.ts`);\n\n // Clear status after 3 seconds\n setTimeout(() => setSaveStatus(null), 3000);\n } catch (err) {\n setSaveStatus(`Error: ${err}`);\n setTimeout(() => setSaveStatus(null), 3000);\n }\n };\n\n const handleCopy = async () => {\n if (!generatedCode || step !== \"done\") {\n return;\n }\n\n const success = await copyToClipboard(generatedCode);\n setCopyStatus(success ? \"Copied to clipboard!\" : \"Failed to copy\");\n setTimeout(() => setCopyStatus(null), 2000);\n };\n\n useInput((input, key) => {\n if (key.escape) {\n onDone();\n }\n\n // Save and copy shortcuts when skill is generated\n if (step === \"done\" && !error) {\n if (input === \"s\" || input === \"S\") {\n handleSave();\n }\n if (input === \"c\" || input === \"C\") {\n handleCopy();\n }\n }\n });\n\n const renderCurrentInput = () => {\n const currentStep = STEPS.find((s) => s.key === step);\n if (!currentStep) {\n return null;\n }\n\n const placeholders: Record<string, string> = {\n name: \"sentiment-analyzer, code-reviewer, email-writer...\",\n description: \"Analyzes text sentiment, Reviews code for issues...\",\n behavior:\n \"Input: text string. Output: { sentiment, confidence }. Use gerbil.json() for structured output...\",\n };\n\n const helpText: Record<string, string> = {\n name: \"Use lowercase letters, numbers, and hyphens only\",\n description: \"A clear, one-line description of what the skill does\",\n behavior: \"Describe inputs, outputs, and logic. Be specific about data types and structure.\",\n };\n\n return (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n {currentStep.hint}\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>{helpText[step]}</Text>\n </Box>\n <Box borderColor=\"cyan\" borderStyle=\"single\" paddingX={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder={placeholders[step]}\n value={input}\n />\n </Box>\n </Box>\n );\n };\n\n // Generating state\n if (step === \"generating\") {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>{renderProgress()}</Box>\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Generating skill code with AI...</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>This may take a few seconds</Text>\n </Box>\n </Box>\n );\n }\n\n // Done state\n if (step === \"done\") {\n if (error) {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>\n <Text bold color=\"red\">\n Error generating skill\n </Text>\n </Box>\n <Text color=\"red\">{error}</Text>\n <Box marginTop={1}>\n <Text dimColor>Esc go back</Text>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>\n <Text bold color=\"green\">\n [done] Skill Generated: {draft.name}\n </Text>\n </Box>\n <Box marginBottom={1}>\n <Text dimColor>\n Save this code as: <Text color=\"white\">.gerbil/skills/{draft.name}.skill.ts</Text>\n </Text>\n </Box>\n <Box borderColor=\"green\" borderStyle=\"single\" flexDirection=\"column\" padding={1}>\n <Text color=\"white\" wrap=\"wrap\">\n {generatedCode}\n </Text>\n </Box>\n\n {/* Status messages */}\n {saveStatus && (\n <Box marginTop={1}>\n <Text color={saveStatus.startsWith(\"Error\") ? \"red\" : \"green\"}>{saveStatus}</Text>\n </Box>\n )}\n {copyStatus && (\n <Box marginTop={1}>\n <Text color={copyStatus.includes(\"Failed\") ? \"red\" : \"green\"}>{copyStatus}</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">s</Text> save to .gerbil/skills/ | <Text color=\"yellow\">c</Text>{\" \"}\n copy to clipboard | <Text color=\"gray\">Esc</Text> go back\n </Text>\n </Box>\n </Box>\n );\n }\n\n // Input steps (name, description, behavior)\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n \\ Create New Skill\n </Text>\n <Box marginY={1}>{renderProgress()}</Box>\n {renderCurrentInput()}\n <Box marginTop={1}>\n <Text dimColor>Enter continue | Esc cancel</Text>\n </Box>\n </Box>\n );\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\ntype CreateToolViewProps = {\n gerbil: Gerbil;\n onDone: () => void;\n};\n\ntype Step = \"name\" | \"description\" | \"params\" | \"execute\" | \"generating\" | \"done\";\n\ntype ToolDraft = {\n name: string;\n description: string;\n params: string;\n execute: string;\n};\n\nconst STEPS = [\n { key: \"name\", num: 1, title: \"Name\", hint: \"Tool name (e.g., get_weather)\" },\n { key: \"description\", num: 2, title: \"Description\", hint: \"What does this tool do?\" },\n { key: \"params\", num: 3, title: \"Parameters\", hint: \"What inputs does it need?\" },\n { key: \"execute\", num: 4, title: \"Logic\", hint: \"What should it return?\" },\n] as const;\n\n// Template for tool files - simple format, no imports needed\nfunction buildToolCode(draft: ToolDraft, executeBody: string): string {\n return `// Gerbil Tool: ${draft.name}\n// No imports needed - just export a config object\n\nexport default {\n name: \"${draft.name}\",\n description: \"${draft.description}\",\n execute: async (params) => {\n${executeBody}\n },\n};\n`;\n}\n\nexport function CreateToolView({ gerbil, onDone }: CreateToolViewProps) {\n const [step, setStep] = useState<Step>(\"name\");\n const [draft, setDraft] = useState<ToolDraft>({\n name: \"\",\n description: \"\",\n params: \"\",\n execute: \"\",\n });\n const [input, setInput] = useState(\"\");\n const [generatedCode, setGeneratedCode] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n const [saveStatus, setSaveStatus] = useState<string | null>(null);\n\n const currentStepNum =\n step === \"name\"\n ? 1\n : step === \"description\"\n ? 2\n : step === \"params\"\n ? 3\n : step === \"execute\"\n ? 4\n : 5;\n\n useInput((char, key) => {\n if (key.escape && step === \"done\") {\n onDone();\n }\n if ((char === \"s\" || char === \"S\") && step === \"done\" && !error) {\n saveToFile();\n }\n });\n\n const handleSubmit = async (value: string) => {\n if (step === \"name\") {\n if (!value.trim()) {\n return; // Name is required\n }\n const snakeCase = value\n .toLowerCase()\n .replace(/\\s+/g, \"_\")\n .replace(/[^a-z0-9_]/g, \"\");\n setDraft((d) => ({ ...d, name: snakeCase }));\n setInput(\"\");\n setStep(\"description\");\n } else if (step === \"description\") {\n if (!value.trim()) {\n return; // Description is required\n }\n setDraft((d) => ({ ...d, description: value }));\n setInput(\"\");\n setStep(\"params\");\n } else if (step === \"params\") {\n // Params are optional - can press enter to skip\n setDraft((d) => ({ ...d, params: value.trim() || \"(none)\" }));\n setInput(\"\");\n setStep(\"execute\");\n } else if (step === \"execute\") {\n if (!value.trim()) {\n return; // Logic is required\n }\n setDraft((d) => ({ ...d, execute: value }));\n setInput(\"\");\n setStep(\"generating\");\n await generateTool({ ...draft, execute: value });\n }\n };\n\n const cleanCode = (text: string): string => {\n return text\n .replace(/<think>[\\s\\S]*?<\\/think>/g, \"\") // Remove think blocks\n .replace(/<\\/?think>/g, \"\") // Remove unclosed think tags\n .replace(/^```[\\w]*\\n?/gm, \"\") // Remove code fence starts\n .replace(/```$/gm, \"\") // Remove code fence ends\n .replace(/\\*\\*/g, \"\") // Remove markdown bold **\n .replace(/`([^`]+)`/g, \"$1\") // Remove inline code backticks\n .trim();\n };\n\n const generateTool = async (finalDraft: ToolDraft) => {\n try {\n // Generate just the execute function body\n const hasParams = finalDraft.params !== \"(none)\";\n const executePrompt = `Write a simple function body for: ${finalDraft.execute}\n${hasParams ? `params has: ${finalDraft.params}` : \"\"}\nUse params?.fieldName with defaults. Return a string.\n\nExample:\n const value = params?.name || \"default\";\n return \\`Result: \\${value}\\`;`;\n\n let executeBody = \"\";\n for await (const chunk of gerbil.stream(executePrompt, {\n maxTokens: 200,\n system: \"Output only function body code. No markdown. No backticks. Must return a string.\",\n })) {\n executeBody += chunk;\n }\n executeBody = cleanCode(executeBody);\n\n // Ensure proper indentation\n if (!executeBody.startsWith(\" \")) {\n executeBody = executeBody\n .split(\"\\n\")\n .map((line) => ` ${line.trim()}`)\n .join(\"\\n\");\n }\n\n // Build final code from template\n const code = buildToolCode(finalDraft, executeBody);\n setGeneratedCode(code);\n setStep(\"done\");\n } catch (e) {\n setError(`${e}`);\n setStep(\"done\");\n }\n };\n\n const saveToFile = () => {\n try {\n const toolsDir = path.join(process.cwd(), \".gerbil\", \"tools\");\n if (!fs.existsSync(toolsDir)) {\n fs.mkdirSync(toolsDir, { recursive: true });\n }\n const filePath = path.join(toolsDir, `${draft.name}.tool.ts`);\n fs.writeFileSync(filePath, generatedCode);\n setSaveStatus(`Saved to .gerbil/tools/${draft.name}.tool.ts`);\n } catch (e) {\n setSaveStatus(`Error: ${e}`);\n }\n };\n\n const renderProgress = () => (\n <Box marginBottom={1}>\n {STEPS.map((s, i) => {\n const isComplete = currentStepNum > s.num;\n const isCurrent = step === s.key;\n return (\n <Box key={s.key} marginRight={1}>\n <Text color={isComplete ? \"green\" : isCurrent ? \"cyan\" : \"gray\"}>\n {isComplete ? \"[✓]\" : isCurrent ? \"[>]\" : \"[ ]\"} {s.title}\n </Text>\n {i < STEPS.length - 1 && <Text dimColor> --- </Text>}\n </Box>\n );\n })}\n </Box>\n );\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n {renderProgress()}\n\n {/* Current draft */}\n <Box\n borderColor=\"gray\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginBottom={1}\n paddingX={1}\n >\n <Text>\n <Text color=\"cyan\">Name:</Text> {draft.name || \"...\"}\n </Text>\n <Text>\n <Text color=\"cyan\">Description:</Text> {draft.description || \"...\"}\n </Text>\n <Text>\n <Text color=\"cyan\">Parameters:</Text> {draft.params || \"...\"}\n </Text>\n <Text>\n <Text color=\"cyan\">Logic:</Text> {draft.execute || \"...\"}\n </Text>\n </Box>\n\n {/* Step content */}\n {step === \"name\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Tool Name\n </Text>\n <Text dimColor>Use snake_case (e.g., get_weather, search_docs)</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"my_tool\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"description\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Description\n </Text>\n <Text dimColor>What does this tool do? (one sentence)</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"Gets the current weather for a city\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"params\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Parameters\n </Text>\n <Text dimColor>What inputs does this tool need? (press Enter to skip)</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"theme as a string (optional)\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"execute\" && (\n <Box flexDirection=\"column\">\n <Text bold color=\"cyan\">\n Logic\n </Text>\n <Text dimColor>What should this tool do and return?</Text>\n <Box marginTop={1}>\n <Text color=\"cyan\">> </Text>\n <TextInput\n onChange={setInput}\n onSubmit={handleSubmit}\n placeholder=\"fetch weather from API and return temperature\"\n value={input}\n />\n </Box>\n </Box>\n )}\n\n {step === \"generating\" && (\n <Box flexDirection=\"column\">\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Generating execute function...</Text>\n </Box>\n <Text dimColor>Using template with your inputs</Text>\n </Box>\n )}\n\n {step === \"done\" && (\n <Box flexDirection=\"column\">\n {error ? (\n <Text color=\"red\">Error: {error}</Text>\n ) : (\n <>\n <Text bold color=\"green\">\n ✓ Tool Generated\n </Text>\n <Box\n borderColor=\"green\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text color=\"gray\">\n {generatedCode.slice(0, 500)}\n {generatedCode.length > 500 ? \"...\" : \"\"}\n </Text>\n </Box>\n\n <Box>\n <Text dimColor>Press </Text>\n <Text color=\"yellow\">s</Text>\n <Text dimColor> to save to .gerbil/tools/ | </Text>\n <Text color=\"gray\">Esc</Text>\n <Text dimColor> to go back</Text>\n </Box>\n\n {saveStatus && (\n <Box marginTop={1}>\n <Text color={saveStatus.startsWith(\"Error\") ? \"red\" : \"green\"}>{saveStatus}</Text>\n </Box>\n )}\n </>\n )}\n </Box>\n )}\n </Box>\n );\n}\n","import { Box, Text, useInput } from \"ink\";\nimport { useState } from \"react\";\nimport { hyperlink } from \"../utils.js\";\n\n// ============================================\n// Capability Tabs\n// ============================================\n\nconst CAPABILITIES = [\n { id: \"text\", name: \"Text\" },\n { id: \"vision\", name: \"Vision\" },\n { id: \"tts\", name: \"Text to Speech\" },\n { id: \"stt\", name: \"Transcription\" },\n { id: \"embeddings\", name: \"Embeddings\" },\n] as const;\n\ntype CapabilityId = (typeof CAPABILITIES)[number][\"id\"];\n\n// ============================================\n// Frameworks\n// ============================================\n\nconst FRAMEWORKS = [\n { id: \"standalone\", name: \"Standalone\" },\n { id: \"ai-sdk\", name: \"AI SDK v5\" },\n { id: \"browser\", name: \"React Hooks\" },\n { id: \"nextjs\", name: \"Next.js\" },\n { id: \"express\", name: \"Express\" },\n { id: \"langchain\", name: \"LangChain\" },\n] as const;\n\ntype FrameworkId = (typeof FRAMEWORKS)[number][\"id\"];\n\n// ============================================\n// Examples Data\n// ============================================\n\ninterface Example {\n install: string;\n code: string;\n description: string;\n}\n\nconst EXAMPLES: Record<CapabilityId, Partial<Record<FrameworkId, Example>>> = {\n // ============================================\n // TEXT\n // ============================================\n text: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Direct Gerbil usage for text generation with streaming.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"qwen3-0.6b\");\n\n// Generate text\nconst result = await g.generate(\"Write a haiku\", {\n maxTokens: 100,\n temperature: 0.7,\n});\nconsole.log(result.text);\n\n// Stream\nfor await (const chunk of g.stream(\"Tell me a story\")) {\n process.stdout.write(chunk);\n}\n\n// With thinking mode\nconst math = await g.generate(\"What is 127 × 43?\", { thinking: true });\nconsole.log(math.thinking); // Shows reasoning\nconsole.log(math.text); // \"5461\"`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"Vercel AI SDK v5 with LanguageModelV2 interface.\",\n code: `import { generateText, streamText } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\n\n// Generate\nconst { text } = await generateText({\n model: gerbil(\"qwen3-0.6b\"),\n prompt: \"Explain quantum computing\",\n});\n\n// Stream\nconst { textStream } = streamText({\n model: gerbil(\"qwen3-0.6b\"),\n system: \"You are a helpful assistant.\",\n messages: [{ role: \"user\", content: \"Hello!\" }],\n});\n\nfor await (const chunk of textStream) {\n process.stdout.write(chunk);\n}`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hooks for browser-based inference with WebGPU.\",\n code: `import { useChat, useCompletion } from \"@tryhamster/gerbil/browser\";\n\n// useChat - Full chat with message history\nfunction Chat() {\n const { messages, input, setInput, handleSubmit, isLoading } = useChat({\n model: \"qwen3-0.6b\",\n thinking: true,\n });\n if (isLoading) return <div>Loading...</div>;\n return (\n <form onSubmit={handleSubmit}>\n {messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)}\n <input value={input} onChange={e => setInput(e.target.value)} />\n </form>\n );\n}\n\n// useCompletion - One-off generation\nfunction Generator() {\n const { complete, completion, isLoading } = useCompletion();\n if (isLoading) return <div>Loading...</div>;\n return <button onClick={() => complete(\"Write a haiku\")}>{completion}</button>;\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js App Router with streaming chat endpoint.\",\n code: `// app/api/chat/route.ts\nimport { gerbil } from \"@tryhamster/gerbil/next\";\n\nexport const POST = gerbil.handler({ \n model: \"qwen3-0.6b\",\n system: \"You are a helpful assistant.\",\n});\n\n// Client usage with AI SDK React:\n// import { useChat } from \"@ai-sdk/react\";\n// const { messages, input, handleSubmit } = useChat();`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express middleware with generate and stream endpoints.\",\n code: `import express from \"express\";\nimport { gerbil } from \"@tryhamster/gerbil/express\";\n\nconst app = express();\napp.use(express.json());\napp.use(\"/api/ai\", gerbil({ model: \"qwen3-0.6b\" })());\n\n// POST /api/ai/generate { prompt, options }\n// POST /api/ai/stream { prompt, options } (SSE)\n// GET /api/ai/info\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain LLM wrapper for chains and agents.\",\n code: `import { GerbilLLM } from \"@tryhamster/gerbil/langchain\";\nimport { PromptTemplate } from \"langchain/prompts\";\nimport { LLMChain } from \"langchain/chains\";\n\nconst llm = new GerbilLLM({ \n model: \"qwen3-0.6b\",\n temperature: 0.7,\n});\n\nconst prompt = PromptTemplate.fromTemplate(\"Summarize: {text}\");\nconst chain = new LLMChain({ llm, prompt });\nconst result = await chain.call({ text: \"...\" });`,\n },\n },\n\n // ============================================\n // VISION\n // ============================================\n vision: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Vision models for image understanding and description.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\nawait g.loadModel(\"ministral-3b\"); // Vision-capable model\n\n// Describe an image\nconst result = await g.generate(\"What's in this image?\", {\n images: [{ source: \"https://example.com/photo.jpg\" }]\n});\nconsole.log(result.text);\n\n// Compare images\nconst diff = await g.generate(\"What's different?\", {\n images: [\n { source: \"before.jpg\" },\n { source: \"after.jpg\" }\n ]\n});\n\n// Check if model supports vision\nconsole.log(g.supportsVision()); // true`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK with image content parts.\",\n code: `import { generateText } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\n\nconst { text } = await generateText({\n model: gerbil(\"ministral-3b\"),\n messages: [{\n role: \"user\",\n content: [\n { type: \"image\", image: new URL(\"https://example.com/photo.jpg\") },\n { type: \"text\", text: \"Describe this image in detail\" },\n ],\n }],\n});\n\n// Also accepts:\n// - Base64 strings: { type: \"image\", image: \"data:image/png;base64,...\" }\n// - Uint8Array: { type: \"image\", image: bytes, mimeType: \"image/png\" }`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hooks with image attachment support.\",\n code: `import { useChat } from \"@tryhamster/gerbil/browser\";\n\nfunction VisionChat() {\n const { messages, input, setInput, handleSubmit, attachImage, attachedImages } = useChat({\n model: \"ministral-3b\"\n });\n\n const handleFile = (e) => {\n const file = e.target.files?.[0];\n if (file) {\n const reader = new FileReader();\n reader.onload = () => attachImage(reader.result);\n reader.readAsDataURL(file);\n }\n };\n\n return (\n <div>\n {messages.map(m => <div key={m.id}>{m.content}</div>)}\n <input type=\"file\" accept=\"image/*\" onChange={handleFile} />\n {attachedImages.length > 0 && <span>📎 {attachedImages.length} attached</span>}\n <form onSubmit={handleSubmit}>\n <input value={input} onChange={e => setInput(e.target.value)} />\n </form>\n </div>\n );\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js vision endpoint with image handling.\",\n code: `// app/api/vision/route.ts\nimport { gerbil } from \"@tryhamster/gerbil/next\";\n\nexport const POST = gerbil.handler({ model: \"ministral-3b\" });\n\n// Client fetch:\n// await fetch(\"/api/vision\", {\n// method: \"POST\",\n// body: JSON.stringify({\n// prompt: \"What's in this image?\",\n// images: [{ source: dataUri }]\n// })\n// });`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express vision endpoint with image input.\",\n code: `import express from \"express\";\nimport { gerbil } from \"@tryhamster/gerbil/express\";\n\nconst app = express();\napp.use(express.json({ limit: \"10mb\" })); // For large images\napp.use(\"/api/vision\", gerbil({ model: \"ministral-3b\" })());\n\n// POST /api/vision/generate\n// Body: { prompt: \"Describe this\", images: [{ source: \"...\" }] }\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain LLM with vision support.\",\n code: `import { GerbilLLM } from \"@tryhamster/gerbil/langchain\";\n\nconst llm = new GerbilLLM({ model: \"ministral-3b\" });\n\n// Check vision support\nconst hasVision = await llm.supportsVision();\n\n// Generate with images\nconst result = await llm.invokeWithImages(\n \"Describe what you see in this image\",\n [{ source: \"https://example.com/photo.jpg\" }]\n);\n\nconsole.log(result);`,\n },\n },\n\n // ============================================\n // TTS (Text-to-Speech)\n // ============================================\n tts: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Kokoro TTS with 28 natural voices.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\n// Generate speech\nconst result = await g.speak(\"Hello, I'm Gerbil!\", {\n voice: \"af_heart\", // American female (best quality)\n speed: 1.0\n});\n\n// result.audio = Float32Array (PCM samples)\n// result.sampleRate = 24000\n// result.duration = seconds\n\n// Stream long text\nfor await (const chunk of g.speakStream(\"Long paragraph...\")) {\n // Play each chunk as it's generated\n console.log(\\`Chunk: \\${chunk.samples.length} samples\\`);\n}\n\n// List voices\nconst voices = g.listVoices();\n// af_heart, bf_emma, am_fenrir, bm_george, ...`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK SpeechModelV2 interface.\",\n code: `import { experimental_generateSpeech as generateSpeech } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\n\nconst result = await generateSpeech({\n model: gerbil.speech(), // kokoro-82m\n text: \"Hello from Gerbil!\",\n voice: \"bf_emma\", // British female\n});\n\n// result.audio = Uint8Array (WAV format)\nawait writeFile(\"output.wav\", result.audio);\n\n// List available voices\nconst voices = gerbil.listVoices();\n// [{ id: \"af_heart\", name: \"Heart\", gender: \"female\", language: \"en-US\" }, ...]`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hook for browser TTS with playback.\",\n code: `import { useSpeech } from \"@tryhamster/gerbil/browser\";\n\nfunction SpeechDemo() {\n const { speak, stop, isSpeaking, isLoading, listVoices, setVoice } = useSpeech();\n\n if (isLoading) return <div>Loading TTS model...</div>;\n\n return (\n <div>\n <select onChange={e => setVoice(e.target.value)}>\n {listVoices().map(v => (\n <option key={v.id} value={v.id}>{v.name} ({v.language})</option>\n ))}\n </select>\n <button onClick={() => speak(\"Hello world!\")}>\n {isSpeaking ? \"Speaking...\" : \"Speak\"}\n </button>\n {isSpeaking && <button onClick={stop}>Stop</button>}\n </div>\n );\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js TTS endpoint returning audio.\",\n code: `// app/api/tts/route.ts\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\nexport async function POST(req: Request) {\n const { text, voice = \"af_heart\" } = await req.json();\n const result = await g.speak(text, { voice });\n \n // Convert Float32Array to WAV buffer\n const wavBuffer = float32ToWav(result.audio, result.sampleRate);\n \n return new Response(wavBuffer, {\n headers: { \"Content-Type\": \"audio/wav\" }\n });\n}`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express TTS endpoint with voice selection.\",\n code: `import express from \"express\";\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst app = express();\nconst g = new Gerbil();\n\napp.post(\"/api/tts\", express.json(), async (req, res) => {\n const { text, voice = \"af_heart\", speed = 1.0 } = req.body;\n const result = await g.speak(text, { voice, speed });\n \n // Send as WAV\n res.type(\"audio/wav\");\n res.send(float32ToWav(result.audio, result.sampleRate));\n});\n\napp.get(\"/api/tts/voices\", (req, res) => {\n res.json(g.listVoices());\n});\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain TTS utility for pipelines.\",\n code: `import { GerbilTTS } from \"@tryhamster/gerbil/langchain\";\n\nconst tts = new GerbilTTS({ voice: \"af_heart\", speed: 1.0 });\n\n// Generate speech\nconst result = await tts.speak(\"Hello from LangChain!\");\n// result.audio = Float32Array, result.sampleRate = 24000\n\n// Stream long text\nfor await (const chunk of tts.speakStream(\"Long text...\")) {\n console.log(\\`Chunk \\${chunk.index}: \\${chunk.samples.length} samples\\`);\n}\n\n// List available voices\nconst voices = await tts.listVoices();`,\n },\n },\n\n // ============================================\n // STT (Speech-to-Text / Transcription)\n // ============================================\n stt: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Whisper STT with timestamps and language detection.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\nimport { readFileSync } from \"fs\";\n\nconst g = new Gerbil();\n\n// Transcribe audio file\nconst audio = new Uint8Array(readFileSync(\"audio.wav\"));\nconst result = await g.transcribe(audio);\nconsole.log(result.text);\n\n// With timestamps\nconst result = await g.transcribe(audio, { timestamps: true });\nfor (const seg of result.segments) {\n console.log(\\`[\\${seg.start}s] \\${seg.text}\\`);\n}\n\n// Record from microphone (5 seconds)\nconst live = await g.listen(5000);\nconsole.log(live.text);\n\n// Available models: whisper-tiny.en, whisper-base.en, whisper-small, etc.`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK TranscriptionModelV2 interface.\",\n code: `import { experimental_transcribe as transcribe } from \"ai\";\nimport { gerbil } from \"@tryhamster/gerbil/ai\";\nimport { readFile } from \"fs/promises\";\n\nconst result = await transcribe({\n model: gerbil.transcription(), // whisper-tiny.en\n audio: await readFile(\"audio.wav\"),\n});\n\nconsole.log(result.text); // \"Hello world\"\nconsole.log(result.language); // \"en\"\nconsole.log(result.durationInSeconds); // 2.5\nconsole.log(result.segments); // Timestamped segments\n\n// Use larger model for better accuracy\nconst result2 = await transcribe({\n model: gerbil.transcription(\"whisper-base\"),\n audio: audioBuffer,\n});\n\n// List available models\nconst models = gerbil.listTranscriptionModels();`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"React hooks for recording and transcription.\",\n code: `import { useVoiceInput, useVoiceChat } from \"@tryhamster/gerbil/browser\";\n\n// Record and transcribe\nfunction VoiceInput() {\n const { startRecording, stopRecording, isRecording, transcript } = useVoiceInput({\n model: \"whisper-tiny.en\",\n onTranscript: (text) => console.log(\"User said:\", text),\n });\n\n return (\n <button onClick={isRecording ? stopRecording : startRecording}>\n {isRecording ? \"🔴 Stop\" : \"🎤 Record\"}\n </button>\n );\n}\n\n// Full voice conversation: STT → LLM → TTS\nfunction VoiceAssistant() {\n const { startListening, stopListening, stage, messages } = useVoiceChat({\n llmModel: \"qwen3-0.6b\",\n sttModel: \"whisper-tiny.en\",\n voice: \"af_bella\",\n });\n\n return (\n <button onMouseDown={startListening} onMouseUp={stopListening}>\n {stage === \"idle\" ? \"🎤 Hold to Speak\" : stage}\n </button>\n );\n}`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js transcription endpoint.\",\n code: `// app/api/transcribe/route.ts\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\nexport async function POST(req: Request) {\n const formData = await req.formData();\n const audioFile = formData.get(\"audio\") as File;\n const audioBuffer = new Uint8Array(await audioFile.arrayBuffer());\n \n const result = await g.transcribe(audioBuffer, {\n timestamps: true,\n });\n \n return Response.json({\n text: result.text,\n segments: result.segments,\n duration: result.duration,\n });\n}`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express multer\",\n description: \"Express transcription endpoint with file upload.\",\n code: `import express from \"express\";\nimport multer from \"multer\";\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst app = express();\nconst upload = multer();\nconst g = new Gerbil();\n\napp.post(\"/api/transcribe\", upload.single(\"audio\"), async (req, res) => {\n const audioBuffer = new Uint8Array(req.file.buffer);\n const result = await g.transcribe(audioBuffer, {\n timestamps: req.query.timestamps === \"true\",\n language: req.query.language,\n });\n \n res.json({\n text: result.text,\n segments: result.segments,\n duration: result.duration,\n });\n});\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain STT utility for pipelines.\",\n code: `import { GerbilSTT } from \"@tryhamster/gerbil/langchain\";\nimport { readFileSync } from \"fs\";\n\nconst stt = new GerbilSTT({ model: \"whisper-tiny.en\" });\n\n// Transcribe audio\nconst audio = new Uint8Array(readFileSync(\"audio.wav\"));\nconst result = await stt.transcribe(audio);\nconsole.log(result.text);\n\n// With timestamps\nconst result = await stt.transcribe(audio, { timestamps: true });\nfor (const seg of result.segments) {\n console.log(\\`[\\${seg.start}s] \\${seg.text}\\`);\n}\n\n// List available models\nconst models = await stt.listModels();`,\n },\n },\n\n // ============================================\n // EMBEDDINGS\n // ============================================\n embeddings: {\n standalone: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Generate embeddings for semantic search and RAG.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\n// Single embedding\nconst result = await g.embed(\"Hello world\");\nconsole.log(result.vector); // number[] (384 dimensions)\nconsole.log(result.dimensions); // 384\n\n// Batch embeddings\nconst results = await g.embedBatch([\n \"First document\",\n \"Second document\",\n \"Third document\",\n]);\n\n// Cosine similarity for search\nfunction cosineSim(a: number[], b: number[]): number {\n const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);\n const magA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));\n const magB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));\n return dot / (magA * magB);\n}`,\n },\n \"ai-sdk\": {\n install: \"npm install @tryhamster/gerbil ai\",\n description: \"AI SDK embeddings (use standalone for now).\",\n code: `// Embeddings are best used directly with Gerbil\n// AI SDK embedding integration coming soon\n\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\nconst result = await g.embed(\"Your text here\");\n\n// Use with AI SDK's embed() when available:\n// import { embed } from \"ai\";\n// import { gerbil } from \"@tryhamster/gerbil/ai\";\n// const { embedding } = await embed({\n// model: gerbil.embedding(),\n// value: \"Hello world\",\n// });`,\n },\n browser: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Browser-based embeddings with WebGPU.\",\n code: `import { createGerbilWorker } from \"@tryhamster/gerbil/browser\";\n\n// Create worker for embeddings\nconst worker = await createGerbilWorker({ modelId: \"qwen3-0.6b\" });\n\n// Note: Browser embeddings use the main model\n// For dedicated embedding model, use Gerbil on server\n\n// Server-side approach (recommended):\n// 1. Create API endpoint with Gerbil\n// 2. Call from browser\n\n// app/api/embed/route.ts\n// const g = new Gerbil();\n// export async function POST(req) {\n// const { text } = await req.json();\n// const result = await g.embed(text);\n// return Response.json(result);\n// }`,\n },\n nextjs: {\n install: \"npm install @tryhamster/gerbil\",\n description: \"Next.js embedding endpoint for RAG.\",\n code: `// app/api/embed/route.ts\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst g = new Gerbil();\n\nexport async function POST(req: Request) {\n const { text, texts } = await req.json();\n \n if (texts) {\n // Batch embedding\n const results = await g.embedBatch(texts);\n return Response.json({ embeddings: results.map(r => r.vector) });\n }\n \n // Single embedding\n const result = await g.embed(text);\n return Response.json({\n vector: result.vector,\n dimensions: result.dimensions,\n });\n}`,\n },\n express: {\n install: \"npm install @tryhamster/gerbil express\",\n description: \"Express embedding endpoint.\",\n code: `import express from \"express\";\nimport { Gerbil } from \"@tryhamster/gerbil\";\n\nconst app = express();\napp.use(express.json());\nconst g = new Gerbil();\n\napp.post(\"/api/embed\", async (req, res) => {\n const { text, texts } = req.body;\n \n if (texts) {\n const results = await g.embedBatch(texts);\n res.json({ embeddings: results.map(r => r.vector) });\n } else {\n const result = await g.embed(text);\n res.json({ vector: result.vector, dimensions: result.dimensions });\n }\n});\n\napp.listen(3000);`,\n },\n langchain: {\n install: \"npm install @tryhamster/gerbil langchain\",\n description: \"LangChain embeddings for RAG pipelines.\",\n code: `import { Gerbil } from \"@tryhamster/gerbil\";\nimport { Document } from \"langchain/document\";\nimport { MemoryVectorStore } from \"langchain/vectorstores/memory\";\n\nconst g = new Gerbil();\n\n// Custom embeddings class for LangChain\nclass GerbilEmbeddings {\n async embedDocuments(texts: string[]) {\n const results = await g.embedBatch(texts);\n return results.map(r => r.vector);\n }\n async embedQuery(text: string) {\n const result = await g.embed(text);\n return result.vector;\n }\n}\n\n// Use in vector store\nconst embeddings = new GerbilEmbeddings();\nconst vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);\nconst results = await vectorStore.similaritySearch(\"query\", 5);`,\n },\n },\n};\n\n// ============================================\n// Component\n// ============================================\n\nexport function FrameworksView() {\n const [capabilityIndex, setCapabilityIndex] = useState(0);\n // Track the selected framework by ID to maintain position across tabs\n const [selectedFrameworkId, setSelectedFrameworkId] = useState<FrameworkId>(\"standalone\");\n\n const currentCapability = CAPABILITIES[capabilityIndex];\n const capabilityExamples = EXAMPLES[currentCapability.id];\n const availableFrameworks = FRAMEWORKS.filter((f) => capabilityExamples[f.id]);\n\n // Find index of selected framework in available list, or fall back to closest position\n let frameworkIndex = availableFrameworks.findIndex((f) => f.id === selectedFrameworkId);\n if (frameworkIndex === -1) {\n // Framework not available in this tab - find by original position in FRAMEWORKS\n const originalIndex = FRAMEWORKS.findIndex((f) => f.id === selectedFrameworkId);\n // Clamp to available range\n frameworkIndex = Math.min(originalIndex, availableFrameworks.length - 1);\n frameworkIndex = Math.max(0, frameworkIndex);\n }\n\n const currentFramework = availableFrameworks[frameworkIndex] || availableFrameworks[0];\n const example = currentFramework ? capabilityExamples[currentFramework.id] : null;\n\n useInput((_input, key) => {\n if (key.upArrow) {\n const newIndex = Math.max(0, frameworkIndex - 1);\n setSelectedFrameworkId(availableFrameworks[newIndex].id);\n }\n if (key.downArrow) {\n const newIndex = Math.min(availableFrameworks.length - 1, frameworkIndex + 1);\n setSelectedFrameworkId(availableFrameworks[newIndex].id);\n }\n if (key.leftArrow) {\n setCapabilityIndex((i) => Math.max(0, i - 1));\n // Keep selectedFrameworkId - will auto-adjust in next render\n }\n if (key.rightArrow) {\n setCapabilityIndex((i) => Math.min(CAPABILITIES.length - 1, i + 1));\n // Keep selectedFrameworkId - will auto-adjust in next render\n }\n if (key.tab) {\n // Tab cycles through capabilities\n setCapabilityIndex((i) => (i + 1) % CAPABILITIES.length);\n // Keep selectedFrameworkId - will auto-adjust in next render\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold>Gerbil Integrations</Text>\n <Text dimColor>Select a capability and framework to see examples</Text>\n\n {/* Capability Tabs */}\n <Box marginY={1} gap={1}>\n {CAPABILITIES.map((cap, i) => (\n <Box key={cap.id}>\n <Text\n bold={i === capabilityIndex}\n color={i === capabilityIndex ? \"cyan\" : \"gray\"}\n inverse={i === capabilityIndex}\n >\n {\" \"}\n {cap.name}{\" \"}\n </Text>\n </Box>\n ))}\n </Box>\n\n <Box flexDirection=\"row\">\n {/* Framework List */}\n <Box flexDirection=\"column\" marginRight={2} minWidth={16}>\n {availableFrameworks.map((f, i) => (\n <Text\n bold={i === frameworkIndex}\n color={i === frameworkIndex ? \"cyan\" : \"gray\"}\n key={f.id}\n >\n {i === frameworkIndex ? \"> \" : \" \"}\n {f.name}\n </Text>\n ))}\n </Box>\n\n {/* Code Panel */}\n {example && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n flexGrow={1}\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n {currentCapability.name} + {currentFramework.name}\n </Text>\n <Text dimColor>{example.description}</Text>\n\n <Box marginTop={1}>\n <Text color=\"yellow\">$ </Text>\n <Text>{example.install}</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Example:</Text>\n <Text color=\"green\">{example.code}</Text>\n </Box>\n </Box>\n )}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">←/→</Text> or <Text color=\"yellow\">Tab</Text> switch capability |{\" \"}\n <Text color=\"yellow\">↑/↓</Text> select framework | See{\" \"}\n {hyperlink(\"https://github.com/gethamster/gerbil/tree/main/docs\", \"docs\")} |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","import os from \"node:os\";\nimport { Box, Text, useInput } from \"ink\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { Gerbil } from \"../../../core/gerbil.js\";\nimport { getCacheInfo, getMemoryInfo, hyperlink, type SessionStats } from \"../utils.js\";\n\n/** WebGPU process info from Gerbil.getWebGPUProcesses() */\ntype WebGPUInfo = {\n browser: {\n running: boolean;\n pid: number | null;\n port: number;\n activePagesCount: number;\n maxPages: number;\n };\n backends: Array<{\n modelId: string;\n isVision: boolean;\n isReady: boolean;\n memory: { usedGB: number; totalGB: number; usedPercent: number } | null;\n }>;\n};\n\n/** Cross-process Chrome page info */\ntype ChromePageInfo = {\n url: string;\n title: string;\n isOurs: boolean;\n modelId: string | null;\n memory: { usedGB: number; totalGB: number } | null;\n};\n\n/** TTS/STT model info */\ntype VoiceModelInfo = {\n id: string;\n loaded: boolean;\n device?: \"webgpu\" | \"cpu\";\n};\n\ntype InfoViewProps = {\n gerbil: Gerbil;\n model: string;\n modelFamily: string;\n stats: SessionStats;\n onGoToCache?: () => void;\n};\n\nfunction getDeviceLabel(deviceMode: \"webgpu\" | \"cpu\" | \"wasm\"): string {\n const platform = os.platform();\n const arch = os.arch();\n\n if (deviceMode === \"webgpu\") {\n if (platform === \"darwin\" && arch === \"arm64\") {\n return \"WebGPU (Chrome → Metal)\";\n }\n if (platform === \"linux\") {\n return \"WebGPU (Chrome → Vulkan)\";\n }\n if (platform === \"win32\") {\n return \"WebGPU (Chrome → D3D12)\";\n }\n return \"WebGPU (Chrome)\";\n }\n\n if (platform === \"darwin\" && arch === \"arm64\") {\n return `Apple Silicon (${deviceMode.toUpperCase()})`;\n }\n if (platform === \"darwin\" && arch === \"x64\") {\n return `Intel Mac (${deviceMode.toUpperCase()})`;\n }\n\n return deviceMode.toUpperCase();\n}\n\nexport function InfoView({ gerbil, model, modelFamily, stats, onGoToCache }: InfoViewProps) {\n const deviceMode = gerbil.getDeviceMode();\n const dtype = gerbil.getDtype();\n const chromeStatus = gerbil.getChromeStatus();\n const deviceLabel = getDeviceLabel(deviceMode);\n\n const cacheInfo = getCacheInfo();\n const memInfo = getMemoryInfo();\n\n // WebGPU process monitoring state\n const [webgpuInfo, setWebgpuInfo] = useState<WebGPUInfo | null>(null);\n const [allPages, setAllPages] = useState<ChromePageInfo[]>([]);\n const [totalPageCount, setTotalPageCount] = useState(0);\n const [selectedIndex, setSelectedIndex] = useState<number>(-1); // -1 = none, 0+ = page index\n const [killing, setKilling] = useState(false);\n const [killResult, setKillResult] = useState<string | null>(null);\n\n // TTS/STT model info - in state so they update when voice mode is enabled\n const [ttsInfo, setTtsInfo] = useState<VoiceModelInfo | null>(gerbil.getTTSModelInfo());\n const [sttInfo, setSttInfo] = useState<VoiceModelInfo | null>(gerbil.getSTTModelInfo());\n\n // Fetch WebGPU info (cross-process)\n const fetchInfo = useCallback(async () => {\n const [info, pages, totalPages] = await Promise.all([\n Gerbil.getWebGPUProcesses(),\n Gerbil.getAllChromePagesInfo(),\n Gerbil.getTotalChromePageCount(),\n ]);\n setWebgpuInfo(info);\n setAllPages(pages || []);\n setTotalPageCount(totalPages);\n\n // Update TTS/STT info (these can change when voice mode is toggled)\n setTtsInfo(gerbil.getTTSModelInfo());\n setSttInfo(gerbil.getSTTModelInfo());\n\n // Reset selection if it's out of bounds\n const pageCount = pages?.length ?? 0;\n if (selectedIndex >= pageCount) {\n setSelectedIndex(pageCount > 0 ? 0 : -1);\n }\n }, [selectedIndex, gerbil]);\n\n // Fetch on mount and periodically (every 2s for more responsive updates)\n useEffect(() => {\n fetchInfo();\n const interval = setInterval(fetchInfo, 2000);\n return () => clearInterval(interval);\n }, [fetchInfo]);\n\n useInput(async (input, key) => {\n if (input === \"c\" || input === \"C\") {\n onGoToCache?.();\n return;\n }\n\n // Refresh with 'r'\n if (input === \"r\" || input === \"R\") {\n await fetchInfo();\n return;\n }\n\n // Navigate selection with arrow keys or j/k\n const pageCount = allPages.length;\n if (pageCount > 0) {\n if (key.downArrow || input === \"j\") {\n setSelectedIndex((prev) => Math.min(prev + 1, pageCount - 1));\n return;\n }\n if (key.upArrow || input === \"k\") {\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\n return;\n }\n // Number keys to select page (1-9)\n const num = Number.parseInt(input, 10);\n if (num >= 1 && num <= 9 && num <= pageCount) {\n setSelectedIndex(num - 1);\n return;\n }\n }\n\n // Kill selected page with 'x' (cross-process)\n if (input === \"x\" && !killing && selectedIndex >= 0 && allPages[selectedIndex]) {\n setKilling(true);\n setKillResult(null);\n try {\n const page = allPages[selectedIndex];\n const success = await Gerbil.killChromePage(selectedIndex);\n if (success) {\n const shortName = page.modelId?.split(\"/\").pop() || \"page\";\n const suffix = page.isOurs ? \"\" : \" (other session)\";\n setKillResult(`Killed: ${shortName}${suffix}`);\n } else {\n setKillResult(\"Failed to kill page\");\n }\n await fetchInfo();\n } catch (err: any) {\n setKillResult(`Error: ${err.message}`);\n }\n setKilling(false);\n setTimeout(() => setKillResult(null), 3000);\n return;\n }\n\n // Kill ALL with 'K' (shift+k)\n if (input === \"K\" && !killing && webgpuInfo?.browser.running) {\n setKilling(true);\n setKillResult(null);\n try {\n const result = await Gerbil.killAllWebGPU();\n if (result) {\n setKillResult(\n `Killed ${result.pagesKilled} page(s)${result.browserKilled ? \", browser closed\" : \"\"}`,\n );\n } else {\n setKillResult(\"No WebGPU processes to kill\");\n }\n await fetchInfo();\n setSelectedIndex(-1);\n } catch (err: any) {\n setKillResult(`Error: ${err.message}`);\n }\n setKilling(false);\n setTimeout(() => setKillResult(null), 3000);\n }\n });\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold>System Information</Text>\n\n <Box borderColor=\"gray\" borderStyle=\"single\" flexDirection=\"column\" marginY={1} paddingX={1}>\n <Text bold color=\"cyan\">\n LLM Model\n </Text>\n <Text>\n Current:{\" \"}\n <Text bold color=\"cyan\">\n {model}\n </Text>\n </Text>\n <Text>\n Family: <Text color=\"gray\">{modelFamily}</Text>\n </Text>\n <Text>\n Device: <Text color={deviceMode === \"webgpu\" ? \"green\" : \"yellow\"}>{deviceLabel}</Text>\n </Text>\n <Text>\n Dtype: <Text color={dtype === \"q4f16\" ? \"green\" : \"gray\"}>{dtype}</Text>\n </Text>\n </Box>\n\n {/* TTS/STT Voice Models Section */}\n <Box flexDirection=\"row\" marginBottom={1}>\n <Box\n borderColor={ttsInfo?.loaded ? \"green\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n minWidth={30}\n paddingX={1}\n >\n <Text bold color={ttsInfo?.loaded ? \"cyan\" : \"gray\"}>\n 🔊 TTS (Text-to-Speech)\n </Text>\n {ttsInfo ? (\n <>\n <Text>\n Model:{\" \"}\n <Text bold color={ttsInfo.loaded ? \"cyan\" : \"yellow\"}>\n {ttsInfo.id}\n </Text>\n </Text>\n <Text>\n Status:{\" \"}\n <Text color={ttsInfo.loaded ? \"green\" : \"yellow\"}>\n {ttsInfo.loaded ? \"● Loaded\" : \"○ Not loaded\"}\n </Text>\n </Text>\n {ttsInfo.device && (\n <Text>\n Device:{\" \"}\n <Text color={ttsInfo.device === \"webgpu\" ? \"green\" : \"gray\"}>\n {ttsInfo.device.toUpperCase()}\n </Text>\n </Text>\n )}\n </>\n ) : (\n <Text color=\"gray\" dimColor>\n Not initialized\n </Text>\n )}\n </Box>\n\n <Box\n borderColor={sttInfo?.loaded ? \"green\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n minWidth={30}\n paddingX={1}\n >\n <Text bold color={sttInfo?.loaded ? \"cyan\" : \"gray\"}>\n 🎤 STT (Speech-to-Text)\n </Text>\n {sttInfo ? (\n <>\n <Text>\n Model:{\" \"}\n <Text bold color={sttInfo.loaded ? \"cyan\" : \"yellow\"}>\n {sttInfo.id}\n </Text>\n </Text>\n <Text>\n Status:{\" \"}\n <Text color={sttInfo.loaded ? \"green\" : \"yellow\"}>\n {sttInfo.loaded ? \"● Loaded\" : \"○ Not loaded\"}\n </Text>\n </Text>\n {sttInfo.device && (\n <Text>\n Device:{\" \"}\n <Text color={sttInfo.device === \"webgpu\" ? \"green\" : \"gray\"}>\n {sttInfo.device.toUpperCase()}\n </Text>\n </Text>\n )}\n </>\n ) : (\n <Text color=\"gray\" dimColor>\n Not initialized\n </Text>\n )}\n </Box>\n </Box>\n\n <Box flexDirection=\"row\" marginBottom={1}>\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n minWidth={30}\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n Memory\n </Text>\n <Text>\n Total: <Text color=\"gray\">{memInfo.total}</Text>\n </Text>\n <Text>\n Used:{\" \"}\n <Text color={memInfo.percentUsed > 80 ? \"red\" : \"yellow\"}>\n {memInfo.used} ({memInfo.percentUsed}%)\n </Text>\n </Text>\n <Text>\n Free: <Text color=\"green\">{memInfo.free}</Text>\n </Text>\n </Box>\n\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={1}\n minWidth={30}\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n Session Stats\n </Text>\n <Text>\n Prompts: <Text color=\"gray\">{stats.prompts}</Text>\n </Text>\n <Text>\n Tokens In: <Text color=\"gray\">{stats.tokensIn}</Text>\n </Text>\n <Text>\n Tokens Out: <Text color=\"gray\">{stats.tokensOut}</Text>\n </Text>\n <Text>\n Total Time: <Text color=\"gray\">{Math.round(stats.totalTimeMs / 1000)}s</Text>\n </Text>\n {stats.lastTokPerSec > 0 && (\n <Text>\n Last tok/s: <Text color=\"yellow\">{stats.lastTokPerSec.toFixed(1)}</Text>\n </Text>\n )}\n {stats.avgTokPerSec > 0 && (\n <Text>\n Avg tok/s: <Text color=\"cyan\">{stats.avgTokPerSec.toFixed(1)}</Text>\n </Text>\n )}\n {stats.benchResults.length > 0 && (\n <Text>\n Best Bench:{\" \"}\n <Text color=\"green\">\n {Math.max(...stats.benchResults.map((b) => b.tokPerSec))} tok/s\n </Text>\n </Text>\n )}\n </Box>\n\n {chromeStatus && (\n <Box\n borderColor=\"green\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n minWidth={38}\n paddingX={1}\n >\n <Text bold color=\"green\">\n WebGPU Backend\n </Text>\n <Text>\n Status: <Text color=\"green\">● Running</Text>\n </Text>\n <Text>\n PID:{\" \"}\n <Text color={chromeStatus.pid ? \"gray\" : \"yellow\"}>\n {chromeStatus.pid ?? \"connected (no PID)\"}\n </Text>\n </Text>\n <Text>\n Port: <Text color=\"gray\">{chromeStatus.port}</Text>\n </Text>\n <Text>\n Model:{\" \"}\n <Text color=\"gray\">\n {chromeStatus.modelId.length > 28\n ? `...${chromeStatus.modelId.slice(-25)}`\n : chromeStatus.modelId}\n </Text>\n </Text>\n </Box>\n )}\n </Box>\n\n {/* WebGPU Processes Section */}\n {webgpuInfo && (\n <Box\n borderColor={webgpuInfo.browser.running ? \"green\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginBottom={1}\n paddingX={1}\n >\n <Text bold color={webgpuInfo.browser.running ? \"green\" : \"gray\"}>\n WebGPU Processes <Text dimColor>(all sessions)</Text>\n </Text>\n <Text>\n Chrome:{\" \"}\n <Text color={webgpuInfo.browser.running ? \"green\" : \"gray\"}>\n {webgpuInfo.browser.running ? \"● Running\" : \"○ Not running\"}\n </Text>\n </Text>\n {webgpuInfo.browser.running && (\n <>\n <Text>\n {\" \"}\n PID: <Text color=\"gray\">{webgpuInfo.browser.pid ?? \"unknown\"}</Text>\n </Text>\n <Text>\n {\" \"}\n Port: <Text color=\"gray\">{webgpuInfo.browser.port}</Text>\n </Text>\n <Text>\n {\" \"}\n Total Pages:{\" \"}\n <Text color={totalPageCount > 0 ? \"yellow\" : \"gray\"}>{totalPageCount}</Text>{\" \"}\n <Text dimColor>({webgpuInfo.browser.activePagesCount} ours)</Text>\n </Text>\n </>\n )}\n\n {allPages.length > 0 && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>\n Active Pages <Text color=\"gray\">(↑↓ select, x kill, K kill all)</Text>:\n </Text>\n {allPages.map((page, i) => {\n const isSelected = i === selectedIndex;\n const modelName = page.modelId || \"loading...\";\n const shortName = modelName.length > 30 ? `...${modelName.slice(-27)}` : modelName;\n\n return (\n <Box flexDirection=\"column\" key={i} marginLeft={1}>\n <Text>\n <Text color=\"gray\">{i + 1}.</Text>{\" \"}\n <Text color={isSelected ? \"cyan\" : page.isOurs ? \"green\" : \"yellow\"}>\n {isSelected ? \"▶\" : page.isOurs ? \"●\" : \"○\"}\n </Text>{\" \"}\n <Text bold={isSelected} inverse={isSelected}>\n {shortName}\n </Text>\n {page.isOurs ? (\n <Text color=\"green\" dimColor>\n {\" \"}\n (this session)\n </Text>\n ) : (\n <Text color=\"yellow\" dimColor>\n {\" \"}\n (other session)\n </Text>\n )}\n </Text>\n {page.memory && (\n <Text dimColor>\n {\" \"}Heap: {page.memory.usedGB.toFixed(2)}GB used of{\" \"}\n {page.memory.totalGB.toFixed(2)}GB\n </Text>\n )}\n </Box>\n );\n })}\n </Box>\n )}\n\n {totalPageCount === 0 && webgpuInfo.browser.running && (\n <Text color=\"yellow\" dimColor>\n ⚠ No active pages (zombie browser?) - press K to kill\n </Text>\n )}\n\n {killing && (\n <Box marginTop={1}>\n <Text color=\"cyan\">Killing...</Text>\n </Box>\n )}\n {killResult && (\n <Box marginTop={1}>\n <Text color=\"cyan\">{killResult}</Text>\n </Box>\n )}\n </Box>\n )}\n\n <Box borderColor=\"gray\" borderStyle=\"single\" flexDirection=\"column\" paddingX={1}>\n <Text bold color=\"cyan\">\n Cache\n </Text>\n <Text>\n Location:{\" \"}\n <Text dimColor>\n {(cacheInfo.locations[0] || \"\").length > 50\n ? `...${(cacheInfo.locations[0] || \"\").slice(-47)}`\n : cacheInfo.locations[0] || \"none\"}\n </Text>\n </Text>\n <Text>\n Total Size: <Text color=\"yellow\">{cacheInfo.totalSize || \"empty\"}</Text>\n </Text>\n <Text>\n Models: <Text color=\"gray\">{cacheInfo.models.length}</Text>\n </Text>\n <Text dimColor>\n Press <Text color=\"cyan\">c</Text> to manage cached models\n </Text>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>\n Runtime: <Text color=\"gray\">Node.js {process.version}</Text> | Gerbil:{\" \"}\n <Text color=\"gray\">0.1.x</Text> |\n {hyperlink(\"https://github.com/gethamster/gerbil\", \"GitHub\")}\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"cyan\">c</Text> cache | <Text color=\"cyan\">r</Text> refresh\n {allPages.length > 0 && (\n <>\n {\" \"}\n | <Text color=\"red\">x</Text> kill selected | <Text color=\"red\">K</Text> kill all\n </>\n )}\n {webgpuInfo?.browser.running && allPages.length === 0 && (\n <>\n {\" \"}\n | <Text color=\"red\">K</Text> kill browser\n </>\n )}\n {\" \"}| <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text } from \"ink\";\nimport Spinner from \"ink-spinner\";\n\ntype LoadingViewProps = {\n message: string;\n};\n\nexport function LoadingView({ message }: LoadingViewProps) {\n return (\n <Box\n alignItems=\"center\"\n flexDirection=\"column\"\n height=\"100%\"\n justifyContent=\"center\"\n padding={2}\n >\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n 🐹 Gerbil\n </Text>\n </Box>\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> {message}</Text>\n </Box>\n </Box>\n );\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { refreshCachedModelSizes } from \"../../../core/chrome-backend.js\";\nimport {\n type CachedModelInfo,\n fetchModelMetadata,\n formatBytes,\n getCacheInfo,\n getModelBenchmarkStats,\n isModelCached,\n PRESET_MODELS,\n} from \"../utils.js\";\n\ntype HFModel = {\n id: string;\n downloads: number;\n likes: number;\n createdAt?: string;\n // These are fetched separately via individual model API\n sizeBytes?: number;\n contextLength?: number;\n params?: number;\n loading?: boolean;\n};\n\ntype HFApiModel = {\n id: string;\n modelId: string;\n downloads: number;\n likes: number;\n createdAt?: string;\n pipeline_tag?: string;\n library_name?: string;\n tags?: string[];\n};\n\ntype HFModelDetails = {\n siblings?: { rfilename: string; size?: number }[];\n safetensors?: { parameters?: Record<string, number>; total?: number };\n usedStorage?: number;\n};\n\n// Extract param count from model name (e.g., \"Qwen3-0.6B\" -> 0.6B, \"llama-7b\" -> 7B)\nfunction extractParamsFromName(modelId: string): number | null {\n // Match patterns like: 0.6B, 7B, 1.7B, 70B, 360M, etc.\n const match = modelId.match(/[-_](\\d+(?:\\.\\d+)?)(B|M|K)(?:[-_]|$)/i);\n if (match) {\n const num = Number.parseFloat(match[1]);\n const unit = match[2].toUpperCase();\n if (unit === \"B\") {\n return num * 1e9;\n }\n if (unit === \"M\") {\n return num * 1e6;\n }\n if (unit === \"K\") {\n return num * 1e3;\n }\n }\n return null;\n}\n\nfunction formatParams(params: number | undefined, modelId?: string): string {\n // Try the provided params first\n if (params && params > 0) {\n if (params >= 1e9) {\n return `${(params / 1e9).toFixed(1)}B`;\n }\n if (params >= 1e6) {\n return `${Math.round(params / 1e6)}M`;\n }\n if (params >= 1e3) {\n return `${Math.round(params / 1e3)}K`;\n }\n return `${params}`;\n }\n\n // Fallback: try to extract from model name\n if (modelId) {\n const extracted = extractParamsFromName(modelId);\n if (extracted) {\n if (extracted >= 1e9) {\n return `${(extracted / 1e9).toFixed(1)}B`;\n }\n if (extracted >= 1e6) {\n return `${Math.round(extracted / 1e6)}M`;\n }\n if (extracted >= 1e3) {\n return `${Math.round(extracted / 1e3)}K`;\n }\n }\n }\n\n return \"-\";\n}\n\nfunction formatDownloads(n: number): string {\n if (n >= 1e6) {\n return `${(n / 1e6).toFixed(1)}M`;\n }\n if (n >= 1e3) {\n return `${(n / 1e3).toFixed(1)}K`;\n }\n return `${n}`;\n}\n\nfunction formatDate(dateStr: string | undefined): string {\n if (!dateStr) {\n return \"-\";\n }\n const date = new Date(dateStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));\n\n if (diffDays === 0) {\n return \"today\";\n }\n if (diffDays === 1) {\n return \"1d ago\";\n }\n if (diffDays < 7) {\n return `${diffDays}d ago`;\n }\n if (diffDays < 30) {\n return `${Math.floor(diffDays / 7)}w ago`;\n }\n if (diffDays < 365) {\n return `${Math.floor(diffDays / 30)}mo ago`;\n }\n return `${Math.floor(diffDays / 365)}y ago`;\n}\n\nfunction formatContext(contextLength: number | undefined): string {\n if (!contextLength) {\n return \"-\";\n }\n if (contextLength >= 1e6) {\n return `${(contextLength / 1e6).toFixed(0)}M`;\n }\n if (contextLength >= 1e3) {\n return `${(contextLength / 1e3).toFixed(0)}K`;\n }\n return `${contextLength}`;\n}\n\ntype ModelViewProps = {\n currentModel: string;\n onSelect: (model: string) => void;\n onDownloadOnly?: (model: string, hfId: string) => Promise<boolean>;\n onTabChange?: (tab: \"preset\" | \"cached\" | \"huggingface\" | \"voice\") => void;\n onSearchChange?: (query: string) => void;\n /** Callback when TTS model is selected */\n onSelectTTS?: (modelId: string) => void;\n /** Callback when STT model is selected */\n onSelectSTT?: (modelId: string) => void;\n /** Current TTS model ID */\n currentTTSModel?: string;\n /** Current STT model ID */\n currentSTTModel?: string;\n};\n\n// TTS models available\nconst TTS_MODELS = [\n {\n id: \"kokoro-82m\",\n name: \"Kokoro 82M\",\n size: \"~200MB\",\n voices: 10,\n quality: \"High\",\n speed: \"Fast\",\n },\n {\n id: \"supertonic-66m\",\n name: \"Supertonic 66M\",\n size: \"~150MB\",\n voices: 4,\n quality: \"Good\",\n speed: \"Faster\",\n },\n];\n\n// STT models available\nconst STT_MODELS = [\n {\n id: \"whisper-tiny.en\",\n name: \"Whisper Tiny (English)\",\n size: \"~75MB\",\n quality: \"Basic\",\n speed: \"Fastest\",\n },\n {\n id: \"whisper-base.en\",\n name: \"Whisper Base (English)\",\n size: \"~150MB\",\n quality: \"Good\",\n speed: \"Fast\",\n },\n {\n id: \"whisper-small.en\",\n name: \"Whisper Small (English)\",\n size: \"~500MB\",\n quality: \"Better\",\n speed: \"Medium\",\n },\n {\n id: \"whisper-large-v3-turbo\",\n name: \"Whisper Large v3 Turbo\",\n size: \"~1.5GB\",\n quality: \"Best\",\n speed: \"Slower\",\n },\n];\n\n// Secondary sort applied on top of downloads (primary sort)\ntype SortMode = \"none\" | \"likes\" | \"updatedAt\";\ntype SizeFilter = \"all\" | \"small\" | \"medium\" | \"large\";\n\nconst PAGE_SIZE = 15;\n\nexport function ModelView({\n currentModel,\n onSelect,\n onDownloadOnly,\n onTabChange,\n onSearchChange,\n onSelectTTS,\n onSelectSTT,\n currentTTSModel = \"kokoro-82m\",\n currentSTTModel = \"whisper-tiny.en\",\n}: ModelViewProps) {\n const [tab, setTab] = useState<\"preset\" | \"cached\" | \"huggingface\" | \"voice\">(\"preset\");\n const [selected, setSelected] = useState(\n PRESET_MODELS.findIndex((m) => m.id === currentModel) || 0,\n );\n const [hfModels, setHfModels] = useState<HFModel[]>([]);\n const [hfSelected, setHfSelected] = useState(0);\n const [hfLoading, setHfLoading] = useState(false);\n const [hfSearchInput, setHfSearchInput] = useState(\"\");\n const [hfSearchFocused, setHfSearchFocused] = useState(false);\n const [hfError, setHfError] = useState(\"\");\n const [hfHasMore, setHfHasMore] = useState(true);\n const [sortMode, setSortMode] = useState<SortMode>(\"none\");\n const [sizeFilter, setSizeFilter] = useState<SizeFilter>(\"all\");\n const [cachedModels, setCachedModels] = useState<Set<string>>(new Set());\n const [localModels, setLocalModels] = useState<CachedModelInfo[]>([]);\n const [localSelected, setLocalSelected] = useState(0);\n const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null);\n\n // Voice tab state\n const [voiceSection, setVoiceSection] = useState<\"tts\" | \"stt\">(\"tts\");\n const [ttsSelected, setTtsSelected] = useState(\n TTS_MODELS.findIndex((m) => m.id === currentTTSModel) || 0,\n );\n const [sttSelected, setSttSelected] = useState(\n STT_MODELS.findIndex((m) => m.id === currentSTTModel) || 0,\n );\n\n // Track actual metadata for preset models (fetched from HF)\n const [presetMetadata, setPresetMetadata] = useState<\n Record<string, { sizeBytes?: number; contextLength?: number }>\n >({});\n\n // Track which models we've fetched details for\n const fetchedDetailsRef = useRef<Set<string>>(new Set());\n const abortControllerRef = useRef<AbortController | null>(null);\n const hasFetchedOnMountRef = useRef(false);\n\n const refreshCachedModels = async () => {\n // Refresh sizes for models with missing or suspiciously small sizes\n await refreshCachedModelSizes().catch(() => {});\n\n const cached = new Set<string>();\n for (const model of PRESET_MODELS) {\n if (isModelCached(model.hfId)) {\n cached.add(model.id);\n }\n }\n setCachedModels(cached);\n const cacheInfo = getCacheInfo();\n setLocalModels(cacheInfo.models);\n\n // Fetch missing metadata for cached models (context, size)\n fetchCachedModelMetadata(cacheInfo.models);\n };\n\n // Fetch metadata for cached models that are missing context or have wrong size\n const fetchCachedModelMetadata = async (models: CachedModelInfo[]) => {\n const MIN_SIZE = 1_000_000; // 1MB - anything smaller is likely wrong\n const needsFetch = models.filter(\n (m) =>\n !(m.contextLength && m.modelSize) ||\n m.modelSize === \"~\" ||\n (m.modelSize.endsWith(\"B\") && Number.parseInt(m.modelSize, 10) < MIN_SIZE),\n );\n if (needsFetch.length === 0) {\n return;\n }\n\n const batchSize = 3;\n const updatedModels = [...models];\n\n for (let i = 0; i < needsFetch.length; i += batchSize) {\n const batch = needsFetch.slice(i, i + batchSize);\n await Promise.all(\n batch.map(async (model) => {\n try {\n // Convert model name to HF ID format\n const hfId = model.name.replace(\"--\", \"/\");\n const meta = await fetchModelMetadata(hfId);\n\n // Update in the array\n const idx = updatedModels.findIndex((m) => m.name === model.name);\n if (idx >= 0 && meta) {\n if (meta.sizeBytes) {\n updatedModels[idx].modelSize = formatBytes(meta.sizeBytes);\n updatedModels[idx].totalSize = formatBytes(meta.sizeBytes);\n }\n if (meta.contextLength) {\n updatedModels[idx].contextLength = meta.contextLength;\n }\n }\n } catch {\n // Ignore fetch errors\n }\n }),\n );\n }\n // Update state only once at the end\n setLocalModels([...updatedModels]);\n };\n\n // Fetch actual metadata (size, context) for preset models from HuggingFace\n const fetchPresetModelMetadata = async () => {\n const metadata: Record<string, { sizeBytes?: number; contextLength?: number }> = {};\n\n // Fetch in parallel with small batches\n const batchSize = 3;\n for (let i = 0; i < PRESET_MODELS.length; i += batchSize) {\n const batch = PRESET_MODELS.slice(i, i + batchSize);\n await Promise.all(\n batch.map(async (model) => {\n try {\n const result = await fetchModelMetadata(model.hfId);\n if (result.sizeBytes || result.contextLength) {\n metadata[model.id] = result;\n }\n } catch {\n // Ignore fetch errors\n }\n }),\n );\n }\n // Update state only once at the end\n setPresetMetadata({ ...metadata });\n };\n\n useEffect(() => {\n if (hasFetchedOnMountRef.current) return;\n hasFetchedOnMountRef.current = true;\n refreshCachedModels();\n fetchPresetModelMetadata();\n }, []);\n\n // Fetch model details (size, params, context) for a single model\n const fetchModelDetails = async (\n modelId: string,\n ): Promise<{ sizeBytes: number; params: number; contextLength?: number } | null> => {\n try {\n // First get model info for params\n const res = await fetch(`https://huggingface.co/api/models/${modelId}`);\n if (!res.ok) {\n return null;\n }\n const info: HFModelDetails = await res.json();\n\n // Try to get params from safetensors info\n let params = 0;\n if (info.safetensors?.parameters) {\n const p = info.safetensors.parameters;\n params = p.BF16 || p.F16 || p.F32 || info.safetensors.total || 0;\n }\n\n // Fetch size and context using our utility\n const metadata = await fetchModelMetadata(modelId);\n\n // Fallback to usedStorage if we couldn't get ONNX size\n const sizeBytes = metadata.sizeBytes || info.usedStorage || 0;\n\n return { sizeBytes, params, contextLength: metadata.contextLength };\n } catch {\n return null;\n }\n };\n\n // Fetch all model details in parallel after getting the list\n const fetchAllModelDetails = async (models: HFModel[]) => {\n // Fetch all details in parallel (batch of 5 at a time to avoid rate limiting)\n const batchSize = 5;\n const updatedModels = [...models];\n\n for (let i = 0; i < models.length; i += batchSize) {\n const batch = models.slice(i, i + batchSize);\n const results = await Promise.all(batch.map((m) => fetchModelDetails(m.id)));\n\n results.forEach((result, idx) => {\n const modelIdx = i + idx;\n if (result && updatedModels[modelIdx]) {\n updatedModels[modelIdx] = {\n ...updatedModels[modelIdx],\n sizeBytes: result.sizeBytes,\n params: result.params,\n contextLength: result.contextLength,\n loading: false,\n };\n } else if (updatedModels[modelIdx]) {\n updatedModels[modelIdx] = { ...updatedModels[modelIdx], loading: false };\n }\n });\n\n // Update state after each batch so user sees progress\n setHfModels([...updatedModels]);\n }\n };\n\n const fetchHFModels = async (query?: string, append = false, sort: SortMode = sortMode) => {\n if (hfLoading) {\n return;\n }\n\n // Cancel any pending request\n abortControllerRef.current?.abort();\n abortControllerRef.current = new AbortController();\n\n setHfLoading(true);\n setHfError(\"\");\n\n try {\n const offset = append ? hfModels.length : 0;\n\n // Build URL with query params - fetch extra for preloading\n // Note: HF API's library=onnx filter isn't reliable, so we use filter=onnx and search\n // Always sort by downloads first (primary), secondary sort applied client-side\n const params = new URLSearchParams({\n filter: \"onnx\",\n sort: \"downloads\",\n direction: \"-1\",\n limit: String(PAGE_SIZE * 2), // Fetch double for preloading next page\n });\n\n // Handle compound search (comma-separated terms like \"qwen3, onnx\")\n // Combine all terms with spaces for the search API\n if (query) {\n const searchTerms = query\n .split(\",\")\n .map((t) => t.trim())\n .filter(Boolean);\n // Always include \"onnx\" in search to help filter results\n if (!searchTerms.some((t) => t.toLowerCase().includes(\"onnx\"))) {\n searchTerms.push(\"onnx\");\n }\n params.set(\"search\", searchTerms.join(\" \"));\n } else {\n // Default search includes onnx\n params.set(\"search\", \"onnx\");\n }\n\n if (offset > 0) {\n params.set(\"skip\", String(offset));\n }\n\n const res = await fetch(`https://huggingface.co/api/models?${params}`, {\n signal: abortControllerRef.current.signal,\n });\n\n if (!res.ok) {\n throw new Error(\"API error\");\n }\n const data: HFApiModel[] = await res.json();\n\n // Apply secondary sort on top of downloads (primary sort from API)\n const sortedData = [...data];\n if (sort === \"likes\") {\n // Sort by likes within download tiers\n sortedData.sort((a, b) => (b.likes || 0) - (a.likes || 0));\n } else if (sort === \"updatedAt\") {\n // Sort by recency within download tiers\n sortedData.sort((a, b) => {\n const aDate = a.createdAt ? new Date(a.createdAt).getTime() : 0;\n const bDate = b.createdAt ? new Date(b.createdAt).getTime() : 0;\n return bDate - aDate;\n });\n }\n\n // Then boost models with q4f16/q4 quantization or from onnx-community\n sortedData.sort((a, b) => {\n const aHasQuant =\n a.id.includes(\"q4f16\") || a.id.includes(\"q4\") || a.id.startsWith(\"onnx-community/\");\n const bHasQuant =\n b.id.includes(\"q4f16\") || b.id.includes(\"q4\") || b.id.startsWith(\"onnx-community/\");\n if (aHasQuant && !bHasQuant) {\n return -1;\n }\n if (!aHasQuant && bHasQuant) {\n return 1;\n }\n return 0; // Keep original order for equal priority\n });\n\n const models: HFModel[] = sortedData.map((m) => ({\n id: m.id,\n downloads: m.downloads || 0,\n likes: m.likes || 0,\n createdAt: m.createdAt,\n loading: true,\n }));\n\n setHfHasMore(models.length >= PAGE_SIZE);\n\n if (append) {\n const newModels = [...hfModels, ...models];\n setHfModels(newModels);\n // Fetch details for new models in background\n fetchAllModelDetails(models);\n } else {\n fetchedDetailsRef.current.clear();\n setHfModels(models);\n setHfSelected(0);\n // Fetch all details immediately\n fetchAllModelDetails(models);\n }\n } catch (e: any) {\n if (e.name !== \"AbortError\") {\n setHfError(\"Failed to fetch models. Check your connection.\");\n if (!append) {\n setHfModels([]);\n }\n }\n }\n setHfLoading(false);\n };\n\n useEffect(() => {\n if (tab === \"huggingface\" && hfModels.length === 0 && !hfLoading) {\n fetchHFModels();\n }\n }, [tab, fetchHFModels, hfLoading, hfModels.length]);\n\n const loadMoreModels = () => {\n if (!hfLoading && hfHasMore) {\n fetchHFModels(hfSearchInput || undefined, true);\n }\n };\n\n // Filter models by size (only works for models with fetched params)\n const getFilteredModels = () => {\n if (sizeFilter === \"all\") {\n return hfModels;\n }\n return hfModels.filter((m) => {\n if (!m.params) {\n return true; // Show models without params info\n }\n if (sizeFilter === \"small\") {\n return m.params < 500e6;\n }\n if (sizeFilter === \"medium\") {\n return m.params >= 500e6 && m.params < 3e9;\n }\n if (sizeFilter === \"large\") {\n return m.params >= 3e9;\n }\n return true;\n });\n };\n\n const filteredModels = getFilteredModels();\n\n const deleteModel = (model: CachedModelInfo) => {\n try {\n const possiblePaths = [\n path.join(model.location, model.name),\n path.join(model.location, model.name.replace(\"/\", \"--\")),\n path.join(model.location, `models--${model.name.replace(\"/\", \"--\")}`),\n ];\n\n for (const p of possiblePaths) {\n if (fs.existsSync(p)) {\n fs.rmSync(p, { recursive: true, force: true });\n break;\n }\n }\n\n refreshCachedModels();\n setDeleteConfirm(null);\n\n if (localSelected >= localModels.length - 1) {\n setLocalSelected(Math.max(0, localModels.length - 2));\n }\n } catch {\n setDeleteConfirm(null);\n refreshCachedModels();\n }\n };\n\n useInput((input, key) => {\n if (deleteConfirm !== null) {\n if (input === \"y\" || input === \"Y\") {\n // Find model in localModels by matching either name or hfId format\n const model = localModels.find(\n (m) =>\n m.name === deleteConfirm ||\n m.name.replace(\"--\", \"/\") === deleteConfirm ||\n m.name === deleteConfirm.replace(\"/\", \"--\"),\n );\n if (model) {\n deleteModel(model);\n }\n } else {\n setDeleteConfirm(null);\n }\n return;\n }\n\n if (tab === \"huggingface\" && hfSearchFocused) {\n if (key.escape) {\n setHfSearchFocused(false);\n return;\n }\n return;\n }\n\n if (key.tab) {\n // Cycle through tabs: preset -> voice -> huggingface -> preset\n const tabOrder: Array<\"preset\" | \"voice\" | \"huggingface\"> = [\n \"preset\",\n \"voice\",\n \"huggingface\",\n ];\n const currentIndex = tabOrder.indexOf(tab as \"preset\" | \"voice\" | \"huggingface\");\n const newTab = tabOrder[(currentIndex + 1) % tabOrder.length];\n setTab(newTab);\n setHfSearchFocused(false);\n onTabChange?.(newTab);\n return;\n }\n\n if (tab === \"preset\" || tab === \"cached\") {\n // Unified Models tab\n if (key.upArrow) {\n setSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n setSelected((s) => Math.min(unifiedModels.length - 1, s + 1));\n }\n\n if (key.return && unifiedModels.length > 0) {\n const model = unifiedModels[selected];\n if (model.isCached) {\n onSelect(model.isRecommended ? model.id : model.hfId);\n } else {\n // Download if not cached\n onDownloadOnly?.(model.id, model.hfId).then((success) => {\n if (success) {\n refreshCachedModels();\n }\n });\n }\n }\n\n if (input === \"d\" || input === \"D\") {\n const model = unifiedModels[selected];\n if (!model.isCached) {\n onDownloadOnly?.(model.id, model.hfId).then((success) => {\n if (success) {\n refreshCachedModels();\n }\n });\n }\n }\n\n if ((input === \"x\" || input === \"X\") && unifiedModels.length > 0) {\n const model = unifiedModels[selected];\n if (model.isCached) {\n setDeleteConfirm(model.hfId);\n }\n }\n } else if (tab === \"huggingface\") {\n if (input === \"s\" || input === \"S\") {\n setHfSearchFocused(true);\n return;\n }\n if (input === \"o\" || input === \"O\") {\n // Cycle through secondary sort options (downloads is always primary)\n const modes: SortMode[] = [\"none\", \"likes\", \"updatedAt\"];\n const idx = modes.indexOf(sortMode);\n const newSort = modes[(idx + 1) % modes.length];\n setSortMode(newSort);\n fetchedDetailsRef.current.clear();\n fetchHFModels(hfSearchInput || undefined, false, newSort);\n return;\n }\n if (input === \"f\" || input === \"F\") {\n const filters: SizeFilter[] = [\"all\", \"small\", \"medium\", \"large\"];\n const idx = filters.indexOf(sizeFilter);\n setSizeFilter(filters[(idx + 1) % filters.length]);\n setHfSelected(0);\n return;\n }\n if (key.upArrow) {\n setHfSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n const newSelected = Math.min(filteredModels.length - 1, hfSelected + 1);\n setHfSelected(newSelected);\n if (newSelected >= filteredModels.length - 3 && hfHasMore && !hfLoading) {\n loadMoreModels();\n }\n }\n if (input === \"n\" || input === \"N\") {\n loadMoreModels();\n }\n if (key.return && filteredModels.length > 0) {\n const selectedModel = filteredModels[hfSelected];\n // Only allow Enter if model is cached\n if (selectedModel && isModelCached(selectedModel.id)) {\n onSelect(selectedModel.id);\n }\n }\n if ((input === \"d\" || input === \"D\") && filteredModels.length > 0) {\n const model = filteredModels[hfSelected];\n if (model) {\n onDownloadOnly?.(model.id, model.id).then((success) => {\n if (success) {\n refreshCachedModels();\n }\n });\n }\n }\n }\n\n // Voice tab input handling\n if (tab === \"voice\") {\n // Switch between TTS and STT sections with left/right arrows\n if (key.leftArrow || key.rightArrow) {\n setVoiceSection((s) => (s === \"tts\" ? \"stt\" : \"tts\"));\n return;\n }\n\n if (voiceSection === \"tts\") {\n if (key.upArrow) {\n setTtsSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n setTtsSelected((s) => Math.min(TTS_MODELS.length - 1, s + 1));\n }\n if (key.return) {\n const model = TTS_MODELS[ttsSelected];\n onSelectTTS?.(model.id);\n }\n } else {\n if (key.upArrow) {\n setSttSelected((s) => Math.max(0, s - 1));\n }\n if (key.downArrow) {\n setSttSelected((s) => Math.min(STT_MODELS.length - 1, s + 1));\n }\n if (key.return) {\n const model = STT_MODELS[sttSelected];\n onSelectSTT?.(model.id);\n }\n }\n }\n });\n\n const handleSearch = (value: string) => {\n setHfSearchInput(value);\n };\n\n const handleSearchSubmit = () => {\n setHfHasMore(true);\n fetchedDetailsRef.current.clear();\n fetchHFModels(hfSearchInput || undefined, false);\n setHfSearchFocused(false);\n onSearchChange?.(hfSearchInput);\n };\n\n // Helper to render colored shortcuts\n const renderHelpText = () => {\n if (deleteConfirm !== null) {\n return (\n <Text dimColor>\n Press <Text color=\"red\">y</Text> to confirm delete, any other key to cancel\n </Text>\n );\n }\n if (tab === \"huggingface\") {\n if (hfSearchFocused) {\n return (\n <Text dimColor>\n Type to search | <Text color=\"yellow\">Enter</Text> submit |{\" \"}\n <Text color=\"gray\">Esc</Text> cancel\n </Text>\n );\n }\n const selectedModel = filteredModels[hfSelected];\n const isCached = selectedModel ? isModelCached(selectedModel.id) : false;\n return (\n <Text dimColor>\n <Text color=\"gray\">↑↓</Text> nav |{\" \"}\n {isCached ? (\n <>\n <Text color=\"yellow\">Enter</Text> use |{\" \"}\n </>\n ) : null}\n <Text color=\"cyan\">s</Text> search | <Text color=\"cyan\">o</Text> sort |{\" \"}\n <Text color=\"cyan\">f</Text> filter | <Text color=\"cyan\">n</Text> more |{\" \"}\n <Text color=\"green\">d</Text> download | <Text color=\"cyan\">Tab</Text> |{\" \"}\n <Text color=\"gray\">Esc</Text>\n </Text>\n );\n }\n // Voice tab\n if (tab === \"voice\") {\n return (\n <Text dimColor>\n <Text color=\"gray\">←→</Text> switch TTS/STT | <Text color=\"gray\">↑↓</Text> select |{\" \"}\n <Text color=\"yellow\">Enter</Text> use | <Text color=\"cyan\">Tab</Text> switch |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </Text>\n );\n }\n // Unified Models tab\n const selectedUnified = unifiedModels[selected];\n const showDownload = selectedUnified && !selectedUnified.isCached;\n const showDelete = selectedUnified?.isCached;\n return (\n <Text dimColor>\n <Text color=\"gray\">↑↓</Text> select | <Text color=\"yellow\">Enter</Text> use\n {showDownload ? (\n <>\n {\" \"}\n | <Text color=\"green\">d</Text> download\n </>\n ) : null}\n {showDelete ? (\n <>\n {\" \"}\n | <Text color=\"red\">x</Text> delete\n </>\n ) : null}{\" \"}\n | <Text color=\"cyan\">Tab</Text> switch | <Text color=\"gray\">Esc</Text> back\n </Text>\n );\n };\n\n const getSortLabel = () => {\n switch (sortMode) {\n case \"none\":\n return \"downloads\";\n case \"updatedAt\":\n return \"recent\";\n case \"likes\":\n return \"likes\";\n }\n };\n\n const getFilterLabel = () => {\n switch (sizeFilter) {\n case \"small\":\n return \"<500M\";\n case \"medium\":\n return \"500M-3B\";\n case \"large\":\n return \"3B+\";\n default:\n return \"all\";\n }\n };\n\n // Build unified model list: recommended first, then other cached models\n const unifiedModels = React.useMemo(() => {\n const models: Array<{\n id: string;\n name: string;\n hfId: string;\n size: string;\n contextLength?: number;\n speed?: string;\n isCached: boolean;\n isRecommended: boolean;\n lastUsed?: string;\n benchStats?: { avgTokPerSec: number };\n }> = [];\n\n // Add recommended models first\n for (const preset of PRESET_MODELS) {\n const isCached = cachedModels.has(preset.id) || isModelCached(preset.hfId);\n const meta = presetMetadata[preset.id];\n const cachedInfo = localModels.find(\n (m) => m.name === preset.hfId || m.name.replace(\"--\", \"/\") === preset.hfId,\n );\n const benchStats = getModelBenchmarkStats(preset.hfId);\n models.push({\n id: preset.id,\n name: preset.name,\n hfId: preset.hfId,\n size: meta?.sizeBytes ? formatBytes(meta.sizeBytes) : preset.size,\n contextLength: meta?.contextLength || cachedInfo?.contextLength,\n speed: preset.speed,\n isCached,\n isRecommended: true,\n lastUsed: cachedInfo?.lastUsed,\n benchStats: benchStats ? { avgTokPerSec: benchStats.avgTokPerSec } : undefined,\n });\n }\n\n // Add other cached models (not in recommended)\n for (const cached of localModels) {\n const isRecommended = PRESET_MODELS.some(\n (p) => p.hfId === cached.name || p.hfId === cached.name.replace(\"--\", \"/\"),\n );\n if (!isRecommended) {\n const benchStats = getModelBenchmarkStats(cached.name);\n models.push({\n id: cached.name,\n name: cached.name.length > 35 ? `${cached.name.slice(0, 32)}...` : cached.name,\n hfId: cached.name.replace(\"--\", \"/\"),\n size:\n cached.modelSize !== cached.totalSize\n ? `${cached.modelSize} (${cached.totalSize})`\n : cached.modelSize,\n contextLength: cached.contextLength,\n isCached: true,\n isRecommended: false,\n lastUsed: cached.lastUsed,\n benchStats: benchStats ? { avgTokPerSec: benchStats.avgTokPerSec } : undefined,\n });\n }\n }\n\n return models;\n }, [cachedModels, presetMetadata, localModels]);\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text\n bold={tab === \"preset\" || tab === \"cached\"}\n color={tab === \"preset\" || tab === \"cached\" ? \"cyan\" : \"gray\"}\n >\n [Models]\n </Text>\n <Text color=\"gray\"> </Text>\n <Text bold={tab === \"voice\"} color={tab === \"voice\" ? \"magenta\" : \"gray\"}>\n [Voice]\n </Text>\n <Text color=\"gray\"> </Text>\n <Text bold={tab === \"huggingface\"} color={tab === \"huggingface\" ? \"cyan\" : \"gray\"}>\n [HuggingFace]\n </Text>\n <Text dimColor> (Tab to switch)</Text>\n </Box>\n\n {(tab === \"preset\" || tab === \"cached\") && (\n <>\n <Text dimColor>\n Your models {localModels.length > 0 ? `(${localModels.length} cached)` : \"\"} •{\" \"}\n <Text color=\"yellow\">★</Text> = recommended\n </Text>\n <Box marginTop={1}>\n <Text dimColor>\n {\"Model\".padEnd(44)}\n {\"Size\".padEnd(14)}\n {\"Context\".padEnd(10)}\n {\"Speed\".padEnd(12)}\n {\"Benchmark\".padEnd(14)}\n {\"Last Used\"}\n </Text>\n </Box>\n <Box flexDirection=\"column\" marginBottom={1}>\n {unifiedModels.length === 0 ? (\n <Text color=\"gray\">No models yet. Press Tab to browse HuggingFace.</Text>\n ) : (\n unifiedModels.map((model, i) => {\n const isCurrent = currentModel === model.id || currentModel === model.hfId;\n const isPendingDelete = deleteConfirm === model.hfId;\n const ctxDisplay = formatContext(model.contextLength);\n return (\n <Box key={model.id}>\n <Text color={isPendingDelete ? \"red\" : model.isCached ? \"green\" : \"gray\"}>\n {isPendingDelete ? \"✕\" : model.isCached ? \"●\" : \"○\"}{\" \"}\n </Text>\n <Text color={model.isRecommended ? \"yellow\" : \"gray\"}>\n {model.isRecommended ? \"★ \" : \" \"}\n </Text>\n <Text color={isPendingDelete ? \"red\" : i === selected ? \"cyan\" : \"white\"}>\n {i === selected ? \"> \" : \" \"}\n {model.name.length > 38\n ? `${model.name.slice(0, 35)}...`\n : model.name.padEnd(38)}\n </Text>\n <Text dimColor>{model.size.padEnd(14)}</Text>\n <Text color=\"magenta\">{ctxDisplay.padEnd(10)}</Text>\n <Text\n color={\n model.speed === \"fastest\"\n ? \"green\"\n : model.speed === \"fast\"\n ? \"yellow\"\n : \"gray\"\n }\n >\n {(model.speed || \"-\").padEnd(12)}\n </Text>\n <Text color=\"cyan\">\n {(model.benchStats ? `${model.benchStats.avgTokPerSec} tok/s` : \"-\").padEnd(\n 14,\n )}\n </Text>\n <Text dimColor>{model.lastUsed || \"-\"}</Text>\n {isCurrent && <Text color=\"cyan\"> [current]</Text>}\n {isPendingDelete && <Text color=\"red\"> DELETE? (y/n)</Text>}\n </Box>\n );\n })\n )}\n </Box>\n {deleteConfirm && (\n <Text color=\"red\">⚠ This will permanently delete the model from disk</Text>\n )}\n </>\n )}\n\n {tab === \"huggingface\" && (\n <>\n <Box alignItems=\"center\" flexDirection=\"row\" marginBottom={1}>\n <Text color=\"gray\">Search:</Text>\n <Box\n borderColor={hfSearchFocused ? \"cyan\" : \"gray\"}\n borderStyle=\"single\"\n marginLeft={1}\n paddingX={1}\n width={30}\n >\n {hfSearchFocused ? (\n <TextInput\n focus={true}\n onChange={handleSearch}\n onSubmit={handleSearchSubmit}\n placeholder=\"qwen, llama, whisper...\"\n value={hfSearchInput}\n />\n ) : (\n <Text color=\"gray\">{hfSearchInput || \"'s' to search\"}</Text>\n )}\n </Box>\n <Box marginLeft={2}>\n <Text color=\"gray\">Sort: </Text>\n <Text color=\"yellow\">{getSortLabel()}</Text>\n <Text dimColor> (o)</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"gray\">Size: </Text>\n <Text color={sizeFilter !== \"all\" ? \"yellow\" : \"gray\"}>{getFilterLabel()}</Text>\n <Text dimColor> (f)</Text>\n </Box>\n </Box>\n\n {hfLoading && hfModels.length === 0 && (\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />{\" \"}\n </Text>\n <Text dimColor>Fetching ONNX models...</Text>\n </Box>\n )}\n\n {hfError && <Text color=\"red\">{hfError}</Text>}\n\n {filteredModels.length > 0 && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box>\n <Text dimColor>{\" \"}</Text>\n <Text bold dimColor>\n {\"Model\".padEnd(44)}\n </Text>\n <Text bold dimColor>\n {\"Params\".padStart(8)}\n </Text>\n <Text bold dimColor>\n {\"Size\".padStart(10)}\n </Text>\n <Text bold dimColor>\n {\"Context\".padStart(8)}\n </Text>\n <Text bold dimColor>\n {\"Updated\".padStart(10)}\n </Text>\n <Text bold dimColor>\n {\"Downloads\".padStart(10)}\n </Text>\n </Box>\n {filteredModels.map((model, i) => {\n const isCached = isModelCached(model.id);\n return (\n <Box key={`${model.id}-${i}`}>\n <Text color={isCached ? \"green\" : \"gray\"}>{isCached ? \"●\" : \"○\"} </Text>\n <Text color={i === hfSelected ? \"cyan\" : \"white\"}>\n {i === hfSelected ? \"> \" : \" \"}\n {model.id.length > 42 ? `${model.id.slice(0, 39)}...` : model.id.padEnd(42)}\n </Text>\n <Text color=\"yellow\">{formatParams(model.params, model.id).padStart(8)}</Text>\n <Text color=\"magenta\">\n {(model.sizeBytes ? formatBytes(model.sizeBytes) : \"-\").padStart(10)}\n </Text>\n <Text color=\"cyan\">{formatContext(model.contextLength).padStart(8)}</Text>\n <Text dimColor>{formatDate(model.createdAt).padStart(10)}</Text>\n <Text color=\"gray\">{formatDownloads(model.downloads).padStart(10)}</Text>\n {model.id === currentModel && <Text color=\"cyan\"> [cur]</Text>}\n </Box>\n );\n })}\n {hfLoading && (\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />{\" \"}\n </Text>\n <Text dimColor>Loading more...</Text>\n </Box>\n )}\n {!hfLoading && hfHasMore && (\n <Text dimColor> 'n' for more ({filteredModels.length} shown)</Text>\n )}\n {!hfHasMore && <Text dimColor> End of results ({filteredModels.length} total)</Text>}\n </Box>\n )}\n\n {!hfLoading && filteredModels.length === 0 && hfModels.length > 0 && (\n <Text dimColor>No models match size filter. Press 'f' to change.</Text>\n )}\n\n {!hfLoading && hfModels.length === 0 && !hfError && (\n <Text dimColor>Loading ONNX models...</Text>\n )}\n </>\n )}\n\n {/* Voice Tab */}\n {tab === \"voice\" && (\n <Box flexDirection=\"row\">\n {/* TTS Section */}\n <Box\n borderColor={voiceSection === \"tts\" ? \"magenta\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginRight={2}\n minWidth={45}\n paddingX={1}\n >\n <Text bold color={voiceSection === \"tts\" ? \"magenta\" : \"gray\"}>\n Text-to-Speech (TTS)\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>Voices: Kokoro or Supertonic</Text>\n </Box>\n {TTS_MODELS.map((model, i) => {\n const isCurrent = model.id === currentTTSModel;\n const isSelected = voiceSection === \"tts\" && i === ttsSelected;\n return (\n <Box key={model.id}>\n <Text color={isCurrent ? \"green\" : \"gray\"}>{isCurrent ? \"●\" : \"○\"} </Text>\n <Text bold={isSelected} color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n {model.name}\n </Text>\n <Text dimColor> ({model.size})</Text>\n {isCurrent && <Text color=\"cyan\"> [current]</Text>}\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text dimColor>\n Voices: <Text color=\"yellow\">{TTS_MODELS[ttsSelected]?.voices}</Text> | Quality:{\" \"}\n <Text color=\"green\">{TTS_MODELS[ttsSelected]?.quality}</Text>\n </Text>\n </Box>\n </Box>\n\n {/* STT Section */}\n <Box\n borderColor={voiceSection === \"stt\" ? \"magenta\" : \"gray\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n minWidth={45}\n paddingX={1}\n >\n <Text bold color={voiceSection === \"stt\" ? \"magenta\" : \"gray\"}>\n Speech-to-Text (STT)\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>Whisper models for transcription</Text>\n </Box>\n {STT_MODELS.map((model, i) => {\n const isCurrent = model.id === currentSTTModel;\n const isSelected = voiceSection === \"stt\" && i === sttSelected;\n return (\n <Box key={model.id}>\n <Text color={isCurrent ? \"green\" : \"gray\"}>{isCurrent ? \"●\" : \"○\"} </Text>\n <Text bold={isSelected} color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n {model.name}\n </Text>\n <Text dimColor> ({model.size})</Text>\n {isCurrent && <Text color=\"cyan\"> [current]</Text>}\n </Box>\n );\n })}\n <Box marginTop={1}>\n <Text dimColor>\n Quality: <Text color=\"green\">{STT_MODELS[sttSelected]?.quality}</Text> | Speed:{\" \"}\n <Text color=\"yellow\">{STT_MODELS[sttSelected]?.speed}</Text>\n </Text>\n </Box>\n </Box>\n </Box>\n )}\n\n <Box marginTop={1}>{renderHelpText()}</Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport http from \"node:http\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\n\ntype ServeViewProps = {\n gerbil: Gerbil;\n model: string;\n};\n\nexport function ServeView({ gerbil, model }: ServeViewProps) {\n const [mode, setMode] = useState<\"http\" | \"mcp\">(\"http\");\n const [port, setPort] = useState(3000);\n const [serverStatus, setServerStatus] = useState<\"idle\" | \"starting\" | \"running\" | \"error\">(\n \"idle\",\n );\n const [logs, setLogs] = useState<string[]>([]);\n const [copied, setCopied] = useState(false);\n const [requestCount, setRequestCount] = useState(0);\n const serverRef = useRef<http.Server | null>(null);\n\n // Cleanup on unmount\n useEffect(\n () => () => {\n if (serverRef.current) {\n serverRef.current.close();\n serverRef.current = null;\n }\n },\n [],\n );\n\n useInput((input, key) => {\n if (key.tab) {\n setMode((m) => (m === \"http\" ? \"mcp\" : \"http\"));\n }\n if (key.return && serverStatus === \"idle\" && mode === \"http\") {\n startServer();\n }\n if ((input === \"s\" || input === \"S\") && serverStatus === \"running\") {\n stopServer();\n }\n if (key.upArrow && serverStatus === \"idle\" && mode === \"http\") {\n setPort((p) => Math.min(65_535, p + 1));\n }\n if (key.downArrow && serverStatus === \"idle\" && mode === \"http\") {\n setPort((p) => Math.max(1024, p - 1));\n }\n if ((input === \"c\" || input === \"C\") && mode === \"mcp\") {\n copyMcpConfig();\n }\n });\n\n const addLog = (msg: string) => {\n setLogs((prev) => [...prev.slice(-8), `${new Date().toLocaleTimeString()} ${msg}`]);\n };\n\n const startServer = () => {\n setServerStatus(\"starting\");\n setLogs([]);\n setRequestCount(0);\n\n const server = http.createServer(async (req, res) => {\n // CORS headers\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type\");\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = req.url || \"/\";\n\n if (req.method === \"POST\" && url === \"/generate\") {\n let body = \"\";\n req.on(\"data\", (chunk) => (body += chunk));\n req.on(\"end\", async () => {\n try {\n const { prompt, ...opts } = JSON.parse(body);\n addLog(`POST /generate: \"${prompt.slice(0, 30)}...\"`);\n setRequestCount((c) => c + 1);\n\n const result = await gerbil.generate(prompt, opts);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (e) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: String(e) }));\n }\n });\n } else if (req.method === \"POST\" && url === \"/json\") {\n let body = \"\";\n req.on(\"data\", (chunk) => (body += chunk));\n req.on(\"end\", async () => {\n try {\n const { prompt, schema, ...opts } = JSON.parse(body);\n addLog(`POST /json: \"${prompt.slice(0, 30)}...\"`);\n setRequestCount((c) => c + 1);\n\n // Simple JSON generation without Zod validation in HTTP API\n const result = await gerbil.generate(`${prompt}\\n\\nRespond with valid JSON only.`, {\n ...opts,\n temperature: opts.temperature ?? 0.3,\n });\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(result.text);\n } catch (e) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: String(e) }));\n }\n });\n } else if (req.method === \"GET\" && url === \"/info\") {\n addLog(\"GET /info\");\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(gerbil.getInfo()));\n } else if (req.method === \"GET\" && url === \"/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", model }));\n } else {\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: \"Not found\",\n endpoints: [\"POST /generate\", \"POST /json\", \"GET /info\", \"GET /health\"],\n }),\n );\n }\n });\n\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n addLog(`Port ${port} in use, trying ${port + 1}...`);\n setPort((p) => p + 1);\n setTimeout(() => startServer(), 100);\n } else {\n addLog(`Error: ${err.message}`);\n setServerStatus(\"error\");\n }\n });\n\n server.listen(port, () => {\n addLog(`Server listening on http://localhost:${port}`);\n setServerStatus(\"running\");\n });\n\n serverRef.current = server;\n };\n\n const stopServer = () => {\n if (serverRef.current) {\n serverRef.current.close(() => {\n addLog(\"Server stopped\");\n });\n serverRef.current = null;\n setServerStatus(\"idle\");\n }\n };\n\n const copyMcpConfig = () => {\n const config = JSON.stringify(\n {\n mcpServers: {\n gerbil: {\n command: \"npx\",\n args: [\"-y\", \"@tryhamster/gerbil\", \"serve\", \"--mcp\"],\n },\n },\n },\n null,\n 2,\n );\n\n const platform = process.platform;\n const cmd =\n platform === \"darwin\"\n ? \"pbcopy\"\n : platform === \"win32\"\n ? \"clip\"\n : \"xclip -selection clipboard\";\n const proc = exec(cmd);\n proc.stdin?.write(config);\n proc.stdin?.end();\n\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold>Server Mode</Text>\n <Text dimColor>Expose Gerbil as an API server or MCP server</Text>\n\n <Box marginY={1}>\n <Text bold={mode === \"http\"} color={mode === \"http\" ? \"cyan\" : \"gray\"}>\n [HTTP API]\n </Text>\n <Text color=\"gray\"> </Text>\n <Text bold={mode === \"mcp\"} color={mode === \"mcp\" ? \"cyan\" : \"gray\"}>\n [MCP Server]\n </Text>\n <Text dimColor> (Tab to switch)</Text>\n </Box>\n\n <Box borderColor=\"gray\" borderStyle=\"single\" flexDirection=\"column\" paddingX={1}>\n <Text bold color=\"cyan\">\n {mode === \"http\" ? \"HTTP REST API\" : \"Model Context Protocol\"}\n </Text>\n <Text>\n Model: <Text color=\"cyan\">{model}</Text>\n </Text>\n\n {mode === \"http\" ? (\n <>\n <Text>\n Port: <Text color=\"yellow\">{port}</Text> <Text dimColor>(Up/Down to change)</Text>\n </Text>\n <Text dimColor>Endpoints: /generate, /json, /info, /health</Text>\n </>\n ) : (\n <>\n <Text dimColor>For Claude Desktop and Cursor AI integration</Text>\n <Text dimColor>Tools: generate, stream, json, embed, plus all skills</Text>\n </>\n )}\n </Box>\n\n {mode === \"http\" && serverStatus !== \"idle\" && (\n <Box flexDirection=\"column\" marginY={1}>\n <Box>\n {serverStatus === \"starting\" && (\n <Text color=\"yellow\">\n <Spinner type=\"dots\" /> Starting server...\n </Text>\n )}\n {serverStatus === \"running\" && (\n <Text color=\"green\">\n ● Running on http://localhost:{port} ({requestCount} requests)\n </Text>\n )}\n {serverStatus === \"error\" && <Text color=\"red\">✗ Server error</Text>}\n </Box>\n\n {logs.length > 0 && (\n <Box\n borderColor=\"gray\"\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginTop={1}\n paddingX={1}\n >\n {logs.slice(-5).map((log, i) => (\n <Text dimColor key={i}>\n {log}\n </Text>\n ))}\n </Box>\n )}\n </Box>\n )}\n\n {mode === \"http\" && serverStatus === \"running\" && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Test with:</Text>\n <Text color=\"green\">curl -X POST http://localhost:{port}/generate \\</Text>\n <Text color=\"green\"> -H \"Content-Type: application/json\" \\</Text>\n <Text color=\"green\"> -d '{`{\"prompt\": \"Hello!\"}`}'</Text>\n </Box>\n )}\n\n {mode === \"mcp\" && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box marginBottom={1}>\n <Text color=\"yellow\">⚠ </Text>\n <Text>MCP servers use stdio - run from terminal, not REPL</Text>\n </Box>\n\n <Text dimColor>Terminal command:</Text>\n <Text color=\"cyan\">npx @tryhamster/gerbil serve --mcp</Text>\n\n <Box marginTop={1}>\n <Text dimColor>Add to MCP config (e.g. .cursor/mcp.json):</Text>\n </Box>\n <Text color=\"green\">{`{\n \"mcpServers\": {\n \"gerbil\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@tryhamster/gerbil\", \"serve\", \"--mcp\"]\n }\n }\n}`}</Text>\n\n {copied && (\n <Box marginTop={1}>\n <Text color=\"green\">✓ Config copied to clipboard!</Text>\n </Box>\n )}\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n {mode === \"mcp\" ? (\n <>\n <Text color=\"yellow\">c</Text> copy config | <Text color=\"yellow\">Tab</Text> HTTP mode\n | <Text color=\"gray\">Esc</Text> back\n </>\n ) : serverStatus === \"idle\" ? (\n <>\n <Text color=\"yellow\">Enter</Text> start | <Text color=\"yellow\">Tab</Text> MCP mode |{\" \"}\n <Text color=\"gray\">Esc</Text> back\n </>\n ) : (\n <>\n <Text color=\"yellow\">s</Text> stop | <Text color=\"gray\">Esc</Text> back\n </>\n )}\n </Text>\n </Box>\n </Box>\n );\n}\n","import { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useEffect, useState } from \"react\";\nimport type { Gerbil } from \"../../../core/gerbil.js\";\nimport { getSkillInfo, listSkills, loadProjectSkills, useSkill } from \"../../../skills/index.js\";\nimport { type CachedModelInfo, getCacheInfo, processImagePath } from \"../utils.js\";\n\ntype SkillsViewProps = {\n gerbil: Gerbil;\n onCreateNew?: () => void;\n onSwitchModel?: (modelId: string) => void;\n};\n\ntype Phase = \"select\" | \"input\" | \"running\" | \"result\" | \"model-select\";\n\n// Vision skills that require image input\nconst VISION_SKILLS = new Set([\n \"describe-image\",\n \"analyze-screenshot\",\n \"extract-from-image\",\n \"compare-images\",\n \"caption-image\",\n]);\n\n// Detailed skill information with examples\nconst SKILL_DETAILS: Record<\n string,\n {\n emoji: string;\n what: string;\n example: string;\n cliExample?: string;\n inputHint: string;\n isVision?: boolean;\n }\n> = {\n commit: {\n emoji: \"->\",\n what: \"Generate git commit messages from staged changes\",\n example: \"feat(auth): add OAuth2 login flow\",\n cliExample: \"gerbil commit --type conventional\",\n inputHint: \"Leave empty to use staged changes, or paste a diff\",\n },\n summarize: {\n emoji: \"=\",\n what: \"Condense documents, articles, or code into key points\",\n example: \"• Key insight 1\\\\n• Key insight 2\\\\n• Conclusion\",\n cliExample: \"gerbil summarize README.md --format bullets\",\n inputHint: \"Paste text, article, or document to summarize\",\n },\n explain: {\n emoji: \"?\",\n what: \"Break down code or concepts for any skill level\",\n example: \"This function uses recursion to...\",\n cliExample: \"gerbil explain src/utils.ts --level beginner\",\n inputHint: \"Paste code or describe a concept to explain\",\n },\n review: {\n emoji: \"!\",\n what: \"Find bugs, suggest improvements, check security\",\n example: \"⚠️ Line 5: potential null reference\\\\n✓ Good use of...\",\n cliExample: \"gerbil review src/api.ts --focus security,performance\",\n inputHint: \"Paste code to get a detailed review\",\n },\n extract: {\n emoji: \"<>\",\n what: \"Pull structured data from messy text (names, dates, etc)\",\n example: '{ \"name\": \"John\", \"email\": \"john@...\" }',\n cliExample: 'echo \"John Doe, john@email.com\" | gerbil extract',\n inputHint: \"Paste unstructured text to extract data from\",\n },\n title: {\n emoji: \"#\",\n what: \"Generate catchy titles, headlines, or subject lines\",\n example: \"10 Tips for Better Code Reviews\",\n cliExample: \"cat article.md | gerbil title --style seo\",\n inputHint: \"Paste content to generate a title for\",\n },\n test: {\n emoji: \"T\",\n what: \"Auto-generate unit tests for functions and classes\",\n example: \"describe('add', () => { it('adds numbers'... })\",\n cliExample: \"gerbil test src/utils.ts --framework vitest\",\n inputHint: \"Paste a function or class to generate tests for\",\n },\n translate: {\n emoji: \"~\",\n what: \"Translate text to any language while preserving tone\",\n example: \"Hola mundo (Spanish)\",\n cliExample: 'gerbil translate \"Hello world\" --to spanish',\n inputHint: 'Type: \"your text\" to [language]',\n },\n // Vision skills\n \"describe-image\": {\n emoji: \"👁\",\n what: \"Describe an image using vision AI - identify objects, text, and scenes\",\n example: \"A sunset over mountains with orange and purple hues...\",\n cliExample: \"gerbil describe-image ~/photo.png --focus details\",\n inputHint: \"Enter image URL or local path (supports ~)\",\n isVision: true,\n },\n \"analyze-screenshot\": {\n emoji: \"📱\",\n what: \"Analyze a UI screenshot for design, accessibility, or QA issues\",\n example: \"UI Review: Good visual hierarchy, but contrast ratio on buttons...\",\n cliExample: \"gerbil analyze-screenshot ~/app.png --type accessibility\",\n inputHint: \"Enter screenshot URL or local path\",\n isVision: true,\n },\n \"extract-from-image\": {\n emoji: \"📋\",\n what: \"Extract text, code, tables, or structured data from images\",\n example: '{ \"title\": \"Invoice #123\", \"total\": \"$50.00\" }',\n cliExample: \"gerbil extract-from-image ~/receipt.jpg --type text\",\n inputHint: \"Enter image URL or local path\",\n isVision: true,\n },\n \"compare-images\": {\n emoji: \"🔄\",\n what: \"Compare two images and describe differences\",\n example: \"Differences: 1. Button color changed from blue to green...\",\n cliExample: \"gerbil compare-images --image1 old.png --image2 new.png\",\n inputHint: \"Enter first image, then second (comma-separated)\",\n isVision: true,\n },\n \"caption-image\": {\n emoji: \"img\",\n what: \"Generate alt text, captions, or titles for images\",\n example: \"A developer working late at night, illuminated by monitor glow\",\n cliExample: \"gerbil caption-image ~/photo.jpg --style alt-text\",\n inputHint: \"Enter image URL or local path\",\n isVision: true,\n },\n // TTS skills\n speak: {\n emoji: \"spk\",\n what: \"Convert text to speech using on-device TTS (Kokoro-82M)\",\n example: \"[Audio plays through speakers]\",\n cliExample: \"gerbil speak 'Hello world' --voice af_bella -o hello.wav\",\n inputHint: \"Enter text to speak aloud\",\n },\n announce: {\n emoji: \"ann\",\n what: \"Generate and speak an AI-crafted announcement with style\",\n example: \"[Casual] 'Hey, great news - the build passed!'\",\n cliExample: \"gerbil announce 'Build complete' --style excited\",\n inputHint: \"Enter message to announce (AI will rephrase it)\",\n },\n \"read-aloud\": {\n emoji: \"rd\",\n what: \"Read text or file content aloud using TTS\",\n example: \"[Audio plays paragraph by paragraph]\",\n cliExample: \"gerbil read-aloud ./README.md --voice bf_emma\",\n inputHint: \"Enter file path or text to read aloud\",\n },\n // STT skills\n transcribe: {\n emoji: \"stt\",\n what: \"Transcribe audio to text using on-device STT (Whisper)\",\n example: \"And so, my fellow Americans, ask not what your country can do for you...\",\n cliExample: \"gerbil transcribe audio.wav --timestamps\",\n inputHint: \"Enter audio file path (WAV, MP3, etc.)\",\n },\n};\n\nexport function SkillsView({ gerbil, onCreateNew, onSwitchModel }: SkillsViewProps) {\n const [phase, setPhase] = useState<Phase>(\"select\");\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [selectedSkill, setSelectedSkill] = useState<string | null>(null);\n const [inputValue, setInputValue] = useState(\"\");\n const [result, setResult] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [skillsLoaded, setSkillsLoaded] = useState(false);\n const [cachedModels, setCachedModels] = useState<CachedModelInfo[]>([]);\n const [modelSelectIndex, setModelSelectIndex] = useState(0);\n const [currentModel, setCurrentModel] = useState<string>(\n gerbil.getModelInfo()?.id || \"qwen3-0.6b\",\n );\n\n // Image picker state (for vision skills)\n const [attachedImage, setAttachedImage] = useState<string | null>(null);\n const [attachedImageName, setAttachedImageName] = useState<string | null>(null);\n const [imageError, setImageError] = useState<string | null>(null);\n\n // Load project skills from .gerbil/skills/ on mount\n useEffect(() => {\n if (!skillsLoaded) {\n loadProjectSkills()\n .then(() => {\n setSkillsLoaded(true);\n })\n .catch(() => {\n setSkillsLoaded(true);\n });\n }\n // Load cached models\n const cacheInfo = getCacheInfo();\n setCachedModels(cacheInfo.models);\n }, [skillsLoaded]);\n\n const skills = listSkills();\n\n // Create skill list with \"Create new\" at the end\n const allItems = [\n ...skills.map((name) => {\n const info = getSkillInfo(name);\n const details = SKILL_DETAILS[name];\n const isVision = details?.isVision || VISION_SKILLS.has(name);\n return {\n name,\n description: info?.description || \"\",\n builtin: info?.builtin,\n emoji: details?.emoji || (isVision ? \"👁\" : \"*\"),\n what: details?.what || info?.description || \"\",\n example: details?.example || \"\",\n cliExample: details?.cliExample || \"\",\n inputHint:\n details?.inputHint || (isVision ? \"Enter image URL or local path\" : \"Enter input...\"),\n isVision,\n };\n }),\n {\n name: \"__create__\",\n description: \"Build a custom AI skill with the wizard\",\n builtin: false,\n emoji: \"+\",\n what: \"Create your own skill using AI-assisted code generation\",\n example: \"sentiment-analyzer, email-writer, data-formatter...\",\n cliExample: \"\",\n inputHint: \"\",\n isVision: false,\n },\n ];\n\n useInput((input, key) => {\n if (phase === \"model-select\") {\n if (key.upArrow) {\n setModelSelectIndex((i) => Math.max(0, i - 1));\n }\n if (key.downArrow) {\n setModelSelectIndex((i) => Math.min(cachedModels.length - 1, i + 1));\n }\n if (key.return && cachedModels.length > 0) {\n const model = cachedModels[modelSelectIndex];\n const modelId = model.name.includes(\"/\") ? model.name : model.name.replace(\"--\", \"/\");\n setCurrentModel(modelId);\n onSwitchModel?.(modelId);\n setPhase(\"select\");\n }\n if (key.escape) {\n setPhase(\"select\");\n }\n return;\n }\n\n if (phase === \"select\") {\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(0, i - 1));\n }\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(allItems.length - 1, i + 1));\n }\n if (key.return) {\n const item = allItems[selectedIndex];\n if (item.name === \"__create__\") {\n onCreateNew?.();\n } else {\n setSelectedSkill(item.name);\n setPhase(\"input\");\n }\n }\n // C to create new skill\n if (input === \"c\" || input === \"C\") {\n onCreateNew?.();\n }\n // M to select model\n if ((input === \"m\" || input === \"M\") && cachedModels.length > 0) {\n setPhase(\"model-select\");\n }\n }\n if (key.escape && (phase === \"result\" || phase === \"input\")) {\n handleReset();\n }\n });\n\n const handleRunSkill = async (input: string) => {\n if (!selectedSkill) {\n return;\n }\n\n // Check if this is a vision skill\n const isVision = VISION_SKILLS.has(selectedSkill) || SKILL_DETAILS[selectedSkill]?.isVision;\n\n setPhase(\"running\");\n setError(null);\n\n try {\n const skill = useSkill(selectedSkill);\n\n // Parse input as JSON or use as content/text\n let parsedInput: Record<string, unknown>;\n try {\n parsedInput = JSON.parse(input);\n } catch {\n // Smart input parsing based on skill type\n if (isVision) {\n // For vision skills, process the image path\n let imageSource = attachedImage;\n\n // If no attached image but input provided, try to process it as path\n if (!imageSource && input.trim()) {\n const result = processImagePath(input);\n if (result.success && result.imageSource) {\n imageSource = result.imageSource;\n } else if (result.message) {\n throw new Error(result.message);\n }\n }\n\n if (!imageSource) {\n throw new Error(\"No image provided. Enter an image URL or local path.\");\n }\n\n // Build input based on skill\n if (selectedSkill === \"compare-images\") {\n // compare-images needs two images - for now just use one\n parsedInput = { image1: imageSource, image2: imageSource };\n } else {\n parsedInput = { image: imageSource };\n }\n } else if (selectedSkill === \"commit\") {\n parsedInput = { diff: input, type: \"conventional\" };\n } else if (selectedSkill === \"translate\") {\n const toMatch = input.match(/(.+?)\\s+to\\s+(\\w+)$/i);\n if (toMatch) {\n parsedInput = { text: toMatch[1].trim(), to: toMatch[2].trim() };\n } else {\n parsedInput = { text: input, to: \"spanish\" };\n }\n } else if (selectedSkill === \"summarize\") {\n parsedInput = { content: input, format: \"bullet\" };\n } else if (selectedSkill === \"explain\") {\n parsedInput = { content: input, level: \"beginner\" };\n } else if (selectedSkill === \"review\") {\n parsedInput = { code: input };\n } else if (selectedSkill === \"test\") {\n parsedInput = { code: input, framework: \"jest\" };\n } else if (selectedSkill === \"speak\") {\n // TTS: speak skill expects 'text'\n parsedInput = { text: input };\n } else if (selectedSkill === \"announce\") {\n // TTS: announce skill expects 'message'\n parsedInput = { message: input };\n } else if (selectedSkill === \"read-aloud\") {\n // TTS: read-aloud skill expects 'content' (file path or text)\n parsedInput = { content: input };\n } else if (selectedSkill === \"transcribe\") {\n // STT: transcribe skill expects 'audio' (file path)\n parsedInput = { audio: input };\n } else {\n parsedInput = { content: input, text: input, code: input, message: input };\n }\n }\n\n const output = await skill.run(parsedInput, gerbil);\n\n // Clean up think tags from output\n let cleanOutput = typeof output === \"string\" ? output : JSON.stringify(output, null, 2);\n cleanOutput = cleanOutput.replace(/<think>[\\s\\S]*?<\\/think>/g, \"\").trim();\n\n setResult(cleanOutput);\n setPhase(\"result\");\n } catch (err) {\n setError(String(err));\n setPhase(\"result\");\n } finally {\n // Clear attached image after running\n setAttachedImage(null);\n setAttachedImageName(null);\n }\n };\n\n const handleReset = () => {\n setPhase(\"select\");\n setSelectedSkill(null);\n setInputValue(\"\");\n setResult(null);\n setError(null);\n setAttachedImage(null);\n setAttachedImageName(null);\n setImageError(null);\n };\n\n // Handle image path input for vision skills\n const handleImageInput = (imagePath: string) => {\n setImageError(null);\n const result = processImagePath(imagePath);\n if (result.success && result.imageSource) {\n setAttachedImage(result.imageSource);\n setAttachedImageName(result.filename || \"image\");\n setInputValue(imagePath); // Keep path in input for display\n } else if (result.message) {\n setImageError(result.message);\n }\n };\n\n // Selection phase\n if (phase === \"select\") {\n const currentItem = allItems[selectedIndex];\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n {/* Header explanation */}\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold color=\"cyan\">\n What are Skills?\n </Text>\n <Text dimColor>\n Skills are pre-built AI tasks that solve specific problems. Each skill has optimized\n prompts and structured outputs.\n </Text>\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">CLI Usage:</Text> gerbil {\"<skill>\"} [input] [--options]\n </Text>\n </Box>\n <Text dimColor>\n Examples: <Text color=\"gray\">gerbil commit</Text> ·{\" \"}\n <Text color=\"gray\">gerbil summarize README.md</Text> ·{\" \"}\n <Text color=\"gray\">gerbil explain src/utils.ts</Text>\n </Text>\n </Box>\n\n {/* Skill list */}\n <Box flexDirection=\"row\">\n {/* Left: Skill list */}\n <Box flexDirection=\"column\" marginRight={2} minWidth={28}>\n <Box marginBottom={1}>\n <Text bold>Available Skills:</Text>\n </Box>\n {allItems.map((item, i) => {\n const isSelected = i === selectedIndex;\n const isCreate = item.name === \"__create__\";\n return (\n <Box key={item.name}>\n <Text\n bold={isSelected}\n color={\n isSelected ? (isCreate ? \"green\" : item.isVision ? \"yellow\" : \"cyan\") : \"gray\"\n }\n >\n {isSelected ? \"> \" : \" \"}[{item.emoji}]{\" \"}\n {isCreate ? \"Create new...\" : item.name}\n </Text>\n {item.isVision && !isSelected && <Text dimColor> 📷</Text>}\n </Box>\n );\n })}\n </Box>\n\n {/* Right: Selected skill details */}\n <Box\n borderColor={\n currentItem.name === \"__create__\" ? \"green\" : currentItem.isVision ? \"yellow\" : \"cyan\"\n }\n borderStyle=\"single\"\n flexDirection=\"column\"\n flexGrow={1}\n paddingX={2}\n paddingY={1}\n >\n <Box>\n <Text\n bold\n color={\n currentItem.name === \"__create__\"\n ? \"green\"\n : currentItem.isVision\n ? \"yellow\"\n : \"cyan\"\n }\n >\n {currentItem.name === \"__create__\" ? \"Create New Skill\" : currentItem.name}\n </Text>\n {currentItem.isVision && <Text color=\"yellow\"> 📷 vision</Text>}\n </Box>\n\n {currentItem.builtin && <Text dimColor>[built-in]</Text>}\n\n <Box marginTop={1}>\n <Text wrap=\"wrap\">{currentItem.what}</Text>\n </Box>\n\n {currentItem.example && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Output example:</Text>\n <Text color=\"green\">\n {currentItem.example.slice(0, 80)}\n {currentItem.example.length > 80 ? \"...\" : \"\"}\n </Text>\n </Box>\n )}\n\n {currentItem.cliExample && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>CLI usage:</Text>\n <Text color=\"yellow\">{currentItem.cliExample}</Text>\n </Box>\n )}\n\n {currentItem.isVision && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor>Input:</Text>\n <Text color=\"gray\">Image URL or local path (~/path/to/image.png)</Text>\n </Box>\n )}\n\n {currentItem.name !== \"__create__\" && (\n <Box marginTop={1}>\n <Text dimColor>\n Press <Text color=\"cyan\">Enter</Text> to run this skill\n </Text>\n </Box>\n )}\n </Box>\n </Box>\n\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text dimColor>Model: </Text>\n <Text color=\"cyan\">{currentModel}</Text>\n {cachedModels.length > 0 && <Text dimColor> (press </Text>}\n {cachedModels.length > 0 && <Text color=\"yellow\">m</Text>}\n {cachedModels.length > 0 && <Text dimColor> to change)</Text>}\n </Box>\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Up/Down</Text> navigate | <Text color=\"yellow\">Enter</Text>{\" \"}\n select | <Text color=\"yellow\">m</Text> model | <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n </Box>\n );\n }\n\n // Model selection phase\n if (phase === \"model-select\") {\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Text bold color=\"cyan\">\n Select Model for Skills\n </Text>\n <Box marginBottom={1}>\n <Text dimColor>Choose a cached model to run skills with</Text>\n </Box>\n\n <Box flexDirection=\"column\" marginY={1}>\n {cachedModels.length === 0 ? (\n <Text color=\"gray\">No cached models found. Download models from Model view.</Text>\n ) : (\n cachedModels.map((model, i) => {\n const isSelected = i === modelSelectIndex;\n const isCurrent =\n model.name === currentModel || model.name.replace(\"--\", \"/\") === currentModel;\n return (\n <Box key={model.name}>\n <Text\n bold={isSelected}\n color={isSelected ? \"cyan\" : isCurrent ? \"green\" : \"gray\"}\n >\n {isSelected ? \"> \" : \" \"}\n {model.name.length > 50 ? `${model.name.slice(0, 47)}...` : model.name}\n </Text>\n <Text color=\"yellow\"> {model.modelSize}</Text>\n {isCurrent && <Text color=\"green\"> [current]</Text>}\n </Box>\n );\n })\n )}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Up/Down</Text> navigate | <Text color=\"yellow\">Enter</Text> select\n | <Text color=\"gray\">Esc</Text> cancel\n </Text>\n </Box>\n </Box>\n );\n }\n\n // Input phase\n if (phase === \"input\") {\n const info = getSkillInfo(selectedSkill!);\n const details = SKILL_DETAILS[selectedSkill!];\n const isVision = VISION_SKILLS.has(selectedSkill!) || details?.isVision;\n\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text bold color={isVision ? \"yellow\" : \"cyan\"}>\n [{details?.emoji || \"*\"}] {selectedSkill}\n </Text>\n {isVision && (\n <Text color=\"yellow\" dimColor>\n {\" \"}\n (vision)\n </Text>\n )}\n </Box>\n <Text dimColor>{details?.what || info?.description}</Text>\n </Box>\n\n {details?.example && (\n <Box marginBottom={1}>\n <Text dimColor>Example output: </Text>\n <Text color=\"green\">\n {details.example.slice(0, 60)}\n {details.example.length > 60 ? \"...\" : \"\"}\n </Text>\n </Box>\n )}\n\n {/* Vision skill - show image-specific UI */}\n {isVision && (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box>\n <Text color=\"yellow\">📷 </Text>\n <Text color=\"white\">{details?.inputHint || \"Enter image URL or local path:\"}</Text>\n </Box>\n <Text dimColor>Supports: https://..., ~/path/to/image.png, ./local.jpg</Text>\n </Box>\n )}\n\n {/* Non-vision skill - show regular hint */}\n {!isVision && (\n <Box marginBottom={1}>\n <Text color=\"white\">{details?.inputHint || \"Enter input:\"}</Text>\n </Box>\n )}\n\n {/* Attached image indicator */}\n {attachedImage && attachedImageName && (\n <Box marginBottom={1}>\n <Text color=\"green\">✓ Image ready: {attachedImageName}</Text>\n </Box>\n )}\n\n {/* Image error */}\n {imageError && (\n <Box marginBottom={1}>\n <Text color=\"red\">{imageError}</Text>\n </Box>\n )}\n\n <Box borderColor={isVision ? \"yellow\" : \"cyan\"} borderStyle=\"single\" paddingX={1}>\n <Text color={isVision ? \"yellow\" : \"cyan\"}>> </Text>\n <TextInput\n onChange={(val) => {\n setInputValue(val);\n // For vision skills, validate image path on change\n if (isVision && val.trim()) {\n handleImageInput(val);\n }\n }}\n onSubmit={handleRunSkill}\n placeholder={isVision ? \"https://... or ~/path/to/image.png\" : \"Type here...\"}\n value={inputValue}\n />\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">Enter</Text> run | <Text color=\"gray\">Esc</Text> cancel\n {isVision && attachedImage && <Text color=\"green\"> | ✓ image ready</Text>}\n </Text>\n </Box>\n </Box>\n );\n }\n\n // Running phase\n if (phase === \"running\") {\n const details = SKILL_DETAILS[selectedSkill!];\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n [{details?.emoji || \"*\"}] {selectedSkill}\n </Text>\n </Box>\n <Box>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />{\" \"}\n </Text>\n <Text>Processing...</Text>\n </Box>\n <Box marginTop={1}>\n <Text dimColor>This may take a few seconds</Text>\n </Box>\n </Box>\n );\n }\n\n // Result phase\n const _details = SKILL_DETAILS[selectedSkill!];\n return (\n <Box flexDirection=\"column\" padding={1}>\n <Box marginBottom={1}>\n <Text bold color={error ? \"red\" : \"green\"}>\n {error ? \"[x] Error\" : \"[done]\"} {selectedSkill}\n </Text>\n </Box>\n\n <Box\n borderColor={error ? \"red\" : \"green\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n padding={1}\n >\n <Text color={error ? \"red\" : \"white\"} wrap=\"wrap\">\n {error || result}\n </Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"gray\">Esc</Text> back to skills\n </Text>\n </Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Box, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport TextInput from \"ink-text-input\";\nimport { useEffect, useState } from \"react\";\nimport { getTool, getToolDefinitions, loadProjectTools } from \"../../../core/tools.js\";\n\ntype ToolsViewProps = {\n onCreateTool: () => void;\n};\n\ntype ToolInfo = {\n name: string;\n description: string;\n source: \"builtin\" | \"project\" | \"error\";\n path?: string;\n error?: string;\n parameters?: object;\n};\n\ntype ExecuteState = \"idle\" | \"input\" | \"running\" | \"done\";\n\nexport function ToolsView({ onCreateTool }: ToolsViewProps) {\n const [tools, setTools] = useState<ToolInfo[]>([]);\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [expandedTool, setExpandedTool] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n\n // Execution state\n const [executeState, setExecuteState] = useState<ExecuteState>(\"idle\");\n const [executeInput, setExecuteInput] = useState(\"\");\n const [executeResult, setExecuteResult] = useState<string | null>(null);\n const [executeError, setExecuteError] = useState<string | null>(null);\n\n useEffect(() => {\n const loadAllTools = async () => {\n const allTools: ToolInfo[] = [];\n\n // First load project tools from .gerbil/tools/\n const projectResults = await loadProjectTools();\n\n // Add failed tools to list with error status\n for (const result of projectResults) {\n if (!result.loaded) {\n allTools.push({\n name: result.name,\n description: result.error || \"Failed to load\",\n source: \"error\",\n path: result.path,\n error: result.error,\n });\n }\n }\n\n // Then get all successfully registered tool definitions\n const defs = getToolDefinitions();\n const toolsDir = path.join(process.cwd(), \".gerbil\", \"tools\");\n\n for (const t of defs) {\n const isBuiltin = t.name.startsWith(\"gerbil_\");\n let toolPath: string | undefined;\n\n if (!isBuiltin) {\n // Check if tool file exists\n const tsPath = path.join(toolsDir, `${t.name}.tool.ts`);\n const jsPath = path.join(toolsDir, `${t.name}.tool.js`);\n if (fs.existsSync(tsPath)) {\n toolPath = tsPath;\n } else if (fs.existsSync(jsPath)) {\n toolPath = jsPath;\n }\n }\n\n allTools.push({\n name: t.name,\n description: t.description,\n source: isBuiltin ? \"builtin\" : \"project\",\n path: toolPath,\n parameters: t.parameters ? {} : undefined,\n });\n }\n\n setTools(allTools);\n setLoading(false);\n };\n\n loadAllTools();\n }, []);\n\n const selectedTool = selectedIndex > 0 ? tools[selectedIndex - 1] : null;\n\n const executeTool = async (toolName: string, inputStr: string) => {\n setExecuteState(\"running\");\n setExecuteError(null);\n setExecuteResult(null);\n\n try {\n const tool = getTool(toolName);\n if (!tool) {\n throw new Error(`Tool \"${toolName}\" not found`);\n }\n\n // Simple param handling:\n // - If JSON object, use as-is\n // - Otherwise, set all common param names to the input value\n let params: any = {};\n const trimmed = (inputStr || \"\").trim();\n\n if (trimmed) {\n if (trimmed.startsWith(\"{\")) {\n params = JSON.parse(trimmed);\n } else {\n // Set multiple common param names so the tool gets the value\n params = {\n query: trimmed,\n input: trimmed,\n text: trimmed,\n theme: trimmed,\n content: trimmed,\n value: trimmed,\n };\n }\n }\n\n const result = await tool(params);\n setExecuteResult(result);\n setExecuteState(\"done\");\n } catch (e) {\n setExecuteError(e instanceof Error ? e.message : String(e));\n setExecuteState(\"done\");\n }\n };\n\n const handleExecuteSubmit = (value: string) => {\n if (selectedTool && selectedTool.source !== \"error\") {\n executeTool(selectedTool.name, value);\n }\n };\n\n useInput((char, key) => {\n // Handle escape to exit execute mode\n if (key.escape && executeState !== \"idle\") {\n setExecuteState(\"idle\");\n setExecuteInput(\"\");\n setExecuteResult(null);\n setExecuteError(null);\n return;\n }\n\n // Don't process other keys during input/running\n if (executeState === \"input\" || executeState === \"running\") {\n return;\n }\n\n // Clear result on any key when done\n if (executeState === \"done\") {\n setExecuteState(\"idle\");\n setExecuteResult(null);\n setExecuteError(null);\n return;\n }\n\n if (key.upArrow && selectedIndex > 0) {\n setSelectedIndex(selectedIndex - 1);\n setExpandedTool(null);\n }\n if (key.downArrow && selectedIndex < tools.length) {\n setSelectedIndex(selectedIndex + 1);\n setExpandedTool(null);\n }\n if (key.return) {\n if (selectedIndex === 0) {\n onCreateTool();\n } else if (selectedTool) {\n // Toggle expanded view\n setExpandedTool(expandedTool === selectedTool.name ? null : selectedTool.name);\n }\n }\n if (char === \"c\" || char === \"C\") {\n onCreateTool();\n }\n // Open in editor (works for both project and error tools)\n if ((char === \"o\" || char === \"O\") && selectedTool?.path) {\n exec(`code \"${selectedTool.path}\"`);\n }\n // Execute tool\n if ((char === \"x\" || char === \"X\") && selectedTool && selectedTool.source !== \"error\") {\n setExecuteInput(\"\");\n setExecuteState(\"input\");\n }\n });\n\n if (loading) {\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Text dimColor>Loading tools...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <Box marginBottom={1}>\n <Text bold color=\"magenta\">\n Tools\n </Text>\n <Text dimColor> — {tools.length} registered</Text>\n </Box>\n\n {/* Create new option */}\n <Box marginBottom={1}>\n <Text color={selectedIndex === 0 ? \"cyan\" : \"gray\"}>\n {selectedIndex === 0 ? \"> \" : \" \"}\n <Text bold color=\"green\">\n + Create new tool\n </Text>\n </Text>\n </Box>\n\n {/* Tool list */}\n <Box flexDirection=\"column\">\n {tools.length === 0 ? (\n <Text dimColor>No tools registered yet</Text>\n ) : (\n tools.map((tool, i) => {\n const isSelected = selectedIndex === i + 1;\n const isExpanded = expandedTool === tool.name;\n\n return (\n <Box flexDirection=\"column\" key={tool.name}>\n <Box>\n <Text color={isSelected ? \"cyan\" : \"white\"}>\n {isSelected ? \"> \" : \" \"}\n <Text\n bold\n color={isSelected ? \"cyan\" : tool.source === \"error\" ? \"red\" : \"white\"}\n >\n {tool.name}\n </Text>\n </Text>\n <Text\n color={tool.source === \"error\" ? \"red\" : undefined}\n dimColor={tool.source !== \"error\"}\n >\n {\" \"}\n — {tool.description.slice(0, 40)}\n {tool.description.length > 40 ? \"...\" : \"\"}\n </Text>\n <Text\n color={\n tool.source === \"builtin\"\n ? \"yellow\"\n : tool.source === \"error\"\n ? \"red\"\n : \"green\"\n }\n dimColor={tool.source !== \"error\"}\n >\n {\" \"}\n [{tool.source}]\n </Text>\n </Box>\n\n {/* Expanded details */}\n {isExpanded && (\n <Box\n borderColor={tool.source === \"error\" ? \"red\" : \"cyan\"}\n borderStyle=\"single\"\n flexDirection=\"column\"\n marginLeft={4}\n marginY={1}\n paddingX={1}\n >\n {tool.source === \"error\" ? (\n <>\n <Text>\n <Text color=\"red\">Error:</Text> {tool.error}\n </Text>\n <Text>\n <Text color=\"gray\">File:</Text> {tool.path}\n </Text>\n <Text dimColor>Press 'o' to open and fix in editor</Text>\n </>\n ) : (\n <>\n <Text>\n <Text color=\"gray\">Description:</Text> {tool.description}\n </Text>\n <Text>\n <Text color=\"gray\">Source:</Text>{\" \"}\n {tool.source === \"builtin\"\n ? \"Built-in (src/core/tools.ts)\"\n : tool.path || \".gerbil/tools/\"}\n </Text>\n {tool.source === \"project\" && tool.path && (\n <Text dimColor>Press 'o' to open in editor</Text>\n )}\n </>\n )}\n </Box>\n )}\n </Box>\n );\n })\n )}\n </Box>\n\n {/* Execute UI */}\n {executeState === \"input\" && selectedTool && (\n <Box\n borderColor=\"yellow\"\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text bold color=\"yellow\">\n Execute: {selectedTool.name}\n </Text>\n <Text dimColor>Enter params (JSON or single value, empty for none):</Text>\n <Box marginTop={1}>\n <Text color=\"yellow\">> </Text>\n <TextInput\n onChange={setExecuteInput}\n onSubmit={handleExecuteSubmit}\n placeholder='{\"param\": \"value\"} or just: value'\n value={executeInput}\n />\n </Box>\n </Box>\n )}\n\n {executeState === \"running\" && (\n <Box marginY={1} paddingX={1}>\n <Text color=\"yellow\">\n <Spinner type=\"dots\" />\n </Text>\n <Text> Running {selectedTool?.name}...</Text>\n </Box>\n )}\n\n {executeState === \"done\" && (\n <Box\n borderColor={executeError ? \"red\" : \"green\"}\n borderStyle=\"round\"\n flexDirection=\"column\"\n marginY={1}\n paddingX={1}\n >\n <Text bold color={executeError ? \"red\" : \"green\"}>\n {executeError ? \"✗ Error\" : \"✓ Result\"}\n </Text>\n <Text color={executeError ? \"red\" : \"white\"}>\n {executeError || executeResult || \"(no output)\"}\n </Text>\n <Text dimColor>Press any key to dismiss</Text>\n </Box>\n )}\n\n <Box marginTop={1}>\n <Text dimColor>\n <Text color=\"yellow\">x</Text> execute |<Text color=\"green\">c</Text> create |\n <Text color=\"cyan\">Enter</Text> expand |<Text color=\"gray\">↑↓</Text> navigate |\n <Text color=\"gray\">Esc</Text> back\n </Text>\n </Box>\n </Box>\n );\n}\n","import { exec } from \"node:child_process\";\nimport { Box, Text, useApp, useInput } from \"ink\";\nimport type React from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { Gerbil } from \"../../core/gerbil.js\";\nimport { setToolContext } from \"../../core/tools.js\";\nimport {\n CURRENT_VERSION,\n checkForUpdate,\n installUpdate,\n type UpdateCheckResult,\n} from \"./auto-update.js\";\nimport { cleanResponse, getCacheInfo, hyperlink, type SessionStats } from \"./utils.js\";\nimport { type BenchmarkResult, type BenchmarkStats, BenchmarkView } from \"./views/BenchmarkView.js\";\nimport { ChatView } from \"./views/ChatView.js\";\nimport { CodeView } from \"./views/CodeView.js\";\nimport { CreateSkillView } from \"./views/CreateSkillView.js\";\nimport { CreateToolView } from \"./views/CreateToolView.js\";\nimport { FrameworksView } from \"./views/FrameworksView.js\";\nimport { InfoView } from \"./views/InfoView.js\";\nimport { LoadingView } from \"./views/LoadingView.js\";\nimport { ModelView } from \"./views/ModelView.js\";\nimport { ServeView } from \"./views/ServeView.js\";\nimport { SkillsView } from \"./views/SkillsView\";\nimport { ToolsView } from \"./views/ToolsView.js\";\n\nexport type View =\n | \"menu\"\n | \"chat\"\n | \"tools\"\n | \"skills\"\n | \"create-skill\"\n | \"create-tool\"\n | \"code\"\n | \"model\"\n | \"frameworks\"\n | \"benchmark\"\n | \"info\"\n | \"serve\";\n\nexport type AppState = {\n view: View;\n gerbil: Gerbil | null;\n model: string;\n loading: boolean;\n loadingMessage: string;\n thinkingMode: boolean;\n agentMode: boolean;\n voiceMode: boolean;\n downloadStatus: string;\n deviceMode: \"webgpu\" | \"cpu\";\n ttsModel: string;\n sttModel: string;\n};\n\nconst MENU_ITEMS = [\n {\n key: \"1\",\n view: \"chat\" as View,\n label: \"Chat\",\n desc: \"Talk with Gerbil\",\n color: \"cyan\",\n },\n {\n key: \"2\",\n view: \"skills\" as View,\n label: \"Skills\",\n desc: \"Run AI skills\",\n color: \"yellow\",\n },\n {\n key: \"3\",\n view: \"tools\" as View,\n label: \"Tools\",\n desc: \"Manage tools\",\n color: \"magenta\",\n },\n {\n key: \"4\",\n view: \"model\" as View,\n label: \"Model\",\n desc: \"Change model\",\n color: \"blue\",\n },\n {\n key: \"5\",\n view: \"frameworks\" as View,\n label: \"Integrate\",\n desc: \"Framework setup\",\n color: \"cyan\",\n },\n {\n key: \"6\",\n view: \"benchmark\" as View,\n label: \"Benchmark\",\n desc: \"Speed tests\",\n color: \"red\",\n },\n {\n key: \"7\",\n view: \"info\" as View,\n label: \"Info\",\n desc: \"System info\",\n color: \"white\",\n },\n {\n key: \"8\",\n view: \"serve\" as View,\n label: \"Serve\",\n desc: \"Start server\",\n color: \"green\",\n },\n];\n\nfunction openBrowser(url: string) {\n const cmd =\n process.platform === \"darwin\"\n ? `open \"${url}\"`\n : process.platform === \"win32\"\n ? `start \"${url}\"`\n : `xdg-open \"${url}\"`;\n exec(cmd);\n}\n\nconst WELCOME_PROMPT =\n \"You are Gerbil, a helpful AI assistant. Write a warm, friendly greeting in 1 short sentence (under 12 words). Be natural and welcoming. No markdown, no emojis, no XML tags, no asterisks, no quotes, no punctuation at the end.\";\n\n// Context-aware view prompts - can include dynamic state\ntype ViewContext = {\n model?: string;\n deviceMode?: \"cpu\" | \"webgpu\";\n thinkingMode?: boolean;\n agentMode?: boolean;\n cachedModelsCount?: number;\n modelTab?: \"preset\" | \"cached\" | \"huggingface\" | \"voice\";\n hfSearchQuery?: string;\n benchmarkStatus?: \"idle\" | \"running\" | \"done\";\n benchmarkStats?: BenchmarkStats;\n};\n\nfunction getViewPrompt(view: View, ctx: ViewContext = {}): string {\n // System instruction to prevent conversational responses\n const system =\n \"Complete the sentence. Do NOT start with 'Sure', 'I', 'Here', 'Of course', or any greeting. Just continue the sentence naturally. Max 20 words.\";\n\n switch (view) {\n case \"chat\":\n return `${system} \"Ready to chat about...\"`;\n case \"skills\":\n return `${system} \"Skills let you...\"`;\n case \"model\": {\n if (ctx.modelTab === \"preset\") {\n return `${system} \"These tested models...\"`;\n }\n if (ctx.modelTab === \"cached\") {\n const count = ctx.cachedModelsCount || 0;\n return `${system} \"${\n count > 0 ? `Your ${count} downloaded models` : \"Downloaded models\"\n }...\"`;\n }\n // HuggingFace tab - mention search if active\n if (ctx.hfSearchQuery) {\n return `${system} \"Searching '${ctx.hfSearchQuery}' to find...\"`;\n }\n return `${system} \"Search HuggingFace to...\"`;\n }\n case \"create-skill\":\n return `${system} \"Create a skill to...\"`;\n case \"code\":\n return `${system} \"Describe what code you need and...\"`;\n case \"benchmark\": {\n const stats = ctx.benchmarkStats;\n if (!stats || stats.runCount === 0) {\n return `${system} \"Run a benchmark to...\"`;\n }\n\n const last = stats.lastResult;\n const bests = stats.bests;\n const avgs = stats.averages;\n\n // Build a rich context for the AI\n let context = `Benchmark results: ${stats.runCount} total runs. `;\n\n if (last) {\n context += `Last run: ${last.tokensPerSec} tok/s, ${last.firstTokenMs}ms first token on ${last.device}. `;\n }\n\n if (bests) {\n context += `Records: fastest ${bests.fastestRun.tokensPerSec} tok/s (${bests.fastestRun.model}/${bests.fastestRun.device}), quickest first token ${bests.fastestFirstToken.firstTokenMs}ms. `;\n }\n\n if (avgs.length > 0) {\n const avgStrs = avgs.map(\n (a) => `${a.model} on ${a.device}: avg ${a.avgTokPerSec} tok/s over ${a.runs} runs`,\n );\n context += `Averages: ${avgStrs.join(\"; \")}. `;\n }\n\n // Compare last to best\n if (last && bests && last.tokensPerSec === bests.fastestRun.tokensPerSec) {\n context += \"This last run set a NEW RECORD! \";\n } else if (last && bests) {\n const diff = bests.fastestRun.tokensPerSec - last.tokensPerSec;\n if (diff > 0) {\n context += `Last run was ${diff} tok/s below the record. `;\n }\n }\n\n return `Based on this benchmark data, give an encouraging and insightful comment. Be specific about the numbers. ${context} Output ONLY the comment, 1-2 sentences. No greetings.`;\n }\n case \"info\":\n return `${system} \"System info shows...\"`;\n case \"serve\":\n return `${system} \"Start a server to...\"`;\n case \"frameworks\":\n return `${system} \"Add Gerbil to your app with...\"`;\n default:\n return \"\";\n }\n}\n\n// Keep static version for backwards compatibility\nconst _VIEW_PROMPTS: Record<View, string> = {\n menu: \"\",\n chat: getViewPrompt(\"chat\"),\n skills: getViewPrompt(\"skills\"),\n tools: \"\",\n \"create-skill\": getViewPrompt(\"create-skill\"),\n \"create-tool\": \"\",\n code: getViewPrompt(\"code\"),\n model: \"\",\n frameworks: \"\",\n benchmark: \"\",\n info: \"\",\n serve: \"\",\n};\n\nexport type DeviceMode = \"cpu\" | \"webgpu\";\n\nexport type AppProps = {\n /** Initial view to show after loading (default: \"menu\") */\n initialView?: View;\n /** Force a specific device mode (cpu or webgpu) */\n forcedDevice?: DeviceMode;\n};\n\nexport function App({ initialView = \"menu\", forcedDevice }: AppProps = {}) {\n const { exit } = useApp();\n const gerbilRef = useRef<Gerbil | null>(null); // Ref for cleanup\n // Determine initial device mode\n const initialDevice = forcedDevice ?? (process.env.GERBIL_CPU === \"1\" ? \"cpu\" : \"webgpu\");\n\n const [state, setState] = useState<AppState>({\n view: \"menu\", // Always start at menu during loading\n gerbil: null,\n model: \"qwen3-0.6b\",\n loading: true,\n loadingMessage: \"Loading model...\",\n thinkingMode: false,\n agentMode: true, // On by default - using proper Qwen tool format\n voiceMode: false,\n downloadStatus: \"\",\n deviceMode: initialDevice,\n ttsModel: \"kokoro-82m\",\n sttModel: \"whisper-tiny.en\",\n });\n const [pendingView, setPendingView] = useState<View | null>(\n initialView !== \"menu\" ? initialView : null,\n );\n const [selectedIndex, setSelectedIndex] = useState(0);\n const [prevSelectedIndex, setPrevSelectedIndex] = useState(0);\n const [selectionPulse, setSelectionPulse] = useState(false);\n const [viewTransition, setViewTransition] = useState<\"in\" | \"out\" | null>(null);\n const [welcomeMessage, setWelcomeMessage] = useState(\"\");\n const [viewTip, setViewTip] = useState(\"\");\n const [generatingTip, setGeneratingTip] = useState(false);\n const generatingTipRef = useRef(false); // Ref to check without causing re-renders\n const [ctrlCPressed, setCtrlCPressed] = useState(false);\n const [modelTab, setModelTab] = useState<\"preset\" | \"cached\" | \"huggingface\" | \"voice\">(\"preset\");\n const [hfSearchQuery, setHfSearchQuery] = useState(\"\");\n const [benchmarkStatus, setBenchmarkStatus] = useState<\"idle\" | \"running\" | \"done\">(\"idle\");\n const [benchmarkStatsState, setBenchmarkStatsState] = useState<BenchmarkStats | undefined>();\n\n // Konami code easter egg\n const [konamiSequence, setKonamiSequence] = useState<string[]>([]);\n const [konamiActivated, setKonamiActivated] = useState(false);\n const [konamiMessage, setKonamiMessage] = useState(\"\");\n const KONAMI_CODE = [\"up\", \"up\", \"down\", \"down\", \"left\", \"right\", \"left\", \"right\", \"b\", \"a\"];\n\n const [sessionStats, setSessionStats] = useState<SessionStats>({\n prompts: 0,\n tokensIn: 0,\n tokensOut: 0,\n totalTimeMs: 0,\n startTime: Date.now(),\n benchResults: [],\n lastTokPerSec: 0,\n avgTokPerSec: 0,\n });\n\n // Benchmark comparison state (persists across model switches)\n const [benchmarkResults, setBenchmarkResults] = useState<BenchmarkResult[]>([]);\n const [benchmarkModels, setBenchmarkModels] = useState<string[]>([]);\n const [returnToBenchmark, setReturnToBenchmark] = useState(false);\n\n // Update state\n const [updateCheck, setUpdateCheck] = useState<UpdateCheckResult | null>(null);\n const [isUpdating, setIsUpdating] = useState(false);\n const [updateSuccess, setUpdateSuccess] = useState(false);\n\n // Animation: pulse effect when selection changes\n useEffect(() => {\n if (selectedIndex !== prevSelectedIndex) {\n setPrevSelectedIndex(selectedIndex);\n setSelectionPulse(true);\n const timer = setTimeout(() => setSelectionPulse(false), 150);\n return () => clearTimeout(timer);\n }\n }, [selectedIndex, prevSelectedIndex]);\n\n // Animation: view transition\n const transitionToView = (newView: View) => {\n setViewTransition(\"out\");\n setTimeout(() => {\n setState((s) => ({ ...s, view: newView }));\n setViewTransition(\"in\");\n setTimeout(() => setViewTransition(null), 200);\n }, 100);\n };\n\n // Generate welcome message (stable ref - only depends on setter)\n const generateWelcome = useCallback(async (g: Gerbil) => {\n try {\n setWelcomeMessage(\"\");\n for await (const chunk of g.stream(WELCOME_PROMPT, { maxTokens: 20 })) {\n setWelcomeMessage((m) => m + chunk);\n }\n } catch {\n // Ignore welcome generation errors\n setWelcomeMessage(\"Ready to help!\");\n }\n }, []);\n\n // Track if we've done the initial model load\n const hasLoadedInitialModelRef = useRef(false);\n\n // Load model on start - only runs ONCE on mount\n // Model switching is handled by handleModelSelect, not this effect\n useEffect(() => {\n // Skip if we've already loaded (prevents double-load on dependency changes)\n if (hasLoadedInitialModelRef.current) {\n return;\n }\n hasLoadedInitialModelRef.current = true;\n\n let mounted = true; // Guard against strict mode double-render\n\n const loadModel = async () => {\n const g = new Gerbil();\n try {\n // Use the device mode from state (set from CLI flag or env var)\n const device = state.deviceMode;\n\n await g.loadModel(state.model, {\n device,\n onProgress: (p) => {\n if (!mounted) {\n return;\n }\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n loadingMessage: p.file ? `${p.file} ${p.progress || 0}%` : p.status,\n }));\n },\n });\n\n if (!mounted) {\n // Component unmounted during load - clean up\n g.dispose();\n return;\n }\n\n const modelInfo = g.getModelInfo();\n const supportsThinking = modelInfo?.supportsThinking ?? false;\n\n // Set tool context so tools can use gerbil.generate()\n setToolContext({\n generate: async (prompt: string) => {\n const result = await g.generate(prompt, { maxTokens: 200 });\n return result.text;\n },\n });\n\n gerbilRef.current = g; // Store in ref for cleanup\n setState((s) => ({\n ...s,\n gerbil: g,\n loading: false,\n thinkingMode: supportsThinking,\n // If we have a pending view, go directly there\n view: pendingView || \"menu\",\n }));\n\n // Clear pending view\n if (pendingView) {\n setPendingView(null);\n }\n\n // Generate welcome in background\n // For WebGPU, add a delay to let Chrome GPU stabilize\n const isWebGPU = g.getDeviceMode() === \"webgpu\";\n if (isWebGPU) {\n setTimeout(() => generateWelcome(g).catch(() => {}), 1500);\n } else {\n generateWelcome(g).catch(() => {});\n }\n } catch (error) {\n if (!mounted) {\n return;\n }\n setState((s) => ({\n ...s,\n loading: false,\n loadingMessage: `Error: ${error}`,\n }));\n }\n };\n loadModel();\n\n return () => {\n mounted = false;\n // Only dispose if we actually have a gerbil instance\n if (gerbilRef.current) {\n // Set cleanup promise so index.tsx can await it before process.exit\n import(\"./index.js\").then(({ setCleanupPromise }) => {\n const cleanupPromise = gerbilRef.current?.dispose(true) ?? Promise.resolve();\n setCleanupPromise(cleanupPromise);\n });\n gerbilRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []); // Empty deps - only run on mount, model switching handled by handleModelSelect\n\n // Check for updates on mount (non-blocking)\n useEffect(() => {\n const performUpdateCheck = async () => {\n const result = await checkForUpdate();\n if (result.updateAvailable) {\n setUpdateCheck(result);\n }\n };\n\n // Run in background, don't block\n performUpdateCheck().catch(() => {\n // Silent fail - don't interrupt user experience\n });\n }, []);\n\n // Handle update installation\n const handleUpdate = async () => {\n setIsUpdating(true);\n const result = await installUpdate();\n setIsUpdating(false);\n\n if (result.success) {\n setUpdateSuccess(true);\n setUpdateCheck(null);\n // Clear success message after 10s\n setTimeout(() => setUpdateSuccess(false), 10_000);\n } else {\n // Show error briefly\n setUpdateCheck({\n ...updateCheck!,\n error: result.error,\n });\n setTimeout(() => {\n if (updateCheck) {\n setUpdateCheck({ ...updateCheck, error: undefined });\n }\n }, 5000);\n }\n };\n\n // Generate view tip when changing views - with context awareness\n useEffect(() => {\n if (!state.gerbil || state.view === \"menu\") {\n return;\n }\n\n // Skip regenerating tip while benchmark is running - this would cause concurrent\n // generation which crashes transformers.js (tensor disposed error)\n if (state.view === \"benchmark\" && benchmarkStatus === \"running\") {\n return;\n }\n\n // Get cached models count for model view\n const cacheInfo = state.view === \"model\" ? getCacheInfo() : null;\n\n // Build context for the prompt\n const ctx: ViewContext = {\n model: state.model,\n deviceMode: state.deviceMode,\n thinkingMode: state.thinkingMode,\n agentMode: state.agentMode,\n modelTab: state.view === \"model\" ? modelTab : undefined,\n hfSearchQuery:\n state.view === \"model\" && modelTab === \"huggingface\" ? hfSearchQuery : undefined,\n cachedModelsCount: cacheInfo?.models.length,\n benchmarkStatus: state.view === \"benchmark\" ? benchmarkStatus : undefined,\n benchmarkStats: state.view === \"benchmark\" ? benchmarkStatsState : undefined,\n };\n\n // Get context-aware prompt\n const prompt = getViewPrompt(state.view, ctx);\n if (!prompt) {\n return;\n }\n\n // Use a ref to track if we should abort (view changed during generation)\n let cancelled = false;\n\n const generateTip = async () => {\n // Don't start if already generating - prevents concurrent generation crash\n if (generatingTipRef.current) {\n return;\n }\n\n setViewTip(\"\");\n setGeneratingTip(true);\n generatingTipRef.current = true;\n try {\n const stream = state.gerbil?.stream(prompt, { maxTokens: 50 });\n if (stream) {\n for await (const chunk of stream) {\n if (cancelled) {\n break;\n }\n setViewTip((t) => t + chunk);\n }\n }\n } catch {\n // Ignore tip generation errors (including interrupts)\n }\n generatingTipRef.current = false;\n if (!cancelled) {\n setGeneratingTip(false);\n }\n };\n generateTip();\n\n // Cleanup: mark as cancelled if effect re-runs before completion\n return () => {\n cancelled = true;\n };\n }, [\n state.view,\n state.gerbil,\n state.model,\n state.deviceMode,\n state.thinkingMode,\n state.agentMode,\n modelTab,\n hfSearchQuery,\n benchmarkStatus,\n benchmarkStatsState,\n ]);\n\n // Reset Ctrl+C warning after a delay\n useEffect(() => {\n if (ctrlCPressed) {\n const timer = setTimeout(() => setCtrlCPressed(false), 3000);\n return () => clearTimeout(timer);\n }\n }, [ctrlCPressed]);\n\n // Reload with a specific device mode (for menu 'm' key and benchmark)\n const handleDeviceSwitch = async (newDevice: \"webgpu\" | \"cpu\") => {\n if (newDevice === state.deviceMode) {\n return;\n }\n\n setState((s) => ({\n ...s,\n deviceMode: newDevice,\n loading: true,\n loadingMessage: `Switching to ${newDevice.toUpperCase()}...`,\n }));\n\n // Dispose old gerbil - must await to prevent zombie pages\n if (gerbilRef.current) {\n await gerbilRef.current.dispose();\n gerbilRef.current = null;\n }\n\n const g = new Gerbil();\n try {\n await g.loadModel(state.model, {\n device: newDevice,\n onProgress: (p) => {\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n loadingMessage: p.file ? `${p.file} ${p.progress || 0}%` : p.status,\n }));\n },\n });\n const modelInfo = g.getModelInfo();\n const supportsThinking = modelInfo?.supportsThinking ?? false;\n\n setToolContext({\n generate: async (prompt: string) => {\n const result = await g.generate(prompt, { maxTokens: 200 });\n return result.text;\n },\n });\n\n gerbilRef.current = g;\n setState((s) => ({\n ...s,\n gerbil: g,\n loading: false,\n thinkingMode: supportsThinking,\n }));\n } catch (error) {\n setState((s) => ({\n ...s,\n loading: false,\n loadingMessage: `Error: ${error}`,\n }));\n }\n };\n\n // Global key handlers\n useInput((input, key) => {\n // Ctrl+C handling\n if (key.ctrl && input === \"c\") {\n if (ctrlCPressed) {\n // Exit immediately - cleanup happens in useEffect unmount\n exit();\n } else {\n setCtrlCPressed(true);\n }\n return;\n }\n\n if (ctrlCPressed) {\n setCtrlCPressed(false);\n }\n\n // Konami code detection (only on menu)\n if (state.view === \"menu\" && !konamiActivated) {\n let keyPressed = \"\";\n if (key.upArrow) {\n keyPressed = \"up\";\n } else if (key.downArrow) {\n keyPressed = \"down\";\n } else if (key.leftArrow) {\n keyPressed = \"left\";\n } else if (key.rightArrow) {\n keyPressed = \"right\";\n } else if (input === \"b\" || input === \"B\") {\n keyPressed = \"b\";\n } else if (input === \"a\" || input === \"A\") {\n keyPressed = \"a\";\n }\n\n if (keyPressed) {\n const newSequence = [...konamiSequence, keyPressed].slice(-10);\n setKonamiSequence(newSequence);\n\n if (newSequence.length === 10 && newSequence.every((k, i) => k === KONAMI_CODE[i])) {\n setKonamiActivated(true);\n setKonamiSequence([]);\n\n if (state.gerbil) {\n setKonamiMessage(\"*~* KONAMI CODE ACTIVATED *~*\");\n state.gerbil\n .generate(\n \"You just discovered a secret easter egg! Generate a single fun, quirky one-liner message (max 15 words) celebrating the user's achievement. Be playful and geeky. No quotes.\",\n { maxTokens: 50, temperature: 0.9 },\n )\n .then((result) => {\n const msg = result.text.replace(/<\\/?think>/g, \"\").trim();\n setKonamiMessage(`*~* ${msg} *~*`);\n setTimeout(() => {\n setKonamiActivated(false);\n setKonamiMessage(\"\");\n }, 5000);\n })\n .catch(() => {\n setKonamiMessage(\"*~* +30 LIVES! Just kidding, this is an LLM. *~*\");\n setTimeout(() => {\n setKonamiActivated(false);\n setKonamiMessage(\"\");\n }, 5000);\n });\n }\n }\n }\n }\n\n // Q to quit from menu\n if ((input === \"q\" || input === \"Q\") && state.view === \"menu\") {\n // Exit immediately - cleanup happens in useEffect unmount\n exit();\n return;\n }\n\n // Ctrl+Q to quit from anywhere\n if (input === \"q\" && key.ctrl) {\n // Exit immediately - cleanup happens in useEffect unmount\n exit();\n return;\n }\n\n // U to update (when update available)\n if ((input === \"u\" || input === \"U\") && updateCheck && !isUpdating) {\n handleUpdate();\n return;\n }\n\n // Menu navigation (grid: row 0 has 4 items, row 1 has 4 items)\n const ROW1_SIZE = 4;\n\n if (state.view === \"menu\") {\n if (key.leftArrow) {\n setSelectedIndex((i) => {\n if (i === 0) {\n return ROW1_SIZE - 1;\n }\n if (i === ROW1_SIZE) {\n return MENU_ITEMS.length - 1;\n }\n return i - 1;\n });\n }\n if (key.rightArrow) {\n setSelectedIndex((i) => {\n if (i === ROW1_SIZE - 1) {\n return 0;\n }\n if (i === MENU_ITEMS.length - 1) {\n return ROW1_SIZE;\n }\n return i + 1;\n });\n }\n if (key.upArrow) {\n setSelectedIndex((i) => {\n if (i < ROW1_SIZE) {\n const col = i;\n return Math.min(ROW1_SIZE + col, MENU_ITEMS.length - 1);\n }\n const col = i - ROW1_SIZE;\n return Math.min(col, ROW1_SIZE - 1);\n });\n }\n if (key.downArrow) {\n setSelectedIndex((i) => {\n if (i < ROW1_SIZE) {\n return Math.min(ROW1_SIZE + i, MENU_ITEMS.length - 1);\n }\n const col = i - ROW1_SIZE;\n return Math.min(col, ROW1_SIZE - 1);\n });\n }\n if (key.return) {\n transitionToView(MENU_ITEMS[selectedIndex].view);\n }\n // Number keys for quick select (0-9)\n if (/^[0-9]$/.test(input)) {\n const item = MENU_ITEMS.find((m) => m.key === input);\n if (item) {\n const idx = MENU_ITEMS.indexOf(item);\n setSelectedIndex(idx);\n transitionToView(item.view);\n }\n }\n // D for docs\n if (input === \"d\" || input === \"D\") {\n openBrowser(\"https://github.com/gethamster/gerbil#readme\");\n }\n // G for github\n if (input === \"g\" || input === \"G\") {\n openBrowser(\"https://github.com/gethamster/gerbil\");\n }\n } else {\n // Escape goes back to menu\n if (key.escape) {\n transitionToView(\"menu\");\n }\n }\n\n // Ctrl+T to toggle thinking mode (anywhere)\n if (input === \"t\" && key.ctrl) {\n const modelInfo = state.gerbil?.getModelInfo();\n if (modelInfo?.supportsThinking) {\n setState((s) => ({ ...s, thinkingMode: !s.thinkingMode }));\n }\n } else if ((input === \"t\" || input === \"T\") && state.view === \"menu\") {\n const modelInfo = state.gerbil?.getModelInfo();\n if (modelInfo?.supportsThinking) {\n setState((s) => ({ ...s, thinkingMode: !s.thinkingMode }));\n }\n }\n\n // Ctrl+A to toggle agent mode (anywhere)\n if (input === \"a\" && key.ctrl) {\n setState((s) => ({ ...s, agentMode: !s.agentMode }));\n } else if ((input === \"a\" || input === \"A\") && state.view === \"menu\") {\n setState((s) => ({ ...s, agentMode: !s.agentMode }));\n }\n\n // Ctrl+V to toggle voice mode (anywhere) - speaks responses aloud\n if (input === \"v\" && key.ctrl) {\n setState((s) => ({ ...s, voiceMode: !s.voiceMode }));\n } else if ((input === \"v\" || input === \"V\") && state.view === \"menu\") {\n setState((s) => ({ ...s, voiceMode: !s.voiceMode }));\n }\n\n // M to toggle device mode (menu only)\n if ((input === \"m\" || input === \"M\") && state.view === \"menu\" && !state.loading) {\n const newDevice = state.deviceMode === \"webgpu\" ? \"cpu\" : \"webgpu\";\n handleDeviceSwitch(newDevice);\n }\n });\n\n if (state.loading) {\n return <LoadingView message={state.loadingMessage} />;\n }\n\n // Menu view\n if (state.view === \"menu\") {\n return (\n <Box flexDirection=\"column\" paddingX={2} paddingY={1}>\n <Box alignItems=\"center\" flexDirection=\"column\">\n <Text bold color=\"cyan\">\n 🐹 Gerbil\n </Text>\n <Text dimColor>Local LLM inference for Node.js</Text>\n <Text dimColor>by {hyperlink(\"https://tryhamster.com\", \"tryhamster.com\")}</Text>\n </Box>\n\n {welcomeMessage ? (\n <Box alignItems=\"center\" justifyContent=\"center\" marginY={1}>\n <Text color=\"gray\" italic>\n {cleanResponse(welcomeMessage)}\n </Text>\n </Box>\n ) : null}\n\n <Box justifyContent=\"center\">\n <Text dimColor>model: </Text>\n <Text color=\"white\">{state.model}</Text>\n <Text dimColor> · </Text>\n <Text bold color={state.gerbil?.getDeviceMode() === \"webgpu\" ? \"green\" : \"yellow\"}>\n {state.gerbil?.getDeviceMode()?.toUpperCase() || \"CPU\"}\n </Text>\n {state.gerbil?.getModelInfo()?.supportsThinking ? (\n <>\n <Text dimColor> · thinking: </Text>\n <Text bold={state.thinkingMode} color={state.thinkingMode ? \"magenta\" : \"gray\"}>\n {state.thinkingMode ? \"on\" : \"off\"}\n </Text>\n </>\n ) : null}\n <Text dimColor> · agent: </Text>\n <Text bold={state.agentMode} color={state.agentMode ? \"green\" : \"gray\"}>\n {state.agentMode ? \"on\" : \"off\"}\n </Text>\n <Text dimColor> · voice: </Text>\n <Text bold={state.voiceMode} color={state.voiceMode ? \"cyan\" : \"gray\"}>\n {state.voiceMode ? \"on\" : \"off\"}\n </Text>\n {state.gerbil?.isTTSLoaded() && (\n <>\n <Text dimColor> · tts: </Text>\n <Text color=\"cyan\">{state.gerbil.getTTSModelInfo()?.id || \"kokoro\"}</Text>\n </>\n )}\n {state.gerbil?.isSTTLoaded() && (\n <>\n <Text dimColor> · stt: </Text>\n <Text color=\"magenta\">{state.gerbil.getSTTModelInfo()?.id || \"whisper\"}</Text>\n </>\n )}\n </Box>\n\n {konamiActivated && konamiMessage ? (\n <Box justifyContent=\"center\" marginY={1}>\n <Text backgroundColor=\"black\" bold color=\"magenta\">\n {konamiMessage}\n </Text>\n </Box>\n ) : null}\n\n <Box alignItems=\"center\" flexDirection=\"column\" marginY={1}>\n <Box justifyContent=\"center\">\n {MENU_ITEMS.slice(0, 4).map((item, idx) => (\n <MenuCard\n isTopRow={true}\n item={item}\n key={item.key}\n pulse={selectionPulse && idx === selectedIndex}\n selected={idx === selectedIndex}\n />\n ))}\n </Box>\n <Box justifyContent=\"center\">\n {MENU_ITEMS.slice(4).map((item, idx) => (\n <MenuCard\n isTopRow={false}\n item={item}\n key={item.key}\n pulse={selectionPulse && idx + 4 === selectedIndex}\n selected={idx + 4 === selectedIndex}\n />\n ))}\n </Box>\n </Box>\n\n <Box justifyContent=\"center\">\n <Text dimColor>arrows · enter · </Text>\n <Text color=\"yellow\">1-8</Text>\n {state.gerbil?.getModelInfo()?.supportsThinking && (\n <>\n <Text dimColor> · </Text>\n <Text color=\"magenta\">t</Text>\n <Text dimColor>hink</Text>\n </>\n )}\n <Text dimColor> · </Text>\n <Text color=\"green\">a</Text>\n <Text dimColor>gent · </Text>\n <Text color=\"cyan\">v</Text>\n <Text dimColor>oice · </Text>\n <Text color=\"cyan\">m</Text>\n <Text dimColor>ode · </Text>\n <Text color=\"blue\">d</Text>\n <Text dimColor>ocs · </Text>\n <Text color=\"blue\">g</Text>\n <Text dimColor>ithub · </Text>\n <Text color=\"red\">q</Text>\n <Text dimColor>uit</Text>\n </Box>\n\n {ctrlCPressed ? (\n <Box justifyContent=\"center\" marginTop={1}>\n <Text color=\"yellow\">Press Ctrl+C again to quit</Text>\n </Box>\n ) : null}\n </Box>\n );\n }\n\n // Handle model switching\n const handleModelSelect = async (model: string, deviceOverride?: \"webgpu\" | \"cpu\") => {\n const returnView = returnToBenchmark ? \"benchmark\" : \"menu\";\n const newDevice = deviceOverride ?? state.deviceMode;\n setState((s) => ({\n ...s,\n model,\n deviceMode: newDevice,\n view: returnView,\n loading: true,\n loadingMessage: \"Switching model...\",\n }));\n setReturnToBenchmark(false);\n\n // Dispose old gerbil using ref - must await to prevent zombie pages\n if (gerbilRef.current) {\n await gerbilRef.current.dispose();\n gerbilRef.current = null;\n }\n\n const g = new Gerbil();\n try {\n await g.loadModel(model, {\n device: newDevice,\n onProgress: (p) => {\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n loadingMessage: p.file ? `${p.file} ${p.progress || 0}%` : p.status,\n }));\n },\n });\n const modelInfo = g.getModelInfo();\n const supportsThinking = modelInfo?.supportsThinking ?? false;\n\n // Update tool context for new model\n setToolContext({\n generate: async (prompt: string) => {\n const result = await g.generate(prompt, { maxTokens: 200 });\n return result.text;\n },\n });\n\n gerbilRef.current = g; // Update ref\n setState((s) => ({\n ...s,\n gerbil: g,\n loading: false,\n thinkingMode: supportsThinking,\n }));\n generateWelcome(g);\n } catch (error) {\n setState((s) => ({\n ...s,\n loading: false,\n loadingMessage: `Error: ${error}`,\n }));\n }\n };\n\n const handleDownloadOnly = async (modelId: string, _hfId: string): Promise<boolean> => {\n setState((s) => ({ ...s, downloadStatus: `Downloading ${modelId}...` }));\n const tempGerbil = new Gerbil();\n try {\n await tempGerbil.loadModel(modelId, {\n onProgress: (p) => {\n if (p.status?.includes(\"Unable to determine content-length\")) {\n return;\n }\n setState((s) => ({\n ...s,\n downloadStatus: p.file ? `${p.file} ${p.progress || 0}%` : p.status || \"\",\n }));\n },\n });\n await tempGerbil.dispose();\n setState((s) => ({\n ...s,\n downloadStatus: `[done] Downloaded ${modelId}`,\n }));\n setTimeout(() => setState((s) => ({ ...s, downloadStatus: \"\" })), 2500);\n return true; // Success\n } catch (error) {\n setState((s) => ({ ...s, downloadStatus: `[error] ${error}` }));\n setTimeout(() => setState((s) => ({ ...s, downloadStatus: \"\" })), 3000);\n return false; // Failed\n }\n };\n\n // Active view with minimal header\n return (\n <Box flexDirection=\"column\" height=\"100%\">\n <Box\n borderBottom={false}\n borderColor=\"gray\"\n borderLeft={false}\n borderRight={false}\n borderStyle=\"single\"\n paddingX={1}\n >\n <Text bold color=\"cyan\">\n gerbil\n </Text>\n <Text color=\"gray\"> </Text>\n <Text dimColor>{CURRENT_VERSION}</Text>\n <Text color=\"gray\"> / </Text>\n <Text color=\"white\">\n {MENU_ITEMS.find((m) => m.view === state.view)?.label || state.view}\n </Text>\n {state.thinkingMode && (\n <>\n <Text color=\"gray\"> / </Text>\n <Text color=\"magenta\">thinking</Text>\n </>\n )}\n {state.agentMode && (\n <>\n <Text color=\"gray\"> / </Text>\n <Text color=\"green\">agent</Text>\n </>\n )}\n {state.downloadStatus && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"yellow\">{state.downloadStatus}</Text>\n </>\n )}\n {isUpdating && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"yellow\">⬇ Updating...</Text>\n </>\n )}\n {updateSuccess && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"green\">✅ Updated! Restart to use v{updateCheck?.latestVersion}</Text>\n </>\n )}\n {updateCheck && !isUpdating && !updateSuccess && (\n <>\n <Text color=\"gray\"> | </Text>\n <Text color=\"cyan\">Update v{updateCheck.latestVersion} → Press </Text>\n <Text bold color=\"cyan\">\n u\n </Text>\n <Text color=\"cyan\"> to update</Text>\n </>\n )}\n <Text color=\"gray\"> | </Text>\n <Text dimColor>esc back | ^C quit</Text>\n {ctrlCPressed && <Text color=\"yellow\"> (again to quit)</Text>}\n </Box>\n\n {viewTip && (\n <Box paddingX={2} paddingY={1}>\n <Text dimColor italic>\n {generatingTip ? `${cleanResponse(viewTip)}▋` : cleanResponse(viewTip)}\n </Text>\n </Box>\n )}\n\n <Box flexDirection=\"column\" flexGrow={1}>\n <ViewTransition transition={viewTransition}>\n {state.view === \"chat\" && state.gerbil && (\n <ChatView\n agentMode={state.agentMode}\n avgTokPerSec={sessionStats.avgTokPerSec}\n gerbil={state.gerbil}\n lastTokPerSec={sessionStats.lastTokPerSec}\n onCreateTool={() => transitionToView(\"create-tool\")}\n onGenerationComplete={(stats) => {\n setSessionStats((s) => {\n const newPrompts = s.prompts + 1;\n const newTotalTokens = s.tokensOut + stats.tokensOut;\n const newTotalTime = s.totalTimeMs + stats.timeMs;\n return {\n ...s,\n prompts: newPrompts,\n tokensOut: newTotalTokens,\n totalTimeMs: newTotalTime,\n lastTokPerSec: stats.tokPerSec,\n avgTokPerSec: newTotalTime > 0 ? (newTotalTokens / newTotalTime) * 1000 : 0,\n };\n });\n }}\n onToggleAgent={() => setState((s) => ({ ...s, agentMode: !s.agentMode }))}\n onToggleThinking={() => setState((s) => ({ ...s, thinkingMode: !s.thinkingMode }))}\n onToggleVoice={() => setState((s) => ({ ...s, voiceMode: !s.voiceMode }))}\n thinkingMode={state.thinkingMode}\n voiceMode={state.voiceMode}\n />\n )}\n {state.view === \"skills\" && state.gerbil && (\n <SkillsView\n gerbil={state.gerbil}\n onCreateNew={() => transitionToView(\"create-skill\")}\n onSwitchModel={async (modelId) => {\n // Switch to the selected model\n setState((s) => ({\n ...s,\n loading: true,\n loadingMessage: `Loading ${modelId}...`,\n }));\n try {\n await state.gerbil?.loadModel(modelId);\n setState((s) => ({ ...s, model: modelId, loading: false }));\n } catch (_err) {\n setState((s) => ({ ...s, loading: false }));\n }\n }}\n />\n )}\n {state.view === \"create-skill\" && state.gerbil && (\n <CreateSkillView gerbil={state.gerbil} onDone={() => transitionToView(\"menu\")} />\n )}\n {state.view === \"tools\" && (\n <ToolsView onCreateTool={() => transitionToView(\"create-tool\")} />\n )}\n {state.view === \"create-tool\" && state.gerbil && (\n <CreateToolView gerbil={state.gerbil} onDone={() => transitionToView(\"tools\")} />\n )}\n {state.view === \"code\" && state.gerbil && <CodeView gerbil={state.gerbil} />}\n {state.view === \"model\" && (\n <ModelView\n currentModel={state.model}\n currentSTTModel={state.sttModel}\n currentTTSModel={state.ttsModel}\n onDownloadOnly={handleDownloadOnly}\n onSearchChange={setHfSearchQuery}\n onSelect={handleModelSelect}\n onSelectTTS={async (modelId) => {\n if (state.gerbil) {\n await state.gerbil.loadTTS({ model: modelId });\n setState((s) => ({ ...s, ttsModel: modelId }));\n }\n }}\n onSelectSTT={async (modelId) => {\n if (state.gerbil) {\n await state.gerbil.loadSTT(modelId);\n setState((s) => ({ ...s, sttModel: modelId }));\n }\n }}\n onTabChange={setModelTab}\n />\n )}\n {state.view === \"frameworks\" && <FrameworksView />}\n {state.view === \"benchmark\" && state.gerbil && (\n <BenchmarkView\n benchmarkModels={benchmarkModels}\n benchmarkResults={benchmarkResults}\n disabled={generatingTip}\n gerbil={state.gerbil}\n model={state.model}\n onResult={(tokPerSec) => {\n setSessionStats((s) => ({\n ...s,\n benchResults: [...s.benchResults, { model: state.model, tokPerSec }],\n }));\n }}\n onStatusChange={(status, stats) => {\n setBenchmarkStatus(status);\n setBenchmarkStatsState(stats);\n }}\n onSwitchDevice={handleDeviceSwitch}\n onSwitchModel={() => {\n setReturnToBenchmark(true);\n transitionToView(\"model\");\n }}\n setBenchmarkModels={setBenchmarkModels}\n setBenchmarkResults={setBenchmarkResults}\n />\n )}\n {state.view === \"info\" && state.gerbil && (\n <InfoView\n gerbil={state.gerbil}\n model={state.model}\n modelFamily={state.gerbil.getModelInfo()?.family || \"other\"}\n onGoToCache={() => {\n // Go to Model view with Cached tab selected\n transitionToView(\"model\");\n // ModelView will need to handle starting on cached tab\n }}\n stats={sessionStats}\n />\n )}\n {state.view === \"serve\" && state.gerbil && (\n <ServeView gerbil={state.gerbil} model={state.model} />\n )}\n </ViewTransition>\n </Box>\n </Box>\n );\n}\n\nfunction ViewTransition({\n transition,\n children,\n}: {\n transition: \"in\" | \"out\" | null;\n children: React.ReactNode;\n}) {\n if (transition === \"out\") {\n return (\n <Box alignItems=\"center\" flexDirection=\"column\" height={3} justifyContent=\"center\">\n <Text dimColor>...</Text>\n </Box>\n );\n }\n return <>{children}</>;\n}\n\nfunction MenuCard({\n item,\n selected,\n pulse,\n}: {\n item: (typeof MENU_ITEMS)[0];\n selected: boolean;\n pulse: boolean;\n isTopRow?: boolean;\n}) {\n const borderStyle = selected ? (pulse ? \"round\" : \"double\") : \"single\";\n const pointer = selected ? (pulse ? \">\" : \"*\") : \" \";\n\n return (\n <Box\n alignItems=\"center\"\n borderColor={selected ? (item.color as any) : \"gray\"}\n borderStyle={borderStyle}\n flexDirection=\"column\"\n justifyContent=\"center\"\n paddingX={1}\n paddingY={1}\n width={20}\n >\n <Box justifyContent=\"center\">\n <Text bold={selected} color={selected ? (item.color as any) : \"gray\"}>\n {pointer}[{item.key}]\n </Text>\n <Text\n bold={selected}\n color={selected ? (item.color as any) : \"white\"}\n inverse={pulse && selected}\n >\n {\" \"}\n {item.label}\n </Text>\n </Box>\n <Text color={selected ? (item.color as any) : undefined} dimColor={!selected}>\n {item.desc}\n </Text>\n </Box>\n );\n}\n","import { render } from \"ink\";\nimport { App, type View } from \"./App.js\";\n\nexport type DeviceMode = \"cpu\" | \"webgpu\";\n\nexport type ReplOptions = {\n /** Initial view to show (default: \"menu\") */\n initialView?: View;\n /** Force a specific device mode (cpu or webgpu) */\n forcedDevice?: DeviceMode;\n};\n\n// Shared cleanup promise that App can set\nlet pendingCleanup: Promise<void> | null = null;\nexport function setCleanupPromise(promise: Promise<void>) {\n pendingCleanup = promise;\n}\n\nexport async function startRepl(options?: ReplOptions) {\n // Track SIGINT count for force-exit on repeated signals\n let sigintCount = 0;\n let sigintTimer: NodeJS.Timeout | null = null;\n\n const handleSignal = () => {\n sigintCount += 1;\n\n // Reset count after 2 seconds\n if (sigintTimer) {\n clearTimeout(sigintTimer);\n }\n sigintTimer = setTimeout(() => {\n sigintCount = 0;\n }, 2000);\n\n if (sigintCount >= 2) {\n // Force exit on second SIGINT (user really wants out)\n process.exit(0);\n }\n // First SIGINT is handled by ink's useInput - don't exit yet\n };\n\n process.on(\"SIGINT\", handleSignal);\n process.on(\"SIGTERM\", () => process.exit(0));\n\n try {\n const { waitUntilExit } = render(\n <App forcedDevice={options?.forcedDevice} initialView={options?.initialView} />,\n );\n await waitUntilExit();\n\n // Wait for cleanup to complete (set by App component)\n if (pendingCleanup) {\n try {\n await Promise.race([\n pendingCleanup,\n new Promise((r) => setTimeout(r, 500)), // Max 500ms wait\n ]);\n } catch {\n // Ignore cleanup errors\n }\n }\n\n // Additional delay for Chrome's native cleanup\n await new Promise((r) => setTimeout(r, 200));\n\n // Clean exit after ink finishes\n process.exit(0);\n } finally {\n process.off(\"SIGINT\", handleSignal);\n if (sigintTimer) {\n clearTimeout(sigintTimer);\n }\n }\n}\n","#!/usr/bin/env node\n\n/**\n * Gerbil CLI\n *\n * @example\n * ```bash\n * gerbil \"Write a haiku\"\n * gerbil generate \"Hello world\" --thinking\n * gerbil commit\n * gerbil chat\n * gerbil serve --mcp\n * gerbil models\n * gerbil info\n * ```\n */\n\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport ora from \"ora\";\nimport { version } from \"../../package.json\" with { type: \"json\" };\nimport { Gerbil } from \"../core/gerbil.js\";\nimport { BUILTIN_MODELS } from \"../core/models.js\";\nimport { startMCPServer } from \"../integrations/mcp.js\";\nimport * as skills from \"../skills/index.js\";\nimport { startRepl } from \"./repl/index.js\";\n\nconst program = new Command();\n\nprogram.name(\"gerbil\").description(\"🐹 Local LLM inference for Node.js\").version(version);\n\n// ============================================\n// Default command: generate\n// ============================================\n\nprogram\n .argument(\"[prompt...]\", \"Prompt to generate from (opens REPL if empty)\")\n .option(\"-m, --model <id>\", \"Model to use\", \"qwen3-0.6b\")\n .option(\"-n, --max-tokens <n>\", \"Max tokens\", \"256\")\n .option(\"-t, --temperature <t>\", \"Temperature\", \"0.7\")\n .option(\"-s, --system <text>\", \"System prompt\")\n .option(\"--thinking\", \"Enable thinking mode\")\n .option(\"--stream\", \"Stream output\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (promptParts, opts) => {\n // If no prompt provided, launch REPL\n if (promptParts.length === 0) {\n await startRepl({});\n return;\n }\n\n const prompt = promptParts.join(\" \");\n await runGenerate(prompt, opts);\n });\n\n// ============================================\n// Generate command\n// ============================================\n\nprogram\n .command(\"generate <prompt...>\")\n .alias(\"g\")\n .description(\"Generate text\")\n .option(\"-m, --model <id>\", \"Model to use\", \"qwen3-0.6b\")\n .option(\"-n, --max-tokens <n>\", \"Max tokens\", \"256\")\n .option(\"-t, --temperature <t>\", \"Temperature\", \"0.7\")\n .option(\"-s, --system <text>\", \"System prompt\")\n .option(\"--thinking\", \"Enable thinking mode\")\n .option(\"--stream\", \"Stream output\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (promptParts, opts) => {\n const prompt = promptParts.join(\" \");\n await runGenerate(prompt, opts);\n });\n\nasync function runGenerate(prompt: string, opts: any) {\n const g = new Gerbil();\n const spinner = ora(`Loading ${chalk.cyan(opts.model)}...`).start();\n\n try {\n await g.loadModel(opts.model, {\n onProgress: (p) => {\n spinner.text = p.progress ? `${p.status} (${p.progress}%)` : p.status;\n },\n });\n spinner.succeed(\"Model loaded\");\n\n if (opts.thinking) {\n }\n\n if (opts.stream) {\n process.stdout.write(chalk.green(\"Response: \"));\n for await (const chunk of g.stream(prompt, {\n maxTokens: Number.parseInt(opts.maxTokens, 10),\n temperature: Number.parseFloat(opts.temperature),\n system: opts.system,\n thinking: opts.thinking,\n })) {\n process.stdout.write(chunk);\n }\n } else {\n const genSpinner = ora(\"Generating...\").start();\n const result = await g.generate(prompt, {\n maxTokens: Number.parseInt(opts.maxTokens, 10),\n temperature: Number.parseFloat(opts.temperature),\n system: opts.system,\n thinking: opts.thinking,\n });\n genSpinner.stop();\n\n if (opts.json) {\n } else if (result.thinking) {\n }\n }\n\n await g.dispose();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n}\n\n// ============================================\n// REPL command (TUI)\n// ============================================\n\nprogram\n .command(\"repl\")\n .alias(\"r\")\n .description(\"Interactive TUI with chat, skills, and more\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ forcedDevice });\n });\n\n// ============================================\n// Chat command (opens REPL in chat view)\n// ============================================\n\nprogram\n .command(\"chat\")\n .alias(\"c\")\n .description(\"Interactive chat (opens REPL chat view)\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"chat\", forcedDevice });\n });\n\n// ============================================\n// REPL View Shortcuts (no args = open view)\n// ============================================\n\nprogram\n .command(\"skills\")\n .description(\"Open REPL skills view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"skills\", forcedDevice });\n });\n\nprogram\n .command(\"tools\")\n .description(\"Open REPL tools view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"tools\", forcedDevice });\n });\n\nprogram\n .command(\"model\")\n .description(\"Open REPL model view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"model\", forcedDevice });\n });\n\nprogram\n .command(\"integrate\")\n .description(\"Open REPL framework integration view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"frameworks\", forcedDevice });\n });\n\nprogram\n .command(\"benchmark\")\n .description(\"Open REPL benchmark view\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"benchmark\", forcedDevice });\n });\n\n// ============================================\n// Commit command\n// ============================================\n\nprogram\n .command(\"commit\")\n .description(\"Generate commit message from staged changes\")\n .option(\"--type <type>\", \"Commit style: conventional, simple, detailed\", \"conventional\")\n .option(\"--write\", \"Write message to .git/COMMIT_EDITMSG\")\n .action(async (opts) => {\n const spinner = ora(\"Generating commit message...\").start();\n\n try {\n const message = await skills.commit({ type: opts.type });\n spinner.stop();\n\n if (opts.write) {\n const fs = await import(\"node:fs\");\n fs.writeFileSync(\".git/COMMIT_EDITMSG\", message);\n }\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Summarize command\n// ============================================\n\nprogram\n .command(\"summarize <file>\")\n .description(\"Summarize a file\")\n .option(\"-l, --length <length>\", \"Length: short, medium, long\", \"medium\")\n .option(\"-f, --format <format>\", \"Format: paragraph, bullets\", \"paragraph\")\n .action(async (file, opts) => {\n const fs = await import(\"node:fs\");\n const content = fs.readFileSync(file, \"utf-8\");\n\n const spinner = ora(\"Summarizing...\").start();\n\n try {\n const _summary = await skills.summarize({\n content,\n length: opts.length,\n format: opts.format,\n });\n spinner.stop();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Explain command\n// ============================================\n\nprogram\n .command(\"explain <file>\")\n .description(\"Explain code\")\n .option(\"-l, --level <level>\", \"Level: beginner, intermediate, expert\", \"intermediate\")\n .action(async (file, opts) => {\n const fs = await import(\"node:fs\");\n const content = fs.readFileSync(file, \"utf-8\");\n\n const spinner = ora(\"Explaining...\").start();\n\n try {\n const _explanation = await skills.explain({ content, level: opts.level });\n spinner.stop();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Review command\n// ============================================\n\nprogram\n .command(\"review <file>\")\n .description(\"Review code\")\n .option(\"-f, --focus <areas>\", \"Focus areas (comma-separated)\", \"all\")\n .action(async (file, opts) => {\n const fs = await import(\"node:fs\");\n const content = fs.readFileSync(file, \"utf-8\");\n\n const spinner = ora(\"Reviewing...\").start();\n\n try {\n const focus = opts.focus === \"all\" ? [\"all\"] : opts.focus.split(\",\");\n const _reviewResult = await skills.review({\n code: content,\n focus: focus as any,\n });\n spinner.stop();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Speak command (TTS)\n// ============================================\n\nprogram\n .command(\"speak [text...]\")\n .description(\"Convert text to speech using local TTS\")\n .option(\"-v, --voice <voice>\", \"Voice ID (e.g., af_bella, am_adam)\", \"af_bella\")\n .option(\"-s, --speed <speed>\", \"Speech speed (0.5-2.0)\", \"1.0\")\n .option(\"-o, --output <file>\", \"Save to WAV file (use - for stdout)\")\n .option(\"-m, --model <model>\", \"TTS model (kokoro-82m, supertonic-66m)\", \"kokoro-82m\")\n .option(\"--list-voices\", \"List available voices\")\n .option(\"--list-models\", \"List available TTS models\")\n .action(async (textParts, opts) => {\n const g = new Gerbil();\n\n // List models\n if (opts.listModels) {\n console.log(chalk.cyan(\"\\nAvailable TTS models:\\n\"));\n const models = await g.listTTSModels();\n for (const model of models) {\n console.log(\n ` ${chalk.green(model.id.padEnd(18))} ${model.description} (${model.voiceCount} voices, ${model.sampleRate}Hz)`,\n );\n }\n console.log();\n return;\n }\n\n // List voices\n if (opts.listVoices) {\n console.log(chalk.cyan(\"\\nAvailable voices:\\n\"));\n const voices = g.listVoices();\n for (const voice of voices) {\n console.log(\n ` ${chalk.green(voice.id.padEnd(15))} ${voice.name} (${voice.gender}, ${voice.language})`,\n );\n }\n console.log();\n return;\n }\n\n // Get text from args or stdin\n let text = textParts.join(\" \").trim();\n\n // If no text provided, try reading from stdin (for piping)\n if (!text) {\n const fs = await import(\"node:fs\");\n try {\n // Check if stdin has data (non-TTY means piped input)\n if (!process.stdin.isTTY) {\n text = fs.readFileSync(0, \"utf-8\").trim(); // fd 0 = stdin\n }\n } catch {\n // No stdin data\n }\n }\n\n if (!text) {\n console.log(chalk.yellow(\"Usage: gerbil speak <text> [--voice <id>] [--output file.wav]\"));\n console.log(chalk.gray(\" gerbil speak --list-voices\"));\n console.log(chalk.gray(\" gerbil speak --list-models\"));\n console.log(chalk.gray(\" echo 'Hello' | gerbil speak -o output.wav\"));\n console.log(chalk.gray(\" gerbil speak 'Hello' -o - > output.wav # stdout\"));\n return;\n }\n\n // For stdout output, suppress spinner\n const isStdout = opts.output === \"-\";\n const spinner = isStdout ? null : ora(\"Loading TTS model...\").start();\n\n try {\n await g.loadTTS({\n model: opts.model,\n onProgress: (p) => {\n if (spinner) spinner.text = p.status;\n },\n });\n if (spinner) spinner.text = `Generating speech (${opts.voice})...`;\n\n const result = await g.speak(text, {\n voice: opts.voice,\n speed: Number.parseFloat(opts.speed),\n });\n\n if (spinner) spinner.succeed(`Generated ${result.duration.toFixed(1)}s audio`);\n\n if (opts.output === \"-\") {\n // Output raw WAV to stdout (for piping)\n const wavBuffer = audioToWav(result.audio, result.sampleRate);\n process.stdout.write(wavBuffer);\n } else if (opts.output) {\n // Save to WAV file\n const wavBuffer = audioToWav(result.audio, result.sampleRate);\n const fs = await import(\"node:fs\");\n fs.writeFileSync(opts.output, wavBuffer);\n console.log(chalk.green(`\\nSaved to: ${opts.output}`));\n console.log(chalk.gray(` Play with: afplay ${opts.output}`));\n } else {\n // Save to temp file and play with afplay (macOS)\n const os = await import(\"node:os\");\n const path = await import(\"node:path\");\n const fs = await import(\"node:fs\");\n\n const tempFile = path.join(os.tmpdir(), `gerbil-tts-${Date.now()}.wav`);\n const wavBuffer = audioToWav(result.audio, result.sampleRate);\n fs.writeFileSync(tempFile, wavBuffer);\n\n // Play audio (macOS: afplay, Linux: aplay, Windows: not supported yet)\n const { exec } = await import(\"node:child_process\");\n const platform = os.platform();\n\n if (platform === \"darwin\") {\n exec(`afplay \"${tempFile}\"`, (err) => {\n if (err) console.error(chalk.red(\"Error playing audio:\", err.message));\n fs.unlinkSync(tempFile); // Cleanup\n });\n } else if (platform === \"linux\") {\n exec(`aplay \"${tempFile}\"`, (err) => {\n if (err) console.error(chalk.red(\"Error playing audio:\", err.message));\n fs.unlinkSync(tempFile);\n });\n } else {\n console.log(chalk.yellow(`\\nAudio saved to: ${tempFile}`));\n console.log(chalk.gray(\"Playback not supported on this platform. Use --output to save.\"));\n }\n }\n\n await g.dispose();\n } catch (e: any) {\n if (spinner) spinner.fail(\"Error\");\n console.error(chalk.red(e.message));\n process.exit(1);\n }\n });\n\n// ============================================\n// Transcribe command (STT)\n// ============================================\n\n// ============================================\n// Voice command (STT → LLM → TTS)\n// ============================================\n\nprogram\n .command(\"voice [audio]\")\n .description(\"Voice conversation: transcribe audio, generate response, speak it back\")\n .option(\"-m, --model <model>\", \"LLM model to use\", \"qwen3-0.6b\")\n .option(\"-s, --stt-model <model>\", \"STT model ID\", \"whisper-tiny.en\")\n .option(\"-v, --voice <voice>\", \"TTS voice ID\", \"af_heart\")\n .option(\n \"--system <prompt>\",\n \"System prompt\",\n \"You are a helpful voice assistant. Keep responses brief and conversational.\",\n )\n .option(\"--thinking\", \"Enable thinking mode\")\n .action(async (audioFile, opts) => {\n if (!audioFile) {\n console.log(chalk.cyan(\"\\n🎙️ Gerbil Voice Chat\\n\"));\n console.log(chalk.yellow(\"Usage: gerbil voice <audio.wav> [options]\\n\"));\n console.log(\"Options:\");\n console.log(\" -m, --model <model> LLM model (default: qwen3-0.6b)\");\n console.log(\" -s, --stt-model <model> STT model (default: whisper-tiny.en)\");\n console.log(\" -v, --voice <voice> TTS voice (default: af_heart)\");\n console.log(\" --system <prompt> System prompt\");\n console.log(\" --thinking Enable thinking mode\");\n console.log(\"\\nExample:\");\n console.log(chalk.gray(\" gerbil voice question.wav --voice bf_emma\"));\n console.log(chalk.gray(' gerbil voice \"what time is it.wav\" --model qwen3-1.7b\\n'));\n return;\n }\n\n const g = new Gerbil();\n const spinner = ora(\"Initializing...\").start();\n\n try {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n const os = await import(\"node:os\");\n const { exec } = await import(\"node:child_process\");\n\n // Read audio file\n const filePath = path.resolve(audioFile);\n if (!fs.existsSync(filePath)) {\n spinner.fail(`File not found: ${filePath}`);\n process.exit(1);\n }\n\n const audioData = new Uint8Array(fs.readFileSync(filePath));\n\n // Step 1: Transcribe\n spinner.text = \"Loading STT model...\";\n await g.loadSTT(opts.sttModel, {\n onProgress: (p: any) => {\n spinner.text = p.status || \"Loading STT...\";\n },\n });\n\n spinner.text = \"Transcribing...\";\n const transcribeResult = await g.transcribe(audioData);\n const userText = transcribeResult.text.trim();\n\n spinner.succeed(`You said: \"${userText}\"`);\n\n if (!userText) {\n console.log(chalk.yellow(\"No speech detected in audio.\"));\n await g.dispose();\n return;\n }\n\n // Step 2: Generate response\n spinner.start(\"Loading LLM...\");\n await g.loadModel(opts.model, {\n onProgress: (p: any) => {\n spinner.text = p.status || \"Loading LLM...\";\n },\n });\n\n spinner.text = \"Thinking...\";\n let response = \"\";\n for await (const chunk of g.stream(userText, {\n system: opts.system,\n thinking: opts.thinking,\n })) {\n response += chunk;\n }\n\n spinner.succeed(\"Generated response\");\n console.log(chalk.cyan(\"\\nAssistant: \") + response.trim());\n\n // Step 3: Speak response\n spinner.start(\"Loading TTS...\");\n await g.loadTTS({\n onProgress: (p: any) => {\n spinner.text = p.status || \"Loading TTS...\";\n },\n });\n\n spinner.text = \"Speaking...\";\n const speakResult = await g.speak(response.trim(), {\n voice: opts.voice,\n speed: 1.0,\n });\n\n // Save and play audio\n const tempFile = path.join(os.tmpdir(), `gerbil-voice-${Date.now()}.wav`);\n const wavBuffer = audioToWav(speakResult.audio, speakResult.sampleRate);\n fs.writeFileSync(tempFile, wavBuffer);\n\n spinner.succeed(`Speaking (${speakResult.duration.toFixed(1)}s)`);\n\n // Play audio\n const platform = os.platform();\n if (platform === \"darwin\") {\n exec(`afplay \"${tempFile}\"`, () => fs.unlinkSync(tempFile));\n } else if (platform === \"linux\") {\n exec(`aplay \"${tempFile}\"`, () => fs.unlinkSync(tempFile));\n } else {\n console.log(chalk.gray(`Audio saved to: ${tempFile}`));\n }\n\n await g.dispose();\n } catch (e: any) {\n spinner.fail(\"Error\");\n console.error(chalk.red(e.message));\n process.exit(1);\n }\n });\n\n// ============================================\n// Transcribe command (STT)\n// ============================================\n\nprogram\n .command(\"transcribe [file]\")\n .description(\"Transcribe audio to text using local STT (Whisper)\")\n .option(\"-m, --model <model>\", \"STT model ID\", \"whisper-tiny.en\")\n .option(\"-l, --language <lang>\", \"Language hint (for multilingual models)\")\n .option(\"-t, --timestamps\", \"Show timestamps for each segment\")\n .option(\"-o, --output <file>\", \"Save transcription to file\")\n .option(\"--list-models\", \"List available STT models\")\n .action(async (file, opts) => {\n const g = new Gerbil();\n\n // List models\n if (opts.listModels) {\n console.log(chalk.cyan(\"\\n🎤 Available STT models:\\n\"));\n const models = await g.listSTTModels();\n for (const model of models) {\n const langs = model.multilingual ? \"multi\" : model.languages.join(\",\");\n console.log(\n ` ${chalk.green(model.id.padEnd(18))} ${model.size.padEnd(6)} ${model.description} (${langs})`,\n );\n }\n console.log();\n return;\n }\n\n // Require file input\n if (!file) {\n console.log(\n chalk.yellow(\"Usage: gerbil transcribe <audio.wav> [--model <id>] [--timestamps]\"),\n );\n console.log(chalk.gray(\" gerbil transcribe --list-models\"));\n return;\n }\n\n const spinner = ora(`Loading STT model (${opts.model})...`).start();\n\n try {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n\n // Read audio file\n const filePath = path.resolve(file);\n if (!fs.existsSync(filePath)) {\n spinner.fail(`File not found: ${filePath}`);\n process.exit(1);\n }\n\n const audioData = new Uint8Array(fs.readFileSync(filePath));\n spinner.text = `Transcribing ${path.basename(filePath)}...`;\n\n // Transcribe\n const result = await g.transcribe(audioData, {\n language: opts.language,\n timestamps: opts.timestamps,\n onProgress: (p) => {\n spinner.text = p.status;\n },\n });\n\n spinner.succeed(\n `Transcribed ${result.duration.toFixed(1)}s audio in ${result.totalTime.toFixed(0)}ms`,\n );\n\n // Output\n console.log();\n if (opts.timestamps && result.segments) {\n for (const seg of result.segments) {\n console.log(chalk.gray(`[${seg.start.toFixed(1)}s - ${seg.end.toFixed(1)}s]`), seg.text);\n }\n } else {\n console.log(result.text);\n }\n\n // Save to file\n if (opts.output) {\n let content = result.text;\n if (opts.timestamps && result.segments) {\n content = result.segments\n .map((seg) => `[${seg.start.toFixed(1)}s - ${seg.end.toFixed(1)}s] ${seg.text}`)\n .join(\"\\n\");\n }\n fs.writeFileSync(opts.output, content);\n console.log(chalk.green(`\\n💾 Saved to: ${opts.output}`));\n }\n\n await g.dispose();\n } catch (e: any) {\n spinner.fail(\"Error\");\n console.error(chalk.red(e.message));\n process.exit(1);\n }\n });\n\n/**\n * Convert Float32Array audio to WAV buffer\n */\nfunction audioToWav(audio: Float32Array, sampleRate: number): Buffer {\n const numChannels = 1;\n const bitsPerSample = 16;\n const byteRate = (sampleRate * numChannels * bitsPerSample) / 8;\n const blockAlign = (numChannels * bitsPerSample) / 8;\n const dataSize = audio.length * 2;\n const fileSize = 36 + dataSize;\n\n const buffer = Buffer.alloc(44 + dataSize);\n let offset = 0;\n\n buffer.write(\"RIFF\", offset);\n offset += 4;\n buffer.writeUInt32LE(fileSize, offset);\n offset += 4;\n buffer.write(\"WAVE\", offset);\n offset += 4;\n buffer.write(\"fmt \", offset);\n offset += 4;\n buffer.writeUInt32LE(16, offset);\n offset += 4;\n buffer.writeUInt16LE(1, offset);\n offset += 2;\n buffer.writeUInt16LE(numChannels, offset);\n offset += 2;\n buffer.writeUInt32LE(sampleRate, offset);\n offset += 4;\n buffer.writeUInt32LE(byteRate, offset);\n offset += 4;\n buffer.writeUInt16LE(blockAlign, offset);\n offset += 2;\n buffer.writeUInt16LE(bitsPerSample, offset);\n offset += 2;\n buffer.write(\"data\", offset);\n offset += 4;\n buffer.writeUInt32LE(dataSize, offset);\n offset += 4;\n\n for (let i = 0; i < audio.length; i++) {\n const sample = Math.max(-1, Math.min(1, audio[i]));\n const int16 = sample < 0 ? sample * 32768 : sample * 32767;\n buffer.writeInt16LE(Math.round(int16), offset);\n offset += 2;\n }\n\n return buffer;\n}\n\n// ============================================\n// Serve command\n// ============================================\n\nprogram\n .command(\"serve\")\n .description(\"Start Gerbil server (use --mcp or --http for CLI, otherwise opens REPL)\")\n .option(\"-m, --model <id>\", \"Model to use\", \"qwen3-0.6b\")\n .option(\"-p, --port <port>\", \"HTTP port\", \"3000\")\n .option(\"--mcp\", \"Start MCP server (stdio)\")\n .option(\"--http\", \"Start HTTP server (CLI mode)\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n // If no specific server flag, open REPL serve view\n if (!(opts.mcp || opts.http)) {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"serve\", forcedDevice });\n return;\n }\n if (opts.mcp) {\n await startMCPServer({ model: opts.model });\n } else {\n // HTTP server\n const { Gerbil } = await import(\"../core/gerbil.js\");\n const g = new Gerbil();\n\n const spinner = ora(\"Loading model...\").start();\n await g.loadModel(opts.model);\n spinner.succeed(\"Model loaded\");\n\n // Simple HTTP server\n const http = await import(\"node:http\");\n\n const server = http.createServer(async (req, res) => {\n if (req.method === \"POST\" && req.url === \"/generate\") {\n let body = \"\";\n req.on(\"data\", (chunk) => (body += chunk));\n req.on(\"end\", async () => {\n try {\n const { prompt, ...opts } = JSON.parse(body);\n const result = await g.generate(prompt, opts);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (e) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: String(e) }));\n }\n });\n } else if (req.method === \"GET\" && req.url === \"/info\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(g.getInfo()));\n } else {\n res.writeHead(404);\n res.end(\"Not found\");\n }\n });\n\n server.listen(Number.parseInt(opts.port, 10), () => {});\n }\n });\n\n// ============================================\n// Models command\n// ============================================\n\nprogram\n .command(\"models\")\n .description(\"List available models\")\n .option(\"--search <query>\", \"Search models\")\n .action(async (opts) => {\n let models = Object.values(BUILTIN_MODELS);\n\n if (opts.search) {\n const q = opts.search.toLowerCase();\n models = models.filter(\n (m) => m.id.toLowerCase().includes(q) || m.description.toLowerCase().includes(q),\n );\n }\n\n for (const m of models) {\n if (m.supportsThinking) {\n }\n }\n });\n\n// ============================================\n// Info command\n// ============================================\n\nprogram\n .command(\"info\")\n .description(\"Show system and model info (use --cli for text output, otherwise opens REPL)\")\n .option(\"-m, --model <id>\", \"Model to load and show info for\", \"qwen3-0.6b\")\n .option(\"--cli\", \"Output text info instead of opening REPL\")\n .option(\"--cpu\", \"Force CPU inference mode\")\n .option(\"--gpu\", \"Force GPU/WebGPU inference mode\")\n .action(async (opts) => {\n // If no --cli flag, open REPL info view\n if (!opts.cli) {\n const forcedDevice = opts.cpu ? \"cpu\" : opts.gpu ? \"webgpu\" : undefined;\n await startRepl({ initialView: \"info\", forcedDevice });\n return;\n }\n\n const g = new Gerbil();\n const spinner = ora(`Loading ${chalk.cyan(opts.model)}...`).start();\n\n try {\n await g.loadModel(opts.model, {\n onProgress: (p) => {\n spinner.text = p.progress ? `${p.status} (${p.progress}%)` : p.status;\n },\n });\n spinner.stop();\n\n const _info = g.getInfo();\n\n await g.dispose();\n } catch (_e) {\n spinner.fail(\"Error loading model\");\n }\n });\n\n// ============================================\n// Cache command\n// ============================================\n\nprogram\n .command(\"cache\")\n .description(\"Show or manage model cache\")\n .option(\"--clean\", \"Remove all cached models\")\n .option(\"--older-than <days>\", \"Remove models older than N days\")\n .action(async (opts) => {\n const fs = await import(\"node:fs\");\n const path = await import(\"node:path\");\n const os = await import(\"node:os\");\n\n // Check transformers.js cache first (where Gerbil actually stores models)\n const nodeModulesCache = path.join(\n process.cwd(),\n \"node_modules\",\n \"@huggingface\",\n \"transformers\",\n \".cache\",\n );\n const cacheDir = fs.existsSync(nodeModulesCache)\n ? nodeModulesCache\n : process.env.HF_HOME ||\n process.env.TRANSFORMERS_CACHE ||\n path.join(os.homedir(), \".cache\", \"huggingface\", \"hub\");\n\n try {\n if (!fs.existsSync(cacheDir)) {\n return;\n }\n\n const entries = fs.readdirSync(cacheDir);\n const models: {\n name: string;\n size: number;\n mtime: Date;\n path: string;\n }[] = [];\n\n // Helper to get folder size\n const getSize = (dir: string): number => {\n let total = 0;\n try {\n const items = fs.readdirSync(dir);\n for (const item of items) {\n const itemPath = path.join(dir, item);\n const itemStat = fs.statSync(itemPath);\n if (itemStat.isDirectory()) {\n total += getSize(itemPath);\n } else {\n total += itemStat.size;\n }\n }\n } catch {}\n return total;\n };\n\n for (const entry of entries) {\n const entryPath = path.join(cacheDir, entry);\n try {\n const entryStat = fs.statSync(entryPath);\n if (!entryStat.isDirectory()) {\n continue;\n }\n\n // Handle HuggingFace hub format (models--org--model)\n if (entry.startsWith(\"models--\")) {\n const modelName = entry.replace(\"models--\", \"\").replace(\"--\", \"/\");\n const size = getSize(entryPath);\n models.push({\n name: modelName,\n size,\n mtime: entryStat.mtime,\n path: entryPath,\n });\n } else {\n // Handle transformers.js format (org/model directories)\n const subEntries = fs.readdirSync(entryPath);\n for (const subEntry of subEntries) {\n const subPath = path.join(entryPath, subEntry);\n try {\n const subStat = fs.statSync(subPath);\n if (subStat.isDirectory()) {\n const modelName = `${entry}/${subEntry}`;\n const size = getSize(subPath);\n if (size > 0) {\n models.push({\n name: modelName,\n size,\n mtime: subStat.mtime,\n path: subPath,\n });\n }\n }\n } catch {}\n }\n }\n } catch {}\n }\n\n if (models.length === 0) {\n return;\n }\n\n // Handle clean options\n if (opts.clean || opts.olderThan) {\n const daysThreshold = opts.olderThan ? Number.parseInt(opts.olderThan, 10) : 0;\n const cutoffDate = new Date(Date.now() - daysThreshold * 24 * 60 * 60 * 1000);\n\n let removed = 0;\n let _removedSize = 0;\n\n for (const model of models) {\n if (opts.clean || model.mtime < cutoffDate) {\n try {\n fs.rmSync(model.path, { recursive: true, force: true });\n removed += 1;\n _removedSize += model.size;\n } catch (_e) {}\n }\n }\n\n if (removed > 0) {\n } else {\n }\n return;\n }\n let _totalSize = 0;\n\n for (const model of models.sort((a, b) => b.mtime.getTime() - a.mtime.getTime())) {\n const _timeAgo = formatTimeAgo(model.mtime);\n const _sizeStr = formatSize(model.size).padStart(8);\n _totalSize += model.size;\n }\n } catch (_e) {}\n });\n\n// Import shared utilities from repl/utils\nimport { formatBytes, formatTimeAgo } from \"./repl/utils.js\";\n\n// Alias for CLI usage\nconst formatSize = formatBytes;\n\n// ============================================\n// Cleanup command (kill zombie Chrome pages)\n// ============================================\n\nprogram\n .command(\"cleanup\")\n .description(\"Clean up zombie Chrome pages and free memory\")\n .option(\"--kill-browser\", \"Also kill the shared Chrome browser (forces restart on next use)\")\n .option(\"--force\", \"Force kill all Gerbil Chrome processes (use if --kill-browser fails)\")\n .action(async (opts) => {\n const { ChromeGPUBackend } = await import(\"../core/chrome-backend.js\");\n const { execSync } = await import(\"node:child_process\");\n\n console.log(chalk.cyan(\"\\nChecking Chrome backend status...\\n\"));\n\n // Check for orphan processes using ps (works even without WS connection)\n let orphanPids: number[] = [];\n try {\n const psOutput = execSync('ps aux | grep \"gerbil/chrome-cache\" | grep -v grep', {\n encoding: \"utf-8\",\n });\n const lines = psOutput.trim().split(\"\\n\").filter(Boolean);\n orphanPids = lines.map((line) => {\n const parts = line.trim().split(/\\s+/);\n return Number.parseInt(parts[1], 10);\n });\n } catch {\n // No orphan processes\n }\n\n if (opts.force && orphanPids.length > 0) {\n const spinner = ora(`Force killing ${orphanPids.length} Gerbil Chrome processes...`).start();\n try {\n execSync('pkill -f \"gerbil/chrome-cache\"', { stdio: \"ignore\" });\n spinner.succeed(`Force killed ${orphanPids.length} process(es)`);\n } catch {\n spinner.fail(\"Failed to kill processes\");\n }\n console.log();\n return;\n }\n\n const status = ChromeGPUBackend.getGlobalBrowserStatus();\n\n if (!status.running) {\n if (orphanPids.length > 0) {\n console.log(\n chalk.yellow(\n `Found ${orphanPids.length} orphan Chrome process(es) but no WS connection.`,\n ),\n );\n console.log(chalk.gray(\"Use --force to kill them: gerbil cleanup --force\"));\n } else {\n console.log(chalk.gray(\"No Chrome backend running.\"));\n }\n return;\n }\n\n console.log(`Chrome PID: ${chalk.yellow(status.pid)}`);\n console.log(`Server port: ${chalk.yellow(status.port)}`);\n console.log(`Active pages: ${chalk.yellow(status.activePagesCount)}/${status.maxPages}`);\n\n // Get page info\n const pageInfo = await ChromeGPUBackend.getAllChromePages();\n if (pageInfo.length > 0) {\n console.log(chalk.cyan(\"\\nGerbil pages:\"));\n for (let i = 0; i < pageInfo.length; i++) {\n const p = pageInfo[i];\n const memStr = p.memory ? `${p.memory.usedGB.toFixed(2)}GB` : \"?\";\n const owner = p.isOurs ? chalk.green(\"(this process)\") : chalk.yellow(\"(orphan)\");\n console.log(` [${i}] ${p.modelId || \"unknown\"} - ${memStr} ${owner}`);\n }\n }\n\n if (opts.killBrowser) {\n const spinner = ora(\"Killing Chrome browser...\").start();\n const result = await ChromeGPUBackend.killAllBackends();\n spinner.succeed(\n `Killed ${result.pagesKilled} page(s)${result.browserKilled ? \" and browser\" : \"\"}`,\n );\n } else {\n // Just clean up orphan pages\n const spinner = ora(\"Cleaning up orphan pages...\").start();\n let cleaned = 0;\n for (let i = pageInfo.length - 1; i >= 0; i--) {\n if (!pageInfo[i].isOurs) {\n const killed = await ChromeGPUBackend.killPageByIndex(i);\n if (killed) cleaned++;\n }\n }\n if (cleaned > 0) {\n spinner.succeed(`Cleaned up ${cleaned} orphan page(s)`);\n } else {\n spinner.info(\"No orphan pages to clean up\");\n }\n }\n\n console.log();\n });\n\n// ============================================\n// Bench command\n// ============================================\n\nprogram\n .command(\"bench\")\n .description(\"Benchmark model performance\")\n .option(\"-m, --model <id>\", \"Model to benchmark\", \"qwen3-0.6b\")\n .option(\"-n, --runs <n>\", \"Number of runs\", \"3\")\n .action(async (opts) => {\n const g = new Gerbil();\n const spinner = ora(`Loading ${chalk.cyan(opts.model)}...`).start();\n\n try {\n await g.loadModel(opts.model, {\n onProgress: (p) => {\n spinner.text = p.progress ? `${p.status} (${p.progress}%)` : p.status;\n },\n });\n spinner.succeed(\"Model loaded\");\n\n const runs = Number.parseInt(opts.runs, 10);\n const results: {\n tokPerSec: number;\n firstToken: number;\n tokens: number;\n }[] = [];\n const prompts = [\n \"Write a haiku about programming.\",\n \"Explain recursion briefly.\",\n \"List 3 benefits of TypeScript.\",\n ];\n\n for (let i = 0; i < runs; i += 1) {\n const prompt = prompts[i % prompts.length];\n const runSpinner = ora(`Run ${i + 1}/${runs}...`).start();\n\n const startTime = Date.now();\n let firstTokenTime = 0;\n let tokenCount = 0;\n\n for await (const _chunk of g.stream(prompt, { maxTokens: 100 })) {\n if (tokenCount === 0) {\n firstTokenTime = Date.now() - startTime;\n }\n tokenCount += 1;\n }\n\n const totalTime = Date.now() - startTime;\n const tokPerSec = Math.round((tokenCount / totalTime) * 1000);\n\n results.push({\n tokPerSec,\n firstToken: firstTokenTime,\n tokens: tokenCount,\n });\n runSpinner.succeed(\n `Run ${i + 1}: ${chalk.cyan(tokPerSec)} tok/s, ${chalk.yellow(\n `${firstTokenTime}ms`,\n )} first token`,\n );\n }\n\n // Calculate averages\n const _avgTokPerSec = Math.round(\n results.reduce((a, r) => a + r.tokPerSec, 0) / results.length,\n );\n const _avgFirstToken = Math.round(\n results.reduce((a, r) => a + r.firstToken, 0) / results.length,\n );\n\n await g.dispose();\n } catch (_e) {\n spinner.fail(\"Error\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Update command\n// ============================================\n\nprogram\n .command(\"update\")\n .description(\"Update Gerbil to the latest version\")\n .action(async () => {\n const { checkForUpdate, installUpdate, CURRENT_VERSION } = await import(\n \"./repl/auto-update.js\"\n );\n\n const spinner = ora(\"Checking for updates...\").start();\n\n try {\n const check = await checkForUpdate();\n\n if (!check.updateAvailable) {\n spinner.succeed(chalk.green(`Already on latest version (v${CURRENT_VERSION})`));\n return;\n }\n\n spinner.info(chalk.cyan(`Update available: v${CURRENT_VERSION} → v${check.latestVersion}`));\n\n const updateSpinner = ora(`Installing v${check.latestVersion}...`).start();\n const result = await installUpdate();\n\n if (result.success) {\n updateSpinner.succeed(\n chalk.green(`✅ Updated to v${result.version || check.latestVersion}`),\n );\n } else {\n updateSpinner.fail(chalk.red(\"Update failed\"));\n process.exit(1);\n }\n } catch (_e) {\n spinner.fail(\"Update check failed\");\n process.exit(1);\n }\n });\n\n// ============================================\n// Parse and run\n// ============================================\n\n// Check for updates after command completes (non-blocking)\nconst originalParse = program.parse.bind(program);\nprogram.parse = (...args) => {\n const result = originalParse(...args);\n\n // Check for updates in background (don't block)\n checkForUpdateCLI().catch(() => {\n // Silent fail\n });\n\n return result;\n};\n\nasync function checkForUpdateCLI() {\n const { checkForUpdate, CURRENT_VERSION } = await import(\"./repl/auto-update.js\");\n const check = await checkForUpdate();\n\n if (check.updateAvailable) {\n }\n}\n\nprogram.parse();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;cAEa;;;;ACCb,MAAM,YAAY,UAAU,KAAK;AAEjC,MAAM,kBAAkB;AACxB,MAAM,eAAe;;;;AAkBrB,eAAsB,iBAA6C;AACjE,KAAI;EAEF,MAAM,EAAE,WAAW,MAAM,UAAU,YAAY,aAAa,WAAW,EACrE,SAAS,KACV,CAAC;EACF,MAAM,gBAAgB,OAAO,MAAM;AAGnC,MAAI,gBAAgB,eAAe,gBAAgB,GAAG,EACpD,QAAO;GACL,iBAAiB;GACjB,gBAAgB;GAChB;GACD;AAGH,SAAO;GACL,iBAAiB;GACjB,gBAAgB;GAChB;GACD;UACM,OAAO;AAEd,SAAO;GACL,iBAAiB;GACjB,gBAAgB;GAChB,OAAO,wBAAwB;GAChC;;;;;;AAOL,eAAsB,gBAA8C;AAClE,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,UAAU,kBAAkB,aAAa,UAAU,EAC1E,SAAS,KACV,CAAC;EAGF,MAAM,eAAe,OAAO,MAAM,kCAAkC;AAGpE,SAAO;GACL,SAAS;GACT,SAJc,eAAe,aAAa,KAAK;GAKhD;UACM,OAAO;AACd,SAAO;GACL,SAAS;GACT,OAAO,6BAA6B;GACrC;;;;;;;AAQL,SAAgB,gBAAgB,GAAW,GAAmB;CAC5D,MAAM,SAAS,EAAE,MAAM,IAAI,CAAC,IAAI,OAAO;CACvC,MAAM,SAAS,EAAE,MAAM,IAAI,CAAC,IAAI,OAAO;AAEvC,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;EAC7B,MAAM,QAAQ,OAAO,MAAM;EAC3B,MAAM,QAAQ,OAAO,MAAM;AAC3B,MAAI,QAAQ,MACV,QAAO;AAET,MAAI,QAAQ,MACV,QAAO;;AAGX,QAAO;;;;;;;;;;;ACvFT,SAAgB,YAAY,OAAuB;AACjD,KAAI,CAAC,SAAS,UAAU,EACtB,QAAO;AAET,KAAI,SAAS,IACX,QAAO,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;AAErC,KAAI,SAAS,IACX,QAAO,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;AAErC,KAAI,SAAS,IACX,QAAO,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;AAErC,QAAO,GAAG,MAAM;;;;;AAMlB,SAAgB,cAAc,MAAoB;CAChD,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,SAAS,IAAI,IAAK;AAChE,KAAI,UAAU,GACZ,QAAO;AAET,KAAI,UAAU,KACZ,QAAO,GAAG,KAAK,MAAM,UAAU,GAAG,CAAC;AAErC,KAAI,UAAU,MACZ,QAAO,GAAG,KAAK,MAAM,UAAU,KAAK,CAAC;AAEvC,KAAI,UAAU,OACZ,QAAO,GAAG,KAAK,MAAM,UAAU,MAAO,CAAC;AAEzC,QAAO,GAAG,KAAK,MAAM,UAAU,OAAQ,CAAC;;;;;AAmB1C,SAAgB,qBAA6B;AAC3C,QAAO,KAAK,KAAK,QAAQ,KAAK,EAAE,WAAW,SAAS;;;;;AAMtD,SAAgB,0BAAkC;CAChD,MAAM,mBAAmB,KAAK,KAC5B,QAAQ,KAAK,EACb,gBACA,gBACA,gBACA,SACD;AACD,KAAI,GAAG,WAAW,iBAAiB,CACjC,QAAO;AAET,QACE,QAAQ,IAAI,WACZ,QAAQ,IAAI,sBACZ,KAAK,KAAK,GAAG,SAAS,EAAE,UAAU,eAAe,MAAM;;;;;AAc3D,SAAgB,cAAc,MAAuB;CACnD,MAAM,CAAC,KAAK,SAAS,KAAK,MAAM,IAAI;AACpC,KAAI,EAAE,OAAO,OACX,QAAO;AAIT,KAAI;AAEF,MADqB,uBAAuB,CAC3B,MAAM,MAAM,EAAE,YAAY,KAAK,CAC9C,QAAO;SAEH;CAKR,MAAM,YAAY,CAAC,oBAAoB,EAAE,yBAAyB,CAAC;AAEnE,MAAK,MAAM,YAAY,WAAW;EAChC,MAAM,gBAAgB;GACpB,KAAK,KAAK,UAAU,KAAK,MAAM;GAC/B,KAAK,KAAK,UAAU,KAAK,QAAQ,KAAK,KAAK,CAAC;GAC5C,KAAK,KAAK,UAAU,WAAW,IAAI,IAAI,QAAQ;GAChD;AAED,OAAK,MAAM,KAAK,cACd,KAAI;AACF,OAAI,GAAG,WAAW,EAAE,EAElB;QADa,GAAG,SAAS,EAAE,CAClB,aAAa,EAWpB;SAVc,GAAG,YAAY,GAAG,EAAE,WAAW,MAAM,CAAC,CACzB,MAAM,MAAW;MAC1C,MAAM,QAAQ,OAAO,EAAE,CAAC,aAAa;AACrC,aACE,MAAM,SAAS,QAAQ,IACvB,MAAM,SAAS,oBAAoB,IACnC,MAAM,SAAS,gBAAgB,IAC/B,MAAM,SAAS,gBAAgB;OAEjC,CAEA,QAAO;;;UAIP;;AAIZ,QAAO;;;;;AAgBT,SAAS,aAAa,UAAkB,QAAmC;CACzE,IAAI,aAAa;AAEjB,KAAI;AACF,MAAI,CAAC,GAAG,WAAW,SAAS,CAC1B,QAAO;EAGT,MAAM,UAAU,GAAG,YAAY,SAAS;AACxC,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,YAAY,KAAK,KAAK,UAAU,MAAM;GAC5C,MAAM,YAAY,GAAG,SAAS,UAAU;AAExC,OAAI,CAAC,UAAU,aAAa,CAC1B;GAGF,IAAI,YAAY;AAChB,OAAI,MAAM,WAAW,WAAW,CAC9B,aAAY,MAAM,QAAQ,YAAY,GAAG,CAAC,QAAQ,MAAM,IAAI;GAG9D,MAAM,aAAa,GAAG,YAAY,UAAU;AAC5C,QAAK,MAAM,YAAY,YAAY;IACjC,MAAM,UAAU,KAAK,KAAK,WAAW,SAAS;AAC9C,QAAI;KACF,MAAM,UAAU,GAAG,SAAS,QAAQ;AACpC,SAAI,QAAQ,aAAa,EAAE;MACzB,MAAM,gBAAgB,GAAG,MAAM,GAAG;MAClC,IAAI,YAAY;MAChB,IAAI,gBAAgB;AACpB,UAAI;OACF,MAAM,QAAQ,GAAG,YAAY,SAAS,EAAE,WAAW,MAAM,CAAC;AAC1D,YAAK,MAAM,QAAQ,MACjB,KAAI;QACF,MAAM,WAAW,KAAK,KAAK,SAAS,OAAO,KAAK,CAAC;QACjD,MAAM,WAAW,GAAG,SAAS,SAAS;AACtC,YAAI,SAAS,QAAQ,EAAE;AACrB,sBAAa,SAAS;SACtB,MAAM,QAAQ,OAAO,KAAK,CAAC,aAAa;AACxC,aAAI,MAAM,SAAS,QAAQ,IAAI,MAAM,SAAS,eAAe,CAC3D,kBAAiB,SAAS;;eAGxB;cAEJ;AACN,mBAAY,QAAQ;;AAGtB,UAAI,YAAY,GAAG;AACjB,qBAAc;OACd,MAAM,WAAW,cAAc,QAAQ,MAAM;AAC7C,cAAO,KAAK;QACV,MAAM;QACN,WAAW,YAAY,iBAAiB,UAAU;QAClD,WAAW,YAAY,UAAU;QACjC;QACA,UAAU;QACX,CAAC;;;YAGA;;AAIV,OADsB,WAAW,MAAM,MAAM,EAAE,SAAS,QAAQ,IAAI,EAAE,SAAS,QAAQ,CAAC,EACrE;IACjB,IAAI,YAAY;IAChB,IAAI,gBAAgB;AACpB,QAAI;KACF,MAAM,QAAQ,GAAG,YAAY,WAAW,EAAE,WAAW,MAAM,CAAC;AAC5D,UAAK,MAAM,QAAQ,MACjB,KAAI;MACF,MAAM,WAAW,KAAK,KAAK,WAAW,OAAO,KAAK,CAAC;MACnD,MAAM,WAAW,GAAG,SAAS,SAAS;AACtC,UAAI,SAAS,QAAQ,EAAE;AACrB,oBAAa,SAAS;OACtB,MAAM,QAAQ,OAAO,KAAK,CAAC,aAAa;AACxC,WAAI,MAAM,SAAS,QAAQ,IAAI,MAAM,SAAS,eAAe,CAC3D,kBAAiB,SAAS;;aAGxB;YAEJ;AACN,iBAAY,UAAU;;AAGxB,kBAAc;IACd,MAAM,WAAW,cAAc,UAAU,MAAM;AAC/C,WAAO,KAAK;KACV,MAAM;KACN,WAAW,YAAY,iBAAiB,UAAU;KAClD,WAAW,YAAY,UAAU;KACjC;KACA,UAAU;KACX,CAAC;;;SAGA;AAER,QAAO;;;;;AAMT,SAAgB,eAId;CACA,MAAM,YAAY,oBAAoB;CACtC,MAAM,kBAAkB,yBAAyB;CACjD,MAAMA,SAA4B,EAAE;CAEpC,IAAI,aAAa;AACjB,eAAc,aAAa,WAAW,OAAO;AAC7C,eAAc,aAAa,iBAAiB,OAAO;CAGnD,IAAI,kBAAkB;AACtB,KAAI;EACF,MAAM,eAAe,uBAAuB;AAC5C,OAAK,MAAM,SAAS,aASlB,KAAI,CAPW,OAAO,MACnB,MACC,EAAE,SAAS,MAAM,WACjB,EAAE,KAAK,QAAQ,MAAM,IAAI,KAAK,MAAM,WACpC,MAAM,QAAQ,QAAQ,KAAK,KAAK,KAAK,EAAE,KAC1C,EAEY;GACX,MAAM,WAAW,MAAM,WAAW,cAAc,IAAI,KAAK,MAAM,SAAS,CAAC,GAAG;AAC5E,OAAI,EAAE,MAAM,aAAa,MAAM,eAC7B,mBAAkB;AAEpB,UAAO,KAAK;IACV,MAAM,MAAM;IACZ,WAAW,MAAM,YAAY,YAAY,MAAM,UAAU,GAAG;IAC5D,WAAW,MAAM,YAAY,YAAY,MAAM,UAAU,GAAG;IAC5D;IACA,UAAU;IACV,eAAe,MAAM;IACtB,CAAC;AACF,OAAI,MAAM,UACR,eAAc,MAAM;;AAM1B,MAAI,gBACF,0BAAyB,CAAC,YAAY,GAAG;SAErC;CAKR,MAAM,kBAAkB,CAAC,GAAG,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,SAAS,CAAC,CAAC;AAEnE,QAAO;EACL,WAAW,gBAAgB,SAAS,IAAI,kBAAkB,CAAC,WAAW,gBAAgB;EACtF,WAAW,YAAY,WAAW;EAClC,QAAQ,OAAO,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,KAAK,CAAC;EAC5D;;;;;AAMH,SAAgB,gBAKd;CACA,MAAM,WAAW,GAAG,UAAU;CAC9B,MAAM,UAAU,GAAG,SAAS;CAC5B,MAAM,UAAU,WAAW;CAC3B,MAAM,cAAc,KAAK,MAAO,UAAU,WAAY,IAAI;AAE1D,QAAO;EACL,OAAO,YAAY,SAAS;EAC5B,MAAM,YAAY,QAAQ;EAC1B,MAAM,YAAY,QAAQ;EAC1B;EACD;;;;;AAMH,SAAgB,UAAU,KAAa,MAAsB;AAC3D,QAAO,WAAW,IAAI,MAAM,KAAK;;;;;AAMnC,SAAgB,cAAc,MAAsB;AAClD,QAAO,KACJ,QAAQ,6BAA6B,GAAG,CACxC,QAAQ,eAAe,GAAG,CAC1B,MAAM;;;;;AAMX,MAAa,gBAAgB;CAC3B;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACP,MAAM;EACP;CACF;;;;AAKD,eAAsB,mBACpB,SACyD;CACzD,MAAMC,SAAyD,EAAE;AAGjE,KAAI;EACF,MAAM,YAAY,MAAM,MAAM,0BAA0B,QAAQ,uBAAuB;AACvF,MAAI,UAAU,IAAI;GAChB,MAAM,SAAS,MAAM,UAAU,MAAM;GAErC,MAAM,aAAa,OAAO,eAAe,EAAE;AAC3C,UAAO,gBACL,OAAO,2BACP,WAAW,2BACX,OAAO,kBACP,WAAW,kBACX,OAAO,eACP,OAAO,uBACP,OAAO,SACP,OAAO;;SAEL;AAKR,KAAI,CAAC,OAAO,cACV,KAAI;EACF,MAAM,SAAS,MAAM,MACnB,0BAA0B,QAAQ,iCACnC;AACD,MAAI,OAAO,IAAI;GACb,MAAM,YAAY,MAAM,OAAO,MAAM;AAErC,OAAI,UAAU,oBAAoB,UAAU,mBAAmB,IAC7D,QAAO,gBAAgB,UAAU;;SAG/B;AAMV,KAAI;EACF,MAAM,UAAU,MAAM,MAAM,qCAAqC,QAAQ,iBAAiB;AAC1F,MAAI,QAAQ,IAAI;GACd,MAAMC,QACJ,MAAM,QAAQ,MAAM;GAGtB,MAAM,WAAW,MAAkD,EAAE,KAAK,QAAQ,EAAE,QAAQ;GAG5F,MAAM,QAAQ,MAAM,MAAM,MAAM,EAAE,KAAK,SAAS,QAAQ,IAAI,EAAE,KAAK,SAAS,QAAQ,CAAC;GACrF,MAAM,KAAK,MAAM,MACd,MAAM,EAAE,KAAK,SAAS,KAAK,IAAI,CAAC,EAAE,KAAK,SAAS,MAAM,IAAI,EAAE,KAAK,SAAS,QAAQ,CACpF;GACD,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,KAAK,SAAS,OAAO,IAAI,EAAE,KAAK,SAAS,QAAQ,CAAC;GACnF,MAAM,UAAU,MAAM,MAAM,MAAM,EAAE,KAAK,SAAS,QAAQ,CAAC;GAC3D,MAAM,WAAW,SAAS,MAAM,QAAQ;AAExC,OAAI,UAAU;IACZ,MAAM,WAAW,SAAS,KAAK,QAAQ,SAAS,GAAG;AAInD,WAAO,YAHc,MAAM,QACxB,MAAM,EAAE,SAAS,SAAS,QAAQ,EAAE,KAAK,WAAW,GAAG,SAAS,YAAY,CAC9E,CAC+B,QAAQ,KAAK,MAAM,MAAM,QAAQ,EAAE,EAAE,EAAE;;;SAGrE;AAIR,QAAO;;AAiCT,MAAM,kBAAkB,KAAK,KAAK,GAAG,SAAS,EAAE,WAAW,kBAAkB;;;;AAK7E,SAAgB,sBAAyC;AACvD,KAAI;AACF,MAAI,CAAC,GAAG,WAAW,gBAAgB,CACjC,QAAO,EAAE;AAGX,SADa,KAAK,MAAM,GAAG,aAAa,iBAAiB,QAAQ,CAAC,CACtD,cAAc,EAAE;SACtB;AACN,SAAO,EAAE;;;;;;AAOb,SAAgB,cAAc,QAOrB;AACP,KAAI;EACF,MAAM,MAAM,KAAK,QAAQ,gBAAgB;AACzC,MAAI,CAAC,GAAG,WAAW,IAAI,CACrB,IAAG,UAAU,KAAK,EAAE,WAAW,MAAM,CAAC;EAGxC,MAAM,aAAa,qBAAqB;AACxC,aAAW,KAAK;GACd,GAAG;GACH,4BAAW,IAAI,MAAM,EAAC,aAAa;GACpC,CAAC;EAGF,MAAM,UAAU,WAAW,MAAM,KAAM;AACvC,KAAG,cAAc,iBAAiB,KAAK,UAAU,EAAE,YAAY,SAAS,EAAE,MAAM,EAAE,CAAC;SAC7E;;;;;;;;;;;AAcV,SAAS,iBAAiB,IAAoB;AAI5C,SAFa,GAAG,MAAM,IAAI,CAAC,KAAK,IAAI,IAGjC,aAAa,CACb,QAAQ,eAAe,GAAG,CAC1B,QAAQ,WAAW,GAAG,CACtB,QAAQ,WAAW,GAAG,CACtB,QAAQ,SAAS,GAAG;;;;;;;AAQzB,SAAgB,iBAAiB,WAK/B;CACA,MAAM,cAAc,UAAU,MAAM;AACpC,KAAI,CAAC,YACH,QAAO;EAAE,SAAS;EAAO,SAAS;EAAI;AAGxC,KAAI;AAEF,MAAI,YAAY,WAAW,UAAU,IAAI,YAAY,WAAW,WAAW,EAAE;GAC3E,MAAM,cAAc,YAAY,MAAM,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,MAAM;AACnE,UAAO;IACL,SAAS;IACT,SAAS,oBAAoB;IAC7B,aAAa;IACb,UAAU;IACX;;EAIH,MAAM,eAAe,YAAY,WAAW,IAAI,GAC5C,YAAY,QAAQ,KAAK,QAAQ,IAAI,QAAQ,GAAG,GAChD;AAEJ,MAAI,CAAC,GAAG,WAAW,aAAa,CAC9B,QAAO;GAAE,SAAS;GAAO,SAAS,qBAAqB;GAAe;EAGxE,MAAM,MAAM,KAAK,QAAQ,aAAa,CAAC,aAAa;EAWpD,MAAM,UAAU,QAV0B;GACxC,QAAQ;GACR,SAAS;GACT,QAAQ;GACR,QAAQ;GACR,SAAS;GACV,CAC0B,QAAQ,YAGF,UAFf,GAAG,aAAa,aAAa,CACtB,SAAS,SAAS;EAE3C,MAAM,WAAW,KAAK,SAAS,aAAa;AAE5C,SAAO;GACL,SAAS;GACT,SAAS,gBAAgB;GACzB,aAAa;GACb;GACD;UACM,GAAG;AACV,SAAO;GAAE,SAAS;GAAO,SAAS,0BAA0B;GAAK;;;AAIrE,SAAgB,uBAAuB,SAK9B;CACP,MAAM,aAAa,qBAAqB;CACxC,MAAM,eAAe,iBAAiB,QAAQ;CAE9C,MAAM,kBAAkB,WAAW,QAAQ,MAAM;EAC/C,MAAM,kBAAkB,iBAAiB,EAAE,MAAM;AACjD,SACE,oBAAoB,gBACpB,gBAAgB,SAAS,aAAa,IACtC,aAAa,SAAS,gBAAgB;GAExC;AAEF,KAAI,gBAAgB,WAAW,EAC7B,QAAO;CAGT,MAAM,eAAe,KAAK,MACxB,gBAAgB,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,gBAAgB,OAC3E;CACD,MAAM,gBAAgB,KAAK,MACzB,gBAAgB,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,gBAAgB,OAC3E;CAGD,MAAMC,UAAkE,EAAE;AAC1E,MAAK,MAAM,KAAK,iBAAiB;AAC/B,MAAI,CAAC,QAAQ,EAAE,QACb,SAAQ,EAAE,UAAU;GAAE,cAAc;GAAG,MAAM;GAAG;AAElD,UAAQ,EAAE,QAAQ,QAAQ;;AAE5B,MAAK,MAAM,UAAU,OAAO,KAAK,QAAQ,EAAE;EACzC,MAAM,mBAAmB,gBAAgB,QAAQ,MAAM,EAAE,WAAW,OAAO;AAC3E,UAAQ,QAAQ,eAAe,KAAK,MAClC,iBAAiB,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,iBAAiB,OAC7E;;AAGH,QAAO;EAAE;EAAc;EAAe,MAAM,gBAAgB;EAAQ;EAAS;;;;;AC/oB/E,SAAgB,cAAc,EAC5B,QACA,OACA,WAAW,OACX,UACA,eACA,gBACA,gBACA,kBACA,qBACA,iBACA,sBACqB;CACrB,MAAM,CAAC,QAAQ,aAAa,SAAsC,OAAO;CACzE,MAAM,CAAC,YAAY,iBAAiB,SAAS,iBAAiB,OAAO;CACrE,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,aAAa,kBAAkB,SAA4B,EAAE,CAAC;CAGrE,MAAM,gBACJ,WACA,QACA,eACG;AACH,YAAU,UAAU;EAEpB,MAAM,UAAU,cAAc;EAI9B,MAAM,WADa,CAAC,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC,CACjD,KAAK,QAAQ;GACvC,MAAM,CAAC,GAAG,KAAK,IAAI,MAAM,IAAI;GAC7B,MAAM,WAAW,QAAQ,QAAQ,MAAM,EAAE,UAAU,KAAK,EAAE,WAAW,EAAE;AACvE,UAAO;IACL,OAAO;IACP,QAAQ;IACR,cAAc,KAAK,MACjB,SAAS,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,SAAS,OAC7D;IACD,eAAe,KAAK,MAClB,SAAS,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,SAAS,OAC7D;IACD,MAAM,SAAS;IAChB;IACD;EAGF,MAAMC,UACJ,QAAQ,SAAS,WACN;GACL,MAAM,aAAa,QAAQ,QAAQ,MAAM,MACvC,EAAE,eAAe,KAAK,eAAe,IAAI,KAC1C;GACD,MAAM,oBAAoB,QAAQ,QAAQ,MAAM,MAC9C,EAAE,eAAe,KAAK,eAAe,IAAI,KAC1C;GAGD,MAAM,gBAAgB,SAAS,KAAK,OAAO;IACzC,GAAG;IACH,OAAO,EAAE,gBAAgB,IAAI,EAAE,gBAAgB;IAChD,EAAE;AAMH,UAAO;IAAE;IAAY;IAAmB,aAJtC,cAAc,SAAS,IACnB,cAAc,QAAQ,MAAM,MAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI,KAAM,GACpE;IAE+C;MACnD,GACJ;AAEN,mBAAiB,WAAW;GAC1B,UAAU,cAAc,cAAc,SAAS,IAAI;GACnD,YAAY;GACZ;GACA;GACD,CAAC;;AAGJ,iBAAgB;AACd,MAAI,CAAC,gBAAgB,SAAS,MAAM,CAClC,qBAAoB,SAAS,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,MAAM,CAAC;IAEzD;EAAC;EAAO;EAAiB;EAAmB,CAAC;CAEhD,MAAM,gBAAgB,OAAO,eAAe;AAE5C,WAAU,OAAO,QAAQ;EAEvB,MAAM,UAAU,YAAY,WAAW;AAEvC,MAAI,IAAI,UAAU,CAAC,QACjB,eAAc;AAEhB,OAAK,UAAU,OAAO,UAAU,QAAQ,CAAC,SAAS;AAChD,uBAAoB,EAAE,CAAC;AACvB,iBAAc,EAAE;AAChB,sBAAmB,CAAC,MAAM,CAAC;;AAE7B,OAAK,UAAU,OAAO,UAAU,QAAQ,CAAC,QACvC,kBAAiB;AAGnB,OAAK,UAAU,OAAO,UAAU,QAAQ,CAAC,QAEvC,kBADkB,kBAAkB,WAAW,QAAQ,SAC5B;AAG7B,OAAK,UAAU,OAAO,UAAU,QAAQ,CAAC,SAAS;AAChD,OAAI,CAAC,YAGH,gBADe,qBAAqB,CACd;AAExB,kBAAe,CAAC,YAAY;;AAG9B,OAAK,UAAU,OAAO,UAAU,QAAQ,CAAC,WAAW,aAAa;GAE/D,MAAMC,YADS,qBAAqB,CACQ,KAAK,OAAO;IACtD,OAAO,EAAE;IACT,QAAQ,EAAE;IACV,cAAc,EAAE;IAChB,cAAc,EAAE;IAChB,aAAa,EAAE;IACf,SAAS,EAAE;IACZ,EAAE;AACH,uBAAoB,UAAU;AAC9B,iBAAc,UAAU,OAAO;GAE/B,MAAM,SAAS,CAAC,GAAG,IAAI,IAAI,UAAU,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC;AAC1D,sBAAmB,OAAO,SAAS,IAAI,OAAO,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;AAClE,kBAAe,MAAM;;GAEvB;CAEF,MAAM,eAAe,YAAY;AAC/B,eAAa,UAAU;EACvB,MAAM,UAAU;GACd;GACA;GACA;GACD;EACD,MAAM,SAAS,QAAQ,aAAa,QAAQ;EAI5C,MAAM,YAAY,KAAK,KAAK;EAC5B,IAAI,iBAAiB;EACrB,IAAI,gBAAgB;EAEpB,MAAM,SAAS,MAAM,OAAO,SAAS,QAAQ;GAC3C,WAAW;GACX,eAAe;AACb,QAAI,CAAC,eAAe;AAClB,sBAAiB,KAAK,KAAK,GAAG;AAC9B,qBAAgB;;;GAGrB,CAAC;EAGF,MAAMC,YAA6B;GACjC;GACA,QAHa,OAAO,eAAe;GAKnC,cAAc,KAAK,MAAM,OAAO,gBAAgB;GAChD,cAAc,kBAAkB,KAAK,MAAM,OAAO,YAAY,GAAI;GAClE,aAAa,OAAO;GACpB,SAAS,KAAK,MAAM,OAAO,UAAU;GACtC;AAGD,gBAAc,UAAU;EAExB,MAAM,iBAAiB,CAAC,GAAG,kBAAkB,UAAU;AACvD,sBAAoB,eAAe;AACnC,iBAAe,SAAS,OAAO,EAAE;AACjC,eAAa,QAAQ,WAAW,eAAe;AAC/C,aAAW,UAAU,aAAa;;CAGpC,MAAM,iBAAiB,iBAAiB,QACrC,MAAM,EAAE,UAAU,SAAS,EAAE,WAAW,cAC1C;CAID,MAAM,aADa,CAAC,GAAG,IAAI,IAAI,iBAAiB,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,CAAC,CAAC,CAEnF,KAAK,QAAQ;EACZ,MAAM,CAAC,GAAG,KAAK,IAAI,MAAM,IAAI;EAC7B,MAAM,UAAU,iBAAiB,QAAQ,MAAM,EAAE,UAAU,KAAK,EAAE,WAAW,EAAE;AAC/E,MAAI,QAAQ,WAAW,EACrB,QAAO;AAET,SAAO;GACL,OAAO;GACP,QAAQ;GACR,MAAM,QAAQ;GACd,cAAc,KAAK,MAAM,QAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,QAAQ,OAAO;GAC1F,eAAe,KAAK,MAAM,QAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,cAAc,EAAE,GAAG,QAAQ,OAAO;GAC5F;GACD,CACD,OAAO,QAAQ;CAQlB,MAAM,gBACJ,WAAW,SAAS,IAAI,KAAK,IAAI,GAAG,WAAW,KAAK,MAAM,EAAE,aAAa,CAAC,GAAG;CAI/E,MAAM,QACJ,iBAAiB,SAAS,WACf;EACL,MAAM,aAAa,iBAAiB,QAAQ,MAAM,MAChD,EAAE,eAAe,KAAK,eAAe,IAAI,KAC1C;EACD,MAAM,oBAAoB,iBAAiB,QAAQ,MAAM,MACvD,EAAE,eAAe,KAAK,eAAe,IAAI,KAC1C;EAGD,MAAM,gBAAgB,WAAW,KAAK,OAAO;GAC3C,GAAG;GAGH,OAAO,EAAE,gBAAgB,IAAI,EAAE,gBAAgB;GAChD,EAAE;AAMH,SAAO;GAAE;GAAY;GAAmB,aAJtC,cAAc,SAAS,IACnB,cAAc,QAAQ,MAAM,MAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI,KAAM,GACpE;GAE+C;KACnD,GACJ;AAKN,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,qBAAC;IACC,oBAAC;KAAK;eAAK;MAAkB;IAC7B,oBAAC;KAAK;KAAK,OAAM;eACd;MACI;IACP,oBAAC,kBAAK,SAAW;IACjB,oBAAC;KAAK;KAAK,OAVf,kBAAkB,WAAW,UAAU,kBAAkB,QAAQ,WAAW;eAWrE,cAAc,aAAa;MACvB;IACN,WAAW,SAAS,KAAK,qBAAC;KAAK;;MAAS;MAAa,WAAW;MAAO;;MAAgB;OACpF;GACN,oBAAC;IAAK;cAAS;KAA+C;GAE7D,eAAe,SAAS,KACvB,qBAAC;IAAI,eAAc;IAAS,SAAS;eACnC,qBAAC;KAAK;KAAK,OAAM;;MAAO;MACT;MAAM;MAAK,cAAc,aAAa;MAAC;MAAG,eAAe;MAAO;;MACxE,EACP,oBAAC;KAAI,eAAc;KAAS,WAAW;eACpC,eAAe,MAAM,GAAG,CAAC,KAAK,GAAG,MAChC,qBAAC;MAAK,UAAU,IAAI,eAAe,SAAS;;OAAW;OAChD,IAAI;OAAE;OAAE,oBAAC;QAAK,OAAM;kBAAQ,EAAE;SAAoB;;OAAQ;OAC/D,qBAAC;QAAK,OAAM;mBAAU,EAAE,cAAa;SAAS;;OAAe,EAAE;OAAY;;QAFzB,EAG7C,CACP;MACE;KACF;GAGR,qBAAC;IAAI,eAAc;IAAM,SAAS;eAC/B,WAAW,SAAS,KACnB,qBAAC;KACC,aAAY;KACZ,aAAY;KACZ,eAAc;KACd,aAAa;KACb,UAAU;gBAEV,oBAAC;MAAK;MAAK,OAAM;gBACd,WAAW,SAAS,IAAI,gBAAgB;OACpC,EACN,WAAW,KAAK,MAAM;MACrB,MAAM,YAAY,EAAE,UAAU,SAAS,EAAE,WAAW;MACpD,MAAM,SACJ,EAAE,WAAW,WAAW,UAAU,EAAE,WAAW,QAAQ,WAAW;AACpE,aACE,qBAAC;OACC,qBAAC;QACE,YAAY,MAAM;QAAI;QAAE,EAAE;WACtB;OACP,qBAAC;QAAK,OAAO;;SAAQ;SAAG,EAAE;SAAO;;SAAQ;OACzC,oBAAC,kBAAK,OAAS;OACf,qBAAC;QACC;QACA,OACE,EAAE,iBAAiB,iBAAiB,WAAW,SAAS,IAAI,UAAU;mBAGvE,EAAE,cAAa;SACX;OACP,oBAAC,kBAAK,OAAS;OACf,qBAAC;QAAK,OAAM;mBAAU,EAAE,eAAc;SAAS;OAC/C,qBAAC;QAAK;;SAAS;SAAG,EAAE;SAAK;;SAAa;OACrC,EAAE,iBAAiB,iBAAiB,WAAW,SAAS,KACvD,oBAAC;QAAK,OAAM;kBAAQ;SAAiB;WAlB/B,GAAG,EAAE,MAAM,GAAG,EAAE,SAoBpB;OAER;MACE,EAGP,SAAS,iBAAiB,UAAU,KACnC,qBAAC;KAAI,aAAY;KAAU,aAAY;KAAS,eAAc;KAAS,UAAU;;MAC/E,oBAAC;OAAK;OAAK,OAAM;iBAAU;QAEpB;MACP,qBAAC;OACC,oBAAC;QAAK,OAAM;kBAAO;SAAe;OAAC;OACnC,oBAAC;QAAK;QAAK,OAAM;kBACd,MAAM,WAAW;SACb;OAAC;OAAI;OAEZ,qBAAC;QAAK;;SACH;SAAI;SACH,MAAM,WAAW;SAAM;SAAK,MAAM,WAAW;SAAO;;SACjD;UACF;MACP,qBAAC;OACC,oBAAC;QAAK,OAAM;kBAAO;SAAe;OAAC;OACnC,oBAAC;QAAK;QAAK,OAAM;kBACd,MAAM,kBAAkB;SACpB;;OAEP,qBAAC;QAAK;;SACH;SAAI;SACH,MAAM,kBAAkB;SAAM;SAAK,MAAM,kBAAkB;SAAO;;SAC/D;UACF;MACN,MAAM,eACL,qBAAC;OACC,oBAAC;QAAK,OAAM;kBAAO;SAAc;OAAC;OAClC,oBAAC;QAAK;QAAK,OAAM;kBACd,MAAM,YAAY;SACd;OACP,qBAAC;QAAK;;SACH;SAAI;SACD,MAAM,YAAY;SAAO;SAAG,MAAM,YAAY;SAAa;SAAQ;SACtE,MAAM,YAAY;SAAc;;SAC5B;UACF;;MAEL;KAEJ;GAEL,WAAW,aACV,oBAAC;IAAI,SAAS;cACZ,qBAAC;KAAK,OAAM;;MACV,oBAAC,WAAQ,MAAK,SAAS;;MAAqB,aAAa;MAAE;;MACtD;KACH;GAGP,eACC,qBAAC;IACC,aAAY;IACZ,aAAY;IACZ,eAAc;IACd,SAAS;IACT,UAAU;eAEV,qBAAC;KAAK;KAAK,OAAM;;MAAO;MACF,YAAY;MAAO;;MAClC,EACN,YAAY,WAAW,IACtB,oBAAC;KAAK;eAAS;MAA6D,GAE5E,4CACE,qBAAC;KAAI,eAAc;KAAS,SAAS;gBAClC,YACE,MAAM,IAAI,CACV,SAAS,CACT,KAAK,GAAG,MACP,qBAAC;MAAK;;OACJ,oBAAC;QAAK,OAAM;kBAAQ,EAAE;SAAoB;;OAAQ;OAClD,oBAAC;QAAK,OAAM;kBAAU,EAAE;SAAoB;;OAC5C,qBAAC;QAAK;;SACH;SAAI;SACF,EAAE;SAAM;SAAG,EAAE;SAAO;SAAG,IAAI,KAAK,EAAE,UAAU,CAAC,oBAAoB;;SAC/D;;QANW,EAOb,CACP,EACH,YAAY,SAAS,MACpB,qBAAC;MAAK;;OAAS;OAAS,YAAY,SAAS;OAAG;;OAAY;MAE1D,EACN,qBAAC;KAAK;;MAAS;MACP,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAoC;MACvE,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;;MACxB,IACN;KAED;GAGR,oBAAC;IAAI,WAAW;cACd,qBAAC;KAAK;;MACJ,oBAAC;OAAK,OAAM;iBAAS;QAAY;;MAAO,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAQ;MAC5E,kBAAkB,WAAW,QAAQ;MAAM;MAAG,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAS;MACrF,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAW,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAS;MAC9E,oBAAC;OAAK,OAAM;iBAAO;QAAU;;;MACxB;KACH;;GACF;;;;;;;;;;;;;;;;;;;;;;;;;;AC/ZV,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACD;;;;AAKD,SAAgB,cAA6B;AAC3C,MAAK,MAAM,WAAW,UACpB,KAAI;AAMF,MALe,UAAU,SAAS,CAAC,YAAY,EAAE;GAC/C,UAAU;GACV,OAAO;GACP,SAAS;GACV,CAAC,CACS,WAAW,EACpB,QAAO;SAEH;AAIV,QAAO;;;;;AAMT,SAAgB,iBAA0B;AACxC,QAAO,aAAa,KAAK;;;;;AAM3B,IAAa,aAAb,MAAwB;CACtB,AAAQ,UAA+B;CACvC,AAAQ,WAA0B;CAClC,AAAQ;CACR,AAAQ,cAAc;CAEtB,YAAY,UAA6B,EAAE,EAAE;AAC3C,OAAK,UAAU;GACb,YAAY,QAAQ,cAAc;GAClC,UAAU,QAAQ,YAAY;GAC9B,UAAU,QAAQ,YAAY;GAC9B,QAAQ,QAAQ,UAAU;GAC1B,kBAAkB,QAAQ,oBAAoB;GAC9C,iBAAiB,QAAQ,mBAAmB;GAC7C;;;;;CAMH,MAAM,QAAuB;AAC3B,MAAI,KAAK,YACP,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,UAAU,aAAa;AAC7B,MAAI,CAAC,QACH,OAAM,IAAI,MACR,6IAID;AAIH,OAAK,WAAW,KAAK,QAAQ,EAAE,cAAc,KAAK,KAAK,CAAC,MAAM;EAG9D,MAAMC,OAAiB,EAAE;AAGzB,MAAI,QAAQ,aAAa,SACvB,MAAK,KAAK,KAAK;WACN,QAAQ,aAAa,QAC9B,MAAK,KAAK,MAAM,QAAQ,UAAU;WACzB,QAAQ,aAAa,QAC9B,MAAK,KAAK,MAAM,aAAa,UAAU;MAEvC,MAAK,KAAK,KAAK;AAIjB,OAAK,KACH,MACA,OAAO,KAAK,QAAQ,WAAW,EAC/B,MACA,OAAO,KAAK,QAAQ,SAAS,EAC7B,MACA,OAAO,KAAK,QAAQ,SAAS,EAC7B,KAAK,SACN;AAED,OAAK,UAAU,MAAM,SAAS,MAAM,EAClC,OAAO;GAAC;GAAQ;GAAQ;GAAO,EAChC,CAAC;AAEF,OAAK,cAAc;AAGnB,OAAK,QAAQ,GAAG,UAAU,QAAQ;AAChC,WAAQ,MAAM,qBAAqB,IAAI,QAAQ;AAC/C,QAAK,cAAc;IACnB;;;;;CAMJ,MAAM,OAAiC;AACrC,MAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAC7B,OAAM,IAAI,MAAM,gBAAgB;AAGlC,SAAO,IAAI,SAAS,SAAS,WAAW;AACtC,QAAK,QAAS,GAAG,eAAe;AAC9B,SAAK,cAAc;AAEnB,QAAI;AACF,SAAI,CAAC,KAAK,YAAY,CAAC,WAAW,KAAK,SAAS,EAAE;AAChD,6BAAO,IAAI,MAAM,2BAA2B,CAAC;AAC7C;;KAIF,MAAM,SAAS,aAAa,KAAK,SAAS;KAG1C,MAAM,QAAQ,KAAK,SAAS,IAAI,WAAW,OAAO,CAAC;AAGnD,SAAI;AACF,iBAAW,KAAK,SAAS;aACnB;AAGR,UAAK,WAAW;AAEhB,aAAQ;MACN;MACA,YAAY,KAAK,QAAQ;MACzB,UAAU,MAAM,SAAS,KAAK,QAAQ;MACvC,CAAC;aACK,KAAK;AACZ,YAAO,IAAI;;KAEb;AAGF,QAAK,QAAS,KAAK,UAAU;IAC7B;;;;;CAMJ,SAAe;AACb,MAAI,KAAK,SAAS;AAEhB,QAAK,QAAQ,KAAK,UAAU;GAE5B,MAAM,OAAO,KAAK;AAClB,oBAAiB;AACf,QAAI;AACF,UAAK,KAAK,UAAU;YACd;MAGP,IAAI;AACP,QAAK,UAAU;;AAEjB,OAAK,cAAc;AAGnB,MAAI,KAAK,YAAY,WAAW,KAAK,SAAS,CAC5C,KAAI;AACF,cAAW,KAAK,SAAS;UACnB;AAIV,OAAK,WAAW;;;;;CAMlB,YAAqB;AACnB,SAAO,KAAK;;;;;CAMd,AAAQ,SAAS,QAAkC;EACjD,MAAM,OAAO,IAAI,SAAS,OAAO,QAAQ,OAAO,YAAY,OAAO,WAAW;EAG9E,IAAI,aAAa;AACjB,OAAK,IAAI,IAAI,IAAI,IAAI,OAAO,SAAS,GAAG,IACtC,KACE,OAAO,OAAO,OACd,OAAO,IAAI,OAAO,MAClB,OAAO,IAAI,OAAO,OAClB,OAAO,IAAI,OAAO,IAClB;AACA,gBAAa,IAAI;AACjB;;EAKJ,MAAM,iBAAiB,KAAK,QAAQ,WAAW;EAC/C,MAAM,aAAa,KAAK,OAAO,OAAO,SAAS,cAAc,eAAe;EAC5E,MAAM,QAAQ,IAAI,aAAa,WAAW;EAG1C,MAAM,WAAW,MAAM,KAAK,QAAQ,WAAW;AAC/C,OAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;GACnC,MAAM,SAAS,aAAa,IAAI;AAChC,OAAI,SAAS,kBAAkB,OAAO,OAGpC,OAAM,MADJ,KAAK,QAAQ,aAAa,KAAK,KAAK,SAAS,QAAQ,KAAK,GAAG,KAAK,SAAS,QAAQ,KAAK,IACtE;;AAIxB,SAAO;;;;;;;;;;;;;;;;;;;;;AAgDX,IAAa,sBAAb,MAAiC;CAC/B,AAAQ,UAA+B;CACvC,AAAQ;CACR,AAAQ,cAAc;CACtB,AAAQ,cAA8B,EAAE;CACxC,AAAQ,YAAsB,EAAE;CAChC,AAAQ,gBAAuD;CAC/D,AAAQ,YAAY;CAEpB,YAAY,UAAsC,EAAE,EAAE;AACpD,OAAK,UAAU;GACb,YAAY,QAAQ,cAAc;GAClC,UAAU,QAAQ,YAAY;GAC9B,UAAU,QAAQ,YAAY;GAC9B,QAAQ,QAAQ,UAAU;GAC1B,kBAAkB,QAAQ,oBAAoB;GAC9C,iBAAiB,QAAQ,mBAAmB;GAC5C,cAAc,QAAQ,uBAAuB;GAC7C,eAAe,QAAQ,iBAAiB;GACzC;;;;;CAMH,MAAM,QAAuB;AAC3B,MAAI,KAAK,YACP,OAAM,IAAI,MAAM,oBAAoB;EAGtC,MAAM,UAAU,aAAa;AAC7B,MAAI,CAAC,QACH,OAAM,IAAI,MACR,6IAID;EAIH,MAAMA,OAAiB,EAAE;AAGzB,MAAI,QAAQ,aAAa,SACvB,MAAK,KAAK,KAAK;WACN,QAAQ,aAAa,QAC9B,MAAK,KAAK,MAAM,QAAQ,UAAU;WACzB,QAAQ,aAAa,QAC9B,MAAK,KAAK,MAAM,aAAa,UAAU;MAEvC,MAAK,KAAK,KAAK;AAIjB,OAAK,KACH,MACA,OACA,MACA,OAAO,KAAK,QAAQ,WAAW,EAC/B,MACA,OAAO,KAAK,QAAQ,SAAS,EAC7B,MACA,OAAO,KAAK,QAAQ,SAAS,EAC7B,MACA,kBACA,IACD;AAED,OAAK,UAAU,MAAM,SAAS,MAAM,EAClC,OAAO;GAAC;GAAQ;GAAQ;GAAO,EAChC,CAAC;AAEF,OAAK,cAAc;AACnB,OAAK,YAAY,KAAK,KAAK;AAC3B,OAAK,YAAY,EAAE;AACnB,OAAK,cAAc,EAAE;AAGrB,OAAK,QAAQ,QAAQ,GAAG,SAAS,UAAkB;AACjD,QAAK,UAAU,KAAK,MAAM;IAC1B;AAGF,OAAK,QAAQ,GAAG,UAAU,QAAQ;AAChC,WAAQ,MAAM,qBAAqB,IAAI,QAAQ;AAC/C,QAAK,cAAc;IACnB;AAGF,OAAK,gBAAgB,kBAAkB;AACrC,QAAK,aAAa;KACjB,KAAK,QAAQ,cAAc;;;;;CAMhC,AAAQ,cAAoB;AAC1B,MAAI,KAAK,UAAU,WAAW,EAAG;EAGjC,MAAM,WAAW,OAAO,OAAO,KAAK,UAAU;AAC9C,OAAK,YAAY,EAAE;EAGnB,MAAM,QAAQ,KAAK,gBAAgB,SAAS;AAC5C,MAAI,MAAM,SAAS,GAAG;AACpB,QAAK,YAAY,KAAK,MAAM;AAC5B,QAAK,QAAQ,aAAa,MAAM;;;;;;CAOpC,AAAQ,gBAAgB,QAA8B;EACpD,MAAM,iBAAiB,KAAK,QAAQ,WAAW;EAC/C,MAAM,aAAa,KAAK,MAAM,OAAO,SAAS,eAAe;EAC7D,MAAM,QAAQ,IAAI,aAAa,WAAW;EAC1C,MAAM,WAAW,MAAM,KAAK,QAAQ,WAAW;AAE/C,OAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;GACnC,MAAM,SAAS,IAAI;AAGnB,SAAM,MADJ,KAAK,QAAQ,aAAa,KAAK,OAAO,YAAY,OAAO,GAAG,OAAO,YAAY,OAAO,IACpE;;AAGtB,SAAO;;;;;CAMT,MAAM,OAAiC;AACrC,MAAI,CAAC,KAAK,eAAe,CAAC,KAAK,QAC7B,OAAM,IAAI,MAAM,gBAAgB;AAIlC,MAAI,KAAK,eAAe;AACtB,iBAAc,KAAK,cAAc;AACjC,QAAK,gBAAgB;;AAIvB,OAAK,aAAa;AAElB,SAAO,IAAI,SAAS,SAAS,WAAW;AACtC,QAAK,QAAS,GAAG,eAAe;AAC9B,SAAK,cAAc;AAEnB,QAAI;KAEF,MAAM,cAAc,KAAK,YAAY,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,EAAE;KAClF,MAAM,QAAQ,IAAI,aAAa,YAAY;KAC3C,IAAI,SAAS;AACb,UAAK,MAAM,SAAS,KAAK,aAAa;AACpC,YAAM,IAAI,OAAO,OAAO;AACxB,gBAAU,MAAM;;AAGlB,UAAK,cAAc,EAAE;AAErB,aAAQ;MACN;MACA,YAAY,KAAK,QAAQ;MACzB,UAAU,MAAM,SAAS,KAAK,QAAQ;MACvC,CAAC;aACK,KAAK;AACZ,YAAO,IAAI;;KAEb;AAGF,QAAK,QAAS,KAAK,UAAU;IAC7B;;;;;CAMJ,SAAe;AACb,MAAI,KAAK,eAAe;AACtB,iBAAc,KAAK,cAAc;AACjC,QAAK,gBAAgB;;AAGvB,MAAI,KAAK,SAAS;AAEhB,QAAK,QAAQ,KAAK,UAAU;GAE5B,MAAM,OAAO,KAAK;AAClB,oBAAiB;AACf,QAAI;AACF,UAAK,KAAK,UAAU;YACd;MAGP,IAAI;AACP,QAAK,UAAU;;AAGjB,OAAK,cAAc;AACnB,OAAK,YAAY,EAAE;AACnB,OAAK,cAAc,EAAE;;;;;CAMvB,YAAqB;AACnB,SAAO,KAAK;;;;;CAMd,aAAqB;AACnB,MAAI,CAAC,KAAK,YAAa,QAAO;AAC9B,UAAQ,KAAK,KAAK,GAAG,KAAK,aAAa;;;;;;ACxgB3C,MAAM,aAAa;CACjB,WAAW;EACT,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;;;;;EAKT;CACD,OAAO;EACL,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;;;;;;EAMT;CACD,SAAS;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;;;;;;EAMT;CACD,UAAU;EACR,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;;;;;;EAMT;CACD,SAAS;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACT;CACF;AAKD,SAAS,eAAe,MAAsB;AAC5C,QAAO,KAAK,KAAK,KAAK,SAAS,EAAE;;AAInC,SAAS,eAAe,SAAyB;AAC/C,QAAO,QACJ,QAAQ,6BAA6B,GAAG,CACxC,QAAQ,eAAe,GAAG,CAC1B,MAAM;;AAIX,SAAS,eAAe,SAAoC;CAC1D,MAAM,QAAQ,QAAQ,MAAM,KAAK;CACjC,MAAMC,WAA8B,EAAE;CACtC,IAAI,cAAc;CAClB,IAAIC,mBAA6B,EAAE;CACnC,IAAI,gBAAgB;AAEpB,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxC,MAAM,OAAO,MAAM;AAGnB,MAAI,KAAK,WAAW,MAAM,EAAE;AAC1B,OAAI,aAAa;AAEf,aAAS,KACP,qBAAC;KACC,aAAY;KACZ,aAAY;KACZ,eAAc;KAEd,SAAS;KACT,UAAU;gBAET,iBAAiB,oBAAC;MAAK;gBAAU;OAAqB,EACvD,oBAAC;MAAK,OAAM;gBAAS,iBAAiB,KAAK,KAAK;OAAQ;OALnD,QAAQ,IAMT,CACP;AACD,uBAAmB,EAAE;AACrB,oBAAgB;AAChB,kBAAc;UACT;AACL,kBAAc;AACd,oBAAgB,KAAK,MAAM,EAAE,CAAC,MAAM;;AAEtC;;AAGF,MAAI,aAAa;AACf,oBAAiB,KAAK,KAAK;AAC3B;;AAIF,MAAI,KAAK,WAAW,OAAO,EAAE;AAC3B,YAAS,KACP,oBAAC;IAAK;IAAK,OAAM;cACd,KAAK,MAAM,EAAE;MADa,EAEtB,CACR;AACD;;AAEF,MAAI,KAAK,WAAW,MAAM,EAAE;AAC1B,YAAS,KACP,oBAAC;IAAK;IAAK,OAAM;cACd,KAAK,MAAM,EAAE;MADe,EAExB,CACR;AACD;;AAEF,MAAI,KAAK,WAAW,KAAK,EAAE;AACzB,YAAS,KACP,oBAAC;IAAK;IAAK,OAAM;cACd,KAAK,MAAM,EAAE;MADgB,EAEzB,CACR;AACD;;AAIF,MAAI,KAAK,MAAM,UAAU,EAAE;GACzB,MAAM,gBAAgB,KAAK,MAAM,EAAE;AACnC,YAAS,KACP,qBAAC,mBACC,oBAAC;IAAK,OAAM;cAAO;KAAU,EAC5B,qBAAqB,cAAc,KAF3B,EAGJ,CACR;AACD;;EAIF,MAAM,WAAW,KAAK,MAAM,kBAAkB;AAC9C,MAAI,UAAU;AACZ,YAAS,KACP,qBAAC,mBACC,qBAAC;IAAK,OAAM;;KAAO;KAAE,SAAS;KAAG;;KAAS,EACzC,qBAAqB,SAAS,GAAG,KAFzB,EAGJ,CACR;AACD;;AAIF,WAAS,KAAK,oBAAC,kBAAc,qBAAqB,KAAK,IAA9B,EAAsC,CAAC;;AAGlE,QAAO;;AAIT,SAAS,qBAAqB,MAA+B;CAE3D,MAAMC,QAA2B,EAAE;CACnC,IAAI,YAAY;CAChB,IAAI,MAAM;AAEV,QAAO,UAAU,SAAS,GAAG;EAE3B,MAAM,YAAY,UAAU,MAAM,uBAAuB;AACzD,MAAI,WAAW;AACb,OAAI,UAAU,GACZ,OAAM,KAAK,oBAAC,kBAAuB,UAAU,MAAtB,OAAO,EAAyB,CAAC;AAE1D,SAAM,KACJ,qBAAC;IAAK,iBAAgB;IAAQ,OAAM;;KACjC;KACA,UAAU;KAAI;;MAFiC,OAAO,EAGlD,CACR;AACD,eAAY,UAAU;AACtB;;EAIF,MAAM,YAAY,UAAU,MAAM,6BAA6B;AAC/D,MAAI,WAAW;AACb,OAAI,UAAU,GACZ,OAAM,KAAK,oBAAC,kBAAuB,UAAU,MAAtB,OAAO,EAAyB,CAAC;AAE1D,SAAM,KACJ,oBAAC;IAAK;cACH,UAAU;MADI,OAAO,EAEjB,CACR;AACD,eAAY,UAAU;AACtB;;EAIF,MAAM,cAAc,UAAU,MAAM,yBAAyB;AAC7D,MAAI,aAAa;AACf,OAAI,YAAY,GACd,OAAM,KAAK,oBAAC,kBAAuB,YAAY,MAAxB,OAAO,EAA2B,CAAC;AAE5D,SAAM,KACJ,oBAAC;IAAK;cACH,YAAY;MADI,OAAO,EAEnB,CACR;AACD,eAAY,YAAY;AACxB;;AAIF,QAAM,KAAK,oBAAC,kBAAuB,aAAZ,OAAO,EAAsB,CAAC;AACrD;;AAGF,QAAO,0CAAG,QAAS;;AAIrB,SAAS,mBACP,SACA,cAC6D;AAC7D,KAAI,CAAC,aACH,QAAO;EAAE,UAAU;EAAI,UAAU,eAAe,QAAQ;EAAE,YAAY;EAAO;CAI/E,MAAM,aAAa,QAAQ,QAAQ,UAAU;CAC7C,MAAM,WAAW,QAAQ,QAAQ,WAAW;AAG5C,KAAI,eAAe,MAAM,aAAa,GAGpC,QAAO;EAAE,UAFQ,QAAQ,MAAM,aAAa,EAAE;EAE3B,UADF,QAAQ,MAAM,GAAG,WAAW,CAAC,MAAM;EACvB,YAAY;EAAM;AAIjD,KAAI,eAAe,MAAM,aAAa,MAAM,WAAW,WAGrD,QAAO;EAAE,UAFQ,QAAQ,MAAM,aAAa,GAAG,SAAS,CAAC,MAAM;EAE5C,UADF,eAAe,QAAQ;EACX,YAAY;EAAO;AAIlD,QAAO;EAAE,UAAU;EAAI,UAAU,eAAe,QAAQ;EAAE,YAAY;EAAO;;AAG/E,SAAS,kBAAkB,EACzB,SACA,QACA,gBAKC;CACD,MAAM,EAAE,UAAU,UAAU,eAAe,mBAAmB,SAAS,aAAa;AAEpF,QACE,qBAAC;EAAI,eAAc;EAAS,cAAc;EAAG,UAAU;;GACpD,gBAAgB,YACf,qBAAC;IAAI,eAAc;IAAS,cAAc;eACxC,oBAAC;KAAK,OAAM;KAAO;KAAS;eACzB;MACI,EACP,qBAAC;KAAK;KAAS;gBACZ,eAAe,SAAS,EACxB,cAAc,oBAAC,WAAQ,MAAK,SAAS;MACjC;KACH;GAEP,YACC,qBAAC;IACC,qBAAC;KAAK;KAAK,OAAM;gBACd,QAAQ;MACJ;IACP,oBAAC;KAAK;KAAK,OAAM;eACd;MACI;IACN,CAAC,cACA,oBAAC;KAAK,OAAM;eACV,oBAAC,WAAQ,MAAK,SAAS;MAClB;OAEL;GAEP,EAAE,YAAY,aACb,oBAAC;IAAK,OAAM;cACV,oBAAC,WAAQ,MAAK,SAAS;KAClB;;GAEL;;AAKV,SAAS,yBAAyB,UAAqB,MAAwB;CAE7E,IAAI,UAAU,uBADO,WAAW,MAAM,OACY;AAElD,MAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,OACf,YAAW,qBAAqB,IAAI,QAAQ;UACnC,IAAI,SAAS,YACtB,YAAW,0BAA0B,IAAI,QAAQ;AAKrD,QAAO;;AAGT,SAAgB,SAAS,EACvB,QACA,cACA,WACA,WACA,kBACA,eACA,eACA,cACA,sBACA,eACA,gBACgB;CAChB,MAAM,CAAC,UAAU,eAAe,SAAoB,EAAE,CAAC;CACvD,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,GAAG;CAC1D,MAAM,CAAC,MAAM,WAAW,SAAmB,YAAY;CACvD,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,MAAM;CAC/D,MAAM,CAAC,aAAa,kBAAkB,SAAS,MAAM;CACrD,MAAM,CAAC,UAAU,eAAe,SAAS,MAAM;CAC/C,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;CACjD,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,GAAG;CAC1D,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,EAAE;CAC3D,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,SAAS,OAAmC,KAAK;CACvD,MAAM,sBAAsB,OAA6C,KAAK;CAC9E,MAAM,qBAAqB,OAAe,EAAE;CAC5C,MAAM,iBAAiB,OAA8B,KAAK;CAI1D,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,MAAM;CAC/D,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,EAAE;CAGvD,MAAM,CAAC,gBAAgB,qBAAqB,SAAmB,EAAE,CAAC;CAClE,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,MAAM;CAC7D,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,EAAE;CAGvD,MAAM,iBAAiB,OAAO,kBAAkB,IAAI;CAGpD,MAAM,CAAC,SAAS,cAAc,SAAmB,EAAE,CAAC;CACpD,MAAM,CAAC,cAAc,mBAAmB,SAAS,GAAG;CACpD,MAAM,CAAC,YAAY,iBAAiB,SAAS,GAAG;CAGhD,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,aAAa;CACtE,MAAM,CAAC,eAAe,oBAAoB,SAAS,UAAU;AAG7D,iBAAgB;AACd,MAAI,iBAAiB,kBAAkB;AACrC,uBAAoB,aAAa;AACjC,gBAAa,MAAM,CACjB,GAAG,GACH;IACE,MAAM;IACN,SAAS,eAAe,6BAA6B;IACtD,CACF,CAAC;;IAEH,CAAC,cAAc,iBAAiB,CAAC;AAEpC,iBAAgB;AACd,MAAI,cAAc,eAAe;AAC/B,oBAAiB,UAAU;AAC3B,gBAAa,MAAM,CACjB,GAAG,GACH;IACE,MAAM;IACN,SAAS,YAAY,4CAA4C;IAClE,CACF,CAAC;;IAEH,CAAC,WAAW,cAAc,CAAC;AAG9B,iBAAgB;AACd,eAAa;AAEX,OAAI,oBAAoB,SAAS;AAC/B,QAAI;AACF,yBAAoB,QAAQ,OAAO;YAC7B;AAGR,wBAAoB,UAAU;;AAGhC,OAAI,OAAO,SAAS;AAClB,QAAI;AACF,YAAO,QAAQ,QAAQ;YACjB;AAGR,WAAO,UAAU;;AAEnB,OAAI,eAAe,SAAS;AAC1B,iBAAa,eAAe,QAAQ;AACpC,mBAAe,UAAU;;;IAG5B,EAAE,CAAC;CAEN,MAAM,QAAQ,OAAO,KAAK,WAAW;CAIrC,MAAM,YAAY,OAAO,cAAc;CACvC,MAAM,UAAU,WAAW;CAC3B,MAAM,eAAe,UAAU,uBAAuB,GAAG,EAAE;CAE3D,MAAM,cADc,UAAU,aAAa,MAAM,MAAM,EAAE,YAAY,QAAQ,GAAG,OAChD,iBAAiB,WAAW,iBAAiB;CAI7E,MAAM,gBAAgB,eADF,yBAAyB,UAAU,KAAK,CACX;CACjD,MAAM,iBAAiB,KAAK,MAAO,gBAAgB,aAAc,IAAI;CAGrE,MAAM,eAAe,iBAAiB,IAAI;CAG1C,MAAM,2BAA2B;EAC/B,MAAM,cAAc,iBAAiB,IAAI;EACzC,MAAM,aAAa,iBAAiB,IAAI;AAExC,UAAQ,gBAAR;GACE,KAAK;AACH,wBAAoB;AACpB;GACF,KAAK;AACH,qBAAiB;AACjB;GACF,KAAK;AACH,qBAAiB;AACjB;GACF;AACE,QAAI,mBAAmB,YAAY;AAEjC,wBAAmB,KAAK;AACxB,uBAAkB,GAAG;AACrB,wBAAmB,MAAM,IAAI,EAAE;eACtB,mBAAmB,aAAa;AAG3C;;;AAKN,WAAU,MAAM,QAAQ;AAEtB,MAAI,IAAI,OAAO,CAAC,cAAc,CAAC,iBAAiB;AAC9C,OAAI,iBAEF,qBAAoB,MAAM;QACrB;AAEL,wBAAoB,KAAK;AACzB,wBAAoB,MAAM;;AAE5B;;AAIF,MAAI,oBAAoB,CAAC,YAAY;AAEnC,OAAI,IAAI,WAAW;AACjB,uBAAmB,OAAO,IAAI,IAAI,gBAAgB,aAAa;AAC/D;;AAEF,OAAI,IAAI,YAAY;AAClB,uBAAmB,OAAO,IAAI,KAAK,aAAa;AAChD;;AAGF,OAAI,IAAI,QAAQ;AACd,wBAAoB;AAEpB;;AAGF,OAAI,IAAI,QAAQ;AACd,wBAAoB,MAAM;AAC1B;;AAGF,OAAI,QAAQ,OAAO,QAAQ,KAAK;AAE9B,sBADY,OAAO,SAAS,MAAM,GAAG,GAAG,EAClB;AACtB,wBAAoB;AACpB;;;AAKJ,MAAI,SAAS,OAAO,IAAI,QAAQ,kBAAkB,CAAC,cAAc,CAAC,kBAAkB;AAClF,sBAAmB,KAAK;AACxB,qBAAkB,GAAG;AACrB,sBAAmB,MAAM,IAAI,EAAE;;AAIjC,MAAI,SAAS,OAAO,IAAI,KACtB,WAAU;AAIZ,MAAI,SAAS,OAAO,IAAI,KACtB,WAAU;AAKZ,MAAI,SAAS,OAAO,CAAC,cAAc,CAAC,oBAAoB,CAAC,gBACvD,KAAI,UAEF,eAAc,KAAK;MAGnB,iBAAgB;AAKpB,MAAI,IAAI,QACN;OAAI,iBAAiB;AACnB,uBAAmB,MAAM;AACzB,sBAAkB,GAAG;AACrB,uBAAmB,MAAM,IAAI,EAAE;cACtB,iBACT,qBAAoB,MAAM;;AAK9B,MAAI,EAAE,cAAc,oBAAoB,mBAAmB,mBAAmB;AAC5E,OAAI,IAAI,WAAW,QAAQ,SAAS,GAClC;QAAI,iBAAiB,IAAI;AAEvB,mBAAc,MAAM;AACpB,qBAAgB,QAAQ,SAAS,EAAE;AACnC,cAAS,QAAQ,GAAG,GAAG,IAAI,GAAG;eACrB,eAAe,GAAG;AAC3B,qBAAgB,eAAe,EAAE;AACjC,cAAS,QAAQ,eAAe,GAAG;;;AAGvC,OAAI,IAAI,aAAa,iBAAiB,GACpC,KAAI,eAAe,QAAQ,SAAS,GAAG;AACrC,oBAAgB,eAAe,EAAE;AACjC,aAAS,QAAQ,eAAe,GAAG;UAC9B;AAEL,oBAAgB,GAAG;AACnB,aAAS,WAAW;;;GAI1B;CAGF,MAAM,wBAAwB,YAAY;AACxC,MAAI,SAAS,SAAS,GAAG;AACvB,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS;IAAqC,CAAC,CAAC;AAC5F;;AAGF,iBAAe,KAAK;AACpB,eAAa,MAAM,CAAC,GAAG,GAAG;GAAE,MAAM;GAAU,SAAS;GAA+B,CAAC,CAAC;AAEtF,MAAI;GACF,MAAM,mBAAmB,SACtB,QAAQ,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,YAAY,CAC1D,KAAK,MAAM,GAAG,EAAE,SAAS,SAAS,SAAS,YAAY,IAAI,EAAE,UAAU,CACvE,KAAK,KAAK;GAEb,IAAI,UAAU;AACd,cAAW,MAAM,SAAS,OAAO,OAC/B,8FAA8F,oBAC9F;IAAE,WAAW;IAAK,QAAQ;IAAqC,CAChE,CACC,YAAW;AAGb,aAAU,eAAe,QAAQ;AAGjC,eAAY,CACV;IAAE,MAAM;IAAU,SAAS,kCAAkC;IAAW,EACxE;IAAE,MAAM;IAAU,SAAS;IAAsD,CAClF,CAAC;WACK,GAAG;AACV,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS,sBAAsB;IAAK,CAAC,CAAC;;AAGpF,iBAAe,MAAM;;CAIvB,MAAM,0BAA0B;EAC9B,IAAI,UAAU,oCAAmB,IAAI,MAAM,EAAC,gBAAgB,CAAC;AAC7D,aAAW,aAAa,WAAW,MAAM,KAAK;AAC9C,aAAW,cAAc,WAAW,MAAM,UAAU;AACpD,aAAW;AAEX,OAAK,MAAM,OAAO,SAChB,KAAI,IAAI,SAAS,OACf,YAAW,YAAY,IAAI,QAAQ;WAC1B,IAAI,SAAS,YACtB,YAAW,eAAe,IAAI,QAAQ;WAC7B,IAAI,SAAS,SACtB,YAAW,IAAI,IAAI,QAAQ;AAG/B,SAAO;;CAIT,MAAM,iBAAiB;EACrB,MAAM,UAAU,mBAAmB;EACnC,MAAM,EAAE,2BAAiB,qBAAqB;EAC9C,MAAM,WAAW,QAAQ;EAQzB,MAAM,OAAOC,OALX,aAAa,WACT,WACA,aAAa,UACX,SACA,+BACgB,QAAa;AACnC,OAAI,IACF,cAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS,gBAAgB;IAAO,CAAC,CAAC;OAE9E,cAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS;IAA4B,CAAC,CAAC;IAErF;AACF,OAAK,OAAO,MAAM,QAAQ;AAC1B,OAAK,OAAO,KAAK;;CAInB,MAAM,YAAY,aAAsB;EACtC,MAAM,6BAAY,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,SAAS,IAAI,CAAC,MAAM,GAAG,GAAG;EAC7E,MAAM,QAAQ,YAAY,eAAe,UAAU;EACnD,MAAM,QAAQ,KAAK,KAAK,QAAQ,KAAK,EAAE,MAAM;EAE7C,MAAM,UAAU,mBAAmB;AAEnC,MAAI;AACF,MAAG,cAAc,OAAO,QAAQ;AAChC,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS,iBAAiB;IAAS,CAAC,CAAC;WAC1E,GAAG;AACV,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS,iBAAiB;IAAK,CAAC,CAAC;;;CAKjF,MAAM,iBAAiB,YAAY;AACjC,MAAI,aAAa,WAAY;AAE7B,MAAI;AAEF,OAAI,CADiB,MAAM,OAAO,uBAAuB,EACtC;AACjB,iBAAa,MAAM,CACjB,GAAG,GACH;KACE,MAAM;KACN,SACE;KAGH,CACF,CAAC;AACF;;AAIF,SAAM,OAAO,SAAS;GAGtB,MAAM,UAAU,MAAM,OAAO,6BAA6B;IACxD,eAAe;IACf,UAAU,MAAM,QAAQ;AAEtB,wBAAmB,SAAU,OAAO,GAAG,KAAK,GAAG,SAAS,KAAM;;IAEhE,eAAe,aAAa;AAE1B,kBAAa,MAAM;MACjB,MAAM,cAAc,CAAC,GAAG,EAAE;MAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,UAAI,WAAW,KAAK,YAAY,SAAS,SAAS,UAAU;OAC1D,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,mBAAmB,WAAW,IAAK;AAC5E,mBAAY,WAAW;QACrB,MAAM;QACN,SAAS,WACL,gBAAgB,QAAQ,OAAO,aAC/B,gBAAgB,QAAQ;QAC7B;;AAEH,aAAO;OACP;;IAEJ,UAAU,QAAQ;AAChB,aAAQ,MAAM,wBAAwB,IAAI;;IAE7C,CAAC;AAEF,uBAAoB,UAAU;AAC9B,WAAQ,OAAO;AAGf,UAAO,UAAU,IAAI,oBAAoB;IACvC,eAAe,UAAU;AACvB,aAAQ,UAAU,MAAM;;IAE1B,eAAe;IAChB,CAAC;AAEF,SAAM,OAAO,QAAQ,OAAO;AAE5B,sBAAmB,UAAU,KAAK,KAAK;AACvC,gBAAa,KAAK;AAClB,uBAAoB,EAAE;AACtB,sBAAmB,eAAe;AAClC,qBAAkB,GAAG;AAGrB,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS;IAAkC,CAAC,CAAC;GAGzF,MAAM,gBAAgB,kBAAkB;AACtC,QAAI,CAAC,OAAO,SAAS,WAAW,EAAE;AAChC,mBAAc,cAAc;AAC5B;;IAEF,MAAM,UAAU,KAAK,OAAO,KAAK,KAAK,GAAG,mBAAmB,WAAW,IAAK;AAC5E,wBAAoB,QAAQ;AAI5B,QAAI,EADsB,oBAAoB,SAAS,eAAe,IAAI,KAClD;AACtB,wBAAmB,gBAAgB,QAAQ,GAAG;AAC9C,kBAAa,MAAM;MACjB,MAAM,cAAc,CAAC,GAAG,EAAE;MAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,UAAI,WAAW,KAAK,YAAY,SAAS,SAAS,SAChD,aAAY,WAAW;OACrB,MAAM;OACN,SAAS,gBAAgB,QAAQ;OAClC;AAEH,aAAO;OACP;UAEF,oBAAmB,gBAAgB,QAAQ,kBAAkB;MAE9D,IAAI;AAGP,GAAC,OAAO,QAAgB,iBAAiB;WAClCC,KAAU;AACjB,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAU,SAAS,oBAAoB,IAAI;IAAW,CAAC,CAAC;AAC1F,gBAAa,MAAM;AACnB,UAAO,UAAU;AACjB,uBAAoB,UAAU;;;CAMlC,MAAM,gBAAgB,OAAO,aAAa,UAAU;AAClD,MAAI,CAAC,aAAa,CAAC,OAAO,QAAS;EAEnC,MAAM,MAAM,OAAO;EACnB,MAAM,UAAU,oBAAoB;AACpC,SAAO,UAAU;AACjB,sBAAoB,UAAU;AAG9B,MAAI,eAAe,SAAS;AAC1B,gBAAa,eAAe,QAAQ;AACpC,kBAAe,UAAU;;AAI3B,MAAK,IAAY,eACf,eAAe,IAAY,eAAe;AAG5C,qBAAmB,gBAAgB;AAEnC,MAAI;GAEF,MAAM,SAAS,MAAM,IAAI,MAAM;GAC/B,MAAM,cAAc,OAAO,SAAS,QAAQ,EAAE;AAG9C,gBAAa,MAAM;IACjB,MAAM,cAAc,CAAC,GAAG,EAAE;IAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,QAAI,WAAW,KAAK,YAAY,SAAS,SAAS,SAChD,aAAY,WAAW;KACrB,MAAM;KACN,SAAS,YAAY,YAAY;KAClC;AAEH,WAAO;KACP;GAGF,IAAI,OAAO;AACX,OAAI,SAAS;AACX,WAAO,MAAM,QAAQ,MAAM;AAC3B,WAAO,KAAK,MAAM;;AAIpB,OAAI,CAAC,QAAQ,OAAO,MAAM,SAAS,GAAG;AACpC,uBAAmB,kBAAkB;AAErC,YADsB,MAAM,OAAO,WAAW,OAAO,MAAM,EACtC,KAAK,MAAM;;AAGlC,OAAI,MAAM;AACR,QAAI,YAAY;AAEd,kBAAa,MAAM;MACjB,MAAM,cAAc,CAAC,GAAG,EAAE;MAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,UAAI,WAAW,KAAK,YAAY,SAAS,SAAS,SAChD,aAAY,KAAK;AAEnB,aAAO;OACP;AAEF,kBAAa,MAAM;AACnB,wBAAmB,GAAG;AACtB,yBAAoB,EAAE;AACtB,uBAAkB,GAAG;AAErB,WAAM,aAAa,KAAK;AACxB;;AAGF,aAAS,KAAK;AACd,iBAAa,MAAM;KACjB,MAAM,cAAc,CAAC,GAAG,EAAE;KAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,SAAI,WAAW,KAAK,YAAY,SAAS,SAAS,SAChD,aAAY,WAAW;MACrB,MAAM;MACN,SAAS,iBAAiB,KAAK;MAChC;AAEH,YAAO;MACP;SAEF,cAAa,MAAM;IACjB,MAAM,cAAc,CAAC,GAAG,EAAE;IAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,QAAI,WAAW,KAAK,YAAY,SAAS,SAAS,SAChD,aAAY,WAAW;KACrB,MAAM;KACN,SAAS;KACV;AAEH,WAAO;KACP;WAEGA,KAAU;AACjB,gBAAa,MAAM;IACjB,MAAM,cAAc,CAAC,GAAG,EAAE;IAC1B,MAAM,UAAU,YAAY,SAAS;AACrC,QAAI,WAAW,KAAK,YAAY,SAAS,SAAS,SAChD,aAAY,WAAW;KACrB,MAAM;KACN,SAAS,wBAAwB,IAAI;KACtC;AAEH,WAAO;KACP;;AAGJ,eAAa,MAAM;AACnB,qBAAmB,GAAG;AACtB,sBAAoB,EAAE;AACtB,oBAAkB,GAAG;;CAYvB,MAAM,eAAe,OAAO,UAAkB;AAC5C,MAAI,CAAC,MAAM,MAAM,IAAI,cAAc,YACjC;AAIF,MAAI,MAAM,WAAW,IAAI,EAAE;GACzB,MAAM,QAAQ,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI;GACvC,MAAM,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM;GACzC,MAAM,MAAM,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI;AAEpC,OAAI,QAAQ,WAAW,QAAQ,OAAO;AACpC,gBAAY,EAAE,CAAC;AACf,aAAS,GAAG;AACZ;;AAEF,OAAI,QAAQ,QAAQ;AAClB,wBAAoB,KAAK;AACzB,aAAS,GAAG;AACZ;;AAEF,OAAI,QAAQ,eAAe,QAAQ,YAAY;AAC7C,aAAS,GAAG;AACZ,UAAM,uBAAuB;AAC7B;;AAEF,OAAI,QAAQ,iBAAiB,QAAQ,SAAS;AAC5C,aAAS,GAAG;AACZ,QAAI;AACF,WAAM,OAAO,YAAY;KACzB,MAAM,MAAM,MAAM,OAAO,gBAAgB;KACzC,MAAM,UAAU,MAAM,SAAS,IAAI,OAAO,QAAQ,EAAE,CAAC,OAAO;AAC5D,kBAAa,MAAM,CACjB,GAAG,GACH;MAAE,MAAM;MAAU,SAAS,mBAAmB,QAAQ;MAAgC,CACvF,CAAC;aACK,KAAK;AACZ,kBAAa,MAAM,CACjB,GAAG,GACH;MAAE,MAAM;MAAU,SAAS,4BAA4B;MAAO,CAC/D,CAAC;;AAEJ;;AAEF,OAAI,QAAQ,YAAY,QAAQ,OAAO;AACrC,aAAS,GAAG;AACZ,QAAI;KACF,MAAM,MAAM,MAAM,OAAO,gBAAgB;AACzC,SAAI,IACF,cAAa,MAAM,CACjB,GAAG,GACH;MACE,MAAM;MACN,SAAS,cAAc,IAAI,OAAO,QAAQ,EAAE,CAAC,OAAO,IAAI,QAAQ,QAAQ,EAAE,CAAC,MAAM,IAAI,YAAY,QAAQ,EAAE,CAAC;MAC7G,CACF,CAAC;SAEF,cAAa,MAAM,CACjB,GAAG,GACH;MAAE,MAAM;MAAU,SAAS;MAA8C,CAC1E,CAAC;aAEG,KAAK;AACZ,kBAAa,MAAM,CACjB,GAAG,GACH;MAAE,MAAM;MAAU,SAAS,2BAA2B;MAAO,CAC9D,CAAC;;AAEJ;;AAEF,OAAI,QAAQ,QAAQ;AAClB,aAAS,GAAG;AACZ,aAAS,OAAO,OAAU;AAC1B;;AAEF,OAAI,QAAQ,QAAQ;IAClB,MAAMC,mBAAiB,OAAO,kBAAkB,IAAI;AACpD,iBAAa,MAAM,CACjB,GAAG,GACH;KACE,MAAM;KACN,SAAS;;;;;;;mCAOcA,mBAAiB,MAAM,0BAA0B;;;;;;;;;;;;;oBAahEA,mBAAiB,MAAM;KAChC,CACF,CAAC;AACF,aAAS,GAAG;AACZ;;AAIF,OAAI,QAAQ,WAAW,KAAK;IAC1B,MAAM,SAAS,iBAAiB,IAAI;AACpC,QAAI,OAAO,WAAW,OAAO,aAAa;AACxC,wBAAmB,SAAS,CAAC,GAAG,MAAM,OAAO,YAAa,CAAC;KAC3D,MAAM,QAAQ,eAAe,SAAS;AACtC,kBAAa,MAAM,CACjB,GAAG,GACH;MACE,MAAM;MACN,SAAS,GAAG,OAAO,QAAQ,IAAI,MAAM,QAAQ,QAAQ,IAAI,MAAM,GAAG;MACnE,CACF,CAAC;eACO,OAAO,QAChB,cAAa,MAAM,CAAC,GAAG,GAAG;KAAE,MAAM;KAAU,SAAS,OAAO;KAAS,CAAC,CAAC;AAEzE,aAAS,GAAG;AACZ;;AAIF,OAAI,QAAQ,UAAU;AACpB,QAAI,eAAe,WAAW,EAC5B,cAAa,MAAM,CACjB,GAAG,GACH;KAAE,MAAM;KAAU,SAAS;KAAoD,CAChF,CAAC;QAEF,cAAa,MAAM,CACjB,GAAG,GACH;KACE,MAAM;KACN,SAAS,MAAM,eAAe,OAAO;KACtC,CACF,CAAC;AAEJ,aAAS,GAAG;AACZ;;AAIF,OAAI,QAAQ,gBAAgB;IAC1B,MAAM,QAAQ,eAAe;AAC7B,sBAAkB,EAAE,CAAC;AACrB,iBAAa,MAAM,CACjB,GAAG,GACH;KACE,MAAM;KACN,SAAS,QAAQ,IAAI,eAAe,MAAM,sBAAsB;KACjE,CACF,CAAC;AACF,aAAS,GAAG;AACZ;;AAIF,OAAI,QAAQ,UAAU,KAAK;AACzB,aAAS,GAAG;AACZ,kBAAc,KAAK;AACnB,QAAI;KACF,MAAM,SAAS,MAAM,gBAAgB,eAAe,EAAE,OAAO,KAAK,CAAC;AACnE,kBAAa,MAAM,CACjB,GAAG,GACH;MACE,MAAM;MACN,SAAS,WAAW,IAAI,MAAM,OAAO,MAAM,GAAG,IAAI,GAAG,OAAO,SAAS,MAAM,QAAQ;MACpF,CACF,CAAC;aACK,GAAG;AACV,kBAAa,MAAM,CAAC,GAAG,GAAG;MAAE,MAAM;MAAU,SAAS,UAAU;MAAK,CAAC,CAAC;;AAExE,kBAAc,MAAM;AACpB;;;EAIJ,MAAM,cAAc,MAAM,MAAM;AAChC,WAAS,GAAG;AAGZ,MAAI,QAAQ,GAAG,GAAG,KAAK,YACrB,aAAY,MAAM,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,YAAY,CAAC;AAEnD,kBAAgB,GAAG;AACnB,gBAAc,GAAG;EAGjB,MAAM,gBAAgB,CAAC,GAAG,eAAe;AAEzC,eAAa,MAAM,CACjB,GAAG,GACH;GACE,MAAM;GACN,SAAS;GACT,QAAQ,cAAc,SAAS,IAAI,gBAAgB;GACpD,CACF,CAAC;AACF,gBAAc,KAAK;AACnB,qBAAmB,GAAG;AAGtB,MAAI,cAAc,SAAS,EACzB,mBAAkB,EAAE,CAAC;AAGvB,MAAI;GACF,IAAI,eAAe;GACnB,MAAM,cAAc,WAAW;GAC/B,MAAM,YAAY,YAAY,KAAK;GACnC,IAAI,aAAa;GAGjB,IAAI,eAAe,YAAY;AAE/B,OAAI,UAGF,gBAAe,qBAFD,oBAAoB,CAEQ;AAG5C,cAAW,MAAM,SAAS,OAAO,OAAO,aAAa;IACnD,UAAU;IACV,WAAW;IACX,QAAQ;IAER,QACE,cAAc,SAAS,IAAI,cAAc,KAAK,SAAS,EAAE,QAAQ,KAAK,EAAE,GAAG;IAC9E,CAAC,EAAE;AACF,oBAAgB;AAChB,kBAAc;AACd,uBAAmB,aAAa;;GAKlC,MAAM,SADU,YAAY,KAAK,GACR;GACzB,MAAM,YAAY,SAAS,IAAK,aAAa,SAAU,MAAO;AAC9D,0BAAuB;IAAE,WAAW;IAAY;IAAQ;IAAW,CAAC;GAEpE,IAAI,WAAW,eAAe,aAAa;AAG3C,OAAI,WAAW;IACb,MAAM,WAAW,cAAc,SAAS;AACxC,QAAI,UAAU;AAEZ,kBAAa,MAAM,CACjB,GAAG,GACH;MACE,MAAM;MACN,SAAS,eAAe,SAAS,KAAK,GAAG,KAAK,UAAU,SAAS,OAAO,CAAC;MAC1E,CACF,CAAC;AACF,wBAAmB,GAAG;KAGtB,MAAM,aAAa,MAAM,gBAAgB,SAAS,MAAM,SAAS,OAAO;KAGxE,MAAM,kBACJ,WAAW,SAAS,MAAM,GAAG,WAAW,MAAM,GAAG,IAAI,CAAC,qBAAqB;AAC7E,kBAAa,MAAM,CAAC,GAAG,GAAG;MAAE,MAAM;MAAU,SAAS,eAAe;MAAmB,CAAC,CAAC;KAGzF,IAAI,WAAW;AACf,gBAAW,MAAM,SAAS,OAAO,OAC/B,0BAA0B,WAAW,8CAA8C,YAAY,IAC/F;MAAE,WAAW;MAAK,QAAQ;MAAyD,CACpF,EAAE;AACD,kBAAY;AACZ,yBAAmB,SAAS;;AAE9B,gBAAW,eAAe,SAAS;AAGnC,SAAI,CAAC,SAAS,MAAM,CAClB,YAAW;;;GAKjB,MAAM,aAAa,aAAa,MAAM,6BAA6B;GACnE,IAAI,WAAW;AAEf,OAAI,cAAc,aAEhB,YAAW,eAAe,WAAW,GAAG,CAAC,MAAM;AAGjD,gBAAa,MAAM;IACjB,MAAM,cAAc,CAAC,GAAG,EAAE;AAC1B,QAAI,YAAY,aACd,aAAY,KAAK;KAAE,MAAM;KAAY,SAAS;KAAU,CAAC;AAE3D,gBAAY,KAAK;KAAE,MAAM;KAAa,SAAS;KAAU,CAAC;AAC1D,WAAO;KACP;AAGF,OAAI,aAAa,SAAS,MAAM,CAC9B,eAAc,SAAS;WAElB,OAAO;AACd,gBAAa,MAAM,CAAC,GAAG,GAAG;IAAE,MAAM;IAAa,SAAS,UAAU;IAAS,CAAC,CAAC;YACrE;AACR,iBAAc,MAAM;AACpB,sBAAmB,GAAG;;;CAM1B,MAAM,eAAe,SAA0B;EAC7C,MAAM,UAAU,KAAK,MAAM;AAC3B,MAAI,QAAQ,SAAS,EAAG,QAAO;AAI/B,OADmB,QAAQ,MAAM,kBAAkB,IAAI,EAAE,EAC1C,SAAS,QAAQ,SAAS,GAAK,QAAO;AAGrD,MAAI,YAAY,KAAK,QAAQ,CAAE,QAAO;AACtC,MAAI,iBAAiB,KAAK,QAAQ,CAAE,QAAO;AAI3C,OADiB,QAAQ,MAAM,eAAe,IAAI,EAAE,EACvC,SAAS,QAAQ,SAAS,GAAK,QAAO;AAGnD,MAAI,eAAe,KAAK,QAAQ,CAAE,QAAO;AAEzC,SAAO;;CAGT,MAAM,gBAAgB,OAAO,SAAiB;AAE5C,MAAI,YAAY,KAAK,CACnB;AAGF,cAAY,KAAK;AACjB,MAAI;GACF,MAAM,EAAE,yBAAa,MAAM,OAAO;GAClC,MAAM,EAAE,gCAAe,6BAAe,MAAM,OAAO;GACnD,MAAM,EAAE,qBAAW,MAAM,OAAO;GAChC,MAAM,EAAE,iBAAS,MAAM,OAAO;GAK9B,IAAIC;GACJ,MAAM,YAAY,KAAK,MAAM;AAE7B,OAAI,UAAU,SAAS,IACrB,UAAS,CAAC,UAAU;YACX,UAAU,SAAS,OAAO,CACnC,UAAS,UAAU,MAAM,QAAQ,CAAC,QAAQ,MAAM,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YACnE,UAAU,SAAS,KAAK,CACjC,UAAS,UAAU,MAAM,MAAM,CAAC,QAAQ,MAAM,EAAE,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QACrE;AAEL,aAAS,EAAE;IACX,IAAI,UAAU;AACd,SAAK,MAAM,YAAY,UAAU,MAAM,gBAAgB,CACrD,KAAI,QAAQ,SAAS,SAAS,SAAS,OAAO,QAAQ,SAAS,GAAG;AAChE,YAAO,KAAK,QAAQ,MAAM,CAAC;AAC3B,eAAU;UAEV,aAAY,UAAU,MAAM,MAAM;AAGtC,QAAI,QAAQ,MAAM,CAAE,QAAO,KAAK,QAAQ,MAAM,CAAC;AAC/C,aAAS,OAAO,QAAQ,MAAM,CAAC,YAAY,EAAE,CAAC;;AAGhD,QAAK,MAAM,SAAS,QAAQ;AAC1B,QAAI,CAAC,MAAM,MAAM,CAAE;IAEnB,MAAM,SAAS,MAAM,OAAO,MAAM,OAAO;KAAE,OAAO;KAAY,OAAO;KAAK,CAAC;IAG3E,MAAM,WAAWC,OAAKC,UAAQ,EAAE,gBAAgB,KAAK,KAAK,CAAC,MAAM;IAGjE,MAAM,SAAS,OAAO,MAAM,KAAK,OAAO,MAAM,SAAS,EAAE;AACzD,WAAO,MAAM,QAAQ,EAAE;AACvB,WAAO,cAAc,KAAK,OAAO,MAAM,SAAS,GAAG,EAAE;AACrD,WAAO,MAAM,QAAQ,EAAE;AACvB,WAAO,MAAM,QAAQ,GAAG;AACxB,WAAO,cAAc,IAAI,GAAG;AAC5B,WAAO,cAAc,GAAG,GAAG;AAC3B,WAAO,cAAc,GAAG,GAAG;AAC3B,WAAO,cAAc,OAAO,YAAY,GAAG;AAC3C,WAAO,cAAc,OAAO,aAAa,GAAG,GAAG;AAC/C,WAAO,cAAc,GAAG,GAAG;AAC3B,WAAO,cAAc,IAAI,GAAG;AAC5B,WAAO,MAAM,QAAQ,GAAG;AACxB,WAAO,cAAc,OAAO,MAAM,SAAS,GAAG,GAAG;AAEjD,SAAK,IAAI,IAAI,GAAG,IAAI,OAAO,MAAM,QAAQ,KAAK;KAC5C,MAAM,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,OAAO,MAAM,GAAG,CAAC;AACpD,YAAO,aAAa,KAAK,MAAM,IAAI,MAAM,EAAE,KAAK,IAAI,EAAE;;AAExD,oBAAc,UAAU,OAAO;AAG/B,QAAI;KACF,MAAM,WAAW,QAAQ;AACzB,SAAI,aAAa,SACf,YAAS,WAAW,SAAS,IAAI,EAAE,OAAO,UAAU,CAAC;cAC5C,aAAa,QACtB,KAAI;AACF,iBAAS,UAAU,SAAS,IAAI,EAAE,OAAO,UAAU,CAAC;aAC9C;AACN,iBAAS,WAAW,SAAS,IAAI,EAAE,OAAO,UAAU,CAAC;;cAE9C,aAAa,QACtB,YAAS,iDAAiD,SAAS,iBAAiB,EAClF,OAAO,UACR,CAAC;cAEI;AACR,SAAI;AACF,mBAAW,SAAS;aACd;;;WAKL,KAAK,WAEJ;AACR,eAAY,MAAM;;;CAItB,MAAM,YAAY,MAAM,KAAK,SAAS;EACpC;EACA,OAAO,IAAI,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK;EACtD,OAAO;EACR,EAAE;AAEH,QACE,qBAAC;EAAI,eAAc;EAAS,UAAU;;GAEnC,oBACC,qBAAC;IACC,aAAY;IACZ,aAAY;IACZ,eAAc;IACd,SAAS;IACT,UAAU;IACV,UAAU;;KAEV,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAI,SAAS;gBACZ,oBAAC;OACC,cAAc,MAAM,QAAQ,KAAK;OACjC,gBAAgB,EAAE,YAAY,YAAY;QACxC,MAAM,UAAU,UAAU,MAAM,MAAM,EAAE,UAAU,MAAM,EAAE;QAC1D,MAAM,WAAW,UAAU,WAAW,WAAW;AACjD,eACE,qBAAC,kBACC,qBAAC;SAAK,MAAM;SAAY,OAAO,aAAa,SAAS;oBAClD,aAAa,OAAO,MACpB;UACI,EACN,cAAc,YAAY,qBAAC;SAAK;oBAAS,OAAI,SAAS;UAAmB,IACtE;;OAGV,OAAO;OACP,WAAW,SAAS;AAClB,gBAAQ,KAAK,MAAkB;AAC/B,4BAAoB,MAAM;;QAE5B;OACE;KACN,oBAAC;MAAK;gBAAS;OAAmB;;KAC9B;GAIP,mBACC,qBAAC;IACC,aAAY;IACZ,aAAY;IACZ,eAAc;IACd,SAAS;IACT,UAAU;IACV,UAAU;IACV,OAAO;;KAEP,oBAAC;MAAK;MAAK,OAAM;gBAAS;OAEnB;KACP,qBAAC;MAAI,eAAc;MAAS,SAAS;;OACnC,oBAAC;QAAK;kBAAS;SAAyD;OACxE,qBAAC;QAAI,WAAW;QAAG,OAAO;mBACxB,oBAAC;SAAK,OAAM;mBAAS;UAAY,EACjC,oBAAC;SAEC,UAAU;SACV,WAAW,cAAc;AACvB,cAAI,CAAC,UAAU,MAAM,EAAE;AACrB,8BAAmB,MAAM;AACzB,8BAAmB,MAAM,IAAI,EAAE;AAC/B;;UAGF,MAAM,SAAS,iBAAiB,UAAU;AAC1C,cAAI,OAAO,WAAW,OAAO,aAAa;AACxC,8BAAmB,SAAS,CAAC,GAAG,MAAM,OAAO,YAAa,CAAC;AAC3D,wBAAa,MAAM,CAAC,GAAG,GAAG;YAAE,MAAM;YAAU,SAAS,OAAO;YAAS,CAAC,CAAC;qBAC9D,OAAO,QAChB,cAAa,MAAM,CAAC,GAAG,GAAG;WAAE,MAAM;WAAU,SAAS,OAAO;WAAS,CAAC,CAAC;AAGzE,6BAAmB,MAAM;AACzB,4BAAkB,GAAG;AACrB,6BAAmB,MAAM,IAAI,EAAE;;SAEjC,aAAY;SACZ,OAAO;WAtBF,eAuBL;SACE;OACL,kBACC,oBAAC;QAAI,WAAW;kBACd,qBAAC;SAAK;SAAS,MAAK;oBAAe,UAC1B;UACF;SACH;OAEP,eAAe,SAAS,KACvB,oBAAC;QAAI,WAAW;kBACd,qBAAC;SAAK,OAAM;;UAAQ;UAAG,eAAe;UAAO;;UAAyB;SAClE;;OAEJ;KACN,oBAAC;MAAK;gBAAS;OAAuD;;KAClE;GAIP,EAAE,oBAAoB,oBACrB,qBAAC;IAAI,gBAAe;IAAgB,UAAU;eAC5C,qBAAC;KAAK,OAAM;;MAAO;MACf,WAAW,MAAM;MAAO;MAAG,WAAW,MAAM;MAC7C,aAAa,oBAAC;OAAK,OAAM;iBAAO;QAAc;MAC/C,oBAAC;OAAK;iBAAS;QAAU;MAExB,mBACC;OACE,qBAAC;QACC,iBAAiB,mBAAmB,IAAI,YAAY;QACpD,MAAM,mBAAmB;QACzB,OAAO,mBAAmB,IAAI,UAAU,eAAe,YAAY;mBACpE,UACQ,eAAe,OAAO;SACxB;OACP,oBAAC;QAAK;kBAAS;SAAQ;OACvB,qBAAC;QACC,iBAAiB,mBAAmB,IAAI,UAAU;QAClD,MAAM,mBAAmB;QACzB,OAAO,mBAAmB,IAAI,UAAU,YAAY,UAAU;mBAC/D,UACQ,YAAY,OAAO;SACrB;OACP,oBAAC;QAAK;kBAAS;SAAQ;OACvB,qBAAC;QACC,iBAAiB,mBAAmB,IAAI,SAAS;QACjD,MAAM,mBAAmB;QACzB,OAAO,mBAAmB,IAAI,UAAU,YAAY,SAAS;mBAC9D,UACQ,YAAY,OAAO;SACrB;OACP,oBAAC;QAAK;kBAAS;SAAQ;OACtB,kBACC,4CACE,qBAAC;QACC,iBAAiB,mBAAmB,IAAI,SAAS;QACjD,MAAM,mBAAmB;QACzB,OACE,mBAAmB,IAAI,UAAU,eAAe,SAAS,IAAI,SAAS;mBAEzE,SACO,eAAe,SAAS,IAAI,IAAI,eAAe,WAAW;SAC3D,EACP,oBAAC;QAAK;kBAAS;SAAQ,IACtB;OAEL,oBAAC;QACC,iBAAiB,oBAAoB,iBAAiB,IAAI,KAAK,QAAQ;QACvE,MAAM,oBAAoB,iBAAiB,IAAI;QAC/C,OACE,oBAAoB,iBAAiB,IAAI,KACrC,UACA,YACE,QACA;kBAGP,YAAY,aAAa,iBAAiB,KAAK;SAC3C;OACP,oBAAC;QAAK;kBAAS;SAAuB;UACrC,GAEH;OACE,qBAAC;QAAK,OAAO,eAAe,YAAY;mBAAQ,UACvC,eAAe,OAAO;SACxB;OACP,oBAAC;QAAK;kBAAS;SAAQ;OACvB,qBAAC;QAAK,OAAO,YAAY,UAAU;mBAAQ,UAAO,YAAY,OAAO;SAAa;OAClF,oBAAC;QAAK;kBAAS;SAAQ;OACvB,qBAAC;QAAK,OAAO,YAAY,SAAS;mBAAQ,UAAO,YAAY,OAAO;SAAa;OACjF,oBAAC;QAAK;kBAAS;SAAQ;OACtB,kBACC,4CACE,qBAAC;QAAK,OAAO,eAAe,SAAS,IAAI,SAAS;mBAAQ,SAClD,eAAe,SAAS,IAAI,IAAI,eAAe,WAAW;SAC3D,EACP,oBAAC;QAAK;kBAAS;SAAQ,IACtB;OAEL,oBAAC;QAAK,OAAO,YAAY,QAAQ;kBAC9B,YAAY,aAAa,iBAAiB,KAAK;SAC3C;UACN;MAEL,oBAAC;OAAK;iBAAS;QAAe;;MACzB,EACP,qBAAC,kBACE,kBAAkB,UAAa,gBAAgB,KAC9C;KACE,oBAAC;MAAK,OAAM;gBAAU,cAAc,QAAQ,EAAE;OAAQ;KACtD,oBAAC;MAAK;gBAAS;OAAa;KAC3B,iBAAiB,UAAa,eAAe,KAC5C;MACE,oBAAC;OAAK;iBAAS;QAAa;MAC5B,oBAAC;OAAK,OAAM;iBAAQ,aAAa,QAAQ,EAAE;QAAQ;MACnD,oBAAC;OAAK;iBAAS;QAAQ;SACtB;KAEL,oBAAC;MAAK;gBAAS;OAAQ;QACtB,EAEL,qBAAC;KAAK,OAAO,iBAAiB,KAAK,QAAQ,iBAAiB,KAAK,WAAW;;MACzE,cAAc,gBAAgB;MAAC;MAAE,WAAW,gBAAgB;MAAC;MAC7D;MAAe;;MACX,IACH;KACF;GAIR,qBAAC;IAAI,eAAc;IAAS,UAAU;IAAG,WAAU;;KAChD,SAAS,WAAW,KAAK,CAAC,cACzB,oBAAC;MAAI,SAAS;gBACZ,oBAAC;OAAK,OAAM;iBAAO;QAA4D;OAC3E;KAGP,SAAS,KAAK,KAAK,MAClB,qBAAC;MAAY,cAAc;MAAG,UAAU;;OACrC,IAAI,SAAS,UACZ,qBAAC;QACC,qBAAC;SAAK;SAAK,OAAM;oBAAO,QACjB;UACA;QACN,IAAI,UAAU,IAAI,OAAO,SAAS,KACjC,qBAAC;SAAK,OAAM;;UAAS;UAChB,IAAI,OAAO,SAAS,IAAI,IAAI,IAAI,OAAO,WAAW;UAAI;;UACpD;QAET,oBAAC,kBAAM,IAAI,UAAe;WACtB;OAEP,IAAI,SAAS,cACZ,qBAAC;QAAI,eAAc;mBACjB,oBAAC;SAAK,OAAM;SAAO;SAAS;mBACzB;UACI,EACP,oBAAC;SAAK;SAAS;mBACZ,IAAI;UACA;SACH;OAEP,IAAI,SAAS,eACZ,qBAAC,kBACC,qBAAC;QAAK;QAAK,OAAM;mBACd,WAAW,MAAM,QAAQ;SACrB,EACP,oBAAC;QAAI,eAAc;QAAS,UAAU;kBACnC,eAAe,IAAI,QAAQ;SACxB,IACF;OAEP,IAAI,SAAS,aACX,IAAI,QAAQ,WAAW,aAAa,GACnC,qBAAC;QAAK,OAAM;QAAO;;SAAS;SACxB;SACD,IAAI,QACF,QAAQ,gBAAgB,GAAG,CAC3B,QAAQ,eAAe,OAAO,CAC9B,QAAQ,aAAa,QAAQ;;SAC3B,GACL,IAAI,QAAQ,WAAW,KAAK,GAC9B,oBAAC;QAAI,aAAY;QAAO,aAAY;QAAS,cAAc;QAAG,UAAU;kBACtE,qBAAC;SAAK,OAAM;oBACT,IAAI,QAAQ,QAAQ,gBAAgB,GAAG,CAAC,MAAM,GAAG,IAAI,EACrD,IAAI,QAAQ,QAAQ,gBAAgB,GAAG,CAAC,SAAS,MAAM,QAAQ;UAC3D;SACH,GACJ,IAAI,QAAQ,WAAW,KAAK,GAC9B,qBAAC;QAAK,OAAM;QAAU;mBAAS,MAC1B,IAAI;SACF,GACL,IAAI,QAAQ,WAAW,WAAW,GACpC,qBAAC;QAAK,OAAM;QAAQ;mBAAS,MACxB,IAAI;SACF,GAEP,oBAAC;QAAK,OAAM;QAAS;kBAClB,IAAI;SACA;;QA7DH,EA+DJ,CACN;KAGD,cAAc,mBACb,oBAAC;MACC,SAAS;MACT,cAAc;MACd,QAAQ,WAAW,MAAM;OACzB;KAGH,cAAc,CAAC,mBACd,qBAAC;MAAI,UAAU;iBACb,oBAAC;OAAK,OAAM;iBACV,oBAAC,WAAQ,MAAK,SAAS;QAClB,EACP,oBAAC;OAAK,OAAM;iBAAO;QAAmB;OAClC;KAGP,eACC,qBAAC;MAAI,UAAU;iBACb,oBAAC;OAAK,OAAM;iBACV,oBAAC,WAAQ,MAAK,SAAS;QAClB,EACP,oBAAC;OAAK,OAAM;iBAAO;QAAsB;OACrC;;KAEJ;GAGL,iBAAiB,MAChB,oBAAC;IAAI,UAAU;cACb,qBAAC;KAAK,OAAM;;MAAM;MAAS;MAAe;;MAA0C;KAChF;GAIP,CAAC,mBACA,qBAAC;IACC,aAAa,cAAc,cAAc,SAAS;IAClD,aAAY;IACZ,UAAU;;KAET,eAAe,SAAS,KACvB,qBAAC;MAAK,OAAM;;OAAS;OAChB,eAAe,SAAS,IAAI,IAAI,eAAe,WAAW;OAAI;;OAC5D;KAET,oBAAC;MAAK,OAAM;gBAAO;OAAY;KAC/B,oBAAC;MACC,UAAU;MACV,UAAU;MACV,aACE,YACI,MAAM,mBAAmB,mBACzB,aACE,kBACA,cACE,mBACA,eAAe,SAAS,IACtB,0BACA,mBACE,8CACA;MAEd,OAAO;OACP;;KACE;;GAEJ;;;;;ACzuDV,SAASC,kBAAgB,MAAgC;AACvD,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,WAAW,GAAG,UAAU;EAC9B,IAAIC;AAEJ,MAAI,aAAa,SACf,OAAM;WACG,aAAa,QACtB,OAAM;WACG,aAAa,QACtB,OAAM;OACD;AACL,WAAQ,MAAM;AACd;;EAGF,MAAM,OAAO,KAAK,MAAM,QAAQ;AAC9B,WAAQ,CAAC,IAAI;IACb;AACF,OAAK,OAAO,MAAM,KAAK;AACvB,OAAK,OAAO,KAAK;GACjB;;AAIJ,MAAMC,kBAA0C;CAC9C,YAAY;CACZ,YAAY;CACZ,QAAQ;CACR,MAAM;CACN,IAAI;CACJ,MAAM;CACN,KAAK;CACL,GAAG;CACH,MAAM;CACN,KAAK;CACL,OAAO;CACP,QAAQ;CACR,KAAK;CACL,MAAM;CACN,OAAO;CACP,MAAM;CACN,KAAK;CACL,MAAM;CACN,MAAM;CACN,UAAU;CACX;AAQD,SAAgB,SAAS,EAAE,UAAyB;CAClD,MAAM,CAAC,OAAO,YAAY,SAAgB,QAAQ;CAClD,MAAM,CAAC,QAAQ,aAAa,SAAS,GAAG;CACxC,MAAM,CAAC,MAAM,WAAW,SAAS,GAAG;CACpC,MAAM,CAAC,UAAU,eAAe,SAAS,aAAa;CACtD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CACjE,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CAEjE,MAAM,aAAa,YAAY;AAC7B,MAAI,CAAC,QAAQ,UAAU,YAAY,MACjC;EAYF,MAAM,WAAW,GAPf,OACG,aAAa,CACb,QAAQ,eAAe,IAAI,CAC3B,QAAQ,UAAU,GAAG,CACrB,MAAM,GAAG,GAAG,IAAI,YAGQ,GADjB,gBAAgB,aAAa;EAEzC,MAAM,WAAW,KAAK,KAAK,QAAQ,KAAK,EAAE,SAAS;AAEnD,MAAI;AACF,MAAG,cAAc,UAAU,MAAM,QAAQ;AACzC,iBAAc,YAAY,WAAW;AACrC,oBAAiB,cAAc,KAAK,EAAE,IAAK;WACpC,KAAK;AACZ,iBAAc,UAAU,MAAM;AAC9B,oBAAiB,cAAc,KAAK,EAAE,IAAK;;;CAI/C,MAAM,aAAa,YAAY;AAC7B,MAAI,CAAC,QAAQ,UAAU,YAAY,MACjC;AAIF,gBADgB,MAAMF,kBAAgB,KAAK,GACnB,yBAAyB,iBAAiB;AAClE,mBAAiB,cAAc,KAAK,EAAE,IAAK;;CAG7C,MAAM,eAAe,OAAO,UAAkB;AAC5C,MAAI,CAAC,MAAM,MAAM,CACf;AAGF,YAAU,MAAM;AAChB,WAAS,aAAa;AACtB,WAAS,KAAK;AAEd,MAAI;GAaF,IAAI,iBAZW,MAAM,OAAO,SAAS,OAAO;IAC1C,WAAW;IACX,aAAa;IACb,QAAQ;;;;;;IAMT,CAAC,EAGyB;GAG3B,MAAM,YAAY,cAAc,MAAM,aAAa;AACnD,OAAI,UACF,aAAY,UAAU,GAAG;AAI3B,mBAAgB,cACb,QAAQ,6BAA6B,GAAG,CACxC,QAAQ,cAAc,GAAG,CACzB,QAAQ,WAAW,GAAG,CACtB,MAAM;AAET,WAAQ,cAAc;AACtB,YAAS,SAAS;WACX,KAAK;AACZ,YAAS,OAAO,IAAI,CAAC;AACrB,YAAS,SAAS;;;CAItB,MAAM,oBAAoB;AACxB,WAAS,QAAQ;AACjB,YAAU,GAAG;AACb,UAAQ,GAAG;AACX,WAAS,KAAK;;AAGhB,WAAU,OAAO,QAAQ;AACvB,MAAI,IAAI,UAAU,UAAU,SAC1B,cAAa;AAIf,MAAI,UAAU,YAAY,CAAC,OAAO;AAChC,OAAI,UAAU,OAAO,UAAU,IAC7B,aAAY;AAEd,OAAI,UAAU,OAAO,UAAU,IAC7B,aAAY;;GAGhB;AAEF,KAAI,UAAU,QACZ,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAI,cAAc;cACjB,oBAAC;KAAK;eAAK;MAAuB;KAC9B;GACN,oBAAC;IAAI,cAAc;cACjB,oBAAC;KAAK,OAAM;eAAO;MAA+C;KAC9D;GACN,qBAAC;IAAI,aAAY;IAAO,aAAY;IAAS,UAAU;eACrD,oBAAC;KAAK,OAAM;eAAO;MAAS,EAC5B,oBAAC;KACC,UAAU;KACV,UAAU;KACV,aAAY;KACZ,OAAO;MACP;KACE;GACN,oBAAC;IAAI,WAAW;cACd,oBAAC;KAAK,OAAM;KAAO;eAAS;MAErB;KACH;GACN,oBAAC;IAAK,OAAM;IAAO;cAAS;KAErB;GACP,oBAAC;IAAK,OAAM;IAAO;cAAS;KAErB;GACP,oBAAC;IAAK,OAAM;IAAO;cAAS;KAErB;;GACH;AAIV,KAAI,UAAU,aACZ,QACE,qBAAC;EAAI,SAAS;aACZ,oBAAC;GAAK,OAAM;aACV,oBAAC,WAAQ,MAAK,SAAS;IAClB,EACP,oBAAC,kBAAK,wBAA0B;GAC5B;AAKV,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,qBAAC;IAAI,cAAc;eACjB,oBAAC;KAAK;KAAK,OAAO,QAAQ,QAAQ;eAC/B,QAAQ,cAAc;MAClB,EACN,CAAC,SAAS,qBAAC;KAAK,OAAM;gBAAO,QAAK;MAAgB;KAC/C;GAEL,CAAC,SACA,oBAAC;IAAI,cAAc;cACjB,qBAAC;KAAK,OAAM;KAAO;gBAAS,YACjB;MACJ;KACH;GAGR,oBAAC;IACC,aAAa,QAAQ,QAAQ;IAC7B,aAAY;IACZ,eAAc;IACd,SAAS;cAET,oBAAC;KAAK,OAAO,QAAQ,QAAQ;KAAS,MAAK;eACxC,SAAS;MACL;KACH;GAGL,cACC,oBAAC;IAAI,WAAW;cACd,oBAAC;KAAK,OAAO,WAAW,WAAW,QAAQ,GAAG,QAAQ;eAAU;MAAkB;KAC9E;GAEP,cACC,oBAAC;IAAI,WAAW;cACd,oBAAC;KAAK,OAAO,WAAW,SAAS,SAAS,GAAG,QAAQ;eAAU;MAAkB;KAC7E;GAGR,oBAAC;IAAI,WAAW;cACb,QACC,oBAAC;KAAK;eAAS;MAAuB,GAEtC,qBAAC;KAAK;;MACJ,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAgB,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAC9D,oBAAC;OAAK,OAAM;iBAAO;QAAU;;;MACpC;KAEL;;GACF;;;;;AC1QV,SAAS,gBAAgB,MAAgC;AACvD,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,WAAW,GAAG,UAAU;EAC9B,IAAIG;AAEJ,MAAI,aAAa,SACf,OAAM;WACG,aAAa,QACtB,OAAM;WACG,aAAa,QACtB,OAAM;OACD;AACL,WAAQ,MAAM;AACd;;EAGF,MAAM,OAAO,KAAK,MAAM,QAAQ;AAC9B,WAAQ,CAAC,IAAI;IACb;AACF,OAAK,OAAO,MAAM,KAAK;AACvB,OAAK,OAAO,KAAK;GACjB;;AAiBJ,MAAMC,UAAQ;CACZ;EAAE,KAAK;EAAQ,KAAK;EAAG,OAAO;EAAQ,MAAM;EAAuC;CACnF;EAAE,KAAK;EAAe,KAAK;EAAG,OAAO;EAAW,MAAM;EAA4B;CAClF;EAAE,KAAK;EAAY,KAAK;EAAG,OAAO;EAAS,MAAM;EAAuB;CACzE;AAED,SAAgB,gBAAgB,EAAE,QAAQ,UAAgC;CACxE,MAAM,CAAC,MAAM,WAAW,SAAe,OAAO;CAC9C,MAAM,CAAC,OAAO,YAAY,SAAqB;EAC7C,MAAM;EACN,aAAa;EACb,UAAU;EACX,CAAC;CACF,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CACjE,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CAEjE,MAAM,iBACJ,SAAS,SAAS,IAAI,SAAS,gBAAgB,IAAI,SAAS,aAAa,IAAI;CAE/E,MAAM,eAAe,OAAO,UAAkB;AAC5C,MAAI,CAAC,MAAM,MAAM,CACf;AAGF,MAAI,SAAS,QAAQ;GAEnB,MAAM,YAAY,MACf,aAAa,CACb,QAAQ,QAAQ,IAAI,CACpB,QAAQ,eAAe,GAAG;AAC7B,aAAU,OAAO;IAAE,GAAG;IAAG,MAAM;IAAW,EAAE;AAC5C,YAAS,GAAG;AACZ,WAAQ,cAAc;aACb,SAAS,eAAe;AACjC,aAAU,OAAO;IAAE,GAAG;IAAG,aAAa;IAAO,EAAE;AAC/C,YAAS,GAAG;AACZ,WAAQ,WAAW;aACV,SAAS,YAAY;AAC9B,aAAU,OAAO;IAAE,GAAG;IAAG,UAAU;IAAO,EAAE;AAC5C,YAAS,GAAG;AACZ,WAAQ,aAAa;AACrB,SAAM,cAAc;IAAE,GAAG;IAAO,UAAU;IAAO,CAAC;;;CAKtD,MAAM,uBACJ,qBAAC;EAAI,eAAc;EAAS,cAAc;aAExC,oBAAC;GAAI,cAAc;aAChBA,QAAM,KAAK,GAAG,MAAM;IACnB,MAAM,aAAa,iBAAiB,EAAE;IACtC,MAAM,YAAY,SAAS,EAAE;AACV,IAAiB,EAAE;AAEtC,WACE,qBAAC;KAEC,oBAAC;MAAK,MAAM;MAAW,OAAO,aAAa,UAAU,YAAY,SAAS;gBACvE,aAAa,QAAQ,YAAY,QAAQ;OACrC;KACP,qBAAC;MAAK,MAAM;MAAW,OAAO,aAAa,UAAU,YAAY,SAAS;iBACvE,KACA,EAAE;OACE;KAEN,IAAIA,QAAM,SAAS,KAAK,oBAAC;MAAK,OAAO,aAAa,UAAU;gBAAQ;OAAY;SAVzE,EAAE,IAWN;KAER;IACE,GAGJ,MAAM,QAAQ,MAAM,gBACpB,qBAAC;GACC,aAAY;GACZ,aAAY;GACZ,eAAc;GACd,cAAc;GACd,UAAU;cAET,MAAM,QACL,qBAAC,mBACC,oBAAC;IAAK;cAAS;KAAa,EAC5B,oBAAC;IAAK,OAAM;cAAS,MAAM;KAAY,IAClC,EAER,MAAM,eACL,qBAAC,mBACC,oBAAC;IAAK;cAAS;KAAgB,EAC/B,oBAAC;IAAK,OAAM;cAAS,MAAM;KAAmB,IACzC;IAEL;GAEJ;CAGR,MAAM,gBAAgB,OAAO,eAA2B;AACtD,MAAI;GACF,MAAM,SAAS;QACb,WAAW,KAAK;eACT,WAAW,YAAY;YAC1B,WAAW,SAAS;;;;;;;;;GAiB1B,IAAI,QAPW,MAAM,OAAO,SAAS,QAAQ;IAC3C,WAAW;IACX,aAAa;IACb,QAAQ;IACT,CAAC,EAGgB;AAClB,UAAO,KACJ,QAAQ,6BAA6B,GAAG,CACxC,QAAQ,qBAAqB,GAAG,CAChC,QAAQ,WAAW,GAAG,CACtB,MAAM;AAET,oBAAiB,KAAK;AACtB,WAAQ,OAAO;WACR,KAAK;AACZ,YAAS,OAAO,IAAI,CAAC;AACrB,WAAQ,OAAO;;;CAInB,MAAM,aAAa,YAAY;AAC7B,MAAI,CAAC,iBAAiB,SAAS,OAC7B;EAIF,MAAM,YAAY,KAAK,KAAK,QAAQ,KAAK,EAAE,UAAU;EACrD,MAAM,YAAY,KAAK,KAAK,WAAW,SAAS;EAChD,MAAM,WAAW,KAAK,KAAK,WAAW,GAAG,MAAM,KAAK,WAAW;AAE/D,MAAI;AAEF,OAAI,CAAC,GAAG,WAAW,UAAU,CAC3B,IAAG,UAAU,WAAW,EAAE,WAAW,MAAM,CAAC;AAG9C,MAAG,cAAc,UAAU,eAAe,QAAQ;AAClD,iBAAc,2BAA2B,MAAM,KAAK,WAAW;AAG/D,oBAAiB,cAAc,KAAK,EAAE,IAAK;WACpC,KAAK;AACZ,iBAAc,UAAU,MAAM;AAC9B,oBAAiB,cAAc,KAAK,EAAE,IAAK;;;CAI/C,MAAM,aAAa,YAAY;AAC7B,MAAI,CAAC,iBAAiB,SAAS,OAC7B;AAIF,gBADgB,MAAM,gBAAgB,cAAc,GAC5B,yBAAyB,iBAAiB;AAClE,mBAAiB,cAAc,KAAK,EAAE,IAAK;;AAG7C,WAAU,SAAO,QAAQ;AACvB,MAAI,IAAI,OACN,SAAQ;AAIV,MAAI,SAAS,UAAU,CAAC,OAAO;AAC7B,OAAIC,YAAU,OAAOA,YAAU,IAC7B,aAAY;AAEd,OAAIA,YAAU,OAAOA,YAAU,IAC7B,aAAY;;GAGhB;CAEF,MAAM,2BAA2B;EAC/B,MAAM,cAAcD,QAAM,MAAM,MAAM,EAAE,QAAQ,KAAK;AACrD,MAAI,CAAC,YACH,QAAO;AAgBT,SACE,qBAAC;GAAI,eAAc;;IACjB,oBAAC;KAAK;KAAK,OAAM;eACd,YAAY;MACR;IACP,oBAAC;KAAI,cAAc;eACjB,oBAAC;MAAK;gBAZ6B;OACvC,MAAM;OACN,aAAa;OACb,UAAU;OACX,CAQ8B;OAAa;MAClC;IACN,qBAAC;KAAI,aAAY;KAAO,aAAY;KAAS,UAAU;gBACrD,oBAAC;MAAK,OAAM;gBAAO;OAAY,EAC/B,oBAAC;MACC,UAAU;MACV,UAAU;MACV,aA1BqC;OAC3C,MAAM;OACN,aAAa;OACb,UACE;OACH,CAqBiC;MAC1B,OAAO;OACP;MACE;;IACF;;AAKV,KAAI,SAAS,aACX,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAK;IAAK,OAAM;cAAO;KAEjB;GACP,oBAAC;IAAI,SAAS;cAAI,gBAAgB;KAAO;GACzC,qBAAC,kBACC,oBAAC;IAAK,OAAM;cACV,oBAAC,WAAQ,MAAK,SAAS;KAClB,EACP,oBAAC,kBAAK,sCAAwC,IAC1C;GACN,oBAAC;IAAI,WAAW;cACd,oBAAC;KAAK;eAAS;MAAkC;KAC7C;;GACF;AAKV,KAAI,SAAS,QAAQ;AACnB,MAAI,MACF,QACE,qBAAC;GAAI,eAAc;GAAS,SAAS;;IACnC,oBAAC;KAAK;KAAK,OAAM;eAAO;MAEjB;IACP,oBAAC;KAAI,SAAS;eACZ,oBAAC;MAAK;MAAK,OAAM;gBAAM;OAEhB;MACH;IACN,oBAAC;KAAK,OAAM;eAAO;MAAa;IAChC,oBAAC;KAAI,WAAW;eACd,oBAAC;MAAK;gBAAS;OAAkB;MAC7B;;IACF;AAIV,SACE,qBAAC;GAAI,eAAc;GAAS,SAAS;;IACnC,oBAAC;KAAK;KAAK,OAAM;eAAO;MAEjB;IACP,oBAAC;KAAI,SAAS;eACZ,qBAAC;MAAK;MAAK,OAAM;iBAAQ,4BACE,MAAM;OAC1B;MACH;IACN,oBAAC;KAAI,cAAc;eACjB,qBAAC;MAAK;iBAAS,uBACM,qBAAC;OAAK,OAAM;;QAAQ;QAAgB,MAAM;QAAK;;QAAgB;OAC7E;MACH;IACN,oBAAC;KAAI,aAAY;KAAQ,aAAY;KAAS,eAAc;KAAS,SAAS;eAC5E,oBAAC;MAAK,OAAM;MAAQ,MAAK;gBACtB;OACI;MACH;IAGL,cACC,oBAAC;KAAI,WAAW;eACd,oBAAC;MAAK,OAAO,WAAW,WAAW,QAAQ,GAAG,QAAQ;gBAAU;OAAkB;MAC9E;IAEP,cACC,oBAAC;KAAI,WAAW;eACd,oBAAC;MAAK,OAAO,WAAW,SAAS,SAAS,GAAG,QAAQ;gBAAU;OAAkB;MAC7E;IAGR,oBAAC;KAAI,WAAW;eACd,qBAAC;MAAK;;OACJ,oBAAC;QAAK,OAAM;kBAAS;SAAQ;;OAA2B,oBAAC;QAAK,OAAM;kBAAS;SAAQ;OAAC;OAAI;OACtE,oBAAC;QAAK,OAAM;kBAAO;SAAU;;;OAC5C;MACH;;IACF;;AAKV,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAK;IAAK,OAAM;cAAO;KAEjB;GACP,oBAAC;IAAI,SAAS;cAAI,gBAAgB;KAAO;GACxC,oBAAoB;GACrB,oBAAC;IAAI,WAAW;cACd,oBAAC;KAAK;eAAS;MAAkC;KAC7C;;GACF;;;;;AClWV,MAAM,QAAQ;CACZ;EAAE,KAAK;EAAQ,KAAK;EAAG,OAAO;EAAQ,MAAM;EAAiC;CAC7E;EAAE,KAAK;EAAe,KAAK;EAAG,OAAO;EAAe,MAAM;EAA2B;CACrF;EAAE,KAAK;EAAU,KAAK;EAAG,OAAO;EAAc,MAAM;EAA6B;CACjF;EAAE,KAAK;EAAW,KAAK;EAAG,OAAO;EAAS,MAAM;EAA0B;CAC3E;AAGD,SAAS,cAAc,OAAkB,aAA6B;AACpE,QAAO,mBAAmB,MAAM,KAAK;;;;WAI5B,MAAM,KAAK;kBACJ,MAAM,YAAY;;EAElC,YAAY;;;;;AAMd,SAAgB,eAAe,EAAE,QAAQ,UAA+B;CACtE,MAAM,CAAC,MAAM,WAAW,SAAe,OAAO;CAC9C,MAAM,CAAC,OAAO,YAAY,SAAoB;EAC5C,MAAM;EACN,aAAa;EACb,QAAQ;EACR,SAAS;EACV,CAAC;CACF,MAAM,CAAC,OAAO,YAAY,SAAS,GAAG;CACtC,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CAEjE,MAAM,iBACJ,SAAS,SACL,IACA,SAAS,gBACP,IACA,SAAS,WACP,IACA,SAAS,YACP,IACA;AAEZ,WAAU,MAAM,QAAQ;AACtB,MAAI,IAAI,UAAU,SAAS,OACzB,SAAQ;AAEV,OAAK,SAAS,OAAO,SAAS,QAAQ,SAAS,UAAU,CAAC,MACxD,aAAY;GAEd;CAEF,MAAM,eAAe,OAAO,UAAkB;AAC5C,MAAI,SAAS,QAAQ;AACnB,OAAI,CAAC,MAAM,MAAM,CACf;GAEF,MAAM,YAAY,MACf,aAAa,CACb,QAAQ,QAAQ,IAAI,CACpB,QAAQ,eAAe,GAAG;AAC7B,aAAU,OAAO;IAAE,GAAG;IAAG,MAAM;IAAW,EAAE;AAC5C,YAAS,GAAG;AACZ,WAAQ,cAAc;aACb,SAAS,eAAe;AACjC,OAAI,CAAC,MAAM,MAAM,CACf;AAEF,aAAU,OAAO;IAAE,GAAG;IAAG,aAAa;IAAO,EAAE;AAC/C,YAAS,GAAG;AACZ,WAAQ,SAAS;aACR,SAAS,UAAU;AAE5B,aAAU,OAAO;IAAE,GAAG;IAAG,QAAQ,MAAM,MAAM,IAAI;IAAU,EAAE;AAC7D,YAAS,GAAG;AACZ,WAAQ,UAAU;aACT,SAAS,WAAW;AAC7B,OAAI,CAAC,MAAM,MAAM,CACf;AAEF,aAAU,OAAO;IAAE,GAAG;IAAG,SAAS;IAAO,EAAE;AAC3C,YAAS,GAAG;AACZ,WAAQ,aAAa;AACrB,SAAM,aAAa;IAAE,GAAG;IAAO,SAAS;IAAO,CAAC;;;CAIpD,MAAM,aAAa,SAAyB;AAC1C,SAAO,KACJ,QAAQ,6BAA6B,GAAG,CACxC,QAAQ,eAAe,GAAG,CAC1B,QAAQ,kBAAkB,GAAG,CAC7B,QAAQ,UAAU,GAAG,CACrB,QAAQ,SAAS,GAAG,CACpB,QAAQ,cAAc,KAAK,CAC3B,MAAM;;CAGX,MAAM,eAAe,OAAO,eAA0B;AACpD,MAAI;GAEF,MAAM,YAAY,WAAW,WAAW;GACxC,MAAM,gBAAgB,qCAAqC,WAAW,QAAQ;EAClF,YAAY,eAAe,WAAW,WAAW,GAAG;;;;;;GAOhD,IAAI,cAAc;AAClB,cAAW,MAAM,SAAS,OAAO,OAAO,eAAe;IACrD,WAAW;IACX,QAAQ;IACT,CAAC,CACA,gBAAe;AAEjB,iBAAc,UAAU,YAAY;AAGpC,OAAI,CAAC,YAAY,WAAW,OAAO,CACjC,eAAc,YACX,MAAM,KAAK,CACX,KAAK,SAAS,OAAO,KAAK,MAAM,GAAG,CACnC,KAAK,KAAK;AAKf,oBADa,cAAc,YAAY,YAAY,CAC7B;AACtB,WAAQ,OAAO;WACR,GAAG;AACV,YAAS,GAAG,IAAI;AAChB,WAAQ,OAAO;;;CAInB,MAAM,mBAAmB;AACvB,MAAI;GACF,MAAM,WAAW,KAAK,KAAK,QAAQ,KAAK,EAAE,WAAW,QAAQ;AAC7D,OAAI,CAAC,GAAG,WAAW,SAAS,CAC1B,IAAG,UAAU,UAAU,EAAE,WAAW,MAAM,CAAC;GAE7C,MAAM,WAAW,KAAK,KAAK,UAAU,GAAG,MAAM,KAAK,UAAU;AAC7D,MAAG,cAAc,UAAU,cAAc;AACzC,iBAAc,0BAA0B,MAAM,KAAK,UAAU;WACtD,GAAG;AACV,iBAAc,UAAU,IAAI;;;CAIhC,MAAM,uBACJ,oBAAC;EAAI,cAAc;YAChB,MAAM,KAAK,GAAG,MAAM;GACnB,MAAM,aAAa,iBAAiB,EAAE;GACtC,MAAM,YAAY,SAAS,EAAE;AAC7B,UACE,qBAAC;IAAgB,aAAa;eAC5B,qBAAC;KAAK,OAAO,aAAa,UAAU,YAAY,SAAS;;MACtD,aAAa,QAAQ,YAAY,QAAQ;MAAM;MAAE,EAAE;;MAC/C,EACN,IAAI,MAAM,SAAS,KAAK,oBAAC;KAAK;eAAS;MAAY;MAJ5C,EAAE,IAKN;IAER;GACE;AAGR,QACE,qBAAC;EAAI,eAAc;EAAS,UAAU;;GACnC,gBAAgB;GAGjB,qBAAC;IACC,aAAY;IACZ,aAAY;IACZ,eAAc;IACd,cAAc;IACd,UAAU;;KAEV,qBAAC;MACC,oBAAC;OAAK,OAAM;iBAAO;QAAY;;MAAE,MAAM,QAAQ;SAC1C;KACP,qBAAC;MACC,oBAAC;OAAK,OAAM;iBAAO;QAAmB;;MAAE,MAAM,eAAe;SACxD;KACP,qBAAC;MACC,oBAAC;OAAK,OAAM;iBAAO;QAAkB;;MAAE,MAAM,UAAU;SAClD;KACP,qBAAC;MACC,oBAAC;OAAK,OAAM;iBAAO;QAAa;;MAAE,MAAM,WAAW;SAC9C;;KACH;GAGL,SAAS,UACR,qBAAC;IAAI,eAAc;;KACjB,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAK;gBAAS;OAAsD;KACrE,qBAAC;MAAI,WAAW;iBACd,oBAAC;OAAK,OAAM;iBAAO;QAAY,EAC/B,oBAAC;OACC,UAAU;OACV,UAAU;OACV,aAAY;OACZ,OAAO;QACP;OACE;;KACF;GAGP,SAAS,iBACR,qBAAC;IAAI,eAAc;;KACjB,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAK;gBAAS;OAA6C;KAC5D,qBAAC;MAAI,WAAW;iBACd,oBAAC;OAAK,OAAM;iBAAO;QAAY,EAC/B,oBAAC;OACC,UAAU;OACV,UAAU;OACV,aAAY;OACZ,OAAO;QACP;OACE;;KACF;GAGP,SAAS,YACR,qBAAC;IAAI,eAAc;;KACjB,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAK;gBAAS;OAA6D;KAC5E,qBAAC;MAAI,WAAW;iBACd,oBAAC;OAAK,OAAM;iBAAO;QAAY,EAC/B,oBAAC;OACC,UAAU;OACV,UAAU;OACV,aAAY;OACZ,OAAO;QACP;OACE;;KACF;GAGP,SAAS,aACR,qBAAC;IAAI,eAAc;;KACjB,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAK;gBAAS;OAA2C;KAC1D,qBAAC;MAAI,WAAW;iBACd,oBAAC;OAAK,OAAM;iBAAO;QAAY,EAC/B,oBAAC;OACC,UAAU;OACV,UAAU;OACV,aAAY;OACZ,OAAO;QACP;OACE;;KACF;GAGP,SAAS,gBACR,qBAAC;IAAI,eAAc;eACjB,qBAAC,kBACC,oBAAC;KAAK,OAAM;eACV,oBAAC,WAAQ,MAAK,SAAS;MAClB,EACP,oBAAC,kBAAK,oCAAsC,IACxC,EACN,oBAAC;KAAK;eAAS;MAAsC;KACjD;GAGP,SAAS,UACR,oBAAC;IAAI,eAAc;cAChB,QACC,qBAAC;KAAK,OAAM;gBAAM,WAAQ;MAAa,GAEvC;KACE,oBAAC;MAAK;MAAK,OAAM;gBAAQ;OAElB;KACP,oBAAC;MACC,aAAY;MACZ,aAAY;MACZ,eAAc;MACd,SAAS;MACT,UAAU;gBAEV,qBAAC;OAAK,OAAM;kBACT,cAAc,MAAM,GAAG,IAAI,EAC3B,cAAc,SAAS,MAAM,QAAQ;QACjC;OACH;KAEN,qBAAC;MACC,oBAAC;OAAK;iBAAS;QAAa;MAC5B,oBAAC;OAAK,OAAM;iBAAS;QAAQ;MAC7B,oBAAC;OAAK;iBAAS;QAAoC;MACnD,oBAAC;OAAK,OAAM;iBAAO;QAAU;MAC7B,oBAAC;OAAK;iBAAS;QAAkB;SAC7B;KAEL,cACC,oBAAC;MAAI,WAAW;gBACd,oBAAC;OAAK,OAAO,WAAW,WAAW,QAAQ,GAAG,QAAQ;iBAAU;QAAkB;OAC9E;QAEP;KAED;;GAEJ;;;;;AC/UV,MAAM,eAAe;CACnB;EAAE,IAAI;EAAQ,MAAM;EAAQ;CAC5B;EAAE,IAAI;EAAU,MAAM;EAAU;CAChC;EAAE,IAAI;EAAO,MAAM;EAAkB;CACrC;EAAE,IAAI;EAAO,MAAM;EAAiB;CACpC;EAAE,IAAI;EAAc,MAAM;EAAc;CACzC;AAQD,MAAM,aAAa;CACjB;EAAE,IAAI;EAAc,MAAM;EAAc;CACxC;EAAE,IAAI;EAAU,MAAM;EAAa;CACnC;EAAE,IAAI;EAAW,MAAM;EAAe;CACtC;EAAE,IAAI;EAAU,MAAM;EAAW;CACjC;EAAE,IAAI;EAAW,MAAM;EAAW;CAClC;EAAE,IAAI;EAAa,MAAM;EAAa;CACvC;AAcD,MAAME,WAAwE;CAI5E,MAAM;EACJ,YAAY;GACV,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;GAqBP;EACD,UAAU;GACR,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;GAmBP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;GAuBP;EACD,QAAQ;GACN,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;GAWP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;GAYP;EACD,WAAW;GACT,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;GAYP;EACF;CAKD,QAAQ;EACN,YAAY;GACV,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;GAqBP;EACD,UAAU;GACR,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;GAiBP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BP;EACD,QAAQ;GACN,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;GAaP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;GAWP;EACD,WAAW;GACT,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;GAcP;EACF;CAKD,KAAK;EACH,YAAY;GACV,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;GAuBP;EACD,UAAU;GACR,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;GAeP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;GAqBP;EACD,QAAQ;GACN,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;GAgBP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;GAoBP;EACD,WAAW;GACT,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;GAeP;EACF;CAKD,KAAK;EACH,YAAY;GACV,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;GAqBP;EACD,UAAU;GACR,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;GAsBP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BP;EACD,QAAQ;GACN,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;GAoBP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;GAuBP;EACD,WAAW;GACT,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;GAkBP;EACF;CAKD,YAAY;EACV,YAAY;GACV,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;;GAuBP;EACD,UAAU;GACR,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;GAeP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;GAmBP;EACD,QAAQ;GACN,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;GAqBP;EACD,SAAS;GACP,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;GAoBP;EACD,WAAW;GACT,SAAS;GACT,aAAa;GACb,MAAM;;;;;;;;;;;;;;;;;;;;;;GAsBP;EACF;CACF;AAMD,SAAgB,iBAAiB;CAC/B,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,EAAE;CAEzD,MAAM,CAAC,qBAAqB,0BAA0B,SAAsB,aAAa;CAEzF,MAAM,oBAAoB,aAAa;CACvC,MAAM,qBAAqB,SAAS,kBAAkB;CACtD,MAAM,sBAAsB,WAAW,QAAQ,MAAM,mBAAmB,EAAE,IAAI;CAG9E,IAAI,iBAAiB,oBAAoB,WAAW,MAAM,EAAE,OAAO,oBAAoB;AACvF,KAAI,mBAAmB,IAAI;EAEzB,MAAM,gBAAgB,WAAW,WAAW,MAAM,EAAE,OAAO,oBAAoB;AAE/E,mBAAiB,KAAK,IAAI,eAAe,oBAAoB,SAAS,EAAE;AACxE,mBAAiB,KAAK,IAAI,GAAG,eAAe;;CAG9C,MAAM,mBAAmB,oBAAoB,mBAAmB,oBAAoB;CACpF,MAAM,UAAU,mBAAmB,mBAAmB,iBAAiB,MAAM;AAE7E,WAAU,QAAQ,QAAQ;AACxB,MAAI,IAAI,QAEN,wBAAuB,oBADN,KAAK,IAAI,GAAG,iBAAiB,EAAE,EACK,GAAG;AAE1D,MAAI,IAAI,UAEN,wBAAuB,oBADN,KAAK,IAAI,oBAAoB,SAAS,GAAG,iBAAiB,EAAE,EACxB,GAAG;AAE1D,MAAI,IAAI,UACN,qBAAoB,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAG/C,MAAI,IAAI,WACN,qBAAoB,MAAM,KAAK,IAAI,aAAa,SAAS,GAAG,IAAI,EAAE,CAAC;AAGrE,MAAI,IAAI,IAEN,qBAAoB,OAAO,IAAI,KAAK,aAAa,OAAO;GAG1D;AAEF,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAK;cAAK;KAA0B;GACrC,oBAAC;IAAK;cAAS;KAAwD;GAGvE,oBAAC;IAAI,SAAS;IAAG,KAAK;cACnB,aAAa,KAAK,KAAK,MACtB,oBAAC,iBACC,qBAAC;KACC,MAAM,MAAM;KACZ,OAAO,MAAM,kBAAkB,SAAS;KACxC,SAAS,MAAM;;MAEd;MACA,IAAI;MAAM;;MACN,IARC,IAAI,GASR,CACN;KACE;GAEN,qBAAC;IAAI,eAAc;eAEjB,oBAAC;KAAI,eAAc;KAAS,aAAa;KAAG,UAAU;eACnD,oBAAoB,KAAK,GAAG,MAC3B,qBAAC;MACC,MAAM,MAAM;MACZ,OAAO,MAAM,iBAAiB,SAAS;iBAGtC,MAAM,iBAAiB,OAAO,MAC9B,EAAE;QAHE,EAAE,GAIF,CACP;MACE,EAGL,WACC,qBAAC;KACC,aAAY;KACZ,aAAY;KACZ,eAAc;KACd,UAAU;KACV,UAAU;;MAEV,qBAAC;OAAK;OAAK,OAAM;;QACd,kBAAkB;QAAK;QAAI,iBAAiB;;QACxC;MACP,oBAAC;OAAK;iBAAU,QAAQ;QAAmB;MAE3C,qBAAC;OAAI,WAAW;kBACd,oBAAC;QAAK,OAAM;kBAAS;SAAS,EAC9B,oBAAC,kBAAM,QAAQ,UAAe;QAC1B;MAEN,qBAAC;OAAI,eAAc;OAAS,WAAW;kBACrC,oBAAC;QAAK;kBAAS;SAAe,EAC9B,oBAAC;QAAK,OAAM;kBAAS,QAAQ;SAAY;QACrC;;MACF;KAEJ;GAEN,oBAAC;IAAI,WAAW;cACd,qBAAC;KAAK;;MACJ,oBAAC;OAAK,OAAM;iBAAS;QAAU;;MAAI,oBAAC;OAAK,OAAM;iBAAS;QAAU;;MAAqB;MACvF,oBAAC;OAAK,OAAM;iBAAS;QAAU;;MAAwB;MACtD,UAAU,uDAAuD,OAAO;MAAC;MAAG;MAC7E,oBAAC;OAAK,OAAM;iBAAO;QAAU;;;MACxB;KACH;;GACF;;;;;ACl0BV,SAAS,eAAe,YAA+C;CACrE,MAAM,WAAW,GAAG,UAAU;CAC9B,MAAM,OAAO,GAAG,MAAM;AAEtB,KAAI,eAAe,UAAU;AAC3B,MAAI,aAAa,YAAY,SAAS,QACpC,QAAO;AAET,MAAI,aAAa,QACf,QAAO;AAET,MAAI,aAAa,QACf,QAAO;AAET,SAAO;;AAGT,KAAI,aAAa,YAAY,SAAS,QACpC,QAAO,kBAAkB,WAAW,aAAa,CAAC;AAEpD,KAAI,aAAa,YAAY,SAAS,MACpC,QAAO,cAAc,WAAW,aAAa,CAAC;AAGhD,QAAO,WAAW,aAAa;;AAGjC,SAAgB,SAAS,EAAE,QAAQ,OAAO,aAAa,OAAO,eAA8B;CAC1F,MAAM,aAAa,OAAO,eAAe;CACzC,MAAM,QAAQ,OAAO,UAAU;CAC/B,MAAM,eAAe,OAAO,iBAAiB;CAC7C,MAAM,cAAc,eAAe,WAAW;CAE9C,MAAM,YAAY,cAAc;CAChC,MAAM,UAAU,eAAe;CAG/B,MAAM,CAAC,YAAY,iBAAiB,SAA4B,KAAK;CACrE,MAAM,CAAC,UAAU,eAAe,SAA2B,EAAE,CAAC;CAC9D,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,EAAE;CACvD,MAAM,CAAC,eAAe,oBAAoB,SAAiB,GAAG;CAC9D,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;CAGjE,MAAM,CAAC,SAAS,cAAc,SAAgC,OAAO,iBAAiB,CAAC;CACvF,MAAM,CAAC,SAAS,cAAc,SAAgC,OAAO,iBAAiB,CAAC;CAGvF,MAAM,YAAY,YAAY,YAAY;EACxC,MAAM,CAAC,MAAM,OAAO,cAAc,MAAM,QAAQ,IAAI;GAClD,OAAO,oBAAoB;GAC3B,OAAO,uBAAuB;GAC9B,OAAO,yBAAyB;GACjC,CAAC;AACF,gBAAc,KAAK;AACnB,cAAY,SAAS,EAAE,CAAC;AACxB,oBAAkB,WAAW;AAG7B,aAAW,OAAO,iBAAiB,CAAC;AACpC,aAAW,OAAO,iBAAiB,CAAC;EAGpC,MAAM,YAAY,OAAO,UAAU;AACnC,MAAI,iBAAiB,UACnB,kBAAiB,YAAY,IAAI,IAAI,GAAG;IAEzC,CAAC,eAAe,OAAO,CAAC;AAG3B,iBAAgB;AACd,aAAW;EACX,MAAM,WAAW,YAAY,WAAW,IAAK;AAC7C,eAAa,cAAc,SAAS;IACnC,CAAC,UAAU,CAAC;AAEf,UAAS,OAAO,OAAO,QAAQ;AAC7B,MAAI,UAAU,OAAO,UAAU,KAAK;AAClC,kBAAe;AACf;;AAIF,MAAI,UAAU,OAAO,UAAU,KAAK;AAClC,SAAM,WAAW;AACjB;;EAIF,MAAM,YAAY,SAAS;AAC3B,MAAI,YAAY,GAAG;AACjB,OAAI,IAAI,aAAa,UAAU,KAAK;AAClC,sBAAkB,SAAS,KAAK,IAAI,OAAO,GAAG,YAAY,EAAE,CAAC;AAC7D;;AAEF,OAAI,IAAI,WAAW,UAAU,KAAK;AAChC,sBAAkB,SAAS,KAAK,IAAI,OAAO,GAAG,EAAE,CAAC;AACjD;;GAGF,MAAM,MAAM,OAAO,SAAS,OAAO,GAAG;AACtC,OAAI,OAAO,KAAK,OAAO,KAAK,OAAO,WAAW;AAC5C,qBAAiB,MAAM,EAAE;AACzB;;;AAKJ,MAAI,UAAU,OAAO,CAAC,WAAW,iBAAiB,KAAK,SAAS,gBAAgB;AAC9E,cAAW,KAAK;AAChB,iBAAc,KAAK;AACnB,OAAI;IACF,MAAM,OAAO,SAAS;AAEtB,QADgB,MAAM,OAAO,eAAe,cAAc,CAIxD,eAAc,WAFI,KAAK,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,SACrC,KAAK,SAAS,KAAK,qBACY;QAE9C,eAAc,sBAAsB;AAEtC,UAAM,WAAW;YACVC,KAAU;AACjB,kBAAc,UAAU,IAAI,UAAU;;AAExC,cAAW,MAAM;AACjB,oBAAiB,cAAc,KAAK,EAAE,IAAK;AAC3C;;AAIF,MAAI,UAAU,OAAO,CAAC,WAAW,YAAY,QAAQ,SAAS;AAC5D,cAAW,KAAK;AAChB,iBAAc,KAAK;AACnB,OAAI;IACF,MAAM,SAAS,MAAM,OAAO,eAAe;AAC3C,QAAI,OACF,eACE,UAAU,OAAO,YAAY,UAAU,OAAO,gBAAgB,qBAAqB,KACpF;QAED,eAAc,8BAA8B;AAE9C,UAAM,WAAW;AACjB,qBAAiB,GAAG;YACbA,KAAU;AACjB,kBAAc,UAAU,IAAI,UAAU;;AAExC,cAAW,MAAM;AACjB,oBAAiB,cAAc,KAAK,EAAE,IAAK;;GAE7C;AAEF,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAK;cAAK;KAAyB;GAEpC,qBAAC;IAAI,aAAY;IAAO,aAAY;IAAS,eAAc;IAAS,SAAS;IAAG,UAAU;;KACxF,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,qBAAC;MAAK;MACK;MACT,oBAAC;OAAK;OAAK,OAAM;iBACd;QACI;SACF;KACP,qBAAC,mBAAK,YACI,oBAAC;MAAK,OAAM;gBAAQ;OAAmB,IAC1C;KACP,qBAAC,mBAAK,YACI,oBAAC;MAAK,OAAO,eAAe,WAAW,UAAU;gBAAW;OAAmB,IAClF;KACP,qBAAC,mBAAK,WACG,oBAAC;MAAK,OAAO,UAAU,UAAU,UAAU;gBAAS;OAAa,IACnE;;KACH;GAGN,qBAAC;IAAI,eAAc;IAAM,cAAc;eACrC,qBAAC;KACC,aAAa,SAAS,SAAS,UAAU;KACzC,aAAY;KACZ,eAAc;KACd,aAAa;KACb,UAAU;KACV,UAAU;gBAEV,oBAAC;MAAK;MAAK,OAAO,SAAS,SAAS,SAAS;gBAAQ;OAE9C,EACN,UACC;MACE,qBAAC;OAAK;OACG;OACP,oBAAC;QAAK;QAAK,OAAO,QAAQ,SAAS,SAAS;kBACzC,QAAQ;SACJ;UACF;MACP,qBAAC;OAAK;OACI;OACR,oBAAC;QAAK,OAAO,QAAQ,SAAS,UAAU;kBACrC,QAAQ,SAAS,aAAa;SAC1B;UACF;MACN,QAAQ,UACP,qBAAC;OAAK;OACI;OACR,oBAAC;QAAK,OAAO,QAAQ,WAAW,WAAW,UAAU;kBAClD,QAAQ,OAAO,aAAa;SACxB;UACF;SAER,GAEH,oBAAC;MAAK,OAAM;MAAO;gBAAS;OAErB;MAEL,EAEN,qBAAC;KACC,aAAa,SAAS,SAAS,UAAU;KACzC,aAAY;KACZ,eAAc;KACd,UAAU;KACV,UAAU;gBAEV,oBAAC;MAAK;MAAK,OAAO,SAAS,SAAS,SAAS;gBAAQ;OAE9C,EACN,UACC;MACE,qBAAC;OAAK;OACG;OACP,oBAAC;QAAK;QAAK,OAAO,QAAQ,SAAS,SAAS;kBACzC,QAAQ;SACJ;UACF;MACP,qBAAC;OAAK;OACI;OACR,oBAAC;QAAK,OAAO,QAAQ,SAAS,UAAU;kBACrC,QAAQ,SAAS,aAAa;SAC1B;UACF;MACN,QAAQ,UACP,qBAAC;OAAK;OACI;OACR,oBAAC;QAAK,OAAO,QAAQ,WAAW,WAAW,UAAU;kBAClD,QAAQ,OAAO,aAAa;SACxB;UACF;SAER,GAEH,oBAAC;MAAK,OAAM;MAAO;gBAAS;OAErB;MAEL;KACF;GAEN,qBAAC;IAAI,eAAc;IAAM,cAAc;;KACrC,qBAAC;MACC,aAAY;MACZ,aAAY;MACZ,eAAc;MACd,aAAa;MACb,UAAU;MACV,UAAU;;OAEV,oBAAC;QAAK;QAAK,OAAM;kBAAO;SAEjB;OACP,qBAAC,mBAAK,WACG,oBAAC;QAAK,OAAM;kBAAQ,QAAQ;SAAa,IAC3C;OACP,qBAAC;QAAK;QACE;QACN,qBAAC;SAAK,OAAO,QAAQ,cAAc,KAAK,QAAQ;;UAC7C,QAAQ;UAAK;UAAG,QAAQ;UAAY;;UAChC;WACF;OACP,qBAAC,mBAAK,UACE,oBAAC;QAAK,OAAM;kBAAS,QAAQ;SAAY,IAC1C;;OACH;KAEN,qBAAC;MACC,aAAY;MACZ,aAAY;MACZ,eAAc;MACd,aAAa;MACb,UAAU;MACV,UAAU;;OAEV,oBAAC;QAAK;QAAK,OAAM;kBAAO;SAEjB;OACP,qBAAC,mBAAK,aACK,oBAAC;QAAK,OAAM;kBAAQ,MAAM;SAAe,IAC7C;OACP,qBAAC,mBAAK,eACO,oBAAC;QAAK,OAAM;kBAAQ,MAAM;SAAgB,IAChD;OACP,qBAAC,mBAAK,gBACQ,oBAAC;QAAK,OAAM;kBAAQ,MAAM;SAAiB,IAClD;OACP,qBAAC,mBAAK,gBACQ,qBAAC;QAAK,OAAM;mBAAQ,KAAK,MAAM,MAAM,cAAc,IAAK,EAAC;SAAQ,IACxE;OACN,MAAM,gBAAgB,KACrB,qBAAC,mBAAK,gBACQ,oBAAC;QAAK,OAAM;kBAAU,MAAM,cAAc,QAAQ,EAAE;SAAQ,IACnE;OAER,MAAM,eAAe,KACpB,qBAAC,mBAAK,eACO,oBAAC;QAAK,OAAM;kBAAQ,MAAM,aAAa,QAAQ,EAAE;SAAQ,IAC/D;OAER,MAAM,aAAa,SAAS,KAC3B,qBAAC;QAAK;QACQ;QACZ,qBAAC;SAAK,OAAM;oBACT,KAAK,IAAI,GAAG,MAAM,aAAa,KAAK,MAAM,EAAE,UAAU,CAAC,EAAC;UACpD;WACF;;OAEL;KAEL,gBACC,qBAAC;MACC,aAAY;MACZ,aAAY;MACZ,eAAc;MACd,UAAU;MACV,UAAU;;OAEV,oBAAC;QAAK;QAAK,OAAM;kBAAQ;SAElB;OACP,qBAAC,mBAAK,YACI,oBAAC;QAAK,OAAM;kBAAQ;SAAgB,IACvC;OACP,qBAAC;QAAK;QACC;QACL,oBAAC;SAAK,OAAO,aAAa,MAAM,SAAS;mBACtC,aAAa,OAAO;UAChB;WACF;OACP,qBAAC,mBAAK,UACE,oBAAC;QAAK,OAAM;kBAAQ,aAAa;SAAY,IAC9C;OACP,qBAAC;QAAK;QACG;QACP,oBAAC;SAAK,OAAM;mBACT,aAAa,QAAQ,SAAS,KAC3B,MAAM,aAAa,QAAQ,MAAM,IAAI,KACrC,aAAa;UACZ;WACF;;OACH;;KAEJ;GAGL,cACC,qBAAC;IACC,aAAa,WAAW,QAAQ,UAAU,UAAU;IACpD,aAAY;IACZ,eAAc;IACd,cAAc;IACd,UAAU;;KAEV,qBAAC;MAAK;MAAK,OAAO,WAAW,QAAQ,UAAU,UAAU;iBAAQ,qBAC9C,oBAAC;OAAK;iBAAS;QAAqB;OAChD;KACP,qBAAC;MAAK;MACI;MACR,oBAAC;OAAK,OAAO,WAAW,QAAQ,UAAU,UAAU;iBACjD,WAAW,QAAQ,UAAU,cAAc;QACvC;SACF;KACN,WAAW,QAAQ,WAClB;MACE,qBAAC;OACE;OAAI;OACA,oBAAC;QAAK,OAAM;kBAAQ,WAAW,QAAQ,OAAO;SAAiB;UAC/D;MACP,qBAAC;OACE;OAAI;OACC,oBAAC;QAAK,OAAM;kBAAQ,WAAW,QAAQ;SAAY;UACpD;MACP,qBAAC;OACE;OAAI;OACQ;OACb,oBAAC;QAAK,OAAO,iBAAiB,IAAI,WAAW;kBAAS;SAAsB;OAAC;OAC7E,qBAAC;QAAK;;SAAS;SAAE,WAAW,QAAQ;SAAiB;;SAAa;UAC7D;SACN;KAGJ,SAAS,SAAS,KACjB,qBAAC;MAAI,eAAc;MAAS,WAAW;iBACrC,qBAAC;OAAK;;QAAS;QACA,oBAAC;SAAK,OAAM;mBAAO;UAAsC;;;QACjE,EACN,SAAS,KAAK,MAAM,MAAM;OACzB,MAAM,aAAa,MAAM;OACzB,MAAM,YAAY,KAAK,WAAW;OAClC,MAAM,YAAY,UAAU,SAAS,KAAK,MAAM,UAAU,MAAM,IAAI,KAAK;AAEzE,cACE,qBAAC;QAAI,eAAc;QAAiB,YAAY;mBAC9C,qBAAC;SACC,qBAAC;UAAK,OAAM;qBAAQ,IAAI,GAAE;WAAQ;SAAC;SACnC,oBAAC;UAAK,OAAO,aAAa,SAAS,KAAK,SAAS,UAAU;oBACxD,aAAa,MAAM,KAAK,SAAS,MAAM;WACnC;SAAC;SACR,oBAAC;UAAK,MAAM;UAAY,SAAS;oBAC9B;WACI;SACN,KAAK,SACJ,qBAAC;UAAK,OAAM;UAAQ;qBACjB,KAAI;WAEA,GAEP,qBAAC;UAAK,OAAM;UAAS;qBAClB,KAAI;WAEA;YAEJ,EACN,KAAK,UACJ,qBAAC;SAAK;;UACH;UAAM;UAAO,KAAK,OAAO,OAAO,QAAQ,EAAE;UAAC;UAAW;UACtD,KAAK,OAAO,QAAQ,QAAQ,EAAE;UAAC;;UAC3B;UAzBsB,EA2B3B;QAER;OACE;KAGP,mBAAmB,KAAK,WAAW,QAAQ,WAC1C,oBAAC;MAAK,OAAM;MAAS;gBAAS;OAEvB;KAGR,WACC,oBAAC;MAAI,WAAW;gBACd,oBAAC;OAAK,OAAM;iBAAO;QAAiB;OAChC;KAEP,cACC,oBAAC;MAAI,WAAW;gBACd,oBAAC;OAAK,OAAM;iBAAQ;QAAkB;OAClC;;KAEJ;GAGR,qBAAC;IAAI,aAAY;IAAO,aAAY;IAAS,eAAc;IAAS,UAAU;;KAC5E,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,qBAAC;MAAK;MACM;MACV,oBAAC;OAAK;kBACF,UAAU,UAAU,MAAM,IAAI,SAAS,KACrC,OAAO,UAAU,UAAU,MAAM,IAAI,MAAM,IAAI,KAC/C,UAAU,UAAU,MAAM;QACzB;SACF;KACP,qBAAC,mBAAK,gBACQ,oBAAC;MAAK,OAAM;gBAAU,UAAU,aAAa;OAAe,IACnE;KACP,qBAAC,mBAAK,YACI,oBAAC;MAAK,OAAM;gBAAQ,UAAU,OAAO;OAAc,IACtD;KACP,qBAAC;MAAK;;OAAS;OACP,oBAAC;QAAK,OAAM;kBAAO;SAAQ;;;OAC5B;;KACH;GAEN,oBAAC;IAAI,eAAc;IAAS,WAAW;cACrC,qBAAC;KAAK;;MAAS;MACJ,qBAAC;OAAK,OAAM;kBAAO,YAAS,QAAQ;QAAe;;MAAW;MACvE,oBAAC;OAAK,OAAM;iBAAO;QAAY;;MAC9B,UAAU,wCAAwC,SAAS;;MACvD;KACH;GAEN,oBAAC;IAAI,WAAW;cACd,qBAAC;KAAK;;MACJ,oBAAC;OAAK,OAAM;iBAAO;QAAQ;;MAAS,oBAAC;OAAK,OAAM;iBAAO;QAAQ;;MAC9D,SAAS,SAAS,KACjB;OACG;OAAI;OACH,oBAAC;QAAK,OAAM;kBAAM;SAAQ;;OAAiB,oBAAC;QAAK,OAAM;kBAAM;SAAQ;;UACtE;MAEJ,YAAY,QAAQ,WAAW,SAAS,WAAW,KAClD;OACG;OAAI;OACH,oBAAC;QAAK,OAAM;kBAAM;SAAQ;;UAC3B;MAEJ;MAAK;MAAE,oBAAC;OAAK,OAAM;iBAAO;QAAU;;;MAChC;KACH;;GACF;;;;;AC5iBV,SAAgB,YAAY,EAAE,WAA6B;AACzD,QACE,qBAAC;EACC,YAAW;EACX,eAAc;EACd,QAAO;EACP,gBAAe;EACf,SAAS;aAET,oBAAC;GAAI,cAAc;aACjB,oBAAC;IAAK;IAAK,OAAM;cAAO;KAEjB;IACH,EACN,qBAAC,kBACC,oBAAC;GAAK,OAAM;aACV,oBAAC,WAAQ,MAAK,SAAS;IAClB,EACP,qBAAC,mBAAK,KAAE,WAAe,IACnB;GACF;;;;;ACoBV,SAAS,sBAAsB,SAAgC;CAE7D,MAAM,QAAQ,QAAQ,MAAM,wCAAwC;AACpE,KAAI,OAAO;EACT,MAAM,MAAM,OAAO,WAAW,MAAM,GAAG;EACvC,MAAM,OAAO,MAAM,GAAG,aAAa;AACnC,MAAI,SAAS,IACX,QAAO,MAAM;AAEf,MAAI,SAAS,IACX,QAAO,MAAM;AAEf,MAAI,SAAS,IACX,QAAO,MAAM;;AAGjB,QAAO;;AAGT,SAAS,aAAa,QAA4B,SAA0B;AAE1E,KAAI,UAAU,SAAS,GAAG;AACxB,MAAI,UAAU,IACZ,QAAO,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;AAEtC,MAAI,UAAU,IACZ,QAAO,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC;AAErC,MAAI,UAAU,IACZ,QAAO,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC;AAErC,SAAO,GAAG;;AAIZ,KAAI,SAAS;EACX,MAAM,YAAY,sBAAsB,QAAQ;AAChD,MAAI,WAAW;AACb,OAAI,aAAa,IACf,QAAO,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;AAEzC,OAAI,aAAa,IACf,QAAO,GAAG,KAAK,MAAM,YAAY,IAAI,CAAC;AAExC,OAAI,aAAa,IACf,QAAO,GAAG,KAAK,MAAM,YAAY,IAAI,CAAC;;;AAK5C,QAAO;;AAGT,SAAS,gBAAgB,GAAmB;AAC1C,KAAI,KAAK,IACP,QAAO,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;AAEjC,KAAI,KAAK,IACP,QAAO,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;AAEjC,QAAO,GAAG;;AAGZ,SAAS,WAAW,SAAqC;AACvD,KAAI,CAAC,QACH,QAAO;CAET,MAAM,OAAO,IAAI,KAAK,QAAQ;CAE9B,MAAM,0BADM,IAAI,MAAM,EACH,SAAS,GAAG,KAAK,SAAS;CAC7C,MAAM,WAAW,KAAK,MAAM,UAAU,MAAO,KAAK,KAAK,IAAI;AAE3D,KAAI,aAAa,EACf,QAAO;AAET,KAAI,aAAa,EACf,QAAO;AAET,KAAI,WAAW,EACb,QAAO,GAAG,SAAS;AAErB,KAAI,WAAW,GACb,QAAO,GAAG,KAAK,MAAM,WAAW,EAAE,CAAC;AAErC,KAAI,WAAW,IACb,QAAO,GAAG,KAAK,MAAM,WAAW,GAAG,CAAC;AAEtC,QAAO,GAAG,KAAK,MAAM,WAAW,IAAI,CAAC;;AAGvC,SAAS,cAAc,eAA2C;AAChE,KAAI,CAAC,cACH,QAAO;AAET,KAAI,iBAAiB,IACnB,QAAO,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;AAE7C,KAAI,iBAAiB,IACnB,QAAO,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;AAE7C,QAAO,GAAG;;AAoBZ,MAAM,aAAa,CACjB;CACE,IAAI;CACJ,MAAM;CACN,MAAM;CACN,QAAQ;CACR,SAAS;CACT,OAAO;CACR,EACD;CACE,IAAI;CACJ,MAAM;CACN,MAAM;CACN,QAAQ;CACR,SAAS;CACT,OAAO;CACR,CACF;AAGD,MAAM,aAAa;CACjB;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACR;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACR;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACR;CACD;EACE,IAAI;EACJ,MAAM;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACR;CACF;AAMD,MAAM,YAAY;AAElB,SAAgB,UAAU,EACxB,cACA,UACA,gBACA,aACA,gBACA,aACA,aACA,kBAAkB,cAClB,kBAAkB,qBACD;CACjB,MAAM,CAAC,KAAK,UAAU,SAAwD,SAAS;CACvF,MAAM,CAAC,UAAU,eAAe,SAC9B,cAAc,WAAW,MAAM,EAAE,OAAO,aAAa,IAAI,EAC1D;CACD,MAAM,CAAC,UAAU,eAAe,SAAoB,EAAE,CAAC;CACvD,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;CACjD,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,MAAM;CAC7D,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,CAAC,WAAW,gBAAgB,SAAS,KAAK;CAChD,MAAM,CAAC,UAAU,eAAe,SAAmB,OAAO;CAC1D,MAAM,CAAC,YAAY,iBAAiB,SAAqB,MAAM;CAC/D,MAAM,CAAC,cAAc,mBAAmB,yBAAsB,IAAI,KAAK,CAAC;CACxE,MAAM,CAAC,aAAa,kBAAkB,SAA4B,EAAE,CAAC;CACrE,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CACrD,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CAGvE,MAAM,CAAC,cAAc,mBAAmB,SAAwB,MAAM;CACtE,MAAM,CAAC,aAAa,kBAAkB,SACpC,WAAW,WAAW,MAAM,EAAE,OAAO,gBAAgB,IAAI,EAC1D;CACD,MAAM,CAAC,aAAa,kBAAkB,SACpC,WAAW,WAAW,MAAM,EAAE,OAAO,gBAAgB,IAAI,EAC1D;CAGD,MAAM,CAAC,gBAAgB,qBAAqB,SAE1C,EAAE,CAAC;CAGL,MAAM,oBAAoB,uBAAoB,IAAI,KAAK,CAAC;CACxD,MAAM,qBAAqB,OAA+B,KAAK;CAC/D,MAAM,uBAAuB,OAAO,MAAM;CAE1C,MAAM,sBAAsB,YAAY;AAEtC,QAAM,yBAAyB,CAAC,YAAY,GAAG;EAE/C,MAAM,yBAAS,IAAI,KAAa;AAChC,OAAK,MAAM,SAAS,cAClB,KAAI,cAAc,MAAM,KAAK,CAC3B,QAAO,IAAI,MAAM,GAAG;AAGxB,kBAAgB,OAAO;EACvB,MAAM,YAAY,cAAc;AAChC,iBAAe,UAAU,OAAO;AAGhC,2BAAyB,UAAU,OAAO;;CAI5C,MAAM,2BAA2B,OAAO,WAA8B;EACpE,MAAM,WAAW;EACjB,MAAM,aAAa,OAAO,QACvB,MACC,EAAE,EAAE,iBAAiB,EAAE,cACvB,EAAE,cAAc,OACf,EAAE,UAAU,SAAS,IAAI,IAAI,OAAO,SAAS,EAAE,WAAW,GAAG,GAAG,SACpE;AACD,MAAI,WAAW,WAAW,EACxB;EAGF,MAAM,YAAY;EAClB,MAAM,gBAAgB,CAAC,GAAG,OAAO;AAEjC,OAAK,IAAI,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,WAAW;GACrD,MAAM,QAAQ,WAAW,MAAM,GAAG,IAAI,UAAU;AAChD,SAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,UAAU;AACzB,QAAI;KAGF,MAAM,OAAO,MAAM,mBADN,MAAM,KAAK,QAAQ,MAAM,IAAI,CACC;KAG3C,MAAM,MAAM,cAAc,WAAW,MAAM,EAAE,SAAS,MAAM,KAAK;AACjE,SAAI,OAAO,KAAK,MAAM;AACpB,UAAI,KAAK,WAAW;AAClB,qBAAc,KAAK,YAAY,YAAY,KAAK,UAAU;AAC1D,qBAAc,KAAK,YAAY,YAAY,KAAK,UAAU;;AAE5D,UAAI,KAAK,cACP,eAAc,KAAK,gBAAgB,KAAK;;YAGtC;KAGR,CACH;;AAGH,iBAAe,CAAC,GAAG,cAAc,CAAC;;CAIpC,MAAM,2BAA2B,YAAY;EAC3C,MAAMC,WAA2E,EAAE;EAGnF,MAAM,YAAY;AAClB,OAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK,WAAW;GACxD,MAAM,QAAQ,cAAc,MAAM,GAAG,IAAI,UAAU;AACnD,SAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,UAAU;AACzB,QAAI;KACF,MAAM,SAAS,MAAM,mBAAmB,MAAM,KAAK;AACnD,SAAI,OAAO,aAAa,OAAO,cAC7B,UAAS,MAAM,MAAM;YAEjB;KAGR,CACH;;AAGH,oBAAkB,EAAE,GAAG,UAAU,CAAC;;AAGpC,iBAAgB;AACd,MAAI,qBAAqB,QAAS;AAClC,uBAAqB,UAAU;AAC/B,uBAAqB;AACrB,4BAA0B;IACzB,EAAE,CAAC;CAGN,MAAM,oBAAoB,OACxB,YACkF;AAClF,MAAI;GAEF,MAAM,MAAM,MAAM,MAAM,qCAAqC,UAAU;AACvE,OAAI,CAAC,IAAI,GACP,QAAO;GAET,MAAMC,OAAuB,MAAM,IAAI,MAAM;GAG7C,IAAI,SAAS;AACb,OAAI,KAAK,aAAa,YAAY;IAChC,MAAM,IAAI,KAAK,YAAY;AAC3B,aAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,KAAK,YAAY,SAAS;;GAIjE,MAAM,WAAW,MAAM,mBAAmB,QAAQ;AAKlD,UAAO;IAAE,WAFS,SAAS,aAAa,KAAK,eAAe;IAExC;IAAQ,eAAe,SAAS;IAAe;UAC7D;AACN,UAAO;;;CAKX,MAAM,uBAAuB,OAAO,WAAsB;EAExD,MAAM,YAAY;EAClB,MAAM,gBAAgB,CAAC,GAAG,OAAO;AAEjC,OAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW;GACjD,MAAM,QAAQ,OAAO,MAAM,GAAG,IAAI,UAAU;AAG5C,IAFgB,MAAM,QAAQ,IAAI,MAAM,KAAK,MAAM,kBAAkB,EAAE,GAAG,CAAC,CAAC,EAEpE,SAAS,QAAQ,QAAQ;IAC/B,MAAM,WAAW,IAAI;AACrB,QAAI,UAAU,cAAc,UAC1B,eAAc,YAAY;KACxB,GAAG,cAAc;KACjB,WAAW,OAAO;KAClB,QAAQ,OAAO;KACf,eAAe,OAAO;KACtB,SAAS;KACV;aACQ,cAAc,UACvB,eAAc,YAAY;KAAE,GAAG,cAAc;KAAW,SAAS;KAAO;KAE1E;AAGF,eAAY,CAAC,GAAG,cAAc,CAAC;;;CAInC,MAAM,gBAAgB,OAAO,OAAgB,SAAS,OAAO,OAAiB,aAAa;AACzF,MAAI,UACF;AAIF,qBAAmB,SAAS,OAAO;AACnC,qBAAmB,UAAU,IAAI,iBAAiB;AAElD,eAAa,KAAK;AAClB,aAAW,GAAG;AAEd,MAAI;GACF,MAAM,SAAS,SAAS,SAAS,SAAS;GAK1C,MAAM,SAAS,IAAI,gBAAgB;IACjC,QAAQ;IACR,MAAM;IACN,WAAW;IACX,OAAO,OAAO,YAAY,EAAE;IAC7B,CAAC;AAIF,OAAI,OAAO;IACT,MAAM,cAAc,MACjB,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ;AAElB,QAAI,CAAC,YAAY,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,OAAO,CAAC,CAC5D,aAAY,KAAK,OAAO;AAE1B,WAAO,IAAI,UAAU,YAAY,KAAK,IAAI,CAAC;SAG3C,QAAO,IAAI,UAAU,OAAO;AAG9B,OAAI,SAAS,EACX,QAAO,IAAI,QAAQ,OAAO,OAAO,CAAC;GAGpC,MAAM,MAAM,MAAM,MAAM,qCAAqC,UAAU,EACrE,QAAQ,mBAAmB,QAAQ,QACpC,CAAC;AAEF,OAAI,CAAC,IAAI,GACP,OAAM,IAAI,MAAM,YAAY;GAK9B,MAAM,aAAa,CAAC,GAHO,MAAM,IAAI,MAAM,CAGf;AAC5B,OAAI,SAAS,QAEX,YAAW,MAAM,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,SAAS,GAAG;YACjD,SAAS,YAElB,YAAW,MAAM,GAAG,MAAM;IACxB,MAAM,QAAQ,EAAE,YAAY,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG;AAE9D,YADc,EAAE,YAAY,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,KAC/C;KACf;AAIJ,cAAW,MAAM,GAAG,MAAM;IACxB,MAAM,YACJ,EAAE,GAAG,SAAS,QAAQ,IAAI,EAAE,GAAG,SAAS,KAAK,IAAI,EAAE,GAAG,WAAW,kBAAkB;IACrF,MAAM,YACJ,EAAE,GAAG,SAAS,QAAQ,IAAI,EAAE,GAAG,SAAS,KAAK,IAAI,EAAE,GAAG,WAAW,kBAAkB;AACrF,QAAI,aAAa,CAAC,UAChB,QAAO;AAET,QAAI,CAAC,aAAa,UAChB,QAAO;AAET,WAAO;KACP;GAEF,MAAMC,SAAoB,WAAW,KAAK,OAAO;IAC/C,IAAI,EAAE;IACN,WAAW,EAAE,aAAa;IAC1B,OAAO,EAAE,SAAS;IAClB,WAAW,EAAE;IACb,SAAS;IACV,EAAE;AAEH,gBAAa,OAAO,UAAU,UAAU;AAExC,OAAI,QAAQ;AAEV,gBADkB,CAAC,GAAG,UAAU,GAAG,OAAO,CACpB;AAEtB,yBAAqB,OAAO;UACvB;AACL,sBAAkB,QAAQ,OAAO;AACjC,gBAAY,OAAO;AACnB,kBAAc,EAAE;AAEhB,yBAAqB,OAAO;;WAEvBC,GAAQ;AACf,OAAI,EAAE,SAAS,cAAc;AAC3B,eAAW,iDAAiD;AAC5D,QAAI,CAAC,OACH,aAAY,EAAE,CAAC;;;AAIrB,eAAa,MAAM;;AAGrB,iBAAgB;AACd,MAAI,QAAQ,iBAAiB,SAAS,WAAW,KAAK,CAAC,UACrD,gBAAe;IAEhB;EAAC;EAAK;EAAe;EAAW,SAAS;EAAO,CAAC;CAEpD,MAAM,uBAAuB;AAC3B,MAAI,CAAC,aAAa,UAChB,eAAc,iBAAiB,QAAW,KAAK;;CAKnD,MAAM,0BAA0B;AAC9B,MAAI,eAAe,MACjB,QAAO;AAET,SAAO,SAAS,QAAQ,MAAM;AAC5B,OAAI,CAAC,EAAE,OACL,QAAO;AAET,OAAI,eAAe,QACjB,QAAO,EAAE,SAAS;AAEpB,OAAI,eAAe,SACjB,QAAO,EAAE,UAAU,OAAS,EAAE,SAAS;AAEzC,OAAI,eAAe,QACjB,QAAO,EAAE,UAAU;AAErB,UAAO;IACP;;CAGJ,MAAM,iBAAiB,mBAAmB;CAE1C,MAAM,eAAe,UAA2B;AAC9C,MAAI;GACF,MAAM,gBAAgB;IACpB,KAAK,KAAK,MAAM,UAAU,MAAM,KAAK;IACrC,KAAK,KAAK,MAAM,UAAU,MAAM,KAAK,QAAQ,KAAK,KAAK,CAAC;IACxD,KAAK,KAAK,MAAM,UAAU,WAAW,MAAM,KAAK,QAAQ,KAAK,KAAK,GAAG;IACtE;AAED,QAAK,MAAM,KAAK,cACd,KAAI,GAAG,WAAW,EAAE,EAAE;AACpB,OAAG,OAAO,GAAG;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC;AAC9C;;AAIJ,wBAAqB;AACrB,oBAAiB,KAAK;AAEtB,OAAI,iBAAiB,YAAY,SAAS,EACxC,kBAAiB,KAAK,IAAI,GAAG,YAAY,SAAS,EAAE,CAAC;UAEjD;AACN,oBAAiB,KAAK;AACtB,wBAAqB;;;AAIzB,WAAU,OAAO,QAAQ;AACvB,MAAI,kBAAkB,MAAM;AAC1B,OAAI,UAAU,OAAO,UAAU,KAAK;IAElC,MAAM,QAAQ,YAAY,MACvB,MACC,EAAE,SAAS,iBACX,EAAE,KAAK,QAAQ,MAAM,IAAI,KAAK,iBAC9B,EAAE,SAAS,cAAc,QAAQ,KAAK,KAAK,CAC9C;AACD,QAAI,MACF,aAAY,MAAM;SAGpB,kBAAiB,KAAK;AAExB;;AAGF,MAAI,QAAQ,iBAAiB,iBAAiB;AAC5C,OAAI,IAAI,QAAQ;AACd,uBAAmB,MAAM;AACzB;;AAEF;;AAGF,MAAI,IAAI,KAAK;GAEX,MAAMC,WAAsD;IAC1D;IACA;IACA;IACD;GAED,MAAM,SAAS,UADM,SAAS,QAAQ,IAA0C,GACxC,KAAK,SAAS;AACtD,UAAO,OAAO;AACd,sBAAmB,MAAM;AACzB,iBAAc,OAAO;AACrB;;AAGF,MAAI,QAAQ,YAAY,QAAQ,UAAU;AAExC,OAAI,IAAI,QACN,cAAa,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAExC,OAAI,IAAI,UACN,cAAa,MAAM,KAAK,IAAI,cAAc,SAAS,GAAG,IAAI,EAAE,CAAC;AAG/D,OAAI,IAAI,UAAU,cAAc,SAAS,GAAG;IAC1C,MAAM,QAAQ,cAAc;AAC5B,QAAI,MAAM,SACR,UAAS,MAAM,gBAAgB,MAAM,KAAK,MAAM,KAAK;QAGrD,kBAAiB,MAAM,IAAI,MAAM,KAAK,CAAC,MAAM,YAAY;AACvD,SAAI,QACF,sBAAqB;MAEvB;;AAIN,OAAI,UAAU,OAAO,UAAU,KAAK;IAClC,MAAM,QAAQ,cAAc;AAC5B,QAAI,CAAC,MAAM,SACT,kBAAiB,MAAM,IAAI,MAAM,KAAK,CAAC,MAAM,YAAY;AACvD,SAAI,QACF,sBAAqB;MAEvB;;AAIN,QAAK,UAAU,OAAO,UAAU,QAAQ,cAAc,SAAS,GAAG;IAChE,MAAM,QAAQ,cAAc;AAC5B,QAAI,MAAM,SACR,kBAAiB,MAAM,KAAK;;aAGvB,QAAQ,eAAe;AAChC,OAAI,UAAU,OAAO,UAAU,KAAK;AAClC,uBAAmB,KAAK;AACxB;;AAEF,OAAI,UAAU,OAAO,UAAU,KAAK;IAElC,MAAMC,QAAoB;KAAC;KAAQ;KAAS;KAAY;IAExD,MAAM,UAAU,OADJ,MAAM,QAAQ,SAAS,GACN,KAAK,MAAM;AACxC,gBAAY,QAAQ;AACpB,sBAAkB,QAAQ,OAAO;AACjC,kBAAc,iBAAiB,QAAW,OAAO,QAAQ;AACzD;;AAEF,OAAI,UAAU,OAAO,UAAU,KAAK;IAClC,MAAMC,UAAwB;KAAC;KAAO;KAAS;KAAU;KAAQ;AAEjE,kBAAc,SADF,QAAQ,QAAQ,WAAW,GACV,KAAK,QAAQ,QAAQ;AAClD,kBAAc,EAAE;AAChB;;AAEF,OAAI,IAAI,QACN,gBAAe,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAE1C,OAAI,IAAI,WAAW;IACjB,MAAM,cAAc,KAAK,IAAI,eAAe,SAAS,GAAG,aAAa,EAAE;AACvE,kBAAc,YAAY;AAC1B,QAAI,eAAe,eAAe,SAAS,KAAK,aAAa,CAAC,UAC5D,iBAAgB;;AAGpB,OAAI,UAAU,OAAO,UAAU,IAC7B,iBAAgB;AAElB,OAAI,IAAI,UAAU,eAAe,SAAS,GAAG;IAC3C,MAAM,gBAAgB,eAAe;AAErC,QAAI,iBAAiB,cAAc,cAAc,GAAG,CAClD,UAAS,cAAc,GAAG;;AAG9B,QAAK,UAAU,OAAO,UAAU,QAAQ,eAAe,SAAS,GAAG;IACjE,MAAM,QAAQ,eAAe;AAC7B,QAAI,MACF,kBAAiB,MAAM,IAAI,MAAM,GAAG,CAAC,MAAM,YAAY;AACrD,SAAI,QACF,sBAAqB;MAEvB;;;AAMR,MAAI,QAAQ,SAAS;AAEnB,OAAI,IAAI,aAAa,IAAI,YAAY;AACnC,qBAAiB,MAAO,MAAM,QAAQ,QAAQ,MAAO;AACrD;;AAGF,OAAI,iBAAiB,OAAO;AAC1B,QAAI,IAAI,QACN,iBAAgB,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAE3C,QAAI,IAAI,UACN,iBAAgB,MAAM,KAAK,IAAI,WAAW,SAAS,GAAG,IAAI,EAAE,CAAC;AAE/D,QAAI,IAAI,QAAQ;KACd,MAAM,QAAQ,WAAW;AACzB,mBAAc,MAAM,GAAG;;UAEpB;AACL,QAAI,IAAI,QACN,iBAAgB,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAE3C,QAAI,IAAI,UACN,iBAAgB,MAAM,KAAK,IAAI,WAAW,SAAS,GAAG,IAAI,EAAE,CAAC;AAE/D,QAAI,IAAI,QAAQ;KACd,MAAM,QAAQ,WAAW;AACzB,mBAAc,MAAM,GAAG;;;;GAI7B;CAEF,MAAM,gBAAgB,UAAkB;AACtC,mBAAiB,MAAM;;CAGzB,MAAM,2BAA2B;AAC/B,eAAa,KAAK;AAClB,oBAAkB,QAAQ,OAAO;AACjC,gBAAc,iBAAiB,QAAW,MAAM;AAChD,qBAAmB,MAAM;AACzB,mBAAiB,cAAc;;CAIjC,MAAM,uBAAuB;AAC3B,MAAI,kBAAkB,KACpB,QACE,qBAAC;GAAK;;IAAS;IACP,oBAAC;KAAK,OAAM;eAAM;MAAQ;;;IAC3B;AAGX,MAAI,QAAQ,eAAe;AACzB,OAAI,gBACF,QACE,qBAAC;IAAK;;KAAS;KACI,oBAAC;MAAK,OAAM;gBAAS;OAAY;;KAAU;KAC5D,oBAAC;MAAK,OAAM;gBAAO;OAAU;;;KACxB;GAGX,MAAM,gBAAgB,eAAe;GACrC,MAAM,WAAW,gBAAgB,cAAc,cAAc,GAAG,GAAG;AACnE,UACE,qBAAC;IAAK;;KACJ,oBAAC;MAAK,OAAM;gBAAO;OAAS;;KAAO;KAClC,WACC;MACE,oBAAC;OAAK,OAAM;iBAAS;QAAY;;MAAO;SACvC,GACD;KACJ,oBAAC;MAAK,OAAM;gBAAO;OAAQ;;KAAU,oBAAC;MAAK,OAAM;gBAAO;OAAQ;;KAAQ;KACxE,oBAAC;MAAK,OAAM;gBAAO;OAAQ;;KAAU,oBAAC;MAAK,OAAM;gBAAO;OAAQ;;KAAQ;KACxE,oBAAC;MAAK,OAAM;gBAAQ;OAAQ;;KAAY,oBAAC;MAAK,OAAM;gBAAO;OAAU;;KAAG;KACxE,oBAAC;MAAK,OAAM;gBAAO;OAAU;;KACxB;;AAIX,MAAI,QAAQ,QACV,QACE,qBAAC;GAAK;;IACJ,oBAAC;KAAK,OAAM;eAAO;MAAS;;IAAkB,oBAAC;KAAK,OAAM;eAAO;MAAS;;IAAU;IACpF,oBAAC;KAAK,OAAM;eAAS;MAAY;;IAAO,oBAAC;KAAK,OAAM;eAAO;MAAU;;IAAU;IAC/E,oBAAC;KAAK,OAAM;eAAO;MAAU;;;IACxB;EAIX,MAAM,kBAAkB,cAAc;EACtC,MAAM,eAAe,mBAAmB,CAAC,gBAAgB;EACzD,MAAM,aAAa,iBAAiB;AACpC,SACE,qBAAC;GAAK;;IACJ,oBAAC;KAAK,OAAM;eAAO;MAAS;;IAAU,oBAAC;KAAK,OAAM;eAAS;MAAY;;IACtE,eACC;KACG;KAAI;KACH,oBAAC;MAAK,OAAM;gBAAQ;OAAQ;;QAC7B,GACD;IACH,aACC;KACG;KAAI;KACH,oBAAC;MAAK,OAAM;gBAAM;OAAQ;;QAC3B,GACD;IAAM;IAAI;IACZ,oBAAC;KAAK,OAAM;eAAO;MAAU;;IAAU,oBAAC;KAAK,OAAM;eAAO;MAAU;;;IACjE;;CAIX,MAAM,qBAAqB;AACzB,UAAQ,UAAR;GACE,KAAK,OACH,QAAO;GACT,KAAK,YACH,QAAO;GACT,KAAK,QACH,QAAO;;;CAIb,MAAM,uBAAuB;AAC3B,UAAQ,YAAR;GACE,KAAK,QACH,QAAO;GACT,KAAK,SACH,QAAO;GACT,KAAK,QACH,QAAO;GACT,QACE,QAAO;;;CAKb,MAAM,gBAAgB,MAAM,cAAc;EACxC,MAAMC,SAWD,EAAE;AAGP,OAAK,MAAM,UAAU,eAAe;GAClC,MAAM,WAAW,aAAa,IAAI,OAAO,GAAG,IAAI,cAAc,OAAO,KAAK;GAC1E,MAAM,OAAO,eAAe,OAAO;GACnC,MAAM,aAAa,YAAY,MAC5B,MAAM,EAAE,SAAS,OAAO,QAAQ,EAAE,KAAK,QAAQ,MAAM,IAAI,KAAK,OAAO,KACvE;GACD,MAAM,aAAa,uBAAuB,OAAO,KAAK;AACtD,UAAO,KAAK;IACV,IAAI,OAAO;IACX,MAAM,OAAO;IACb,MAAM,OAAO;IACb,MAAM,MAAM,YAAY,YAAY,KAAK,UAAU,GAAG,OAAO;IAC7D,eAAe,MAAM,iBAAiB,YAAY;IAClD,OAAO,OAAO;IACd;IACA,eAAe;IACf,UAAU,YAAY;IACtB,YAAY,aAAa,EAAE,cAAc,WAAW,cAAc,GAAG;IACtE,CAAC;;AAIJ,OAAK,MAAM,UAAU,YAInB,KAAI,CAHkB,cAAc,MACjC,MAAM,EAAE,SAAS,OAAO,QAAQ,EAAE,SAAS,OAAO,KAAK,QAAQ,MAAM,IAAI,CAC3E,EACmB;GAClB,MAAM,aAAa,uBAAuB,OAAO,KAAK;AACtD,UAAO,KAAK;IACV,IAAI,OAAO;IACX,MAAM,OAAO,KAAK,SAAS,KAAK,GAAG,OAAO,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO,OAAO;IAC1E,MAAM,OAAO,KAAK,QAAQ,MAAM,IAAI;IACpC,MACE,OAAO,cAAc,OAAO,YACxB,GAAG,OAAO,UAAU,IAAI,OAAO,UAAU,KACzC,OAAO;IACb,eAAe,OAAO;IACtB,UAAU;IACV,eAAe;IACf,UAAU,OAAO;IACjB,YAAY,aAAa,EAAE,cAAc,WAAW,cAAc,GAAG;IACtE,CAAC;;AAIN,SAAO;IACN;EAAC;EAAc;EAAgB;EAAY,CAAC;AAE/C,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,qBAAC;IAAI,cAAc;;KACjB,oBAAC;MACC,MAAM,QAAQ,YAAY,QAAQ;MAClC,OAAO,QAAQ,YAAY,QAAQ,WAAW,SAAS;gBACxD;OAEM;KACP,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK,MAAM,QAAQ;MAAS,OAAO,QAAQ,UAAU,YAAY;gBAAQ;OAEnE;KACP,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK,MAAM,QAAQ;MAAe,OAAO,QAAQ,gBAAgB,SAAS;gBAAQ;OAE5E;KACP,oBAAC;MAAK;gBAAS;OAAuB;;KAClC;IAEJ,QAAQ,YAAY,QAAQ,aAC5B;IACE,qBAAC;KAAK;;MAAS;MACA,YAAY,SAAS,IAAI,IAAI,YAAY,OAAO,YAAY;MAAG;MAAG;MAC/E,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;;MACxB;IACP,oBAAC;KAAI,WAAW;eACd,qBAAC;MAAK;;OACH,QAAQ,OAAO,GAAG;OAClB,OAAO,OAAO,GAAG;OACjB,UAAU,OAAO,GAAG;OACpB,QAAQ,OAAO,GAAG;OAClB,YAAY,OAAO,GAAG;OACtB;;OACI;MACH;IACN,oBAAC;KAAI,eAAc;KAAS,cAAc;eACvC,cAAc,WAAW,IACxB,oBAAC;MAAK,OAAM;gBAAO;OAAsD,GAEzE,cAAc,KAAK,OAAO,MAAM;MAC9B,MAAM,YAAY,iBAAiB,MAAM,MAAM,iBAAiB,MAAM;MACtE,MAAM,kBAAkB,kBAAkB,MAAM;MAChD,MAAM,aAAa,cAAc,MAAM,cAAc;AACrD,aACE,qBAAC;OACC,qBAAC;QAAK,OAAO,kBAAkB,QAAQ,MAAM,WAAW,UAAU;mBAC/D,kBAAkB,MAAM,MAAM,WAAW,MAAM,KAAK;SAChD;OACP,oBAAC;QAAK,OAAO,MAAM,gBAAgB,WAAW;kBAC3C,MAAM,gBAAgB,OAAO;SACzB;OACP,qBAAC;QAAK,OAAO,kBAAkB,QAAQ,MAAM,WAAW,SAAS;mBAC9D,MAAM,WAAW,OAAO,MACxB,MAAM,KAAK,SAAS,KACjB,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC,OAC3B,MAAM,KAAK,OAAO,GAAG;SACpB;OACP,oBAAC;QAAK;kBAAU,MAAM,KAAK,OAAO,GAAG;SAAQ;OAC7C,oBAAC;QAAK,OAAM;kBAAW,WAAW,OAAO,GAAG;SAAQ;OACpD,oBAAC;QACC,OACE,MAAM,UAAU,YACZ,UACA,MAAM,UAAU,SACd,WACA;mBAGN,MAAM,SAAS,KAAK,OAAO,GAAG;SAC3B;OACP,oBAAC;QAAK,OAAM;mBACR,MAAM,aAAa,GAAG,MAAM,WAAW,aAAa,UAAU,KAAK,OACnE,GACD;SACI;OACP,oBAAC;QAAK;kBAAU,MAAM,YAAY;SAAW;OAC5C,aAAa,oBAAC;QAAK,OAAM;kBAAO;SAAiB;OACjD,mBAAmB,oBAAC;QAAK,OAAM;kBAAM;SAAqB;WAjCnD,MAAM,GAkCV;OAER;MAEA;IACL,iBACC,oBAAC;KAAK,OAAM;eAAM;MAAyD;OAE5E;GAGJ,QAAQ,iBACP;IACE,qBAAC;KAAI,YAAW;KAAS,eAAc;KAAM,cAAc;;MACzD,oBAAC;OAAK,OAAM;iBAAO;QAAc;MACjC,oBAAC;OACC,aAAa,kBAAkB,SAAS;OACxC,aAAY;OACZ,YAAY;OACZ,UAAU;OACV,OAAO;iBAEN,kBACC,oBAAC;QACC,OAAO;QACP,UAAU;QACV,UAAU;QACV,aAAY;QACZ,OAAO;SACP,GAEF,oBAAC;QAAK,OAAM;kBAAQ,iBAAiB;SAAuB;QAE1D;MACN,qBAAC;OAAI,YAAY;;QACf,oBAAC;SAAK,OAAM;mBAAO;UAAa;QAChC,oBAAC;SAAK,OAAM;mBAAU,cAAc;UAAQ;QAC5C,oBAAC;SAAK;mBAAS;UAAW;;QACtB;MACN,qBAAC;OAAI,YAAY;;QACf,oBAAC;SAAK,OAAM;mBAAO;UAAa;QAChC,oBAAC;SAAK,OAAO,eAAe,QAAQ,WAAW;mBAAS,gBAAgB;UAAQ;QAChF,oBAAC;SAAK;mBAAS;UAAW;;QACtB;;MACF;IAEL,aAAa,SAAS,WAAW,KAChC,qBAAC,kBACC,qBAAC;KAAK,OAAM;gBACV,oBAAC,WAAQ,MAAK,SAAS,EAAC;MACnB,EACP,oBAAC;KAAK;eAAS;MAA8B,IACzC;IAGP,WAAW,oBAAC;KAAK,OAAM;eAAO;MAAe;IAE7C,eAAe,SAAS,KACvB,qBAAC;KAAI,eAAc;KAAS,SAAS;;MACnC,qBAAC;OACC,oBAAC;QAAK;kBAAU;SAAa;OAC7B,oBAAC;QAAK;QAAK;kBACR,QAAQ,OAAO,GAAG;SACd;OACP,oBAAC;QAAK;QAAK;kBACR,SAAS,SAAS,EAAE;SAChB;OACP,oBAAC;QAAK;QAAK;kBACR,OAAO,SAAS,GAAG;SACf;OACP,oBAAC;QAAK;QAAK;kBACR,UAAU,SAAS,EAAE;SACjB;OACP,oBAAC;QAAK;QAAK;kBACR,UAAU,SAAS,GAAG;SAClB;OACP,oBAAC;QAAK;QAAK;kBACR,YAAY,SAAS,GAAG;SACpB;UACH;MACL,eAAe,KAAK,OAAO,MAAM;OAChC,MAAM,WAAW,cAAc,MAAM,GAAG;AACxC,cACE,qBAAC;QACC,qBAAC;SAAK,OAAO,WAAW,UAAU;oBAAS,WAAW,MAAM,KAAI;UAAQ;QACxE,qBAAC;SAAK,OAAO,MAAM,aAAa,SAAS;oBACtC,MAAM,aAAa,OAAO,MAC1B,MAAM,GAAG,SAAS,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,GAAG,CAAC,OAAO,MAAM,GAAG,OAAO,GAAG;UACtE;QACP,oBAAC;SAAK,OAAM;mBAAU,aAAa,MAAM,QAAQ,MAAM,GAAG,CAAC,SAAS,EAAE;UAAQ;QAC9E,oBAAC;SAAK,OAAM;oBACR,MAAM,YAAY,YAAY,MAAM,UAAU,GAAG,KAAK,SAAS,GAAG;UAC/D;QACP,oBAAC;SAAK,OAAM;mBAAQ,cAAc,MAAM,cAAc,CAAC,SAAS,EAAE;UAAQ;QAC1E,oBAAC;SAAK;mBAAU,WAAW,MAAM,UAAU,CAAC,SAAS,GAAG;UAAQ;QAChE,oBAAC;SAAK,OAAM;mBAAQ,gBAAgB,MAAM,UAAU,CAAC,SAAS,GAAG;UAAQ;QACxE,MAAM,OAAO,gBAAgB,oBAAC;SAAK,OAAM;mBAAO;UAAa;YAbtD,GAAG,MAAM,GAAG,GAAG,IAcnB;QAER;MACD,aACC,qBAAC,kBACC,qBAAC;OAAK,OAAM;kBACV,oBAAC,WAAQ,MAAK,SAAS,EAAC;QACnB,EACP,oBAAC;OAAK;iBAAS;QAAsB,IACjC;MAEP,CAAC,aAAa,aACb,qBAAC;OAAK;;QAAS;QAAgB,eAAe;QAAO;;QAAc;MAEpE,CAAC,aAAa,qBAAC;OAAK;;QAAS;QAAkB,eAAe;QAAO;;QAAc;;MAChF;IAGP,CAAC,aAAa,eAAe,WAAW,KAAK,SAAS,SAAS,KAC9D,oBAAC;KAAK;eAAS;MAAwD;IAGxE,CAAC,aAAa,SAAS,WAAW,KAAK,CAAC,WACvC,oBAAC;KAAK;eAAS;MAA6B;OAE7C;GAIJ,QAAQ,WACP,qBAAC;IAAI,eAAc;eAEjB,qBAAC;KACC,aAAa,iBAAiB,QAAQ,YAAY;KAClD,aAAY;KACZ,eAAc;KACd,aAAa;KACb,UAAU;KACV,UAAU;;MAEV,oBAAC;OAAK;OAAK,OAAO,iBAAiB,QAAQ,YAAY;iBAAQ;QAExD;MACP,oBAAC;OAAI,cAAc;iBACjB,oBAAC;QAAK;kBAAS;SAAmC;QAC9C;MACL,WAAW,KAAK,OAAO,MAAM;OAC5B,MAAM,YAAY,MAAM,OAAO;OAC/B,MAAM,aAAa,iBAAiB,SAAS,MAAM;AACnD,cACE,qBAAC;QACC,qBAAC;SAAK,OAAO,YAAY,UAAU;oBAAS,YAAY,MAAM,KAAI;UAAQ;QAC1E,qBAAC;SAAK,MAAM;SAAY,OAAO,aAAa,SAAS;oBAClD,aAAa,OAAO,MACpB,MAAM;UACF;QACP,qBAAC;SAAK;;UAAS;UAAG,MAAM;UAAK;;UAAQ;QACpC,aAAa,oBAAC;SAAK,OAAM;mBAAO;UAAiB;YAP1C,MAAM,GAQV;QAER;MACF,oBAAC;OAAI,WAAW;iBACd,qBAAC;QAAK;;SAAS;SACL,oBAAC;UAAK,OAAM;oBAAU,WAAW,cAAc;WAAc;;SAAY;SACjF,oBAAC;UAAK,OAAM;oBAAS,WAAW,cAAc;WAAe;;SACxD;QACH;;MACF,EAGN,qBAAC;KACC,aAAa,iBAAiB,QAAQ,YAAY;KAClD,aAAY;KACZ,eAAc;KACd,UAAU;KACV,UAAU;;MAEV,oBAAC;OAAK;OAAK,OAAO,iBAAiB,QAAQ,YAAY;iBAAQ;QAExD;MACP,oBAAC;OAAI,cAAc;iBACjB,oBAAC;QAAK;kBAAS;SAAuC;QAClD;MACL,WAAW,KAAK,OAAO,MAAM;OAC5B,MAAM,YAAY,MAAM,OAAO;OAC/B,MAAM,aAAa,iBAAiB,SAAS,MAAM;AACnD,cACE,qBAAC;QACC,qBAAC;SAAK,OAAO,YAAY,UAAU;oBAAS,YAAY,MAAM,KAAI;UAAQ;QAC1E,qBAAC;SAAK,MAAM;SAAY,OAAO,aAAa,SAAS;oBAClD,aAAa,OAAO,MACpB,MAAM;UACF;QACP,qBAAC;SAAK;;UAAS;UAAG,MAAM;UAAK;;UAAQ;QACpC,aAAa,oBAAC;SAAK,OAAM;mBAAO;UAAiB;YAP1C,MAAM,GAQV;QAER;MACF,oBAAC;OAAI,WAAW;iBACd,qBAAC;QAAK;;SAAS;SACJ,oBAAC;UAAK,OAAM;oBAAS,WAAW,cAAc;WAAe;;SAAU;SAChF,oBAAC;UAAK,OAAM;oBAAU,WAAW,cAAc;WAAa;;SACvD;QACH;;MACF;KACF;GAGR,oBAAC;IAAI,WAAW;cAAI,gBAAgB;KAAO;;GACvC;;;;;ACrsCV,SAAgB,UAAU,EAAE,QAAQ,SAAyB;CAC3D,MAAM,CAAC,MAAM,WAAW,SAAyB,OAAO;CACxD,MAAM,CAAC,MAAM,WAAW,SAAS,IAAK;CACtC,MAAM,CAAC,cAAc,mBAAmB,SACtC,OACD;CACD,MAAM,CAAC,MAAM,WAAW,SAAmB,EAAE,CAAC;CAC9C,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAC3C,MAAM,CAAC,cAAc,mBAAmB,SAAS,EAAE;CACnD,MAAM,YAAY,OAA2B,KAAK;AAGlD,uBACc;AACV,MAAI,UAAU,SAAS;AACrB,aAAU,QAAQ,OAAO;AACzB,aAAU,UAAU;;IAGxB,EAAE,CACH;AAED,WAAU,OAAO,QAAQ;AACvB,MAAI,IAAI,IACN,UAAS,MAAO,MAAM,SAAS,QAAQ,OAAQ;AAEjD,MAAI,IAAI,UAAU,iBAAiB,UAAU,SAAS,OACpD,cAAa;AAEf,OAAK,UAAU,OAAO,UAAU,QAAQ,iBAAiB,UACvD,aAAY;AAEd,MAAI,IAAI,WAAW,iBAAiB,UAAU,SAAS,OACrD,UAAS,MAAM,KAAK,IAAI,OAAQ,IAAI,EAAE,CAAC;AAEzC,MAAI,IAAI,aAAa,iBAAiB,UAAU,SAAS,OACvD,UAAS,MAAM,KAAK,IAAI,MAAM,IAAI,EAAE,CAAC;AAEvC,OAAK,UAAU,OAAO,UAAU,QAAQ,SAAS,MAC/C,gBAAe;GAEjB;CAEF,MAAM,UAAU,QAAgB;AAC9B,WAAS,SAAS,CAAC,GAAG,KAAK,MAAM,GAAG,EAAE,oBAAG,IAAI,MAAM,EAAC,oBAAoB,CAAC,GAAG,MAAM,CAAC;;CAGrF,MAAM,oBAAoB;AACxB,kBAAgB,WAAW;AAC3B,UAAQ,EAAE,CAAC;AACX,kBAAgB,EAAE;EAElB,MAAM,SAAS,KAAK,aAAa,OAAO,KAAK,QAAQ;AAEnD,OAAI,UAAU,+BAA+B,IAAI;AACjD,OAAI,UAAU,gCAAgC,qBAAqB;AACnE,OAAI,UAAU,gCAAgC,eAAe;AAE7D,OAAI,IAAI,WAAW,WAAW;AAC5B,QAAI,UAAU,IAAI;AAClB,QAAI,KAAK;AACT;;GAGF,MAAM,MAAM,IAAI,OAAO;AAEvB,OAAI,IAAI,WAAW,UAAU,QAAQ,aAAa;IAChD,IAAI,OAAO;AACX,QAAI,GAAG,SAAS,UAAW,QAAQ,MAAO;AAC1C,QAAI,GAAG,OAAO,YAAY;AACxB,SAAI;MACF,MAAM,EAAE,QAAQ,GAAG,SAAS,KAAK,MAAM,KAAK;AAC5C,aAAO,oBAAoB,OAAO,MAAM,GAAG,GAAG,CAAC,MAAM;AACrD,uBAAiB,MAAM,IAAI,EAAE;MAE7B,MAAM,SAAS,MAAM,OAAO,SAAS,QAAQ,KAAK;AAClD,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,KAAK,UAAU,OAAO,CAAC;cACxB,GAAG;AACV,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,EAAE,EAAE,CAAC,CAAC;;MAE/C;cACO,IAAI,WAAW,UAAU,QAAQ,SAAS;IACnD,IAAI,OAAO;AACX,QAAI,GAAG,SAAS,UAAW,QAAQ,MAAO;AAC1C,QAAI,GAAG,OAAO,YAAY;AACxB,SAAI;MACF,MAAM,EAAE,QAAQ,QAAQ,GAAG,SAAS,KAAK,MAAM,KAAK;AACpD,aAAO,gBAAgB,OAAO,MAAM,GAAG,GAAG,CAAC,MAAM;AACjD,uBAAiB,MAAM,IAAI,EAAE;MAG7B,MAAM,SAAS,MAAM,OAAO,SAAS,GAAG,OAAO,oCAAoC;OACjF,GAAG;OACH,aAAa,KAAK,eAAe;OAClC,CAAC;AACF,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,OAAO,KAAK;cACb,GAAG;AACV,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,EAAE,EAAE,CAAC,CAAC;;MAE/C;cACO,IAAI,WAAW,SAAS,QAAQ,SAAS;AAClD,WAAO,YAAY;AACnB,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,OAAO,SAAS,CAAC,CAAC;cAChC,IAAI,WAAW,SAAS,QAAQ,WAAW;AACpD,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IAAI,KAAK,UAAU;KAAE,QAAQ;KAAM;KAAO,CAAC,CAAC;UAC3C;AACL,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IACF,KAAK,UAAU;KACb,OAAO;KACP,WAAW;MAAC;MAAkB;MAAc;MAAa;MAAc;KACxE,CAAC,CACH;;IAEH;AAEF,SAAO,GAAG,UAAU,QAA+B;AACjD,OAAI,IAAI,SAAS,cAAc;AAC7B,WAAO,QAAQ,KAAK,kBAAkB,OAAO,EAAE,KAAK;AACpD,aAAS,MAAM,IAAI,EAAE;AACrB,qBAAiB,aAAa,EAAE,IAAI;UAC/B;AACL,WAAO,UAAU,IAAI,UAAU;AAC/B,oBAAgB,QAAQ;;IAE1B;AAEF,SAAO,OAAO,YAAY;AACxB,UAAO,wCAAwC,OAAO;AACtD,mBAAgB,UAAU;IAC1B;AAEF,YAAU,UAAU;;CAGtB,MAAM,mBAAmB;AACvB,MAAI,UAAU,SAAS;AACrB,aAAU,QAAQ,YAAY;AAC5B,WAAO,iBAAiB;KACxB;AACF,aAAU,UAAU;AACpB,mBAAgB,OAAO;;;CAI3B,MAAM,sBAAsB;EAC1B,MAAM,SAAS,KAAK,UAClB,EACE,YAAY,EACV,QAAQ;GACN,SAAS;GACT,MAAM;IAAC;IAAM;IAAsB;IAAS;IAAQ;GACrD,EACF,EACF,EACD,MACA,EACD;EAED,MAAM,WAAW,QAAQ;EAOzB,MAAM,OAAO,KALX,aAAa,WACT,WACA,aAAa,UACX,SACA,6BACc;AACtB,OAAK,OAAO,MAAM,OAAO;AACzB,OAAK,OAAO,KAAK;AAEjB,YAAU,KAAK;AACf,mBAAiB,UAAU,MAAM,EAAE,IAAK;;AAG1C,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAK;cAAK;KAAkB;GAC7B,oBAAC;IAAK;cAAS;KAAmD;GAElE,qBAAC;IAAI,SAAS;;KACZ,oBAAC;MAAK,MAAM,SAAS;MAAQ,OAAO,SAAS,SAAS,SAAS;gBAAQ;OAEhE;KACP,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK,MAAM,SAAS;MAAO,OAAO,SAAS,QAAQ,SAAS;gBAAQ;OAE9D;KACP,oBAAC;MAAK;gBAAS;OAAuB;;KAClC;GAEN,qBAAC;IAAI,aAAY;IAAO,aAAY;IAAS,eAAc;IAAS,UAAU;;KAC5E,oBAAC;MAAK;MAAK,OAAM;gBACd,SAAS,SAAS,kBAAkB;OAChC;KACP,qBAAC,mBAAK,WACG,oBAAC;MAAK,OAAM;gBAAQ;OAAa,IACnC;KAEN,SAAS,SACR,4CACE,qBAAC;MAAK;MACE,oBAAC;OAAK,OAAM;iBAAU;QAAY;;MAAC,oBAAC;OAAK;iBAAS;QAA0B;SAC7E,EACP,oBAAC;MAAK;gBAAS;OAAkD,IAChE,GAEH,4CACE,oBAAC;MAAK;gBAAS;OAAmD,EAClE,oBAAC;MAAK;gBAAS;OAA4D,IAC1E;;KAED;GAEL,SAAS,UAAU,iBAAiB,UACnC,qBAAC;IAAI,eAAc;IAAS,SAAS;eACnC,qBAAC;KACE,iBAAiB,cAChB,qBAAC;MAAK,OAAM;iBACV,oBAAC,WAAQ,MAAK,SAAS;OAClB;KAER,iBAAiB,aAChB,qBAAC;MAAK,OAAM;;OAAQ;OACa;OAAK;OAAG;OAAa;;OAC/C;KAER,iBAAiB,WAAW,oBAAC;MAAK,OAAM;gBAAM;OAAqB;QAChE,EAEL,KAAK,SAAS,KACb,oBAAC;KACC,aAAY;KACZ,aAAY;KACZ,eAAc;KACd,WAAW;KACX,UAAU;eAET,KAAK,MAAM,GAAG,CAAC,KAAK,KAAK,MACxB,oBAAC;MAAK;gBACH;QADiB,EAEb,CACP;MACE;KAEJ;GAGP,SAAS,UAAU,iBAAiB,aACnC,qBAAC;IAAI,eAAc;IAAS,WAAW;;KACrC,oBAAC;MAAK;gBAAS;OAAiB;KAChC,qBAAC;MAAK,OAAM;;OAAQ;OAA+B;OAAK;;OAAkB;KAC1E,oBAAC;MAAK,OAAM;gBAAQ;OAA6C;KACjE,qBAAC;MAAK,OAAM;;OAAQ;OAAM;OAAuB;;OAAQ;;KACrD;GAGP,SAAS,SACR,qBAAC;IAAI,eAAc;IAAS,WAAW;;KACrC,qBAAC;MAAI,cAAc;iBACjB,oBAAC;OAAK,OAAM;iBAAS;QAAS,EAC9B,oBAAC,kBAAK,wDAA0D;OAC5D;KAEN,oBAAC;MAAK;gBAAS;OAAwB;KACvC,oBAAC;MAAK,OAAM;gBAAO;OAAyC;KAE5D,oBAAC;MAAI,WAAW;gBACd,oBAAC;OAAK;iBAAS;QAAiD;OAC5D;KACN,oBAAC;MAAK,OAAM;gBAAS;;;;;;;;OAOrB;KAEC,UACC,oBAAC;MAAI,WAAW;gBACd,oBAAC;OAAK,OAAM;iBAAQ;QAAoC;OACpD;;KAEJ;GAGR,oBAAC;IAAI,WAAW;cACd,oBAAC;KAAK;eACH,SAAS,QACR;MACE,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAe,oBAAC;OAAK,OAAM;iBAAS;QAAU;;MACzE,oBAAC;OAAK,OAAM;iBAAO;QAAU;;SAC9B,GACD,iBAAiB,SACnB;MACE,oBAAC;OAAK,OAAM;iBAAS;QAAY;;MAAS,oBAAC;OAAK,OAAM;iBAAS;QAAU;;MAAY;MACrF,oBAAC;OAAK,OAAM;iBAAO;QAAU;;SAC5B,GAEH;MACE,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAQ,oBAAC;OAAK,OAAM;iBAAO;QAAU;;SACjE;MAEA;KACH;;GACF;;;;;AClTV,MAAM,gBAAgB,IAAI,IAAI;CAC5B;CACA;CACA;CACA;CACA;CACD,CAAC;AAGF,MAAMC,gBAUF;CACF,QAAQ;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,WAAW;EACT,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,SAAS;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,QAAQ;EACN,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,SAAS;EACP,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,OAAO;EACL,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,MAAM;EACJ,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,WAAW;EACT,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CAED,kBAAkB;EAChB,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACD,sBAAsB;EACpB,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACD,sBAAsB;EACpB,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACD,kBAAkB;EAChB,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CACD,iBAAiB;EACf,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,UAAU;EACX;CAED,OAAO;EACL,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,UAAU;EACR,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACD,cAAc;EACZ,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CAED,YAAY;EACV,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACZ;CACF;AAED,SAAgB,WAAW,EAAE,QAAQ,aAAa,iBAAkC;CAClF,MAAM,CAAC,OAAO,YAAY,SAAgB,SAAS;CACnD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CACrD,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CACvE,MAAM,CAAC,YAAY,iBAAiB,SAAS,GAAG;CAChD,MAAM,CAAC,QAAQ,aAAa,SAAwB,KAAK;CACzD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,cAAc,mBAAmB,SAA4B,EAAE,CAAC;CACvE,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,EAAE;CAC3D,MAAM,CAAC,cAAc,mBAAmB,SACtC,OAAO,cAAc,EAAE,MAAM,aAC9B;CAGD,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CACvE,MAAM,CAAC,mBAAmB,wBAAwB,SAAwB,KAAK;CAC/E,MAAM,CAAC,YAAY,iBAAiB,SAAwB,KAAK;AAGjE,iBAAgB;AACd,MAAI,CAAC,aACH,oBAAmB,CAChB,WAAW;AACV,mBAAgB,KAAK;IACrB,CACD,YAAY;AACX,mBAAgB,KAAK;IACrB;AAIN,kBADkB,cAAc,CACN,OAAO;IAChC,CAAC,aAAa,CAAC;CAKlB,MAAM,WAAW,CACf,GAJa,YAAY,CAIf,KAAK,SAAS;EACtB,MAAM,OAAO,aAAa,KAAK;EAC/B,MAAM,UAAU,cAAc;EAC9B,MAAM,WAAW,SAAS,YAAY,cAAc,IAAI,KAAK;AAC7D,SAAO;GACL;GACA,aAAa,MAAM,eAAe;GAClC,SAAS,MAAM;GACf,OAAO,SAAS,UAAU,WAAW,OAAO;GAC5C,MAAM,SAAS,QAAQ,MAAM,eAAe;GAC5C,SAAS,SAAS,WAAW;GAC7B,YAAY,SAAS,cAAc;GACnC,WACE,SAAS,cAAc,WAAW,kCAAkC;GACtE;GACD;GACD,EACF;EACE,MAAM;EACN,aAAa;EACb,SAAS;EACT,OAAO;EACP,MAAM;EACN,SAAS;EACT,YAAY;EACZ,WAAW;EACX,UAAU;EACX,CACF;AAED,WAAU,OAAO,QAAQ;AACvB,MAAI,UAAU,gBAAgB;AAC5B,OAAI,IAAI,QACN,sBAAqB,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAEhD,OAAI,IAAI,UACN,sBAAqB,MAAM,KAAK,IAAI,aAAa,SAAS,GAAG,IAAI,EAAE,CAAC;AAEtE,OAAI,IAAI,UAAU,aAAa,SAAS,GAAG;IACzC,MAAM,QAAQ,aAAa;IAC3B,MAAM,UAAU,MAAM,KAAK,SAAS,IAAI,GAAG,MAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,IAAI;AACrF,oBAAgB,QAAQ;AACxB,oBAAgB,QAAQ;AACxB,aAAS,SAAS;;AAEpB,OAAI,IAAI,OACN,UAAS,SAAS;AAEpB;;AAGF,MAAI,UAAU,UAAU;AACtB,OAAI,IAAI,QACN,mBAAkB,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAAC;AAE7C,OAAI,IAAI,UACN,mBAAkB,MAAM,KAAK,IAAI,SAAS,SAAS,GAAG,IAAI,EAAE,CAAC;AAE/D,OAAI,IAAI,QAAQ;IACd,MAAM,OAAO,SAAS;AACtB,QAAI,KAAK,SAAS,aAChB,gBAAe;SACV;AACL,sBAAiB,KAAK,KAAK;AAC3B,cAAS,QAAQ;;;AAIrB,OAAI,UAAU,OAAO,UAAU,IAC7B,gBAAe;AAGjB,QAAK,UAAU,OAAO,UAAU,QAAQ,aAAa,SAAS,EAC5D,UAAS,eAAe;;AAG5B,MAAI,IAAI,WAAW,UAAU,YAAY,UAAU,SACjD,cAAa;GAEf;CAEF,MAAM,iBAAiB,OAAO,UAAkB;AAC9C,MAAI,CAAC,cACH;EAIF,MAAM,WAAW,cAAc,IAAI,cAAc,IAAI,cAAc,gBAAgB;AAEnF,WAAS,UAAU;AACnB,WAAS,KAAK;AAEd,MAAI;GACF,MAAM,QAAQ,SAAS,cAAc;GAGrC,IAAIC;AACJ,OAAI;AACF,kBAAc,KAAK,MAAM,MAAM;WACzB;AAEN,QAAI,UAAU;KAEZ,IAAI,cAAc;AAGlB,SAAI,CAAC,eAAe,MAAM,MAAM,EAAE;MAChC,MAAMC,WAAS,iBAAiB,MAAM;AACtC,UAAIA,SAAO,WAAWA,SAAO,YAC3B,eAAcA,SAAO;eACZA,SAAO,QAChB,OAAM,IAAI,MAAMA,SAAO,QAAQ;;AAInC,SAAI,CAAC,YACH,OAAM,IAAI,MAAM,uDAAuD;AAIzE,SAAI,kBAAkB,iBAEpB,eAAc;MAAE,QAAQ;MAAa,QAAQ;MAAa;SAE1D,eAAc,EAAE,OAAO,aAAa;eAE7B,kBAAkB,SAC3B,eAAc;KAAE,MAAM;KAAO,MAAM;KAAgB;aAC1C,kBAAkB,aAAa;KACxC,MAAM,UAAU,MAAM,MAAM,uBAAuB;AACnD,SAAI,QACF,eAAc;MAAE,MAAM,QAAQ,GAAG,MAAM;MAAE,IAAI,QAAQ,GAAG,MAAM;MAAE;SAEhE,eAAc;MAAE,MAAM;MAAO,IAAI;MAAW;eAErC,kBAAkB,YAC3B,eAAc;KAAE,SAAS;KAAO,QAAQ;KAAU;aACzC,kBAAkB,UAC3B,eAAc;KAAE,SAAS;KAAO,OAAO;KAAY;aAC1C,kBAAkB,SAC3B,eAAc,EAAE,MAAM,OAAO;aACpB,kBAAkB,OAC3B,eAAc;KAAE,MAAM;KAAO,WAAW;KAAQ;aACvC,kBAAkB,QAE3B,eAAc,EAAE,MAAM,OAAO;aACpB,kBAAkB,WAE3B,eAAc,EAAE,SAAS,OAAO;aACvB,kBAAkB,aAE3B,eAAc,EAAE,SAAS,OAAO;aACvB,kBAAkB,aAE3B,eAAc,EAAE,OAAO,OAAO;QAE9B,eAAc;KAAE,SAAS;KAAO,MAAM;KAAO,MAAM;KAAO,SAAS;KAAO;;GAI9E,MAAM,SAAS,MAAM,MAAM,IAAI,aAAa,OAAO;GAGnD,IAAI,cAAc,OAAO,WAAW,WAAW,SAAS,KAAK,UAAU,QAAQ,MAAM,EAAE;AACvF,iBAAc,YAAY,QAAQ,6BAA6B,GAAG,CAAC,MAAM;AAEzE,aAAU,YAAY;AACtB,YAAS,SAAS;WACX,KAAK;AACZ,YAAS,OAAO,IAAI,CAAC;AACrB,YAAS,SAAS;YACV;AAER,oBAAiB,KAAK;AACtB,wBAAqB,KAAK;;;CAI9B,MAAM,oBAAoB;AACxB,WAAS,SAAS;AAClB,mBAAiB,KAAK;AACtB,gBAAc,GAAG;AACjB,YAAU,KAAK;AACf,WAAS,KAAK;AACd,mBAAiB,KAAK;AACtB,uBAAqB,KAAK;AAC1B,gBAAc,KAAK;;CAIrB,MAAM,oBAAoB,cAAsB;AAC9C,gBAAc,KAAK;EACnB,MAAMA,WAAS,iBAAiB,UAAU;AAC1C,MAAIA,SAAO,WAAWA,SAAO,aAAa;AACxC,oBAAiBA,SAAO,YAAY;AACpC,wBAAqBA,SAAO,YAAY,QAAQ;AAChD,iBAAc,UAAU;aACfA,SAAO,QAChB,eAAcA,SAAO,QAAQ;;AAKjC,KAAI,UAAU,UAAU;EACtB,MAAM,cAAc,SAAS;AAE7B,SACE,qBAAC;GAAI,eAAc;GAAS,SAAS;;IAEnC,qBAAC;KAAI,eAAc;KAAS,cAAc;;MACxC,oBAAC;OAAK;OAAK,OAAM;iBAAO;QAEjB;MACP,oBAAC;OAAK;iBAAS;QAGR;MACP,oBAAC;OAAI,WAAW;iBACd,qBAAC;QAAK;;SACJ,oBAAC;UAAK,OAAM;oBAAS;WAAiB;;SAAS;SAAU;;SACpD;QACH;MACN,qBAAC;OAAK;;QAAS;QACH,oBAAC;SAAK,OAAM;mBAAO;UAAoB;;QAAG;QACpD,oBAAC;SAAK,OAAM;mBAAO;UAAiC;;QAAG;QACvD,oBAAC;SAAK,OAAM;mBAAO;UAAkC;;QAChD;;MACH;IAGN,qBAAC;KAAI,eAAc;gBAEjB,qBAAC;MAAI,eAAc;MAAS,aAAa;MAAG,UAAU;iBACpD,oBAAC;OAAI,cAAc;iBACjB,oBAAC;QAAK;kBAAK;SAAwB;QAC/B,EACL,SAAS,KAAK,MAAM,MAAM;OACzB,MAAM,aAAa,MAAM;OACzB,MAAM,WAAW,KAAK,SAAS;AAC/B,cACE,qBAAC,kBACC,qBAAC;QACC,MAAM;QACN,OACE,aAAc,WAAW,UAAU,KAAK,WAAW,WAAW,SAAU;;SAGzE,aAAa,OAAO;SAAK;SAAE,KAAK;SAAM;SAAE;SACxC,WAAW,kBAAkB,KAAK;;SAC9B,EACN,KAAK,YAAY,CAAC,cAAc,oBAAC;QAAK;kBAAS;SAAU,KAVlD,KAAK,KAWT;QAER;OACE,EAGN,qBAAC;MACC,aACE,YAAY,SAAS,eAAe,UAAU,YAAY,WAAW,WAAW;MAElF,aAAY;MACZ,eAAc;MACd,UAAU;MACV,UAAU;MACV,UAAU;;OAEV,qBAAC,kBACC,oBAAC;QACC;QACA,OACE,YAAY,SAAS,eACjB,UACA,YAAY,WACV,WACA;kBAGP,YAAY,SAAS,eAAe,qBAAqB,YAAY;SACjE,EACN,YAAY,YAAY,oBAAC;QAAK,OAAM;kBAAS;SAAiB,IAC3D;OAEL,YAAY,WAAW,oBAAC;QAAK;kBAAS;SAAiB;OAExD,oBAAC;QAAI,WAAW;kBACd,oBAAC;SAAK,MAAK;mBAAQ,YAAY;UAAY;SACvC;OAEL,YAAY,WACX,qBAAC;QAAI,eAAc;QAAS,WAAW;mBACrC,oBAAC;SAAK;mBAAS;UAAsB,EACrC,qBAAC;SAAK,OAAM;oBACT,YAAY,QAAQ,MAAM,GAAG,GAAG,EAChC,YAAY,QAAQ,SAAS,KAAK,QAAQ;UACtC;SACH;OAGP,YAAY,cACX,qBAAC;QAAI,eAAc;QAAS,WAAW;mBACrC,oBAAC;SAAK;mBAAS;UAAiB,EAChC,oBAAC;SAAK,OAAM;mBAAU,YAAY;UAAkB;SAChD;OAGP,YAAY,YACX,qBAAC;QAAI,eAAc;QAAS,WAAW;mBACrC,oBAAC;SAAK;mBAAS;UAAa,EAC5B,oBAAC;SAAK,OAAM;mBAAO;UAAoD;SACnE;OAGP,YAAY,SAAS,gBACpB,oBAAC;QAAI,WAAW;kBACd,qBAAC;SAAK;;UAAS;UACP,oBAAC;WAAK,OAAM;qBAAO;YAAY;;;UAChC;SACH;;OAEJ;MACF;IAEN,qBAAC;KAAI,eAAc;KAAS,WAAW;gBACrC,qBAAC;MACC,oBAAC;OAAK;iBAAS;QAAc;MAC7B,oBAAC;OAAK,OAAM;iBAAQ;QAAoB;MACvC,aAAa,SAAS,KAAK,oBAAC;OAAK;iBAAS;QAAe;MACzD,aAAa,SAAS,KAAK,oBAAC;OAAK,OAAM;iBAAS;QAAQ;MACxD,aAAa,SAAS,KAAK,oBAAC;OAAK;iBAAS;QAAkB;SACzD,EACN,oBAAC;MAAI,WAAW;gBACd,qBAAC;OAAK;;QACJ,oBAAC;SAAK,OAAM;mBAAS;UAAc;;QAAY,oBAAC;SAAK,OAAM;mBAAS;UAAY;QAAC;QAAI;QAC5E,oBAAC;SAAK,OAAM;mBAAS;UAAQ;;QAAS,oBAAC;SAAK,OAAM;mBAAO;UAAU;;;QACvE;OACH;MACF;;IACF;;AAKV,KAAI,UAAU,eACZ,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAK;IAAK,OAAM;cAAO;KAEjB;GACP,oBAAC;IAAI,cAAc;cACjB,oBAAC;KAAK;eAAS;MAA+C;KAC1D;GAEN,oBAAC;IAAI,eAAc;IAAS,SAAS;cAClC,aAAa,WAAW,IACvB,oBAAC;KAAK,OAAM;eAAO;MAA+D,GAElF,aAAa,KAAK,OAAO,MAAM;KAC7B,MAAM,aAAa,MAAM;KACzB,MAAM,YACJ,MAAM,SAAS,gBAAgB,MAAM,KAAK,QAAQ,MAAM,IAAI,KAAK;AACnE,YACE,qBAAC;MACC,qBAAC;OACC,MAAM;OACN,OAAO,aAAa,SAAS,YAAY,UAAU;kBAElD,aAAa,OAAO,MACpB,MAAM,KAAK,SAAS,KAAK,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC,OAAO,MAAM;QAC7D;MACP,qBAAC;OAAK,OAAM;kBAAS,KAAE,MAAM;QAAiB;MAC7C,aAAa,oBAAC;OAAK,OAAM;iBAAQ;QAAiB;UAT3C,MAAM,KAUV;MAER;KAEA;GAEN,oBAAC;IAAI,WAAW;cACd,qBAAC;KAAK;;MACJ,oBAAC;OAAK,OAAM;iBAAS;QAAc;;MAAY,oBAAC;OAAK,OAAM;iBAAS;QAAY;;MAC9E,oBAAC;OAAK,OAAM;iBAAO;QAAU;;;MAC1B;KACH;;GACF;AAKV,KAAI,UAAU,SAAS;EACrB,MAAM,OAAO,aAAa,cAAe;EACzC,MAAM,UAAU,cAAc;EAC9B,MAAM,WAAW,cAAc,IAAI,cAAe,IAAI,SAAS;AAE/D,SACE,qBAAC;GAAI,eAAc;GAAS,SAAS;;IACnC,qBAAC;KAAI,eAAc;KAAS,cAAc;gBACxC,qBAAC,kBACC,qBAAC;MAAK;MAAK,OAAO,WAAW,WAAW;;OAAQ;OAC5C,SAAS,SAAS;OAAI;OAAG;;OACtB,EACN,YACC,qBAAC;MAAK,OAAM;MAAS;iBAClB,KAAI;OAEA,IAEL,EACN,oBAAC;MAAK;gBAAU,SAAS,QAAQ,MAAM;OAAmB;MACtD;IAEL,SAAS,WACR,qBAAC;KAAI,cAAc;gBACjB,oBAAC;MAAK;gBAAS;OAAuB,EACtC,qBAAC;MAAK,OAAM;iBACT,QAAQ,QAAQ,MAAM,GAAG,GAAG,EAC5B,QAAQ,QAAQ,SAAS,KAAK,QAAQ;OAClC;MACH;IAIP,YACC,qBAAC;KAAI,eAAc;KAAS,cAAc;gBACxC,qBAAC,kBACC,oBAAC;MAAK,OAAM;gBAAS;OAAU,EAC/B,oBAAC;MAAK,OAAM;gBAAS,SAAS,aAAa;OAAwC,IAC/E,EACN,oBAAC;MAAK;gBAAS;OAA8D;MACzE;IAIP,CAAC,YACA,oBAAC;KAAI,cAAc;eACjB,oBAAC;MAAK,OAAM;gBAAS,SAAS,aAAa;OAAsB;MAC7D;IAIP,iBAAiB,qBAChB,oBAAC;KAAI,cAAc;eACjB,qBAAC;MAAK,OAAM;iBAAQ,mBAAgB;OAAyB;MACzD;IAIP,cACC,oBAAC;KAAI,cAAc;eACjB,oBAAC;MAAK,OAAM;gBAAO;OAAkB;MACjC;IAGR,qBAAC;KAAI,aAAa,WAAW,WAAW;KAAQ,aAAY;KAAS,UAAU;gBAC7E,oBAAC;MAAK,OAAO,WAAW,WAAW;gBAAQ;OAAY,EACvD,oBAAC;MACC,WAAW,QAAQ;AACjB,qBAAc,IAAI;AAElB,WAAI,YAAY,IAAI,MAAM,CACxB,kBAAiB,IAAI;;MAGzB,UAAU;MACV,aAAa,WAAW,uCAAuC;MAC/D,OAAO;OACP;MACE;IAEN,oBAAC;KAAI,WAAW;eACd,qBAAC;MAAK;;OACJ,oBAAC;QAAK,OAAM;kBAAS;SAAY;;OAAO,oBAAC;QAAK,OAAM;kBAAO;SAAU;;OACpE,YAAY,iBAAiB,oBAAC;QAAK,OAAM;kBAAQ;SAAuB;;OACpE;MACH;;IACF;;AAKV,KAAI,UAAU,WAAW;EACvB,MAAM,UAAU,cAAc;AAC9B,SACE,qBAAC;GAAI,eAAc;GAAS,SAAS;;IACnC,oBAAC;KAAI,cAAc;eACjB,qBAAC;MAAK;MAAK,OAAM;;OAAO;OACpB,SAAS,SAAS;OAAI;OAAG;;OACtB;MACH;IACN,qBAAC,kBACC,qBAAC;KAAK,OAAM;gBACV,oBAAC,WAAQ,MAAK,SAAS,EAAC;MACnB,EACP,oBAAC,kBAAK,kBAAoB,IACtB;IACN,oBAAC;KAAI,WAAW;eACd,oBAAC;MAAK;gBAAS;OAAkC;MAC7C;;IACF;;AAKO,eAAc;AAC/B,QACE,qBAAC;EAAI,eAAc;EAAS,SAAS;;GACnC,oBAAC;IAAI,cAAc;cACjB,qBAAC;KAAK;KAAK,OAAO,QAAQ,QAAQ;;MAC/B,QAAQ,cAAc;MAAS;MAAE;;MAC7B;KACH;GAEN,oBAAC;IACC,aAAa,QAAQ,QAAQ;IAC7B,aAAY;IACZ,eAAc;IACd,SAAS;cAET,oBAAC;KAAK,OAAO,QAAQ,QAAQ;KAAS,MAAK;eACxC,SAAS;MACL;KACH;GAEN,oBAAC;IAAI,WAAW;cACd,qBAAC;KAAK;gBACJ,oBAAC;MAAK,OAAM;gBAAO;OAAU;MACxB;KACH;;GACF;;;;;ACrsBV,SAAgB,UAAU,EAAE,gBAAgC;CAC1D,MAAM,CAAC,OAAO,YAAY,SAAqB,EAAE,CAAC;CAClD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CACrD,MAAM,CAAC,cAAc,mBAAmB,SAAwB,KAAK;CACrE,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAG5C,MAAM,CAAC,cAAc,mBAAmB,SAAuB,OAAO;CACtE,MAAM,CAAC,cAAc,mBAAmB,SAAS,GAAG;CACpD,MAAM,CAAC,eAAe,oBAAoB,SAAwB,KAAK;CACvE,MAAM,CAAC,cAAc,mBAAmB,SAAwB,KAAK;AAErE,iBAAgB;EACd,MAAM,eAAe,YAAY;GAC/B,MAAMC,WAAuB,EAAE;GAG/B,MAAM,iBAAiB,MAAM,kBAAkB;AAG/C,QAAK,MAAM,UAAU,eACnB,KAAI,CAAC,OAAO,OACV,UAAS,KAAK;IACZ,MAAM,OAAO;IACb,aAAa,OAAO,SAAS;IAC7B,QAAQ;IACR,MAAM,OAAO;IACb,OAAO,OAAO;IACf,CAAC;GAKN,MAAM,OAAO,oBAAoB;GACjC,MAAM,WAAW,KAAK,KAAK,QAAQ,KAAK,EAAE,WAAW,QAAQ;AAE7D,QAAK,MAAM,KAAK,MAAM;IACpB,MAAM,YAAY,EAAE,KAAK,WAAW,UAAU;IAC9C,IAAIC;AAEJ,QAAI,CAAC,WAAW;KAEd,MAAM,SAAS,KAAK,KAAK,UAAU,GAAG,EAAE,KAAK,UAAU;KACvD,MAAM,SAAS,KAAK,KAAK,UAAU,GAAG,EAAE,KAAK,UAAU;AACvD,SAAI,GAAG,WAAW,OAAO,CACvB,YAAW;cACF,GAAG,WAAW,OAAO,CAC9B,YAAW;;AAIf,aAAS,KAAK;KACZ,MAAM,EAAE;KACR,aAAa,EAAE;KACf,QAAQ,YAAY,YAAY;KAChC,MAAM;KACN,YAAY,EAAE,aAAa,EAAE,GAAG;KACjC,CAAC;;AAGJ,YAAS,SAAS;AAClB,cAAW,MAAM;;AAGnB,gBAAc;IACb,EAAE,CAAC;CAEN,MAAM,eAAe,gBAAgB,IAAI,MAAM,gBAAgB,KAAK;CAEpE,MAAM,cAAc,OAAO,UAAkB,aAAqB;AAChE,kBAAgB,UAAU;AAC1B,kBAAgB,KAAK;AACrB,mBAAiB,KAAK;AAEtB,MAAI;GACF,MAAM,OAAO,QAAQ,SAAS;AAC9B,OAAI,CAAC,KACH,OAAM,IAAI,MAAM,SAAS,SAAS,aAAa;GAMjD,IAAIC,SAAc,EAAE;GACpB,MAAM,WAAW,YAAY,IAAI,MAAM;AAEvC,OAAI,QACF,KAAI,QAAQ,WAAW,IAAI,CACzB,UAAS,KAAK,MAAM,QAAQ;OAG5B,UAAS;IACP,OAAO;IACP,OAAO;IACP,MAAM;IACN,OAAO;IACP,SAAS;IACT,OAAO;IACR;AAKL,oBADe,MAAM,KAAK,OAAO,CACT;AACxB,mBAAgB,OAAO;WAChB,GAAG;AACV,mBAAgB,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC;AAC3D,mBAAgB,OAAO;;;CAI3B,MAAM,uBAAuB,UAAkB;AAC7C,MAAI,gBAAgB,aAAa,WAAW,QAC1C,aAAY,aAAa,MAAM,MAAM;;AAIzC,WAAU,MAAM,QAAQ;AAEtB,MAAI,IAAI,UAAU,iBAAiB,QAAQ;AACzC,mBAAgB,OAAO;AACvB,mBAAgB,GAAG;AACnB,oBAAiB,KAAK;AACtB,mBAAgB,KAAK;AACrB;;AAIF,MAAI,iBAAiB,WAAW,iBAAiB,UAC/C;AAIF,MAAI,iBAAiB,QAAQ;AAC3B,mBAAgB,OAAO;AACvB,oBAAiB,KAAK;AACtB,mBAAgB,KAAK;AACrB;;AAGF,MAAI,IAAI,WAAW,gBAAgB,GAAG;AACpC,oBAAiB,gBAAgB,EAAE;AACnC,mBAAgB,KAAK;;AAEvB,MAAI,IAAI,aAAa,gBAAgB,MAAM,QAAQ;AACjD,oBAAiB,gBAAgB,EAAE;AACnC,mBAAgB,KAAK;;AAEvB,MAAI,IAAI,QACN;OAAI,kBAAkB,EACpB,eAAc;YACL,aAET,iBAAgB,iBAAiB,aAAa,OAAO,OAAO,aAAa,KAAK;;AAGlF,MAAI,SAAS,OAAO,SAAS,IAC3B,eAAc;AAGhB,OAAK,SAAS,OAAO,SAAS,QAAQ,cAAc,KAClD,MAAK,SAAS,aAAa,KAAK,GAAG;AAGrC,OAAK,SAAS,OAAO,SAAS,QAAQ,gBAAgB,aAAa,WAAW,SAAS;AACrF,mBAAgB,GAAG;AACnB,mBAAgB,QAAQ;;GAE1B;AAEF,KAAI,QACF,QACE,oBAAC;EAAI,eAAc;EAAS,UAAU;YACpC,oBAAC;GAAK;aAAS;IAAuB;GAClC;AAIV,QACE,qBAAC;EAAI,eAAc;EAAS,UAAU;;GACpC,qBAAC;IAAI,cAAc;eACjB,oBAAC;KAAK;KAAK,OAAM;eAAU;MAEpB,EACP,qBAAC;KAAK;;MAAS;MAAI,MAAM;MAAO;;MAAkB;KAC9C;GAGN,oBAAC;IAAI,cAAc;cACjB,qBAAC;KAAK,OAAO,kBAAkB,IAAI,SAAS;gBACzC,kBAAkB,IAAI,OAAO,MAC9B,oBAAC;MAAK;MAAK,OAAM;gBAAQ;OAElB;MACF;KACH;GAGN,oBAAC;IAAI,eAAc;cAChB,MAAM,WAAW,IAChB,oBAAC;KAAK;eAAS;MAA8B,GAE7C,MAAM,KAAK,MAAM,MAAM;KACrB,MAAM,aAAa,kBAAkB,IAAI;KACzC,MAAM,aAAa,iBAAiB,KAAK;AAEzC,YACE,qBAAC;MAAI,eAAc;iBACjB,qBAAC;OACC,qBAAC;QAAK,OAAO,aAAa,SAAS;mBAChC,aAAa,OAAO,MACrB,oBAAC;SACC;SACA,OAAO,aAAa,SAAS,KAAK,WAAW,UAAU,QAAQ;mBAE9D,KAAK;UACD;SACF;OACP,qBAAC;QACC,OAAO,KAAK,WAAW,UAAU,QAAQ;QACzC,UAAU,KAAK,WAAW;;SAEzB;SAAI;SACF,KAAK,YAAY,MAAM,GAAG,GAAG;SAC/B,KAAK,YAAY,SAAS,KAAK,QAAQ;;SACnC;OACP,qBAAC;QACC,OACE,KAAK,WAAW,YACZ,WACA,KAAK,WAAW,UACd,QACA;QAER,UAAU,KAAK,WAAW;;SAEzB;SAAI;SACH,KAAK;SAAO;;SACT;UACH,EAGL,cACC,oBAAC;OACC,aAAa,KAAK,WAAW,UAAU,QAAQ;OAC/C,aAAY;OACZ,eAAc;OACd,YAAY;OACZ,SAAS;OACT,UAAU;iBAET,KAAK,WAAW,UACf;QACE,qBAAC;SACC,oBAAC;UAAK,OAAM;oBAAM;WAAa;;SAAE,KAAK;YACjC;QACP,qBAAC;SACC,oBAAC;UAAK,OAAM;oBAAO;WAAY;;SAAE,KAAK;YACjC;QACP,oBAAC;SAAK;mBAAS;UAA0C;WACxD,GAEH;QACE,qBAAC;SACC,oBAAC;UAAK,OAAM;oBAAO;WAAmB;;SAAE,KAAK;YACxC;QACP,qBAAC;SACC,oBAAC;UAAK,OAAM;oBAAO;WAAc;SAAC;SACjC,KAAK,WAAW,YACb,iCACA,KAAK,QAAQ;YACZ;QACN,KAAK,WAAW,aAAa,KAAK,QACjC,oBAAC;SAAK;mBAAS;UAAkC;WAElD;QAED;QAtEuB,KAAK,KAwEhC;MAER;KAEA;GAGL,iBAAiB,WAAW,gBAC3B,qBAAC;IACC,aAAY;IACZ,aAAY;IACZ,eAAc;IACd,SAAS;IACT,UAAU;;KAEV,qBAAC;MAAK;MAAK,OAAM;iBAAS,aACd,aAAa;OAClB;KACP,oBAAC;MAAK;gBAAS;OAA2D;KAC1E,qBAAC;MAAI,WAAW;iBACd,oBAAC;OAAK,OAAM;iBAAS;QAAY,EACjC,oBAAC;OACC,UAAU;OACV,UAAU;OACV,aAAY;OACZ,OAAO;QACP;OACE;;KACF;GAGP,iBAAiB,aAChB,qBAAC;IAAI,SAAS;IAAG,UAAU;eACzB,oBAAC;KAAK,OAAM;eACV,oBAAC,WAAQ,MAAK,SAAS;MAClB,EACP,qBAAC;KAAK;KAAU,cAAc;KAAK;QAAU;KACzC;GAGP,iBAAiB,UAChB,qBAAC;IACC,aAAa,eAAe,QAAQ;IACpC,aAAY;IACZ,eAAc;IACd,SAAS;IACT,UAAU;;KAEV,oBAAC;MAAK;MAAK,OAAO,eAAe,QAAQ;gBACtC,eAAe,YAAY;OACvB;KACP,oBAAC;MAAK,OAAO,eAAe,QAAQ;gBACjC,gBAAgB,iBAAiB;OAC7B;KACP,oBAAC;MAAK;gBAAS;OAA+B;;KAC1C;GAGR,oBAAC;IAAI,WAAW;cACd,qBAAC;KAAK;;MACJ,oBAAC;OAAK,OAAM;iBAAS;QAAQ;;MAAU,oBAAC;OAAK,OAAM;iBAAQ;QAAQ;;MACnE,oBAAC;OAAK,OAAM;iBAAO;QAAY;;MAAS,oBAAC;OAAK,OAAM;iBAAO;QAAS;;MACpE,oBAAC;OAAK,OAAM;iBAAO;QAAU;;;MACxB;KACH;;GACF;;;;;ACzTV,MAAM,aAAa;CACjB;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACD;EACE,KAAK;EACL,MAAM;EACN,OAAO;EACP,MAAM;EACN,OAAO;EACR;CACF;AAED,SAAS,YAAY,KAAa;AAOhC,MALE,QAAQ,aAAa,WACjB,SAAS,IAAI,KACb,QAAQ,aAAa,UACnB,UAAU,IAAI,KACd,aAAa,IAAI,GAChB;;AAGX,MAAM,iBACJ;AAeF,SAAS,cAAc,MAAY,MAAmB,EAAE,EAAU;CAEhE,MAAM,SACJ;AAEF,SAAQ,MAAR;EACE,KAAK,OACH,QAAO,GAAG,OAAO;EACnB,KAAK,SACH,QAAO,GAAG,OAAO;EACnB,KAAK;AACH,OAAI,IAAI,aAAa,SACnB,QAAO,GAAG,OAAO;AAEnB,OAAI,IAAI,aAAa,UAAU;IAC7B,MAAM,QAAQ,IAAI,qBAAqB;AACvC,WAAO,GAAG,OAAO,IACf,QAAQ,IAAI,QAAQ,MAAM,sBAAsB,oBACjD;;AAGH,OAAI,IAAI,cACN,QAAO,GAAG,OAAO,eAAe,IAAI,cAAc;AAEpD,UAAO,GAAG,OAAO;EAEnB,KAAK,eACH,QAAO,GAAG,OAAO;EACnB,KAAK,OACH,QAAO,GAAG,OAAO;EACnB,KAAK,aAAa;GAChB,MAAM,QAAQ,IAAI;AAClB,OAAI,CAAC,SAAS,MAAM,aAAa,EAC/B,QAAO,GAAG,OAAO;GAGnB,MAAM,OAAO,MAAM;GACnB,MAAM,QAAQ,MAAM;GACpB,MAAM,OAAO,MAAM;GAGnB,IAAI,UAAU,sBAAsB,MAAM,SAAS;AAEnD,OAAI,KACF,YAAW,aAAa,KAAK,aAAa,UAAU,KAAK,aAAa,oBAAoB,KAAK,OAAO;AAGxG,OAAI,MACF,YAAW,oBAAoB,MAAM,WAAW,aAAa,UAAU,MAAM,WAAW,MAAM,GAAG,MAAM,WAAW,OAAO,0BAA0B,MAAM,kBAAkB,aAAa;AAG1L,OAAI,KAAK,SAAS,GAAG;IACnB,MAAM,UAAU,KAAK,KAClB,MAAM,GAAG,EAAE,MAAM,MAAM,EAAE,OAAO,QAAQ,EAAE,aAAa,cAAc,EAAE,KAAK,OAC9E;AACD,eAAW,aAAa,QAAQ,KAAK,KAAK,CAAC;;AAI7C,OAAI,QAAQ,SAAS,KAAK,iBAAiB,MAAM,WAAW,aAC1D,YAAW;YACF,QAAQ,OAAO;IACxB,MAAM,OAAO,MAAM,WAAW,eAAe,KAAK;AAClD,QAAI,OAAO,EACT,YAAW,gBAAgB,KAAK;;AAIpC,UAAO,4GAA4G,QAAQ;;EAE7H,KAAK,OACH,QAAO,GAAG,OAAO;EACnB,KAAK,QACH,QAAO,GAAG,OAAO;EACnB,KAAK,aACH,QAAO,GAAG,OAAO;EACnB,QACE,QAAO;;;AAOL,cAAc,OAAO,EACnB,cAAc,SAAS,EAEf,cAAc,eAAe,EAEvC,cAAc,OAAO;AAiB7B,SAAgB,IAAI,EAAE,cAAc,QAAQ,iBAA2B,EAAE,EAAE;CACzE,MAAM,EAAE,SAAS,QAAQ;CACzB,MAAM,YAAY,OAAsB,KAAK;CAI7C,MAAM,CAAC,OAAO,YAAY,SAAmB;EAC3C,MAAM;EACN,QAAQ;EACR,OAAO;EACP,SAAS;EACT,gBAAgB;EAChB,cAAc;EACd,WAAW;EACX,WAAW;EACX,gBAAgB;EAChB,YAZoB,iBAAiB,QAAQ,IAAI,eAAe,MAAM,QAAQ;EAa9E,UAAU;EACV,UAAU;EACX,CAAC;CACF,MAAM,CAAC,aAAa,kBAAkB,SACpC,gBAAgB,SAAS,cAAc,KACxC;CACD,MAAM,CAAC,eAAe,oBAAoB,SAAS,EAAE;CACrD,MAAM,CAAC,mBAAmB,wBAAwB,SAAS,EAAE;CAC7D,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,CAAC,gBAAgB,qBAAqB,SAA8B,KAAK;CAC/E,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,GAAG;CACxD,MAAM,CAAC,SAAS,cAAc,SAAS,GAAG;CAC1C,MAAM,CAAC,eAAe,oBAAoB,SAAS,MAAM;CACzD,MAAM,mBAAmB,OAAO,MAAM;CACtC,MAAM,CAAC,cAAc,mBAAmB,SAAS,MAAM;CACvD,MAAM,CAAC,UAAU,eAAe,SAAwD,SAAS;CACjG,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,CAAC,iBAAiB,sBAAsB,SAAsC,OAAO;CAC3F,MAAM,CAAC,qBAAqB,0BAA0B,UAAsC;CAG5F,MAAM,CAAC,gBAAgB,qBAAqB,SAAmB,EAAE,CAAC;CAClE,MAAM,CAAC,iBAAiB,sBAAsB,SAAS,MAAM;CAC7D,MAAM,CAAC,eAAe,oBAAoB,SAAS,GAAG;CACtD,MAAM,cAAc;EAAC;EAAM;EAAM;EAAQ;EAAQ;EAAQ;EAAS;EAAQ;EAAS;EAAK;EAAI;CAE5F,MAAM,CAAC,cAAc,mBAAmB,SAAuB;EAC7D,SAAS;EACT,UAAU;EACV,WAAW;EACX,aAAa;EACb,WAAW,KAAK,KAAK;EACrB,cAAc,EAAE;EAChB,eAAe;EACf,cAAc;EACf,CAAC;CAGF,MAAM,CAAC,kBAAkB,uBAAuB,SAA4B,EAAE,CAAC;CAC/E,MAAM,CAAC,iBAAiB,sBAAsB,SAAmB,EAAE,CAAC;CACpE,MAAM,CAAC,mBAAmB,wBAAwB,SAAS,MAAM;CAGjE,MAAM,CAAC,aAAa,kBAAkB,SAAmC,KAAK;CAC9E,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,CAAC,eAAe,oBAAoB,SAAS,MAAM;AAGzD,iBAAgB;AACd,MAAI,kBAAkB,mBAAmB;AACvC,wBAAqB,cAAc;AACnC,qBAAkB,KAAK;GACvB,MAAM,QAAQ,iBAAiB,kBAAkB,MAAM,EAAE,IAAI;AAC7D,gBAAa,aAAa,MAAM;;IAEjC,CAAC,eAAe,kBAAkB,CAAC;CAGtC,MAAM,oBAAoB,YAAkB;AAC1C,oBAAkB,MAAM;AACxB,mBAAiB;AACf,aAAU,OAAO;IAAE,GAAG;IAAG,MAAM;IAAS,EAAE;AAC1C,qBAAkB,KAAK;AACvB,oBAAiB,kBAAkB,KAAK,EAAE,IAAI;KAC7C,IAAI;;CAIT,MAAM,kBAAkB,YAAY,OAAO,MAAc;AACvD,MAAI;AACF,qBAAkB,GAAG;AACrB,cAAW,MAAM,SAAS,EAAE,OAAO,gBAAgB,EAAE,WAAW,IAAI,CAAC,CACnE,oBAAmB,MAAM,IAAI,MAAM;UAE/B;AAEN,qBAAkB,iBAAiB;;IAEpC,EAAE,CAAC;CAGN,MAAM,2BAA2B,OAAO,MAAM;AAI9C,iBAAgB;AAEd,MAAI,yBAAyB,QAC3B;AAEF,2BAAyB,UAAU;EAEnC,IAAI,UAAU;EAEd,MAAM,YAAY,YAAY;GAC5B,MAAM,IAAI,IAAI,QAAQ;AACtB,OAAI;IAEF,MAAM,SAAS,MAAM;AAErB,UAAM,EAAE,UAAU,MAAM,OAAO;KAC7B;KACA,aAAa,MAAM;AACjB,UAAI,CAAC,QACH;AAEF,UAAI,EAAE,QAAQ,SAAS,qCAAqC,CAC1D;AAEF,gBAAU,OAAO;OACf,GAAG;OACH,gBAAgB,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE;OAC9D,EAAE;;KAEN,CAAC;AAEF,QAAI,CAAC,SAAS;AAEZ,OAAE,SAAS;AACX;;IAIF,MAAM,mBADY,EAAE,cAAc,EACE,oBAAoB;AAGxD,mBAAe,EACb,UAAU,OAAO,WAAmB;AAElC,aADe,MAAM,EAAE,SAAS,QAAQ,EAAE,WAAW,KAAK,CAAC,EAC7C;OAEjB,CAAC;AAEF,cAAU,UAAU;AACpB,cAAU,OAAO;KACf,GAAG;KACH,QAAQ;KACR,SAAS;KACT,cAAc;KAEd,MAAM,eAAe;KACtB,EAAE;AAGH,QAAI,YACF,gBAAe,KAAK;AAMtB,QADiB,EAAE,eAAe,KAAK,SAErC,kBAAiB,gBAAgB,EAAE,CAAC,YAAY,GAAG,EAAE,KAAK;QAE1D,iBAAgB,EAAE,CAAC,YAAY,GAAG;YAE7B,OAAO;AACd,QAAI,CAAC,QACH;AAEF,cAAU,OAAO;KACf,GAAG;KACH,SAAS;KACT,gBAAgB,UAAU;KAC3B,EAAE;;;AAGP,aAAW;AAEX,eAAa;AACX,aAAU;AAEV,OAAI,UAAU,SAAS;AAErB,WAAO,uBAAc,MAAM,EAAE,6CAAwB;AAEnD,yBADuB,UAAU,SAAS,QAAQ,KAAK,IAAI,QAAQ,SAAS,CAC3C;MACjC;AACF,cAAU,UAAU;;;IAIvB,EAAE,CAAC;AAGN,iBAAgB;EACd,MAAM,qBAAqB,YAAY;GACrC,MAAM,SAAS,MAAM,gBAAgB;AACrC,OAAI,OAAO,gBACT,gBAAe,OAAO;;AAK1B,sBAAoB,CAAC,YAAY,GAE/B;IACD,EAAE,CAAC;CAGN,MAAM,eAAe,YAAY;AAC/B,gBAAc,KAAK;EACnB,MAAM,SAAS,MAAM,eAAe;AACpC,gBAAc,MAAM;AAEpB,MAAI,OAAO,SAAS;AAClB,oBAAiB,KAAK;AACtB,kBAAe,KAAK;AAEpB,oBAAiB,iBAAiB,MAAM,EAAE,IAAO;SAC5C;AAEL,kBAAe;IACb,GAAG;IACH,OAAO,OAAO;IACf,CAAC;AACF,oBAAiB;AACf,QAAI,YACF,gBAAe;KAAE,GAAG;KAAa,OAAO;KAAW,CAAC;MAErD,IAAK;;;AAKZ,iBAAgB;AACd,MAAI,CAAC,MAAM,UAAU,MAAM,SAAS,OAClC;AAKF,MAAI,MAAM,SAAS,eAAe,oBAAoB,UACpD;EAIF,MAAM,YAAY,MAAM,SAAS,UAAU,cAAc,GAAG;EAG5D,MAAMC,MAAmB;GACvB,OAAO,MAAM;GACb,YAAY,MAAM;GAClB,cAAc,MAAM;GACpB,WAAW,MAAM;GACjB,UAAU,MAAM,SAAS,UAAU,WAAW;GAC9C,eACE,MAAM,SAAS,WAAW,aAAa,gBAAgB,gBAAgB;GACzE,mBAAmB,WAAW,OAAO;GACrC,iBAAiB,MAAM,SAAS,cAAc,kBAAkB;GAChE,gBAAgB,MAAM,SAAS,cAAc,sBAAsB;GACpE;EAGD,MAAM,SAAS,cAAc,MAAM,MAAM,IAAI;AAC7C,MAAI,CAAC,OACH;EAIF,IAAI,YAAY;EAEhB,MAAM,cAAc,YAAY;AAE9B,OAAI,iBAAiB,QACnB;AAGF,cAAW,GAAG;AACd,oBAAiB,KAAK;AACtB,oBAAiB,UAAU;AAC3B,OAAI;IACF,MAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,EAAE,WAAW,IAAI,CAAC;AAC9D,QAAI,OACF,YAAW,MAAM,SAAS,QAAQ;AAChC,SAAI,UACF;AAEF,iBAAY,MAAM,IAAI,MAAM;;WAG1B;AAGR,oBAAiB,UAAU;AAC3B,OAAI,CAAC,UACH,kBAAiB,MAAM;;AAG3B,eAAa;AAGb,eAAa;AACX,eAAY;;IAEb;EACD,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN;EACA;EACA;EACA;EACD,CAAC;AAGF,iBAAgB;AACd,MAAI,cAAc;GAChB,MAAM,QAAQ,iBAAiB,gBAAgB,MAAM,EAAE,IAAK;AAC5D,gBAAa,aAAa,MAAM;;IAEjC,CAAC,aAAa,CAAC;CAGlB,MAAM,qBAAqB,OAAO,cAAgC;AAChE,MAAI,cAAc,MAAM,WACtB;AAGF,YAAU,OAAO;GACf,GAAG;GACH,YAAY;GACZ,SAAS;GACT,gBAAgB,gBAAgB,UAAU,aAAa,CAAC;GACzD,EAAE;AAGH,MAAI,UAAU,SAAS;AACrB,SAAM,UAAU,QAAQ,SAAS;AACjC,aAAU,UAAU;;EAGtB,MAAM,IAAI,IAAI,QAAQ;AACtB,MAAI;AACF,SAAM,EAAE,UAAU,MAAM,OAAO;IAC7B,QAAQ;IACR,aAAa,MAAM;AACjB,SAAI,EAAE,QAAQ,SAAS,qCAAqC,CAC1D;AAEF,eAAU,OAAO;MACf,GAAG;MACH,gBAAgB,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE;MAC9D,EAAE;;IAEN,CAAC;GAEF,MAAM,mBADY,EAAE,cAAc,EACE,oBAAoB;AAExD,kBAAe,EACb,UAAU,OAAO,WAAmB;AAElC,YADe,MAAM,EAAE,SAAS,QAAQ,EAAE,WAAW,KAAK,CAAC,EAC7C;MAEjB,CAAC;AAEF,aAAU,UAAU;AACpB,aAAU,OAAO;IACf,GAAG;IACH,QAAQ;IACR,SAAS;IACT,cAAc;IACf,EAAE;WACI,OAAO;AACd,aAAU,OAAO;IACf,GAAG;IACH,SAAS;IACT,gBAAgB,UAAU;IAC3B,EAAE;;;AAKP,WAAU,OAAO,QAAQ;AAEvB,MAAI,IAAI,QAAQ,UAAU,KAAK;AAC7B,OAAI,aAEF,OAAM;OAEN,iBAAgB,KAAK;AAEvB;;AAGF,MAAI,aACF,iBAAgB,MAAM;AAIxB,MAAI,MAAM,SAAS,UAAU,CAAC,iBAAiB;GAC7C,IAAI,aAAa;AACjB,OAAI,IAAI,QACN,cAAa;YACJ,IAAI,UACb,cAAa;YACJ,IAAI,UACb,cAAa;YACJ,IAAI,WACb,cAAa;YACJ,UAAU,OAAO,UAAU,IACpC,cAAa;YACJ,UAAU,OAAO,UAAU,IACpC,cAAa;AAGf,OAAI,YAAY;IACd,MAAM,cAAc,CAAC,GAAG,gBAAgB,WAAW,CAAC,MAAM,IAAI;AAC9D,sBAAkB,YAAY;AAE9B,QAAI,YAAY,WAAW,MAAM,YAAY,OAAO,GAAG,MAAM,MAAM,YAAY,GAAG,EAAE;AAClF,wBAAmB,KAAK;AACxB,uBAAkB,EAAE,CAAC;AAErB,SAAI,MAAM,QAAQ;AAChB,uBAAiB,gCAAgC;AACjD,YAAM,OACH,SACC,gLACA;OAAE,WAAW;OAAI,aAAa;OAAK,CACpC,CACA,MAAM,WAAW;AAEhB,wBAAiB,OADL,OAAO,KAAK,QAAQ,eAAe,GAAG,CAAC,MAAM,CAC7B,MAAM;AAClC,wBAAiB;AACf,2BAAmB,MAAM;AACzB,yBAAiB,GAAG;UACnB,IAAK;QACR,CACD,YAAY;AACX,wBAAiB,mDAAmD;AACpE,wBAAiB;AACf,2BAAmB,MAAM;AACzB,yBAAiB,GAAG;UACnB,IAAK;QACR;;;;;AAOZ,OAAK,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,QAAQ;AAE7D,SAAM;AACN;;AAIF,MAAI,UAAU,OAAO,IAAI,MAAM;AAE7B,SAAM;AACN;;AAIF,OAAK,UAAU,OAAO,UAAU,QAAQ,eAAe,CAAC,YAAY;AAClE,iBAAc;AACd;;EAIF,MAAM,YAAY;AAElB,MAAI,MAAM,SAAS,QAAQ;AACzB,OAAI,IAAI,UACN,mBAAkB,MAAM;AACtB,QAAI,MAAM,EACR,QAAO,YAAY;AAErB,QAAI,MAAM,UACR,QAAO,WAAW,SAAS;AAE7B,WAAO,IAAI;KACX;AAEJ,OAAI,IAAI,WACN,mBAAkB,MAAM;AACtB,QAAI,MAAM,YAAY,EACpB,QAAO;AAET,QAAI,MAAM,WAAW,SAAS,EAC5B,QAAO;AAET,WAAO,IAAI;KACX;AAEJ,OAAI,IAAI,QACN,mBAAkB,MAAM;AACtB,QAAI,IAAI,WAAW;KACjB,MAAMC,QAAM;AACZ,YAAO,KAAK,IAAI,YAAYA,OAAK,WAAW,SAAS,EAAE;;IAEzD,MAAM,MAAM,IAAI;AAChB,WAAO,KAAK,IAAI,KAAK,YAAY,EAAE;KACnC;AAEJ,OAAI,IAAI,UACN,mBAAkB,MAAM;AACtB,QAAI,IAAI,UACN,QAAO,KAAK,IAAI,YAAY,GAAG,WAAW,SAAS,EAAE;IAEvD,MAAM,MAAM,IAAI;AAChB,WAAO,KAAK,IAAI,KAAK,YAAY,EAAE;KACnC;AAEJ,OAAI,IAAI,OACN,kBAAiB,WAAW,eAAe,KAAK;AAGlD,OAAI,UAAU,KAAK,MAAM,EAAE;IACzB,MAAM,OAAO,WAAW,MAAM,MAAM,EAAE,QAAQ,MAAM;AACpD,QAAI,MAAM;AAER,sBADY,WAAW,QAAQ,KAAK,CACf;AACrB,sBAAiB,KAAK,KAAK;;;AAI/B,OAAI,UAAU,OAAO,UAAU,IAC7B,aAAY,8CAA8C;AAG5D,OAAI,UAAU,OAAO,UAAU,IAC7B,aAAY,uCAAuC;aAIjD,IAAI,OACN,kBAAiB,OAAO;AAK5B,MAAI,UAAU,OAAO,IAAI,MAEvB;QADkB,MAAM,QAAQ,cAAc,GAC/B,iBACb,WAAU,OAAO;IAAE,GAAG;IAAG,cAAc,CAAC,EAAE;IAAc,EAAE;cAElD,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,QAE5D;QADkB,MAAM,QAAQ,cAAc,GAC/B,iBACb,WAAU,OAAO;IAAE,GAAG;IAAG,cAAc,CAAC,EAAE;IAAc,EAAE;;AAK9D,MAAI,UAAU,OAAO,IAAI,KACvB,WAAU,OAAO;GAAE,GAAG;GAAG,WAAW,CAAC,EAAE;GAAW,EAAE;YAC1C,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,OAC5D,WAAU,OAAO;GAAE,GAAG;GAAG,WAAW,CAAC,EAAE;GAAW,EAAE;AAItD,MAAI,UAAU,OAAO,IAAI,KACvB,WAAU,OAAO;GAAE,GAAG;GAAG,WAAW,CAAC,EAAE;GAAW,EAAE;YAC1C,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,OAC5D,WAAU,OAAO;GAAE,GAAG;GAAG,WAAW,CAAC,EAAE;GAAW,EAAE;AAItD,OAAK,UAAU,OAAO,UAAU,QAAQ,MAAM,SAAS,UAAU,CAAC,MAAM,QAEtE,oBADkB,MAAM,eAAe,WAAW,QAAQ,SAC7B;GAE/B;AAEF,KAAI,MAAM,QACR,QAAO,oBAAC,eAAY,SAAS,MAAM,iBAAkB;AAIvD,KAAI,MAAM,SAAS,OACjB,QACE,qBAAC;EAAI,eAAc;EAAS,UAAU;EAAG,UAAU;;GACjD,qBAAC;IAAI,YAAW;IAAS,eAAc;;KACrC,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAK;gBAAS;OAAsC;KACrD,qBAAC;MAAK;iBAAS,OAAI,UAAU,0BAA0B,iBAAiB;OAAQ;;KAC5E;GAEL,iBACC,oBAAC;IAAI,YAAW;IAAS,gBAAe;IAAS,SAAS;cACxD,oBAAC;KAAK,OAAM;KAAO;eAChB,cAAc,eAAe;MACzB;KACH,GACJ;GAEJ,qBAAC;IAAI,gBAAe;;KAClB,oBAAC;MAAK;gBAAS;OAAc;KAC7B,oBAAC;MAAK,OAAM;gBAAS,MAAM;OAAa;KACxC,oBAAC;MAAK;gBAAS;OAAU;KACzB,oBAAC;MAAK;MAAK,OAAO,MAAM,QAAQ,eAAe,KAAK,WAAW,UAAU;gBACtE,MAAM,QAAQ,eAAe,EAAE,aAAa,IAAI;OAC5C;KACN,MAAM,QAAQ,cAAc,EAAE,mBAC7B,4CACE,oBAAC;MAAK;gBAAS;OAAoB,EACnC,oBAAC;MAAK,MAAM,MAAM;MAAc,OAAO,MAAM,eAAe,YAAY;gBACrE,MAAM,eAAe,OAAO;OACxB,IACN,GACD;KACJ,oBAAC;MAAK;gBAAS;OAAiB;KAChC,oBAAC;MAAK,MAAM,MAAM;MAAW,OAAO,MAAM,YAAY,UAAU;gBAC7D,MAAM,YAAY,OAAO;OACrB;KACP,oBAAC;MAAK;gBAAS;OAAiB;KAChC,oBAAC;MAAK,MAAM,MAAM;MAAW,OAAO,MAAM,YAAY,SAAS;gBAC5D,MAAM,YAAY,OAAO;OACrB;KACN,MAAM,QAAQ,aAAa,IAC1B,4CACE,oBAAC;MAAK;gBAAS;OAAe,EAC9B,oBAAC;MAAK,OAAM;gBAAQ,MAAM,OAAO,iBAAiB,EAAE,MAAM;OAAgB,IACzE;KAEJ,MAAM,QAAQ,aAAa,IAC1B,4CACE,oBAAC;MAAK;gBAAS;OAAe,EAC9B,oBAAC;MAAK,OAAM;gBAAW,MAAM,OAAO,iBAAiB,EAAE,MAAM;OAAiB,IAC7E;;KAED;GAEL,mBAAmB,gBAClB,oBAAC;IAAI,gBAAe;IAAS,SAAS;cACpC,oBAAC;KAAK,iBAAgB;KAAQ;KAAK,OAAM;eACtC;MACI;KACH,GACJ;GAEJ,qBAAC;IAAI,YAAW;IAAS,eAAc;IAAS,SAAS;eACvD,oBAAC;KAAI,gBAAe;eACjB,WAAW,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,QACjC,oBAAC;MACC,UAAU;MACJ;MAEN,OAAO,kBAAkB,QAAQ;MACjC,UAAU,QAAQ;QAFb,KAAK,IAGV,CACF;MACE,EACN,oBAAC;KAAI,gBAAe;eACjB,WAAW,MAAM,EAAE,CAAC,KAAK,MAAM,QAC9B,oBAAC;MACC,UAAU;MACJ;MAEN,OAAO,kBAAkB,MAAM,MAAM;MACrC,UAAU,MAAM,MAAM;QAFjB,KAAK,IAGV,CACF;MACE;KACF;GAEN,qBAAC;IAAI,gBAAe;;KAClB,oBAAC;MAAK;gBAAS;OAAwB;KACvC,oBAAC;MAAK,OAAM;gBAAS;OAAU;KAC9B,MAAM,QAAQ,cAAc,EAAE,oBAC7B;MACE,oBAAC;OAAK;iBAAS;QAAU;MACzB,oBAAC;OAAK,OAAM;iBAAU;QAAQ;MAC9B,oBAAC;OAAK;iBAAS;QAAW;SACzB;KAEL,oBAAC;MAAK;gBAAS;OAAU;KACzB,oBAAC;MAAK,OAAM;gBAAQ;OAAQ;KAC5B,oBAAC;MAAK;gBAAS;OAAc;KAC7B,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK;gBAAS;OAAc;KAC7B,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK;gBAAS;OAAa;KAC5B,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK;gBAAS;OAAa;KAC5B,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK;gBAAS;OAAe;KAC9B,oBAAC;MAAK,OAAM;gBAAM;OAAQ;KAC1B,oBAAC;MAAK;gBAAS;OAAU;;KACrB;GAEL,eACC,oBAAC;IAAI,gBAAe;IAAS,WAAW;cACtC,oBAAC;KAAK,OAAM;eAAS;MAAiC;KAClD,GACJ;;GACA;CAKV,MAAM,oBAAoB,OAAO,OAAe,mBAAsC;EACpF,MAAM,aAAa,oBAAoB,cAAc;EACrD,MAAM,YAAY,kBAAkB,MAAM;AAC1C,YAAU,OAAO;GACf,GAAG;GACH;GACA,YAAY;GACZ,MAAM;GACN,SAAS;GACT,gBAAgB;GACjB,EAAE;AACH,uBAAqB,MAAM;AAG3B,MAAI,UAAU,SAAS;AACrB,SAAM,UAAU,QAAQ,SAAS;AACjC,aAAU,UAAU;;EAGtB,MAAM,IAAI,IAAI,QAAQ;AACtB,MAAI;AACF,SAAM,EAAE,UAAU,OAAO;IACvB,QAAQ;IACR,aAAa,MAAM;AACjB,SAAI,EAAE,QAAQ,SAAS,qCAAqC,CAC1D;AAEF,eAAU,OAAO;MACf,GAAG;MACH,gBAAgB,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE;MAC9D,EAAE;;IAEN,CAAC;GAEF,MAAM,mBADY,EAAE,cAAc,EACE,oBAAoB;AAGxD,kBAAe,EACb,UAAU,OAAO,WAAmB;AAElC,YADe,MAAM,EAAE,SAAS,QAAQ,EAAE,WAAW,KAAK,CAAC,EAC7C;MAEjB,CAAC;AAEF,aAAU,UAAU;AACpB,aAAU,OAAO;IACf,GAAG;IACH,QAAQ;IACR,SAAS;IACT,cAAc;IACf,EAAE;AACH,mBAAgB,EAAE;WACX,OAAO;AACd,aAAU,OAAO;IACf,GAAG;IACH,SAAS;IACT,gBAAgB,UAAU;IAC3B,EAAE;;;CAIP,MAAM,qBAAqB,OAAO,SAAiB,UAAoC;AACrF,YAAU,OAAO;GAAE,GAAG;GAAG,gBAAgB,eAAe,QAAQ;GAAM,EAAE;EACxE,MAAM,aAAa,IAAI,QAAQ;AAC/B,MAAI;AACF,SAAM,WAAW,UAAU,SAAS,EAClC,aAAa,MAAM;AACjB,QAAI,EAAE,QAAQ,SAAS,qCAAqC,CAC1D;AAEF,cAAU,OAAO;KACf,GAAG;KACH,gBAAgB,EAAE,OAAO,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU;KACxE,EAAE;MAEN,CAAC;AACF,SAAM,WAAW,SAAS;AAC1B,aAAU,OAAO;IACf,GAAG;IACH,gBAAgB,qBAAqB;IACtC,EAAE;AACH,oBAAiB,UAAU,OAAO;IAAE,GAAG;IAAG,gBAAgB;IAAI,EAAE,EAAE,KAAK;AACvE,UAAO;WACA,OAAO;AACd,aAAU,OAAO;IAAE,GAAG;IAAG,gBAAgB,WAAW;IAAS,EAAE;AAC/D,oBAAiB,UAAU,OAAO;IAAE,GAAG;IAAG,gBAAgB;IAAI,EAAE,EAAE,IAAK;AACvE,UAAO;;;AAKX,QACE,qBAAC;EAAI,eAAc;EAAS,QAAO;;GACjC,qBAAC;IACC,cAAc;IACd,aAAY;IACZ,YAAY;IACZ,aAAa;IACb,aAAY;IACZ,UAAU;;KAEV,oBAAC;MAAK;MAAK,OAAM;gBAAO;OAEjB;KACP,oBAAC;MAAK,OAAM;gBAAO;OAAQ;KAC3B,oBAAC;MAAK;gBAAU;OAAuB;KACvC,oBAAC;MAAK,OAAM;gBAAO;OAAU;KAC7B,oBAAC;MAAK,OAAM;gBACT,WAAW,MAAM,MAAM,EAAE,SAAS,MAAM,KAAK,EAAE,SAAS,MAAM;OAC1D;KACN,MAAM,gBACL,4CACE,oBAAC;MAAK,OAAM;gBAAO;OAAU,EAC7B,oBAAC;MAAK,OAAM;gBAAU;OAAe,IACpC;KAEJ,MAAM,aACL,4CACE,oBAAC;MAAK,OAAM;gBAAO;OAAU,EAC7B,oBAAC;MAAK,OAAM;gBAAQ;OAAY,IAC/B;KAEJ,MAAM,kBACL,4CACE,oBAAC;MAAK,OAAM;gBAAO;OAAU,EAC7B,oBAAC;MAAK,OAAM;gBAAU,MAAM;OAAsB,IACjD;KAEJ,cACC,4CACE,oBAAC;MAAK,OAAM;gBAAO;OAAU,EAC7B,oBAAC;MAAK,OAAM;gBAAS;OAAoB,IACxC;KAEJ,iBACC,4CACE,oBAAC;MAAK,OAAM;gBAAO;OAAU,EAC7B,qBAAC;MAAK,OAAM;iBAAQ,+BAA4B,aAAa;OAAqB,IACjF;KAEJ,eAAe,CAAC,cAAc,CAAC,iBAC9B;MACE,oBAAC;OAAK,OAAM;iBAAO;QAAU;MAC7B,qBAAC;OAAK,OAAM;;QAAO;QAAS,YAAY;QAAc;;QAAgB;MACtE,oBAAC;OAAK;OAAK,OAAM;iBAAO;QAEjB;MACP,oBAAC;OAAK,OAAM;iBAAO;QAAiB;SACnC;KAEL,oBAAC;MAAK,OAAM;gBAAO;OAAU;KAC7B,oBAAC;MAAK;gBAAS;OAAyB;KACvC,gBAAgB,oBAAC;MAAK,OAAM;gBAAS;OAAuB;;KACzD;GAEL,WACC,oBAAC;IAAI,UAAU;IAAG,UAAU;cAC1B,oBAAC;KAAK;KAAS;eACZ,gBAAgB,GAAG,cAAc,QAAQ,CAAC,KAAK,cAAc,QAAQ;MACjE;KACH;GAGR,oBAAC;IAAI,eAAc;IAAS,UAAU;cACpC,qBAAC;KAAe,YAAY;;MACzB,MAAM,SAAS,UAAU,MAAM,UAC9B,oBAAC;OACC,WAAW,MAAM;OACjB,cAAc,aAAa;OAC3B,QAAQ,MAAM;OACd,eAAe,aAAa;OAC5B,oBAAoB,iBAAiB,cAAc;OACnD,uBAAuB,UAAU;AAC/B,yBAAiB,MAAM;SACrB,MAAM,aAAa,EAAE,UAAU;SAC/B,MAAM,iBAAiB,EAAE,YAAY,MAAM;SAC3C,MAAM,eAAe,EAAE,cAAc,MAAM;AAC3C,gBAAO;UACL,GAAG;UACH,SAAS;UACT,WAAW;UACX,aAAa;UACb,eAAe,MAAM;UACrB,cAAc,eAAe,IAAK,iBAAiB,eAAgB,MAAO;UAC3E;UACD;;OAEJ,qBAAqB,UAAU,OAAO;QAAE,GAAG;QAAG,WAAW,CAAC,EAAE;QAAW,EAAE;OACzE,wBAAwB,UAAU,OAAO;QAAE,GAAG;QAAG,cAAc,CAAC,EAAE;QAAc,EAAE;OAClF,qBAAqB,UAAU,OAAO;QAAE,GAAG;QAAG,WAAW,CAAC,EAAE;QAAW,EAAE;OACzE,cAAc,MAAM;OACpB,WAAW,MAAM;QACjB;MAEH,MAAM,SAAS,YAAY,MAAM,UAChC,oBAAC;OACC,QAAQ,MAAM;OACd,mBAAmB,iBAAiB,eAAe;OACnD,eAAe,OAAO,YAAY;AAEhC,kBAAU,OAAO;SACf,GAAG;SACH,SAAS;SACT,gBAAgB,WAAW,QAAQ;SACpC,EAAE;AACH,YAAI;AACF,eAAM,MAAM,QAAQ,UAAU,QAAQ;AACtC,mBAAU,OAAO;UAAE,GAAG;UAAG,OAAO;UAAS,SAAS;UAAO,EAAE;iBACpD,MAAM;AACb,mBAAU,OAAO;UAAE,GAAG;UAAG,SAAS;UAAO,EAAE;;;QAG/C;MAEH,MAAM,SAAS,kBAAkB,MAAM,UACtC,oBAAC;OAAgB,QAAQ,MAAM;OAAQ,cAAc,iBAAiB,OAAO;QAAI;MAElF,MAAM,SAAS,WACd,oBAAC,aAAU,oBAAoB,iBAAiB,cAAc,GAAI;MAEnE,MAAM,SAAS,iBAAiB,MAAM,UACrC,oBAAC;OAAe,QAAQ,MAAM;OAAQ,cAAc,iBAAiB,QAAQ;QAAI;MAElF,MAAM,SAAS,UAAU,MAAM,UAAU,oBAAC,YAAS,QAAQ,MAAM,SAAU;MAC3E,MAAM,SAAS,WACd,oBAAC;OACC,cAAc,MAAM;OACpB,iBAAiB,MAAM;OACvB,iBAAiB,MAAM;OACvB,gBAAgB;OAChB,gBAAgB;OAChB,UAAU;OACV,aAAa,OAAO,YAAY;AAC9B,YAAI,MAAM,QAAQ;AAChB,eAAM,MAAM,OAAO,QAAQ,EAAE,OAAO,SAAS,CAAC;AAC9C,mBAAU,OAAO;UAAE,GAAG;UAAG,UAAU;UAAS,EAAE;;;OAGlD,aAAa,OAAO,YAAY;AAC9B,YAAI,MAAM,QAAQ;AAChB,eAAM,MAAM,OAAO,QAAQ,QAAQ;AACnC,mBAAU,OAAO;UAAE,GAAG;UAAG,UAAU;UAAS,EAAE;;;OAGlD,aAAa;QACb;MAEH,MAAM,SAAS,gBAAgB,oBAAC,mBAAiB;MACjD,MAAM,SAAS,eAAe,MAAM,UACnC,oBAAC;OACkB;OACC;OAClB,UAAU;OACV,QAAQ,MAAM;OACd,OAAO,MAAM;OACb,WAAW,cAAc;AACvB,yBAAiB,OAAO;SACtB,GAAG;SACH,cAAc,CAAC,GAAG,EAAE,cAAc;UAAE,OAAO,MAAM;UAAO;UAAW,CAAC;SACrE,EAAE;;OAEL,iBAAiB,QAAQ,UAAU;AACjC,2BAAmB,OAAO;AAC1B,+BAAuB,MAAM;;OAE/B,gBAAgB;OAChB,qBAAqB;AACnB,6BAAqB,KAAK;AAC1B,yBAAiB,QAAQ;;OAEP;OACC;QACrB;MAEH,MAAM,SAAS,UAAU,MAAM,UAC9B,oBAAC;OACC,QAAQ,MAAM;OACd,OAAO,MAAM;OACb,aAAa,MAAM,OAAO,cAAc,EAAE,UAAU;OACpD,mBAAmB;AAEjB,yBAAiB,QAAQ;;OAG3B,OAAO;QACP;MAEH,MAAM,SAAS,WAAW,MAAM,UAC/B,oBAAC;OAAU,QAAQ,MAAM;OAAQ,OAAO,MAAM;QAAS;;MAE1C;KACb;;GACF;;AAIV,SAAS,eAAe,EACtB,YACA,YAIC;AACD,KAAI,eAAe,MACjB,QACE,oBAAC;EAAI,YAAW;EAAS,eAAc;EAAS,QAAQ;EAAG,gBAAe;YACxE,oBAAC;GAAK;aAAS;IAAU;GACrB;AAGV,QAAO,gCAAG,WAAY;;AAGxB,SAAS,SAAS,EAChB,MACA,UACA,SAMC;CACD,MAAM,cAAc,WAAY,QAAQ,UAAU,WAAY;CAC9D,MAAM,UAAU,WAAY,QAAQ,MAAM,MAAO;AAEjD,QACE,qBAAC;EACC,YAAW;EACX,aAAa,WAAY,KAAK,QAAgB;EACjC;EACb,eAAc;EACd,gBAAe;EACf,UAAU;EACV,UAAU;EACV,OAAO;aAEP,qBAAC;GAAI,gBAAe;cAClB,qBAAC;IAAK,MAAM;IAAU,OAAO,WAAY,KAAK,QAAgB;;KAC3D;KAAQ;KAAE,KAAK;KAAI;;KACf,EACP,qBAAC;IACC,MAAM;IACN,OAAO,WAAY,KAAK,QAAgB;IACxC,SAAS,SAAS;eAEjB,KACA,KAAK;KACD;IACH,EACN,oBAAC;GAAK,OAAO,WAAY,KAAK,QAAgB;GAAW,UAAU,CAAC;aACjE,KAAK;IACD;GACH;;;;;ACtxCV,IAAIC,iBAAuC;AAC3C,SAAgB,kBAAkB,SAAwB;AACxD,kBAAiB;;AAGnB,eAAsB,UAAU,SAAuB;CAErD,IAAI,cAAc;CAClB,IAAIC,cAAqC;CAEzC,MAAM,qBAAqB;AACzB,iBAAe;AAGf,MAAI,YACF,cAAa,YAAY;AAE3B,gBAAc,iBAAiB;AAC7B,iBAAc;KACb,IAAK;AAER,MAAI,eAAe,EAEjB,SAAQ,KAAK,EAAE;;AAKnB,SAAQ,GAAG,UAAU,aAAa;AAClC,SAAQ,GAAG,iBAAiB,QAAQ,KAAK,EAAE,CAAC;AAE5C,KAAI;EACF,MAAM,EAAE,kBAAkB,OACxB,oBAAC;GAAI,cAAc,SAAS;GAAc,aAAa,SAAS;IAAe,CAChF;AACD,QAAM,eAAe;AAGrB,MAAI,eACF,KAAI;AACF,SAAM,QAAQ,KAAK,CACjB,gBACA,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC,CACvC,CAAC;UACI;AAMV,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;AAG5C,UAAQ,KAAK,EAAE;WACP;AACR,UAAQ,IAAI,UAAU,aAAa;AACnC,MAAI,YACF,cAAa,YAAY;;;;;;;;;;;;;;;;;;;;AC3C/B,MAAM,UAAU,IAAI,SAAS;AAE7B,QAAQ,KAAK,SAAS,CAAC,YAAY,qCAAqC,CAAC,QAAQ,QAAQ;AAMzF,QACG,SAAS,eAAe,gDAAgD,CACxE,OAAO,oBAAoB,gBAAgB,aAAa,CACxD,OAAO,wBAAwB,cAAc,MAAM,CACnD,OAAO,yBAAyB,eAAe,MAAM,CACrD,OAAO,uBAAuB,gBAAgB,CAC9C,OAAO,cAAc,uBAAuB,CAC5C,OAAO,YAAY,gBAAgB,CACnC,OAAO,UAAU,iBAAiB,CAClC,OAAO,OAAO,aAAa,SAAS;AAEnC,KAAI,YAAY,WAAW,GAAG;AAC5B,QAAM,UAAU,EAAE,CAAC;AACnB;;AAIF,OAAM,YADS,YAAY,KAAK,IAAI,EACV,KAAK;EAC/B;AAMJ,QACG,QAAQ,uBAAuB,CAC/B,MAAM,IAAI,CACV,YAAY,gBAAgB,CAC5B,OAAO,oBAAoB,gBAAgB,aAAa,CACxD,OAAO,wBAAwB,cAAc,MAAM,CACnD,OAAO,yBAAyB,eAAe,MAAM,CACrD,OAAO,uBAAuB,gBAAgB,CAC9C,OAAO,cAAc,uBAAuB,CAC5C,OAAO,YAAY,gBAAgB,CACnC,OAAO,UAAU,iBAAiB,CAClC,OAAO,OAAO,aAAa,SAAS;AAEnC,OAAM,YADS,YAAY,KAAK,IAAI,EACV,KAAK;EAC/B;AAEJ,eAAe,YAAY,QAAgB,MAAW;CACpD,MAAM,IAAI,IAAI,QAAQ;CACtB,MAAM,UAAU,IAAI,WAAW,MAAM,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO;AAEnE,KAAI;AACF,QAAM,EAAE,UAAU,KAAK,OAAO,EAC5B,aAAa,MAAM;AACjB,WAAQ,OAAO,EAAE,WAAW,GAAG,EAAE,OAAO,IAAI,EAAE,SAAS,MAAM,EAAE;KAElE,CAAC;AACF,UAAQ,QAAQ,eAAe;AAE/B,MAAI,KAAK,UAAU;AAGnB,MAAI,KAAK,QAAQ;AACf,WAAQ,OAAO,MAAM,MAAM,MAAM,aAAa,CAAC;AAC/C,cAAW,MAAM,SAAS,EAAE,OAAO,QAAQ;IACzC,WAAW,OAAO,SAAS,KAAK,WAAW,GAAG;IAC9C,aAAa,OAAO,WAAW,KAAK,YAAY;IAChD,QAAQ,KAAK;IACb,UAAU,KAAK;IAChB,CAAC,CACA,SAAQ,OAAO,MAAM,MAAM;SAExB;GACL,MAAM,aAAa,IAAI,gBAAgB,CAAC,OAAO;GAC/C,MAAM,SAAS,MAAM,EAAE,SAAS,QAAQ;IACtC,WAAW,OAAO,SAAS,KAAK,WAAW,GAAG;IAC9C,aAAa,OAAO,WAAW,KAAK,YAAY;IAChD,QAAQ,KAAK;IACb,UAAU,KAAK;IAChB,CAAC;AACF,cAAW,MAAM;AAEjB,OAAI,KAAK,MAAM,YACJ,OAAO,UAAU;;AAI9B,QAAM,EAAE,SAAS;UACV,IAAI;AACX,UAAQ,KAAK,QAAQ;AACrB,UAAQ,KAAK,EAAE;;;AAQnB,QACG,QAAQ,OAAO,CACf,MAAM,IAAI,CACV,YAAY,8CAA8C,CAC1D,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU,EAAE,cADG,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW,QAC9B,CAAC;EACjC;AAMJ,QACG,QAAQ,OAAO,CACf,MAAM,IAAI,CACV,YAAY,0CAA0C,CACtD,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU;EAAE,aAAa;EAAQ,cADlB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;EACT,CAAC;EACtD;AAMJ,QACG,QAAQ,SAAS,CACjB,YAAY,wBAAwB,CACpC,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU;EAAE,aAAa;EAAU,cADpB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;EACP,CAAC;EACxD;AAEJ,QACG,QAAQ,QAAQ,CAChB,YAAY,uBAAuB,CACnC,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU;EAAE,aAAa;EAAS,cADnB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;EACR,CAAC;EACvD;AAEJ,QACG,QAAQ,QAAQ,CAChB,YAAY,uBAAuB,CACnC,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU;EAAE,aAAa;EAAS,cADnB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;EACR,CAAC;EACvD;AAEJ,QACG,QAAQ,YAAY,CACpB,YAAY,uCAAuC,CACnD,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU;EAAE,aAAa;EAAc,cADxB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;EACH,CAAC;EAC5D;AAEJ,QACG,QAAQ,YAAY,CACpB,YAAY,2BAA2B,CACvC,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,OAAM,UAAU;EAAE,aAAa;EAAa,cADvB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;EACJ,CAAC;EAC3D;AAMJ,QACG,QAAQ,SAAS,CACjB,YAAY,8CAA8C,CAC1D,OAAO,iBAAiB,gDAAgD,eAAe,CACvF,OAAO,WAAW,uCAAuC,CACzD,OAAO,OAAO,SAAS;CACtB,MAAM,UAAU,IAAI,+BAA+B,CAAC,OAAO;AAE3D,KAAI;EACF,MAAM,UAAU,MAAMC,OAAc,EAAE,MAAM,KAAK,MAAM,CAAC;AACxD,UAAQ,MAAM;AAEd,MAAI,KAAK,MAEP,EADW,MAAM,OAAO,YACrB,cAAc,uBAAuB,QAAQ;UAE3C,IAAI;AACX,UAAQ,KAAK,QAAQ;AACrB,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,mBAAmB,CAC3B,YAAY,mBAAmB,CAC/B,OAAO,yBAAyB,+BAA+B,SAAS,CACxE,OAAO,yBAAyB,8BAA8B,YAAY,CAC1E,OAAO,OAAO,MAAM,SAAS;CAE5B,MAAM,WADK,MAAM,OAAO,YACL,aAAa,MAAM,QAAQ;CAE9C,MAAM,UAAU,IAAI,iBAAiB,CAAC,OAAO;AAE7C,KAAI;AACe,QAAMC,UAAiB;GACtC;GACA,QAAQ,KAAK;GACb,QAAQ,KAAK;GACd,CAAC;AACF,UAAQ,MAAM;UACP,IAAI;AACX,UAAQ,KAAK,QAAQ;AACrB,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,iBAAiB,CACzB,YAAY,eAAe,CAC3B,OAAO,uBAAuB,yCAAyC,eAAe,CACtF,OAAO,OAAO,MAAM,SAAS;CAE5B,MAAM,WADK,MAAM,OAAO,YACL,aAAa,MAAM,QAAQ;CAE9C,MAAM,UAAU,IAAI,gBAAgB,CAAC,OAAO;AAE5C,KAAI;AACmB,QAAMC,QAAe;GAAE;GAAS,OAAO,KAAK;GAAO,CAAC;AACzE,UAAQ,MAAM;UACP,IAAI;AACX,UAAQ,KAAK,QAAQ;AACrB,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,gBAAgB,CACxB,YAAY,cAAc,CAC1B,OAAO,uBAAuB,iCAAiC,MAAM,CACrE,OAAO,OAAO,MAAM,SAAS;CAE5B,MAAM,WADK,MAAM,OAAO,YACL,aAAa,MAAM,QAAQ;CAE9C,MAAM,UAAU,IAAI,eAAe,CAAC,OAAO;AAE3C,KAAI;EACF,MAAM,QAAQ,KAAK,UAAU,QAAQ,CAAC,MAAM,GAAG,KAAK,MAAM,MAAM,IAAI;AAC9C,QAAMC,OAAc;GACxC,MAAM;GACC;GACR,CAAC;AACF,UAAQ,MAAM;UACP,IAAI;AACX,UAAQ,KAAK,QAAQ;AACrB,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,kBAAkB,CAC1B,YAAY,yCAAyC,CACrD,OAAO,uBAAuB,sCAAsC,WAAW,CAC/E,OAAO,uBAAuB,0BAA0B,MAAM,CAC9D,OAAO,uBAAuB,sCAAsC,CACpE,OAAO,uBAAuB,0CAA0C,aAAa,CACrF,OAAO,iBAAiB,wBAAwB,CAChD,OAAO,iBAAiB,4BAA4B,CACpD,OAAO,OAAO,WAAW,SAAS;CACjC,MAAM,IAAI,IAAI,QAAQ;AAGtB,KAAI,KAAK,YAAY;AACnB,UAAQ,IAAI,MAAM,KAAK,4BAA4B,CAAC;EACpD,MAAM,SAAS,MAAM,EAAE,eAAe;AACtC,OAAK,MAAM,SAAS,OAClB,SAAQ,IACN,KAAK,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,YAAY,IAAI,MAAM,WAAW,WAAW,MAAM,WAAW,KAC7G;AAEH,UAAQ,KAAK;AACb;;AAIF,KAAI,KAAK,YAAY;AACnB,UAAQ,IAAI,MAAM,KAAK,wBAAwB,CAAC;EAChD,MAAM,SAAS,EAAE,YAAY;AAC7B,OAAK,MAAM,SAAS,OAClB,SAAQ,IACN,KAAK,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,KAAK,IAAI,MAAM,OAAO,IAAI,MAAM,SAAS,GACzF;AAEH,UAAQ,KAAK;AACb;;CAIF,IAAI,OAAO,UAAU,KAAK,IAAI,CAAC,MAAM;AAGrC,KAAI,CAAC,MAAM;EACT,MAAMC,OAAK,MAAM,OAAO;AACxB,MAAI;AAEF,OAAI,CAAC,QAAQ,MAAM,MACjB,QAAOA,KAAG,aAAa,GAAG,QAAQ,CAAC,MAAM;UAErC;;AAKV,KAAI,CAAC,MAAM;AACT,UAAQ,IAAI,MAAM,OAAO,gEAAgE,CAAC;AAC1F,UAAQ,IAAI,MAAM,KAAK,oCAAoC,CAAC;AAC5D,UAAQ,IAAI,MAAM,KAAK,oCAAoC,CAAC;AAC5D,UAAQ,IAAI,MAAM,KAAK,mDAAmD,CAAC;AAC3E,UAAQ,IAAI,MAAM,KAAK,0DAA0D,CAAC;AAClF;;CAKF,MAAM,UADW,KAAK,WAAW,MACN,OAAO,IAAI,uBAAuB,CAAC,OAAO;AAErE,KAAI;AACF,QAAM,EAAE,QAAQ;GACd,OAAO,KAAK;GACZ,aAAa,MAAM;AACjB,QAAI,QAAS,SAAQ,OAAO,EAAE;;GAEjC,CAAC;AACF,MAAI,QAAS,SAAQ,OAAO,sBAAsB,KAAK,MAAM;EAE7D,MAAM,SAAS,MAAM,EAAE,MAAM,MAAM;GACjC,OAAO,KAAK;GACZ,OAAO,OAAO,WAAW,KAAK,MAAM;GACrC,CAAC;AAEF,MAAI,QAAS,SAAQ,QAAQ,aAAa,OAAO,SAAS,QAAQ,EAAE,CAAC,SAAS;AAE9E,MAAI,KAAK,WAAW,KAAK;GAEvB,MAAM,YAAY,WAAW,OAAO,OAAO,OAAO,WAAW;AAC7D,WAAQ,OAAO,MAAM,UAAU;aACtB,KAAK,QAAQ;GAEtB,MAAM,YAAY,WAAW,OAAO,OAAO,OAAO,WAAW;AAE7D,IADW,MAAM,OAAO,YACrB,cAAc,KAAK,QAAQ,UAAU;AACxC,WAAQ,IAAI,MAAM,MAAM,eAAe,KAAK,SAAS,CAAC;AACtD,WAAQ,IAAI,MAAM,KAAK,wBAAwB,KAAK,SAAS,CAAC;SACzD;GAEL,MAAMC,OAAK,MAAM,OAAO;GACxB,MAAMC,SAAO,MAAM,OAAO;GAC1B,MAAMF,OAAK,MAAM,OAAO;GAExB,MAAM,WAAWE,OAAK,KAAKD,KAAG,QAAQ,EAAE,cAAc,KAAK,KAAK,CAAC,MAAM;GACvE,MAAM,YAAY,WAAW,OAAO,OAAO,OAAO,WAAW;AAC7D,QAAG,cAAc,UAAU,UAAU;GAGrC,MAAM,EAAE,iBAAS,MAAM,OAAO;GAC9B,MAAM,WAAWA,KAAG,UAAU;AAE9B,OAAI,aAAa,SACf,QAAK,WAAW,SAAS,KAAK,QAAQ;AACpC,QAAI,IAAK,SAAQ,MAAM,MAAM,IAAI,wBAAwB,IAAI,QAAQ,CAAC;AACtE,SAAG,WAAW,SAAS;KACvB;YACO,aAAa,QACtB,QAAK,UAAU,SAAS,KAAK,QAAQ;AACnC,QAAI,IAAK,SAAQ,MAAM,MAAM,IAAI,wBAAwB,IAAI,QAAQ,CAAC;AACtE,SAAG,WAAW,SAAS;KACvB;QACG;AACL,YAAQ,IAAI,MAAM,OAAO,qBAAqB,WAAW,CAAC;AAC1D,YAAQ,IAAI,MAAM,KAAK,iEAAiE,CAAC;;;AAI7F,QAAM,EAAE,SAAS;UACVE,GAAQ;AACf,MAAI,QAAS,SAAQ,KAAK,QAAQ;AAClC,UAAQ,MAAM,MAAM,IAAI,EAAE,QAAQ,CAAC;AACnC,UAAQ,KAAK,EAAE;;EAEjB;AAUJ,QACG,QAAQ,gBAAgB,CACxB,YAAY,yEAAyE,CACrF,OAAO,uBAAuB,oBAAoB,aAAa,CAC/D,OAAO,2BAA2B,gBAAgB,kBAAkB,CACpE,OAAO,uBAAuB,gBAAgB,WAAW,CACzD,OACC,qBACA,iBACA,8EACD,CACA,OAAO,cAAc,uBAAuB,CAC5C,OAAO,OAAO,WAAW,SAAS;AACjC,KAAI,CAAC,WAAW;AACd,UAAQ,IAAI,MAAM,KAAK,6BAA6B,CAAC;AACrD,UAAQ,IAAI,MAAM,OAAO,8CAA8C,CAAC;AACxE,UAAQ,IAAI,WAAW;AACvB,UAAQ,IAAI,6DAA6D;AACzE,UAAQ,IAAI,kEAAkE;AAC9E,UAAQ,IAAI,2DAA2D;AACvE,UAAQ,IAAI,2CAA2C;AACvD,UAAQ,IAAI,kDAAkD;AAC9D,UAAQ,IAAI,aAAa;AACzB,UAAQ,IAAI,MAAM,KAAK,8CAA8C,CAAC;AACtE,UAAQ,IAAI,MAAM,KAAK,8DAA4D,CAAC;AACpF;;CAGF,MAAM,IAAI,IAAI,QAAQ;CACtB,MAAM,UAAU,IAAI,kBAAkB,CAAC,OAAO;AAE9C,KAAI;EACF,MAAMH,OAAK,MAAM,OAAO;EACxB,MAAME,SAAO,MAAM,OAAO;EAC1B,MAAMD,OAAK,MAAM,OAAO;EACxB,MAAM,EAAE,iBAAS,MAAM,OAAO;EAG9B,MAAM,WAAWC,OAAK,QAAQ,UAAU;AACxC,MAAI,CAACF,KAAG,WAAW,SAAS,EAAE;AAC5B,WAAQ,KAAK,mBAAmB,WAAW;AAC3C,WAAQ,KAAK,EAAE;;EAGjB,MAAM,YAAY,IAAI,WAAWA,KAAG,aAAa,SAAS,CAAC;AAG3D,UAAQ,OAAO;AACf,QAAM,EAAE,QAAQ,KAAK,UAAU,EAC7B,aAAa,MAAW;AACtB,WAAQ,OAAO,EAAE,UAAU;KAE9B,CAAC;AAEF,UAAQ,OAAO;EAEf,MAAM,YADmB,MAAM,EAAE,WAAW,UAAU,EACpB,KAAK,MAAM;AAE7C,UAAQ,QAAQ,cAAc,SAAS,GAAG;AAE1C,MAAI,CAAC,UAAU;AACb,WAAQ,IAAI,MAAM,OAAO,+BAA+B,CAAC;AACzD,SAAM,EAAE,SAAS;AACjB;;AAIF,UAAQ,MAAM,iBAAiB;AAC/B,QAAM,EAAE,UAAU,KAAK,OAAO,EAC5B,aAAa,MAAW;AACtB,WAAQ,OAAO,EAAE,UAAU;KAE9B,CAAC;AAEF,UAAQ,OAAO;EACf,IAAI,WAAW;AACf,aAAW,MAAM,SAAS,EAAE,OAAO,UAAU;GAC3C,QAAQ,KAAK;GACb,UAAU,KAAK;GAChB,CAAC,CACA,aAAY;AAGd,UAAQ,QAAQ,qBAAqB;AACrC,UAAQ,IAAI,MAAM,KAAK,gBAAgB,GAAG,SAAS,MAAM,CAAC;AAG1D,UAAQ,MAAM,iBAAiB;AAC/B,QAAM,EAAE,QAAQ,EACd,aAAa,MAAW;AACtB,WAAQ,OAAO,EAAE,UAAU;KAE9B,CAAC;AAEF,UAAQ,OAAO;EACf,MAAM,cAAc,MAAM,EAAE,MAAM,SAAS,MAAM,EAAE;GACjD,OAAO,KAAK;GACZ,OAAO;GACR,CAAC;EAGF,MAAM,WAAWE,OAAK,KAAKD,KAAG,QAAQ,EAAE,gBAAgB,KAAK,KAAK,CAAC,MAAM;EACzE,MAAM,YAAY,WAAW,YAAY,OAAO,YAAY,WAAW;AACvE,OAAG,cAAc,UAAU,UAAU;AAErC,UAAQ,QAAQ,aAAa,YAAY,SAAS,QAAQ,EAAE,CAAC,IAAI;EAGjE,MAAM,WAAWA,KAAG,UAAU;AAC9B,MAAI,aAAa,SACf,QAAK,WAAW,SAAS,UAAUD,KAAG,WAAW,SAAS,CAAC;WAClD,aAAa,QACtB,QAAK,UAAU,SAAS,UAAUA,KAAG,WAAW,SAAS,CAAC;MAE1D,SAAQ,IAAI,MAAM,KAAK,mBAAmB,WAAW,CAAC;AAGxD,QAAM,EAAE,SAAS;UACVG,GAAQ;AACf,UAAQ,KAAK,QAAQ;AACrB,UAAQ,MAAM,MAAM,IAAI,EAAE,QAAQ,CAAC;AACnC,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,oBAAoB,CAC5B,YAAY,qDAAqD,CACjE,OAAO,uBAAuB,gBAAgB,kBAAkB,CAChE,OAAO,yBAAyB,0CAA0C,CAC1E,OAAO,oBAAoB,mCAAmC,CAC9D,OAAO,uBAAuB,6BAA6B,CAC3D,OAAO,iBAAiB,4BAA4B,CACpD,OAAO,OAAO,MAAM,SAAS;CAC5B,MAAM,IAAI,IAAI,QAAQ;AAGtB,KAAI,KAAK,YAAY;AACnB,UAAQ,IAAI,MAAM,KAAK,+BAA+B,CAAC;EACvD,MAAM,SAAS,MAAM,EAAE,eAAe;AACtC,OAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,QAAQ,MAAM,eAAe,UAAU,MAAM,UAAU,KAAK,IAAI;AACtE,WAAQ,IACN,KAAK,MAAM,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,MAAM,KAAK,OAAO,EAAE,CAAC,GAAG,MAAM,YAAY,IAAI,MAAM,GAC9F;;AAEH,UAAQ,KAAK;AACb;;AAIF,KAAI,CAAC,MAAM;AACT,UAAQ,IACN,MAAM,OAAO,qEAAqE,CACnF;AACD,UAAQ,IAAI,MAAM,KAAK,yCAAyC,CAAC;AACjE;;CAGF,MAAM,UAAU,IAAI,sBAAsB,KAAK,MAAM,MAAM,CAAC,OAAO;AAEnE,KAAI;EACF,MAAMH,OAAK,MAAM,OAAO;EACxB,MAAME,SAAO,MAAM,OAAO;EAG1B,MAAM,WAAWA,OAAK,QAAQ,KAAK;AACnC,MAAI,CAACF,KAAG,WAAW,SAAS,EAAE;AAC5B,WAAQ,KAAK,mBAAmB,WAAW;AAC3C,WAAQ,KAAK,EAAE;;EAGjB,MAAM,YAAY,IAAI,WAAWA,KAAG,aAAa,SAAS,CAAC;AAC3D,UAAQ,OAAO,gBAAgBE,OAAK,SAAS,SAAS,CAAC;EAGvD,MAAM,SAAS,MAAM,EAAE,WAAW,WAAW;GAC3C,UAAU,KAAK;GACf,YAAY,KAAK;GACjB,aAAa,MAAM;AACjB,YAAQ,OAAO,EAAE;;GAEpB,CAAC;AAEF,UAAQ,QACN,eAAe,OAAO,SAAS,QAAQ,EAAE,CAAC,aAAa,OAAO,UAAU,QAAQ,EAAE,CAAC,IACpF;AAGD,UAAQ,KAAK;AACb,MAAI,KAAK,cAAc,OAAO,SAC5B,MAAK,MAAM,OAAO,OAAO,SACvB,SAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,QAAQ,EAAE,CAAC,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK;MAG1F,SAAQ,IAAI,OAAO,KAAK;AAI1B,MAAI,KAAK,QAAQ;GACf,IAAI,UAAU,OAAO;AACrB,OAAI,KAAK,cAAc,OAAO,SAC5B,WAAU,OAAO,SACd,KAAK,QAAQ,IAAI,IAAI,MAAM,QAAQ,EAAE,CAAC,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC,KAAK,IAAI,OAAO,CAC/E,KAAK,KAAK;AAEf,QAAG,cAAc,KAAK,QAAQ,QAAQ;AACtC,WAAQ,IAAI,MAAM,MAAM,kBAAkB,KAAK,SAAS,CAAC;;AAG3D,QAAM,EAAE,SAAS;UACVC,GAAQ;AACf,UAAQ,KAAK,QAAQ;AACrB,UAAQ,MAAM,MAAM,IAAI,EAAE,QAAQ,CAAC;AACnC,UAAQ,KAAK,EAAE;;EAEjB;;;;AAKJ,SAAS,WAAW,OAAqB,YAA4B;CACnE,MAAM,cAAc;CACpB,MAAM,gBAAgB;CACtB,MAAM,WAAY,aAAa,cAAc,gBAAiB;CAC9D,MAAM,aAAc,cAAc,gBAAiB;CACnD,MAAM,WAAW,MAAM,SAAS;CAChC,MAAM,WAAW,KAAK;CAEtB,MAAM,SAAS,OAAO,MAAM,KAAK,SAAS;CAC1C,IAAI,SAAS;AAEb,QAAO,MAAM,QAAQ,OAAO;AAC5B,WAAU;AACV,QAAO,cAAc,UAAU,OAAO;AACtC,WAAU;AACV,QAAO,MAAM,QAAQ,OAAO;AAC5B,WAAU;AACV,QAAO,MAAM,QAAQ,OAAO;AAC5B,WAAU;AACV,QAAO,cAAc,IAAI,OAAO;AAChC,WAAU;AACV,QAAO,cAAc,GAAG,OAAO;AAC/B,WAAU;AACV,QAAO,cAAc,aAAa,OAAO;AACzC,WAAU;AACV,QAAO,cAAc,YAAY,OAAO;AACxC,WAAU;AACV,QAAO,cAAc,UAAU,OAAO;AACtC,WAAU;AACV,QAAO,cAAc,YAAY,OAAO;AACxC,WAAU;AACV,QAAO,cAAc,eAAe,OAAO;AAC3C,WAAU;AACV,QAAO,MAAM,QAAQ,OAAO;AAC5B,WAAU;AACV,QAAO,cAAc,UAAU,OAAO;AACtC,WAAU;AAEV,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,SAAS,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,MAAM,GAAG,CAAC;EAClD,MAAM,QAAQ,SAAS,IAAI,SAAS,QAAQ,SAAS;AACrD,SAAO,aAAa,KAAK,MAAM,MAAM,EAAE,OAAO;AAC9C,YAAU;;AAGZ,QAAO;;AAOT,QACG,QAAQ,QAAQ,CAChB,YAAY,0EAA0E,CACtF,OAAO,oBAAoB,gBAAgB,aAAa,CACxD,OAAO,qBAAqB,aAAa,OAAO,CAChD,OAAO,SAAS,2BAA2B,CAC3C,OAAO,UAAU,+BAA+B,CAChD,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,KAAI,EAAE,KAAK,OAAO,KAAK,OAAO;AAE5B,QAAM,UAAU;GAAE,aAAa;GAAS,cADnB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;GACR,CAAC;AACvD;;AAEF,KAAI,KAAK,IACP,OAAM,eAAe,EAAE,OAAO,KAAK,OAAO,CAAC;MACtC;EAEL,MAAM,EAAE,qBAAW,MAAM,OAAO;EAChC,MAAM,IAAI,IAAIC,UAAQ;EAEtB,MAAM,UAAU,IAAI,mBAAmB,CAAC,OAAO;AAC/C,QAAM,EAAE,UAAU,KAAK,MAAM;AAC7B,UAAQ,QAAQ,eAAe;AA6B/B,GA1Ba,MAAM,OAAO,cAEN,aAAa,OAAO,KAAK,QAAQ;AACnD,OAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,aAAa;IACpD,IAAI,OAAO;AACX,QAAI,GAAG,SAAS,UAAW,QAAQ,MAAO;AAC1C,QAAI,GAAG,OAAO,YAAY;AACxB,SAAI;MACF,MAAM,EAAE,QAAQ,GAAGC,WAAS,KAAK,MAAM,KAAK;MAC5C,MAAM,SAAS,MAAM,EAAE,SAAS,QAAQA,OAAK;AAC7C,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,KAAK,UAAU,OAAO,CAAC;cACxB,GAAG;AACV,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,UAAI,IAAI,KAAK,UAAU,EAAE,OAAO,OAAO,EAAE,EAAE,CAAC,CAAC;;MAE/C;cACO,IAAI,WAAW,SAAS,IAAI,QAAQ,SAAS;AACtD,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,QAAI,IAAI,KAAK,UAAU,EAAE,SAAS,CAAC,CAAC;UAC/B;AACL,QAAI,UAAU,IAAI;AAClB,QAAI,IAAI,YAAY;;IAEtB,CAEK,OAAO,OAAO,SAAS,KAAK,MAAM,GAAG,QAAQ,GAAG;;EAEzD;AAMJ,QACG,QAAQ,SAAS,CACjB,YAAY,wBAAwB,CACpC,OAAO,oBAAoB,gBAAgB,CAC3C,OAAO,OAAO,SAAS;CACtB,IAAI,SAAS,OAAO,OAAO,eAAe;AAE1C,KAAI,KAAK,QAAQ;EACf,MAAM,IAAI,KAAK,OAAO,aAAa;AACnC,WAAS,OAAO,QACb,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,EAAE,YAAY,aAAa,CAAC,SAAS,EAAE,CACjF;;AAGH,MAAK,MAAM,KAAK,OACd,KAAI,EAAE,kBAAkB;EAG1B;AAMJ,QACG,QAAQ,OAAO,CACf,YAAY,+EAA+E,CAC3F,OAAO,oBAAoB,mCAAmC,aAAa,CAC3E,OAAO,SAAS,2CAA2C,CAC3D,OAAO,SAAS,2BAA2B,CAC3C,OAAO,SAAS,kCAAkC,CAClD,OAAO,OAAO,SAAS;AAEtB,KAAI,CAAC,KAAK,KAAK;AAEb,QAAM,UAAU;GAAE,aAAa;GAAQ,cADlB,KAAK,MAAM,QAAQ,KAAK,MAAM,WAAW;GACT,CAAC;AACtD;;CAGF,MAAM,IAAI,IAAI,QAAQ;CACtB,MAAM,UAAU,IAAI,WAAW,MAAM,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO;AAEnE,KAAI;AACF,QAAM,EAAE,UAAU,KAAK,OAAO,EAC5B,aAAa,MAAM;AACjB,WAAQ,OAAO,EAAE,WAAW,GAAG,EAAE,OAAO,IAAI,EAAE,SAAS,MAAM,EAAE;KAElE,CAAC;AACF,UAAQ,MAAM;AAEA,IAAE,SAAS;AAEzB,QAAM,EAAE,SAAS;UACV,IAAI;AACX,UAAQ,KAAK,sBAAsB;;EAErC;AAMJ,QACG,QAAQ,QAAQ,CAChB,YAAY,6BAA6B,CACzC,OAAO,WAAW,2BAA2B,CAC7C,OAAO,uBAAuB,kCAAkC,CAChE,OAAO,OAAO,SAAS;CACtB,MAAML,OAAK,MAAM,OAAO;CACxB,MAAME,SAAO,MAAM,OAAO;CAC1B,MAAMD,OAAK,MAAM,OAAO;CAGxB,MAAM,mBAAmBC,OAAK,KAC5B,QAAQ,KAAK,EACb,gBACA,gBACA,gBACA,SACD;CACD,MAAM,WAAWF,KAAG,WAAW,iBAAiB,GAC5C,mBACA,QAAQ,IAAI,WACZ,QAAQ,IAAI,sBACZE,OAAK,KAAKD,KAAG,SAAS,EAAE,UAAU,eAAe,MAAM;AAE3D,KAAI;AACF,MAAI,CAACD,KAAG,WAAW,SAAS,CAC1B;EAGF,MAAM,UAAUA,KAAG,YAAY,SAAS;EACxC,MAAMM,SAKA,EAAE;EAGR,MAAM,WAAW,QAAwB;GACvC,IAAI,QAAQ;AACZ,OAAI;IACF,MAAM,QAAQN,KAAG,YAAY,IAAI;AACjC,SAAK,MAAM,QAAQ,OAAO;KACxB,MAAM,WAAWE,OAAK,KAAK,KAAK,KAAK;KACrC,MAAM,WAAWF,KAAG,SAAS,SAAS;AACtC,SAAI,SAAS,aAAa,CACxB,UAAS,QAAQ,SAAS;SAE1B,UAAS,SAAS;;WAGhB;AACR,UAAO;;AAGT,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,YAAYE,OAAK,KAAK,UAAU,MAAM;AAC5C,OAAI;IACF,MAAM,YAAYF,KAAG,SAAS,UAAU;AACxC,QAAI,CAAC,UAAU,aAAa,CAC1B;AAIF,QAAI,MAAM,WAAW,WAAW,EAAE;KAChC,MAAM,YAAY,MAAM,QAAQ,YAAY,GAAG,CAAC,QAAQ,MAAM,IAAI;KAClE,MAAM,OAAO,QAAQ,UAAU;AAC/B,YAAO,KAAK;MACV,MAAM;MACN;MACA,OAAO,UAAU;MACjB,MAAM;MACP,CAAC;WACG;KAEL,MAAM,aAAaA,KAAG,YAAY,UAAU;AAC5C,UAAK,MAAM,YAAY,YAAY;MACjC,MAAM,UAAUE,OAAK,KAAK,WAAW,SAAS;AAC9C,UAAI;OACF,MAAM,UAAUF,KAAG,SAAS,QAAQ;AACpC,WAAI,QAAQ,aAAa,EAAE;QACzB,MAAM,YAAY,GAAG,MAAM,GAAG;QAC9B,MAAM,OAAO,QAAQ,QAAQ;AAC7B,YAAI,OAAO,EACT,QAAO,KAAK;SACV,MAAM;SACN;SACA,OAAO,QAAQ;SACf,MAAM;SACP,CAAC;;cAGA;;;WAGN;;AAGV,MAAI,OAAO,WAAW,EACpB;AAIF,MAAI,KAAK,SAAS,KAAK,WAAW;GAChC,MAAM,gBAAgB,KAAK,YAAY,OAAO,SAAS,KAAK,WAAW,GAAG,GAAG;GAC7E,MAAM,6BAAa,IAAI,KAAK,KAAK,KAAK,GAAG,gBAAgB,KAAK,KAAK,KAAK,IAAK;GAE7E,IAAI,UAAU;GACd,IAAI,eAAe;AAEnB,QAAK,MAAM,SAAS,OAClB,KAAI,KAAK,SAAS,MAAM,QAAQ,WAC9B,KAAI;AACF,SAAG,OAAO,MAAM,MAAM;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC;AACvD,eAAW;AACX,oBAAgB,MAAM;YACf,IAAI;AAIjB,OAAI,UAAU,GAAG;AAGjB;;EAEF,IAAI,aAAa;AAEjB,OAAK,MAAM,SAAS,OAAO,MAAM,GAAG,MAAM,EAAE,MAAM,SAAS,GAAG,EAAE,MAAM,SAAS,CAAC,EAAE;AAC/D,iBAAc,MAAM,MAAM;AAC1B,cAAW,MAAM,KAAK,CAAC,SAAS,EAAE;AACnD,iBAAc,MAAM;;UAEf,IAAI;EACb;AAMJ,MAAM,aAAa;AAMnB,QACG,QAAQ,UAAU,CAClB,YAAY,+CAA+C,CAC3D,OAAO,kBAAkB,mEAAmE,CAC5F,OAAO,WAAW,uEAAuE,CACzF,OAAO,OAAO,SAAS;CACtB,MAAM,EAAE,qBAAqB,MAAM,OAAO;CAC1C,MAAM,EAAE,yBAAa,MAAM,OAAO;AAElC,SAAQ,IAAI,MAAM,KAAK,wCAAwC,CAAC;CAGhE,IAAIO,aAAuB,EAAE;AAC7B,KAAI;AAKF,eAJiBC,WAAS,wDAAsD,EAC9E,UAAU,SACX,CAAC,CACqB,MAAM,CAAC,MAAM,KAAK,CAAC,OAAO,QAAQ,CACtC,KAAK,SAAS;GAC/B,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,MAAM;AACtC,UAAO,OAAO,SAAS,MAAM,IAAI,GAAG;IACpC;SACI;AAIR,KAAI,KAAK,SAAS,WAAW,SAAS,GAAG;EACvC,MAAM,UAAU,IAAI,iBAAiB,WAAW,OAAO,6BAA6B,CAAC,OAAO;AAC5F,MAAI;AACF,cAAS,oCAAkC,EAAE,OAAO,UAAU,CAAC;AAC/D,WAAQ,QAAQ,gBAAgB,WAAW,OAAO,cAAc;UAC1D;AACN,WAAQ,KAAK,2BAA2B;;AAE1C,UAAQ,KAAK;AACb;;CAGF,MAAM,SAAS,iBAAiB,wBAAwB;AAExD,KAAI,CAAC,OAAO,SAAS;AACnB,MAAI,WAAW,SAAS,GAAG;AACzB,WAAQ,IACN,MAAM,OACJ,SAAS,WAAW,OAAO,kDAC5B,CACF;AACD,WAAQ,IAAI,MAAM,KAAK,mDAAmD,CAAC;QAE3E,SAAQ,IAAI,MAAM,KAAK,6BAA6B,CAAC;AAEvD;;AAGF,SAAQ,IAAI,eAAe,MAAM,OAAO,OAAO,IAAI,GAAG;AACtD,SAAQ,IAAI,gBAAgB,MAAM,OAAO,OAAO,KAAK,GAAG;AACxD,SAAQ,IAAI,iBAAiB,MAAM,OAAO,OAAO,iBAAiB,CAAC,GAAG,OAAO,WAAW;CAGxF,MAAM,WAAW,MAAM,iBAAiB,mBAAmB;AAC3D,KAAI,SAAS,SAAS,GAAG;AACvB,UAAQ,IAAI,MAAM,KAAK,kBAAkB,CAAC;AAC1C,OAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GACxC,MAAM,IAAI,SAAS;GACnB,MAAM,SAAS,EAAE,SAAS,GAAG,EAAE,OAAO,OAAO,QAAQ,EAAE,CAAC,MAAM;GAC9D,MAAM,QAAQ,EAAE,SAAS,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,WAAW;AACjF,WAAQ,IAAI,MAAM,EAAE,IAAI,EAAE,WAAW,UAAU,KAAK,OAAO,GAAG,QAAQ;;;AAI1E,KAAI,KAAK,aAAa;EACpB,MAAM,UAAU,IAAI,4BAA4B,CAAC,OAAO;EACxD,MAAM,SAAS,MAAM,iBAAiB,iBAAiB;AACvD,UAAQ,QACN,UAAU,OAAO,YAAY,UAAU,OAAO,gBAAgB,iBAAiB,KAChF;QACI;EAEL,MAAM,UAAU,IAAI,8BAA8B,CAAC,OAAO;EAC1D,IAAI,UAAU;AACd,OAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,IACxC,KAAI,CAAC,SAAS,GAAG,QAEf;OADe,MAAM,iBAAiB,gBAAgB,EAAE,CAC5C;;AAGhB,MAAI,UAAU,EACZ,SAAQ,QAAQ,cAAc,QAAQ,iBAAiB;MAEvD,SAAQ,KAAK,8BAA8B;;AAI/C,SAAQ,KAAK;EACb;AAMJ,QACG,QAAQ,QAAQ,CAChB,YAAY,8BAA8B,CAC1C,OAAO,oBAAoB,sBAAsB,aAAa,CAC9D,OAAO,kBAAkB,kBAAkB,IAAI,CAC/C,OAAO,OAAO,SAAS;CACtB,MAAM,IAAI,IAAI,QAAQ;CACtB,MAAM,UAAU,IAAI,WAAW,MAAM,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO;AAEnE,KAAI;AACF,QAAM,EAAE,UAAU,KAAK,OAAO,EAC5B,aAAa,MAAM;AACjB,WAAQ,OAAO,EAAE,WAAW,GAAG,EAAE,OAAO,IAAI,EAAE,SAAS,MAAM,EAAE;KAElE,CAAC;AACF,UAAQ,QAAQ,eAAe;EAE/B,MAAM,OAAO,OAAO,SAAS,KAAK,MAAM,GAAG;EAC3C,MAAMC,UAIA,EAAE;EACR,MAAM,UAAU;GACd;GACA;GACA;GACD;AAED,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,KAAK,GAAG;GAChC,MAAM,SAAS,QAAQ,IAAI,QAAQ;GACnC,MAAM,aAAa,IAAI,OAAO,IAAI,EAAE,GAAG,KAAK,KAAK,CAAC,OAAO;GAEzD,MAAM,YAAY,KAAK,KAAK;GAC5B,IAAI,iBAAiB;GACrB,IAAI,aAAa;AAEjB,cAAW,MAAM,UAAU,EAAE,OAAO,QAAQ,EAAE,WAAW,KAAK,CAAC,EAAE;AAC/D,QAAI,eAAe,EACjB,kBAAiB,KAAK,KAAK,GAAG;AAEhC,kBAAc;;GAGhB,MAAM,YAAY,KAAK,KAAK,GAAG;GAC/B,MAAM,YAAY,KAAK,MAAO,aAAa,YAAa,IAAK;AAE7D,WAAQ,KAAK;IACX;IACA,YAAY;IACZ,QAAQ;IACT,CAAC;AACF,cAAW,QACT,OAAO,IAAI,EAAE,IAAI,MAAM,KAAK,UAAU,CAAC,UAAU,MAAM,OACrD,GAAG,eAAe,IACnB,CAAC,cACH;;AAImB,OAAK,MACzB,QAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,WAAW,EAAE,GAAG,QAAQ,OACxD;AACsB,OAAK,MAC1B,QAAQ,QAAQ,GAAG,MAAM,IAAI,EAAE,YAAY,EAAE,GAAG,QAAQ,OACzD;AAED,QAAM,EAAE,SAAS;UACV,IAAI;AACX,UAAQ,KAAK,QAAQ;AACrB,UAAQ,KAAK,EAAE;;EAEjB;AAMJ,QACG,QAAQ,SAAS,CACjB,YAAY,sCAAsC,CAClD,OAAO,YAAY;CAClB,MAAM,EAAE,kCAAgB,gCAAe,uCAAoB,MAAM,OAC/D;CAGF,MAAM,UAAU,IAAI,0BAA0B,CAAC,OAAO;AAEtD,KAAI;EACF,MAAM,QAAQ,MAAMC,kBAAgB;AAEpC,MAAI,CAAC,MAAM,iBAAiB;AAC1B,WAAQ,QAAQ,MAAM,MAAM,+BAA+BC,kBAAgB,GAAG,CAAC;AAC/E;;AAGF,UAAQ,KAAK,MAAM,KAAK,sBAAsBA,kBAAgB,MAAM,MAAM,gBAAgB,CAAC;EAE3F,MAAM,gBAAgB,IAAI,eAAe,MAAM,cAAc,KAAK,CAAC,OAAO;EAC1E,MAAM,SAAS,MAAMC,iBAAe;AAEpC,MAAI,OAAO,QACT,eAAc,QACZ,MAAM,MAAM,iBAAiB,OAAO,WAAW,MAAM,gBAAgB,CACtE;OACI;AACL,iBAAc,KAAK,MAAM,IAAI,gBAAgB,CAAC;AAC9C,WAAQ,KAAK,EAAE;;UAEV,IAAI;AACX,UAAQ,KAAK,sBAAsB;AACnC,UAAQ,KAAK,EAAE;;EAEjB;AAOJ,MAAM,gBAAgB,QAAQ,MAAM,KAAK,QAAQ;AACjD,QAAQ,SAAS,GAAG,SAAS;CAC3B,MAAM,SAAS,cAAc,GAAG,KAAK;AAGrC,oBAAmB,CAAC,YAAY,GAE9B;AAEF,QAAO;;AAGT,eAAe,oBAAoB;CACjC,MAAM,EAAE,kCAAgB,uCAAoB,MAAM,OAAO;AAGzD,MAFc,MAAMF,kBAAgB,EAE1B,iBAAiB;;AAI7B,QAAQ,OAAO"}
|