@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
@@ -1,16 +1,15 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
- import './register'; // side effect: registers <kitn-chat>, <kitn-conversation-list>, <kitn-prompt-input>
3
+ import './register'; // side effect: registers <kc-chat>, <kc-conversations>, <kc-prompt-input>
4
4
  import type { AttachmentData } from '../components/attachments';
5
- import { ElementSpec } from '../stories/docs/element-spec';
6
- import { argTypesFor } from '../stories/docs/element-controls';
5
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
7
6
 
8
7
  // The web components are custom DOM elements, so declare the tags for JSX.
9
8
  declare module 'solid-js' {
10
9
  // eslint-disable-next-line @typescript-eslint/no-namespace
11
10
  namespace JSX {
12
11
  interface IntrinsicElements {
13
- 'kitn-prompt-input': JSX.HTMLAttributes<HTMLElement>;
12
+ 'kc-prompt-input': JSX.HTMLAttributes<HTMLElement>;
14
13
  }
15
14
  }
16
15
  }
@@ -42,7 +41,7 @@ interface PromptInputEl extends HTMLElement {
42
41
  attachments?: AttachmentData[];
43
42
  }
44
43
 
45
- /** Live demo of the actual `<kitn-prompt-input>` custom element (Shadow DOM and all). */
44
+ /** Live demo of the actual `<kc-prompt-input>` custom element (Shadow DOM and all). */
46
45
  function PromptInputElement(props: { search?: boolean; voice?: boolean; attachments?: AttachmentData[]; args?: Record<string, unknown> }) {
47
46
  let el: PromptInputEl | undefined;
48
47
  onMount(() => {
@@ -68,7 +67,7 @@ function PromptInputElement(props: { search?: boolean; voice?: boolean; attachme
68
67
  el.addEventListener('voice', () => console.log('voice clicked'));
69
68
  });
70
69
  return (
71
- <kitn-prompt-input
70
+ <kc-prompt-input
72
71
  ref={(e) => (el = e as PromptInputEl)}
73
72
  style={{ display: 'block', width: '100%', padding: '16px' }}
74
73
  />
@@ -76,7 +75,7 @@ function PromptInputElement(props: { search?: boolean; voice?: boolean; attachme
76
75
  }
77
76
 
78
77
  const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
79
- <kitn-prompt-input id="input" style="display:block; width:100%;"></kitn-prompt-input>
78
+ <kc-prompt-input id="input" style="display:block; width:100%;"></kc-prompt-input>
80
79
 
81
80
  <script type="module">
82
81
  import '@kitnai/chat/elements'; // registers the custom elements
@@ -109,7 +108,7 @@ function Composer() {
109
108
  el.suggestions = ['Summarize this thread', 'Draft a reply'];
110
109
  });
111
110
  return (
112
- <kitn-prompt-input
111
+ <kc-prompt-input
113
112
  ref={el}
114
113
  style={{ display: 'block', width: '100%' }}
115
114
  on:submit={(e) => console.log('send:', e.detail.value)}
@@ -120,21 +119,19 @@ function Composer() {
120
119
  }`;
121
120
 
122
121
  const meta = {
123
- title: 'Web Components/kitn-prompt-input',
122
+ title: 'Web Components/kc-prompt-input',
124
123
  tags: ['autodocs'],
125
- argTypes: argTypesFor('kitn-prompt-input'),
124
+ argTypes: argTypesFor('kc-prompt-input'),
126
125
  parameters: {
127
126
  layout: 'fullscreen',
128
127
  docs: {
129
- description: {
130
- component: [
131
- '`<kitn-prompt-input>` is the framework-agnostic **web component** version of the chat composer — an auto-resizing textarea with a send button and optional suggestion chips, isolated in **Shadow DOM** so the host page\'s CSS can\'t leak in and the kit\'s styles can\'t leak out. SolidJS is bundled in, so the host needs nothing.',
128
+ description: specDescription('kc-prompt-input', [
129
+ '`<kc-prompt-input>` is the framework-agnostic **web component** version of the chat composer — an auto-resizing textarea with a send button and optional suggestion chips, isolated in **Shadow DOM** so the host page\'s CSS can\'t leak in and the kit\'s styles can\'t leak out. SolidJS is bundled in, so the host needs nothing.',
132
130
  '**When to use:** adding a message composer to a non-Solid app (React, Vue, Svelte, plain HTML), or anywhere you want zero style conflicts. If you *are* in SolidJS and want fine-grained control, compose the `PromptInput` primitives instead.',
133
131
  '**How to use:** register once with `import \'@kitnai/chat/elements\'`, configure it with JS **properties** (`placeholder`, `value`, `disabled`, `loading`, `suggestions`, `attachments`) and flag attributes (`search`, `voice` to show the Globe/Mic toolbar buttons), and listen for **CustomEvents** (`submit`, `valuechange`, `suggestionclick`, `search`, `voice`) directly on the element. Leave `value` unset to let the element manage its own input state; seed `attachments` to pre-populate staged files.',
134
132
  '**Placement:** pinned to the bottom of a chat surface, full width. Set `loading` while a response streams to show the busy state, and `disabled` to block input entirely.',
135
133
  'See the **Code** tab below for the HTML usage; the *SolidJS* story shows the same element inside a Solid component.',
136
- ].join('\n\n'),
137
- },
134
+ ]),
138
135
  },
