@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
@@ -2,15 +2,14 @@ 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
4
  import type { ToolPart } from '../components/tool';
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-tool': JSX.HTMLAttributes<HTMLElement>;
12
+ 'kc-tool': JSX.HTMLAttributes<HTMLElement>;
14
13
  }
15
14
  }
16
15
  }
@@ -28,7 +27,7 @@ const runningTool: ToolPart = {
28
27
  input: { query: 'kitn docs' },
29
28
  };
30
29
 
31
- /** Render the actual `<kitn-tool>` custom element with a `tool` property. */
30
+ /** Render the actual `<kc-tool>` custom element with a `tool` property. */
32
31
  function ToolElement(props: { tool: ToolPart; open?: boolean }) {
33
32
  let el: (HTMLElement & { tool?: ToolPart; open?: boolean }) | undefined;
34
33
  onMount(() => {
@@ -38,12 +37,12 @@ function ToolElement(props: { tool: ToolPart; open?: boolean }) {
38
37
  }
39
38
  });
40
39
  return (
41
- <kitn-tool ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
40
+ <kc-tool ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
42
41
  );
43
42
  }
44
43
 
45
44
  const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
46
- <kitn-tool id="tool" open></kitn-tool>
45
+ <kc-tool id="tool" open></kc-tool>
47
46
 
48
47
  <script type="module">
49
48
  import '@kitnai/chat/elements'; // registers the custom elements
@@ -58,20 +57,18 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
58
57
  </script>`;
59
58
 
60
59
  const meta = {
61
- title: 'Web Components/kitn-tool',
60
+ title: 'Web Components/kc-tool',
62
61
  tags: ['autodocs'],
63
- argTypes: argTypesFor('kitn-tool'),
62
+ argTypes: argTypesFor('kc-tool'),
64
63
  parameters: {
65
64
  layout: 'fullscreen',
66
65
  docs: {
67
- description: {
68
- component: [
69
- '`<kitn-tool>` is the framework-agnostic **web component** for a single tool-call panel — a collapsible input/output inspector with a state badge — isolated in **Shadow DOM**.',
66
+ description: specDescription('kc-tool', [
67
+ '`<kc-tool>` is the framework-agnostic **web component** for a single tool-call panel — a collapsible input/output inspector with a state badge — isolated in **Shadow DOM**.',
70
68
  '**When to use:** rendering an agent/tool-call trace in a non-Solid app. In SolidJS, use the `Tool` primitive directly.',
71
69
  "**How to use:** register once with `import '@kitnai/chat/elements'`, set the call via the `tool` **property** (`el.tool = {...}`), and add the `open` flag to start it expanded.",
72
70
  'See the **Code** tab for HTML usage.',
73
- ].join('\n\n'),
74
- },
71
+ ]),
75
72
  },
76
73
  },
77
74
  } satisfies Meta;
@@ -79,12 +76,6 @@ const meta = {
79
76
  export default meta;
80
77
  type Story = StoryObj;
81
78
 
82
- /** Full generated API reference — properties, events, tokens, and composed-from. */
83
- export const API: Story = {
84
- render: () => <ElementSpec tag="kitn-tool" />,
85
- parameters: { layout: 'padded' },
86
- };
87
-
88
79
  /** A completed call with input and output, started expanded. */
89
80
  export const Completed: Story = {
90
81
  render: () => <ToolElement tool={completedTool} open />,
@@ -1,5 +1,5 @@
1
1
  import { Show } from 'solid-js';
2
- import { defineKitnElement } from './define';
2
+ import { defineWebComponent } from './define';
3
3
  import { Tool, type ToolPart } from '../components/tool';
4
4
 
5
5
  interface Props extends Record<string, unknown> {
@@ -10,10 +10,10 @@ interface Props extends Record<string, unknown> {
10
10
  }
11
11
 
12
12
  /**
13
- * `<kitn-tool>` — a collapsible tool-call panel (input/output inspection with a
13
+ * `<kc-tool>` — a collapsible tool-call panel (input/output inspection with a
14
14
  * state badge). Data via the `tool` property; `open` flag starts it expanded.
15
15
  */
16
- defineKitnElement<Props>('kitn-tool', {
16
+ defineWebComponent<Props>('kc-tool', {
17
17
  tool: undefined,
18
18
  open: false,
19
19
  }, (props, { flag }) => (
@@ -1,22 +1,21 @@
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-voice-input': JSX.HTMLAttributes<HTMLElement> & {
11
+ 'kc-voice-input': JSX.HTMLAttributes<HTMLElement> & {
13
12
  disabled?: boolean | string;
14
13
  };
15
14
  }
16
15
  }
17
16
  }
18
17
 
19
- /** Render `<kitn-voice-input>` with a stub `transcribe` function-property. */
18
+ /** Render `<kc-voice-input>` with a stub `transcribe` function-property. */
20
19
  function VoiceElement(props: { disabled?: boolean }) {
21
20
  let el: (HTMLElement & { transcribe?: (audio: Blob) => Promise<string> }) | undefined;
22
21
  onMount(() => {
@@ -33,7 +32,7 @@ function VoiceElement(props: { disabled?: boolean }) {
33
32
  });
34
33
  });
35
34
  return (
36
- <kitn-voice-input
35
+ <kc-voice-input
37
36
  ref={(e) => (el = e as HTMLElement)}
38
37
  disabled={props.disabled ? true : undefined}
39
38
  style={{ display: 'inline-block', padding: '40px' }}
@@ -42,7 +41,7 @@ function VoiceElement(props: { disabled?: boolean }) {
42
41
  }
43
42
 
44
43
  const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
45
- <kitn-voice-input id="voice"></kitn-voice-input>
44
+ <kc-voice-input id="voice"></kc-voice-input>
46
45
 
47
46
  <script type="module">
48
47
  import '@kitnai/chat/elements'; // registers the custom elements
@@ -57,20 +56,18 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
57
56
  </script>`;
