@kitn.ai/ui 0.14.1

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 (370) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +394 -0
  3. package/dist/bash-InADTalH.js +1 -0
  4. package/dist/core-AYMC6_lb.js +12 -0
  5. package/dist/css-M7EaDHN_.js +1 -0
  6. package/dist/custom-elements.json +4739 -0
  7. package/dist/engine-javascript-vq0WuIJl.js +141 -0
  8. package/dist/github-dark-dimmed-DUshB20C.js +1 -0
  9. package/dist/github-light-JYsPkUQd.js +1 -0
  10. package/dist/host-embed.d.ts +33 -0
  11. package/dist/html-CPZ3oZQ7.js +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/javascript-C25yR2R2.js +1 -0
  14. package/dist/json-DxJze_jm.js +1 -0
  15. package/dist/kitn-chat-provider.d.ts +2 -0
  16. package/dist/kitn-chat-provider.es.js +240 -0
  17. package/dist/kitn-chat.es.js +77 -0
  18. package/dist/llms/llms-full.txt +1018 -0
  19. package/dist/llms/llms.txt +104 -0
  20. package/dist/origin.d.ts +5 -0
  21. package/dist/provider-runtime.d.ts +27 -0
  22. package/dist/schemas/card-envelope.schema.json +14 -0
  23. package/dist/schemas/card-event.schema.json +12 -0
  24. package/dist/schemas/choice.schema.json +70 -0
  25. package/dist/schemas/confirm.schema.json +65 -0
  26. package/dist/schemas/embed.schema.json +65 -0
  27. package/dist/schemas/form.result.schema.json +7 -0
  28. package/dist/schemas/form.schema.json +33 -0
  29. package/dist/schemas/link.schema.json +56 -0
  30. package/dist/schemas/tasks.result.schema.json +16 -0
  31. package/dist/schemas/tasks.schema.json +78 -0
  32. package/dist/svelte--5p79yCD.js +1 -0
  33. package/dist/theme.tokens.css +176 -0
  34. package/dist/tsx-B8rCNbgL.js +1 -0
  35. package/dist/typescript-RycA9KXf.js +1 -0
  36. package/dist/validate.d.ts +4 -0
  37. package/dist/version.d.ts +4 -0
  38. package/dist/vue-BmIZj4XD.js +1 -0
  39. package/dist/wire.d.ts +53 -0
  40. package/frameworks/react/index.tsx +948 -0
  41. package/frameworks/react/runtime.tsx +94 -0
  42. package/llms-full.txt +1018 -0
  43. package/llms.txt +104 -0
  44. package/package.json +141 -0
  45. package/src/components/artifact.stories.tsx +138 -0
  46. package/src/components/artifact.tsx +743 -0
  47. package/src/components/attachments.stories.tsx +303 -0
  48. package/src/components/attachments.test.tsx +64 -0
  49. package/src/components/attachments.tsx +399 -0
  50. package/src/components/card-fallback.tsx +28 -0
  51. package/src/components/card-renderer.tsx +52 -0
  52. package/src/components/card.tsx +110 -0
  53. package/src/components/chain-of-thought.stories.tsx +211 -0
  54. package/src/components/chain-of-thought.tsx +139 -0
  55. package/src/components/chat-container.stories.tsx +187 -0
  56. package/src/components/chat-container.tsx +82 -0
  57. package/src/components/chat-scope-picker.tsx +47 -0
  58. package/src/components/chat-thread.test.tsx +58 -0
  59. package/src/components/chat-thread.tsx +249 -0
  60. package/src/components/checkpoint.stories.tsx +102 -0
  61. package/src/components/checkpoint.tsx +88 -0
  62. package/src/components/choice-card.tsx +569 -0
  63. package/src/components/code-block.stories.tsx +150 -0
  64. package/src/components/code-block.tsx +105 -0
  65. package/src/components/component-meta.json +3735 -0
  66. package/src/components/confirm-card.stories.tsx +74 -0
  67. package/src/components/confirm-card.tsx +315 -0
  68. package/src/components/context.stories.tsx +229 -0
  69. package/src/components/context.test.tsx +133 -0
  70. package/src/components/context.tsx +363 -0
  71. package/src/components/conversation-item.stories.tsx +125 -0
  72. package/src/components/conversation-item.tsx +16 -0
  73. package/src/components/conversation-list.stories.tsx +133 -0
  74. package/src/components/conversation-list.tsx +101 -0
  75. package/src/components/default-input.test.tsx +111 -0
  76. package/src/components/embed.tsx +196 -0
  77. package/src/components/empty.stories.tsx +434 -0
  78. package/src/components/empty.tsx +166 -0
  79. package/src/components/feedback-bar.stories.tsx +138 -0
  80. package/src/components/feedback-bar.test.tsx +103 -0
  81. package/src/components/feedback-bar.tsx +166 -0
  82. package/src/components/file-tree.stories.tsx +73 -0
  83. package/src/components/file-tree.tsx +383 -0
  84. package/src/components/file-upload.stories.tsx +156 -0
  85. package/src/components/file-upload.tsx +161 -0
  86. package/src/components/form-widgets.tsx +461 -0
  87. package/src/components/form.tsx +873 -0
  88. package/src/components/image.stories.tsx +89 -0
  89. package/src/components/image.tsx +67 -0
  90. package/src/components/link-preview.tsx +194 -0
  91. package/src/components/loader.stories.tsx +181 -0
  92. package/src/components/loader.tsx +333 -0
  93. package/src/components/markdown.stories.tsx +180 -0
  94. package/src/components/markdown.tsx +81 -0
  95. package/src/components/message-action-bar.test.tsx +128 -0
  96. package/src/components/message-narrow.stories.tsx +329 -0
  97. package/src/components/message-skills.stories.tsx +211 -0
  98. package/src/components/message-skills.tsx +36 -0
  99. package/src/components/message.stories.tsx +281 -0
  100. package/src/components/message.tsx +227 -0
  101. package/src/components/model-switcher.stories.tsx +123 -0
  102. package/src/components/model-switcher.test.tsx +65 -0
  103. package/src/components/model-switcher.tsx +87 -0
  104. package/src/components/prompt-input.stories.tsx +222 -0
  105. package/src/components/prompt-input.tsx +211 -0
  106. package/src/components/prompt-suggestion.stories.tsx +142 -0
  107. package/src/components/prompt-suggestion.tsx +115 -0
  108. package/src/components/reasoning.stories.tsx +140 -0
  109. package/src/components/reasoning.tsx +156 -0
  110. package/src/components/resizable.test.tsx +226 -0
  111. package/src/components/response-stream.tsx +103 -0
  112. package/src/components/scroll-button.stories.tsx +100 -0
  113. package/src/components/scroll-button.tsx +34 -0
  114. package/src/components/slash-command.stories.tsx +163 -0
  115. package/src/components/slash-command.tsx +232 -0
  116. package/src/components/source-list.test.tsx +152 -0
  117. package/src/components/source.stories.tsx +124 -0
  118. package/src/components/source.tsx +129 -0
  119. package/src/components/tasks-card.stories.tsx +78 -0
  120. package/src/components/tasks-card.tsx +432 -0
  121. package/src/components/text-shimmer.stories.tsx +87 -0
  122. package/src/components/text-shimmer.tsx +37 -0
  123. package/src/components/thinking-bar.stories.tsx +87 -0
  124. package/src/components/thinking-bar.tsx +50 -0
  125. package/src/components/tool.stories.tsx +153 -0
  126. package/src/components/tool.tsx +184 -0
  127. package/src/components/use-card-resolution.ts +28 -0
  128. package/src/components/voice-input.stories.tsx +83 -0
  129. package/src/components/voice-input.tsx +107 -0
  130. package/src/elements/artifact.stories.tsx +506 -0
  131. package/src/elements/artifact.tsx +137 -0
  132. package/src/elements/attachments.stories.tsx +190 -0
  133. package/src/elements/attachments.tsx +136 -0
  134. package/src/elements/card.stories.tsx +113 -0
  135. package/src/elements/card.tsx +40 -0
  136. package/src/elements/cards.stories.tsx +59 -0
  137. package/src/elements/cards.tsx +97 -0
  138. package/src/elements/catalog.stories.tsx +491 -0
  139. package/src/elements/chain-of-thought.declarative.test.tsx +153 -0
  140. package/src/elements/chain-of-thought.stories.tsx +113 -0
  141. package/src/elements/chain-of-thought.tsx +92 -0
  142. package/src/elements/chat-scope-picker.stories.tsx +73 -0
  143. package/src/elements/chat-scope-picker.tsx +36 -0
  144. package/src/elements/chat-types.ts +42 -0
  145. package/src/elements/chat-workspace.stories.tsx +288 -0
  146. package/src/elements/chat-workspace.tsx +161 -0
  147. package/src/elements/chat.stories.tsx +151 -0
  148. package/src/elements/chat.tsx +80 -0
  149. package/src/elements/checkpoint.stories.tsx +72 -0
  150. package/src/elements/checkpoint.tsx +47 -0
  151. package/src/elements/choice.stories.tsx +240 -0
  152. package/src/elements/choice.tsx +48 -0
  153. package/src/elements/code-block.stories.tsx +83 -0
  154. package/src/elements/code-block.tsx +42 -0
  155. package/src/elements/compiled.css +2 -0
  156. package/src/elements/composed-shell.stories.tsx +316 -0
  157. package/src/elements/confirm-card.stories.tsx +227 -0
  158. package/src/elements/confirm-card.tsx +51 -0
  159. package/src/elements/context-meter.stories.tsx +151 -0
  160. package/src/elements/context-meter.tsx +121 -0
  161. package/src/elements/conversation-list.declarative.test.tsx +137 -0
  162. package/src/elements/conversation-list.stories.tsx +243 -0
  163. package/src/elements/conversation-list.tsx +80 -0
  164. package/src/elements/css.ts +5 -0
  165. package/src/elements/default-input.tsx +247 -0
  166. package/src/elements/define.tsx +171 -0
  167. package/src/elements/element-meta.json +3314 -0
  168. package/src/elements/element-types.d.ts +710 -0
  169. package/src/elements/embed.stories.tsx +213 -0
  170. package/src/elements/embed.tsx +35 -0
  171. package/src/elements/empty.stories.tsx +111 -0
  172. package/src/elements/empty.tsx +29 -0
  173. package/src/elements/feedback-bar.stories.tsx +114 -0
  174. package/src/elements/feedback-bar.tsx +61 -0
  175. package/src/elements/file-tree.stories.tsx +134 -0
  176. package/src/elements/file-tree.tsx +52 -0
  177. package/src/elements/file-upload.stories.tsx +82 -0
  178. package/src/elements/file-upload.tsx +44 -0
  179. package/src/elements/form.stories.tsx +248 -0
  180. package/src/elements/form.tsx +43 -0
  181. package/src/elements/framework-usage.json +563 -0
  182. package/src/elements/image.stories.tsx +71 -0
  183. package/src/elements/image.tsx +32 -0
  184. package/src/elements/link-preview.stories.tsx +209 -0
  185. package/src/elements/link-preview.tsx +34 -0
  186. package/src/elements/loader.stories.tsx +88 -0
  187. package/src/elements/loader.tsx +25 -0
  188. package/src/elements/markdown.stories.tsx +76 -0
  189. package/src/elements/markdown.tsx +38 -0
  190. package/src/elements/message-skills.declarative.test.tsx +104 -0
  191. package/src/elements/message-skills.stories.tsx +114 -0
  192. package/src/elements/message-skills.tsx +85 -0
  193. package/src/elements/message.stories.tsx +218 -0
  194. package/src/elements/message.tsx +162 -0
  195. package/src/elements/model-switcher.declarative.test.tsx +130 -0
  196. package/src/elements/model-switcher.stories.tsx +198 -0
  197. package/src/elements/model-switcher.tsx +93 -0
  198. package/src/elements/popover.stories.tsx +80 -0
  199. package/src/elements/popover.tsx +59 -0
  200. package/src/elements/prompt-input-slash-command.test.tsx +128 -0
  201. package/src/elements/prompt-input-stoppable.test.tsx +88 -0
  202. package/src/elements/prompt-input-toolbar-actions.test.tsx +120 -0
  203. package/src/elements/prompt-input.stories.tsx +331 -0
  204. package/src/elements/prompt-input.tsx +185 -0
  205. package/src/elements/prompt-suggestions.declarative.test.tsx +164 -0
  206. package/src/elements/prompt-suggestions.stories.tsx +213 -0
  207. package/src/elements/prompt-suggestions.tsx +94 -0
  208. package/src/elements/reasoning.stories.tsx +77 -0
  209. package/src/elements/reasoning.tsx +50 -0
  210. package/src/elements/register.ts +56 -0
  211. package/src/elements/remote.stories.tsx +314 -0
  212. package/src/elements/remote.tsx +221 -0
  213. package/src/elements/resizable.d.ts +27 -0
  214. package/src/elements/resizable.stories.tsx +426 -0
  215. package/src/elements/resizable.tsx +553 -0
  216. package/src/elements/response-stream.stories.tsx +79 -0
  217. package/src/elements/response-stream.tsx +40 -0
  218. package/src/elements/scroll-button.stories.tsx +202 -0
  219. package/src/elements/scroll-button.test.tsx +148 -0
  220. package/src/elements/scroll-button.tsx +126 -0
  221. package/src/elements/source-list.stories.tsx +143 -0
  222. package/src/elements/source.stories.tsx +88 -0
  223. package/src/elements/source.tsx +120 -0
  224. package/src/elements/styles.css +199 -0
  225. package/src/elements/switch.stories.tsx +56 -0
  226. package/src/elements/switch.tsx +43 -0
  227. package/src/elements/tasks.stories.tsx +237 -0
  228. package/src/elements/tasks.tsx +46 -0
  229. package/src/elements/text-shimmer.stories.tsx +64 -0
  230. package/src/elements/text-shimmer.tsx +28 -0
  231. package/src/elements/thinking-bar.stories.tsx +72 -0
  232. package/src/elements/thinking-bar.tsx +34 -0
  233. package/src/elements/tool.stories.tsx +89 -0
  234. package/src/elements/tool.tsx +23 -0
  235. package/src/elements/voice-input.stories.tsx +88 -0
  236. package/src/elements/voice-input.tsx +41 -0
  237. package/src/index.ts +253 -0
  238. package/src/primitives/card-contract.ts +71 -0
  239. package/src/primitives/card-host.tsx +35 -0
  240. package/src/primitives/card-registry.tsx +67 -0
  241. package/src/primitives/card-resolution.ts +36 -0
  242. package/src/primitives/card-routing.ts +79 -0
  243. package/src/primitives/card-schemas/card-envelope.schema.json +14 -0
  244. package/src/primitives/card-schemas/card-event.schema.json +12 -0
  245. package/src/primitives/card-schemas/choice.schema.json +70 -0
  246. package/src/primitives/card-schemas/confirm.schema.json +65 -0
  247. package/src/primitives/card-schemas/embed.schema.json +65 -0
  248. package/src/primitives/card-schemas/form.result.schema.json +7 -0
  249. package/src/primitives/card-schemas/form.schema.json +33 -0
  250. package/src/primitives/card-schemas/link.schema.json +56 -0
  251. package/src/primitives/card-schemas/tasks.result.schema.json +16 -0
  252. package/src/primitives/card-schemas/tasks.schema.json +78 -0
  253. package/src/primitives/card-validate.ts +95 -0
  254. package/src/primitives/chat-config.tsx +76 -0
  255. package/src/primitives/controllable.test.ts +47 -0
  256. package/src/primitives/controllable.ts +21 -0
  257. package/src/primitives/embed-providers.ts +254 -0
  258. package/src/primitives/highlighter.ts +157 -0
  259. package/src/primitives/link-preview.ts +87 -0
  260. package/src/primitives/pdf-preview.ts +121 -0
  261. package/src/primitives/use-auto-resize.ts +31 -0
  262. package/src/primitives/use-resize-observer.ts +12 -0
  263. package/src/primitives/use-stick-to-bottom.ts +43 -0
  264. package/src/primitives/use-text-stream.ts +112 -0
  265. package/src/primitives/use-voice-recorder.ts +50 -0
  266. package/src/remote/host-embed.ts +345 -0
  267. package/src/remote/index.ts +2 -0
  268. package/src/remote/origin.ts +30 -0
  269. package/src/remote/provider-runtime.ts +259 -0
  270. package/src/remote/provider.ts +2 -0
  271. package/src/remote/validate.ts +22 -0
  272. package/src/remote/version.ts +12 -0
  273. package/src/remote/wire.ts +48 -0
  274. package/src/stories/chat-panel-layout.stories.tsx +145 -0
  275. package/src/stories/chat-scene.tsx +571 -0
  276. package/src/stories/checkpoint-restore.stories.tsx +256 -0
  277. package/src/stories/context-usage.stories.tsx +212 -0
  278. package/src/stories/conversation-with-reasoning.stories.tsx +182 -0
  279. package/src/stories/conversation-with-sources.stories.tsx +471 -0
  280. package/src/stories/docs/Accessibility.mdx +119 -0
  281. package/src/stories/docs/ChildElements.mdx +269 -0
  282. package/src/stories/docs/ForAIAgents.mdx +162 -0
  283. package/src/stories/docs/GettingStarted.mdx +65 -0
  284. package/src/stories/docs/Installation.mdx +83 -0
  285. package/src/stories/docs/Introduction.mdx +50 -0
  286. package/src/stories/docs/SolidJsAdvanced.mdx +17 -0
  287. package/src/stories/docs/Theming.mdx +85 -0
  288. package/src/stories/docs/element-controls.ts +121 -0
  289. package/src/stories/docs/frameworks/Angular.mdx +245 -0
  290. package/src/stories/docs/frameworks/Html.mdx +227 -0
  291. package/src/stories/docs/frameworks/Overview.mdx +47 -0
  292. package/src/stories/docs/frameworks/React.mdx +227 -0
  293. package/src/stories/docs/frameworks/Solid.mdx +173 -0
  294. package/src/stories/docs/frameworks/Svelte.mdx +194 -0
  295. package/src/stories/docs/frameworks/Vue.mdx +238 -0
  296. package/src/stories/docs/generative-ui-overview.mdx +186 -0
  297. package/src/stories/docs/recipes/SpeechToText.mdx +7 -0
  298. package/src/stories/docs/recipes/Streaming.mdx +68 -0
  299. package/src/stories/docs/recipes/TextToSpeech.mdx +35 -0
  300. package/src/stories/docs/theme-editor/canvas.tsx +32 -0
  301. package/src/stories/docs/theme-editor/inspector.tsx +66 -0
  302. package/src/stories/docs/theme-editor/presets.test.ts +32 -0
  303. package/src/stories/docs/theme-editor/presets.ts +64 -0
  304. package/src/stories/docs/theme-editor/theme-css.test.ts +19 -0
  305. package/src/stories/docs/theme-editor/theme-css.ts +15 -0
  306. package/src/stories/docs/theme-editor/theme-editor.tsx +146 -0
  307. package/src/stories/docs/theme-tokens.tsx +174 -0
  308. package/src/stories/examples/ChoosingComponents.mdx +106 -0
  309. package/src/stories/examples/sample-data.ts +79 -0
  310. package/src/stories/examples/usage/checkpoint-restore.ts +156 -0
  311. package/src/stories/examples/usage/context-usage.ts +591 -0
  312. package/src/stories/examples/usage/conversation-with-reasoning.ts +224 -0
  313. package/src/stories/examples/usage/conversation-with-sources.ts +589 -0
  314. package/src/stories/examples/usage/empty-state.ts +144 -0
  315. package/src/stories/examples/usage/full-chat-app.ts +277 -0
  316. package/src/stories/examples/usage/index.ts +63 -0
  317. package/src/stories/examples/usage/message-actions.ts +704 -0
  318. package/src/stories/examples/usage/pattern-centered-conversation.ts +446 -0
  319. package/src/stories/examples/usage/pattern-chat-panel-layout.ts +442 -0
  320. package/src/stories/examples/usage/pattern-docked-widget.ts +479 -0
  321. package/src/stories/examples/usage/prompt-input-variants.ts +1356 -0
  322. package/src/stories/examples/usage/streaming-response.ts +687 -0
  323. package/src/stories/examples/usage/types.ts +33 -0
  324. package/src/stories/full-chat.stories.tsx +18 -0
  325. package/src/stories/message-actions.stories.tsx +230 -0
  326. package/src/stories/pattern-centered-conversation.stories.tsx +93 -0
  327. package/src/stories/pattern-docked-widget.stories.tsx +93 -0
  328. package/src/stories/pattern-empty-state.stories.tsx +76 -0
  329. package/src/stories/prompt-input-variants.stories.tsx +456 -0
  330. package/src/stories/streaming-response.stories.tsx +438 -0
  331. package/src/stories/theme-editor.stories.tsx +16 -0
  332. package/src/stories/token-reference.stories.tsx +18 -0
  333. package/src/stories/typography.stories.tsx +78 -0
  334. package/src/types.ts +47 -0
  335. package/src/ui/action-icons.ts +52 -0
  336. package/src/ui/avatar.stories.tsx +103 -0
  337. package/src/ui/avatar.tsx +23 -0
  338. package/src/ui/badge.stories.tsx +86 -0
  339. package/src/ui/badge.tsx +21 -0
  340. package/src/ui/button.stories.tsx +145 -0
  341. package/src/ui/button.tsx +38 -0
  342. package/src/ui/collapsible.stories.tsx +69 -0
  343. package/src/ui/collapsible.tsx +125 -0
  344. package/src/ui/dropdown.stories.tsx +59 -0
  345. package/src/ui/dropdown.tsx +196 -0
  346. package/src/ui/hover-card.stories.tsx +77 -0
  347. package/src/ui/hover-card.test.tsx +30 -0
  348. package/src/ui/hover-card.tsx +172 -0
  349. package/src/ui/overlay.stories.tsx +115 -0
  350. package/src/ui/overlay.tsx +158 -0
  351. package/src/ui/popover.stories.tsx +81 -0
  352. package/src/ui/popover.test.tsx +99 -0
  353. package/src/ui/popover.tsx +92 -0
  354. package/src/ui/resizable.stories.tsx +236 -0
  355. package/src/ui/resizable.tsx +576 -0
  356. package/src/ui/scroll-area.stories.tsx +50 -0
  357. package/src/ui/scroll-area.tsx +15 -0
  358. package/src/ui/separator.stories.tsx +81 -0
  359. package/src/ui/separator.tsx +10 -0
  360. package/src/ui/skeleton.stories.tsx +337 -0
  361. package/src/ui/skeleton.tsx +16 -0
  362. package/src/ui/switch.stories.tsx +51 -0
  363. package/src/ui/switch.test.tsx +59 -0
  364. package/src/ui/switch.tsx +62 -0
  365. package/src/ui/textarea.stories.tsx +76 -0
  366. package/src/ui/textarea.tsx +21 -0
  367. package/src/ui/tooltip.stories.tsx +74 -0
  368. package/src/ui/tooltip.tsx +68 -0
  369. package/src/utils/cn.ts +24 -0
  370. package/theme.css +187 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Unit tests for the declarative `<kai-step>` light-DOM API of