139
136
  },
140
137
  } satisfies Meta;
@@ -165,7 +162,7 @@ export const InSolidJS: Story = {
165
162
  };
166
163
 
167
164
  const TOOLBAR_SNIPPET = `<!-- show the Search (Globe) + Voice (Mic) toolbar buttons -->
168
- <kitn-prompt-input id="input" search voice></kitn-prompt-input>
165
+ <kc-prompt-input id="input" search voice></kc-prompt-input>
169
166
 
170
167
  <script type="module">
171
168
  import '@kitnai/chat/elements';
@@ -183,7 +180,7 @@ export const WithVoiceAndSearch: Story = {
183
180
  };
184
181
 
185
182
  const ATTACHMENTS_SNIPPET = `<!-- seed staged attachments without an upload -->
186
- <kitn-prompt-input id="input" voice></kitn-prompt-input>
183
+ <kc-prompt-input id="input" voice></kc-prompt-input>
187
184
 
188
185
  <script type="module">
189
186
  import '@kitnai/chat/elements';
@@ -204,8 +201,3 @@ export const WithAttachments: Story = {
204
201
  parameters: { docs: { source: { code: ATTACHMENTS_SNIPPET, language: 'html' } } },
205
202
  };
206
203
 
207
- /** Full generated API reference — properties, events, tokens, and the SolidJS components this element is composed from. */
208
- export const API: Story = {
209
- render: () => <ElementSpec tag="kitn-prompt-input" />,
210
- parameters: { layout: 'padded' },
211
- };
@@ -1,5 +1,5 @@
1
1
  import { createEffect, createSignal } from 'solid-js';
2
- import { defineKitnElement } from './define';
2
+ import { defineWebComponent } from './define';
3
3
  import { DefaultPromptInput } from './default-input';
4
4
  import type { AttachmentData } from '../components/attachments';
5
5
  import type { SlashCommandItem } from '../components/slash-command';
@@ -40,7 +40,7 @@ interface Props extends Record<string, unknown> {
40
40
  attachments?: AttachmentData[];
41
41
  }
42
42
 
