@kitnai/chat 0.7.0 → 0.8.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 (214) hide show
  1. package/README.md +9 -9
  2. package/dist/custom-elements.json +1626 -883
  3. package/dist/kitn-chat.es.js +36 -36
  4. package/dist/llms/llms-full.txt +303 -142
  5. package/dist/llms/llms.txt +18 -18
  6. package/dist/schemas/card-envelope.schema.json +14 -0
  7. package/dist/schemas/card-event.schema.json +12 -0
  8. package/dist/schemas/confirm.schema.json +65 -0
  9. package/dist/schemas/embed.schema.json +65 -0
  10. package/dist/schemas/form.result.schema.json +7 -0
  11. package/dist/schemas/form.schema.json +33 -0
  12. package/dist/schemas/link.schema.json +56 -0
  13. package/dist/schemas/task-list.result.schema.json +16 -0
  14. package/dist/schemas/task-list.schema.json +78 -0
  15. package/dist/theme.tokens.css +65 -65
  16. package/dist/tsx-B8rCNbgL.js +1 -0
  17. package/dist/typescript-RycA9KXf.js +1 -0
  18. package/frameworks/react/index.tsx +356 -189
  19. package/frameworks/react/runtime.tsx +2 -2
  20. package/llms-full.txt +303 -142
  21. package/llms.txt +18 -18
  22. package/package.json +5 -2
  23. package/src/components/artifact.stories.tsx +138 -0
  24. package/src/components/artifact.tsx +581 -0
  25. package/src/components/attachments.stories.tsx +7 -8
  26. package/src/components/attachments.tsx +2 -2
  27. package/src/components/card.tsx +110 -0
  28. package/src/components/chain-of-thought.stories.tsx +7 -8
  29. package/src/components/chat-container.stories.tsx +7 -8
  30. package/src/components/chat-container.tsx +4 -0
  31. package/src/components/checkpoint.stories.tsx +7 -8
  32. package/src/components/checkpoint.tsx +3 -0
  33. package/src/components/code-block.stories.tsx +8 -9
  34. package/src/components/code-block.tsx +5 -2
  35. package/src/components/component-meta.json +3419 -0
  36. package/src/components/confirm-card.stories.tsx +74 -0
  37. package/src/components/confirm-card.tsx +299 -0
  38. package/src/components/context.stories.tsx +7 -8
  39. package/src/components/conversation-item.stories.tsx +7 -8
  40. package/src/components/conversation-item.tsx +2 -2
  41. package/src/components/conversation-list.stories.tsx +7 -8
  42. package/src/components/conversation-list.tsx +1 -1
  43. package/src/components/embed.tsx +196 -0
  44. package/src/components/empty.stories.tsx +8 -9
  45. package/src/components/feedback-bar.stories.tsx +7 -8
  46. package/src/components/file-tree.stories.tsx +73 -0
  47. package/src/components/file-tree.tsx +383 -0
  48. package/src/components/file-upload.stories.tsx +7 -8
  49. package/src/components/form-widgets.tsx +461 -0
  50. package/src/components/form.tsx +796 -0
  51. package/src/components/image.stories.tsx +7 -8
  52. package/src/components/link-card.tsx +194 -0
  53. package/src/components/loader.stories.tsx +7 -8
  54. package/src/components/markdown.stories.tsx +7 -8
  55. package/src/components/message-narrow.stories.tsx +12 -13
  56. package/src/components/message-skills.stories.tsx +16 -17
  57. package/src/components/message.stories.tsx +17 -18
  58. package/src/components/model-switcher.stories.tsx +7 -8
  59. package/src/components/prompt-input.stories.tsx +8 -9
  60. package/src/components/prompt-suggestion.stories.tsx +7 -8
  61. package/src/components/prompt-suggestion.tsx +3 -3
  62. package/src/components/reasoning.stories.tsx +7 -8
  63. package/src/components/scroll-button.stories.tsx +7 -8
  64. package/src/components/slash-command.stories.tsx +8 -9
  65. package/src/components/slash-command.tsx +2 -2
  66. package/src/components/source.stories.tsx +7 -8
  67. package/src/components/source.tsx +1 -1
  68. package/src/components/task-list-card.stories.tsx +78 -0
  69. package/src/components/task-list-card.tsx +388 -0
  70. package/src/components/text-shimmer.stories.tsx +7 -8
  71. package/src/components/thinking-bar.stories.tsx +7 -8
  72. package/src/components/tool.stories.tsx +7 -8
  73. package/src/components/tool.tsx +2 -2
  74. package/src/components/voice-input.stories.tsx +7 -8
  75. package/src/elements/artifact.stories.tsx +291 -0
  76. package/src/elements/artifact.tsx +72 -0
  77. package/src/elements/{kitn-attachments.stories.tsx → attachments.stories.tsx} +11 -20
  78. package/src/elements/attachments.tsx +4 -4
  79. package/src/elements/card.stories.tsx +118 -0
  80. package/src/elements/card.tsx +40 -0
  81. package/src/elements/catalog.stories.tsx +491 -0
  82. package/src/elements/{kitn-chain-of-thought.stories.tsx → chain-of-thought.stories.tsx} +13 -22
  83. package/src/elements/chain-of-thought.tsx +3 -3
  84. package/src/elements/{kitn-chat-scope-picker.stories.tsx → chat-scope-picker.stories.tsx} +10 -19
  85. package/src/elements/chat-scope-picker.tsx +4 -4
  86. package/src/elements/{kitn-chat-workspace.stories.tsx → chat-workspace.stories.tsx} +15 -23
  87. package/src/elements/chat-workspace.tsx +2 -2
  88. package/src/elements/{kitn-chat.stories.tsx → chat.stories.tsx} +12 -20
  89. package/src/elements/chat.tsx +2 -2
  90. package/src/elements/{kitn-checkpoint.stories.tsx → checkpoint.stories.tsx} +11 -20
  91. package/src/elements/checkpoint.tsx +8 -4
  92. package/src/elements/{kitn-code-block.stories.tsx → code-block.stories.tsx} +10 -19
  93. package/src/elements/code-block.tsx +3 -3
  94. package/src/elements/compiled.css +1 -1
  95. package/src/elements/composed-shell.stories.tsx +316 -0
  96. package/src/elements/confirm-card.stories.tsx +186 -0
  97. package/src/elements/confirm-card.tsx +45 -0
  98. package/src/elements/{kitn-context-meter.stories.tsx → context-meter.stories.tsx} +10 -19
  99. package/src/elements/context-meter.tsx +3 -3
  100. package/src/elements/{kitn-conversation-list.stories.tsx → conversation-list.stories.tsx} +12 -20
  101. package/src/elements/conversation-list.tsx +2 -2
  102. package/src/elements/css.ts +1 -1
  103. package/src/elements/define.tsx +10 -10
  104. package/src/elements/element-meta.json +1379 -733
  105. package/src/elements/element-types.d.ts +251 -125
  106. package/src/elements/embed.stories.tsx +197 -0
  107. package/src/elements/embed.tsx +35 -0
  108. package/src/elements/{kitn-empty.stories.tsx → empty.stories.tsx} +12 -21
  109. package/src/elements/empty.tsx +3 -3
  110. package/src/elements/{kitn-feedback-bar.stories.tsx → feedback-bar.stories.tsx} +11 -20
  111. package/src/elements/feedback-bar.tsx +4 -4
  112. package/src/elements/file-tree.stories.tsx +133 -0
  113. package/src/elements/file-tree.tsx +52 -0
  114. package/src/elements/{kitn-file-upload.stories.tsx → file-upload.stories.tsx} +12 -21
  115. package/src/elements/file-upload.tsx +4 -4
  116. package/src/elements/form.stories.tsx +204 -0
  117. package/src/elements/form.tsx +37 -0
  118. package/src/elements/{kitn-image.stories.tsx → image.stories.tsx} +10 -19
  119. package/src/elements/image.tsx +3 -3
  120. package/src/elements/link-card.stories.tsx +193 -0
  121. package/src/elements/link-card.tsx +34 -0
  122. package/src/elements/{kitn-loader.stories.tsx → loader.stories.tsx} +11 -20
  123. package/src/elements/loader.tsx +3 -3
  124. package/src/elements/{kitn-markdown.stories.tsx → markdown.stories.tsx} +10 -19
  125. package/src/elements/markdown.tsx +3 -3
  126. package/src/elements/{kitn-message-skills.stories.tsx → message-skills.stories.tsx} +10 -19
  127. package/src/elements/message-skills.tsx +3 -3
  128. package/src/elements/{kitn-message.stories.tsx → message.stories.tsx} +12 -21
  129. package/src/elements/message.tsx +5 -5
  130. package/src/elements/{kitn-model-switcher.stories.tsx → model-switcher.stories.tsx} +10 -19
  131. package/src/elements/model-switcher.tsx +5 -5
  132. package/src/elements/{kitn-prompt-input.stories.tsx → prompt-input.stories.tsx} +14 -22
  133. package/src/elements/prompt-input.tsx +3 -3
  134. package/src/elements/{kitn-prompt-suggestions.stories.tsx → prompt-suggestions.stories.tsx} +13 -22
  135. package/src/elements/prompt-suggestions.tsx +4 -4
  136. package/src/elements/{kitn-reasoning.stories.tsx → reasoning.stories.tsx} +10 -19
  137. package/src/elements/reasoning.tsx +4 -4
  138. package/src/elements/register.ts +11 -1
  139. package/src/elements/resizable.stories.tsx +200 -0
  140. package/src/elements/resizable.tsx +264 -0
  141. package/src/elements/{kitn-response-stream.stories.tsx → response-stream.stories.tsx} +10 -19
  142. package/src/elements/response-stream.tsx +4 -4
  143. package/src/elements/{kitn-source-list.stories.tsx → source-list.stories.tsx} +11 -20
  144. package/src/elements/{kitn-source.stories.tsx → source.stories.tsx} +12 -21
  145. package/src/elements/source.tsx +5 -5
  146. package/src/elements/styles.css +140 -1
  147. package/src/elements/task-list-card.stories.tsx +194 -0
  148. package/src/elements/task-list-card.tsx +40 -0
  149. package/src/elements/{kitn-text-shimmer.stories.tsx → text-shimmer.stories.tsx} +10 -19
  150. package/src/elements/text-shimmer.tsx +3 -3
  151. package/src/elements/{kitn-thinking-bar.stories.tsx → thinking-bar.stories.tsx} +11 -20
  152. package/src/elements/thinking-bar.tsx +5 -5
  153. package/src/elements/{kitn-tool.stories.tsx → tool.stories.tsx} +10 -19
  154. package/src/elements/tool.tsx +3 -3
  155. package/src/elements/{kitn-voice-input.stories.tsx → voice-input.stories.tsx} +10 -19
  156. package/src/elements/voice-input.tsx +4 -4
  157. package/src/index.ts +94 -2
  158. package/src/primitives/card-contract.ts +60 -0
  159. package/src/primitives/card-host.tsx +35 -0
  160. package/src/primitives/card-routing.ts +79 -0
  161. package/src/primitives/card-schemas/card-envelope.schema.json +14 -0
  162. package/src/primitives/card-schemas/card-event.schema.json +12 -0
  163. package/src/primitives/card-schemas/confirm.schema.json +65 -0
  164. package/src/primitives/card-schemas/embed.schema.json +65 -0
  165. package/src/primitives/card-schemas/form.result.schema.json +7 -0
  166. package/src/primitives/card-schemas/form.schema.json +33 -0
  167. package/src/primitives/card-schemas/link.schema.json +56 -0
  168. package/src/primitives/card-schemas/task-list.result.schema.json +16 -0
  169. package/src/primitives/card-schemas/task-list.schema.json +78 -0
  170. package/src/primitives/card-validate.ts +95 -0
  171. package/src/primitives/embed-providers.ts +254 -0
  172. package/src/primitives/highlighter.ts +4 -0
  173. package/src/primitives/link-preview.ts +87 -0
  174. package/src/primitives/pdf-preview.ts +121 -0
  175. package/src/stories/chat-panel-layout.stories.tsx +2 -1
  176. package/src/stories/chat-scene.tsx +22 -21
  177. package/src/stories/checkpoint-restore.stories.tsx +10 -10
  178. package/src/stories/conversation-with-reasoning.stories.tsx +4 -4
  179. package/src/stories/conversation-with-sources.stories.tsx +7 -7
  180. package/src/stories/docs/Accessibility.mdx +2 -2
  181. package/src/stories/docs/ForAIAgents.mdx +3 -3
  182. package/src/stories/docs/GettingStarted.mdx +2 -2
  183. package/src/stories/docs/Installation.mdx +2 -2
  184. package/src/stories/docs/Integrations.mdx +29 -29
  185. package/src/stories/docs/Introduction.mdx +3 -3
  186. package/src/stories/docs/Theming.mdx +2 -2
  187. package/src/stories/docs/element-controls.ts +32 -0
  188. package/src/stories/docs/theme-editor/theme-editor.tsx +1 -0
  189. package/src/stories/examples/ChoosingComponents.mdx +94 -0
  190. package/src/stories/examples/sample-data.ts +79 -0
  191. package/src/stories/message-actions.stories.tsx +13 -13
  192. package/src/stories/pattern-centered-conversation.stories.tsx +3 -3
  193. package/src/stories/pattern-docked-widget.stories.tsx +1 -1
  194. package/src/stories/pattern-empty-state.stories.tsx +3 -3
  195. package/src/stories/prompt-input-variants.stories.tsx +13 -13
  196. package/src/stories/streaming-response.stories.tsx +3 -3
  197. package/src/stories/typography.stories.tsx +4 -4
  198. package/src/ui/avatar.stories.tsx +7 -8
  199. package/src/ui/badge.stories.tsx +7 -8
  200. package/src/ui/button.stories.tsx +8 -9
  201. package/src/ui/button.tsx +1 -0
  202. package/src/ui/collapsible.stories.tsx +6 -7
  203. package/src/ui/dropdown.stories.tsx +6 -7
  204. package/src/ui/hover-card.stories.tsx +6 -7
  205. package/src/ui/resizable.stories.tsx +74 -9
  206. package/src/ui/resizable.tsx +351 -71
  207. package/src/ui/scroll-area.stories.tsx +6 -7
  208. package/src/ui/scroll-area.tsx +3 -1
  209. package/src/ui/separator.stories.tsx +7 -8
  210. package/src/ui/skeleton.stories.tsx +7 -8
  211. package/src/ui/textarea.stories.tsx +6 -7
  212. package/src/ui/tooltip.stories.tsx +8 -9
  213. package/theme.css +65 -65
  214. package/src/stories/docs/element-spec.tsx +0 -86
