@kitnai/chat 0.6.0 → 0.8.0

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 (211) hide show
  1. package/README.md +9 -9
  2. package/dist/custom-elements.json +1676 -881
  3. package/dist/kitn-chat.es.js +36 -36
  4. package/dist/llms/llms-full.txt +316 -155
  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 +382 -193
  19. package/frameworks/react/runtime.tsx +2 -2
  20. package/llms-full.txt +316 -155
  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/code-block.stories.tsx +8 -9
  33. package/src/components/component-meta.json +3411 -0
  34. package/src/components/confirm-card.stories.tsx +74 -0
  35. package/src/components/confirm-card.tsx +299 -0
  36. package/src/components/context.stories.tsx +7 -8
  37. package/src/components/conversation-item.stories.tsx +7 -8
  38. package/src/components/conversation-item.tsx +2 -2
  39. package/src/components/conversation-list.stories.tsx +7 -8
  40. package/src/components/conversation-list.tsx +1 -1
  41. package/src/components/embed.tsx +196 -0
  42. package/src/components/empty.stories.tsx +8 -9
  43. package/src/components/feedback-bar.stories.tsx +7 -8
  44. package/src/components/file-tree.stories.tsx +73 -0
  45. package/src/components/file-tree.tsx +383 -0
  46. package/src/components/file-upload.stories.tsx +7 -8
  47. package/src/components/form-widgets.tsx +461 -0
  48. package/src/components/form.tsx +796 -0
  49. package/src/components/image.stories.tsx +7 -8
  50. package/src/components/link-card.tsx +194 -0
  51. package/src/components/loader.stories.tsx +7 -8
  52. package/src/components/markdown.stories.tsx +7 -8
  53. package/src/components/message-narrow.stories.tsx +12 -13
  54. package/src/components/message-skills.stories.tsx +16 -17
  55. package/src/components/message.stories.tsx +17 -18
  56. package/src/components/model-switcher.stories.tsx +7 -8
  57. package/src/components/prompt-input.stories.tsx +8 -9
  58. package/src/components/prompt-suggestion.stories.tsx +7 -8
  59. package/src/components/prompt-suggestion.tsx +3 -3
  60. package/src/components/reasoning.stories.tsx +7 -8
  61. package/src/components/scroll-button.stories.tsx +7 -8
  62. package/src/components/slash-command.stories.tsx +8 -9
  63. package/src/components/slash-command.tsx +2 -2
  64. package/src/components/source.stories.tsx +7 -8
  65. package/src/components/source.tsx +1 -1
  66. package/src/components/task-list-card.stories.tsx +78 -0
  67. package/src/components/task-list-card.tsx +388 -0
  68. package/src/components/text-shimmer.stories.tsx +7 -8
  69. package/src/components/thinking-bar.stories.tsx +7 -8
  70. package/src/components/tool.stories.tsx +7 -8
  71. package/src/components/tool.tsx +2 -2
  72. package/src/components/voice-input.stories.tsx +7 -8
  73. package/src/elements/artifact.stories.tsx +291 -0
  74. package/src/elements/artifact.tsx +72 -0
  75. package/src/elements/{kitn-attachments.stories.tsx → attachments.stories.tsx} +11 -11
  76. package/src/elements/attachments.tsx +4 -4
  77. package/src/elements/card.stories.tsx +118 -0
  78. package/src/elements/card.tsx +40 -0
  79. package/src/elements/catalog.stories.tsx +491 -0
  80. package/src/elements/{kitn-chain-of-thought.stories.tsx → chain-of-thought.stories.tsx} +13 -13
  81. package/src/elements/chain-of-thought.tsx +3 -3
  82. package/src/elements/{kitn-chat-scope-picker.stories.tsx → chat-scope-picker.stories.tsx} +10 -10
  83. package/src/elements/chat-scope-picker.tsx +4 -4
  84. package/src/elements/{kitn-chat-workspace.stories.tsx → chat-workspace.stories.tsx} +71 -29
  85. package/src/elements/chat-workspace.tsx +29 -3
  86. package/src/elements/{kitn-chat.stories.tsx → chat.stories.tsx} +61 -16
  87. package/src/elements/chat.tsx +23 -2
  88. package/src/elements/{kitn-checkpoint.stories.tsx → checkpoint.stories.tsx} +11 -11
  89. package/src/elements/checkpoint.tsx +4 -4
  90. package/src/elements/{kitn-code-block.stories.tsx → code-block.stories.tsx} +10 -10
  91. package/src/elements/code-block.tsx +3 -3
  92. package/src/elements/compiled.css +1 -1
  93. package/src/elements/composed-shell.stories.tsx +316 -0
  94. package/src/elements/confirm-card.stories.tsx +186 -0
  95. package/src/elements/confirm-card.tsx +45 -0
  96. package/src/elements/{kitn-context-meter.stories.tsx → context-meter.stories.tsx} +10 -10
  97. package/src/elements/context-meter.tsx +3 -3
  98. package/src/elements/{kitn-conversation-list.stories.tsx → conversation-list.stories.tsx} +35 -22
  99. package/src/elements/conversation-list.tsx +11 -2
  100. package/src/elements/css.ts +1 -1
  101. package/src/elements/define.tsx +10 -10
  102. package/src/elements/element-meta.json +2649 -0
  103. package/src/elements/element-types.d.ts +251 -125
  104. package/src/elements/embed.stories.tsx +197 -0
  105. package/src/elements/embed.tsx +35 -0
  106. package/src/elements/{kitn-empty.stories.tsx → empty.stories.tsx} +12 -12
  107. package/src/elements/empty.tsx +3 -3
  108. package/src/elements/{kitn-feedback-bar.stories.tsx → feedback-bar.stories.tsx} +11 -11
  109. package/src/elements/feedback-bar.tsx +4 -4
  110. package/src/elements/file-tree.stories.tsx +133 -0
  111. package/src/elements/file-tree.tsx +52 -0
  112. package/src/elements/{kitn-file-upload.stories.tsx → file-upload.stories.tsx} +12 -12
  113. package/src/elements/file-upload.tsx +4 -4
  114. package/src/elements/form.stories.tsx +204 -0
  115. package/src/elements/form.tsx +37 -0
  116. package/src/elements/{kitn-image.stories.tsx → image.stories.tsx} +10 -10
  117. package/src/elements/image.tsx +3 -3
  118. package/src/elements/link-card.stories.tsx +193 -0
  119. package/src/elements/link-card.tsx +34 -0
  120. package/src/elements/{kitn-loader.stories.tsx → loader.stories.tsx} +11 -11
  121. package/src/elements/loader.tsx +3 -3
  122. package/src/elements/{kitn-markdown.stories.tsx → markdown.stories.tsx} +10 -10
  123. package/src/elements/markdown.tsx +3 -3
  124. package/src/elements/{kitn-message-skills.stories.tsx → message-skills.stories.tsx} +10 -10
  125. package/src/elements/message-skills.tsx +3 -3
  126. package/src/elements/{kitn-message.stories.tsx → message.stories.tsx} +12 -12
  127. package/src/elements/message.tsx +5 -5
  128. package/src/elements/{kitn-model-switcher.stories.tsx → model-switcher.stories.tsx} +10 -10
  129. package/src/elements/model-switcher.tsx +5 -5
  130. package/src/elements/{kitn-prompt-input.stories.tsx → prompt-input.stories.tsx} +41 -19
  131. package/src/elements/prompt-input.tsx +5 -5
  132. package/src/elements/{kitn-prompt-suggestions.stories.tsx → prompt-suggestions.stories.tsx} +13 -13
  133. package/src/elements/prompt-suggestions.tsx +4 -4
  134. package/src/elements/{kitn-reasoning.stories.tsx → reasoning.stories.tsx} +10 -10
  135. package/src/elements/reasoning.tsx +4 -4
  136. package/src/elements/register.ts +11 -1
  137. package/src/elements/resizable.stories.tsx +200 -0
  138. package/src/elements/resizable.tsx +264 -0
  139. package/src/elements/{kitn-response-stream.stories.tsx → response-stream.stories.tsx} +10 -10
  140. package/src/elements/response-stream.tsx +4 -4
  141. package/src/elements/{kitn-source-list.stories.tsx → source-list.stories.tsx} +11 -11
  142. package/src/elements/{kitn-source.stories.tsx → source.stories.tsx} +12 -12
  143. package/src/elements/source.tsx +5 -5
  144. package/src/elements/styles.css +140 -1
  145. package/src/elements/task-list-card.stories.tsx +194 -0
  146. package/src/elements/task-list-card.tsx +40 -0
  147. package/src/elements/{kitn-text-shimmer.stories.tsx → text-shimmer.stories.tsx} +10 -10
  148. package/src/elements/text-shimmer.tsx +3 -3
  149. package/src/elements/{kitn-thinking-bar.stories.tsx → thinking-bar.stories.tsx} +11 -11
  150. package/src/elements/thinking-bar.tsx +5 -5
  151. package/src/elements/{kitn-tool.stories.tsx → tool.stories.tsx} +10 -10
  152. package/src/elements/tool.tsx +3 -3
  153. package/src/elements/{kitn-voice-input.stories.tsx → voice-input.stories.tsx} +10 -10
  154. package/src/elements/voice-input.tsx +4 -4
  155. package/src/index.ts +94 -2
  156. package/src/primitives/card-contract.ts +60 -0
  157. package/src/primitives/card-host.tsx +35 -0
  158. package/src/primitives/card-routing.ts +79 -0
  159. package/src/primitives/card-schemas/card-envelope.schema.json +14 -0
  160. package/src/primitives/card-schemas/card-event.schema.json +12 -0
  161. package/src/primitives/card-schemas/confirm.schema.json +65 -0
  162. package/src/primitives/card-schemas/embed.schema.json +65 -0
  163. package/src/primitives/card-schemas/form.result.schema.json +7 -0
  164. package/src/primitives/card-schemas/form.schema.json +33 -0
  165. package/src/primitives/card-schemas/link.schema.json +56 -0
  166. package/src/primitives/card-schemas/task-list.result.schema.json +16 -0
  167. package/src/primitives/card-schemas/task-list.schema.json +78 -0
  168. package/src/primitives/card-validate.ts +95 -0
  169. package/src/primitives/embed-providers.ts +254 -0
  170. package/src/primitives/highlighter.ts +4 -0
  171. package/src/primitives/link-preview.ts +87 -0
  172. package/src/primitives/pdf-preview.ts +121 -0
  173. package/src/stories/chat-panel-layout.stories.tsx +2 -1
  174. package/src/stories/chat-scene.tsx +22 -21
  175. package/src/stories/checkpoint-restore.stories.tsx +10 -10
  176. package/src/stories/conversation-with-reasoning.stories.tsx +4 -4
  177. package/src/stories/conversation-with-sources.stories.tsx +7 -7
  178. package/src/stories/docs/Accessibility.mdx +2 -2
  179. package/src/stories/docs/ForAIAgents.mdx +3 -3
  180. package/src/stories/docs/GettingStarted.mdx +2 -2
  181. package/src/stories/docs/Installation.mdx +2 -2
  182. package/src/stories/docs/Integrations.mdx +29 -29
  183. package/src/stories/docs/Introduction.mdx +3 -3
  184. package/src/stories/docs/Theming.mdx +2 -2
  185. package/src/stories/docs/element-controls.ts +60 -0
  186. package/src/stories/docs/theme-editor/theme-editor.tsx +1 -0
  187. package/src/stories/examples/ChoosingComponents.mdx +94 -0
  188. package/src/stories/examples/sample-data.ts +79 -0
  189. package/src/stories/message-actions.stories.tsx +13 -13
  190. package/src/stories/pattern-centered-conversation.stories.tsx +3 -3
  191. package/src/stories/pattern-docked-widget.stories.tsx +1 -1
  192. package/src/stories/pattern-empty-state.stories.tsx +3 -3
  193. package/src/stories/prompt-input-variants.stories.tsx +13 -13
  194. package/src/stories/streaming-response.stories.tsx +3 -3
  195. package/src/stories/typography.stories.tsx +4 -4
  196. package/src/ui/avatar.stories.tsx +7 -8
  197. package/src/ui/badge.stories.tsx +7 -8
  198. package/src/ui/button.stories.tsx +8 -9
  199. package/src/ui/button.tsx +1 -0
  200. package/src/ui/collapsible.stories.tsx +6 -7
  201. package/src/ui/dropdown.stories.tsx +6 -7
  202. package/src/ui/hover-card.stories.tsx +6 -7
  203. package/src/ui/resizable.stories.tsx +74 -9
  204. package/src/ui/resizable.tsx +351 -71
  205. package/src/ui/scroll-area.stories.tsx +6 -7
  206. package/src/ui/scroll-area.tsx +3 -1
  207. package/src/ui/separator.stories.tsx +7 -8
  208. package/src/ui/skeleton.stories.tsx +7 -8
  209. package/src/ui/textarea.stories.tsx +6 -7
  210. package/src/ui/tooltip.stories.tsx +8 -9
  211. package/theme.css +65 -65
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  <!-- AUTO-GENERATED by scripts/gen-llms.mjs — do not edit by hand. Run `npm run build`. -->
2
2
  # @kitnai/chat