3
+ * `<kai-chain-of-thought>`.
4
+ *
5
+ * Strategy: the `defineWebComponent` call registers a real Shadow-DOM custom
6
+ * element which requires a full browser environment (Constructable Stylesheets,
7
+ * shadow roots, etc.) and is therefore not suitable for jsdom unit tests.
8
+ * Instead we:
9
+ * 1. Test the exported `parseKaiStepElement` helper in isolation — it has no
10
+ * DOM dependencies beyond `Element`, which jsdom provides perfectly.
11
+ * 2. Test that the merged list of prop + slotted steps renders correctly via
12
+ * the `ChainOfThoughtStep`/`ChainOfThoughtTrigger` primitives, mirroring
13
+ * the pattern used in `prompt-suggestions.declarative.test.tsx`.
14
+ */
15
+ import { describe, it, expect, afterEach } from 'vitest';
16
+ import '@testing-library/jest-dom/vitest';
17
+ import { render, cleanup } from '@solidjs/testing-library';
18
+ import { For, Show } from 'solid-js';
19
+ import { parseKaiStepElement } from './chain-of-thought';
20
+
21
+ afterEach(cleanup);
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // parseKaiStepElement — pure helper
25
+ // ---------------------------------------------------------------------------
26
+
27
+ describe('parseKaiStepElement', () => {
28
+ function makeEl(label: string | null, textContent?: string): Element {
29
+ const el = document.createElement('kai-step');
30
+ if (label !== null) el.setAttribute('label', label);
31
+ if (textContent !== undefined) el.textContent = textContent;
32
+ return el;
33
+ }
34
+
35
+ it('maps label attribute to Step.label', () => {
36
+ const step = parseKaiStepElement(makeEl('Searching docs', 'Found 3 hits'));
37
+ expect(step.label).toBe('Searching docs');
38
+ });
39
+
40
+ it('maps trimmed textContent to Step.content', () => {
41
+ const step = parseKaiStepElement(makeEl('Step', ' Found 3 hits '));
42
+ expect(step.content).toBe('Found 3 hits');
43
+ });
44
+
45
+ it('returns undefined for content when textContent is empty', () => {
46
+ const step = parseKaiStepElement(makeEl('Build & verify', ''));
47
+ expect(step.content).toBeUndefined();
48
+ });
49
+
50
+ it('returns undefined for content when textContent is whitespace only', () => {
51
+ const step = parseKaiStepElement(makeEl('Build & verify', ' '));
52
+ expect(step.content).toBeUndefined();
53
+ });
54
+
55
+ it('falls back to empty string for label when attribute is absent', () => {
56
+ const step = parseKaiStepElement(makeEl(null, 'some detail'));
57
+ expect(step.label).toBe('');
58
+ });
59
+
60
+ it('returns a well-formed Step for a heading-only element', () => {
61
+ const step = parseKaiStepElement(makeEl('Understand the request'));
62
+ expect(step).toEqual({ label: 'Understand the request', content: undefined });
63
+ });
64
+
65
+ it('returns a well-formed Step for a step with detail', () => {
66
+ const step = parseKaiStepElement(makeEl('Searching docs', 'Found 3 hits'));
67
+ expect(step).toEqual({ label: 'Searching docs', content: 'Found 3 hits' });
68
+ });
69
+ });
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Merge + render: prop steps + slotted steps combine correctly
73
+ // ---------------------------------------------------------------------------
74
+
75
+ interface Step {
76
+ label: string;
77
+ content?: string;
78
+ }
79
+
80
+ /**
81
+ * Minimal test harness that renders a list of Steps as labelled divs,
82
+ * mirroring the merge logic in chain-of-thought.tsx. We render plain divs
83
+ * rather than the full Shadow DOM component to stay within jsdom.
84
+ */
85
+ function StepList(props: { items: Step[] }) {
86
+ return (
87
+ <div>
88
+ <For each={props.items}>
89
+ {(step) => (
90
+ <div data-testid="step">
91
+ <span data-testid="step-label">{step.label}</span>
92
+ <Show when={step.content}>
93
+ <span data-testid="step-content">{step.content}</span>
94
+ </Show>
95
+ </div>
96
+ )}
97
+ </For>
98
+ </div>
99
+ );
100
+ }
101
+
102
+ describe('Step list rendering', () => {
103
+ it('renders one entry per step', () => {
104
+ const steps: Step[] = [
105
+ { label: 'Understand the request', content: 'The user wants composable web components.' },
106
+ { label: 'Design the API', content: 'Route 1: variant + flags.' },
107
+ { label: 'Build & verify' },
108
+ ];
109
+ const { getAllByTestId } = render(() => <StepList items={steps} />);
110
+ expect(getAllByTestId('step')).toHaveLength(3);
111
+ });
112
+
113
+ it('renders the label for each step', () => {
114
+ const steps: Step[] = [{ label: 'Searching docs' }, { label: 'Synthesising' }];
115
+ const { getAllByTestId } = render(() => <StepList items={steps} />);
116
+ const labels = getAllByTestId('step-label').map((el) => el.textContent);
117
+ expect(labels).toEqual(['Searching docs', 'Synthesising']);
118
+ });
119
+
120
+ it('renders content only when present', () => {
121
+ const steps: Step[] = [
122
+ { label: 'With detail', content: 'Found 3 hits' },
123
+ { label: 'No detail' },
124
+ ];
125
+ const { getAllByTestId, queryAllByTestId } = render(() => <StepList items={steps} />);
126
+ const contents = queryAllByTestId('step-content');
127
+ expect(contents).toHaveLength(1);
128
+ expect(contents[0]).toHaveTextContent('Found 3 hits');
129
+ expect(getAllByTestId('step')).toHaveLength(2);
130
+ });
131
+
132
+ it('renders prop steps before declarative (slotted) steps', () => {
133
+ const propSteps: Step[] = [{ label: 'Prop step' }];
134
+ const slottedSteps: Step[] = [{ label: 'Slotted step' }];
135
+ const merged = [...propSteps, ...slottedSteps];
136
+
137
+ const { getAllByTestId } = render(() => <StepList items={merged} />);
138
+ const labels = getAllByTestId('step-label').map((el) => el.textContent);
139
+ expect(labels).toEqual(['Prop step', 'Slotted step']);
140
+ });
141
+
142
+ it('parseKaiStepElement produces steps that render with the correct label and content', () => {
143
+ const el = document.createElement('kai-step');
144
+ el.setAttribute('label', 'Searching docs');
145
+ el.textContent = 'Found 3 hits';
146
+
147
+ const step = parseKaiStepElement(el);
148
+ const { getByTestId } = render(() => <StepList items={[step]} />);
149
+
150
+ expect(getByTestId('step-label')).toHaveTextContent('Searching docs');
151
+ expect(getByTestId('step-content')).toHaveTextContent('Found 3 hits');
152
+ });
153
+ });
@@ -0,0 +1,113 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
5
+
6
+ interface Step {
7
+ label: string;
8
+ content?: string;
9
+ }
10
+
11
+ // The web components are custom DOM elements, so declare the tags for JSX.
12
+ declare module 'solid-js' {
13
+ // eslint-disable-next-line @typescript-eslint/no-namespace
14
+ namespace JSX {
15
+ interface IntrinsicElements {
16
+ 'kai-chain-of-thought': JSX.HTMLAttributes<HTMLElement>;
17
+ /** Light-DOM data carrier for declarative steps inside `<kai-chain-of-thought>`. */
18
+ 'kai-step': JSX.HTMLAttributes<HTMLElement> & { label?: string };
19
+ }
20
+ }
21
+ }
22
+
23
+ const steps: Step[] = [
24
+ { label: 'Understand the request', content: 'The user wants a composable set of web components.' },
25
+ { label: 'Design the API', content: 'Route 1: variant + flags + events; rich data via properties.' },
26
+ { label: 'Build & verify' },
27
+ ];
28
+
29
+ /** Render `<kai-chain-of-thought>` with the `steps` set as a JS property. */
30
+ function CotElement(props: { steps: Step[] }) {
31
+ let el: (HTMLElement & { steps?: Step[] }) | undefined;
32
+ onMount(() => {
33
+ if (el) el.steps = props.steps;
34
+ });
35
+ return (
36
+ <kai-chain-of-thought ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '24px', 'max-width': '560px' }} />
37
+ );
38
+ }
39
+
40
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
41
+ <kai-chain-of-thought id="cot"></kai-chain-of-thought>
42
+
43
+ <script type="module">
44
+ import '@kitn.ai/ui/elements'; // registers the custom elements
45
+
46
+ document.getElementById('cot').steps = [
47
+ { label: 'Understand the request', content: 'The user wants a composable set.' },
48
+ { label: 'Design the API', content: 'Route 1: variant + flags + events.' },
49
+ { label: 'Build & verify' },
50
+ ];
51
+ </script>`;
52
+
53
+ const meta = {
54
+ title: 'Components/ChainOfThought',
55
+ tags: ['autodocs'],
56
+ argTypes: argTypesFor('kai-chain-of-thought'),
57
+ parameters: {
58
+ layout: 'fullscreen',
59
+ docs: {
60
+ description: specDescription('kai-chain-of-thought', [
61
+ '`<kai-chain-of-thought>` is the framework-agnostic **web component** for step-by-step reasoning — a connected list of steps, each with optional collapsible detail — isolated in **Shadow DOM**. The compound primitive collapses to a single `steps` data model (Route 1).',
62
+ '**When to use:** surfacing an agent\'s plan or reasoning trace in a non-Solid app. In SolidJS, compose the `ChainOfThought` primitives for finer control.',
63
+ '**Placement:** as a block above or below a message, or in a dedicated reasoning panel; it is `display: block` and sizes to content height, so no fixed height is required.',
64
+ "**How to use:** register once with `import '@kitn.ai/ui/elements'`. **Property API:** set the `steps` **property** — an array of `{ label, content? }` objects; steps with `content` become expandable. **Declarative API:** compose `<kai-step label=\"…\">…detail…</kai-step>` child elements directly in the HTML — the `label` attribute becomes the heading, `textContent` the optional expandable detail. Declarative children are merged after any prop steps.",
65
+ 'See the **Code** tab for HTML usage.',
66
+ ]),
67
+ },
68
+ },
69
+ } satisfies Meta;
70
+
71
+ export default meta;
72
+ type Story = StoryObj;
73
+
74
+ /** A three-step reasoning trace; the last step has no detail. */
75
+ export const Default: Story = {
76
+ render: () => <CotElement steps={steps} />,
77
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
78
+ };
79
+
80
+ const DECLARATIVE_HTML_SNIPPET = `<!-- Works in any framework or plain HTML — no JS property assignment needed -->
81
+ <kai-chain-of-thought>
82
+ <kai-step label="Understand the request">The user wants a composable set of web components.</kai-step>
83
+ <kai-step label="Design the API">Route 1: variant + flags + events; rich data via properties.</kai-step>
84
+ <kai-step label="Build &amp; verify"></kai-step>
85
+ </kai-chain-of-thought>
86
+
87
+ <script type="module">
88
+ import '@kitn.ai/ui/elements'; // registers the custom elements
89
+ </script>`;
90
+
91
+ /**
92
+ * Declare each reasoning step as a `<kai-step>` child element — no `steps`
93
+ * property or JS array wiring needed. The `label` attribute becomes the
94
+ * step heading; `textContent` becomes the optional expandable detail.
95
+ * Children are light-DOM data carriers hidden by the Shadow DOM — pure data,
96
+ * no visible output of their own.
97
+ * Mix with the `steps` prop: prop items render first, declarative children after.
98
+ */
99
+ export const DeclarativeSteps: Story = {
100
+ name: 'Declarative Steps (kai-step)',
101
+ render: () => (
102
+ <kai-chain-of-thought style={{ display: 'block', padding: '24px', 'max-width': '560px' }}>
103
+ <kai-step label="Understand the request">The user wants a composable set of web components.</kai-step>
104
+ <kai-step label="Design the API">Route 1: variant + flags + events; rich data via properties.</kai-step>
105
+ <kai-step label="Build &amp; verify"></kai-step>
106
+ </kai-chain-of-thought>
107
+ ),
108
+ parameters: {
109
+ docs: {
110
+ source: { code: DECLARATIVE_HTML_SNIPPET, language: 'html' },
111
+ },
112
+ },
113
+ };
@@ -0,0 +1,92 @@
1
+ import { For, Show, createSignal, onMount, onCleanup } from 'solid-js';
2
+ import { defineWebComponent } from './define';
3
+ import {
4
+ ChainOfThought,
5
+ ChainOfThoughtStep,
6
+ ChainOfThoughtTrigger,
7
+ ChainOfThoughtContent,
8
+ ChainOfThoughtItem,
9
+ } from '../components/chain-of-thought';
10
+
11
+ interface Step {
12
+ /** The step's heading (the always-visible trigger). */
13
+ label: string;
14
+ /** Optional expandable detail. */
15
+ content?: string;
16
+ }
17
+
18
+ interface Props extends Record<string, unknown> {
19
+ /** The reasoning steps. Set as a JS property. Compound sub-parts collapse to
20
+ * this one data model (Route 1). */
21
+ steps: Step[];
22
+ }
23
+
24
+ /** Parse a single light-DOM `<kai-step>` element into a `Step` descriptor.
25
+ * Attribute mapping:
26
+ * - `label` → Step.label (the always-visible heading)
27
+ * - textContent → Step.content (optional expandable detail)
28
+ */
29
+ export function parseKaiStepElement(n: Element): Step {
30
+ return {
31
+ label: n.getAttribute('label') ?? '',
32
+ content: n.textContent?.trim() || undefined,
33
+ };
34
+ }
35
+
36
+ /**
37
+ * `<kai-chain-of-thought>` — step-by-step reasoning with connectors and
38
+ * per-step collapsible detail.
39
+ *
40
+ * **Route 1 — JS property:** set the `steps` property to an array of
41
+ * `{ label, content? }` objects.
42
+ *
43
+ * **Route 2 — declarative children:** compose `<kai-step>` child elements in
44
+ * light DOM (hidden by the Shadow DOM — pure data carriers). The `label`
45
+ * attribute becomes the step heading; `textContent` becomes the expandable
46
+ * detail. Children are merged after any prop steps.
47
+ *
48
+ * ```html
49
+ * <kai-chain-of-thought>
50
+ * <kai-step label="Understand the request">The user wants composable web components.</kai-step>
51
+ * <kai-step label="Design the API">Route 1: variant + flags; rich data via properties.</kai-step>
52
+ * <kai-step label="Build & verify"></kai-step>
53
+ * </kai-chain-of-thought>
54
+ * ```
55
+ */
56
+ defineWebComponent<Props>('kai-chain-of-thought', {
57
+ steps: [],
58
+ }, (props, { element }) => {
59
+ // Read declarative <kai-step> children from light DOM.
60
+ // Shadow DOM with no <slot> suppresses them visually — they're invisible data carriers.
61
+ const [slottedSteps, setSlottedSteps] = createSignal<Step[]>([]);
62
+ onMount(() => {
63
+ const read = () => {
64
+ const nodes = [...element.querySelectorAll('kai-step')];
65
+ setSlottedSteps(nodes.map(parseKaiStepElement));
66
+ };
67
+ read();
68
+ const observer = new MutationObserver(read);
69
+ observer.observe(element, { childList: true, attributes: true, subtree: true });
70
+ onCleanup(() => observer.disconnect());
71
+ });
72
+
73
+ // Prop steps first; declarative children appended after.
74
+ const allSteps = () => [...props.steps, ...slottedSteps()];
75
+
76
+ return (
77
+ <ChainOfThought>
78
+ <For each={allSteps()}>
79
+ {(step, i) => (
80
+ <ChainOfThoughtStep isLast={i() === allSteps().length - 1}>
81
+ <ChainOfThoughtTrigger>{step.label}</ChainOfThoughtTrigger>
82
+ <Show when={step.content}>
83
+ <ChainOfThoughtContent>
84
+ <ChainOfThoughtItem>{step.content}</ChainOfThoughtItem>
85
+ </ChainOfThoughtContent>
86
+ </Show>
87
+ </ChainOfThoughtStep>
88
+ )}
89
+ </For>
90
+ </ChainOfThought>
91
+ );
92
+ });
@@ -0,0 +1,73 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
5
+
6
+ // The web components are custom DOM elements, so declare the tags for JSX.
7
+ declare module 'solid-js' {
8
+ // eslint-disable-next-line @typescript-eslint/no-namespace
9
+ namespace JSX {
10
+ interface IntrinsicElements {
11
+ 'kai-scope-picker': JSX.HTMLAttributes<HTMLElement> & {
12
+ 'current-label'?: string;
13
+ };
14
+ }
15
+ }
16
+ }
17
+
18
+ /** Render `<kai-scope-picker>` with author/tag options set as properties. */
19
+ function ScopePickerElement(props: { authors: string[]; tags: string[] }) {
20
+ let el: (HTMLElement & { availableAuthors?: string[]; availableTags?: string[] }) | undefined;
21
+ onMount(() => {
22
+ if (!el) return;
23
+ el.availableAuthors = props.authors;
24
+ el.availableTags = props.tags;
25
+ el.addEventListener('kai-scope-change', (e) => {
26
+ const ev = e as CustomEvent<{ filters: unknown }>;
27
+ console.log('kai-scope-change', ev.detail.filters ?? 'all');
28
+ });
29
+ });
30
+ return (
31
+ <kai-scope-picker ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '40px' }} />
32
+ );
33
+ }
34
+
35
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
36
+ <kai-scope-picker id="scope"></kai-scope-picker>
37
+
38
+ <script type="module">
39
+ import '@kitn.ai/ui/elements'; // registers the custom elements
40
+
41
+ const scope = document.getElementById('scope');
42
+ scope.availableAuthors = ['Rob', 'Alex'];
43
+ scope.availableTags = ['design', 'api'];
44
+ // undefined filters means "All Content"
45
+ scope.addEventListener('kai-scope-change', (e) => console.log(e.detail.filters ?? 'all'));
46
+ </script>`;
47
+
48
+ const meta = {
49
+ title: 'Components/ScopePicker',
50
+ tags: ['autodocs'],
51
+ argTypes: argTypesFor('kai-scope-picker'),
52
+ parameters: {
53
+ layout: 'fullscreen',
54
+ docs: {
55
+ description: specDescription('kai-scope-picker', [
56
+ '`<kai-scope-picker>` is the framework-agnostic **web component** for scoping a chat by author or tag — a dropdown that emits the chosen filters — isolated in **Shadow DOM**.',
57
+ '**When to use:** letting users narrow a conversation/search to a subset of content. In SolidJS, use the `ChatScopePicker` primitive.',
58
+ '**Placement:** in the chat header or above the conversation list, beside the search input; it is a compact `inline-block` dropdown trigger that pairs naturally with `<kai-model-switcher>` or other header controls.',
59
+ "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the `availableAuthors` / `availableTags` **properties** (and optionally `current-label`), and listen for the `kai-scope-change` **CustomEvent** (`undefined` filters = \"All Content\").",
60
+ 'See the **Code** tab for HTML usage.',
61
+ ]),
62
+ },
63
+ },
64
+ } satisfies Meta;
65
+
66
+ export default meta;
67
+ type Story = StoryObj;
68
+
69
+ /** Authors and tags available as scope filters. */
70
+ export const Default: Story = {
71
+ render: () => <ScopePickerElement authors={['Rob', 'Alex']} tags={['design', 'api']} />,
72
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
73
+ };
@@ -0,0 +1,36 @@
1
+ import { defineWebComponent } from './define';
2
+ import { ChatScopePicker } from '../components/chat-scope-picker';
3
+ import type { SearchFilters } from '../types';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** Authors to offer as scope filters. Set as a JS property. */
7
+ availableAuthors: string[];
8
+ /** Tags to offer as scope filters. Set as a JS property. */
9
+ availableTags: string[];
10
+ /** The label shown on the trigger for the active scope. */
11
+ currentLabel?: string;
12
+ }
13
+
14
+ /** Events fired by `<kai-scope-picker>`. */
15
+ interface Events {
16
+ /** A scope was chosen (`undefined` filters = "All Content"). */
17
+ 'kai-scope-change': { filters: SearchFilters | undefined };
18
+ }
19
+
20
+ /**
21
+ * `<kai-scope-picker>` — a dropdown to scope a chat by author or tag.
22
+ * Options via `available-authors`/`available-tags` properties; emits
23
+ * `kai-scope-change`.
24
+ */
25
+ defineWebComponent<Props, Events>('kai-scope-picker', {
26
+ availableAuthors: [],
27
+ availableTags: [],
28
+ currentLabel: 'All Content',
29
+ }, (props, { dispatch }) => (
30
+ <ChatScopePicker
31
+ currentLabel={props.currentLabel ?? 'All Content'}
32
+ availableAuthors={props.availableAuthors}
33
+ availableTags={props.availableTags}
34
+ onScopeChange={(filters) => dispatch('kai-scope-change', { filters })}
35
+ />
36
+ ));
@@ -0,0 +1,42 @@
1
+ import type { ToolPart } from '../components/tool';
2
+ import type { AttachmentData } from '../components/attachments';
3
+
4
+ /** The five built-in action buttons (each carries its own curated icon + label). */
5
+ export type ChatMessageAction = 'copy' | 'like' | 'dislike' | 'regenerate' | 'edit';
6
+
7
+ /** A host-defined action button. `icon` is a curated registry name (see
8
+ * `src/ui/action-icons.ts`); unknown/absent icons render label-only. */
9
+ export interface CustomAction {
10
+ /** Emitted as the `kai-message-action` detail `action` when clicked. */
11
+ id: string;
12
+ /** Visible/`aria-label` text. */
13
+ label: string;
14
+ /** A curated icon name from the action-icon registry. */
15
+ icon?: string;
16
+ /** Tooltip text shown on hover. Defaults to `label` when omitted. */
17
+ tooltip?: string;
18
+ }
19
+
20
+ /** The speaker avatar for a message row. (Mirrors the `…Data` convention of
21
+ * `AttachmentData`; named to avoid clashing with the `MessageAvatar` component.) */
22
+ export interface AvatarData {
23
+ /** Image URL. When present, renders an `<img>`. */
24
+ src?: string;
25
+ /** Initials/short text shown when there is no `src`. */
26
+ fallback?: string;
27
+ /** Alt text for the image. */
28
+ alt?: string;
29
+ }
30
+
31
+ export interface ChatMessage {
32
+ id: string;
33
+ role: 'user' | 'assistant';
34
+ content: string;
35
+ reasoning?: { text: string; label?: string };
36
+ tools?: ToolPart[];
37
+ attachments?: AttachmentData[];
38
+ /** Action buttons under the message — built-in names and/or custom descriptors. */
39
+ actions?: (ChatMessageAction | CustomAction)[];
40
+ /** Optional speaker avatar shown to the left of the message column. */
41
+ avatar?: AvatarData;
42
+ }