@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,217 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SMRTToggle - An accessible toggle/switch component
4
+ *
5
+ * Features:
6
+ * - Native checkbox semantics for accessibility
7
+ * - Bindable checked state
8
+ * - Disabled state
9
+ * - Labels on either side
10
+ * - Size variants
11
+ * - Material 3 styling
12
+ */
13
+
14
+ export interface Props {
15
+ /** Whether the toggle is checked */
16
+ checked?: boolean;
17
+ /** Whether the toggle is disabled */
18
+ disabled?: boolean;
19
+ /** Name attribute for form submission */
20
+ name?: string;
21
+ /** Value attribute for form submission */
22
+ value?: string;
23
+ /** Label text */
24
+ label?: string;
25
+ /** Position of the label */
26
+ labelPosition?: 'left' | 'right';
27
+ /** Size variant */
28
+ size?: 'sm' | 'md' | 'lg';
29
+ /** ID for the input element */
30
+ id?: string;
31
+ /** ARIA label for accessibility */
32
+ ariaLabel?: string;
33
+ /** Change callback */
34
+ onchange?: (checked: boolean) => void;
35
+ }
36
+
37
+ let {
38
+ checked = $bindable(false),
39
+ disabled = false,
40
+ name,
41
+ value,
42
+ label,
43
+ labelPosition = 'right',
44
+ size = 'md',
45
+ id,
46
+ ariaLabel,
47
+ onchange,
48
+ }: Props = $props();
49
+
50
+ function handleChange(event: Event) {
51
+ const target = event.target as HTMLInputElement;
52
+ checked = target.checked;
53
+ onchange?.(checked);
54
+ }
55
+
56
+ const sizeClasses = {
57
+ sm: 'toggle--sm',
58
+ md: 'toggle--md',
59
+ lg: 'toggle--lg',
60
+ };
61
+ </script>
62
+
63
+ <label class="toggle {sizeClasses[size]}" class:toggle--disabled={disabled}>
64
+ {#if label && labelPosition === 'left'}
65
+ <span class="toggle__label toggle__label--left">{label}</span>
66
+ {/if}
67
+
68
+ <span class="toggle__track">
69
+ <input
70
+ type="checkbox"
71
+ class="toggle__input"
72
+ {id}
73
+ {name}
74
+ {value}
75
+ {disabled}
76
+ {checked}
77
+ onchange={handleChange}
78
+ aria-label={ariaLabel ?? label ?? ''}
79
+ />
80
+ <span class="toggle__thumb"></span>
81
+ </span>
82
+
83
+ {#if label && labelPosition === 'right'}
84
+ <span class="toggle__label toggle__label--right">{label}</span>
85
+ {/if}
86
+ </label>
87
+
88
+ <style>
89
+ .toggle {
90
+ display: inline-flex;
91
+ align-items: center;
92
+ gap: var(--smrt-spacing-2, 0.5rem);
93
+ cursor: pointer;
94
+ user-select: none;
95
+ }
96
+
97
+ .toggle--disabled {
98
+ cursor: not-allowed;
99
+ opacity: 0.5;
100
+ }
101
+
102
+ .toggle__label {
103
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
104
+ color: var(--smrt-color-on-surface, #111827);
105
+ line-height: 1.5;
106
+ }
107
+
108
+ .toggle__track {
109
+ position: relative;
110
+ display: inline-flex;
111
+ align-items: center;
112
+ flex-shrink: 0;
113
+ }
114
+
115
+ .toggle__input {
116
+ position: absolute;
117
+ width: 1px;
118
+ height: 1px;
119
+ padding: 0;
120
+ margin: -1px;
121
+ overflow: hidden;
122
+ clip: rect(0, 0, 0, 0);
123
+ white-space: nowrap;
124
+ border: 0;
125
+ }
126
+
127
+ .toggle__thumb {
128
+ position: relative;
129
+ background: var(--smrt-color-surface-container-highest, #e5e7eb);
130
+ border-radius: var(--smrt-radius-full, 9999px);
131
+ transition: background-color var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
132
+ }
133
+
134
+ .toggle__thumb::after {
135
+ content: '';
136
+ position: absolute;
137
+ background: var(--smrt-color-surface, #ffffff);
138
+ border-radius: var(--smrt-radius-full, 9999px);
139
+ box-shadow: var(--smrt-elevation-1, 0 1px 3px color-mix(in srgb, var(--smrt-color-shadow) 20%, transparent));
140
+ transition: transform var(--smrt-duration-short2, 150ms) var(--smrt-easing-standard, ease);
141
+ }
142
+
143
+ /* Checked state */
144
+ .toggle__input:checked + .toggle__thumb {
145
+ background: var(--smrt-color-primary, #005ac1);
146
+ }
147
+
148
+ /* Focus state */
149
+ .toggle__input:focus-visible + .toggle__thumb {
150
+ outline: 2px solid var(--smrt-color-primary, #005ac1);
151
+ outline-offset: 2px;
152
+ }
153
+
154
+ /* Hover state */
155
+ .toggle:not(.toggle--disabled):hover .toggle__thumb {
156
+ background: var(--smrt-color-surface-container-high, #d1d5db);
157
+ }
158
+
159
+ .toggle:not(.toggle--disabled):hover .toggle__input:checked + .toggle__thumb {
160
+ background: var(--smrt-color-primary, #2563eb);
161
+ }
162
+
163
+ /* Size: Small */
164
+ .toggle--sm .toggle__thumb {
165
+ width: 32px;
166
+ height: 18px;
167
+ }
168
+
169
+ .toggle--sm .toggle__thumb::after {
170
+ width: 14px;
171
+ height: 14px;
172
+ top: 2px;
173
+ left: 2px;
174
+ }
175
+
176
+ .toggle--sm .toggle__input:checked + .toggle__thumb::after {
177
+ transform: translateX(14px);
178
+ }
179
+
180
+ /* Size: Medium (default) */
181
+ .toggle--md .toggle__thumb {
182
+ width: 44px;
183
+ height: 24px;
184
+ }
185
+
186
+ .toggle--md .toggle__thumb::after {
187
+ width: 20px;
188
+ height: 20px;
189
+ top: 2px;
190
+ left: 2px;
191
+ }
192
+
193
+ .toggle--md .toggle__input:checked + .toggle__thumb::after {
194
+ transform: translateX(20px);
195
+ }
196
+
197
+ /* Size: Large */
198
+ .toggle--lg .toggle__thumb {
199
+ width: 56px;
200
+ height: 30px;
201
+ }
202
+
203
+ .toggle--lg .toggle__thumb::after {
204
+ width: 26px;
205
+ height: 26px;
206
+ top: 2px;
207
+ left: 2px;
208
+ }
209
+
210
+ .toggle--lg .toggle__input:checked + .toggle__thumb::after {
211
+ transform: translateX(26px);
212
+ }
213
+
214
+ .toggle--lg .toggle__label {
215
+ font-size: var(--smrt-typography-body-large-size, 1rem);
216
+ }
217
+ </style>
@@ -0,0 +1,37 @@
1
+ /**
2
+ * SMRTToggle - An accessible toggle/switch component
3
+ *
4
+ * Features:
5
+ * - Native checkbox semantics for accessibility
6
+ * - Bindable checked state
7
+ * - Disabled state
8
+ * - Labels on either side
9
+ * - Size variants
10
+ * - Material 3 styling
11
+ */
12
+ export interface Props {
13
+ /** Whether the toggle is checked */
14
+ checked?: boolean;
15
+ /** Whether the toggle is disabled */
16
+ disabled?: boolean;
17
+ /** Name attribute for form submission */
18
+ name?: string;
19
+ /** Value attribute for form submission */
20
+ value?: string;
21
+ /** Label text */
22
+ label?: string;
23
+ /** Position of the label */
24
+ labelPosition?: 'left' | 'right';
25
+ /** Size variant */
26
+ size?: 'sm' | 'md' | 'lg';
27
+ /** ID for the input element */
28
+ id?: string;
29
+ /** ARIA label for accessibility */
30
+ ariaLabel?: string;
31
+ /** Change callback */
32
+ onchange?: (checked: boolean) => void;
33
+ }
34
+ declare const Toggle: import("svelte").Component<Props, {}, "checked">;
35
+ type Toggle = ReturnType<typeof Toggle>;
36
+ export default Toggle;
37
+ //# sourceMappingURL=Toggle.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Toggle.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/forms/Toggle.svelte.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,KAAK;IACpB,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,mBAAmB;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,+BAA+B;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACvC;AAiDD,QAAA,MAAM,MAAM,kDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Behavior tests for AddressInput (Sweep S11, #1416).
3
+ *
4
+ * Exercises the sub-field wiring: typing/selecting each field composes a
5
+ * Partial<AddressValue> emitted via onchange, the `fields` prop gates which
6
+ * sub-fields render (and thus which keys appear in the composed value),
7
+ * province/country selects populate from options, and the error prop marks
8
+ * every field invalid with a single linked alert. useAppState (default mode)
9
+ * is mocked since the hook throws outside a <Provider>.
10
+ */
11
+ import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
12
+ import { fireEvent, render, screen } from '@testing-library/svelte';
13
+ import userEvent from '@testing-library/user-event';
14
+ import { describe, expect, it, vi } from 'vitest';
15
+ vi.mock('../../../hooks/useAppState.svelte.js', () => ({
16
+ useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
17
+ }));
18
+ import AddressInput from '../AddressInput.svelte';
19
+ describe('AddressInput — behavior', () => {
20
+ it('typing the street composes a value including the street', async () => {
21
+ const onchange = vi.fn();
22
+ render(AddressInput, {
23
+ props: { name: 'addr', label: 'Address', onchange },
24
+ });
25
+ await userEvent.type(screen.getByLabelText('Street Address'), '123 Main St');
26
+ expect(onchange).toHaveBeenLastCalledWith(expect.objectContaining({ street: '123 Main St' }));
27
+ });
28
+ it('typing the city updates the composed value', async () => {
29
+ const onchange = vi.fn();
30
+ render(AddressInput, {
31
+ props: { name: 'addr', label: 'Address', onchange },
32
+ });
33
+ await userEvent.type(screen.getByLabelText('City'), 'Toronto');
34
+ expect(onchange).toHaveBeenLastCalledWith(expect.objectContaining({ city: 'Toronto' }));
35
+ });
36
+ it('selecting a province emits its value code', async () => {
37
+ const onchange = vi.fn();
38
+ render(AddressInput, {
39
+ props: { name: 'addr', label: 'Address', onchange },
40
+ });
41
+ await userEvent.selectOptions(screen.getByLabelText('Province/State'), 'ON');
42
+ expect(onchange).toHaveBeenLastCalledWith(expect.objectContaining({ province: 'ON' }));
43
+ });
44
+ it('selecting a country emits its value code', async () => {
45
+ const onchange = vi.fn();
46
+ render(AddressInput, {
47
+ props: { name: 'addr', label: 'Address', onchange },
48
+ });
49
+ await userEvent.selectOptions(screen.getByLabelText('Country'), 'US');
50
+ expect(onchange).toHaveBeenLastCalledWith(expect.objectContaining({ country: 'US' }));
51
+ });
52
+ it('defaults the country to CA', () => {
53
+ render(AddressInput, { props: { name: 'addr', label: 'Address' } });
54
+ expect(screen.getByLabelText('Country')).toHaveValue('CA');
55
+ });
56
+ it('composes a complete address object across all fields', async () => {
57
+ const onchange = vi.fn();
58
+ render(AddressInput, {
59
+ props: { name: 'addr', label: 'Address', onchange },
60
+ });
61
+ await userEvent.type(screen.getByLabelText('Postal/ZIP Code'), 'M5V 2T6');
62
+ expect(onchange).toHaveBeenLastCalledWith(expect.objectContaining({
63
+ street: '',
64
+ city: '',
65
+ province: '',
66
+ postalCode: 'M5V 2T6',
67
+ country: 'CA',
68
+ }));
69
+ });
70
+ it('only emits keys for the fields it was told to render', async () => {
71
+ const onchange = vi.fn();
72
+ render(AddressInput, {
73
+ props: {
74
+ name: 'addr',
75
+ label: 'Address',
76
+ fields: ['city', 'country'],
77
+ onchange,
78
+ },
79
+ });
80
+ expect(screen.queryByLabelText('Street Address')).toBeNull();
81
+ expect(screen.queryByLabelText('Postal/ZIP Code')).toBeNull();
82
+ await userEvent.type(screen.getByLabelText('City'), 'Ottawa');
83
+ const composed = onchange.mock.calls.at(-1)?.[0];
84
+ expect(Object.keys(composed).sort()).toEqual(['city', 'country']);
85
+ expect(composed.city).toBe('Ottawa');
86
+ });
87
+ it('seeds sub-fields from the bound value object', () => {
88
+ render(AddressInput, {
89
+ props: {
90
+ name: 'addr',
91
+ label: 'Address',
92
+ value: { city: 'Calgary', province: 'AB', country: 'CA' },
93
+ },
94
+ });
95
+ expect(screen.getByLabelText('City')).toHaveValue('Calgary');
96
+ expect(screen.getByLabelText('Province/State')).toHaveValue('AB');
97
+ });
98
+ it('renders the province and country option sets', () => {
99
+ render(AddressInput, { props: { name: 'addr', label: 'Address' } });
100
+ const provinces = screen.getByLabelText('Province/State');
101
+ expect(provinces.querySelector('option[value="QC"]')?.textContent).toBe('Quebec');
102
+ const countries = screen.getByLabelText('Country');
103
+ expect(countries.querySelector('option[value="US"]')?.textContent).toBe('United States');
104
+ });
105
+ it('marks every field invalid and links one alert when error is set', () => {
106
+ render(AddressInput, {
107
+ props: { name: 'addr', label: 'Address', error: 'Address required' },
108
+ });
109
+ const street = screen.getByLabelText('Street Address');
110
+ expect(street).toHaveAttribute('aria-invalid', 'true');
111
+ expect(street).toHaveAttribute('aria-describedby', 'addr-error');
112
+ const err = document.getElementById('addr-error');
113
+ expect(err).toHaveTextContent('Address required');
114
+ expect(err).toHaveAttribute('role', 'alert');
115
+ });
116
+ it('is axe-clean with all fields labelled', async () => {
117
+ const { container } = render(AddressInput, {
118
+ props: { name: 'addr', label: 'Address' },
119
+ });
120
+ await expectNoA11yViolations(container);
121
+ });
122
+ });
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Component test for CheckboxInput (Sweep S11, #1416).
3
+ *
4
+ * Follows the golden pattern (src/components/ui/__tests__/Button.test.ts).
5
+ * CheckboxInput calls `useAppState` (throws outside a <Provider>), so we mock it
6
+ * to a stub default mode. Its form-context lookup uses `getContext` and safely
7
+ * returns undefined outside a provider, so no further stubbing is needed.
8
+ */
9
+ import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
10
+ import { render, screen } from '@testing-library/svelte';
11
+ import userEvent from '@testing-library/user-event';
12
+ import { describe, expect, it, vi } from 'vitest';
13
+ vi.mock('../../../hooks/useAppState.svelte.js', () => ({
14
+ useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
15
+ }));
16
+ import CheckboxInput from '../CheckboxInput.svelte';
17
+ describe('CheckboxInput', () => {
18
+ it('renders a checkbox labelled by its label, wired via for/id', () => {
19
+ render(CheckboxInput, { props: { name: 'terms', label: 'Accept terms' } });
20
+ const checkbox = screen.getByRole('checkbox', { name: 'Accept terms' });
21
+ expect(checkbox).toBeInTheDocument();
22
+ expect(checkbox).toHaveAttribute('id', 'terms');
23
+ expect(checkbox).not.toBeChecked();
24
+ });
25
+ it('reflects the checked prop', () => {
26
+ render(CheckboxInput, {
27
+ props: { name: 'terms', label: 'Accept terms', checked: true },
28
+ });
29
+ expect(screen.getByRole('checkbox', { name: 'Accept terms' })).toBeChecked();
30
+ });
31
+ it('reflects the disabled prop', () => {
32
+ render(CheckboxInput, {
33
+ props: { name: 'terms', label: 'Accept terms', disabled: true },
34
+ });
35
+ expect(screen.getByRole('checkbox', { name: 'Accept terms' })).toBeDisabled();
36
+ });
37
+ it('reflects the required prop (and marks the label with *)', () => {
38
+ render(CheckboxInput, {
39
+ props: { name: 'terms', label: 'Accept terms', required: true },
40
+ });
41
+ // Required appends a "*" to the visible label, so the accessible name is "Accept terms*".
42
+ expect(screen.getByRole('checkbox', { name: 'Accept terms*' })).toBeRequired();
43
+ });
44
+ it('toggles checked and fires onchange when clicked', async () => {
45
+ const onchange = vi.fn();
46
+ render(CheckboxInput, {
47
+ props: { name: 'terms', label: 'Accept terms', onchange },
48
+ });
49
+ const checkbox = screen.getByRole('checkbox', { name: 'Accept terms' });
50
+ await userEvent.click(checkbox);
51
+ expect(checkbox).toBeChecked();
52
+ expect(onchange).toHaveBeenCalledWith(true);
53
+ await userEvent.click(checkbox);
54
+ expect(checkbox).not.toBeChecked();
55
+ expect(onchange).toHaveBeenLastCalledWith(false);
56
+ });
57
+ it('toggles when its label text is clicked', async () => {
58
+ const onchange = vi.fn();
59
+ render(CheckboxInput, {
60
+ props: { name: 'terms', label: 'Accept terms', onchange },
61
+ });
62
+ await userEvent.click(screen.getByText('Accept terms'));
63
+ expect(screen.getByRole('checkbox', { name: 'Accept terms' })).toBeChecked();
64
+ expect(onchange).toHaveBeenCalledWith(true);
65
+ });
66
+ it('does not fire onchange when disabled', async () => {
67
+ const onchange = vi.fn();
68
+ render(CheckboxInput, {
69
+ props: { name: 'terms', label: 'Accept terms', disabled: true, onchange },
70
+ });
71
+ await userEvent.click(screen.getByRole('checkbox', { name: 'Accept terms' }));
72
+ expect(onchange).not.toHaveBeenCalled();
73
+ });
74
+ it('is axe-clean with a label', async () => {
75
+ const { container } = render(CheckboxInput, {
76
+ props: { name: 'newsletter', label: 'Subscribe to the newsletter' },
77
+ });
78
+ await expectNoA11yViolations(container);
79
+ });
80
+ it('is axe-clean when checked, required, and disabled', async () => {
81
+ const { container } = render(CheckboxInput, {
82
+ props: {
83
+ name: 'newsletter',
84
+ label: 'Subscribe to the newsletter',
85
+ checked: true,
86
+ required: true,
87
+ disabled: true,
88
+ },
89
+ });
90
+ await expectNoA11yViolations(container);
91
+ });
92
+ });
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Behavior tests for DateRangeInput (Sweep S11, #1416).
3
+ *
4
+ * In default mode the component renders two native date pickers (Start / End)
5
+ * with cross-linked min/max, emits a { startDate, endDate } range via
6
+ * onchange, and validates start <= end. The hooks throw outside a <Provider>,
7
+ * so useAppState (default mode) + useSTT (stub) are mocked.
8
+ */
9
+ import { expectNoA11yViolations } from '@happyvertical/smrt-ui/test-support/a11y';
10
+ import { fireEvent, render, screen } from '@testing-library/svelte';
11
+ import { describe, expect, it, vi } from 'vitest';
12
+ vi.mock('../../../hooks/useAppState.svelte.js', () => ({
13
+ useAppState: () => ({ state: { mode: 'default' }, setMode: vi.fn() }),
14
+ }));
15
+ vi.mock('../../../hooks/useSTT.svelte.js', () => ({
16
+ useSTT: () => ({
17
+ isListening: false,
18
+ isInitializing: false,
19
+ isReady: false,
20
+ adapterType: null,
21
+ downloadProgress: 0,
22
+ lastResult: '',
23
+ initialize: vi.fn(),
24
+ start: vi.fn(),
25
+ stop: vi.fn(),
26
+ }),
27
+ }));
28
+ import DateRangeInput from '../DateRangeInput.svelte';
29
+ describe('DateRangeInput — behavior', () => {
30
+ it('renders labelled Start and End date pickers', () => {
31
+ render(DateRangeInput, { props: { name: 'range', label: 'Dates' } });
32
+ expect(screen.getByLabelText('Start')).toHaveAttribute('type', 'date');
33
+ expect(screen.getByLabelText('End')).toHaveAttribute('type', 'date');
34
+ });
35
+ it('setting the start date emits the range with the new start', async () => {
36
+ const onchange = vi.fn();
37
+ render(DateRangeInput, {
38
+ props: { name: 'range', label: 'Dates', onchange },
39
+ });
40
+ await fireEvent.change(screen.getByLabelText('Start'), {
41
+ target: { value: '2024-01-10' },
42
+ });
43
+ expect(onchange).toHaveBeenLastCalledWith({
44
+ startDate: '2024-01-10',
45
+ endDate: '',
46
+ });
47
+ });
48
+ it('setting both dates emits the full range', async () => {
49
+ const onchange = vi.fn();
50
+ render(DateRangeInput, {
51
+ props: {
52
+ name: 'range',
53
+ label: 'Dates',
54
+ startDate: '2024-01-10',
55
+ onchange,
56
+ },
57
+ });
58
+ await fireEvent.change(screen.getByLabelText('End'), {
59
+ target: { value: '2024-03-20' },
60
+ });
61
+ expect(onchange).toHaveBeenLastCalledWith({
62
+ startDate: '2024-01-10',
63
+ endDate: '2024-03-20',
64
+ });
65
+ });
66
+ it('flags an end-before-start range as invalid with a linked alert', () => {
67
+ render(DateRangeInput, {
68
+ props: {
69
+ name: 'range',
70
+ label: 'Dates',
71
+ startDate: '2024-05-01',
72
+ endDate: '2024-01-01',
73
+ },
74
+ });
75
+ const start = screen.getByLabelText('Start');
76
+ expect(start).toHaveAttribute('aria-invalid', 'true');
77
+ expect(start).toHaveAttribute('aria-describedby', 'range-error');
78
+ const err = document.getElementById('range-error');
79
+ expect(err).toHaveAttribute('role', 'alert');
80
+ expect(err).toHaveTextContent('End date must be after start date');
81
+ });
82
+ it('treats start <= end as a valid range (no error)', () => {
83
+ render(DateRangeInput, {
84
+ props: {
85
+ name: 'range',
86
+ label: 'Dates',
87
+ startDate: '2024-01-01',
88
+ endDate: '2024-05-01',
89
+ },
90
+ });
91
+ expect(screen.getByLabelText('Start')).not.toHaveAttribute('aria-invalid');
92
+ expect(document.getElementById('range-error')).toBeNull();
93
+ });
94
+ it('caps the start picker max at the chosen end date', () => {
95
+ render(DateRangeInput, {
96
+ props: { name: 'range', label: 'Dates', endDate: '2024-03-20' },
97
+ });
98
+ expect(screen.getByLabelText('Start')).toHaveAttribute('max', '2024-03-20');
99
+ });
100
+ it('floors the end picker min at the chosen start date', () => {
101
+ render(DateRangeInput, {
102
+ props: { name: 'range', label: 'Dates', startDate: '2024-01-10' },
103
+ });
104
+ expect(screen.getByLabelText('End')).toHaveAttribute('min', '2024-01-10');
105
+ });
106
+ it('mirrors the dates into hidden submission inputs', async () => {
107
+ const { container } = render(DateRangeInput, {
108
+ props: { name: 'range', label: 'Dates', startDate: '2024-01-10' },
109
+ });
110
+ await fireEvent.change(screen.getByLabelText('End'), {
111
+ target: { value: '2024-03-20' },
112
+ });
113
+ expect(container.querySelector('input[name="range_start"]')).toHaveValue('2024-01-10');
114
+ expect(container.querySelector('input[name="range_end"]')).toHaveValue('2024-03-20');
115
+ });
116
+ it('renders an explicit error prop as a linked alert', () => {
117
+ render(DateRangeInput, {
118
+ props: { name: 'range', label: 'Dates', error: 'Pick a range' },
119
+ });
120
+ const err = document.getElementById('range-error');
121
+ expect(err).toHaveTextContent('Pick a range');
122
+ expect(err).toHaveAttribute('role', 'alert');
123
+ });
124
+ it('is axe-clean in a labelled + error state', async () => {
125
+ const { container } = render(DateRangeInput, {
126
+ props: {
127
+ name: 'range',
128
+ label: 'Dates',
129
+ startDate: '2024-05-01',
130
+ endDate: '2024-01-01',
131
+ },
132
+ });
133
+ await expectNoA11yViolations(container);
134
+ });
135
+ });