3
3
 
4
- > Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. 28 `kitn-*` custom elements: streaming responses, markdown + code rendering, reasoning/tool panels, attachments, conversation sidebar, voice input. Zero framework dependency for consumers; the SolidJS runtime it is authored in is bundled in, so the host needs nothing.
4
+ > Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. 38 `kitn-*` custom elements: streaming responses, markdown + code rendering, reasoning/tool panels, attachments, conversation sidebar, voice input. Zero framework dependency for consumers; the SolidJS runtime it is authored in is bundled in, so the host needs nothing.
5
5
 
6
6
  ## Install
7
7
 
@@ -16,11 +16,11 @@ npm install solid-js
16
16
  This is the single most common mistake. Arrays and objects (`messages`, `models`, `context`, `suggestions`, `slashCommands`, …) MUST be assigned as JavaScript properties on the element. They CANNOT be passed as HTML attributes — an HTML attribute is always a string and will be ignored or mis-parsed.
17
17
 
18
18
  ```js
19
- const chat = document.querySelector('kitn-chat');
19
+ const chat = document.querySelector('kc-chat');
20
20
  chat.messages = [{ id: '1', role: 'assistant', content: 'Hi!' }]; // ✅ property
21
21
  ```
22
22
  ```html
23
- <kitn-chat messages="[...]"></kitn-chat> <!-- ❌ never works -->
23
+ <kc-chat messages="[...]"></kc-chat> <!-- ❌ never works -->
24
24
  ```