@@ -13,9 +13,9 @@ Message threads, prompt inputs, streaming responses, markdown + code rendering,
13
13
 
14
14
  ## Why @kitnai/chat
15
15
 
16
- - **Works in any framework** — drop in the framework-agnostic **web components** (`<kitn-chat>`) and they just work in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS, so SolidJS apps can also import the components natively for full compositional control.
16
+ - **Works in any framework** — drop in the framework-agnostic **web components** (`<kc-chat>`) and they just work in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS, so SolidJS apps can also import the components natively for full compositional control.
17
17
  - **Zero style conflicts** — the web components render in **Shadow DOM**, so the host page's CSS can't leak in and the kit's Tailwind can't leak out.
18
- - **Lightweight** — a markdown-only `<kitn-chat>` is **~61 KB gzip**, a single file. Syntax highlighting loads **on demand, per language, with no WASM** — and never loads at all if you don't render code.
18
+ - **Lightweight** — a markdown-only `<kc-chat>` is **~61 KB gzip**, a single file. Syntax highlighting loads **on demand, per language, with no WASM** — and never loads at all if you don't render code.
19
19
  - **~50 composable components** across three layers: headless primitives → accessible UI primitives (built in-house, WCAG 2.1 AA — no third-party UI dependency) → AI feature components.
