@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.
Files changed (357) hide show
  1. package/AGENTS.md +317 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +185 -0
  5. package/dist/Provider.svelte +204 -0
  6. package/dist/Provider.svelte.d.ts +73 -0
  7. package/dist/Provider.svelte.d.ts.map +1 -0
  8. package/dist/__tests__/app-state.test.js +156 -0
  9. package/dist/__tests__/warm-clients.test.js +186 -0
  10. package/dist/browser-ai/adapters/llm/factory.d.ts +38 -0
  11. package/dist/browser-ai/adapters/llm/factory.d.ts.map +1 -0
  12. package/dist/browser-ai/adapters/llm/factory.js +91 -0
  13. package/dist/browser-ai/adapters/llm/index.d.ts +7 -0
  14. package/dist/browser-ai/adapters/llm/index.d.ts.map +1 -0
  15. package/dist/browser-ai/adapters/llm/index.js +6 -0
  16. package/dist/browser-ai/adapters/llm/types.d.ts +182 -0
  17. package/dist/browser-ai/adapters/llm/types.d.ts.map +1 -0
  18. package/dist/browser-ai/adapters/llm/types.js +43 -0
  19. package/dist/browser-ai/adapters/llm/webllm.d.ts +33 -0
  20. package/dist/browser-ai/adapters/llm/webllm.d.ts.map +1 -0
  21. package/dist/browser-ai/adapters/llm/webllm.js +225 -0
  22. package/dist/browser-ai/adapters/stt/browser-speech.d.ts +31 -0
  23. package/dist/browser-ai/adapters/stt/browser-speech.d.ts.map +1 -0
  24. package/dist/browser-ai/adapters/stt/browser-speech.js +217 -0
  25. package/dist/browser-ai/adapters/stt/factory.d.ts +49 -0
  26. package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -0
  27. package/dist/browser-ai/adapters/stt/factory.js +110 -0
  28. package/dist/browser-ai/adapters/stt/index.d.ts +9 -0
  29. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -0
  30. package/dist/browser-ai/adapters/stt/index.js +8 -0
  31. package/dist/browser-ai/adapters/stt/types.d.ts +154 -0
  32. package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -0
  33. package/dist/browser-ai/adapters/stt/types.js +4 -0
  34. package/dist/browser-ai/adapters/stt/whisper-cpp.d.ts +46 -0
  35. package/dist/browser-ai/adapters/stt/whisper-cpp.d.ts.map +1 -0
  36. package/dist/browser-ai/adapters/stt/whisper-cpp.js +348 -0
  37. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +51 -0
  38. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +1 -0
  39. package/dist/browser-ai/adapters/stt/whisper-wasm.js +380 -0
  40. package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts +42 -0
  41. package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts.map +1 -0
  42. package/dist/browser-ai/adapters/tts/browser-synthesis.js +235 -0
  43. package/dist/browser-ai/adapters/tts/factory.d.ts +53 -0
  44. package/dist/browser-ai/adapters/tts/factory.d.ts.map +1 -0
  45. package/dist/browser-ai/adapters/tts/factory.js +92 -0
  46. package/dist/browser-ai/adapters/tts/index.d.ts +7 -0
  47. package/dist/browser-ai/adapters/tts/index.d.ts.map +1 -0
  48. package/dist/browser-ai/adapters/tts/index.js +6 -0
  49. package/dist/browser-ai/adapters/tts/types.d.ts +140 -0
  50. package/dist/browser-ai/adapters/tts/types.d.ts.map +1 -0
  51. package/dist/browser-ai/adapters/tts/types.js +4 -0
  52. package/dist/browser-ai/capabilities/detector.d.ts +38 -0
  53. package/dist/browser-ai/capabilities/detector.d.ts.map +1 -0
  54. package/dist/browser-ai/capabilities/detector.js +211 -0
  55. package/dist/browser-ai/core/errors.d.ts +62 -0
  56. package/dist/browser-ai/core/errors.d.ts.map +1 -0
  57. package/dist/browser-ai/core/errors.js +92 -0
  58. package/dist/browser-ai/core/index.d.ts +6 -0
  59. package/dist/browser-ai/core/index.d.ts.map +1 -0
  60. package/dist/browser-ai/core/index.js +5 -0
  61. package/dist/browser-ai/core/types.d.ts +115 -0
  62. package/dist/browser-ai/core/types.d.ts.map +1 -0
  63. package/dist/browser-ai/core/types.js +34 -0
  64. package/dist/browser-ai/index.d.ts +12 -0
  65. package/dist/browser-ai/index.d.ts.map +1 -0
  66. package/dist/browser-ai/index.js +16 -0
  67. package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte +77 -0
  68. package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte.d.ts +16 -0
  69. package/dist/browser-ai/svelte/components/AILoadingOverlay.svelte.d.ts.map +1 -0
  70. package/dist/browser-ai/svelte/components/CapabilityGate.svelte +57 -0
  71. package/dist/browser-ai/svelte/components/CapabilityGate.svelte.d.ts +15 -0
  72. package/dist/browser-ai/svelte/components/CapabilityGate.svelte.d.ts.map +1 -0
  73. package/dist/browser-ai/svelte/components/DownloadProgress.svelte +141 -0
  74. package/dist/browser-ai/svelte/components/DownloadProgress.svelte.d.ts +15 -0
  75. package/dist/browser-ai/svelte/components/DownloadProgress.svelte.d.ts.map +1 -0
  76. package/dist/browser-ai/svelte/components/STTTest.svelte +379 -0
  77. package/dist/browser-ai/svelte/components/STTTest.svelte.d.ts +9 -0
  78. package/dist/browser-ai/svelte/components/STTTest.svelte.d.ts.map +1 -0
  79. package/dist/browser-ai/svelte/components/VoiceInput.svelte +200 -0
  80. package/dist/browser-ai/svelte/components/VoiceInput.svelte.d.ts +16 -0
  81. package/dist/browser-ai/svelte/components/VoiceInput.svelte.d.ts.map +1 -0
  82. package/dist/browser-ai/svelte/index.d.ts +15 -0
  83. package/dist/browser-ai/svelte/index.d.ts.map +1 -0
  84. package/dist/browser-ai/svelte/index.js +28 -0
  85. package/dist/browser-ai/ui.d.ts +16 -0
  86. package/dist/browser-ai/ui.d.ts.map +1 -0
  87. package/dist/browser-ai/ui.js +67 -0
  88. package/dist/components/admin/AgentAdminPanel.svelte +111 -0
  89. package/dist/components/admin/AgentAdminPanel.svelte.d.ts +25 -0
  90. package/dist/components/admin/AgentAdminPanel.svelte.d.ts.map +1 -0
  91. package/dist/components/admin/AgentAdminTabs.svelte +280 -0
  92. package/dist/components/admin/AgentAdminTabs.svelte.d.ts +23 -0
  93. package/dist/components/admin/AgentAdminTabs.svelte.d.ts.map +1 -0
  94. package/dist/components/admin/AgentSettingsShell.svelte +257 -0
  95. package/dist/components/admin/AgentSettingsShell.svelte.d.ts +33 -0
  96. package/dist/components/admin/AgentSettingsShell.svelte.d.ts.map +1 -0
  97. package/dist/components/admin/index.d.ts +5 -0
  98. package/dist/components/admin/index.d.ts.map +1 -0
  99. package/dist/components/admin/index.js +6 -0
  100. package/dist/components/forms/AddressInput.svelte +500 -0
  101. package/dist/components/forms/AddressInput.svelte.d.ts +36 -0
  102. package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -0
  103. package/dist/components/forms/CheckboxInput.svelte +208 -0
  104. package/dist/components/forms/CheckboxInput.svelte.d.ts +20 -0
  105. package/dist/components/forms/CheckboxInput.svelte.d.ts.map +1 -0
  106. package/dist/components/forms/DateRangeInput.svelte +628 -0
  107. package/dist/components/forms/DateRangeInput.svelte.d.ts +33 -0
  108. package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -0
  109. package/dist/components/forms/DateTimeInput.svelte +521 -0
  110. package/dist/components/forms/DateTimeInput.svelte.d.ts +24 -0
  111. package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -0
  112. package/dist/components/forms/FileUpload.svelte +358 -0
  113. package/dist/components/forms/FileUpload.svelte.d.ts +22 -0
  114. package/dist/components/forms/FileUpload.svelte.d.ts.map +1 -0
  115. package/dist/components/forms/Form.svelte +771 -0
  116. package/dist/components/forms/Form.svelte.d.ts +26 -0
  117. package/dist/components/forms/Form.svelte.d.ts.map +1 -0
  118. package/dist/components/forms/FormGroup.svelte +86 -0
  119. package/dist/components/forms/FormGroup.svelte.d.ts +13 -0
  120. package/dist/components/forms/FormGroup.svelte.d.ts.map +1 -0
  121. package/dist/components/forms/FormMicButton.svelte +179 -0
  122. package/dist/components/forms/FormMicButton.svelte.d.ts +10 -0
  123. package/dist/components/forms/FormMicButton.svelte.d.ts.map +1 -0
  124. package/dist/components/forms/Input.svelte +83 -0
  125. package/dist/components/forms/Input.svelte.d.ts +9 -0
  126. package/dist/components/forms/Input.svelte.d.ts.map +1 -0
  127. package/dist/components/forms/MeasurementInput.svelte +505 -0
  128. package/dist/components/forms/MeasurementInput.svelte.d.ts +35 -0
  129. package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -0
  130. package/dist/components/forms/MoneyInput.svelte +412 -0
  131. package/dist/components/forms/MoneyInput.svelte.d.ts +30 -0
  132. package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -0
  133. package/dist/components/forms/NumberInput.svelte +310 -0
  134. package/dist/components/forms/NumberInput.svelte.d.ts +28 -0
  135. package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -0
  136. package/dist/components/forms/PhoneInput.svelte +530 -0
  137. package/dist/components/forms/PhoneInput.svelte.d.ts +22 -0
  138. package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -0
  139. package/dist/components/forms/SearchInput.svelte +358 -0
  140. package/dist/components/forms/SearchInput.svelte.d.ts +33 -0
  141. package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
  142. package/dist/components/forms/Select.svelte +83 -0
  143. package/dist/components/forms/Select.svelte.d.ts +11 -0
  144. package/dist/components/forms/Select.svelte.d.ts.map +1 -0
  145. package/dist/components/forms/SelectInput.svelte +254 -0
  146. package/dist/components/forms/SelectInput.svelte.d.ts +25 -0
  147. package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -0
  148. package/dist/components/forms/TextInput.svelte +415 -0
  149. package/dist/components/forms/TextInput.svelte.d.ts +26 -0
  150. package/dist/components/forms/TextInput.svelte.d.ts.map +1 -0
  151. package/dist/components/forms/Textarea.svelte +85 -0
  152. package/dist/components/forms/Textarea.svelte.d.ts +10 -0
  153. package/dist/components/forms/Textarea.svelte.d.ts.map +1 -0
  154. package/dist/components/forms/TextareaInput.svelte +386 -0
  155. package/dist/components/forms/TextareaInput.svelte.d.ts +26 -0
  156. package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -0
  157. package/dist/components/forms/Toggle.svelte +217 -0
  158. package/dist/components/forms/Toggle.svelte.d.ts +37 -0
  159. package/dist/components/forms/Toggle.svelte.d.ts.map +1 -0
  160. package/dist/components/forms/__tests__/AddressInput.behavior.test.js +122 -0
  161. package/dist/components/forms/__tests__/CheckboxInput.test.js +92 -0
  162. package/dist/components/forms/__tests__/DateRangeInput.behavior.test.js +135 -0
  163. package/dist/components/forms/__tests__/DateTimeInput.behavior.test.js +103 -0
  164. package/dist/components/forms/__tests__/FileUpload.test.js +90 -0
  165. package/dist/components/forms/__tests__/Form.behavior.test.js +137 -0
  166. package/dist/components/forms/__tests__/Form.test.js +58 -0
  167. package/dist/components/forms/__tests__/FormGroup.test.js +48 -0
  168. package/dist/components/forms/__tests__/FormMicButton.test.js +86 -0
  169. package/dist/components/forms/__tests__/Input.test.js +49 -0
  170. package/dist/components/forms/__tests__/MeasurementInput.behavior.test.js +129 -0
  171. package/dist/components/forms/__tests__/MoneyInput.behavior.test.js +124 -0
  172. package/dist/components/forms/__tests__/NumberInput.behavior.test.js +141 -0
  173. package/dist/components/forms/__tests__/PhoneInput.behavior.test.js +96 -0
  174. package/dist/components/forms/__tests__/SearchInput.test.js +79 -0
  175. package/dist/components/forms/__tests__/Select.test.js +37 -0
  176. package/dist/components/forms/__tests__/SelectInput.behavior.test.js +132 -0
  177. package/dist/components/forms/__tests__/TextInput.behavior.test.js +131 -0
  178. package/dist/components/forms/__tests__/Textarea.test.js +39 -0
  179. package/dist/components/forms/__tests__/TextareaInput.behavior.test.js +96 -0
  180. package/dist/components/forms/__tests__/Toggle.test.js +87 -0
  181. package/dist/components/forms/__tests__/composite-inputs-a11y.test.js +69 -0
  182. package/dist/components/forms/__tests__/form-group-input.fixture.svelte +16 -0
  183. package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts +9 -0
  184. package/dist/components/forms/__tests__/form-group-input.fixture.svelte.d.ts.map +1 -0
  185. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte +33 -0
  186. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts +12 -0
  187. package/dist/components/forms/__tests__/form-with-fields.fixture.svelte.d.ts.map +1 -0
  188. package/dist/components/forms/__tests__/rich-inputs-a11y.test.js +87 -0
  189. package/dist/components/forms/index.d.ts +25 -0
  190. package/dist/components/forms/index.d.ts.map +1 -0
  191. package/dist/components/forms/index.js +25 -0
  192. package/dist/components/forms/types.d.ts +33 -0
  193. package/dist/components/forms/types.d.ts.map +1 -0
  194. package/dist/components/forms/types.js +4 -0
  195. package/dist/components/module/ModulePanel.svelte +134 -0
  196. package/dist/components/module/ModulePanel.svelte.d.ts +22 -0
  197. package/dist/components/module/ModulePanel.svelte.d.ts.map +1 -0
  198. package/dist/components/module/index.d.ts +5 -0
  199. package/dist/components/module/index.d.ts.map +1 -0
  200. package/dist/components/module/index.js +4 -0
  201. package/dist/components/workspace/Breadcrumbs.svelte +141 -0
  202. package/dist/components/workspace/Breadcrumbs.svelte.d.ts +21 -0
  203. package/dist/components/workspace/Breadcrumbs.svelte.d.ts.map +1 -0
  204. package/dist/components/workspace/NavTree.svelte +354 -0
  205. package/dist/components/workspace/NavTree.svelte.d.ts +45 -0
  206. package/dist/components/workspace/NavTree.svelte.d.ts.map +1 -0
  207. package/dist/components/workspace/README.md +34 -0
  208. package/dist/components/workspace/RoleShell.svelte +309 -0
  209. package/dist/components/workspace/RoleShell.svelte.d.ts +91 -0
  210. package/dist/components/workspace/RoleShell.svelte.d.ts.map +1 -0
  211. package/dist/components/workspace/WorkspaceShell.svelte +951 -0
  212. package/dist/components/workspace/WorkspaceShell.svelte.d.ts +112 -0
  213. package/dist/components/workspace/WorkspaceShell.svelte.d.ts.map +1 -0
  214. package/dist/components/workspace/__tests__/RoleShell.test.js +772 -0
  215. package/dist/components/workspace/__tests__/WorkspaceShell.test.js +630 -0
  216. package/dist/components/workspace/__tests__/breadcrumbs-helpers.test.js +141 -0
  217. package/dist/components/workspace/__tests__/context-forwarding-harness.svelte +45 -0
  218. package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts +21 -0
  219. package/dist/components/workspace/__tests__/context-forwarding-harness.svelte.d.ts.map +1 -0
  220. package/dist/components/workspace/__tests__/define-tools-dock.test.js +1010 -0
  221. package/dist/components/workspace/__tests__/harness.svelte +25 -0
  222. package/dist/components/workspace/__tests__/harness.svelte.d.ts +14 -0
  223. package/dist/components/workspace/__tests__/harness.svelte.d.ts.map +1 -0
  224. package/dist/components/workspace/__tests__/index.test.js +37 -0
  225. package/dist/components/workspace/__tests__/manifest-nav-helpers.test.js +24 -0
  226. package/dist/components/workspace/__tests__/manifest-nav.test.js +599 -0
  227. package/dist/components/workspace/__tests__/nav-helpers.test.js +95 -0
  228. package/dist/components/workspace/__tests__/render-harness.svelte +66 -0
  229. package/dist/components/workspace/__tests__/render-harness.svelte.d.ts +32 -0
  230. package/dist/components/workspace/__tests__/render-harness.svelte.d.ts.map +1 -0
  231. package/dist/components/workspace/__tests__/render-tools-dock.test.js +243 -0
  232. package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte +58 -0
  233. package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts +16 -0
  234. package/dist/components/workspace/__tests__/role-shell-bind-harness.svelte.d.ts.map +1 -0
  235. package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte +41 -0
  236. package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts +13 -0
  237. package/dist/components/workspace/__tests__/role-shell-switch-harness.svelte.d.ts.map +1 -0
  238. package/dist/components/workspace/__tests__/test-icon.svelte +17 -0
  239. package/dist/components/workspace/__tests__/test-icon.svelte.d.ts +19 -0
  240. package/dist/components/workspace/__tests__/test-icon.svelte.d.ts.map +1 -0
  241. package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte +38 -0
  242. package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts +22 -0
  243. package/dist/components/workspace/__tests__/typed-tool-fixture/TypedTool.svelte.d.ts.map +1 -0
  244. package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts +65 -0
  245. package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.d.ts.map +1 -0
  246. package/dist/components/workspace/__tests__/typed-tool-fixture/register-typed-tool.js +115 -0
  247. package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts +15 -0
  248. package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.d.ts.map +1 -0
  249. package/dist/components/workspace/__tests__/typed-tool-fixture/typed-tool-types.js +7 -0
  250. package/dist/components/workspace/__tests__/typed-tool-fixture.test.js +115 -0
  251. package/dist/components/workspace/__tests__/use-harness-orphan.svelte +9 -0
  252. package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts +19 -0
  253. package/dist/components/workspace/__tests__/use-harness-orphan.svelte.d.ts.map +1 -0
  254. package/dist/components/workspace/__tests__/use-harness.svelte +23 -0
  255. package/dist/components/workspace/__tests__/use-harness.svelte.d.ts +8 -0
  256. package/dist/components/workspace/__tests__/use-harness.svelte.d.ts.map +1 -0
  257. package/dist/components/workspace/__tests__/use-tools-dock.test.js +33 -0
  258. package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte +43 -0
  259. package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts +11 -0
  260. package/dist/components/workspace/__tests__/workspace-shell-bind-harness.svelte.d.ts.map +1 -0
  261. package/dist/components/workspace/breadcrumbs-helpers.d.ts +44 -0
  262. package/dist/components/workspace/breadcrumbs-helpers.d.ts.map +1 -0
  263. package/dist/components/workspace/breadcrumbs-helpers.js +88 -0
  264. package/dist/components/workspace/index.d.ts +16 -0
  265. package/dist/components/workspace/index.d.ts.map +1 -0
  266. package/dist/components/workspace/index.js +14 -0
  267. package/dist/components/workspace/manifest-nav.d.ts +200 -0
  268. package/dist/components/workspace/manifest-nav.d.ts.map +1 -0
  269. package/dist/components/workspace/manifest-nav.js +408 -0
  270. package/dist/components/workspace/nav-helpers.d.ts +36 -0
  271. package/dist/components/workspace/nav-helpers.d.ts.map +1 -0
  272. package/dist/components/workspace/nav-helpers.js +60 -0
  273. package/dist/components/workspace/server/__tests__/compose-availability.test.js +383 -0
  274. package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts +78 -0
  275. package/dist/components/workspace/server/__tests__/typed-context-fixture.d.ts.map +1 -0
  276. package/dist/components/workspace/server/__tests__/typed-context-fixture.js +104 -0
  277. package/dist/components/workspace/server/compose-availability.d.ts +73 -0
  278. package/dist/components/workspace/server/compose-availability.d.ts.map +1 -0
  279. package/dist/components/workspace/server/compose-availability.js +114 -0
  280. package/dist/components/workspace/server/index.d.ts +13 -0
  281. package/dist/components/workspace/server/index.d.ts.map +1 -0
  282. package/dist/components/workspace/server/index.js +11 -0
  283. package/dist/components/workspace/server/types.d.ts +108 -0
  284. package/dist/components/workspace/server/types.d.ts.map +1 -0
  285. package/dist/components/workspace/server/types.js +11 -0
  286. package/dist/components/workspace/tools-dock/ToolsDock.svelte +565 -0
  287. package/dist/components/workspace/tools-dock/ToolsDock.svelte.d.ts +14 -0
  288. package/dist/components/workspace/tools-dock/ToolsDock.svelte.d.ts.map +1 -0
  289. package/dist/components/workspace/tools-dock/define-tools-dock.svelte.d.ts +143 -0
  290. package/dist/components/workspace/tools-dock/define-tools-dock.svelte.d.ts.map +1 -0
  291. package/dist/components/workspace/tools-dock/define-tools-dock.svelte.js +487 -0
  292. package/dist/components/workspace/tools-dock/use-tools-dock.d.ts +41 -0
  293. package/dist/components/workspace/tools-dock/use-tools-dock.d.ts.map +1 -0
  294. package/dist/components/workspace/tools-dock/use-tools-dock.js +50 -0
  295. package/dist/components/workspace/types.d.ts +372 -0
  296. package/dist/components/workspace/types.d.ts.map +1 -0
  297. package/dist/components/workspace/types.js +6 -0
  298. package/dist/hooks/index.d.ts +11 -0
  299. package/dist/hooks/index.d.ts.map +1 -0
  300. package/dist/hooks/index.js +10 -0
  301. package/dist/hooks/useAppState.svelte.d.ts +46 -0
  302. package/dist/hooks/useAppState.svelte.d.ts.map +1 -0
  303. package/dist/hooks/useAppState.svelte.js +59 -0
  304. package/dist/hooks/useAuth.svelte.d.ts +41 -0
  305. package/dist/hooks/useAuth.svelte.d.ts.map +1 -0
  306. package/dist/hooks/useAuth.svelte.js +43 -0
  307. package/dist/hooks/useLLM.svelte.d.ts +69 -0
  308. package/dist/hooks/useLLM.svelte.d.ts.map +1 -0
  309. package/dist/hooks/useLLM.svelte.js +85 -0
  310. package/dist/hooks/useSTT.svelte.d.ts +68 -0
  311. package/dist/hooks/useSTT.svelte.d.ts.map +1 -0
  312. package/dist/hooks/useSTT.svelte.js +97 -0
  313. package/dist/hooks/useSocket.svelte.d.ts +45 -0
  314. package/dist/hooks/useSocket.svelte.d.ts.map +1 -0
  315. package/dist/hooks/useSocket.svelte.js +54 -0
  316. package/dist/hooks/useTTS.svelte.d.ts +65 -0
  317. package/dist/hooks/useTTS.svelte.d.ts.map +1 -0
  318. package/dist/hooks/useTTS.svelte.js +93 -0
  319. package/dist/hooks/useTheme.d.ts +13 -0
  320. package/dist/hooks/useTheme.d.ts.map +1 -0
  321. package/dist/hooks/useTheme.js +16 -0
  322. package/dist/i18n/__tests__/server.spec.js +50 -0
  323. package/dist/i18n/server.d.ts +47 -0
  324. package/dist/i18n/server.d.ts.map +1 -0
  325. package/dist/i18n/server.js +58 -0
  326. package/dist/i18n/strings.forms.d.ts +33 -0
  327. package/dist/i18n/strings.forms.d.ts.map +1 -0
  328. package/dist/i18n/strings.forms.js +54 -0
  329. package/dist/i18n/strings.workspace.d.ts +34 -0
  330. package/dist/i18n/strings.workspace.d.ts.map +1 -0
  331. package/dist/i18n/strings.workspace.js +40 -0
  332. package/dist/index.d.ts +18 -0
  333. package/dist/index.d.ts.map +1 -0
  334. package/dist/index.js +23 -0
  335. package/dist/state/__tests__/warm-clients.test.js +40 -0
  336. package/dist/state/app-state.d.ts +308 -0
  337. package/dist/state/app-state.d.ts.map +1 -0
  338. package/dist/state/app-state.js +64 -0
  339. package/dist/state/app-state.svelte.d.ts +196 -0
  340. package/dist/state/app-state.svelte.d.ts.map +1 -0
  341. package/dist/state/app-state.svelte.js +774 -0
  342. package/dist/state/context.d.ts +23 -0
  343. package/dist/state/context.d.ts.map +1 -0
  344. package/dist/state/context.js +32 -0
  345. package/dist/state/form-context.d.ts +59 -0
  346. package/dist/state/form-context.d.ts.map +1 -0
  347. package/dist/state/form-context.js +31 -0
  348. package/dist/state/form-group-context.d.ts +13 -0
  349. package/dist/state/form-group-context.d.ts.map +1 -0
  350. package/dist/state/form-group-context.js +28 -0
  351. package/dist/state/index.d.ts +9 -0
  352. package/dist/state/index.d.ts.map +1 -0
  353. package/dist/state/index.js +8 -0
  354. package/dist/state/warm-clients.d.ts +136 -0
  355. package/dist/state/warm-clients.d.ts.map +1 -0
  356. package/dist/state/warm-clients.js +231 -0
  357. 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
+ }