@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,237 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { createSignal, onMount, type JSX } from 'solid-js';
3
+ import './tasks';
4
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
5
+ import type { TasksCardData } from '../components/tasks-card';
6
+ import type { CardEvent } from '../primitives/card-contract';
7
+
8
+ declare module 'solid-js' {
9
+ // eslint-disable-next-line @typescript-eslint/no-namespace
10
+ namespace JSX {
11
+ interface IntrinsicElements {
12
+ 'kai-tasks': JSX.HTMLAttributes<HTMLElement> & {
13
+ heading?: string;
14
+ 'card-id'?: string;
15
+ ref?: (el: HTMLElement) => void;
16
+ };
17
+ }
18
+ }
19
+ }
20
+
21
+ type TasksEl = HTMLElement & { data?: TasksCardData; resolution?: Record<string, unknown> };
22
+
23
+ function Frame(props: { children: JSX.Element }) {
24
+ return <div style={{ 'max-width': '460px' }}>{props.children}</div>;
25
+ }
26
+
27
+ /** Mounts a <kai-tasks>, sets `.data`, logs the emitted CardEvent under the render. */
28
+ function TasksDemo(props: { def: TasksCardData; cardId: string; heading?: string }) {
29
+ const [log, setLog] = createSignal<CardEvent[]>([]);
30
+ let el: TasksEl | undefined;
31
+ onMount(() => {
32
+ if (!el) return;
33
+ el.data = props.def;
34
+ el.addEventListener('kai-card', (e) => {
35
+ const detail = (e as CustomEvent<CardEvent>).detail;
36
+ setLog((prev) => [...prev, detail]);
37
+ });
38
+ });
39
+ return (
40
+ <Frame>
41
+ <div style={{ display: 'flex', 'flex-direction': 'column', gap: '12px' }}>
42
+ <kai-tasks ref={(e) => (el = e as TasksEl)} card-id={props.cardId} heading={props.heading} />
43
+ <pre
44
+ style={{
45
+ margin: 0,
46
+ 'max-height': '180px',
47
+ overflow: 'auto',
48
+ background: 'var(--color-muted, #f4f4f5)',
49
+ 'border-radius': '8px',
50
+ padding: '8px',
51
+ 'font-size': '12px',
52
+ }}
53
+ >
54
+ {log().length === 0 ? '// emitted CardEvents appear here' : JSON.stringify(log(), null, 2)}
55
+ </pre>
56
+ </div>
57
+ </Frame>
58
+ );
59
+ }
60
+
61
+ const PLAN: TasksCardData = {
62
+ mode: 'select',
63
+ selectAll: true,
64
+ confirmLabel: 'Run selected',
65
+ tasks: [
66
+ { id: 'lint', label: 'Run linter', checked: true },
67
+ { id: 'test', label: 'Run unit tests', checked: true },
68
+ { id: 'build', label: 'Build production bundle' },
69
+ { id: 'deploy', label: 'Deploy to staging', description: 'Reversible; staging only' },
70
+ ],
71
+ };
72
+
73
+ const REQUIRE_ONE: TasksCardData = {
74
+ confirmLabel: 'Apply',
75
+ allowEmpty: false,
76
+ tasks: [
77
+ { id: 'cache', label: 'Clear the CDN cache' },
78
+ { id: 'reindex', label: 'Rebuild the search index' },
79
+ { id: 'restart', label: 'Restart the workers' },
80
+ ],
81
+ };
82
+
83
+ const BOUNDED: TasksCardData = {
84
+ heading: 'Pick up to 2 reviewers',
85
+ confirmLabel: 'Request review',
86
+ min: 1,
87
+ max: 2,
88
+ tasks: [
89
+ { id: 'ana', label: 'Ana' },
90
+ { id: 'ben', label: 'Ben' },
91
+ { id: 'cat', label: 'Cat' },
92
+ { id: 'dan', label: 'Dan' },
93
+ ],
94
+ };
95
+
96
+ const WITH_DESCRIPTIONS: TasksCardData = {
97
+ selectAll: true,
98
+ confirmLabel: 'Run cleanup',
99
+ tasks: [
100
+ { id: 'tmp', label: 'Delete temp files', description: 'Frees ~2.1 GB; safe to remove' },
101
+ { id: 'logs', label: 'Rotate logs', description: 'Archives logs older than 30 days' },
102
+ { id: 'orphans', label: 'Prune orphaned blobs', description: 'Unreferenced uploads only' },
103
+ ],
104
+ };
105
+
106
+ const HEADING_MAP: Record<string, string | undefined> = {
107
+ 'card-plan': 'Approve the plan steps',
108
+ 'card-require': 'Choose maintenance steps',
109
+ 'card-bounded': undefined,
110
+ 'card-desc': 'Storage cleanup',
111
+ };
112
+
113
+ const HTML_SNIPPET = (def: TasksCardData, cardId: string) => {
114
+ const heading = HEADING_MAP[cardId];
115
+ return `<kai-tasks${heading ? ` heading="${heading}"` : ''}></kai-tasks>
116
+ <script type="module">
117
+ import '@kitn.ai/ui/elements'; // registers the custom elements
118
+
119
+ const el = document.querySelector('kai-tasks');
120
+ // \`data\` is the CardEnvelope.data (set as a property).
121
+ el.data = ${JSON.stringify(def, null, 2)};
122
+
123
+ // Toggling rows is local; only CONFIRM emits — a single bubbling \`kai-card\` event.
124
+ el.addEventListener('kai-card', (e) => {
125
+ const ev = e.detail; // { kind:'submit', cardId, data:{ selected } } | ...
126
+ if (ev.kind === 'submit') console.log('selected', ev.data.selected);
127
+ });
128
+ </script>`;
129
+ };
130
+
131
+ const meta = {
132
+ title: 'Generative UI/Cards/kai-tasks',
133
+ tags: ['autodocs'],
134
+ argTypes: argTypesFor('kai-tasks'),
135
+ parameters: {
136
+ layout: 'padded',
137
+ docs: {
138
+ description: specDescription('kai-tasks', [
139
+ "`<kai-tasks>` is a **selectable** task/plan list (set via the `data` **property**): checkbox rows + an optional select-all + a confirm button. The user picks a subset, confirms, and the card emits the Card contract's **`submit`** verb up a bubbling **`kai-card`** CustomEvent of `{ kind: 'submit', cardId, data: { selected } }` — the checked ids in **input order**. Toggling rows is local UI state; **only the final confirm emits** (the wire stays quiet, the result atomic).",
140
+ '**Anatomy:** `<kai-card>` chrome (optional heading) → **select-all row** (shown when `selectAll:true` and ≥2 toggleable tasks; indeterminate state when partially checked) → **task rows** (one per `tasks[]` item: checkbox + label + optional description; disabled when `max` reached or `task.disabled`) → **card footer** (selected-count label + confirm `<Button>`, disabled until gating passes; replaced by a read-only resolved summary after submission).',
141
+ '**Gating:** confirm is enabled when `selectedCount >= (min ?? (allowEmpty ? 0 : 1))` and `<= (max ?? ∞)`. Select-all checks every toggleable (non-`disabled`) row and shows an **indeterminate** (`aria-checked="mixed"`) state when only some are checked. When `max` is reached, unchecked rows become non-toggleable. v1 is **select/approve only** (a future `mode:\'progress\'` is reserved in the schema).',
142
+ '**Events** (all frozen Card-contract verbs): `ready` on mount, `submit` on confirm, `error` for a malformed definition (renders the inline `kai-card` error). It **never invents events**. The same shapes flow over the remote iframe transport unchanged.',
143
+ ]),
144
+ },
145
+ },
146
+ } satisfies Meta;
147
+
148
+ export default meta;
149
+ type Story = StoryObj;
150
+
151
+ /** Select a plan (select-all on, a few pre-checked). */
152
+ export const SelectAPlan: Story = {
153
+ render: () => <TasksDemo def={PLAN} cardId="card-plan" heading="Approve the plan steps" />,
154
+ parameters: { docs: { source: { code: HTML_SNIPPET(PLAN, 'card-plan'), language: 'html' } } },
155
+ };
156
+
157
+ /** Require at least one (`allowEmpty:false`, the default) — confirm stays disabled until a row is checked. */
158
+ export const RequireAtLeastOne: Story = {
159
+ render: () => <TasksDemo def={REQUIRE_ONE} cardId="card-require" heading="Choose maintenance steps" />,
160
+ parameters: { docs: { source: { code: HTML_SNIPPET(REQUIRE_ONE, 'card-require'), language: 'html' } } },
161
+ };
162
+
163
+ /** Bounded (`min`/`max`) — confirm gating + max-reached row disabling. */
164
+ export const Bounded: Story = {
165
+ render: () => <TasksDemo def={BOUNDED} cardId="card-bounded" />,
166
+ parameters: { docs: { source: { code: HTML_SNIPPET(BOUNDED, 'card-bounded'), language: 'html' } } },
167
+ };
168
+
169
+ /** Rows with secondary descriptions (linked via aria-describedby). */
170
+ export const WithDescriptions: Story = {
171
+ render: () => <TasksDemo def={WITH_DESCRIPTIONS} cardId="card-desc" heading="Storage cleanup" />,
172
+ parameters: { docs: { source: { code: HTML_SNIPPET(WITH_DESCRIPTIONS, 'card-desc'), language: 'html' } } },
173
+ };
174
+
175
+ const RESOLVED_TASKS: TasksCardData = {
176
+ confirmLabel: 'Export',
177
+ tasks: [
178
+ { id: 'sum', label: 'Executive summary' },
179
+ { id: 'data', label: 'Raw data' },
180
+ { id: 'charts', label: 'Charts' },
181
+ ],
182
+ };
183
+
184
+ /** The card after the user confirmed their selection — shows "Selected 2 of 3", no checkboxes or button. */
185
+ export const Resolved: Story = {
186
+ name: 'Resolved (read-only)',
187
+ render: () => {
188
+ let el: TasksEl | undefined;
189
+ onMount(() => {
190
+ if (!el) return;
191
+ el.data = RESOLVED_TASKS;
192
+ el.resolution = { kind: 'submit', data: { selected: ['sum', 'charts'] } };
193
+ });
194
+ return (
195
+ <Frame>
196
+ <kai-tasks ref={(e) => (el = e as TasksEl)} card-id="card-resolved-tasks" heading="Export report" />
197
+ </Frame>
198
+ );
199
+ },
200
+ parameters: {
201
+ docs: {
202
+ source: {
203
+ code: `<kai-tasks heading="Export report"></kai-tasks>
204
+ <script type="module">
205
+ import '@kitn.ai/ui/elements';
206
+ const el = document.querySelector('kai-tasks');
207
+ el.data = ${JSON.stringify(RESOLVED_TASKS, null, 2)};
208
+ // Setting .resolution renders the chromed read-only view — no checkboxes or confirm button.
209
+ el.resolution = { kind: 'submit', data: { selected: ['sum', 'charts'] } };
210
+ </script>`,
211
+ language: 'html',
212
+ },
213
+ },
214
+ },
215
+ };
216
+
217
+ /** A malformed `data` (empty `tasks`) → the inline error state + an `error` event. */
218
+ export const ErrorState: Story = {
219
+ render: () => <TasksDemo def={{ tasks: [] } as unknown as TasksCardData} cardId="card-bad" />,
220
+ parameters: {
221
+ docs: {
222
+ source: {
223
+ code: `<kai-tasks></kai-tasks>
224
+ <script type="module">
225
+ import '@kitn.ai/ui/elements';
226
+ const el = document.querySelector('kai-tasks');
227
+ // No tasks → inline error state + an \`error\` event.
228
+ el.data = { tasks: [] };
229
+ el.addEventListener('kai-card', (e) => {
230
+ if (e.detail.kind === 'error') console.warn('tasks error:', e.detail.message);
231
+ });
232
+ </script>`,
233
+ language: 'html',
234
+ },
235
+ },
236
+ },
237
+ };
@@ -0,0 +1,46 @@
1
+ import { defineWebComponent } from './define';
2
+ import { TasksCard, type TasksCardData } from '../components/tasks-card';
3
+ import type { CardResolution } from '../primitives/card-contract';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** The tasks definition (the CardEnvelope.data). Set as a JS PROPERTY:
7
+ * `el.data = { tasks:[…], selectAll, confirmLabel, … }`. Import
8
+ * `TasksCardData` from `@kitn.ai/ui` for the full shape. */
9
+ data?: Record<string, unknown>;
10
+ /** Stable card id correlating every emitted CardEvent. Attribute: `card-id`. */
11
+ cardId?: string;
12
+ /** Heading rendered in the card chrome (= CardEnvelope.title). Attribute: `heading`. */
13
+ heading?: string;
14
+ /** Set when the user resolved this card; renders the read-only view. Property:
15
+ * `el.resolution = { kind:'submit', data:{ selected:[…] } }`. */
16
+ resolution?: Record<string, unknown>;
17
+ }
18
+
19
+ /**
20
+ * `<kai-tasks>` — a **selectable** task/plan list (set via the `data` property):
21
+ * checkbox rows + an optional select-all + a confirm button. Toggling rows is local
22
+ * UI state; only the final confirm emits the Card contract's **`submit`** verb
23
+ * up a bubbling **`kai-card`** CustomEvent (`{ kind:'submit', cardId,
24
+ * data:{ selected } }`) with the checked ids in input order. Also emits `ready` on
25
+ * mount and `error` for a malformed definition (inline error). v1 = select/approve
26
+ * mode only. Routes through a `CardProvider` when present, else the bubbling
27
+ * `kai-card` event. Isolated in Shadow DOM; theme-aware via the shared kit tokens.
28
+ */
29
+ defineWebComponent<Props>(
30
+ 'kai-tasks',
31
+ {
32
+ data: undefined,
33
+ cardId: undefined,
34
+ heading: undefined,
35
+ resolution: undefined,
36
+ },
37
+ (props, { element }) => (
38
+ <TasksCard
39
+ data={props.data as TasksCardData | undefined}
40
+ cardId={props.cardId ?? (element.id || 'kai-tasks')}
41
+ heading={props.heading}
42
+ resolution={props.resolution as CardResolution | undefined}
43
+ hostElement={element}
44
+ />
45
+ ),
46
+ );
@@ -0,0 +1,64 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import './register'; // side effect: registers the custom elements
3
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kai-text-shimmer': JSX.HTMLAttributes<HTMLElement> & {
11
+ text?: string;
12
+ duration?: number;
13
+ spread?: number;
14
+ };
15
+ }
16
+ }
17
+ }
18
+
19
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
20
+ <kai-text-shimmer text="Thinking…" duration="3" spread="20"></kai-text-shimmer>
21
+
22
+ <script type="module">
23
+ import '@kitn.ai/ui/elements'; // registers the custom elements
24
+ </script>`;
25
+
26
+ const meta = {
27
+ title: 'Components/TextShimmer',
28
+ tags: ['autodocs'],
29
+ argTypes: argTypesFor('kai-text-shimmer'),
30
+ parameters: {
31
+ layout: 'fullscreen',
32
+ docs: {
33
+ description: specDescription('kai-text-shimmer', [
34
+ '`<kai-text-shimmer>` is the framework-agnostic **web component** for animated shimmering text — a gradient sweep across a label, isolated in **Shadow DOM**.',
35
+ '**When to use:** signalling a quiet, in-progress state ("Thinking…", "Generating…") inline. In SolidJS, use the `TextShimmer` primitive.',
36
+ '**Placement:** inline within a status row, toolbar label, or above a streaming content area — it renders as an inline `span` so it flows naturally alongside other text or icons.',
37
+ "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the `text` attribute, and tune `duration` (seconds) and `spread` (gradient width, 5–45).",
38
+ 'See the **Code** tab for HTML usage.',
39
+ ]),
40
+ },
41
+ },
42
+ } satisfies Meta;
43
+
44
+ export default meta;
45
+ type Story = StoryObj;
46
+
47
+ /** Default shimmer. */
48
+ export const Default: Story = {
49
+ render: () => (
50
+ <div style={{ padding: '24px', 'font-size': '18px' }}>
51
+ <kai-text-shimmer text="Thinking…" />
52
+ </div>
53
+ ),
54
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
55
+ };
56
+
57
+ /** Faster sweep with a wider gradient spread. */
58
+ export const Tuned: Story = {
59
+ render: () => (
60
+ <div style={{ padding: '24px', 'font-size': '18px' }}>
61
+ <kai-text-shimmer text="Generating response…" duration={2} spread={35} />
62
+ </div>
63
+ ),
64
+ };
@@ -0,0 +1,28 @@
1
+ import { defineWebComponent } from './define';
2
+ import { TextShimmer } from '../components/text-shimmer';
3
+
4
+ interface Props extends Record<string, unknown> {
5
+ /** The text to shimmer. */
6
+ text?: string;
7
+ /** Element tag to render as (default `span`). */
8
+ as?: string;
9
+ /** Animation duration in seconds. */
10
+ duration?: number;
11
+ /** Gradient spread (5–45). */
12
+ spread?: number;
13
+ }
14
+
15
+ /**
16
+ * `<kai-text-shimmer>` — animated shimmering text. Text via the `text`
17
+ * attribute; `duration`/`spread` tune the effect.
18
+ */
19
+ defineWebComponent<Props>('kai-text-shimmer', {
20
+ text: '',
21
+ as: 'span',
22
+ duration: 4,
23
+ spread: 20,
24
+ }, (props) => (
25
+ <TextShimmer as={props.as} duration={props.duration} spread={props.spread} class="text-body">
26
+ {props.text}
27
+ </TextShimmer>
28
+ ));
@@ -0,0 +1,72 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import './register'; // side effect: registers the custom elements
3
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kai-thinking-bar': JSX.HTMLAttributes<HTMLElement> & {
11
+ text?: string;
12
+ stoppable?: boolean | string;
13
+ 'stop-label'?: string;
14
+ 'on:kai-stop'?: (e: CustomEvent) => void;
15
+ };
16
+ }
17
+ }
18
+ }
19
+
20
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
21
+ <kai-thinking-bar text="Thinking" stoppable stop-label="Answer now"></kai-thinking-bar>
22
+
23
+ <script type="module">
24
+ import '@kitn.ai/ui/elements'; // registers the custom elements
25
+
26
+ document.querySelector('kai-thinking-bar')
27
+ .addEventListener('kai-stop', () => console.log('user asked to stop'));
28
+ </script>`;
29
+
30
+ const meta = {
31
+ title: 'Components/ThinkingBar',
32
+ tags: ['autodocs'],
33
+ argTypes: argTypesFor('kai-thinking-bar'),
34
+ parameters: {
35
+ layout: 'fullscreen',
36
+ docs: {
37
+ description: specDescription('kai-thinking-bar', [
38
+ '`<kai-thinking-bar>` is the framework-agnostic **web component** for an animated "thinking" indicator with an optional stop affordance — a pure leaf element isolated in **Shadow DOM**. (`<kai-chat>` does not surface this; compose it yourself.)',
39
+ '**When to use:** showing that the assistant is reasoning, optionally letting the user interrupt with "Answer now". In SolidJS, use the `ThinkingBar` primitive.',
40
+ "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the `text`/`stop-label` attributes, add the `stoppable` flag to show the stop button, and listen for the `kai-stop` **CustomEvent**.",
41
+ 'See the **Code** tab for HTML usage.',
42
+ ]),
43
+ },
44
+ },
45
+ } satisfies Meta;
46
+
47
+ export default meta;
48
+ type Story = StoryObj;
49
+
50
+ /** A plain thinking indicator. */
51
+ export const Default: Story = {
52
+ render: () => (
53
+ <div style={{ padding: '24px' }}>
54
+ <kai-thinking-bar text="Thinking" />
55
+ </div>
56
+ ),
57
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
58
+ };
59
+
60
+ /** Stoppable — shows an "Answer now" affordance that fires a `stop` event. */
61
+ export const Stoppable: Story = {
62
+ render: () => (
63
+ <div style={{ padding: '24px' }}>
64
+ <kai-thinking-bar
65
+ text="Reasoning"
66
+ stoppable
67
+ stop-label="Answer now"
68
+ on:kai-stop={() => console.log('stop')}
69
+ />
70
+ </div>
71
+ ),
72
+ };
@@ -0,0 +1,34 @@
1
+ import { defineWebComponent } from './define';
2
+ import { ThinkingBar } from '../components/thinking-bar';
3
+
4
+ interface Props extends Record<string, unknown> {
5
+ /** The shimmering label, e.g. "Thinking…". */
6
+ text?: string;
7
+ /** When true, show a "stop" affordance that fires a `stop` event. */
8
+ stoppable?: boolean;
9
+ /** Label for the stop affordance. */
10
+ stopLabel?: string;
11
+ }
12
+
13
+ /** Events fired by `<kai-thinking-bar>`. */
14
+ interface Events {
15
+ /** The "stop / answer now" affordance was clicked. */
16
+ 'kai-stop': void;
17
+ }
18
+
19
+ /**
20
+ * `<kai-thinking-bar>` — a pure leaf element: an animated "thinking" indicator
21
+ * (one of the primitives the batteries-included `<kai-chat>` does NOT surface).
22
+ * Config via attributes, the only interaction (`stop`) comes back as an event.
23
+ */
24
+ defineWebComponent<Props, Events>('kai-thinking-bar', {
25
+ text: 'Thinking',
26
+ stoppable: false,
27
+ stopLabel: 'Answer now',
28
+ }, (props, { dispatch, flag }) => (
29
+ <ThinkingBar
30
+ text={props.text}
31
+ stopLabel={props.stopLabel}
32
+ onStop={flag('stoppable') ? () => dispatch('kai-stop') : undefined}
33
+ />
34
+ ));
@@ -0,0 +1,89 @@
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 type { ToolPart } from '../components/tool';
5
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
6
+
7
+ // The web components are custom DOM elements, so declare the tags for JSX.
8
+ declare module 'solid-js' {
9
+ // eslint-disable-next-line @typescript-eslint/no-namespace
10
+ namespace JSX {
11
+ interface IntrinsicElements {
12
+ 'kai-tool': JSX.HTMLAttributes<HTMLElement>;
13
+ }
14
+ }
15
+ }
16
+
17
+ const completedTool: ToolPart = {
18
+ type: 'database_query',
19
+ state: 'output-available',
20
+ input: { table: 'users', limit: 10 },
21
+ output: { rows: 10, ms: 42 },
22
+ };
23
+
24
+ const runningTool: ToolPart = {
25
+ type: 'search',
26
+ state: 'input-available',
27
+ input: { query: 'kitn docs' },
28
+ };
29
+
30
+ /** Render the actual `<kai-tool>` custom element with a `tool` property. */
31
+ function ToolElement(props: { tool: ToolPart; open?: boolean }) {
32
+ let el: (HTMLElement & { tool?: ToolPart; open?: boolean }) | undefined;
33
+ onMount(() => {
34
+ if (el) {
35
+ el.tool = props.tool;
36
+ if (props.open) el.open = true;
37
+ }
38
+ });
39
+ return (
40
+ <kai-tool ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
41
+ );
42
+ }
43
+
44
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
45
+ <kai-tool id="tool" open></kai-tool>
46
+
47
+ <script type="module">
48
+ import '@kitn.ai/ui/elements'; // registers the custom elements
49
+
50
+ const tool = document.getElementById('tool');
51
+ tool.tool = {
52
+ type: 'database_query',
53
+ state: 'output-available',
54
+ input: { table: 'users', limit: 10 },
55
+ output: { rows: 10, ms: 42 },
56
+ };
57
+ </script>`;
58
+
59
+ const meta = {
60
+ title: 'Components/Tool',
61
+ tags: ['autodocs'],
62
+ argTypes: argTypesFor('kai-tool'),
63
+ parameters: {
64
+ layout: 'fullscreen',
65
+ docs: {
66
+ description: specDescription('kai-tool', [
67
+ '`<kai-tool>` is the framework-agnostic **web component** for a single tool-call panel — a collapsible input/output inspector with a state badge — isolated in **Shadow DOM**.',
68
+ '**When to use:** rendering an agent/tool-call trace in a non-Solid app. In SolidJS, use the `Tool` primitive directly.',
69
+ '**Placement:** inline inside an assistant message row, one element per tool call, below the text content; renders as a block sized to its content.',
70
+ "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the call via the `tool` **property** (`el.tool = {...}`), and add the `open` flag to start it expanded.",
71
+ 'See the **Code** tab for HTML usage.',
72
+ ]),
73
+ },
74
+ },
75
+ } satisfies Meta;
76
+
77
+ export default meta;
78
+ type Story = StoryObj;
79
+
80
+ /** A completed call with input and output, started expanded. */
81
+ export const Completed: Story = {
82
+ render: () => <ToolElement tool={completedTool} open />,
83
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
84
+ };
85
+
86
+ /** A call still awaiting output (collapsed). */
87
+ export const Running: Story = {
88
+ render: () => <ToolElement tool={runningTool} />,
89
+ };
@@ -0,0 +1,23 @@
1
+ import { Show } from 'solid-js';
2
+ import { defineWebComponent } from './define';
3
+ import { Tool, type ToolPart } from '../components/tool';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** The tool-call to display. Set as a JS property. */
7
+ tool?: ToolPart;
8
+ /** Start expanded. */
9
+ open?: boolean;
10
+ }
11
+
12
+ /**
13
+ * `<kai-tool>` — a collapsible tool-call panel (input/output inspection with a
14
+ * state badge). Data via the `tool` property; `open` flag starts it expanded.
15
+ */
16
+ defineWebComponent<Props>('kai-tool', {
17
+ tool: undefined,
18
+ open: false,
19
+ }, (props, { flag }) => (
20
+ <Show when={props.tool}>
21
+ <Tool toolPart={props.tool!} defaultOpen={flag('open')} />
22
+ </Show>
23
+ ));