20
20
  - **Themeable** — restyle everything by overriding a handful of `--color-*` design tokens.
21
21
 
@@ -26,7 +26,7 @@ The kit ships at two layers, and the sidebar reflects that. **Use the right one
26
26
  - **Web Components** — the framework-agnostic `<kitn-*>` custom elements. **This is what to copy into a React, Vue, Angular, Svelte, or plain-HTML app.** Data goes on JS properties, interactions come back as events.
27
27
  - **Components · SolidJS** and **UI · SolidJS** — the **native SolidJS** components (feature components) and primitives (Button, Dropdown, HoverCard, …) that the web components are *built from*. Their snippets are SolidJS JSX, so **only copy these into a SolidJS app**.
28
28
 
29
- In other words: the `<kitn-chat>` web component is a thin facade over the SolidJS `ChatContainer`/`Message`/… components — same UI, different consumption model. When in doubt, reach for **Web Components**.
29
+ In other words: the `<kc-chat>` web component is a thin facade over the SolidJS `ChatContainer`/`Message`/… components — same UI, different consumption model. When in doubt, reach for **Web Components**.
30
30
 
31
31
  ## Where to next
32
32
 
@@ -64,7 +64,7 @@ document.documentElement.classList.toggle('dark');
64
64
 
65
65
  ## Appearance settings
66
66
 
67
- `ChatConfig` (SolidJS) and matching properties on `<kitn-chat>` control non-color appearance:
67
+ `ChatConfig` (SolidJS) and matching properties on `<kc-chat>` control non-color appearance:
68
68
 
69
69
  | Setting | Values | Purpose |
70
70
  | --- | --- | --- |
@@ -79,7 +79,7 @@ document.documentElement.classList.toggle('dark');
79
79
  ```
80
80
 
81
81
  ```html