43
- /** Events fired by `<kitn-prompt-input>`. */
43
+ /** Events fired by `<kc-prompt-input>`. */
44
44
  interface Events {
45
45
  /** The user submitted the prompt (Enter or send button) with its attachments. */
46
46
  submit: { value: string; attachments: AttachmentData[] };
@@ -56,7 +56,7 @@ interface Events {
56
56
  voice: Record<string, never>;
57
57
  }
58
58
 
59
- defineKitnElement<Props, Events>('kitn-prompt-input', {
59
+ defineWebComponent<Props, Events>('kc-prompt-input', {
60
60
  value: undefined,
61
61
  placeholder: 'Send a message...',
62
62
  disabled: false,
@@ -1,8 +1,7 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
- import { ElementSpec } from '../stories/docs/element-spec';
5
- import { argTypesFor } from '../stories/docs/element-controls';
4
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
6
5
 
7
6
  type Item = string | { label: string; value?: string };
8
7
 
@@ -11,7 +10,7 @@ declare module 'solid-js' {
11
10
  // eslint-disable-next-line @typescript-eslint/no-namespace
12
11
  namespace JSX {
13
12
  interface IntrinsicElements {
14
- 'kitn-prompt-suggestions': JSX.HTMLAttributes<HTMLElement> & {
13
+ 'kc-suggestions': JSX.HTMLAttributes<HTMLElement> & {
15
14
  variant?: string;
16
15
  size?: string;
17
16
  block?: boolean | string;
@@ -27,7 +26,7 @@ const suggestions: Item[] = [
27
26
  "What's deferred?",
28
27
  ];
29
28
 
30
- /** Render `<kitn-prompt-suggestions>` with `suggestions` set as a property. */
29
+ /** Render `<kc-suggestions>` with `suggestions` set as a property. */
31
30
  function SuggestionsElement(props: { suggestions: Item[]; variant?: string; size?: string; block?: boolean; highlight?: string }) {
32
31
  let el: (HTMLElement & { suggestions?: Item[] }) | undefined;
33
32
  onMount(() => {
@@ -39,7 +38,7 @@ function SuggestionsElement(props: { suggestions: Item[]; variant?: string; size
39
38
  });
40
39
  });
41
40
  return (
42
- <kitn-prompt-suggestions
41
+ <kc-suggestions
43
42
  ref={(e) => (el = e as HTMLElement)}
44
43
  variant={props.variant}
45
44
  size={props.size}
@@ -51,7 +50,7 @@ function SuggestionsElement(props: { suggestions: Item[]; variant?: string; size
51
50
  }
52
51
 
53
52
  const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
54
- <kitn-prompt-suggestions id="suggs" variant="outline"></kitn-prompt-suggestions>
53
+ <kc-suggestions id="suggs" variant="outline"></kc-suggestions>
55
54
 
56
55
  <script type="module">
57
56
  import '@kitnai/chat/elements'; // registers the custom elements
@@ -62,20 +61,18 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
62
61
  </script>`;
63
62
 
64
63
  const meta = {
65
- title: 'Web Components/kitn-prompt-suggestions',
64
+ title: 'Web Components/kc-suggestions',
66
65
  tags: ['autodocs'],
67
- argTypes: argTypesFor('kitn-prompt-suggestions'),
66
+ argTypes: argTypesFor('kc-suggestions'),
68
67
  parameters: {
69
68
  layout: 'fullscreen',
70
69
  docs: {
71
- description: {
72
- component: [
73
- '`<kitn-prompt-suggestions>` is the framework-agnostic **web component** for a row (or list) of clickable suggestion chips — starter prompts or follow-ups — isolated in **Shadow DOM**.',
70
+ description: specDescription('kc-suggestions', [
71
+ '`<kc-suggestions>` is the framework-agnostic **web component** for a row (or list) of clickable suggestion chips — starter prompts or follow-ups — isolated in **Shadow DOM**.',
74
72
  '**When to use:** offering the user quick prompts to click instead of type, usually above an input. In SolidJS, use the `PromptSuggestion` primitive.',
75
73
  "**How to use:** register once with `import '@kitnai/chat/elements'`, set the `suggestions` **property** (strings, or `{ label, value }` when the displayed text differs from the emitted value), choose a `variant` and `size` (`sm` | `md` | `lg`; pills default to `lg`), optionally add the `block` flag for full-width rows or a `highlight` substring to emphasize, and listen for the `select` **CustomEvent**.",
76
74
  'See the **Code** tab for HTML usage.',
77
- ].join('\n\n'),
78
- },
75
+ ]),
79
76
  },
80
77
  },
81
78
  } satisfies Meta;
@@ -83,12 +80,6 @@ const meta = {
83
80
  export default meta;
84
81
  type Story = StoryObj;
85
82
 
86
- /** Full generated API reference — properties, events, tokens, and composed-from. */
87
- export const API: Story = {
88
- render: () => <ElementSpec tag="kitn-prompt-suggestions" />,
89
- parameters: { layout: 'padded' },
90
- };
91
-
92
83
  /** Default outline pills, wrapping in a row. */
93
84
  export const Default: Story = {
94
85
  render: () => <SuggestionsElement suggestions={suggestions} variant="outline" />,
@@ -121,7 +112,7 @@ export const WithHighlightedSearch: Story = {
121
112
  parameters: {
122
113
  docs: {
123
114
  source: {
124
- code: `<kitn-prompt-suggestions id="suggs" highlight="Solid"></kitn-prompt-suggestions>
115
+ code: `<kc-suggestions id="suggs" highlight="Solid"></kc-suggestions>
125
116
 
126
117
  <script type="module">
127
118
  import '@kitnai/chat/elements';
@@ -156,9 +147,9 @@ export const Sizes: Story = {
156
147
  docs: {
157
148
  source: {
158
149
  code: `<!-- default pill -->
159
- <kitn-prompt-suggestions variant="outline"></kitn-prompt-suggestions>
150
+ <kc-suggestions variant="outline"></kc-suggestions>
160
151
  <!-- smaller pill -->
161
- <kitn-prompt-suggestions variant="outline" size="sm"></kitn-prompt-suggestions>`,
152
+ <kc-suggestions variant="outline" size="sm"></kc-suggestions>`,
162
153
  language: 'html',
163
154
  },
164
155
  },
@@ -1,5 +1,5 @@
1
1
  import { For } from 'solid-js';
2
- import { defineKitnElement } from './define';
2
+ import { defineWebComponent } from './define';
3
3
  import { PromptSuggestion } from '../components/prompt-suggestion';
4
4
 
5
5
  type Item = string | { label: string; value?: string };
@@ -19,7 +19,7 @@ interface Props extends Record<string, unknown> {
19
19
  highlight?: string;
20
20
  }
21
21
 
22
- /** Events fired by `<kitn-prompt-suggestions>`. */
22
+ /** Events fired by `<kc-suggestions>`. */
23
23
  interface Events {
24
24
  /** A suggestion was clicked. */
25
25
  select: { value: string };
@@ -29,11 +29,11 @@ const labelOf = (s: Item) => (typeof s === 'string' ? s : s.label);
29
29
  const valueOf = (s: Item) => (typeof s === 'string' ? s : s.value ?? s.label);
30
30
 
31
31
  /**
32
- * `<kitn-prompt-suggestions>` — a row/list of suggestion chips. Data via the
32
+ * `<kc-suggestions>` — a row/list of suggestion chips. Data via the
33
33
  * `suggestions` property; `variant`/`block`/`highlight` attributes; emits
34
34
  * `select`.
35
35
  */
36
- defineKitnElement<Props, Events>('kitn-prompt-suggestions', {
36
+ defineWebComponent<Props, Events>('kc-suggestions', {
37
37
  suggestions: [],
38
38
  variant: 'outline',
39
39
  size: undefined,
@@ -1,15 +1,14 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
- import { ElementSpec } from '../stories/docs/element-spec';
5
- import { argTypesFor } from '../stories/docs/element-controls';
4
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
6
5
 
7
6
  // The web components are custom DOM elements, so declare the tags for JSX.
8
7
  declare module 'solid-js' {
9
8
  // eslint-disable-next-line @typescript-eslint/no-namespace
10
9
  namespace JSX {
11
10
  interface IntrinsicElements {
12
- 'kitn-reasoning': JSX.HTMLAttributes<HTMLElement>;
11
+ 'kc-reasoning': JSX.HTMLAttributes<HTMLElement>;
13
12
  }
14
13
  }
15
14
  }
@@ -17,7 +16,7 @@ declare module 'solid-js' {
17
16
  const sampleText =
18
17
  'First I parse the request, then I plan the steps, then I execute and verify each one before responding.';
19
18
 
20
- /** Render the actual `<kitn-reasoning>` custom element with a `text` property. */
19
+ /** Render the actual `<kc-reasoning>` custom element with a `text` property. */
21
20
  function ReasoningElement(props: { text: string; streaming?: boolean }) {
22
21
  let el: (HTMLElement & { text?: string; streaming?: boolean }) | undefined;
23
22
  onMount(() => {
@@ -27,12 +26,12 @@ function ReasoningElement(props: { text: string; streaming?: boolean }) {
27
26
  }
28
27
  });
29
28
  return (
30
- <kitn-reasoning ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
29
+ <kc-reasoning ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
31
30
  );
32
31
  }
33
32
 
34
33
  const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
35
- <kitn-reasoning id="reason" label="Reasoning"></kitn-reasoning>
34
+ <kc-reasoning id="reason" label="Reasoning"></kc-reasoning>
36
35
 
37
36
  <script type="module">
38
37
  import '@kitnai/chat/elements'; // registers the custom elements
@@ -46,20 +45,18 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
46
45
  </script>`;
47
46
 
48
47
  const meta = {
49
- title: 'Web Components/kitn-reasoning',
48
+ title: 'Web Components/kc-reasoning',
50
49
  tags: ['autodocs'],
51
- argTypes: argTypesFor('kitn-reasoning'),
50
+ argTypes: argTypesFor('kc-reasoning'),
52
51
  parameters: {
53
52
  layout: 'fullscreen',
54
53
  docs: {
55
- description: {
56
- component: [
57
- '`<kitn-reasoning>` is the framework-agnostic **web component** for a collapsible reasoning/thinking block that auto-expands while a thought is `streaming`, isolated in **Shadow DOM**.',
54
+ description: specDescription('kc-reasoning', [
55
+ '`<kc-reasoning>` is the framework-agnostic **web component** for a collapsible reasoning/thinking block that auto-expands while a thought is `streaming`, isolated in **Shadow DOM**.',
58
56
  '**When to use:** surfacing model chain-of-thought in a non-Solid app. In SolidJS, compose the `Reasoning` primitives directly.',
59
57
  "**How to use:** register once with `import '@kitnai/chat/elements'`, set the body via the `text` **property**, set the `streaming` flag while it streams in, optionally drive the controlled `open` property, and listen for the `openchange` **CustomEvent**.",
60
58
  'See the **Code** tab for HTML usage.',
61
- ].join('\n\n'),
62
- },
59
+ ]),
63
60
  },
64
61
  },
65
62
  } satisfies Meta;
@@ -67,12 +64,6 @@ const meta = {
67
64
  export default meta;
68
65
  type Story = StoryObj;
69
66
 
70
- /** Full generated API reference — properties, events, tokens, and composed-from. */
71
- export const API: Story = {
72
- render: () => <ElementSpec tag="kitn-reasoning" />,
73
- parameters: { layout: 'padded' },
74
- };
75
-
76
67
  /** A collapsed reasoning block (the trigger toggles it). */
77
68
  export const Default: Story = {
78
69
  render: () => <ReasoningElement text={sampleText} />,
@@ -1,4 +1,4 @@
1
- import { defineKitnElement } from './define';
1
+ import { defineWebComponent } from './define';
2
2
  import { Reasoning, ReasoningTrigger, ReasoningContent } from '../components/reasoning';
3
3
  import { ChatConfig, useChatConfig } from '../primitives/chat-config';
4
4
 
@@ -16,18 +16,18 @@ interface Props extends Record<string, unknown> {
16
16
  markdown?: boolean;
17
17
  }
18
18
 
19
- /** Events fired by `<kitn-reasoning>`. */
19
+ /** Events fired by `<kc-reasoning>`. */
20
20
  interface Events {
21
21
  /** Open state changed (via the trigger or streaming auto-open). */
22
22
  openchange: { open: boolean };
23
23
  }
24
24
 
25
25
  /**
26
- * `<kitn-reasoning>` — a collapsible reasoning/thinking block that auto-expands
26
+ * `<kc-reasoning>` — a collapsible reasoning/thinking block that auto-expands
27
27
  * while `streaming`. Text via the `text` property; `markdown`/`streaming` flags;
28
28
  * `open` is a controlled property; emits `openchange`.
29
29
  */
30
- defineKitnElement<Props, Events>('kitn-reasoning', {
30
+ defineWebComponent<Props, Events>('kc-reasoning', {
31
31
  text: '',
32
32
  label: 'Reasoning',
33
33
  open: undefined,
@@ -21,7 +21,7 @@ import './chat-scope-picker';
21
21
  // Phase 3 — input ecosystem
22
22
  // (NB: SlashCommand is context-bound to PromptInput — it observes the input
23
23
  // value via usePromptInput() — so it is NOT a standalone element. It will fold
24
- // into <kitn-prompt-input> as a `slash-commands` property in a later pass.)
24
+ // into <kc-prompt-input> as a `slash-commands` property in a later pass.)
25
25
  import './prompt-suggestions';
26
26
  import './file-upload';
27
27
  import './voice-input';
@@ -35,6 +35,16 @@ import './source';
35
35
  import './response-stream';
36
36
  import './empty';
37
37
  import './chain-of-thought';
38
+ import './resizable';
39
+ import './file-tree';
40
+ import './artifact';
41
+ // Generative-UI cards (Card Contract)
42
+ import './card';
43
+ import './form';
44
+ import './link-card';
45
+ import './embed';
46
+ import './confirm-card';
47
+ import './task-list-card';
38
48
 
39
49
  export type { ChatMessage, ChatMessageAction } from './chat-types';
40
50
  export { configureCodeHighlighting, isCodeHighlightingEnabled } from '../primitives/highlighter';
@@ -0,0 +1,200 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { createSignal, type JSX } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+ import { argTypesFor, specDescription } from '../stories/docs/element-controls';
5
+
6
+ // The web components are custom DOM elements, so declare the tags for JSX.
7
+ declare module 'solid-js' {
8
+ // eslint-disable-next-line @typescript-eslint/no-namespace
9
+ namespace JSX {
10
+ interface IntrinsicElements {
11
+ 'kc-resizable': JSX.HTMLAttributes<HTMLElement> & { orientation?: string };
12
+ 'kc-resizable-item': JSX.HTMLAttributes<HTMLElement> & {
13
+ size?: string; min?: string; max?: string; locked?: boolean | string; hidden?: boolean | string;
14
+ };
15
+ }
16
+ }
17
+ }
18
+
19
+ /** A labelled placeholder pane so the layout is visible in stories. */
20
+ function Pane(props: { label: string; tone?: 'muted' | 'plain' }) {
21
+ return (
22
+ <div
23
+ style={{
24
+ height: '100%',
25
+ display: 'flex',
26
+ 'align-items': 'center',
27
+ 'justify-content': 'center',
28
+ padding: '16px',
29
+ background: props.tone === 'plain' ? 'transparent' : 'var(--color-muted, #f4f4f5)',
30
+ color: 'var(--color-muted-foreground, #71717a)',
31
+ 'font-size': '13px',
32
+ }}
33
+ >
34
+ {props.label}
35
+ </div>
36
+ );
37
+ }
38
+
39
+ /** A bordered, sized frame the group fills. */
40
+ function Frame(props: { children: JSX.Element; tall?: boolean }) {
41
+ return (
42
+ <div
43
+ style={{
44
+ height: props.tall ? '384px' : '256px',
45
+ width: '100%',
46
+ 'max-width': '768px',
47
+ border: '1px solid var(--color-border, #e4e4e7)',
48
+ 'border-radius': '8px',
49
+ overflow: 'hidden',
50
+ }}
51
+ >
52
+ {props.children}
53
+ </div>
54
+ );
55
+ }
56
+
57
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
58
+ <kc-resizable orientation="horizontal" style="display:block;height:400px">
59
+ <kc-resizable-item size="25%" min="160px"> ...list... </kc-resizable-item>
60
+ <kc-resizable-item> ...chat... </kc-resizable-item>
61
+ <kc-resizable-item size="30%"> ...preview... </kc-resizable-item>
62
+ </kc-resizable>
63
+
64
+ <script type="module">
65
+ import '@kitnai/chat/elements'; // registers the custom elements
66
+ document.querySelector('kc-resizable')
67
+ .addEventListener('change', (e) => console.log(e.detail.sizes));
68
+ </script>`;
69
+
70
+ const meta = {
71
+ title: 'Web Components/kc-resizable',
72
+ tags: ['autodocs'],
73
+ argTypes: argTypesFor('kc-resizable'),
74
+ parameters: {
75
+ layout: 'padded',
76
+ docs: {
77
+ description: specDescription('kc-resizable', [
78
+ '`<kc-resizable>` is the framework-agnostic **web component** for a composable, resizable multi-panel layout (up to **3** `<kc-resizable-item>` panels) with **auto-inserted draggable dividers** — isolated in **Shadow DOM**.',
79
+ '**When to use:** to compose an app shell out of slotted regions without hand-wiring panels and handles — e.g. `list | chat | preview`. In SolidJS, use the `Resizable` convenience (UI/Resizable) directly.',
80
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set `orientation` (`horizontal` row / `vertical` column), and put a `<kc-resizable-item>` per panel. Each item carries `size` (px or %, e.g. `\"280px\"` or `\"25%\"`), `min`/`max`, `locked` (fixed size + non-draggable neighbour), and `hidden` (drops the panel + its divider). Listen for the **`change`** event (`detail.sizes`, percent).",
81
+ '**Placement:** the layout spine for compose-your-own-chat shells — sidebar + conversation, conversation + inspector, or a three-up list/chat/preview.',
82
+ 'See the **Code** tab for HTML usage.',
83
+ ]),
84
+ },
85
+ },
86
+ args: { orientation: 'horizontal' },
87
+ } satisfies Meta;
88
+
89
+ export default meta;
90
+ type Story = StoryObj;
91
+
92
+ /** Interactive playground — flip orientation, then drag the dividers. */
93
+ export const Playground: Story = {
94
+ render: (args: { orientation?: string }) => (
95
+ <Frame>
96
+ <kc-resizable orientation={args.orientation ?? 'horizontal'}>
97
+ <kc-resizable-item size="25%" min="120px"><Pane label="List" /></kc-resizable-item>
98
+ <kc-resizable-item><Pane label="Chat" tone="plain" /></kc-resizable-item>
99
+ </kc-resizable>
100
+ </Frame>
101
+ ),
102
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
103
+ };
104
+
105
+ /** Two panels: a sized list beside a flexible chat. */
106
+ export const ListChat: Story = {
107
+ name: 'Sidebar + chat',
108
+ render: () => (
109
+ <Frame>
110
+ <kc-resizable orientation="horizontal">
111
+ <kc-resizable-item size="28%" min="140px" max="50%"><Pane label="List" /></kc-resizable-item>
112
+ <kc-resizable-item><Pane label="Chat" tone="plain" /></kc-resizable-item>
113
+ </kc-resizable>
114
+ </Frame>
115
+ ),
116
+ };
117
+
118
+ /** Three panels, two draggable dividers. */
119
+ export const ListChatPreview: Story = {
120
+ name: 'List + chat + preview',
121
+ render: () => (
122
+ <Frame>
123
+ <kc-resizable orientation="horizontal">
124
+ <kc-resizable-item size="22%" min="120px"><Pane label="List" /></kc-resizable-item>
125
+ <kc-resizable-item><Pane label="Chat" tone="plain" /></kc-resizable-item>
126
+ <kc-resizable-item size="30%" min="160px"><Pane label="Preview" /></kc-resizable-item>
127
+ </kc-resizable>
128
+ </Frame>
129
+ ),
130
+ };
131
+
132
+ /** A locked, fixed-px sidebar — its divider is a static (non-draggable) separator. */
133
+ export const LockedSidebar: Story = {
134
+ name: 'Locked sidebar',
135
+ render: () => (
136
+ <Frame>
137
+ <kc-resizable orientation="horizontal">
138
+ <kc-resizable-item size="240px" locked><Pane label="Locked sidebar (240px)" /></kc-resizable-item>
139
+ <kc-resizable-item><Pane label="Chat" tone="plain" /></kc-resizable-item>
140
+ </kc-resizable>
141
+ </Frame>
142
+ ),
143
+ };
144
+
145
+ /** Stacked top/bottom split. */
146
+ export const Vertical: Story = {
147
+ name: 'Vertical split',
148
+ render: () => (
149
+ <Frame tall>
150
+ <kc-resizable orientation="vertical">
151
+ <kc-resizable-item size="40%" min="80px"><Pane label="Top" /></kc-resizable-item>
152
+ <kc-resizable-item><Pane label="Bottom" tone="plain" /></kc-resizable-item>
153
+ </kc-resizable>
154
+ </Frame>
155
+ ),
156
+ };
157
+
158
+ /** Toggle the preview panel — its divider drops and the rest reflow. */
159
+ export const HiddenToggle: Story = {
160
+ name: 'Show / hide a panel',
161
+ render: () => {
162
+ const [showPreview, setShowPreview] = createSignal(true);
163
+ let previewItem: HTMLElement | undefined;
164
+ const toggle = () => {
165
+ setShowPreview((v) => !v);
166
+ // Drive the boolean attribute directly so the group's MutationObserver
167
+ // re-lays out (Solid sets the `hidden` IDL property, which doesn't reflect
168
+ // to the attribute on a custom element).
169
+ if (previewItem) {
170
+ if (showPreview()) previewItem.removeAttribute('hidden');
171
+ else previewItem.setAttribute('hidden', '');
172
+ }
173
+ };
174
+ return (
175
+ <div style={{ display: 'flex', 'flex-direction': 'column', gap: '8px' }}>
176
+ <button
177
+ type="button"
178
+ onClick={toggle}
179
+ style={{
180
+ 'align-self': 'flex-start',
181
+ padding: '4px 12px',
182
+ 'font-size': '13px',
183
+ border: '1px solid var(--color-border, #e4e4e7)',
184
+ 'border-radius': '6px',
185
+ cursor: 'pointer',
186
+ }}
187
+ >
188
+ {showPreview() ? 'Hide preview' : 'Show preview'}
189
+ </button>
190
+ <Frame>
191
+ <kc-resizable orientation="horizontal">
192
+ <kc-resizable-item size="24%" min="120px"><Pane label="List" /></kc-resizable-item>
193
+ <kc-resizable-item><Pane label="Chat" tone="plain" /></kc-resizable-item>
194
+ <kc-resizable-item ref={(e) => (previewItem = e as HTMLElement)} size="30%"><Pane label="Preview" /></kc-resizable-item>
195
+ </kc-resizable>
196
+ </Frame>
197
+ </div>
198
+ );
199
+ },
200
+ };