@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,408 @@
1
+ /**
2
+ * Pure helper that turns a SMRT manifest into the `NavItem[]` shape the
3
+ * existing `NavTree` / `RoleShell` primitives expect.
4
+ *
5
+ * This is the "manifest → admin UI" adapter called out as a friction gap in
6
+ * happyvertical/smrt#1235 / #1226 / #1248: every consumer hand-writes its nav
7
+ * config today, with dozens of `@smrt()` classes that boilerplate drifts.
8
+ * Opt in here to manifest-driven nav while keeping the primitives themselves
9
+ * SmrtObject-agnostic.
10
+ *
11
+ * Design constraints (see #1248 for the full brief):
12
+ * - Pure function. No SvelteKit imports, no SSR coupling, no module side
13
+ * effects. Data in → data out.
14
+ * - Cross-industry-safe. Never assumes apparel / furniture / automotive
15
+ * vocabulary; consumers supply their own `sectionHints`.
16
+ * - Deterministic output. Sections and items both sort alphabetically by
17
+ * title so manifest-order churn doesn't shuffle the rendered nav.
18
+ * - Decoupled from any concrete role/permission package. `permittedResources`
19
+ * is just a plain string array of qualified class names.
20
+ *
21
+ * The structural `SmrtManifestLike` / `SmrtManifestEntryLike` shapes here
22
+ * intentionally subset the real `SmartObjectManifest` from `@happyvertical/smrt-core`.
23
+ * Adding a peer dependency on core would create a circular reference (core
24
+ * pulls in svelte primitives downstream), so the helper accepts anything
25
+ * that matches the documented shape. Pass a `SmartObjectManifest` directly
26
+ * and TypeScript is happy.
27
+ */
28
+ // ────────────────────────────────────────────────────────────────────────
29
+ // Pluralization
30
+ //
31
+ // The brief explicitly says "use a tiny pluralization helper, don't pull
32
+ // in pluralize". This covers the common English suffix rules and is
33
+ // deterministic. Consumers who need exotic plurals override via
34
+ // `decoratorConfig.ui.label`.
35
+ // ────────────────────────────────────────────────────────────────────────
36
+ /**
37
+ * Minimal English pluralizer. Handles the common suffix rules sufficient
38
+ * for class-name → label conversion (Article → Articles, Category →
39
+ * Categories, Box → Boxes, etc.). For irregulars or exotic plurals,
40
+ * override with `@smrt({ ui: { label: 'Foo bars' } })`.
41
+ *
42
+ * Exported only for tests / advanced callers; most code should let
43
+ * `navTreeFromManifest` apply it.
44
+ */
45
+ export function pluralizeClassName(name) {
46
+ if (!name)
47
+ return name;
48
+ // Already plural-ish (very rough — "Categories" passes through).
49
+ if (/(?:[^aeiouy]|qu)ies$/i.test(name))
50
+ return name;
51
+ if (/s$/i.test(name) && !/(?:ss|us|is)$/i.test(name))
52
+ return name;
53
+ // Words ending in consonant + 'y' → 'ies' (Category → Categories).
54
+ if (/[^aeiouy]y$/i.test(name)) {
55
+ return `${name.slice(0, -1)}ies`;
56
+ }
57
+ // Words ending in s, x, z, ch, sh → 'es' (Box → Boxes, Dish → Dishes).
58
+ if (/(?:s|x|z|ch|sh)$/i.test(name)) {
59
+ return `${name}es`;
60
+ }
61
+ return `${name}s`;
62
+ }
63
+ /** Convert a PascalCase class name to a kebab-case URL slug. */
64
+ function classNameToKebab(name) {
65
+ return name
66
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
67
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
68
+ .toLowerCase();
69
+ }
70
+ // ────────────────────────────────────────────────────────────────────────
71
+ // Filtering — drop collection classes, internal/test visibility, etc.
72
+ // ────────────────────────────────────────────────────────────────────────
73
+ /**
74
+ * True when an entry represents a `SmrtCollection` subclass rather than a
75
+ * `SmrtObject`. The manifest captures both — collections appear with
76
+ * `extends: 'SmrtCollection'` (or any ancestor whose own `extends` chain
77
+ * eventually hits one), but the nav helper only cares about the SmrtObject
78
+ * side. Heuristic: `className` ending in `Collection`. This matches the
79
+ * SMRT convention (`ProductCollection`, `CategoryCollection`) and avoids
80
+ * needing the full inheritance walk.
81
+ */
82
+ function looksLikeCollectionClass(entry) {
83
+ if (entry.className.endsWith('Collection'))
84
+ return true;
85
+ if (entry.extends === 'SmrtCollection')
86
+ return true;
87
+ return false;
88
+ }
89
+ /**
90
+ * Whether an entry is publicly visible in the manifest. Classes marked
91
+ * `@smrt({ visibility: 'internal' })` are package-only and should never
92
+ * surface in admin nav — they're typically join tables, sync helpers, or
93
+ * cross-package plumbing. `'test'` fixtures should obviously never leak
94
+ * either. Default (`undefined` / `'public'`) keeps the entry visible.
95
+ *
96
+ * Reads the top-level `visibility` field first (the shape published by
97
+ * `SmartObjectDefinition`) and falls back to `decoratorConfig.visibility`
98
+ * for older / hand-built manifests.
99
+ */
100
+ function isPublicEntry(entry) {
101
+ const visibility = entry.visibility ?? entry.decoratorConfig?.visibility ?? 'public';
102
+ return visibility === 'public';
103
+ }
104
+ /**
105
+ * True when an entry is an STI subtype whose REST route would be the
106
+ * same as some ancestor's. The SMRT scanner copies the STI parent's
107
+ * `collection` field onto every child entry — `Material`, `Style`,
108
+ * `Cart`, `Order`, `ProductionOrder` etc. all carry the parent's
109
+ * `collection` value (`products`, `contracts`). If we emitted a nav
110
+ * item per entry, the user would see "Materials" and "Products" both
111
+ * linking to `/api/v1/products`, with no way to tell which list they
112
+ * landed on. Suppressing the child entries keeps the base class's
113
+ * single nav link — the REST endpoint already exposes the full
114
+ * polymorphic list, which is the conventional shape for STI tables.
115
+ *
116
+ * **Walks the ancestor chain.** A two-level hierarchy like
117
+ * `Product → Material → FabricMaterial` may have its intermediate
118
+ * `Material` entry stripped from a partial manifest while `Product`
119
+ * remains. A one-level check would let `FabricMaterial` through
120
+ * (parent absent → return false → keep) and duplicate the `Product`
121
+ * link. Walking the chain handles this case: as long as any ancestor
122
+ * with a matching `collection` is reachable through the chain, the
123
+ * subtype is suppressed. Fully-orphaned entries (no resolvable
124
+ * ancestor in the manifest) stay visible — better to show one link
125
+ * than none.
126
+ *
127
+ * Detection: starting at `entry.extends`, look up that className in
128
+ * the manifest. If found and `collection` matches, suppress this
129
+ * entry. Otherwise recurse into THAT entry's `extends` and try again.
130
+ * Stop at `SmrtObject`/`SmrtClass`, when no manifest entry resolves,
131
+ * or when a cycle is detected.
132
+ */
133
+ function looksLikeStiSubtypeOfParentRoute(entry, manifest) {
134
+ const byClassName = entryIndexByClassName(manifest);
135
+ const visited = new Set();
136
+ let cursor = entry.extends;
137
+ while (cursor && cursor !== 'SmrtObject' && cursor !== 'SmrtClass') {
138
+ if (visited.has(cursor))
139
+ return false; // pathological cycle
140
+ visited.add(cursor);
141
+ const ancestor = byClassName.get(cursor);
142
+ if (!ancestor)
143
+ return false; // chain broken, keep this entry visible
144
+ if (ancestor.collection && ancestor.collection === entry.collection) {
145
+ return true;
146
+ }
147
+ cursor = ancestor.extends;
148
+ }
149
+ return false;
150
+ }
151
+ /**
152
+ * Build a className → entry lookup over the manifest. Memoised per
153
+ * manifest via WeakMap so a typical `navTreeFromManifest` call only
154
+ * walks `Object.values(manifest.objects)` once even when many entries
155
+ * trigger `looksLikeStiSubtypeOfParentRoute`. When two entries share
156
+ * a className (shouldn't happen in a well-formed manifest), the first
157
+ * one wins — duplicates would still produce the same dedup answer
158
+ * because both would carry the same parent chain.
159
+ */
160
+ const manifestClassNameIndex = new WeakMap();
161
+ function entryIndexByClassName(manifest) {
162
+ const cached = manifestClassNameIndex.get(manifest);
163
+ if (cached)
164
+ return cached;
165
+ const idx = new Map();
166
+ for (const e of Object.values(manifest.objects)) {
167
+ if (!idx.has(e.className))
168
+ idx.set(e.className, e);
169
+ }
170
+ manifestClassNameIndex.set(manifest, idx);
171
+ return idx;
172
+ }
173
+ /**
174
+ * Whether an entry exposes a `list` route through the REST generator.
175
+ *
176
+ * Join-table / link / asset models commonly declare `@smrt({ api: false })`
177
+ * because they're plumbing, not catalog. Linking a nav item to them would
178
+ * 404 — the route literally isn't generated. Conservatively skip those.
179
+ *
180
+ * Handles the three shapes `@smrt({ api })` can take:
181
+ *
182
+ * - `undefined` / `true` → default routes (list included)
183
+ * - `false` → no routes
184
+ * - `{ include: [...] }` → only listed routes
185
+ * - `{ exclude: [...] }` → all except listed routes
186
+ */
187
+ function hasListRoute(entry) {
188
+ const api = entry.decoratorConfig?.api;
189
+ if (api === undefined || api === true)
190
+ return true;
191
+ if (api === false)
192
+ return false;
193
+ if (typeof api !== 'object' || api === null)
194
+ return true;
195
+ const obj = api;
196
+ if (Array.isArray(obj.include)) {
197
+ return obj.include.includes('list');
198
+ }
199
+ if (Array.isArray(obj.exclude)) {
200
+ return !obj.exclude.includes('list');
201
+ }
202
+ return true;
203
+ }
204
+ /**
205
+ * Derive a section title for an entry. Order of preference:
206
+ * 1. First `sectionHints` key whose substring appears in the qualified name.
207
+ * 2. The package suffix between the last `/` and the `:` of the qualified
208
+ * name (e.g. `@happyvertical/smrt-content:Article` → `smrt-content`).
209
+ * 3. The `packageName` field as-is, or `'Resources'` as a final fallback.
210
+ */
211
+ function deriveSectionTitle(entry, qualifier, sectionHints) {
212
+ if (sectionHints) {
213
+ for (const [needle, title] of Object.entries(sectionHints)) {
214
+ if (qualifier.includes(needle))
215
+ return title;
216
+ }
217
+ }
218
+ // `@happyvertical/smrt-content:Article` → after last `/`, before `:`.
219
+ const colonIndex = qualifier.indexOf(':');
220
+ const beforeColon = colonIndex >= 0 ? qualifier.slice(0, colonIndex) : qualifier;
221
+ const lastSlash = beforeColon.lastIndexOf('/');
222
+ if (lastSlash >= 0 && lastSlash < beforeColon.length - 1) {
223
+ return beforeColon.slice(lastSlash + 1);
224
+ }
225
+ return entry.packageName || beforeColon || 'Resources';
226
+ }
227
+ /**
228
+ * Pick the qualifier string for an entry. Prefers the explicit
229
+ * `qualifiedName`; falls back to a synthesised `packageName:className`
230
+ * pair so partial / hand-built manifests still work in tests.
231
+ */
232
+ function entryQualifier(entry) {
233
+ if (entry.qualifiedName)
234
+ return entry.qualifiedName;
235
+ if (entry.packageName)
236
+ return `${entry.packageName}:${entry.className}`;
237
+ return entry.className;
238
+ }
239
+ /**
240
+ * Expand a permitted-qualifiers set so it includes the parent
241
+ * qualifier of every STI subtype that shares its parent's `collection`
242
+ * value. Without this expansion, a role permitted to access an STI
243
+ * subtype (e.g. `Cart`) would lose its nav link, because the dedup
244
+ * step suppresses subtypes that share the parent's REST route and
245
+ * the permission filter would then drop the parent. The expansion
246
+ * threads through the inheritance chain (handles multi-level STI)
247
+ * with a visited-set cycle guard.
248
+ *
249
+ * Pure: returns a new Set; never mutates the caller's set. The
250
+ * className index is the same memoised one used by
251
+ * `looksLikeStiSubtypeOfParentRoute`.
252
+ */
253
+ function expandPermittedThroughStiParents(permitted, manifest) {
254
+ const byClassName = entryIndexByClassName(manifest);
255
+ // Also need to resolve qualifier → entry, because permitted set
256
+ // entries are qualified names (`@pkg/x:Class`), not raw classNames.
257
+ const byQualifier = new Map();
258
+ for (const entry of Object.values(manifest.objects)) {
259
+ byQualifier.set(entryQualifier(entry), entry);
260
+ }
261
+ const expanded = new Set(permitted);
262
+ for (const qualifier of permitted) {
263
+ const entry = byQualifier.get(qualifier);
264
+ if (!entry)
265
+ continue;
266
+ const visited = new Set();
267
+ let cursor = entry.extends;
268
+ let lastSeenCollection = entry.collection;
269
+ while (cursor && cursor !== 'SmrtObject' && cursor !== 'SmrtClass') {
270
+ if (visited.has(cursor))
271
+ break;
272
+ visited.add(cursor);
273
+ const ancestor = byClassName.get(cursor);
274
+ if (!ancestor)
275
+ break;
276
+ if (ancestor.collection && ancestor.collection === lastSeenCollection) {
277
+ expanded.add(entryQualifier(ancestor));
278
+ }
279
+ lastSeenCollection = ancestor.collection ?? lastSeenCollection;
280
+ cursor = ancestor.extends;
281
+ }
282
+ }
283
+ return expanded;
284
+ }
285
+ /**
286
+ * Walk a SMRT manifest and emit the `NavSection[]` shape that
287
+ * `<NavTree items={...}>` and `RoleConfig.sections` consume.
288
+ *
289
+ * Algorithm:
290
+ * 1. Drop collection classes (`*Collection` / `extends: 'SmrtCollection'`).
291
+ * 2. Drop entries marked `@smrt({ visibility: 'internal' | 'test' })` —
292
+ * they're plumbing / fixtures and never belong in admin nav.
293
+ * 3. Drop entries that don't expose a REST `list` route (`@smrt({ api: false })`,
294
+ * `include` without `list`, or `exclude` containing `list`).
295
+ * 4. Drop STI subtypes that share their parent's `collection` value —
296
+ * the REST endpoint at the shared collection URL is polymorphic and
297
+ * shows all subtypes through one link.
298
+ * 5. If `permittedResources` is provided, drop entries whose qualified
299
+ * name isn't in the list. Otherwise keep all remaining entries.
300
+ * 6. Group remaining entries by their resolved section title.
301
+ * 4. For each entry, emit a `NavItem` with:
302
+ * - `label` = `decoratorConfig.ui.label` ?? `pluralizeClassName(className)`
303
+ * - `href` = `${basePath}/${collection}` (defaults to `/api/v1/{collection}`)
304
+ * falling back to `${basePath}/{kebab-case-class}` if the
305
+ * manifest entry omits `collection`.
306
+ * - `icon` = `decoratorConfig.ui.icon` if present.
307
+ * 5. Sort items inside each section by label, then sort sections by title.
308
+ * The result is fully deterministic given a manifest input.
309
+ *
310
+ * @example Plug into RoleShell
311
+ * ```ts
312
+ * import { manifest } from '@my-app/manifest';
313
+ * import { navTreeFromManifest } from '@happyvertical/smrt-svelte/workspace';
314
+ *
315
+ * const sections = navTreeFromManifest(manifest, {
316
+ * sectionHints: {
317
+ * '@happyvertical/smrt-content': 'Content',
318
+ * '@happyvertical/smrt-commerce': 'Commerce',
319
+ * },
320
+ * });
321
+ * const roles: RoleConfig[] = [
322
+ * { id: 'admin', label: 'Admin', sections },
323
+ * ];
324
+ * ```
325
+ *
326
+ * @example Per-role filtering
327
+ * ```ts
328
+ * const editor = navTreeFromManifest(manifest, {
329
+ * permittedResources: [
330
+ * '@happyvertical/smrt-content:Article',
331
+ * '@happyvertical/smrt-content:Document',
332
+ * ],
333
+ * });
334
+ * // editor: [{ label: 'Content', children: [Articles, Documents] }]
335
+ * ```
336
+ *
337
+ * @example Unprefixed hrefs
338
+ * ```ts
339
+ * const sections = navTreeFromManifest(manifest, { basePath: '' });
340
+ * // items[0].href === '/articles' (not '/api/v1/articles')
341
+ * ```
342
+ */
343
+ export function navTreeFromManifest(manifest, options = {}) {
344
+ const { permittedResources, sectionHints, basePath = '/api/v1' } = options;
345
+ // Build the *effective* permitted set. When the caller permits a
346
+ // role to access an STI subtype (e.g. `@acme/shop:Cart`) but not
347
+ // its base (`Contract`), the role still needs the base's nav link
348
+ // because every STI subtype routes through the parent's shared
349
+ // collection URL. Walk each permitted qualifier up the inheritance
350
+ // chain — if any ancestor in the same manifest carries the same
351
+ // `collection` value, that ancestor is the actual link target, so
352
+ // add its qualifier to the permitted set. Without this remap the
353
+ // dedup step below would drop the subtype, the original permission
354
+ // check would drop the base, and the role would lose every link to
355
+ // the shared route.
356
+ const permitted = permittedResources
357
+ ? expandPermittedThroughStiParents(new Set(permittedResources), manifest)
358
+ : undefined;
359
+ // Group items by their resolved section title. Map preserves insertion
360
+ // order, but we sort the final output anyway so callers don't have to
361
+ // care about it.
362
+ const sectionMap = new Map();
363
+ for (const entry of Object.values(manifest.objects)) {
364
+ if (looksLikeCollectionClass(entry))
365
+ continue;
366
+ if (!isPublicEntry(entry))
367
+ continue;
368
+ if (!hasListRoute(entry))
369
+ continue;
370
+ if (looksLikeStiSubtypeOfParentRoute(entry, manifest))
371
+ continue;
372
+ const qualifier = entryQualifier(entry);
373
+ if (permitted && !permitted.has(qualifier))
374
+ continue;
375
+ const ui = entry.decoratorConfig?.ui;
376
+ const label = ui?.label ?? pluralizeClassName(entry.className);
377
+ const slug = entry.collection ?? classNameToKebab(entry.className);
378
+ const href = `${basePath}/${slug}`;
379
+ const item = {
380
+ href,
381
+ label,
382
+ };
383
+ if (ui?.icon)
384
+ item.icon = ui.icon;
385
+ const sectionTitle = deriveSectionTitle(entry, qualifier, sectionHints);
386
+ const bucket = sectionMap.get(sectionTitle);
387
+ if (bucket) {
388
+ bucket.push(item);
389
+ }
390
+ else {
391
+ sectionMap.set(sectionTitle, [item]);
392
+ }
393
+ }
394
+ // Deterministic ordering: sort items inside each section by label, then
395
+ // sort sections by title. Using `localeCompare` for predictable
396
+ // cross-locale behaviour without surprises around case.
397
+ const sections = [];
398
+ for (const [title, items] of sectionMap) {
399
+ items.sort((a, b) => a.label.localeCompare(b.label));
400
+ sections.push({
401
+ href: `#${title.toLowerCase().replace(/\s+/g, '-')}`,
402
+ label: title,
403
+ children: items,
404
+ });
405
+ }
406
+ sections.sort((a, b) => a.label.localeCompare(b.label));
407
+ return sections;
408
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Pure helpers for nav state derivation.
3
+ *
4
+ * Extracted so the recursive active/expanded logic can be unit-tested
5
+ * without needing to mount Svelte components.
6
+ *
7
+ * No DOM, no Svelte runes, no SvelteKit imports — these are SSR-safe and
8
+ * deterministic given their inputs.
9
+ */
10
+ import type { NavItem } from './types.js';
11
+ /**
12
+ * Default activeness check for a nav item against a current path.
13
+ *
14
+ * - Exact match → active
15
+ * - `item.exact === true` → only exact match counts
16
+ * - `item.href === '/'` → only matches `currentPath === '/'`; otherwise
17
+ * the descendant-path fallback would degrade to `startsWith('//')`
18
+ * and never fire (and we don't want every path counting the root as
19
+ * active by ancestry).
20
+ * - Otherwise, active when `currentPath` starts with `item.href + '/'`
21
+ * (the trailing slash avoids `/foo` matching `/foobar`).
22
+ */
23
+ export declare function defaultIsActive(item: NavItem, currentPath: string): boolean;
24
+ /**
25
+ * True if the item or any descendant is active under `isActive`.
26
+ */
27
+ export declare function isItemOrChildActive(item: NavItem, currentPath: string, isActive?: (item: NavItem, currentPath: string) => boolean): boolean;
28
+ /**
29
+ * True when an item's children container should render as expanded.
30
+ *
31
+ * Children are expanded when:
32
+ * - the item has children, AND
33
+ * - one of its descendants is active, OR `item.defaultExpanded` is set.
34
+ */
35
+ export declare function isExpanded(item: NavItem, currentPath: string, isActive?: (item: NavItem, currentPath: string) => boolean): boolean;
36
+ //# sourceMappingURL=nav-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nav-helpers.d.ts","sourceRoot":"","sources":["../../../src/components/workspace/nav-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAO3E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,GAAE,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,OAAyB,GAC1E,OAAO,CAQT;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,OAAO,EACb,WAAW,EAAE,MAAM,EACnB,QAAQ,GAAE,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,KAAK,OAAyB,GAC1E,OAAO,CAIT"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Pure helpers for nav state derivation.
3
+ *
4
+ * Extracted so the recursive active/expanded logic can be unit-tested
5
+ * without needing to mount Svelte components.
6
+ *
7
+ * No DOM, no Svelte runes, no SvelteKit imports — these are SSR-safe and
8
+ * deterministic given their inputs.
9
+ */
10
+ /**
11
+ * Default activeness check for a nav item against a current path.
12
+ *
13
+ * - Exact match → active
14
+ * - `item.exact === true` → only exact match counts
15
+ * - `item.href === '/'` → only matches `currentPath === '/'`; otherwise
16
+ * the descendant-path fallback would degrade to `startsWith('//')`
17
+ * and never fire (and we don't want every path counting the root as
18
+ * active by ancestry).
19
+ * - Otherwise, active when `currentPath` starts with `item.href + '/'`
20
+ * (the trailing slash avoids `/foo` matching `/foobar`).
21
+ */
22
+ export function defaultIsActive(item, currentPath) {
23
+ if (item.href === currentPath)
24
+ return true;
25
+ if (item.exact)
26
+ return false;
27
+ // The root href would produce `startsWith('//')`, which never matches a
28
+ // real path. Treat root as exact-only when not flagged with `exact`.
29
+ if (item.href === '/')
30
+ return false;
31
+ return currentPath.startsWith(`${item.href}/`);
32
+ }
33
+ /**
34
+ * True if the item or any descendant is active under `isActive`.
35
+ */
36
+ export function isItemOrChildActive(item, currentPath, isActive = defaultIsActive) {
37
+ if (isActive(item, currentPath))
38
+ return true;
39
+ if (item.children) {
40
+ for (const child of item.children) {
41
+ if (isItemOrChildActive(child, currentPath, isActive))
42
+ return true;
43
+ }
44
+ }
45
+ return false;
46
+ }
47
+ /**
48
+ * True when an item's children container should render as expanded.
49
+ *
50
+ * Children are expanded when:
51
+ * - the item has children, AND
52
+ * - one of its descendants is active, OR `item.defaultExpanded` is set.
53
+ */
54
+ export function isExpanded(item, currentPath, isActive = defaultIsActive) {
55
+ if (!item.children?.length)
56
+ return false;
57
+ if (item.defaultExpanded)
58
+ return true;
59
+ return isItemOrChildActive(item, currentPath, isActive);
60
+ }