82
- <kitn-chat prose-size="base" code-theme="github-dark-dimmed"></kitn-chat>
82
+ <kc-chat prose-size="base" code-theme="github-dark-dimmed"></kc-chat>
83
83
  ```
84
84
 
85
85
  That's the whole theming surface — tokens for color, `ChatConfig` for sizing and code. Everything else is encapsulated.
@@ -4,6 +4,38 @@ type Prop = { name: string; type: string; default?: string; scalar: boolean };
4
4
  type ElementMeta = { tag: string; props: Prop[] };
5
5
  const all = meta as unknown as ElementMeta[];
6
6
 
7
+ // A small, consistent pointer at the top of every Web Component's Docs tab,
8
+ // directing readers to the dedicated API tab (which holds the full generated
9
+ // spec). Keeps the Docs tab focused on examples while keeping the spec one click
10
+ // away — and discoverable, since everyone reads the description first.
11
+ const API_POINTER =
12
+ '> **Full API reference** — every property, event, default and token is on the **API** tab above.';
13
+
14
+ /**
15
+ * Build a Storybook `docs.description` from an element's intro paragraphs, with
16
+ * the API-tab pointer prepended. The generated spec itself lives in the **API**
17
+ * tab (see `.storybook/api-tab.tsx`), not inline here.
18
+ * (`tag` is kept in the signature so callers don't churn if we re-inline later.)
19
+ */
20
+ export function specDescription(_tag: string, paragraphs: string[]): { component: string } {
21
+ return { component: [API_POINTER, ...paragraphs].join('\n\n') };
22
+ }
23
+
24
+ // The Components/UI sibling of API_POINTER — same idea, component vocabulary
25
+ // (props/callbacks/slots rather than properties/events/attributes).
26
+ const COMPONENT_POINTER =
27
+ '> **Full API reference** — every prop, callback, slot and token is on the **API** tab above.';
28
+
29
+ /**
30
+ * Build a Storybook `docs.description` for a SolidJS/UI component story, with the
31
+ * API-tab pointer prepended. The generated spec (props/callbacks/slots/tokens)
32
+ * lives on the **API** tab (see `.storybook/api-tab.tsx`), from
33
+ * `src/components/component-meta.json`.
34
+ */
35
+ export function componentDescription(paragraphs: string[]): { component: string } {
36
+ return { component: [COMPONENT_POINTER, ...paragraphs].join('\n\n') };
37
+ }
38
+
7
39
  const enumValues = (type: string): string[] | null => {
8
40
  // string-literal unions like "'light' | 'dark' | 'auto'"
9
41
  const parts = type.split('|').map((s) => s.trim());
@@ -110,6 +110,7 @@ export function ThemeEditor() {
110
110
  </div>
111
111
  <div class="flex items-center gap-2">
112
112
  <select
113
+ aria-label="Theme preset"
113
114
  class="bg-input border border-border rounded-md text-xs px-2 h-8"
114
115
  value={presetName()}
115
116
  onChange={(e) => loadPreset(e.currentTarget.value)}
@@ -0,0 +1,94 @@
1
+ import { Meta } from '@storybook/addon-docs/blocks';
2
+
3
+ <Meta title="Examples/Choosing components" />
4
+
5
+ # Choosing components
6
+
7
+ `@kitnai/chat` ships ~32 `kc-*` 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 32
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 **Web Components / kc-\***). 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
+ `<kc-chat>` · `<kc-workspace>`
20
+
21
+ A **whole surface in one tag**. Set `messages`, listen for `submit`, and you have a
22
+ working chat in minutes. `<kc-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
+ <kc-chat id="chat" chat-title="Assistant" style="display:block;height:560px"></kc-chat>
29
+ <script type="module">
30
+ import '@kitnai/chat/elements';
31
+ const chat = document.getElementById('chat');
32
+ chat.messages = [{ id: '1', role: 'assistant', content: 'Hi!' }];
33
+ chat.addEventListener('submit', (e) => {/* call your model */});
34
+ </script>
35
+ ```
36
+
37
+ ### 2. Leaf features — _compose your own layout_
38
+
39
+ `<kc-conversations>` · `<kc-message>` · `<kc-tool>` · `<kc-reasoning>` ·
40
+ `<kc-chain-of-thought>` · `<kc-thinking-bar>` · `<kc-response-stream>` ·
41
+ `<kc-source>` / `<kc-sources>` · `<kc-skills>` · `<kc-prompt-input>` ·
42
+ `<kc-suggestions>` · `<kc-file-upload>` · `<kc-voice-input>` · `<kc-attachments>` ·
43
+ `<kc-model-switcher>` · `<kc-context>` · `<kc-scope-picker>` · `<kc-checkpoint>` ·
44
+ `<kc-feedback-bar>` · `<kc-artifact>` · `<kc-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
+ `<kc-resizable>` is the **layout spine** for compose-your-own shells; `<kc-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
+ `<kc-code-block>` · `<kc-markdown>` · `<kc-loader>` · `<kc-text-shimmer>` ·
58
+ `<kc-image>` · `<kc-empty>` · `<kc-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
+ ## The decision test
65
+
66
+ > **"Would a non-chat app reuse this?"**
67
+ >
68
+ > - **Yes** → it's a **primitive** (tier 3). A docs site wants `<kc-markdown>`; a
69
+ > dashboard wants `<kc-loader>`; a file browser wants `<kc-file-tree>`.
70
+ > - **No, but it's a chat building block** → it's a **leaf feature** (tier 2).
71
+ > `<kc-message>`, `<kc-tool>`, `<kc-conversations>` only make sense in a chat.
72
+ > - **I just want a working chat** → use the **flagship** (tier 1). `<kc-chat>`.
73
+
74
+ ## How they fit together
75
+
76
+ ```
77
+ flagship <kc-chat> / <kc-workspace> ← pre-wires the leaves
78
+ ▲ composed from
79
+ leaf features <kc-conversations> <kc-message> <kc-prompt-input> <kc-artifact> …
80
+ ▲ composed from / laid out by
81
+ primitives + layout <kc-markdown> <kc-code-block> <kc-loader> … inside <kc-resizable>
82
+ ```
83
+
84
+ Start at the top. Drop a tier each time you need more control.
85
+
86
+ ## Where to go next
87
+
88
+ - **Examples / Catalog** — every component with sample data + copy-pasteable
89
+ markup. Answers _"what exists?"_.
90
+ - **Examples / Composed chat shell** — a real chat assembled from leaves inside
91
+ `<kc-resizable>`, shown next to the `<kc-chat>` drop-in so the contrast is
92
+ explicit. Answers _"how do I wire it myself, and when should I?"_.
93
+ - **Web Components / kc-\*** — the per-element pages with full API + the When /
94
+ How / Placement notes.
@@ -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 (`<kc-resizable>`) wrapping leaf components:\n\n```html\n<kc-conversations></kc-conversations>\n<kc-message></kc-message>\n<kc-prompt-input></kc-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
+ ];
@@ -46,10 +46,10 @@ async fn fetch_user(id: u64) -> Result<User, AppError> {
46
46
  Use \`anyhow::Result\` for applications and \`thiserror\` for libraries to define custom error types.`}
