@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,64 @@
1
+ import type { Palette } from './theme-css';
2
+
3
+ export type Preset = { name: string; light: Palette; dark: Palette };
4
+ export type BrandOverride = { light: Palette; dark: Palette };
5
+
6
+ /** Per-preset brand-token overrides (light + dark). Neutrals come from the live base. */
7
+ export const BRAND_OVERRIDES: Record<string, BrandOverride> = {
8
+ Violet: {
9
+ light: {
10
+ '--color-primary': 'hsl(262 83% 58%)',
11
+ '--color-primary-foreground': 'hsl(0 0% 100%)',
12
+ '--color-ring': 'hsl(262 83% 58%)',
13
+ '--color-code-foreground': 'hsl(262 83% 58%)',
14
+ },
15
+ dark: {
16
+ '--color-primary': 'hsl(263 70% 65%)',
17
+ '--color-primary-foreground': 'hsl(0 0% 100%)',
18
+ '--color-ring': 'hsl(263 70% 65%)',
19
+ '--color-code-foreground': 'hsl(263 90% 80%)',
20
+ },
21
+ },
22
+ Emerald: {
23
+ light: {
24
+ '--color-primary': 'hsl(160 84% 39%)',
25
+ '--color-primary-foreground': 'hsl(0 0% 100%)',
26
+ '--color-ring': 'hsl(160 84% 39%)',
27
+ '--color-code-foreground': 'hsl(160 84% 32%)',
28
+ },
29
+ dark: {
30
+ '--color-primary': 'hsl(158 64% 52%)',
31
+ '--color-primary-foreground': 'hsl(160 30% 8%)',
32
+ '--color-ring': 'hsl(158 64% 52%)',
33
+ '--color-code-foreground': 'hsl(158 70% 65%)',
34
+ },
35
+ },
36
+ Mono: {
37
+ light: {
38
+ '--color-primary': 'hsl(0 0% 20%)',
39
+ '--color-primary-foreground': 'hsl(0 0% 98%)',
40
+ '--color-ring': 'hsl(0 0% 40%)',
41
+ '--color-code-foreground': 'hsl(0 0% 30%)',
42
+ },
43
+ dark: {
44
+ '--color-primary': 'hsl(0 0% 92%)',
45
+ '--color-primary-foreground': 'hsl(0 0% 12%)',
46
+ '--color-ring': 'hsl(0 0% 70%)',
47
+ '--color-code-foreground': 'hsl(0 0% 75%)',
48
+ },
49
+ },
50
+ };
51
+
52
+ const ORDER = ['Default', 'Violet', 'Emerald', 'Mono'];
53
+
54
+ /** Build full presets by layering brand overrides onto the live-discovered base palette. */
55
+ export function buildPresets(base: { light: Palette; dark: Palette }): Preset[] {
56
+ return ORDER.map((name) => {
57
+ const ov = BRAND_OVERRIDES[name];
58
+ return {
59
+ name,
60
+ light: ov ? { ...base.light, ...ov.light } : { ...base.light },
61
+ dark: ov ? { ...base.dark, ...ov.dark } : { ...base.dark },
62
+ };
63
+ });
64
+ }
@@ -0,0 +1,19 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { buildThemeCss } from './theme-css';
3
+
4
+ describe('buildThemeCss', () => {
5
+ it('emits sorted :root and .dark blocks', () => {
6
+ const css = buildThemeCss(
7
+ { '--color-primary': '#ffffff', '--radius': '0.6rem' },
8
+ { '--color-primary': '#000000' },
9
+ );
10
+ expect(css).toBe(
11
+ ':root {\n --color-primary: #ffffff;\n --radius: 0.6rem;\n}\n\n.dark {\n --color-primary: #000000;\n}',
12
+ );
13
+ });
14
+
15
+ it('keys are sorted within each block', () => {
16
+ const css = buildThemeCss({ '--b': '2', '--a': '1' }, {});
17
+ expect(css).toBe(':root {\n --a: 1;\n --b: 2;\n}\n\n.dark {\n\n}');
18
+ });
19
+ });
@@ -0,0 +1,15 @@
1
+ /** A map of CSS custom-property name → value, e.g. { '--color-primary': '#fff' }. */
2
+ export type Palette = Record<string, string>;
3
+
4
+ function block(selector: string, palette: Palette): string {
5
+ const body = Object.keys(palette)
6
+ .sort()
7
+ .map((k) => ` ${k}: ${palette[k]};`)
8
+ .join('\n');
9
+ return `${selector} {\n${body}\n}`;
10
+ }
11
+
12
+ /** Build a paste-ready theme override block: full light set on :root, dark set on .dark. */
13
+ export function buildThemeCss(light: Palette, dark: Palette): string {
14
+ return `${block(':root', light)}\n\n${block('.dark', dark)}`;
15
+ }
@@ -0,0 +1,146 @@
1
+ // src/stories/docs/theme-editor/theme-editor.tsx
2
+ import { createSignal, createEffect, onMount, onCleanup, For } from 'solid-js';
3
+ import { Button } from '../../../ui/button';
4
+ import { discoverPalettes } from '../theme-tokens';
5
+ import { buildThemeCss, type Palette } from './theme-css';
6
+ import { buildPresets, type Preset } from './presets';
7
+ import { Inspector } from './inspector';
8
+ import { Canvas, CANVAS_CLASS } from './canvas';
9
+
10
+ const STYLE_ID = 'kitn-theme-editor-overrides';
11
+
12
+ /** Full-screen live theme editor: edit light/dark tokens, preview a real chat, export CSS. */
13
+ export function ThemeEditor() {
14
+ const [presets, setPresets] = createSignal<Preset[]>([]);
15
+ const [mode, setMode] = createSignal<'light' | 'dark'>('light');
16
+ const [light, setLight] = createSignal<Palette>({});
17
+ const [dark, setDark] = createSignal<Palette>({});
18
+ const [presetName, setPresetName] = createSignal('Default');
19
+ const [copied, setCopied] = createSignal(false);
20
+
21
+ onMount(() => {
22
+ const ps = buildPresets(discoverPalettes());
23
+ setPresets(ps);
24
+ const def = ps.find((p) => p.name === 'Default')!;
25
+ setLight({ ...def.light });
26
+ setDark({ ...def.dark });
27
+ });
28
+
29
+ // Apply the ACTIVE mode's palette directly onto the canvas wrapper. Scoping to
30
+ // CANVAS_CLASS (rather than :root/.dark) means the preview reflects the editor's
31
+ // mode independently of any ancestor `.dark` (e.g. Storybook's dark theme), and
32
+ // only the canvas reskins — not the editor chrome. Export (Copy CSS) still emits
33
+ // the full :root + .dark theme separately.
34
+ let styleEl: HTMLStyleElement | undefined;
35
+ createEffect(() => {
36
+ if (!Object.keys(light()).length) return; // not seeded yet
37
+ const colors: Palette = mode() === 'light' ? light() : dark();
38
+ const radius = light()['--radius'] ?? '0.6rem';
39
+ const colorBody = Object.keys(colors)
40
+ .filter((k) => k.startsWith('--color-'))
41
+ .sort()
42
+ .map((k) => ` ${k}: ${colors[k]};`)
43
+ .join('\n');
44
+ // Re-express the radius scale on the wrapper. The kit's --radius-sm/md/lg/xl are
45
+ // calc(var(--radius) …) declared at :root, so their var(--radius) resolves once
46
+ // at :root and inherits as a fixed length — overriding --radius lower in the tree
47
+ // doesn't move them. Re-declaring them here (against the wrapper's --radius) does.
48
+ const radiusBody = [
49
+ ` --radius: ${radius};`,
50
+ ` --radius-sm: calc(var(--radius) - 4px);`,
51
+ ` --radius-md: calc(var(--radius) - 2px);`,
52
+ ` --radius-lg: var(--radius);`,
53
+ ` --radius-xl: calc(var(--radius) + 4px);`,
54
+ ].join('\n');
55
+ // Re-establish the inherited `color` and native `color-scheme` on the wrapper:
56
+ // tokens alone don't fix text whose color is inherited from outside the canvas
57
+ // (e.g. elements with no explicit text-color class), nor native form controls.
58
+ const css = `.${CANVAS_CLASS} {\n${colorBody}\n${radiusBody}\n color: var(--color-foreground);\n color-scheme: ${mode()};\n}`;
59
+ if (!styleEl) {
60
+ styleEl = document.createElement('style');
61
+ styleEl.id = STYLE_ID;
62
+ document.head.appendChild(styleEl);
63
+ }
64
+ styleEl.textContent = css;
65
+ });
66
+ onCleanup(() => styleEl?.remove());
67
+
68
+ const colorTokens = () => Object.keys(light()).filter((n) => n.startsWith('--color-')).sort();
69
+ const activeValues = () => (mode() === 'light' ? light() : dark());
70
+
71
+ const setColor = (token: string, hex: string) => {
72
+ if (mode() === 'light') setLight((v) => ({ ...v, [token]: hex }));
73
+ else setDark((v) => ({ ...v, [token]: hex }));
74
+ };
75
+ const setRadius = (rem: string) => setLight((v) => ({ ...v, '--radius': rem }));
76
+
77
+ const loadPreset = (name: string) => {
78
+ const p = presets().find((x) => x.name === name);
79
+ if (!p) return;
80
+ setLight({ ...p.light });
81
+ setDark({ ...p.dark });
82
+ setPresetName(name);
83
+ };
84
+ const reset = () => loadPreset('Default');
85
+
86
+ const copyCss = async () => {
87
+ try {
88
+ await navigator.clipboard.writeText(buildThemeCss(light(), dark()));
89
+ setCopied(true);
90
+ setTimeout(() => setCopied(false), 1500);
91
+ } catch {
92
+ /* clipboard blocked */
93
+ }
94
+ };
95
+
96
+ return (
97
+ <div class="h-screen w-full flex flex-col bg-background text-foreground">
98
+ {/* Top bar */}
99
+ <div class="flex items-center justify-between gap-3 border-b border-border px-4 h-12 shrink-0">
100
+ <div class="flex items-center gap-3">
101
+ <strong class="text-sm">Theme editor</strong>
102
+ <div class="flex rounded-md border border-border overflow-hidden text-xs">
103
+ <button class="px-2.5 py-1" classList={{ 'bg-secondary': mode() === 'light' }} onClick={() => setMode('light')}>
104
+ Light
105
+ </button>
106
+ <button class="px-2.5 py-1" classList={{ 'bg-secondary': mode() === 'dark' }} onClick={() => setMode('dark')}>
107
+ Dark
108
+ </button>
109
+ </div>
110
+ </div>
111
+ <div class="flex items-center gap-2">
112
+ <select
113
+ aria-label="Theme preset"
114
+ class="bg-input border border-border rounded-md text-xs px-2 h-8"
115
+ value={presetName()}
116
+ onChange={(e) => loadPreset(e.currentTarget.value)}
117
+ >
118
+ <For each={presets()}>{(p) => <option value={p.name}>{p.name}</option>}</For>
119
+ </select>
120
+ <Button size="sm" variant="outline" onClick={copyCss}>
121
+ {copied() ? 'Copied!' : 'Copy CSS'}
122
+ </Button>
123
+ <Button size="sm" variant="outline" onClick={reset}>
124
+ Reset
125
+ </Button>
126
+ </div>
127
+ </div>
128
+
129
+ {/* Body */}
130
+ <div class="flex-1 flex min-h-0">
131
+ <div class="w-[300px] shrink-0 border-r border-border overflow-auto">
132
+ <Inspector
133
+ tokens={colorTokens()}
134
+ values={activeValues()}
135
+ radius={light()['--radius'] ?? '0.6rem'}
136
+ onColorChange={setColor}
137
+ onRadiusChange={setRadius}
138
+ />
139
+ </div>
140
+ <div class="flex-1 min-w-0 p-3">
141
+ <Canvas mode={mode()} />
142
+ </div>
143
+ </div>
144
+ </div>
145
+ );
146
+ }
@@ -0,0 +1,174 @@
1
+ import { createSignal, For, onMount, type JSX } from 'solid-js';
2
+ import type { Palette } from './theme-editor/theme-css';
3
+
4
+ // Docs-only helpers (not part of the kit's public API). They auto-discover the
5
+ // kit's design tokens straight from the loaded CSS, so the reference + editor
6
+ // can never drift from theme.css as tokens are added or changed.
7
+
8
+ type ColorToken = { name: string; light: string; dark: string };
9
+ type RadiusToken = { name: string; value: string };
10
+
11
+ export const PURPOSE: Record<string, string> = {
12
+ '--color-background': 'App / page background',
13
+ '--color-foreground': 'Default text',
14
+ '--color-card': 'Card surface',
15
+ '--color-card-foreground': 'Text on cards',
16
+ '--color-popover': 'Popover / menu surface',
17
+ '--color-popover-foreground': 'Text in popovers',
18
+ '--color-primary': 'Primary action background',
19
+ '--color-primary-foreground': 'Text on primary',
20
+ '--color-secondary': 'Secondary surface',
21
+ '--color-secondary-foreground': 'Text on secondary',
22
+ '--color-muted': 'Muted surface (subtle fills)',
23
+ '--color-muted-foreground': 'Muted / secondary text',
24
+ '--color-accent': 'Accent / hover surface',
25
+ '--color-accent-foreground': 'Text on accent',
26
+ '--color-destructive': 'Destructive / danger',
27
+ '--color-destructive-foreground': 'Text on destructive',
28
+ '--color-border': 'Borders / dividers',
29
+ '--color-input': 'Input field background',
30
+ '--color-ring': 'Focus ring',
31
+ '--color-sidebar': 'Sidebar background',
32
+ '--color-code-foreground': 'Inline code text / accent',
33
+ };
34
+
35
+ /** Raw walk of loaded stylesheets → light/dark custom-property maps (colors + radius).
36
+ * Recurses into grouping rules (@layer, @media, @supports) because Tailwind v4
37
+ * emits the `:root` theme tokens inside an `@layer`, which a flat walk would miss. */
38
+ function collectTokens(): { light: Record<string, string>; dark: Record<string, string> } {
39
+ const light: Record<string, string> = {};
40
+ const dark: Record<string, string> = {};
41
+ const visit = (rules: CSSRuleList) => {
42
+ for (const rule of Array.from(rules)) {
43
+ const r = rule as CSSStyleRule;
44
+ if (r.selectorText && r.style) {
45
+ const isRoot = /(^|,)\s*:root\b/.test(r.selectorText);
46
+ const isDark = /(^|,)\s*\.dark\b/.test(r.selectorText);
47
+ if (isRoot || isDark) {
48
+ for (const prop of Array.from(r.style)) {
49
+ if (!prop.startsWith('--color-') && !prop.startsWith('--radius')) continue;
50
+ const val = r.style.getPropertyValue(prop).trim();
51
+ if (isRoot) light[prop] = val;
52
+ if (isDark) dark[prop] = val;
53
+ }
54
+ }
55
+ }
56
+ // Grouping rules (@layer/@media/@supports) carry nested rules — recurse.
57
+ const nested = (rule as CSSGroupingRule).cssRules;
58
+ if (nested) visit(nested);
59
+ }
60
+ };
61
+ for (const sheet of Array.from(document.styleSheets)) {
62
+ try {
63
+ visit(sheet.cssRules);
64
+ } catch {
65
+ // cross-origin sheet
66
+ }
67
+ }
68
+ return { light, dark };
69
+ }
70
+
71
+ /** Reference data for the token table: a color token is one with a `.dark` override. */
72
+ function discover(): { colors: ColorToken[]; radii: RadiusToken[] } {
73
+ const { light, dark } = collectTokens();
74
+ const colors = Object.keys(dark)
75
+ .filter((n) => n.startsWith('--color-'))
76
+ .sort()
77
+ .map((name) => ({ name, light: light[name] || dark[name], dark: dark[name] }));
78
+ const radii = Object.keys(light)
79
+ .filter((n) => n.startsWith('--radius'))
80
+ .sort()
81
+ .map((name) => ({ name, value: light[name] }));
82
+ return { colors, radii };
83
+ }
84
+
85
+ /** Light/dark palettes for the theme editor: light = colors + --radius, dark = colors.
86
+ * The token set is keyed off the `.dark` overrides — that's what defines a kit token
87
+ * and excludes Tailwind's default palette (which has no `.dark` entry). Light values
88
+ * come from `:root`, falling back to the dark value if a token only exists in dark. */
89
+ export function discoverPalettes(): { light: Palette; dark: Palette } {
90
+ const { light, dark } = collectTokens();
91
+ const names = Object.keys(dark).filter((n) => n.startsWith('--color-')).sort();
92
+ const lightPalette: Palette = { '--radius': light['--radius'] ?? '0.6rem' };
93
+ const darkPalette: Palette = {};
94
+ for (const name of names) {
95
+ lightPalette[name] = light[name] || dark[name];
96
+ darkPalette[name] = dark[name];
97
+ }
98
+ return {
99
+ light: lightPalette,
100
+ dark: darkPalette,
101
+ };
102
+ }
103
+
104
+ /** Resolve any CSS color string (e.g. hsl(...)) to #rrggbb for a color input. */
105
+ export function toHex(css: string): string {
106
+ const el = document.createElement('div');
107
+ el.style.color = css;
108
+ el.style.display = 'none';
109
+ document.body.appendChild(el);
110
+ const rgb = getComputedStyle(el).color;
111
+ el.remove();
112
+ const m = rgb.match(/\d+(\.\d+)?/g);
113
+ if (!m) return '#000000';
114
+ return '#' + m.slice(0, 3).map((x) => Math.round(+x).toString(16).padStart(2, '0')).join('');
115
+ }
116
+
117
+ const cellHead: JSX.CSSProperties = { 'text-align': 'left', padding: '6px 10px', 'border-bottom': '1px solid var(--color-border)', 'font-weight': '600' };
118
+ const cell: JSX.CSSProperties = { padding: '6px 10px', 'border-bottom': '1px solid var(--color-border)', 'vertical-align': 'middle' };
119
+
120
+ function Swatch(props: { color: string }) {
121
+ return (
122
+ <span
123
+ style={{
124
+ display: 'inline-block', width: '1.15rem', height: '1.15rem', 'border-radius': '4px',
125
+ background: props.color, border: '1px solid var(--color-border)', 'vertical-align': 'middle',
126
+ 'margin-right': '.4rem',
127
+ }}
128
+ />
129
+ );
130
+ }
131
+
132
+ /** Auto-generated reference of every overridable token (light + dark values). */
133
+ export function TokenTable() {
134
+ const [data, setData] = createSignal<{ colors: ColorToken[]; radii: RadiusToken[] }>({ colors: [], radii: [] });
135
+ onMount(() => setData(discover()));
136
+ return (
137
+ <div style={{ 'font-size': '13px', color: 'var(--color-foreground)' }}>
138
+ <table style={{ width: '100%', 'border-collapse': 'collapse' }}>
139
+ <thead>
140
+ <tr>
141
+ <th style={cellHead}>Token</th>
142
+ <th style={cellHead}>Purpose</th>
143
+ <th style={cellHead}>Light</th>
144
+ <th style={cellHead}>Dark</th>
145
+ </tr>
146
+ </thead>
147
+ <tbody>
148
+ <For each={data().colors}>
149
+ {(t) => (
150
+ <tr>
151
+ <td style={cell}><code>{t.name}</code></td>
152
+ <td style={{ ...cell, color: 'var(--color-muted-foreground)' }}>{PURPOSE[t.name] || ''}</td>
153
+ <td style={cell}><Swatch color={t.light} /><small>{t.light}</small></td>
154
+ <td style={cell}><Swatch color={t.dark} /><small>{t.dark}</small></td>
155
+ </tr>
156
+ )}
157
+ </For>
158
+ <For each={data().radii}>
159
+ {(t) => (
160
+ <tr>
161
+ <td style={cell}><code>{t.name}</code></td>
162
+ <td style={{ ...cell, color: 'var(--color-muted-foreground)' }}>Corner radius</td>
163
+ <td style={{ ...cell }} colspan={2}><small>{t.value}</small></td>
164
+ </tr>
165
+ )}
166
+ </For>
167
+ </tbody>
168
+ </table>
169
+ <p style={{ 'margin-top': '.75rem', color: 'var(--color-muted-foreground)', 'font-size': '12px' }}>
170
+ This table is generated live from the loaded CSS — it always reflects the current tokens in <code>theme.css</code>.
171
+ </p>
172
+ </div>
173
+ );
174
+ }
@@ -0,0 +1,106 @@
1
+ import { Meta } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="Docs/Choosing Components" />
4
+
5
+ # Choosing components
6
+
7
+ `@kitn.ai/ui` ships 40+ `kai-*` web components across **three tiers**. This page is
8
+ the mental model for **which tier to reach for** — so you don't have to read all 40+
9
+ per-element pages to know where to start.
10
+
11
+ > The per-element **When / How / Placement** notes live on each element's own story
12
+ > (under **Components**). This page is the missing *overview* — the
13
+ > decision guide, not the per-element prose.
14
+
15
+ ## The three tiers
16
+
17
+ ### 1. Flagship / shells — _drop-in, the 90% path_
18
+
19
+ `<kai-chat>` · `<kai-workspace>`
20
+
21
+ A **whole surface in one tag**. Set `messages`, listen for `submit`, and you have a
22
+ working chat in minutes. `<kai-workspace>` adds the conversation sidebar + header
23
+ around it. These pre-wire the leaf components for you.
24
+
25
+ **Reach for these first.** Only step down a tier when the flagship doesn't fit.
26
+
27
+ ```html
28
+ <kai-chat id="chat" chat-title="Assistant" style="display:block;height:560px"></kai-chat>
29
+ <script type="module">
30
+ import '@kitn.ai/ui/elements';
31
+ const chat = document.getElementById('chat');
32
+ chat.messages = [{ id: '1', role: 'assistant', content: 'Hi!' }];
33
+ chat.addEventListener('kai-submit', (e) => {/* call your model */});
34
+ </script>
35
+ ```
36
+
37
+ ### 2. Leaf features — _compose your own layout_
38
+
39
+ `<kai-conversations>` · `<kai-message>` · `<kai-tool>` · `<kai-reasoning>` ·
40
+ `<kai-chain-of-thought>` · `<kai-thinking-bar>` · `<kai-response-stream>` ·
41
+ `<kai-source>` / `<kai-sources>` · `<kai-skills>` · `<kai-prompt-input>` ·
42
+ `<kai-suggestions>` · `<kai-file-upload>` · `<kai-voice-input>` · `<kai-attachments>` ·
43
+ `<kai-model-switcher>` · `<kai-context>` · `<kai-scope-picker>` · `<kai-checkpoint>` ·
44
+ `<kai-feedback-bar>` · `<kai-artifact>` · `<kai-resizable>`
45
+
46
+ The **chat-specific building blocks**. Reach for these when the flagship layout
47
+ doesn't fit — you want a custom three-up shell, an inspector/canvas panel, a bespoke
48
+ header, or to render messages your own way. You own the data flow and the event
49
+ wiring; the leaves render.
50
+
51
+ `<kai-resizable>` is the **layout spine** for compose-your-own shells; `<kai-artifact>`
52
+ is the **preview/canvas** panel. See the worked example in
53
+ **Examples / Composed chat shell**.
54
+
55
+ ### 3. Primitives — _reusable anywhere_
56
+
57
+ `<kai-code-block>` · `<kai-markdown>` · `<kai-loader>` · `<kai-text-shimmer>` ·
58
+ `<kai-image>` · `<kai-empty>` · `<kai-file-tree>`
59
+
60
+ **Not chat-specific.** A code viewer, a markdown renderer, loaders, an image with a
61
+ skeleton, an empty-state, a file explorer — useful in any app. They happen to be
62
+ used inside the chat components, but ship public so you can reuse them standalone.
63
+
64
+ ### Generative UI — _agent-controlled cards_
65
+
66
+ `<kai-cards>` · `<kai-confirm>` · `<kai-choice>` · `<kai-tasks>` · `<kai-form>` · `<kai-link>` · `<kai-embed>` · `<kai-remote>`
67
+
68
+ A separate family of `kai-*` elements for **typed, interactive cards** that agents push into
69
+ the chat at runtime. They follow the same prop/event contract as the leaf features above
70
+ but are designed to be driven by a `CardEnvelope` payload from the agent, not authored
71
+ in markup. Browse **Generative UI** in the sidebar for the envelope format, the dispatcher,
72
+ and per-card API references.
73
+
74
+ ## The decision test
75
+
76
+ > **"Would a non-chat app reuse this?"**
77
+ >
78
+ > - **Yes** → it's a **primitive** (tier 3). A docs site wants `<kai-markdown>`; a
79
+ > dashboard wants `<kai-loader>`; a file browser wants `<kai-file-tree>`.
80
+ > - **No, but it's a chat building block** → it's a **leaf feature** (tier 2).
81
+ > `<kai-message>`, `<kai-tool>`, `<kai-conversations>` only make sense in a chat.
82
+ > - **I just want a working chat** → use the **flagship** (tier 1). `<kai-chat>`.
83
+
84
+ ## How they fit together
85
+
86
+ ```
87
+ flagship <kai-chat> / <kai-workspace> ← pre-wires the leaves
88
+ ▲ composed from
89
+ leaf features <kai-conversations> <kai-message> <kai-prompt-input> <kai-artifact> …
90
+ ▲ composed from / laid out by
91
+ primitives + layout <kai-markdown> <kai-code-block> <kai-loader> … inside <kai-resizable>
92
+ ```
93
+
94
+ Start at the top. Drop a tier each time you need more control.
95
+
96
+ ## Where to go next
97
+
98
+ - **Examples / Catalog** — every component with sample data + copy-pasteable
99
+ markup. Answers _"what exists?"_.
100
+ - **Examples / Composed chat shell** — a real chat assembled from leaves inside
101
+ `<kai-resizable>`, shown next to the `<kai-chat>` drop-in so the contrast is
102
+ explicit. Answers _"how do I wire it myself, and when should I?"_.
103
+ - **Components** — the per-element pages with full API + the When /
104
+ How / Placement notes.
105
+ - **Generative UI** — the agent-card system (`kai-confirm`, `kai-choice`, `kai-tasks`, …):
106
+ envelope format, dispatcher, and per-card API.
@@ -0,0 +1,79 @@
1
+ // Shared sample data for the Examples/* web-component stories. Mirrors the data
2
+ // in examples/composable/main.js so the Storybook catalog matches the external
3
+ // showcase. Plain data only — no DOM wiring here.
4
+
5
+ export const models = [
6
+ { id: 'opus', name: 'Claude Opus', provider: 'Anthropic' },
7
+ { id: 'sonnet', name: 'Claude Sonnet', provider: 'Anthropic' },
8
+ { id: 'haiku', name: 'Claude Haiku', provider: 'Anthropic' },
9
+ ];
10
+
11
+ export const context = {
12
+ usedTokens: 48200,
13
+ maxTokens: 200000,
14
+ inputTokens: 31000,
15
+ outputTokens: 17200,
16
+ estimatedCost: 0.42,
17
+ };
18
+
19
+ /** An inline SVG data-URL — handy for attachment thumbnails without a server. */
20
+ export function imgData(fill: string, glyph: string): string {
21
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96"><rect width="96" height="96" rx="12" fill="${fill}"/><text x="48" y="60" font-size="42" text-anchor="middle" fill="white">${glyph}</text></svg>`;
22
+ return 'data:image/svg+xml;utf8,' + encodeURIComponent(svg);
23
+ }
24
+
25
+ export const attachments = [
26
+ { id: '1', type: 'file', filename: 'architecture.png', mediaType: 'image/png', url: imgData('#7c3aed', '◆') },
27
+ { id: '2', type: 'file', filename: 'spec.pdf', mediaType: 'application/pdf' },
28
+ { id: '3', type: 'source-document', title: 'kitn.dev/docs', filename: 'kitn.dev' },
29
+ ];
30
+
31
+ export const conversations = [
32
+ { id: 'c1', title: 'Web component architecture', scope: { type: 'document' }, messageCount: 12, lastMessageAt: '2026-06-11T10:00:00Z', updatedAt: '2026-06-11T10:00:00Z' },
33
+ { id: 'c2', title: 'Theming & tokens', scope: { type: 'document' }, messageCount: 5, lastMessageAt: '2026-06-10T09:00:00Z', updatedAt: '2026-06-10T09:00:00Z' },
34
+ { id: 'c3', title: 'Publishing pipeline', scope: { type: 'document' }, messageCount: 8, lastMessageAt: '2026-06-09T09:00:00Z', updatedAt: '2026-06-09T09:00:00Z' },
35
+ ];
36
+
37
+ export const slashCommands = [
38
+ { id: 'summarize', label: '/summarize', description: 'Summarize the conversation', category: 'Actions' },
39
+ { id: 'translate', label: '/translate', description: 'Translate the last message', category: 'Actions' },
40
+ { id: 'image', label: '/image', description: 'Generate an image', category: 'Tools' },
41
+ ];
42
+
43
+ export const sources = [
44
+ { href: 'https://kitn.dev', title: 'kitn — the kit', description: 'Composable SolidJS + web-component chat UI.', showFavicon: true },
45
+ { href: 'https://solidjs.com', title: 'SolidJS', description: 'A reactive UI library.', showFavicon: true },
46
+ ];
47
+
48
+ /** A rich assistant turn — reasoning + a tool call + an attachment + actions. */
49
+ export const assistantMessage = {
50
+ id: 'm-a',
51
+ role: 'assistant',
52
+ content: "Here's the plan, with a quick code sample:\n```js\nconst kit = useKitn();\n```",
53
+ reasoning: { text: 'The user wants X, so I should do Y then Z.', label: 'Reasoning' },
54
+ tools: [{ type: 'search', state: 'output-available', input: { query: 'kitn docs' }, output: { hits: 3 } }],
55
+ attachments: [attachments[0]],
56
+ actions: ['copy', 'like', 'dislike', 'regenerate'],
57
+ };
58
+
59
+ export const userMessage = { id: 'm-u', role: 'user', content: 'How do I compose these myself?' };
60
+
61
+ export const cotSteps = [
62
+ { label: 'Understand the request', content: 'The user wants a composable set.' },
63
+ { label: 'Design the API', content: 'Route 1: variant + flags + events.' },
64
+ { label: 'Build & verify' },
65
+ ];
66
+
67
+ /** A short thread for the composed-shell + drop-in stories. */
68
+ export const thread = [
69
+ { id: '1', role: 'user', content: 'Can you sketch a composable chat shell?' },
70
+ {
71
+ id: '2',
72
+ role: 'assistant',
73
+ content:
74
+ 'Sure. A shell is just a layout (`<kai-resizable>`) wrapping leaf components:\n\n```html\n<kai-conversations></kai-conversations>\n<kai-message></kai-message>\n<kai-prompt-input></kai-prompt-input>\n```\n\nYou own the data + events; the leaves render.',
75
+ reasoning: { text: 'Lay out list | chat | artifact, then wire submit + select.', label: 'Reasoning' },
76
+ tools: [{ type: 'plan_layout', state: 'output-available', input: { panels: 3 }, output: { ok: true } }],
77
+ actions: ['copy', 'like', 'dislike', 'regenerate'],
78
+ },
79
+ ];