25
25
 
26
26
  Only scalar values (string/number/boolean) work as attributes (e.g. `placeholder`, `loading`, `theme`).
@@ -30,12 +30,12 @@ Only scalar values (string/number/boolean) work as attributes (e.g. `placeholder
30
30
  **Layer 1 — batteries-included web components** (`import '@kitnai/chat/elements'`):
31
31
  Drop an element into any framework (React, Vue, plain HTML). Data in via JS properties; interactions out via non-bubbling CustomEvents.
32
32
 
33
- - `<kitn-chat>` — full chat UI (message list + prompt input). The primary starting point.
34
- - `<kitn-conversation-list>` — sidebar conversation browser with group support.
35
- - `<kitn-prompt-input>` — standalone composer with send button.
33
+ - `<kc-chat>` — full chat UI (message list + prompt input). The primary starting point.
34
+ - `<kc-conversations>` — sidebar conversation browser with group support.
35
+ - `<kc-prompt-input>` — standalone composer with send button.
36
36
 
37
37
  **Layer 2 — composable primitives** (`import { … } from '@kitnai/chat'`):
38
- All 28 elements are also exported individually. Use them for custom layouts or features `<kitn-chat>` does not expose (ChainOfThought, FeedbackBar, ThinkingBar, VoiceInput, …). Your bundler tree-shakes the rest.
38
+ All 38 elements are also exported individually. Use them for custom layouts or features `<kc-chat>` does not expose (ChainOfThought, FeedbackBar, ThinkingBar, VoiceInput, …). Your bundler tree-shakes the rest.
39
39
 
40
40
  ## Key rules for the web components
41
41
 
@@ -43,9 +43,9 @@ All 28 elements are also exported individually. Use them for custom layouts or f
43
43
  2. **Events are non-bubbling `CustomEvent`s** — listen directly on the element:
44
44
  `chat.addEventListener('submit', (e) => console.log(e.detail.value))`
45
45
  3. **`theme` attribute** (`'light' | 'dark' | 'auto'`) works on every element. Default `auto` follows `prefers-color-scheme`.
46
- 4. **Theming via CSS custom properties** — override `--kitn-color-*` tokens on `:root`; they pierce Shadow DOM.
46
+ 4. **Theming via CSS custom properties** — override `--kc-color-*` tokens on `:root`; they pierce Shadow DOM.
47
47
 
48
- ## ChatMessage schema (required for `<kitn-chat>`)
48
+ ## ChatMessage schema (required for `<kc-chat>`)
49
49
 
50
50
  ```ts
51
51
  interface ChatMessage {
@@ -64,31 +64,31 @@ interface ChatMessage {
64
64
  **Plain HTML / CDN**
65
65
  ```html
66
66
  <script type="module" src="https://unpkg.com/@kitnai/chat/elements"></script>
67
- <kitn-chat style="display:block;height:100vh"></kitn-chat>
67
+ <kc-chat style="display:block;height:100vh"></kc-chat>
68
68
  <script type="module">
69
- const chat = document.querySelector('kitn-chat');
69
+ const chat = document.querySelector('kc-chat');
70
70
  chat.messages = [];
71
71
  </script>
72
72
  ```
73
73
 
74
74
  **React** — typed wrappers auto-set properties and expose `on<Event>` props:
75
75
  ```tsx
76
- import { KitnChat } from '@kitnai/chat/react';
77
- <KitnChat messages={messages} onSubmit={(e) => send(e.detail.value)} />
76
+ import { KcChat } from '@kitnai/chat/react';
77
+ <KcChat messages={messages} onSubmit={(e) => send(e.detail.value)} />
78
78
  ```
79
79
 
80
80
  **Vue** — use the element directly; pass arrays via `.prop`:
81
81
  ```vue
82
- <kitn-chat :messages.prop="messages" @submit="send" />
82
+ <kc-chat :messages.prop="messages" @submit="send" />
83
83
  ```
84
84
 
85
85
  ## Theming
86
86
 
87
87
  ```css
88
88
  :root {
89
- --kitn-color-background: #0f0f0f;
90
- --kitn-color-primary: #7c3aed;
91
- --kitn-color-muted: #1e1e1e;
89
+ --kc-color-background: #0f0f0f;
90
+ --kc-color-primary: #7c3aed;
91
+ --kc-color-muted: #1e1e1e;
92
92
  }
93
93
  ```
94
94
 
@@ -97,7 +97,7 @@ For Tailwind builds: `@import "@kitnai/chat/theme.css"` in your CSS.
97
97
 
98
98
  ## Docs
99
99
 
100
- - Full element reference (all 28 elements, every prop/event): ./llms-full.txt — https://kitn.dev/llms-full.txt
100
+ - Full element reference (all 38 elements, every prop/event): ./llms-full.txt — https://kitn.dev/llms-full.txt
101
101
  - Machine-readable Custom Elements Manifest: https://unpkg.com/@kitnai/chat/dist/custom-elements.json
102
102
  - Working examples: https://github.com/kitn-ai/chat/tree/main/examples
103
103
  - Storybook: https://storybook.kitn.dev
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitnai/chat",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic, Shadow-DOM web components for building AI chat interfaces — works in React, Vue, Angular, Svelte, or plain HTML. Authored in SolidJS.",
6
6
  "keywords": [
@@ -57,11 +57,13 @@
57
57
  "prepublishOnly": "npm run build",
58
58
  "prebuild": "npm run build:css",
59
59
  "build": "vite build --config vite.config.ts",
60
- "postbuild": "npm run build:theme && npm run build:api",
60
+ "postbuild": "npm run build:theme && npm run build:api && npm run build:schemas",
61
61
  "build:theme": "node scripts/build-theme-tokens.mjs",
62
62
  "build:api": "node scripts/gen-element-api.mjs",
63
+ "build:schemas": "node scripts/copy-card-schemas.mjs",
63
64
  "test": "vitest run",
64
65
  "test:react": "vitest run --config vitest.react.config.ts",
66
+ "test:storybook": "vitest run --project=storybook",
65
67
  "test:watch": "vitest",
66
68
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.react.json && tsc --noEmit -p tsconfig.react.test.json",
67
69
  "examples": "echo 'Serving repo root on http://localhost:8000 — open http://localhost:8000/examples/composable/index.html' && python3 -m http.server 8000",
@@ -86,6 +88,7 @@
86
88
  },
87
89
  "devDependencies": {
88
90
  "@solidjs/testing-library": "^0.8.0",
91
+ "@storybook/addon-a11y": "^10.4.2",
89
92
  "@storybook/addon-docs": "^10.3.5",
90
93
  "@storybook/addon-themes": "^10.3.5",
91
94
  "@storybook/addon-vitest": "^10.3.5",
@@ -0,0 +1,138 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { createSignal } from 'solid-js';
3
+ import { Artifact, type ArtifactFile, type ArtifactTab } from './artifact';
4
+ import { componentDescription } from '../stories/docs/element-controls';
5
+
6
+ // Storybook serves examples/artifact-fixtures at /artifact-fixtures.
7
+ const BASE = '/artifact-fixtures';
8
+
9
+ const FILES: ArtifactFile[] = [
10
+ {
11
+ path: 'index.html',
12
+ url: `${BASE}/index.html`,
13
+ type: 'html',
14
+ language: 'html',
15
+ code: `<!DOCTYPE html>\n<html lang="en">\n <head><link rel="stylesheet" href="css/site.css" /></head>\n <body><h1>Starboard</h1><a href="about.html">About</a></body>\n</html>`,
16
+ },
17
+ {
18
+ path: 'about.html',
19
+ url: `${BASE}/about.html`,
20
+ type: 'html',
21
+ language: 'html',
22
+ code: `<!DOCTYPE html>\n<html lang="en">\n <body data-page="about"><h1>About</h1><a href="index.html">Home</a></body>\n</html>`,
23
+ },
24
+ {
25
+ path: 'css/site.css',
26
+ url: `${BASE}/css/site.css`,
27
+ type: 'other',
28
+ language: 'css',
29
+ code: `:root { --accent: #6ea8fe; }\nbody { font-family: system-ui, sans-serif; }`,
30
+ },
31
+ { path: 'assets/logo.svg', url: `${BASE}/assets/logo.svg`, type: 'image' },
32
+ { path: 'assets/report.pdf', url: `${BASE}/assets/report.pdf`, type: 'pdf' },
33
+ ];
34
+
35
+ const meta = {
36
+ title: 'Components/Artifact',
37
+ component: Artifact,
38
+ tags: ['autodocs'],
39
+ parameters: {
40
+ layout: 'padded',
41
+ docs: {
42
+ description: componentDescription([
43
+ 'A framed, switchable **generated-artifact viewer** — the "canvas / artifacts" pattern. A sandboxed `<iframe>` with a functional nav toolbar (back · forward · reload · home + editable path field) and a **Preview | Code** toggle; the Code tab shows a `FileTree` + the active file source via `CodeBlock`.',
44
+ '**When to use:** to show an AI-generated artifact your backend hosts (page, doc, image, PDF) beside the conversation. The component frames; your backend serves — so relative links, back/forward and multi-format work natively.',
45
+ '**How to use:** set `src` to the hosted URL and `files` (`{ path, url?, code?, language?, type? }[]`) for the Code tree. The iframe `sandbox` defaults to `allow-scripts allow-forms` (not `allow-same-origin`). Handle `onNavigate(url)`, `onTabChange(tab)`, `onFileSelect(path)`.',
46
+ '**Placement:** the preview/canvas panel of a chat shell (e.g. `list | chat | artifact`). It **fills** its container — give the parent a height.',
47
+ ]),
48
+ },
49
+ },
50
+ argTypes: {
51
+ files: { control: false },
52
+ src: { control: 'text' },
53
+ tab: { control: 'select', options: ['preview', 'code'] },
54
+ sandbox: { control: 'text' },
55
+ },
56
+ args: { src: `${BASE}/index.html`, files: FILES, tab: 'preview' as ArtifactTab },
57
+ render: (args) => (
58
+ <div class="h-[520px] w-full max-w-[900px]">
59
+ <Artifact
60
+ src={args.src}
61
+ files={args.files}
62
+ tab={args.tab}
63
+ sandbox={args.sandbox}
64
+ iframeTitle="Starboard artifact preview"
65
+ />
66
+ </div>
67
+ ),
68
+ } satisfies Meta<typeof Artifact>;
69
+
70
+ export default meta;
71
+ type Story = StoryObj<typeof meta>;
72
+
73
+ const IMPORT = `import { Artifact } from '@kitnai/chat';`;
74
+ const src = (code: string) => ({
75
+ parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
76
+ });
77
+
78
+ export const Playground: Story = {
79
+ ...src(`<Artifact src={src} files={files} tab="preview" />`),
80
+ };
81
+
82
+ export const CodeTab: Story = {
83
+ name: 'Code tab',
84
+ args: { tab: 'code' as ArtifactTab },
85
+ render: (args: { src?: string; files: ArtifactFile[] }) => (
86
+ <div class="h-[520px] w-full max-w-[900px]">
87
+ <Artifact src={args.src} files={args.files} tab="code" activeFile="index.html" iframeTitle="Starboard artifact preview" />
88
+ </div>
89
+ ),
90
+ ...src(`<Artifact src={src} files={files} tab="code" activeFile="index.html" />`),
91
+ };
92
+
93
+ /** Controlled — observe the emitted nav model. */
94
+ export const Controlled: Story = {
95
+ render: () => {
96
+ const [log, setLog] = createSignal<string[]>([]);
97
+ const push = (s: string) => setLog((l) => [s, ...l].slice(0, 6));
98
+ return (
99
+ <div class="flex flex-col gap-2">
100
+ <div class="h-[420px] w-full max-w-[900px]">
101
+ <Artifact
102
+ src={`${BASE}/index.html`}
103
+ files={FILES}
104
+ iframeTitle="Starboard artifact preview"
105
+ onNavigate={(url) => push(`navigate → ${url}`)}
106
+ onTabChange={(t) => push(`tabchange → ${t}`)}
107
+ onFileSelect={(p) => push(`fileselect → ${p}`)}
108
+ />
109
+ </div>
110
+ <pre class="m-0 min-h-24 max-w-[900px] rounded-lg bg-muted px-3 py-2.5 text-xs text-muted-foreground">
111
+ {log().length ? log().join('\n') : '(navigate the preview / switch tabs / pick a file…)'}
112
+ </pre>
113
+ </div>
114
+ );
115
+ },
116
+ ...src(`<Artifact src={src} files={files}
117
+ onNavigate={(url) => …} onTabChange={(t) => …} onFileSelect={(p) => …} />`),
118
+ };
119
+
120
+ /** PDFs render inline via pdf.js (loaded on demand from a CDN). */
121
+ export const PdfPreview: Story = {
122
+ name: 'PDF (inline)',
123
+ render: () => (
124
+ <div class="h-[520px] w-full max-w-[900px]">
125
+ <Artifact src={`${BASE}/assets/report.pdf`} files={FILES} />
126
+ </div>
127
+ ),
128
+ };
129
+
130
+ /** Fallback card when inline rendering can't work (here the src 404s). */
131
+ export const PdfFallback: Story = {
132
+ name: 'PDF (fallback card)',
133
+ render: () => (
134
+ <div class="h-[520px] w-full max-w-[900px]">
135
+ <Artifact src={`${BASE}/assets/does-not-exist.pdf`} />
136
+ </div>
137
+ ),
138
+ };