47
47
  </MessageContent>
48
48
  <MessageActions class="opacity-0 group-hover:opacity-100 transition-opacity">
49
- <Button variant="ghost" size="icon-sm"><Copy class="size-3.5" /></Button>
50
- <Button variant="ghost" size="icon-sm"><ThumbsUp class="size-3.5" /></Button>
51
- <Button variant="ghost" size="icon-sm"><ThumbsDown class="size-3.5" /></Button>
52
- <Button variant="ghost" size="icon-sm"><RefreshCw class="size-3.5" /></Button>
49
+ <Button variant="ghost" size="icon-sm" aria-label="Copy message"><Copy class="size-3.5" /></Button>
50
+ <Button variant="ghost" size="icon-sm" aria-label="Good response"><ThumbsUp class="size-3.5" /></Button>
51
+ <Button variant="ghost" size="icon-sm" aria-label="Bad response"><ThumbsDown class="size-3.5" /></Button>
52
+ <Button variant="ghost" size="icon-sm" aria-label="Regenerate response"><RefreshCw class="size-3.5" /></Button>
53
53
  </MessageActions>
54
54
  </div>
55
55
  </Message>
@@ -88,12 +88,12 @@ Import Tailwind in your main CSS file:
88
88
  \`\`\``}
89
89
  </MessageContent>
90
90
  <MessageActions>
91
- <Button variant="ghost" size="icon-sm"><Copy class="size-3.5" /></Button>
92
- <Button variant="ghost" size="icon-sm"><ThumbsUp class="size-3.5" /></Button>
93
- <Button variant="ghost" size="icon-sm"><ThumbsDown class="size-3.5" /></Button>
94
- <Button variant="ghost" size="icon-sm"><RefreshCw class="size-3.5" /></Button>
95
- <Button variant="ghost" size="icon-sm"><Share class="size-3.5" /></Button>
96
- <Button variant="ghost" size="icon-sm"><Bookmark class="size-3.5" /></Button>
91
+ <Button variant="ghost" size="icon-sm" aria-label="Copy message"><Copy class="size-3.5" /></Button>
92
+ <Button variant="ghost" size="icon-sm" aria-label="Good response"><ThumbsUp class="size-3.5" /></Button>
93
+ <Button variant="ghost" size="icon-sm" aria-label="Bad response"><ThumbsDown class="size-3.5" /></Button>
94
+ <Button variant="ghost" size="icon-sm" aria-label="Regenerate response"><RefreshCw class="size-3.5" /></Button>
95
+ <Button variant="ghost" size="icon-sm" aria-label="Share"><Share class="size-3.5" /></Button>
96
+ <Button variant="ghost" size="icon-sm" aria-label="Bookmark"><Bookmark class="size-3.5" /></Button>
97
97
  </MessageActions>
98
98
  </div>
99
99
  </Message>