58
57
 
59
58
  const meta = {
60
- title: 'Web Components/kitn-voice-input',
59
+ title: 'Web Components/kc-voice-input',
61
60
  tags: ['autodocs'],
62
- argTypes: argTypesFor('kitn-voice-input'),
61
+ argTypes: argTypesFor('kc-voice-input'),
63
62
  parameters: {
64
63
  layout: 'fullscreen',
65
64
  docs: {
66
- description: {
67
- component: [
68
- '`<kitn-voice-input>` is the framework-agnostic **web component** for a mic button that records and transcribes audio — isolated in **Shadow DOM**. It is the canonical **function-property** element.',
65
+ description: specDescription('kc-voice-input', [
66
+ '`<kc-voice-input>` is the framework-agnostic **web component** for a mic button that records and transcribes audio — isolated in **Shadow DOM**. It is the canonical **function-property** element.',
69
67
  '**When to use:** adding voice dictation to an input in a non-Solid app. In SolidJS, use the `VoiceInput` primitive.',
70
68
  "**How to use:** register once with `import '@kitnai/chat/elements'`, then set the `transcribe` **function property** (`el.transcribe = async blob => '...'`) — a value-returning callback can't be modelled as an event. It also emits `audiocaptured` (raw blob) and `transcription` (text) **CustomEvents**.",
71
69
  'See the **Code** tab for HTML usage.',
72
- ].join('\n\n'),
73
- },
70
+ ]),
74
71
  },
75
72
  },
76
73
  } satisfies Meta;
