@happyvertical/smrt-svelte 0.30.0
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/AGENTS.md +317 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +185 -0
- package/dist/Provider.svelte +204 -0
- package/dist/Provider.svelte.d.ts +73 -0
- package/dist/Provider.svelte.d.ts.map +1 -0
- package/dist/__tests__/app-state.test.js +156 -0
- package/dist/__tests__/warm-clients.test.js +186 -0
- package/dist/browser-ai/adapters/llm/factory.d.ts +38 -0
- package/dist/browser-ai/adapters/llm/factory.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/factory.js +91 -0
- package/dist/browser-ai/adapters/llm/index.d.ts +7 -0
- package/dist/browser-ai/adapters/llm/index.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/index.js +6 -0
- package/dist/browser-ai/adapters/llm/types.d.ts +182 -0
- package/dist/browser-ai/adapters/llm/types.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/types.js +43 -0
- package/dist/browser-ai/adapters/llm/webllm.d.ts +33 -0
- package/dist/browser-ai/adapters/llm/webllm.d.ts.map +1 -0
- package/dist/browser-ai/adapters/llm/webllm.js +225 -0
- package/dist/browser-ai/adapters/stt/browser-speech.d.ts +31 -0
- package/dist/browser-ai/adapters/stt/browser-speech.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/browser-speech.js +217 -0
- package/dist/browser-ai/adapters/stt/factory.d.ts +49 -0
- package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/factory.js +110 -0
- package/dist/browser-ai/adapters/stt/index.d.ts +9 -0
- package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/index.js +8 -0
- package/dist/browser-ai/adapters/stt/types.d.ts +154 -0
- package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/types.js +4 -0
- package/dist/browser-ai/adapters/stt/whisper-cpp.d.ts +46 -0
- package/dist/browser-ai/adapters/stt/whisper-cpp.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/whisper-cpp.js +348 -0
- package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +51 -0
- package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/whisper-wasm.js +380 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts +42 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.js +235 -0
- package/dist/browser-ai/adapters/tts/factory.d.ts +53 -0
- package/dist/browser-ai/adapters/tts/factory.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/factory.js +92 -0
- package/dist/browser-ai/adapters/tts/index.d.ts +7 -0
- package/dist/browser-ai/adapters/tts/index.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/index.js +6 -0
- package/dist/browser-ai/adapters/tts/types.d.ts +140 -0
- package/dist/browser-ai/adapters/tts/types.d.ts.map +1 -0
- package/dist/browser-ai/adapters/tts/types.js +4 -0
- package/dist/browser-ai/capabilities/detector.d.ts +38 -0
- package/dist/browser-ai/capabilities/detector.d.ts.map +1 -0
- package/dist/browser-ai/capabilities/detector.js +211 -0
- package/dist/browser-ai/core/errors.d.ts +62 -0
- package/dist/browser-ai/core/errors.d.ts.map +1 -0
- package/dist/browser-ai/core/errors.js +92 -0
- package/dist/browser-ai/core/index.d.ts +6 -0
- package/dist/browser-ai/core/index.d.ts.map +1 -0
- package/dist/browser-ai/core/index.js +5 -0
- package/dist/browser-ai/core/types.d.ts +115 -0
- package/dist/browser-ai/core/types.d.ts.map +1 -0
- package/dist/browser-ai/core/types.js +34 -0
- package/dist/browser-ai/index.d.ts +12 -0
- package/dist/browser-ai/index.d.ts.map +1 -0
- package/dist/browser-ai/index.js +16 -0
- package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte +77 -0
- package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte.d.ts +16 -0
- package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/CapabilityGate.svelte +57 -0
- package/dist/browser-ai/svelte/components/CapabilityGate.svelte.d.ts +15 -0
- package/dist/browser-ai/svelte/components/CapabilityGate.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/DownloadProgress.svelte +141 -0
- package/dist/browser-ai/svelte/components/DownloadProgress.svelte.d.ts +15 -0
- package/dist/browser-ai/svelte/components/DownloadProgress.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/STTTest.svelte +379 -0
- package/dist/browser-ai/svelte/components/STTTest.svelte.d.ts +9 -0
- package/dist/browser-ai/svelte/components/STTTest.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/components/VoiceInput.svelte +200 -0
- package/dist/browser-ai/svelte/components/VoiceInput.svelte.d.ts +16 -0
- package/dist/browser-ai/svelte/components/VoiceInput.svelte.d.ts.map +1 -0
- package/dist/browser-ai/svelte/index.d.ts +15 -0
- package/dist/browser-ai/svelte/index.d.ts.map +1 -0
- package/dist/browser-ai/svelte/index.js +28 -0
- package/dist/browser-ai/ui.d.ts +16 -0
- package/dist/browser-ai/ui.d.ts.map +1 -0
- package/dist/browser-ai/ui.js +67 -0
- package/dist/components/admin/AgentAdminPanel.svelte +111 -0
- package/dist/components/admin/AgentAdminPanel.svelte.d.ts +25 -0
- package/dist/components/admin/AgentAdminPanel.svelte.d.ts.map +1 -0
- package/dist/components/admin/AgentAdminTabs.svelte +280 -0
- package/dist/components/admin/AgentAdminTabs.svelte.d.ts +23 -0
- package/dist/components/admin/AgentAdminTabs.svelte.d.ts.map +1 -0
- package/dist/components/admin/AgentSettingsShell.svelte +257 -0
- package/dist/components/admin/AgentSettingsShell.svelte.d.ts +33 -0
- package/dist/components/admin/AgentSettingsShell.svelte.d.ts.map +1 -0
- package/dist/components/admin/index.d.ts +5 -0
- package/dist/components/admin/index.d.ts.map +1 -0
- package/dist/components/admin/index.js +6 -0
- package/dist/components/forms/AddressInput.svelte +500 -0
- package/dist/components/forms/AddressInput.svelte.d.ts +36 -0
- package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/CheckboxInput.svelte +208 -0
- package/dist/components/forms/CheckboxInput.svelte.d.ts +20 -0
- package/dist/components/forms/CheckboxInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/DateRangeInput.svelte +628 -0
- package/dist/components/forms/DateRangeInput.svelte.d.ts +33 -0
- package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/DateTimeInput.svelte +521 -0
- package/dist/components/forms/DateTimeInput.svelte.d.ts +24 -0
- package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/FileUpload.svelte +358 -0
- package/dist/components/forms/FileUpload.svelte.d.ts +22 -0
- package/dist/components/forms/FileUpload.svelte.d.ts.map +1 -0
- package/dist/components/forms/Form.svelte +771 -0
- package/dist/components/forms/Form.svelte.d.ts +26 -0
- package/dist/components/forms/Form.svelte.d.ts.map +1 -0
- package/dist/components/forms/FormGroup.svelte +86 -0
- package/dist/components/forms/FormGroup.svelte.d.ts +13 -0
- package/dist/components/forms/FormGroup.svelte.d.ts.map +1 -0
- package/dist/components/forms/FormMicButton.svelte +179 -0
- package/dist/components/forms/FormMicButton.svelte.d.ts +10 -0
- package/dist/components/forms/FormMicButton.svelte.d.ts.map +1 -0
- package/dist/components/forms/Input.svelte +83 -0
- package/dist/components/forms/Input.svelte.d.ts +9 -0
- package/dist/components/forms/Input.svelte.d.ts.map +1 -0
- package/dist/components/forms/MeasurementInput.svelte +505 -0
- package/dist/components/forms/MeasurementInput.svelte.d.ts +35 -0
- package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/MoneyInput.svelte +412 -0
- package/dist/components/forms/MoneyInput.svelte.d.ts +30 -0
- package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/NumberInput.svelte +310 -0
- package/dist/components/forms/NumberInput.svelte.d.ts +28 -0
- package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/PhoneInput.svelte +530 -0
- package/dist/components/forms/PhoneInput.svelte.d.ts +22 -0
- package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/SearchInput.svelte +358 -0
- package/dist/components/forms/SearchInput.svelte.d.ts +33 -0
- package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Select.svelte +83 -0
- package/dist/components/forms/Select.svelte.d.ts +11 -0
- package/dist/components/forms/Select.svelte.d.ts.map +1 -0
- package/dist/components/forms/SelectInput.svelte +254 -0
- package/dist/components/forms/SelectInput.svelte.d.ts +25 -0
- package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/TextInput.svelte +415 -0
- package/dist/components/forms/TextInput.svelte.d.ts +26 -0
- package/dist/components/forms/TextInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Textarea.svelte +85 -0
- package/dist/components/forms/Textarea.svelte.d.ts +10 -0
- package/dist/components/forms/Textarea.svelte.d.ts.map +1 -0
- package/dist/components/forms/TextareaInput.svelte +386 -0
- package/dist/components/forms/TextareaInput.svelte.d.ts +26 -0
- package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Toggle.svelte +217 -0
- package/dist/components/forms/Toggle.svelte.d.ts +37 -0
- package/dist/components/forms/Toggle.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/AddressInput.behavior.test.js +122 -0
- package/dist/components/forms/__tests__/CheckboxInput.test.js +92 -0
- package/dist/components/forms/__tests__/DateRangeInput.behavior.test.js +135 -0
- package/dist/components/forms/__tests__/DateTimeInput.behavior.test.js +103 -0
- package/dist/components/forms/__tests__/FileUpload.test.js +90 -0
- package/dist/components/forms/__tests__/Form.behavior.test.js +137 -0
- package/dist/components/forms/__tests__/Form.test.js +58 -0
- package/dist/components/forms/__tests__/FormGroup.test.js +48 -0
- package/dist/components/forms/__tests__/FormMicButton.test.js +86 -0
- package/dist/components/forms/__tests__/Input.test.js +49 -0
- package/dist/components/forms/__tests__/MeasurementInput.behavior.test.js +129 -0
- package/dist/components/forms/__tests__/MoneyInput.behavior.test.js +124 -0
- package/dist/components/forms/__tests__/NumberInput.behavior.test.js +141 -0
- package/dist/components/forms/__tests__/PhoneInput.behavior.test.js +96 -0
- package/dist/components/forms/__tests__/SearchInput.test.js +79 -0
- package/dist/components/forms/__tests__/Select.test.js +37 -0
- package/dist/components/forms/__tests__/SelectInput.behavior.test.js +132 -0
- package/dist/components/forms/__tests__/TextInput.behavior.test.js +131 -0
- package/dist/components/forms/__tests__/Textarea.test.js +39 -0
- package/dist/components/forms/__tests__/TextareaInput.behavior.test.js +96 -0
- package/dist/components/forms/__tests__/Toggle.test.js +87 -0
- package/dist/components/forms/__tests__/composite-inputs-a11y.test.js +69 -0
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte +16 -0
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts +9 -0
- package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-with-fields.fixture.svelte +33 -0
- package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/rich-inputs-a11y.test.js +87 -0
- package/dist/components/forms/index.d.ts +25 -0
- package/dist/components/forms/index.d.ts.map +1 -0
- package/dist/components/forms/index.js +25 -0
- package/dist/components/forms/types.d.ts +33 -0
- package/dist/components/forms/types.d.ts.map +1 -0
- package/dist/components/forms/types.js +4 -0
- package/dist/components/module/ModulePanel.svelte +134 -0
- package/dist/components/module/ModulePanel.svelte.d.ts +22 -0
- package/dist/components/module/ModulePanel.svelte.d.ts.map +1 -0
- package/dist/components/module/index.d.ts +5 -0
- package/dist/components/module/index.d.ts.map +1 -0
- package/dist/components/module/index.js +4 -0
- package/dist/components/workspace/Breadcrumbs.svelte +141 -0
- package/dist/components/workspace/Breadcrumbs.svelte.d.ts +21 -0
- package/dist/components/workspace/Breadcrumbs.svelte.d.ts.map +1 -0
- package/dist/components/workspace/NavTree.svelte +354 -0
- package/dist/components/workspace/NavTree.svelte.d.ts +45 -0
- package/dist/components/workspace/NavTree.svelte.d.ts.map +1 -0
- package/dist/components/workspace/README.md +34 -0
- package/dist/components/workspace/RoleShell.svelte +309 -0
- package/dist/components/workspace/RoleShell.svelte.d.ts +91 -0
- package/dist/components/workspace/RoleShell.svelte.d.ts.map +1 -0
- package/dist/components/workspace/WorkspaceShell.svelte +951 -0
- package/dist/components/workspace/WorkspaceShell.svelte.d.ts +112 -0
- package/dist/components/workspace/WorkspaceShell.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/RoleShell.test.js +772 -0
- package/dist/components/workspace/__tests__/WorkspaceShell.test.js +630 -0
- package/dist/components/workspace/__tests__/breadcrumbs-helpers.test.js +141 -0
- package/dist/components/workspace/__tests__/context-forwarding-harness.svelte +45 -0
- package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts +21 -0
- package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/define-tools-dock.test.js +1010 -0
- package/dist/components/workspace/__tests__/harness.svelte +25 -0
- package/dist/components/workspace/__tests__/harness.svelte.d.ts +14 -0
- package/dist/components/workspace/__tests__/harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/index.test.js +37 -0
- package/dist/components/workspace/__tests__/manifest-nav-helpers.test.js +24 -0
- package/dist/components/workspace/__tests__/manifest-nav.test.js +599 -0
- package/dist/components/workspace/__tests__/nav-helpers.test.js +95 -0
- package/dist/components/workspace/__tests__/render-harness.svelte +66 -0
- package/dist/components/workspace/__tests__/render-harness.svelte.d.ts +32 -0
- package/dist/components/workspace/__tests__/render-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/render-tools-dock.test.js +243 -0
- package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte +58 -0
- package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts +16 -0
- package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte +41 -0
- package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts +13 -0
- package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/test-icon.svelte +17 -0
- package/dist/components/workspace/__tests__/test-icon.svelte.d.ts +19 -0
- package/dist/components/workspace/__tests__/test-icon.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte +38 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts +22 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts +65 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.js +115 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts +15 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.js +7 -0
- package/dist/components/workspace/__tests__/typed-tool-fixture.test.js +115 -0
- package/dist/components/workspace/__tests__/use-harness-orphan.svelte +9 -0
- package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts +19 -0
- package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/use-harness.svelte +23 -0
- package/dist/components/workspace/__tests__/use-harness.svelte.d.ts +8 -0
- package/dist/components/workspace/__tests__/use-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/__tests__/use-tools-dock.test.js +33 -0
- package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte +43 -0
- package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts +11 -0
- package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts.map +1 -0
- package/dist/components/workspace/breadcrumbs-helpers.d.ts +44 -0
- package/dist/components/workspace/breadcrumbs-helpers.d.ts.map +1 -0
- package/dist/components/workspace/breadcrumbs-helpers.js +88 -0
- package/dist/components/workspace/index.d.ts +16 -0
- package/dist/components/workspace/index.d.ts.map +1 -0
- package/dist/components/workspace/index.js +14 -0
- package/dist/components/workspace/manifest-nav.d.ts +200 -0
- package/dist/components/workspace/manifest-nav.d.ts.map +1 -0
- package/dist/components/workspace/manifest-nav.js +408 -0
- package/dist/components/workspace/nav-helpers.d.ts +36 -0
- package/dist/components/workspace/nav-helpers.d.ts.map +1 -0
- package/dist/components/workspace/nav-helpers.js +60 -0
- package/dist/components/workspace/server/__tests__/compose-availability.test.js +383 -0
- package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts +78 -0
- package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts.map +1 -0
- package/dist/components/workspace/server/__tests__/typed-context-fixture.js +104 -0
- package/dist/components/workspace/server/compose-availability.d.ts +73 -0
- package/dist/components/workspace/server/compose-availability.d.ts.map +1 -0
- package/dist/components/workspace/server/compose-availability.js +114 -0
- package/dist/components/workspace/server/index.d.ts +13 -0
- package/dist/components/workspace/server/index.d.ts.map +1 -0
- package/dist/components/workspace/server/index.js +11 -0
- package/dist/components/workspace/server/types.d.ts +108 -0
- package/dist/components/workspace/server/types.d.ts.map +1 -0
- package/dist/components/workspace/server/types.js +11 -0
- package/dist/components/workspace/tools-dock/ToolsDock.svelte +565 -0
- package/dist/components/workspace/tools-dock/ToolsDock.svelte.d.ts +14 -0
- package/dist/components/workspace/tools-dock/ToolsDock.svelte.d.ts.map +1 -0
- package/dist/components/workspace/tools-dock/define-tools-dock.svelte.d.ts +143 -0
- package/dist/components/workspace/tools-dock/define-tools-dock.svelte.d.ts.map +1 -0
- package/dist/components/workspace/tools-dock/define-tools-dock.svelte.js +487 -0
- package/dist/components/workspace/tools-dock/use-tools-dock.d.ts +41 -0
- package/dist/components/workspace/tools-dock/use-tools-dock.d.ts.map +1 -0
- package/dist/components/workspace/tools-dock/use-tools-dock.js +50 -0
- package/dist/components/workspace/types.d.ts +372 -0
- package/dist/components/workspace/types.d.ts.map +1 -0
- package/dist/components/workspace/types.js +6 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +10 -0
- package/dist/hooks/useAppState.svelte.d.ts +46 -0
- package/dist/hooks/useAppState.svelte.d.ts.map +1 -0
- package/dist/hooks/useAppState.svelte.js +59 -0
- package/dist/hooks/useAuth.svelte.d.ts +41 -0
- package/dist/hooks/useAuth.svelte.d.ts.map +1 -0
- package/dist/hooks/useAuth.svelte.js +43 -0
- package/dist/hooks/useLLM.svelte.d.ts +69 -0
- package/dist/hooks/useLLM.svelte.d.ts.map +1 -0
- package/dist/hooks/useLLM.svelte.js +85 -0
- package/dist/hooks/useSTT.svelte.d.ts +68 -0
- package/dist/hooks/useSTT.svelte.d.ts.map +1 -0
- package/dist/hooks/useSTT.svelte.js +97 -0
- package/dist/hooks/useSocket.svelte.d.ts +45 -0
- package/dist/hooks/useSocket.svelte.d.ts.map +1 -0
- package/dist/hooks/useSocket.svelte.js +54 -0
- package/dist/hooks/useTTS.svelte.d.ts +65 -0
- package/dist/hooks/useTTS.svelte.d.ts.map +1 -0
- package/dist/hooks/useTTS.svelte.js +93 -0
- package/dist/hooks/useTheme.d.ts +13 -0
- package/dist/hooks/useTheme.d.ts.map +1 -0
- package/dist/hooks/useTheme.js +16 -0
- package/dist/i18n/__tests__/server.spec.js +50 -0
- package/dist/i18n/server.d.ts +47 -0
- package/dist/i18n/server.d.ts.map +1 -0
- package/dist/i18n/server.js +58 -0
- package/dist/i18n/strings.forms.d.ts +33 -0
- package/dist/i18n/strings.forms.d.ts.map +1 -0
- package/dist/i18n/strings.forms.js +54 -0
- package/dist/i18n/strings.workspace.d.ts +34 -0
- package/dist/i18n/strings.workspace.d.ts.map +1 -0
- package/dist/i18n/strings.workspace.js +40 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/state/__tests__/warm-clients.test.js +40 -0
- package/dist/state/app-state.d.ts +308 -0
- package/dist/state/app-state.d.ts.map +1 -0
- package/dist/state/app-state.js +64 -0
- package/dist/state/app-state.svelte.d.ts +196 -0
- package/dist/state/app-state.svelte.d.ts.map +1 -0
- package/dist/state/app-state.svelte.js +774 -0
- package/dist/state/context.d.ts +23 -0
- package/dist/state/context.d.ts.map +1 -0
- package/dist/state/context.js +32 -0
- package/dist/state/form-context.d.ts +59 -0
- package/dist/state/form-context.d.ts.map +1 -0
- package/dist/state/form-context.js +31 -0
- package/dist/state/form-group-context.d.ts +13 -0
- package/dist/state/form-group-context.d.ts.map +1 -0
- package/dist/state/form-group-context.js +28 -0
- package/dist/state/index.d.ts +9 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +8 -0
- package/dist/state/warm-clients.d.ts +136 -0
- package/dist/state/warm-clients.d.ts.map +1 -0
- package/dist/state/warm-clients.js +231 -0
- package/package.json +137 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whisper adapter using @xenova/transformers (v2)
|
|
3
|
+
*
|
|
4
|
+
* Uses Whisper models running in the browser via WASM.
|
|
5
|
+
* V2 uses a simpler, more stable pipeline API.
|
|
6
|
+
* Requires downloading model files (~40MB-150MB depending on model size).
|
|
7
|
+
*/
|
|
8
|
+
import { importOptional } from '@happyvertical/smrt-ui/utils/import-optional.js';
|
|
9
|
+
import { CapabilityNotAvailableError, InitializationError, } from '../../core/errors.js';
|
|
10
|
+
// Model IDs - using Xenova's quantized models for v2
|
|
11
|
+
const MODEL_IDS = {
|
|
12
|
+
tiny: 'Xenova/whisper-tiny.en',
|
|
13
|
+
base: 'Xenova/whisper-base.en',
|
|
14
|
+
small: 'Xenova/whisper-small.en',
|
|
15
|
+
};
|
|
16
|
+
// Approximate download sizes
|
|
17
|
+
const MODEL_SIZES = {
|
|
18
|
+
tiny: 40 * 1024 * 1024, // ~40MB
|
|
19
|
+
base: 75 * 1024 * 1024, // ~75MB
|
|
20
|
+
small: 150 * 1024 * 1024, // ~150MB
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Whisper adapter for high-accuracy speech recognition
|
|
24
|
+
* Uses transformers.js v2 pipeline API
|
|
25
|
+
*/
|
|
26
|
+
export class WhisperWasmSTTAdapter {
|
|
27
|
+
type = 'whisper-wasm';
|
|
28
|
+
_initState = 'uninitialized';
|
|
29
|
+
options;
|
|
30
|
+
_isListening = false;
|
|
31
|
+
// Audio recording
|
|
32
|
+
mediaRecorder = null;
|
|
33
|
+
audioChunks = [];
|
|
34
|
+
stream = null;
|
|
35
|
+
// Event listeners
|
|
36
|
+
resultListeners = new Set();
|
|
37
|
+
errorListeners = new Set();
|
|
38
|
+
startListeners = new Set();
|
|
39
|
+
endListeners = new Set();
|
|
40
|
+
// Transformers.js v2 pipeline
|
|
41
|
+
transcriber = null;
|
|
42
|
+
// Promise that resolves when transcription is complete
|
|
43
|
+
processingPromise = null;
|
|
44
|
+
processingResolve = null;
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
this.options = {
|
|
47
|
+
type: 'whisper-wasm',
|
|
48
|
+
modelSize: 'tiny',
|
|
49
|
+
defaultLanguage: 'en',
|
|
50
|
+
...options,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
get initState() {
|
|
54
|
+
return this._initState;
|
|
55
|
+
}
|
|
56
|
+
async ensureInitialized(onProgress) {
|
|
57
|
+
if (this._initState === 'ready')
|
|
58
|
+
return;
|
|
59
|
+
if (this._initState === 'initializing') {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const check = () => {
|
|
62
|
+
if (this._initState === 'ready')
|
|
63
|
+
resolve();
|
|
64
|
+
else if (this._initState === 'error')
|
|
65
|
+
reject(new Error('Initialization failed'));
|
|
66
|
+
else
|
|
67
|
+
setTimeout(check, 50);
|
|
68
|
+
};
|
|
69
|
+
check();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
this._initState = 'initializing';
|
|
73
|
+
try {
|
|
74
|
+
// Check WASM support
|
|
75
|
+
if (typeof WebAssembly === 'undefined') {
|
|
76
|
+
throw new CapabilityNotAvailableError('WebAssembly', 'whisper-wasm');
|
|
77
|
+
}
|
|
78
|
+
const modelSize = this.options.modelSize || 'tiny';
|
|
79
|
+
const modelId = MODEL_IDS[modelSize] || MODEL_IDS.tiny;
|
|
80
|
+
const totalSize = MODEL_SIZES[modelSize] || MODEL_SIZES.tiny;
|
|
81
|
+
onProgress?.({
|
|
82
|
+
state: 'downloading',
|
|
83
|
+
bytesLoaded: 0,
|
|
84
|
+
bytesTotal: totalSize,
|
|
85
|
+
percent: 0,
|
|
86
|
+
currentFile: `${modelId}`,
|
|
87
|
+
});
|
|
88
|
+
// Dynamically import transformers.js v2
|
|
89
|
+
const transformers = await this.importTransformers();
|
|
90
|
+
const { pipeline } = transformers;
|
|
91
|
+
console.log('[Whisper v2] Loading pipeline...');
|
|
92
|
+
// Create ASR pipeline - v2 uses simpler API
|
|
93
|
+
this.transcriber = await pipeline('automatic-speech-recognition', modelId, {
|
|
94
|
+
progress_callback: (progress) => {
|
|
95
|
+
if (progress.status === 'progress' &&
|
|
96
|
+
progress.progress !== undefined) {
|
|
97
|
+
const percent = Math.round(progress.progress);
|
|
98
|
+
onProgress?.({
|
|
99
|
+
state: 'downloading',
|
|
100
|
+
bytesLoaded: Math.round((totalSize * percent) / 100),
|
|
101
|
+
bytesTotal: totalSize,
|
|
102
|
+
percent,
|
|
103
|
+
currentFile: progress.file || modelId,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
console.log('[Whisper v2] Pipeline loaded successfully');
|
|
109
|
+
onProgress?.({
|
|
110
|
+
state: 'complete',
|
|
111
|
+
bytesLoaded: totalSize,
|
|
112
|
+
bytesTotal: totalSize,
|
|
113
|
+
percent: 100,
|
|
114
|
+
});
|
|
115
|
+
this._initState = 'ready';
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
this._initState = 'error';
|
|
119
|
+
console.error('[Whisper v2] Initialization error:', error);
|
|
120
|
+
throw error instanceof Error
|
|
121
|
+
? error
|
|
122
|
+
: new InitializationError('whisper-wasm', String(error));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Store the transformers module for read_audio utility
|
|
126
|
+
transformersModule = null;
|
|
127
|
+
async importTransformers() {
|
|
128
|
+
try {
|
|
129
|
+
// Try v2 (@xenova/transformers) first
|
|
130
|
+
this.transformersModule = await importOptional('@xenova/transformers');
|
|
131
|
+
this.configureTransformersEnv(this.transformersModule);
|
|
132
|
+
return this.transformersModule;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
try {
|
|
136
|
+
// Fall back to v3 (@huggingface/transformers)
|
|
137
|
+
this.transformersModule = await importOptional('@huggingface/transformers');
|
|
138
|
+
this.configureTransformersEnv(this.transformersModule);
|
|
139
|
+
return this.transformersModule;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
throw new InitializationError('whisper-wasm', 'Neither @xenova/transformers nor @huggingface/transformers installed.');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Configure transformers.js environment settings
|
|
148
|
+
*/
|
|
149
|
+
configureTransformersEnv(transformers) {
|
|
150
|
+
if (transformers.env) {
|
|
151
|
+
// Default to remote models unless explicitly allowing local
|
|
152
|
+
const allowLocal = this.options.allowLocalModels ?? false;
|
|
153
|
+
transformers.env.allowLocalModels = allowLocal;
|
|
154
|
+
// Always use browser cache for better performance
|
|
155
|
+
transformers.env.useBrowserCache = true;
|
|
156
|
+
console.log('[Whisper v2] Configured env:', {
|
|
157
|
+
allowLocalModels: allowLocal,
|
|
158
|
+
useBrowserCache: true,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
getCapabilities() {
|
|
163
|
+
const modelSize = this.options.modelSize || 'tiny';
|
|
164
|
+
return {
|
|
165
|
+
continuous: false, // Whisper processes chunks, not continuous
|
|
166
|
+
interimResults: false, // No interim results in standard Whisper
|
|
167
|
+
languages: [
|
|
168
|
+
'en',
|
|
169
|
+
'es',
|
|
170
|
+
'fr',
|
|
171
|
+
'de',
|
|
172
|
+
'it',
|
|
173
|
+
'pt',
|
|
174
|
+
'nl',
|
|
175
|
+
'pl',
|
|
176
|
+
'ru',
|
|
177
|
+
'zh',
|
|
178
|
+
'ja',
|
|
179
|
+
'ko',
|
|
180
|
+
'ar',
|
|
181
|
+
'hi',
|
|
182
|
+
'tr',
|
|
183
|
+
'vi',
|
|
184
|
+
'th',
|
|
185
|
+
],
|
|
186
|
+
accuracy: modelSize === 'tiny' ? 'medium' : 'high',
|
|
187
|
+
requiresDownload: true,
|
|
188
|
+
downloadSize: MODEL_SIZES[modelSize] || MODEL_SIZES.tiny,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async start(_options = {}) {
|
|
192
|
+
await this.ensureInitialized();
|
|
193
|
+
if (this._isListening)
|
|
194
|
+
return;
|
|
195
|
+
try {
|
|
196
|
+
// Get microphone access
|
|
197
|
+
this.stream = await navigator.mediaDevices.getUserMedia({
|
|
198
|
+
audio: {
|
|
199
|
+
channelCount: 1,
|
|
200
|
+
sampleRate: 16000, // Whisper expects 16kHz
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
this.audioChunks = [];
|
|
204
|
+
// Create MediaRecorder
|
|
205
|
+
this.mediaRecorder = new MediaRecorder(this.stream, {
|
|
206
|
+
mimeType: this.getSupportedMimeType(),
|
|
207
|
+
});
|
|
208
|
+
this.mediaRecorder.ondataavailable = (event) => {
|
|
209
|
+
if (event.data.size > 0) {
|
|
210
|
+
this.audioChunks.push(event.data);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
this.mediaRecorder.onstop = async () => {
|
|
214
|
+
await this.processRecording();
|
|
215
|
+
this.processingResolve?.();
|
|
216
|
+
};
|
|
217
|
+
// Create promise that resolves when transcription is done
|
|
218
|
+
this.processingPromise = new Promise((resolve) => {
|
|
219
|
+
this.processingResolve = resolve;
|
|
220
|
+
});
|
|
221
|
+
// Start with timeslice to capture audio in chunks (more reliable)
|
|
222
|
+
this.mediaRecorder.start(500);
|
|
223
|
+
this._isListening = true;
|
|
224
|
+
for (const cb of this.startListeners) {
|
|
225
|
+
cb();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
230
|
+
for (const cb of this.errorListeners) {
|
|
231
|
+
cb(err);
|
|
232
|
+
}
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
getSupportedMimeType() {
|
|
237
|
+
const types = [
|
|
238
|
+
'audio/webm;codecs=opus',
|
|
239
|
+
'audio/webm',
|
|
240
|
+
'audio/ogg;codecs=opus',
|
|
241
|
+
'audio/mp4',
|
|
242
|
+
];
|
|
243
|
+
for (const type of types) {
|
|
244
|
+
if (MediaRecorder.isTypeSupported(type)) {
|
|
245
|
+
return type;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return 'audio/webm'; // Fallback
|
|
249
|
+
}
|
|
250
|
+
async processRecording() {
|
|
251
|
+
console.log('[Whisper v2] processRecording called, chunks:', this.audioChunks.length);
|
|
252
|
+
if (this.audioChunks.length === 0) {
|
|
253
|
+
console.log('[Whisper v2] No audio chunks captured');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// Use the actual MIME type from the recorder
|
|
257
|
+
const mimeType = this.mediaRecorder?.mimeType || 'audio/webm';
|
|
258
|
+
const audioBlob = new Blob(this.audioChunks, { type: mimeType });
|
|
259
|
+
console.log('[Whisper v2] Audio blob size:', audioBlob.size, 'type:', mimeType);
|
|
260
|
+
try {
|
|
261
|
+
if (!this.transcriber || !this.transformersModule) {
|
|
262
|
+
throw new Error('Whisper not initialized');
|
|
263
|
+
}
|
|
264
|
+
// Decode audio using Web Audio API for better format support
|
|
265
|
+
console.log('[Whisper v2] Decoding audio with Web Audio API...');
|
|
266
|
+
const arrayBuffer = await audioBlob.arrayBuffer();
|
|
267
|
+
const audioContext = new AudioContext({ sampleRate: 16000 });
|
|
268
|
+
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
269
|
+
// Get mono audio data
|
|
270
|
+
const audioData = audioBuffer.getChannelData(0);
|
|
271
|
+
console.log('[Whisper v2] Audio decoded - length:', audioData.length, 'samples, duration:', audioBuffer.duration.toFixed(2), 's');
|
|
272
|
+
// Debug: check audio levels
|
|
273
|
+
let maxLevel = 0;
|
|
274
|
+
let sumSquares = 0;
|
|
275
|
+
for (let i = 0; i < audioData.length; i++) {
|
|
276
|
+
const abs = Math.abs(audioData[i]);
|
|
277
|
+
if (abs > maxLevel)
|
|
278
|
+
maxLevel = abs;
|
|
279
|
+
sumSquares += audioData[i] * audioData[i];
|
|
280
|
+
}
|
|
281
|
+
const rms = Math.sqrt(sumSquares / audioData.length);
|
|
282
|
+
console.log('[Whisper v2] Audio levels - max:', maxLevel.toFixed(4), 'RMS:', rms.toFixed(4));
|
|
283
|
+
if (maxLevel < 0.01) {
|
|
284
|
+
console.warn('[Whisper v2] Audio appears to be silent or very quiet!');
|
|
285
|
+
}
|
|
286
|
+
// Close audio context
|
|
287
|
+
await audioContext.close();
|
|
288
|
+
console.log('[Whisper v2] Starting transcription...');
|
|
289
|
+
const startTime = performance.now();
|
|
290
|
+
// Pass the Float32Array audio data
|
|
291
|
+
const result = await this.transcriber(audioData);
|
|
292
|
+
const elapsedTime = performance.now() - startTime;
|
|
293
|
+
console.log('[Whisper v2] Transcription completed in', Math.round(elapsedTime), 'ms');
|
|
294
|
+
console.log('[Whisper v2] Result:', result);
|
|
295
|
+
const text = result.text?.trim() || '';
|
|
296
|
+
console.log('[Whisper v2] Transcription result:', text);
|
|
297
|
+
const sttResult = {
|
|
298
|
+
text,
|
|
299
|
+
confidence: 0.95,
|
|
300
|
+
isFinal: true,
|
|
301
|
+
timestamp: Date.now(),
|
|
302
|
+
};
|
|
303
|
+
for (const cb of this.resultListeners) {
|
|
304
|
+
cb(sttResult);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
console.error('[Whisper v2] Processing error:', error);
|
|
309
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
310
|
+
for (const cb of this.errorListeners) {
|
|
311
|
+
cb(err);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async stop() {
|
|
316
|
+
if (this.mediaRecorder && this._isListening) {
|
|
317
|
+
this.mediaRecorder.stop();
|
|
318
|
+
if (this.stream) {
|
|
319
|
+
for (const track of this.stream.getTracks()) {
|
|
320
|
+
track.stop();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
this.stream = null;
|
|
324
|
+
if (this.processingPromise) {
|
|
325
|
+
await this.processingPromise;
|
|
326
|
+
this.processingPromise = null;
|
|
327
|
+
this.processingResolve = null;
|
|
328
|
+
}
|
|
329
|
+
this._isListening = false;
|
|
330
|
+
for (const cb of this.endListeners) {
|
|
331
|
+
cb();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
abort() {
|
|
336
|
+
if (this.mediaRecorder && this._isListening) {
|
|
337
|
+
this.mediaRecorder.stop();
|
|
338
|
+
this._isListening = false;
|
|
339
|
+
this.audioChunks = [];
|
|
340
|
+
if (this.stream) {
|
|
341
|
+
for (const track of this.stream.getTracks()) {
|
|
342
|
+
track.stop();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
this.stream = null;
|
|
346
|
+
for (const cb of this.endListeners) {
|
|
347
|
+
cb();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
isListening() {
|
|
352
|
+
return this._isListening;
|
|
353
|
+
}
|
|
354
|
+
onResult(callback) {
|
|
355
|
+
this.resultListeners.add(callback);
|
|
356
|
+
return () => this.resultListeners.delete(callback);
|
|
357
|
+
}
|
|
358
|
+
onError(callback) {
|
|
359
|
+
this.errorListeners.add(callback);
|
|
360
|
+
return () => this.errorListeners.delete(callback);
|
|
361
|
+
}
|
|
362
|
+
onStart(callback) {
|
|
363
|
+
this.startListeners.add(callback);
|
|
364
|
+
return () => this.startListeners.delete(callback);
|
|
365
|
+
}
|
|
366
|
+
onEnd(callback) {
|
|
367
|
+
this.endListeners.add(callback);
|
|
368
|
+
return () => this.endListeners.delete(callback);
|
|
369
|
+
}
|
|
370
|
+
async dispose() {
|
|
371
|
+
this.abort();
|
|
372
|
+
this.transcriber = null;
|
|
373
|
+
this.transformersModule = null;
|
|
374
|
+
this.resultListeners.clear();
|
|
375
|
+
this.errorListeners.clear();
|
|
376
|
+
this.startListeners.clear();
|
|
377
|
+
this.endListeners.clear();
|
|
378
|
+
this._initState = 'uninitialized';
|
|
379
|
+
}
|
|
380
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Speech Synthesis API adapter
|
|
3
|
+
*
|
|
4
|
+
* Uses the Web Speech API (SpeechSynthesis) available in most modern browsers.
|
|
5
|
+
* No download required, works immediately.
|
|
6
|
+
*/
|
|
7
|
+
import type { InitState, OnProgress } from '../../core/types.js';
|
|
8
|
+
import type { BrowserSynthesisTTSOptions, TTSAdapter, TTSCapabilities, TTSOptions, TTSVoice } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Browser Speech Synthesis adapter
|
|
11
|
+
*/
|
|
12
|
+
export declare class BrowserSynthesisTTSAdapter implements TTSAdapter {
|
|
13
|
+
readonly type: "browser-synthesis";
|
|
14
|
+
private _initState;
|
|
15
|
+
private synthesis;
|
|
16
|
+
private options;
|
|
17
|
+
private voices;
|
|
18
|
+
private currentUtterance;
|
|
19
|
+
private startListeners;
|
|
20
|
+
private endListeners;
|
|
21
|
+
private errorListeners;
|
|
22
|
+
private boundaryListeners;
|
|
23
|
+
constructor(options?: BrowserSynthesisTTSOptions);
|
|
24
|
+
get initState(): InitState;
|
|
25
|
+
ensureInitialized(_onProgress?: OnProgress): Promise<void>;
|
|
26
|
+
private loadVoices;
|
|
27
|
+
getCapabilities(): TTSCapabilities;
|
|
28
|
+
getVoices(): TTSVoice[];
|
|
29
|
+
private findVoice;
|
|
30
|
+
speak(text: string, options?: TTSOptions): Promise<void>;
|
|
31
|
+
stop(): void;
|
|
32
|
+
pause(): void;
|
|
33
|
+
resume(): void;
|
|
34
|
+
isSpeaking(): boolean;
|
|
35
|
+
isPaused(): boolean;
|
|
36
|
+
onStart(callback: () => void): () => void;
|
|
37
|
+
onEnd(callback: () => void): () => void;
|
|
38
|
+
onError(callback: (error: Error) => void): () => void;
|
|
39
|
+
onBoundary(callback: (charIndex: number, charLength: number) => void): () => void;
|
|
40
|
+
dispose(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=browser-synthesis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-synthesis.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/tts/browser-synthesis.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EACV,0BAA0B,EAC1B,UAAU,EACV,eAAe,EACf,UAAU,EACV,QAAQ,EACT,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,0BAA2B,YAAW,UAAU;IAC3D,QAAQ,CAAC,IAAI,EAAG,mBAAmB,CAAU;IAE7C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,SAAS,CAAgC;IACjD,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAA8B;IAE5C,OAAO,CAAC,gBAAgB,CAAyC;IAGjE,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,iBAAiB,CAErB;gBAEQ,OAAO,GAAE,0BAA+B;IAUpD,IAAI,SAAS,IAAI,SAAS,CAEzB;IAEK,iBAAiB,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;YAqClD,UAAU;IA+BxB,eAAe,IAAI,eAAe;IAUlC,SAAS,IAAI,QAAQ,EAAE;IAUvB,OAAO,CAAC,SAAS;IAkCX,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAiElE,IAAI,IAAI,IAAI;IAOZ,KAAK,IAAI,IAAI;IAMb,MAAM,IAAI,IAAI;IAMd,UAAU,IAAI,OAAO;IAIrB,QAAQ,IAAI,OAAO;IAInB,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKzC,KAAK,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKvC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI;IAKrD,UAAU,CACR,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GACxD,MAAM,IAAI;IAKP,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAU/B"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Speech Synthesis API adapter
|
|
3
|
+
*
|
|
4
|
+
* Uses the Web Speech API (SpeechSynthesis) available in most modern browsers.
|
|
5
|
+
* No download required, works immediately.
|
|
6
|
+
*/
|
|
7
|
+
import { CapabilityNotAvailableError } from '../../core/errors.js';
|
|
8
|
+
/**
|
|
9
|
+
* Browser Speech Synthesis adapter
|
|
10
|
+
*/
|
|
11
|
+
export class BrowserSynthesisTTSAdapter {
|
|
12
|
+
type = 'browser-synthesis';
|
|
13
|
+
_initState = 'uninitialized';
|
|
14
|
+
synthesis = null;
|
|
15
|
+
options;
|
|
16
|
+
voices = [];
|
|
17
|
+
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: Used in speak/stop/pause
|
|
18
|
+
currentUtterance = null;
|
|
19
|
+
// Event listeners
|
|
20
|
+
startListeners = new Set();
|
|
21
|
+
endListeners = new Set();
|
|
22
|
+
errorListeners = new Set();
|
|
23
|
+
boundaryListeners = new Set();
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.options = {
|
|
26
|
+
defaultLanguage: 'en-US',
|
|
27
|
+
defaultRate: 1,
|
|
28
|
+
defaultPitch: 1,
|
|
29
|
+
defaultVolume: 1,
|
|
30
|
+
...options,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
get initState() {
|
|
34
|
+
return this._initState;
|
|
35
|
+
}
|
|
36
|
+
async ensureInitialized(_onProgress) {
|
|
37
|
+
if (this._initState === 'ready')
|
|
38
|
+
return;
|
|
39
|
+
if (this._initState === 'initializing') {
|
|
40
|
+
// Wait for existing initialization
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const check = () => {
|
|
43
|
+
if (this._initState === 'ready')
|
|
44
|
+
resolve();
|
|
45
|
+
else if (this._initState === 'error')
|
|
46
|
+
reject(new Error('Initialization failed'));
|
|
47
|
+
else
|
|
48
|
+
setTimeout(check, 50);
|
|
49
|
+
};
|
|
50
|
+
check();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
this._initState = 'initializing';
|
|
54
|
+
try {
|
|
55
|
+
if (typeof window === 'undefined' || !window.speechSynthesis) {
|
|
56
|
+
throw new CapabilityNotAvailableError('Web Speech Synthesis API', 'browser-synthesis');
|
|
57
|
+
}
|
|
58
|
+
this.synthesis = window.speechSynthesis;
|
|
59
|
+
// Load voices - they may load asynchronously
|
|
60
|
+
await this.loadVoices();
|
|
61
|
+
this._initState = 'ready';
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
this._initState = 'error';
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async loadVoices() {
|
|
69
|
+
if (!this.synthesis)
|
|
70
|
+
return;
|
|
71
|
+
// Get voices - may be empty initially in some browsers
|
|
72
|
+
this.voices = this.synthesis.getVoices();
|
|
73
|
+
if (this.voices.length === 0) {
|
|
74
|
+
// Wait for voices to load
|
|
75
|
+
await new Promise((resolve) => {
|
|
76
|
+
const handleVoicesChanged = () => {
|
|
77
|
+
this.voices = this.synthesis?.getVoices() ?? [];
|
|
78
|
+
if (this.voices.length > 0) {
|
|
79
|
+
this.synthesis?.removeEventListener('voiceschanged', handleVoicesChanged);
|
|
80
|
+
resolve();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
this.synthesis?.addEventListener('voiceschanged', handleVoicesChanged);
|
|
84
|
+
// Timeout after 2 seconds - some browsers may not fire the event
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
this.voices = this.synthesis?.getVoices() ?? [];
|
|
87
|
+
resolve();
|
|
88
|
+
}, 2000);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
getCapabilities() {
|
|
93
|
+
return {
|
|
94
|
+
voices: this.getVoices(),
|
|
95
|
+
ssml: false, // Browser API doesn't support SSML
|
|
96
|
+
rateRange: { min: 0.1, max: 10 },
|
|
97
|
+
pitchRange: { min: 0, max: 2 },
|
|
98
|
+
requiresDownload: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
getVoices() {
|
|
102
|
+
return this.voices.map((voice) => ({
|
|
103
|
+
id: voice.voiceURI,
|
|
104
|
+
name: voice.name,
|
|
105
|
+
language: voice.lang,
|
|
106
|
+
local: voice.localService,
|
|
107
|
+
default: voice.default,
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
findVoice(voiceNameOrId, language) {
|
|
111
|
+
if (!voiceNameOrId && !language) {
|
|
112
|
+
// Use default voice
|
|
113
|
+
return this.voices.find((v) => v.default) || this.voices[0];
|
|
114
|
+
}
|
|
115
|
+
if (voiceNameOrId) {
|
|
116
|
+
// Try to find by name or URI
|
|
117
|
+
const voice = this.voices.find((v) => v.name === voiceNameOrId || v.voiceURI === voiceNameOrId);
|
|
118
|
+
if (voice)
|
|
119
|
+
return voice;
|
|
120
|
+
}
|
|
121
|
+
if (language) {
|
|
122
|
+
// Find voice matching language
|
|
123
|
+
const exactMatch = this.voices.find((v) => v.lang === language);
|
|
124
|
+
if (exactMatch)
|
|
125
|
+
return exactMatch;
|
|
126
|
+
// Try language prefix match (e.g., 'en' matches 'en-US')
|
|
127
|
+
const langPrefix = language.split('-')[0];
|
|
128
|
+
const prefixMatch = this.voices.find((v) => v.lang.startsWith(langPrefix));
|
|
129
|
+
if (prefixMatch)
|
|
130
|
+
return prefixMatch;
|
|
131
|
+
}
|
|
132
|
+
// Fallback to default
|
|
133
|
+
return this.voices.find((v) => v.default) || this.voices[0];
|
|
134
|
+
}
|
|
135
|
+
async speak(text, options = {}) {
|
|
136
|
+
await this.ensureInitialized();
|
|
137
|
+
if (!this.synthesis) {
|
|
138
|
+
throw new Error('Synthesis not initialized');
|
|
139
|
+
}
|
|
140
|
+
// Cancel any current speech
|
|
141
|
+
this.stop();
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
144
|
+
// Set voice
|
|
145
|
+
const voice = this.findVoice(options.voice || this.options.defaultVoice, options.language || this.options.defaultLanguage);
|
|
146
|
+
if (voice) {
|
|
147
|
+
utterance.voice = voice;
|
|
148
|
+
utterance.lang = voice.lang;
|
|
149
|
+
}
|
|
150
|
+
else if (options.language) {
|
|
151
|
+
utterance.lang = options.language;
|
|
152
|
+
}
|
|
153
|
+
// Set parameters
|
|
154
|
+
utterance.rate = options.rate ?? this.options.defaultRate ?? 1;
|
|
155
|
+
utterance.pitch = options.pitch ?? this.options.defaultPitch ?? 1;
|
|
156
|
+
utterance.volume = options.volume ?? this.options.defaultVolume ?? 1;
|
|
157
|
+
// Set up event handlers
|
|
158
|
+
utterance.onstart = () => {
|
|
159
|
+
for (const cb of this.startListeners) {
|
|
160
|
+
cb();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
utterance.onend = () => {
|
|
164
|
+
this.currentUtterance = null;
|
|
165
|
+
for (const cb of this.endListeners) {
|
|
166
|
+
cb();
|
|
167
|
+
}
|
|
168
|
+
resolve();
|
|
169
|
+
};
|
|
170
|
+
utterance.onerror = (event) => {
|
|
171
|
+
this.currentUtterance = null;
|
|
172
|
+
const error = new Error(`Speech synthesis error: ${event.error}`);
|
|
173
|
+
for (const cb of this.errorListeners) {
|
|
174
|
+
cb(error);
|
|
175
|
+
}
|
|
176
|
+
reject(error);
|
|
177
|
+
};
|
|
178
|
+
utterance.onboundary = (event) => {
|
|
179
|
+
for (const cb of this.boundaryListeners) {
|
|
180
|
+
cb(event.charIndex, event.charLength || 1);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
this.currentUtterance = utterance;
|
|
184
|
+
this.synthesis?.speak(utterance);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
stop() {
|
|
188
|
+
if (this.synthesis) {
|
|
189
|
+
this.synthesis.cancel();
|
|
190
|
+
this.currentUtterance = null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
pause() {
|
|
194
|
+
if (this.synthesis) {
|
|
195
|
+
this.synthesis.pause();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
resume() {
|
|
199
|
+
if (this.synthesis) {
|
|
200
|
+
this.synthesis.resume();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
isSpeaking() {
|
|
204
|
+
return this.synthesis?.speaking ?? false;
|
|
205
|
+
}
|
|
206
|
+
isPaused() {
|
|
207
|
+
return this.synthesis?.paused ?? false;
|
|
208
|
+
}
|
|
209
|
+
onStart(callback) {
|
|
210
|
+
this.startListeners.add(callback);
|
|
211
|
+
return () => this.startListeners.delete(callback);
|
|
212
|
+
}
|
|
213
|
+
onEnd(callback) {
|
|
214
|
+
this.endListeners.add(callback);
|
|
215
|
+
return () => this.endListeners.delete(callback);
|
|
216
|
+
}
|
|
217
|
+
onError(callback) {
|
|
218
|
+
this.errorListeners.add(callback);
|
|
219
|
+
return () => this.errorListeners.delete(callback);
|
|
220
|
+
}
|
|
221
|
+
onBoundary(callback) {
|
|
222
|
+
this.boundaryListeners.add(callback);
|
|
223
|
+
return () => this.boundaryListeners.delete(callback);
|
|
224
|
+
}
|
|
225
|
+
async dispose() {
|
|
226
|
+
this.stop();
|
|
227
|
+
this.synthesis = null;
|
|
228
|
+
this.voices = [];
|
|
229
|
+
this.startListeners.clear();
|
|
230
|
+
this.endListeners.clear();
|
|
231
|
+
this.errorListeners.clear();
|
|
232
|
+
this.boundaryListeners.clear();
|
|
233
|
+
this._initState = 'uninitialized';
|
|
234
|
+
}
|
|
235
|
+
}
|