@@ -120,11 +120,11 @@ export const WithCopyConfirmation: Story = {
120
120
  The current LTS version of Node.js is 22.x, which includes built-in support for the fetch API, test runner, and watch mode.
121
121
  </MessageContent>
122
122
  <MessageActions>
123
- <Button variant="ghost" size="icon-sm" onClick={handleCopy}>
123
+ <Button variant="ghost" size="icon-sm" aria-label={copied() ? 'Copied' : 'Copy message'} onClick={handleCopy}>
124
124
  {copied() ? <Check class="size-3.5 text-green-500" /> : <Copy class="size-3.5" />}
125
125
  </Button>
126
- <Button variant="ghost" size="icon-sm"><ThumbsUp class="size-3.5" /></Button>
127
- <Button variant="ghost" size="icon-sm"><ThumbsDown class="size-3.5" /></Button>
126
+ <Button variant="ghost" size="icon-sm" aria-label="Good response"><ThumbsUp class="size-3.5" /></Button>
127
+ <Button variant="ghost" size="icon-sm" aria-label="Bad response"><ThumbsDown class="size-3.5" /></Button>
128
128
  </MessageActions>
129
129
  </div>
130
130
  </Message>
@@ -64,8 +64,8 @@ export const Focused: Story = {
64
64
  {answer}
65
65
  </MessageContent>
66
66
  <MessageActions class="-ml-2.5 flex gap-0 opacity-0 transition-opacity duration-150 group-hover:opacity-100">
67
- <Button variant="ghost" size="icon-sm" class="rounded-full"><Copy class="size-3.5" /></Button>
68
- <Button variant="ghost" size="icon-sm" class="rounded-full"><ThumbsUp class="size-3.5" /></Button>
67
+ <Button variant="ghost" size="icon-sm" class="rounded-full" aria-label="Copy message"><Copy class="size-3.5" /></Button>
68
+ <Button variant="ghost" size="icon-sm" class="rounded-full" aria-label="Good response"><ThumbsUp class="size-3.5" /></Button>
69
69
  </MessageActions>
70
70
  </div>
71
71
  </Message>
@@ -79,7 +79,7 @@ export const Focused: Story = {
79
79
  <PromptInput value={input()} onValueChange={setInput} onSubmit={() => setInput('')}>
80
80
  <PromptInputTextarea placeholder="Reply…" class="min-h-[44px] pt-3 pl-4" />
81
81
  <PromptInputActions class="mt-2 flex w-full items-center justify-end gap-2 px-3 pb-3">
82
- <Button size="icon-sm" class="rounded-full" disabled={!input().trim()}>
82
+ <Button size="icon-sm" class="rounded-full" disabled={!input().trim()} aria-label="Send message">
83
83
  <ArrowUp class="size-4" />
84
84
  </Button>
85
85
  </PromptInputActions>
@@ -79,7 +79,7 @@ export const SupportAssistant: Story = {
79
79
  <PromptInput value={input()} onValueChange={setInput} onSubmit={() => setInput('')}>
80
80
  <PromptInputTextarea placeholder="Message support…" class="min-h-[40px] pt-2.5 pl-3.5" />
81
81
  <PromptInputActions class="mt-1.5 flex w-full items-center justify-end gap-2 px-2.5 pb-2.5">
82
- <Button size="icon-sm" class="rounded-full" disabled={!input().trim()}>
82
+ <Button size="icon-sm" class="rounded-full" disabled={!input().trim()} aria-label="Send message">
83
83
  <ArrowUp class="size-4" />
84
84
  </Button>
85
85
  </PromptInputActions>
@@ -59,10 +59,10 @@ export const NewChat: Story = {
59
59
  <PromptInputTextarea placeholder="Ask anything…" class="min-h-[44px] pt-3 pl-4" />
60
60
  <PromptInputActions class="mt-2 flex w-full items-center justify-between gap-2 px-3 pb-3">
61
61
  <div class="flex items-center gap-2">
62
- <Button variant="outline" size="icon-sm" class="rounded-full"><Plus class="size-4" /></Button>
63
- <Button variant="outline" size="sm" class="rounded-full gap-1"><Globe class="size-4" />Search</Button>
62
+ <Button variant="outline" size="icon-sm" class="rounded-full" aria-label="Add"><Plus class="size-4" /></Button>
63
+ <Button variant="outline" size="sm" class="rounded-full gap-1" aria-label="Search the web"><Globe class="size-4" />Search</Button>
64
64
  </div>
65
- <Button size="icon-sm" class="rounded-full" disabled={!input().trim()}>
65
+ <Button size="icon-sm" class="rounded-full" disabled={!input().trim()} aria-label="Send message">
66
66
  <ArrowUp class="size-4" />
67
67
  </Button>
68
68
  </PromptInputActions>
@@ -24,7 +24,7 @@ export const BasicInput: Story = {
24
24
  <PromptInput value={value()} onValueChange={setValue} onSubmit={() => setValue('')}>
25
25
  <PromptInputTextarea placeholder="Ask anything..." />
26
26
  <PromptInputActions class="justify-end">
27
- <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()}>
27
+ <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()} aria-label="Send message">
28
28
  <ArrowUp class="size-4" />
29
29
  </Button>
30
30
  </PromptInputActions>
@@ -72,7 +72,7 @@ export const WithSuggestions: Story = {
72
72
  <PromptInput value={value()} onValueChange={setValue} onSubmit={() => setValue('')}>
73
73
  <PromptInputTextarea placeholder="Ask about this document..." />
74
74
  <PromptInputActions class="justify-end">
75
- <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()}>
75
+ <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()} aria-label="Send message">
76
76
  <ArrowUp class="size-4" />
77
77
  </Button>
78
78
  </PromptInputActions>
@@ -93,12 +93,12 @@ export const WithActionButtons: Story = {
93
93
  <PromptInputTextarea placeholder="Message..." />
94
94
  <PromptInputActions class="justify-between">
95
95
  <div class="flex items-center gap-1">
96
- <Button variant="ghost" size="icon-sm"><Paperclip class="size-4 text-muted-foreground" /></Button>
97
- <Button variant="ghost" size="icon-sm"><Globe class="size-4 text-muted-foreground" /></Button>
98
- <Button variant="ghost" size="icon-sm"><Mic class="size-4 text-muted-foreground" /></Button>
99
- <Button variant="ghost" size="icon-sm"><Sparkles class="size-4 text-muted-foreground" /></Button>
96
+ <Button variant="ghost" size="icon-sm" aria-label="Attach file"><Paperclip class="size-4 text-muted-foreground" /></Button>
97
+ <Button variant="ghost" size="icon-sm" aria-label="Search the web"><Globe class="size-4 text-muted-foreground" /></Button>
98
+ <Button variant="ghost" size="icon-sm" aria-label="Voice input"><Mic class="size-4 text-muted-foreground" /></Button>
99
+ <Button variant="ghost" size="icon-sm" aria-label="AI suggestions"><Sparkles class="size-4 text-muted-foreground" /></Button>
100
100
  </div>
101
- <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()}>
101
+ <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()} aria-label="Send message">
102
102
  <ArrowUp class="size-4" />
103
103
  </Button>
104
104
  </PromptInputActions>
@@ -119,9 +119,9 @@ export const StreamingState: Story = {
119
119
  <PromptInputActions class="justify-between">
120
120
  <div class="flex items-center gap-2">
121
121
  <Loader variant="typing" size="sm" />
122
- <span class="text-xs text-muted-foreground">Generating...</span>
122
+ <span class="text-xs text-foreground">Generating...</span>
123
123
  </div>
124
- <Button variant="outline" size="icon-sm" class="rounded-full">
124
+ <Button variant="outline" size="icon-sm" class="rounded-full" aria-label="Stop">
125
125
  <Square class="size-3" />
126
126
  </Button>
127
127
  </PromptInputActions>
@@ -135,9 +135,9 @@ export const StreamingState: Story = {
135
135
  <PromptInputActions class="justify-between">
136
136
  <div class="flex items-center gap-2">
137
137
  <Loader variant="dots" size="sm" />
138
- <span class="text-xs text-muted-foreground">Thinking...</span>
138
+ <span class="text-xs text-foreground">Thinking...</span>
139
139
  </div>
140
- <Button variant="outline" size="icon-sm" class="rounded-full">
140
+ <Button variant="outline" size="icon-sm" class="rounded-full" aria-label="Stop">
141
141
  <Square class="size-3" />
142
142
  </Button>
143
143
  </PromptInputActions>
@@ -166,8 +166,8 @@ export const WithModelSelector: Story = {
166
166
  <PromptInputActions class="justify-between">
167
167
  <ModelSwitcher models={models} currentModelId={modelId()} onModelChange={setModelId} />
168
168
  <div class="flex items-center gap-1">
169
- <Button variant="ghost" size="icon-sm"><Paperclip class="size-4 text-muted-foreground" /></Button>
170
- <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()}>
169
+ <Button variant="ghost" size="icon-sm" aria-label="Attach file"><Paperclip class="size-4 text-muted-foreground" /></Button>
170
+ <Button variant="default" size="icon-sm" class="rounded-full" disabled={!value()} aria-label="Send message">
171
171
  <ArrowUp class="size-4" />
172
172
  </Button>
173
173
  </div>
@@ -107,7 +107,7 @@ export const TypewriterStream: Story = {
107
107
  <PromptInput onSubmit={startStream}>
108
108
  <PromptInputTextarea placeholder="Click send to start streaming..." />
109
109
  <PromptInputActions class="justify-end">
110
- <Button variant="default" size="icon-sm" class="rounded-full" onClick={startStream}>
110
+ <Button variant="default" size="icon-sm" class="rounded-full" onClick={startStream} aria-label="Send message">
111
111
  <ArrowUp class="size-4" />
112
112
  </Button>
113
113
  </PromptInputActions>
@@ -167,8 +167,8 @@ export const WaitingForFirstToken: Story = {
167
167
  <PromptInput disabled isLoading>
168
168
  <PromptInputTextarea placeholder="Waiting..." />
169
169
  <PromptInputActions class="justify-between">
170
- <span class="text-xs text-muted-foreground">Waiting for response...</span>
171
- <Button variant="outline" size="icon-sm" class="rounded-full">
170
+ <span class="text-xs text-foreground">Waiting for response...</span>
171
+ <Button variant="outline" size="icon-sm" class="rounded-full" aria-label="Stop">
172
172
  <Square class="size-3" />
173
173
  </Button>
174
174
  </PromptInputActions>
@@ -35,8 +35,8 @@ function TypographyScale() {
35
35
  <p class="text-muted-foreground mb-6 text-sm">
36
36
  Defined once in <code class="text-code-foreground">theme.css</code>. Each token generates a Tailwind utility
37
37
  (<code class="text-code-foreground">text-meta</code>, …). To restyle the kit's typography globally, override the
38
- namespaced <code class="text-code-foreground">--kitn-text-*</code> token on <code class="text-code-foreground">:root</code> —
39
- it pierces the Shadow&nbsp;DOM exactly like the <code class="text-code-foreground">--kitn-color-*</code> tokens. (The bare
38
+ namespaced <code class="text-code-foreground">--kc-text-*</code> token on <code class="text-code-foreground">:root</code> —
39
+ it pierces the Shadow&nbsp;DOM exactly like the <code class="text-code-foreground">--kc-color-*</code> tokens. (The bare
40
40
  <code class="text-code-foreground"> --text-*</code> names stay internal, so a host's own tokens can't collide.)
41
41
  </p>
42
42
 
@@ -60,8 +60,8 @@ function TypographyScale() {
60
60
 
61
61
  <h3 class="mt-8 mb-2 text-sm font-semibold">Override example</h3>
62
62
  <pre class="bg-muted text-foreground overflow-auto rounded-lg p-3 font-mono text-xs">{`:root {
63
- --kitn-text-body: 0.9375rem; /* bump the reading size to 15px */
64
- --kitn-text-meta: 0.8125rem; /* and the control size to 13px */
63
+ --kc-text-body: 0.9375rem; /* bump the reading size to 15px */
64
+ --kc-text-meta: 0.8125rem; /* and the control size to 13px */
65
65
  }`}</pre>
66
66
  <p class="text-muted-foreground mt-2 text-xs">
67
67
  Reading text in messages / input / markdown additionally scales with the
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { Avatar } from './avatar';
3
+ import { componentDescription } from '../stories/docs/element-controls';
3
4
 
4
5
  const meta = {
5
6
  title: 'UI/Avatar',
@@ -9,14 +10,12 @@ const meta = {
9
10
  layout: 'padded',
10
11
  docs: {
11
12
  controls: { exclude: ['use:eventListener'] },
12
- description: {
13
- component: [
14
- 'A small, rounded **avatar** that renders an image when `src` is provided and falls back to short initials otherwise.',
15
- '**When to use:** to identify the author of a message, a conversation participant, or the current user in a header or list.',
16
- '**How to use:** always pass a `fallback` (1–2 initials) and a `size`. Provide `src`/`alt` when you have an image; if the image is missing or fails, the `fallback` text shows instead.',
17
- '**Placement:** message rows (next to assistant/user content), conversation list items, account menus, and headers.',
18
- ].join('\n\n'),
19
- },
13
+ description: componentDescription([
14
+ 'A small, rounded **avatar** that renders an image when `src` is provided and falls back to short initials otherwise.',
15
+ '**When to use:** to identify the author of a message, a conversation participant, or the current user in a header or list.',
16
+ '**How to use:** always pass a `fallback` (1–2 initials) and a `size`. Provide `src`/`alt` when you have an image; if the image is missing or fails, the `fallback` text shows instead.',
17
+ '**Placement:** message rows (next to assistant/user content), conversation list items, account menus, and headers.',
18
+ ]),
20
19
  },
21
20
  },
22
21
  argTypes: {
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { Badge } from './badge';
3
+ import { componentDescription } from '../stories/docs/element-controls';
3
4
 
4
5
  const meta = {
5
6
  title: 'UI/Badge',
@@ -9,14 +10,12 @@ const meta = {
9
10
  layout: 'padded',
10
11
  docs: {
11
12
  controls: { exclude: ['use:eventListener'] },
12
- description: {
13
- component: [
14
- 'A compact pill **badge** for short status text, counts, or citation markers, rendered as an inline `<span>`.',
15
- '**When to use:** to annotate a count (unread, results), tag a status/label, or mark an inline source citation in assistant output.',
16
- '**How to use:** choose a `variant` (`default` label, `count` numeric pill, `citation` clickable source marker) and pass the text or number as children.',
17
- '**Placement:** inline with message text (citations), next to titles or list items (labels), and on toolbar/icon buttons (counts).',
18
- ].join('\n\n'),
19
- },
13
+ description: componentDescription([
14
+ 'A compact pill **badge** for short status text, counts, or citation markers, rendered as an inline `<span>`.',
15
+ '**When to use:** to annotate a count (unread, results), tag a status/label, or mark an inline source citation in assistant output.',
16
+ '**How to use:** choose a `variant` (`default` label, `count` numeric pill, `citation` clickable source marker) and pass the text or number as children.',
17
+ '**Placement:** inline with message text (citations), next to titles or list items (labels), and on toolbar/icon buttons (counts).',
18
+ ]),
20
19
  },
21
20
  },
22
21
  argTypes: {
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { fn } from 'storybook/test';
3
3
  import { Button } from './button';
4
+ import { componentDescription } from '../stories/docs/element-controls';
4
5
 
5
6
  /**
6
7
  * Convention exemplar — every component story should follow this shape:
@@ -15,14 +16,12 @@ const meta = {
15
16
  parameters: {
16
17
  layout: 'padded',
17
18
  docs: {
18
- description: {
19
- component: [
20
- 'A clickable button with style **variants** and **sizes**, built on a native `<button>` (all standard button attributes pass through).',
21
- '**When to use:** any user-triggered action submitting input, toolbar/icon actions, confirming or dismissing. Use `default` for the primary action, `ghost`/`outline` for secondary or low-emphasis actions.',
22
- '**How to use:** set `variant` and `size`, pass label or an icon as children, and wire `onClick`. For icon-only buttons use `size="icon"` / `"icon-sm"` and include an `aria-label`.',
23
- '**Placement:** prompt action bars, message action rows, dialogs, toolbars, and empty-state CTAs.',
24
- ].join('\n\n'),
25
- },
19
+ description: componentDescription([
20
+ 'A clickable button with style **variants** and **sizes**, built on a native `<button>` (all standard button attributes pass through).',
21
+ '**When to use:** any user-triggered action submitting input, toolbar/icon actions, confirming or dismissing. Use `default` for the primary action, `ghost`/`outline` for secondary or low-emphasis actions.',
22
+ '**How to use:** set `variant` and `size`, pass label or an icon as children, and wire `onClick`. For icon-only buttons use `size="icon"` / `"icon-sm"` and include an `aria-label`.',
23
+ '**Placement:** prompt action bars, message action rows, dialogs, toolbars, and empty-state CTAs.',
24
+ ]),
26
25
  },
27
26
  },
28
27
  argTypes: {
@@ -73,7 +72,7 @@ type Story = StoryObj<typeof meta>;
73
72
  *
74
73
  * Note: `Button` is a SolidJS *component* (a scoped import), not a global custom
75
74
  * element, so the unprefixed name can't conflict with anything — alias on import
76
- * if a host already has a `Button`. Only the web components (`<kitn-chat>`, …)
75
+ * if a host already has a `Button`. Only the web components (`<kc-chat>`, …)
77
76
  * are prefixed, because those claim global custom-element tag names.
78
77
  */
79
78
  const IMPORT = `import { Button } from '@kitnai/chat';`;
package/src/ui/button.tsx CHANGED
@@ -10,6 +10,7 @@ const buttonVariants = cva(
10
10
  default: 'bg-primary text-primary-foreground hover:bg-primary/90',
11
11
  ghost: 'hover:bg-muted text-foreground',
12
12
  outline: 'bg-muted/50 text-foreground hover:bg-muted',
13
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
13
14
  },
14
15
  size: {
15
16
  sm: 'h-8 px-3 text-xs rounded-md',
@@ -1,5 +1,6 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { Collapsible, CollapsibleTrigger, CollapsibleContent } from './collapsible';
3
+ import { componentDescription } from '../stories/docs/element-controls';
3
4
 
4
5
  const meta = {
5
6
  title: 'UI/Collapsible',
@@ -8,13 +9,11 @@ const meta = {
8
9
  parameters: {
9
10
  layout: 'padded',
10
11
  docs: {
11
- description: {
12
- component: [
13
- 'A two-state disclosure that expands and collapses a region of content, animating height via a CSS `grid-template-rows` `0fr` → `1fr` transition (no JS measurement, no layout thrash). The trigger carries `aria-expanded`/`aria-controls` and the collapsed content is `inert`, so it is removed from tab order and the accessibility tree. Works controlled (`open` + `onOpenChange`) or uncontrolled (`defaultOpen`).',
14
- '**When to use:** to hide secondary detail behind a toggle a reasoning/"chain of thought" panel, a tool-call payload, an expandable conversation group, an FAQ row.',
15
- '**How to use:** wrap `CollapsibleTrigger` and `CollapsibleContent` in a `Collapsible`. The trigger renders a `<button>` by default; pass `as` to render a custom element.',
16
- ].join('\n\n'),
17
- },
12
+ description: componentDescription([
13
+ 'A two-state disclosure that expands and collapses a region of content, animating height via a CSS `grid-template-rows` `0fr` → `1fr` transition (no JS measurement, no layout thrash). The trigger carries `aria-expanded`/`aria-controls` and the collapsed content is `inert`, so it is removed from tab order and the accessibility tree. Works controlled (`open` + `onOpenChange`) or uncontrolled (`defaultOpen`).',
14
+ '**When to use:** to hide secondary detail behind a toggle a reasoning/"chain of thought" panel, a tool-call payload, an expandable conversation group, an FAQ row.',
15
+ '**How to use:** wrap `CollapsibleTrigger` and `CollapsibleContent` in a `Collapsible`. The trigger renders a `<button>` by default; pass `as` to render a custom element.',
16
+ ]),
18
17
  },
19
18
  },
20
19
  render: () => <CollapsibleDemo defaultOpen />,