@@ -78,12 +75,6 @@ const meta = {
78
75
  export default meta;
79
76
  type Story = StoryObj;
80
77
 
81
- /** Full generated API reference — properties, events, tokens, and composed-from. */
82
- export const API: Story = {
83
- render: () => <ElementSpec tag="kitn-voice-input" />,
84
- parameters: { layout: 'padded' },
85
- };
86
-
87
78
  /** A working mic button wired to a stub transcriber. */
88
79
  export const Default: Story = {
89
80
  render: () => <VoiceElement />,
@@ -1,4 +1,4 @@
1
- import { defineKitnElement } from './define';
1
+ import { defineWebComponent } from './define';
2
2
  import { VoiceInput } from '../components/voice-input';
3
3
 
4
4
  interface Props extends Record<string, unknown> {
@@ -12,7 +12,7 @@ interface Props extends Record<string, unknown> {
12
12
  disabled?: boolean;
13
13
  }
14
14
 
15
- /** Events fired by `<kitn-voice-input>`. */
15
+ /** Events fired by `<kc-voice-input>`. */
16
16
  interface Events {
17
17
  /** Raw audio captured (before transcription) — for hosts that prefer to
18
18
  * handle transcription themselves instead of via the `transcribe` property. */
@@ -22,11 +22,11 @@ interface Events {
22
22
  }
23
23
 
24
24
  /**
25
- * `<kitn-voice-input>` — a mic button that records and transcribes. The
25
+ * `<kc-voice-input>` — a mic button that records and transcribes. The
26
26
  * canonical **function-property** element: set `el.transcribe` to your async
27
27
  * transcriber. Also emits `audiocaptured` (raw blob) and `transcription` (text).
28
28
  */
29
- defineKitnElement<Props, Events>('kitn-voice-input', {
29
+ defineWebComponent<Props, Events>('kc-voice-input', {
30
30
  transcribe: undefined,
31
31
  disabled: false,
32
32
  }, (props, { dispatch, flag }) => (
package/src/index.ts CHANGED
@@ -15,6 +15,92 @@ export { ChatConfig, useChatConfig, proseClass, textClass } from './primitives/c
15
15
  export type { ChatConfigValue, ProseSize, ChatConfigProps } from './primitives/chat-config';
16
16
  export { configureCodeHighlighting, isCodeHighlightingEnabled } from './primitives/highlighter';
17
17
  export type { CodeHighlightingOptions } from './primitives/highlighter';
18
+ export { configurePdfPreview, isPdfPreviewEnabled } from './primitives/pdf-preview';
19
+ export type { PdfPreviewOptions } from './primitives/pdf-preview';
20
+
21
+ // Card Contract (generative-UI foundation)
22
+ export { CARD_CONTRACT_VERSION } from './primitives/card-contract';
23
+ export type {
24
+ CardEnvelope, CardContext, CardEvent, CardEventKind, CardHost, CardPolicy,
25
+ } from './primitives/card-contract';
26
+ export { CardProvider, useCardHost } from './primitives/card-host';
27
+ export type { CardProviderProps } from './primitives/card-host';
28
+ export { CARD_EVENT_NAME, emitCardEvent, routeCardEvent, listenForCardEvents } from './primitives/card-routing';
29
+ export { validateAgainstSchema } from './primitives/card-validate';
30
+ export type { JsonSchema, ValidationResult } from './primitives/card-validate';
31
+
32
+ // Card: kc-card (base shell) + kc-form (JSON-Schema form renderer)
33
+ export { Card } from './components/card';
34
+ export type { CardProps } from './components/card';
35
+ export { Form, validateForm, buildResult, widgetFor, orderedKeys, coerceValue } from './components/form';
36
+ export type {
37
+ FormProps,
38
+ FormField,
39
+ FormDefinition,
40
+ FormCardEnvelope,
41
+ FormValidation,
42
+ WidgetKind,
43
+ } from './components/form';
44
+
45
+ // Card: kc-confirm (approval) + kc-task-list (selectable plan)
46
+ export {
47
+ ConfirmCard,
48
+ CONFIRM_CARD_TYPE,
49
+ buttonVariantForStyle,
50
+ normalizeActions,
51
+ defaultActionId,
52
+ } from './components/confirm-card';
53
+ export type {
54
+ ConfirmCardProps,
55
+ ConfirmAction,
56
+ ConfirmActionStyle,
57
+ ConfirmTone,
58
+ ConfirmCardData,
59
+ ConfirmCardEnvelope,
60
+ } from './components/confirm-card';
61
+ export {
62
+ TaskListCard,
63
+ TASK_LIST_CARD_TYPE,
64
+ normalizeTasks,
65
+ initialSelected,
66
+ selectedInOrder,
67
+ toggleableIds,
68
+ selectAllState,
69
+ showSelectAll,
70
+ canConfirm,
71
+ isMaxReached,
72
+ confirmReason,
73
+ } from './components/task-list-card';
74
+ export type {
75
+ TaskListCardProps,
76
+ TaskListTask,
77
+ TaskListCardData,
78
+ TaskListCardResult,
79
+ TaskListCardEnvelope,
80
+ SelectAllState,
81
+ } from './components/task-list-card';
82
+
83
+ // Card: kc-link-card (OG/link preview) + kc-embed (lazy media embed)
84
+ export { LinkCard } from './components/link-card';
85
+ export type { LinkCardProps } from './components/link-card';
86
+ export { Embed } from './components/embed';
87
+ export type { EmbedProps } from './components/embed';
88
+ export {
89
+ configureLinkPreview,
90
+ resolveLinkMetadata,
91
+ hasLinkPreviewFetcher,
92
+ LINK_CARD_TYPE,
93
+ } from './primitives/link-preview';
94
+ export type { LinkCardData, LinkCardEnvelope, LinkMetadataFetcher } from './primitives/link-preview';
95
+ export {
96
+ resolveEmbed,
97
+ parseYouTubeId,
98
+ parseVimeoId,
99
+ configureEmbedAllowlist,
100
+ isGenericOriginAllowed,
101
+ EMBED_CARD_TYPE,
102
+ } from './primitives/embed-providers';
103
+ export type { EmbedCardData, EmbedCardEnvelope, EmbedProvider, ResolvedEmbed } from './primitives/embed-providers';
18
104
 
19
105
  // Layer 2: UI Primitives
20
106
  export { Button, buttonVariants } from './ui/button';
@@ -31,9 +117,15 @@ export type { TextareaProps } from './ui/textarea';
31
117
  export { Badge } from './ui/badge';
32
118
  export type { BadgeProps } from './ui/badge';
33
119
  export { Separator } from './ui/separator';
34
- export { ResizablePanelGroup, ResizablePanel, ResizableHandle } from './ui/resizable';
35
- export type { ResizablePanelGroupProps, ResizablePanelProps, ResizableHandleProps } from './ui/resizable';
120
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle, Resizable, normalizeSize, resolveToPx } from './ui/resizable';
121
+ export type { ResizablePanelGroupProps, ResizablePanelProps, ResizableHandleProps, ResizableProps, SizeValue } from './ui/resizable';
36
122
  export { Skeleton } from './ui/skeleton';
123
+ export { FileTree, buildFileTree } from './components/file-tree';
124
+ export type {
125
+ FileTreeProps, FileTreeFile, FileTreeNode, FileTreeFolderNode, FileTreeFileNode,
126
+ } from './components/file-tree';
127
+ export { Artifact } from './components/artifact';
128
+ export type { ArtifactProps, ArtifactFile, ArtifactTab } from './components/artifact';
37
129
 
38
130
  // Layer 3: AI/Feature Components
39
131
  export {
@@ -0,0 +1,60 @@
1
+ // src/primitives/card-contract.ts
2
+ // The frozen Card Contract: the one typed contract every card speaks across both
3
+ // transports (native <kc-*> + remote iframe). Pure types only — no runtime, no DOM.
4
+ // See docs/superpowers/specs/2026-06-13-card-contract-design.md.
5
+
6
+ /** Bumped on any BREAKING change to the shapes below. Additive/optional fields do not bump it. */
7
+ export const CARD_CONTRACT_VERSION = '1' as const;
8
+
9
+ /** A card the agent/server asks the chat to render. `data` conforms to the card
10
+ * type's own published JSON Schema (one schema per `type`). */
11
+ export interface CardEnvelope<TType extends string = string, TData = unknown> {
12
+ type: TType;
13
+ id: string;
14
+ data: TData;
15
+ title?: string;
16
+ }
17
+
18
+ /** Context the host pushes to every card; updated when it changes (theme, etc.). */
19
+ export interface CardContext {
20
+ theme: { mode: 'light' | 'dark'; tokens?: Record<string, string> };
21
+ locale: string;
22
+ conversationId?: string;
23
+ /** Remote (iframe) cards only: short-lived signed token. Never long-lived. */
24
+ authToken?: string;
25
+ /** Host-resolved a11y prefs (e.g. reduced-motion, which doesn't cross the iframe). */
26
+ a11y?: { reducedMotion?: boolean };
27
+ }
28
+
29
+ /** Everything a card can ask the host to do. The host authorizes + routes each. */
30
+ export type CardEvent =
31
+ | { kind: 'ready'; cardId: string }
32
+ | { kind: 'submit-data'; cardId: string; data: unknown }
33
+ | { kind: 'action'; cardId: string; action: string; payload?: unknown }
34
+ | { kind: 'send-prompt'; cardId: string; text: string; mode?: 'compose' | 'send'; context?: unknown }
35
+ | { kind: 'open'; cardId: string; url: string; target?: 'tab' | 'artifact' }
36
+ | { kind: 'resize'; cardId: string; height: number }
37
+ | { kind: 'state'; cardId: string; patch: unknown }
38
+ | { kind: 'dismiss'; cardId: string }
39
+ | { kind: 'error'; cardId: string; message: string };
40
+
41
+ export type CardEventKind = CardEvent['kind'];
42
+
43
+ /** What every card is handed (via native context or the iframe bridge). */
44
+ export interface CardHost {
45
+ context(): CardContext;
46
+ emit(event: CardEvent): void;
47
+ }
48
+
49
+ /** How the host routes each verb. Consumers supply handlers; defaults applied otherwise. */
50
+ export interface CardPolicy {
51
+ onSubmitData?: (cardId: string, data: unknown) => void;
52
+ onAction?: (cardId: string, action: string, payload?: unknown) => void;
53
+ onSendPrompt?: (text: string, opts: { mode: 'compose' | 'send'; context?: unknown }) => void;
54
+ onOpen?: (url: string, target: 'tab' | 'artifact') => void;
55
+ onState?: (cardId: string, patch: unknown) => void;
56
+ onDismiss?: (cardId: string) => void;
57
+ onError?: (cardId: string, message: string) => void;
58
+ /** Cap on send-prompt: 'compose' (default) forbids silent sends. 'send' to allow. */
59
+ maxSendPromptMode?: 'compose' | 'send';
60
+ }
@@ -0,0 +1,35 @@
1
+ // src/primitives/card-host.tsx
2
+ // The native transport: a Solid context exposing a CardHost (context() + emit()).
3
+ // emit() routes through the contract policy via the shared routeCardEvent. Cards
4
+ // inside <kc-chat>/<CardProvider> use this; bare cards fall back to the bubbling
5
+ // kc-card event (see card-routing.listenForCardEvents).
6
+ import { createContext, useContext, type JSX } from 'solid-js';
7
+ import type { CardContext, CardEvent, CardHost, CardPolicy } from './card-contract';
8
+ import { routeCardEvent } from './card-routing';
9
+
10
+ const CardHostContext = createContext<CardHost>();
11
+
12
+ export interface CardProviderProps {
13
+ /** Ambient context, static or a reactive getter. */
14
+ context: CardContext | (() => CardContext);
15
+ /** Routing policy applied to every emitted event. */
16
+ policy?: CardPolicy;
17
+ children: JSX.Element;
18
+ }
19
+
20
+ export function CardProvider(props: CardProviderProps): JSX.Element {
21
+ // Never destructure props (Solid norm). Resolve context lazily so a getter stays reactive.
22
+ const host: CardHost = {
23
+ context: () =>
24
+ typeof props.context === 'function'
25
+ ? (props.context as () => CardContext)()
26
+ : props.context,
27
+ emit: (event: CardEvent) => routeCardEvent(props.policy, event),
28
+ };
29
+ return <CardHostContext.Provider value={host}>{props.children}</CardHostContext.Provider>;
30
+ }
31
+
32
+ /** Read the current CardHost. `undefined` when no provider is present (bare card). */
33
+ export function useCardHost(): CardHost | undefined {
34
+ return useContext(CardHostContext);
35
+ }
@@ -0,0 +1,79 @@
1
+ // src/primitives/card-routing.ts
2
+ // Framework-agnostic card-event plumbing: the bubbling kc-card emitter, the single
3
+ // policy router (used by BOTH the native listener and the remote transport), and a
4
+ // host-side listener helper for the bare-element path.
5
+ import type { CardEvent, CardPolicy } from './card-contract';
6
+
7
+ /** The single contract event name. */
8
+ export const CARD_EVENT_NAME = 'kc-card';
9
+
10
+ /** Dispatch a CardEvent as the bubbling, composed `kc-card` event a host listener
11
+ * routes. NB: this is deliberately different from defineWebComponent's built-in
12
+ * non-bubbling dispatch. */
13
+ export function emitCardEvent(element: HTMLElement, event: CardEvent): void {
14
+ element.dispatchEvent(
15
+ new CustomEvent<CardEvent>(CARD_EVENT_NAME, { detail: event, bubbles: true, composed: true }),
16
+ );
17
+ }
18
+
19
+ const SAFE_SCHEMES = ['http:', 'https:', 'mailto:'];
20
+ function isSafeUrl(url: string): boolean {
21
+ try { return SAFE_SCHEMES.includes(new URL(url, 'http://_invalid_base').protocol); } catch { return false; }
22
+ }
23
+ function warnNoHandler(kind: string): void {
24
+ // eslint-disable-next-line no-console
25
+ console.warn(`[kc-card] no policy handler for "${kind}"`);
26
+ }
27
+
28
+ /** Apply the contract's policy to one event. The ONE place routing lives. */
29
+ export function routeCardEvent(policy: CardPolicy | undefined, event: CardEvent): void {
30
+ const p: CardPolicy = policy ?? {};
31
+ switch (event.kind) {
32
+ case 'ready':
33
+ break; // lifecycle; host may react via its own listener
34
+ case 'submit-data':
35
+ p.onSubmitData ? p.onSubmitData(event.cardId, event.data) : warnNoHandler('submit-data');
36
+ break;
37
+ case 'action':
38
+ p.onAction ? p.onAction(event.cardId, event.action, event.payload) : warnNoHandler('action');
39
+ break;
40
+ case 'send-prompt': {
41
+ const requested = event.mode ?? 'compose';
42
+ const mode = p.maxSendPromptMode === 'send' ? requested : 'compose';
43
+ p.onSendPrompt ? p.onSendPrompt(event.text, { mode, context: event.context }) : warnNoHandler('send-prompt');
44
+ break;
45
+ }
46
+ case 'open': {
47
+ if (!isSafeUrl(event.url)) {
48
+ p.onError ? p.onError(event.cardId, `Blocked unsafe url: ${event.url}`) : warnNoHandler('open(unsafe)');
49
+ break;
50
+ }
51
+ const target = event.target ?? 'tab';
52
+ if (p.onOpen) p.onOpen(event.url, target);
53
+ else if (typeof window !== 'undefined') window.open(event.url, '_blank', 'noopener,noreferrer');
54
+ break;
55
+ }
56
+ case 'state':
57
+ p.onState ? p.onState(event.cardId, event.patch) : warnNoHandler('state');
58
+ break;
59
+ case 'dismiss':
60
+ p.onDismiss ? p.onDismiss(event.cardId) : warnNoHandler('dismiss');
61
+ break;
62
+ case 'error':
63
+ p.onError ? p.onError(event.cardId, event.message) : warnNoHandler('error');
64
+ break;
65
+ case 'resize':
66
+ break; // transport plumbing (iframe height); not an app-policy concern natively
67
+ }
68
+ }
69
+
70
+ /** Attach a host-level `kc-card` listener that routes every bubbling card event
71
+ * through `policy`. Returns an unsubscribe fn. For the bare-element path. */
72
+ export function listenForCardEvents(
73
+ root: HTMLElement | Document,
74
+ policy: CardPolicy,
75
+ ): () => void {
76
+ const handler = (e: Event) => routeCardEvent(policy, (e as CustomEvent<CardEvent>).detail);
77
+ root.addEventListener(CARD_EVENT_NAME, handler as EventListener);
78
+ return () => root.removeEventListener(CARD_EVENT_NAME, handler as EventListener);
79
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kitn.ai/schemas/card/card-envelope.schema.json",
4
+ "title": "CardEnvelope",
5
+ "description": "A card the agent/server asks the chat to render. `data` validates against the per-type schema.",
6
+ "type": "object",
7
+ "required": ["type", "id", "data"],
8
+ "properties": {
9
+ "type": { "type": "string", "minLength": 1 },
10
+ "id": { "type": "string", "minLength": 1 },
11
+ "data": {},
12
+ "title": { "type": "string" }
13
+ }
14
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kitn.ai/schemas/card/card-event.schema.json",
4
+ "title": "CardEvent",
5
+ "description": "An event a card emits up to the host. `kind` discriminates the payload.",
6
+ "type": "object",
7
+ "required": ["kind"],
8
+ "properties": {
9
+ "kind": { "enum": ["ready", "submit-data", "action", "send-prompt", "open", "resize", "state", "dismiss", "error"] },
10
+ "cardId": { "type": "string" }
11
+ }
12
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kitn.ai/schemas/card/confirm.schema.json",
4
+ "title": "ConfirmCardData",
5
+ "description": "Data payload for a `confirm` card (CardEnvelope.data when type='confirm').",
6
+ "type": "object",
7
+ "required": ["actions"],
8
+ "properties": {
9
+ "heading": {
10
+ "type": "string",
11
+ "description": "Optional in-body heading. Distinct from CardEnvelope.title (the card chrome title)."
12
+ },
13
+ "body": {
14
+ "type": "string",
15
+ "description": "Body text. Plain text in v1 (rendered safely; no HTML injection)."
16
+ },
17
+ "tone": {
18
+ "type": "string",
19
+ "enum": ["default", "warning", "danger"],
20
+ "default": "default",
21
+ "description": "Overall card tone; 'danger' adds a warning icon + accent for destructive approvals.",
22
+ "x-kc-control": "tone"
23
+ },
24
+ "actions": {
25
+ "type": "array",
26
+ "minItems": 1,
27
+ "maxItems": 4,
28
+ "description": "The choice set. Rendered as buttons in order; max 4 keeps it a decision, not a menu.",
29
+ "items": {
30
+ "type": "object",
31
+ "required": ["id", "label"],
32
+ "properties": {
33
+ "id": {
34
+ "type": "string",
35
+ "minLength": 1,
36
+ "description": "Emitted as CardEvent.action. Must be unique within `actions`.",
37
+ "x-kc-unique": true
38
+ },
39
+ "label": { "type": "string", "minLength": 1, "description": "Visible button label." },
40
+ "style": {
41
+ "type": "string",
42
+ "enum": ["primary", "default", "destructive"],
43
+ "default": "default",
44
+ "description": "Button emphasis. 'destructive' = red/danger; 'primary' = filled accent.",
45
+ "x-kc-control": "button-style"
46
+ },
47
+ "payload": {
48
+ "description": "Optional opaque payload echoed back in CardEvent.payload (any JSON)."
49
+ },
50
+ "default": {
51
+ "type": "boolean",
52
+ "default": false,
53
+ "description": "If true, this action is the keyboard default (Enter) and gets initial focus. At most one should be true; the card uses the first if several are.",
54
+ "x-kc-default-action": true
55
+ }
56
+ }
57
+ }
58
+ },
59
+ "dismissible": {
60
+ "type": "boolean",
61
+ "default": false,
62
+ "description": "Show a close affordance that emits the `dismiss` verb."
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://kitn.ai/schemas/card/embed.schema.json",
4
+ "title": "EmbedCardData",
5
+ "description": "Lazy media-embed payload (YouTube / Vimeo / generic player URL).",
6
+ "type": "object",
7
+ "x-kc-card-type": "embed",
8
+ "x-kc-contract-version": "1",
9
+ "required": ["provider"],
10
+ "additionalProperties": false,
11
+ "properties": {
12
+ "provider": {
13
+ "type": "string",
14
+ "enum": ["youtube", "vimeo", "generic"],
15
+ "description": "Media provider. 'generic' frames `url` directly (must be an https embeddable player URL whose origin is app-allowlisted).",
16
+ "x-kc-control": "select"
17
+ },
18
+ "id": {
19
+ "type": "string",
20
+ "description": "Provider video id (required for youtube/vimeo when `url` is absent). e.g. 'dQw4w9WgXcQ'.",
21
+ "maxLength": 64,
22
+ "pattern": "^[A-Za-z0-9_-]+$"
23
+ },
24
+ "url": {
25
+ "type": "string",
26
+ "format": "uri",
27
+ "description": "Full media/watch URL. For youtube/vimeo it is parsed to an id; for 'generic' it is the embeddable player src (https only).",
28
+ "x-kc-format": "url"
29
+ },
30
+ "title": {
31
+ "type": "string",
32
+ "description": "Accessible title for the player iframe + the poster label. Strongly recommended for a11y.",
33
+ "maxLength": 300
34
+ },
35
+ "poster": {
36
+ "type": "string",
37
+ "format": "uri",
38
+ "description": "Thumbnail shown before play. When omitted, youtube/vimeo derive a default thumbnail; 'generic' shows a neutral play placeholder.",
39
+ "x-kc-format": "url"
40
+ },
41
+ "start": {
42
+ "type": "integer",
43
+ "minimum": 0,
44
+ "description": "Optional start offset in seconds.",
45
+ "x-kc-unit": "seconds"
46
+ },
47
+ "aspectRatio": {
48
+ "type": "string",
49
+ "enum": ["16:9", "4:3", "1:1", "9:16"],
50
+ "default": "16:9",
51
+ "description": "Player box aspect ratio (CSS aspect-ratio).",
52
+ "x-kc-control": "select"
53
+ }
54
+ },
55
+ "allOf": [
56
+ {
57
+ "if": { "properties": { "provider": { "const": "generic" } } },
58
+ "then": { "required": ["url"] }
59
+ },
60
+ {
61
+ "if": { "properties": { "provider": { "enum": ["youtube", "vimeo"] } } },
62
+ "then": { "anyOf": [{ "required": ["id"] }, { "required": ["url"] }] }
63
+ }
64
+ ]
65
+ }