@kitn.ai/ui 0.15.1 → 0.17.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 (333) hide show
  1. package/README.md +37 -13
  2. package/bin/mcp.js +22 -0
  3. package/dist/bash-InADTalH.js +6 -1
  4. package/dist/components/artifact.d.ts +58 -0
  5. package/dist/components/attachments.d.ts +67 -0
  6. package/dist/components/card-fallback.d.ts +10 -0
  7. package/dist/components/card-renderer.d.ts +13 -0
  8. package/dist/components/card.d.ts +28 -0
  9. package/dist/components/chain-of-thought.d.ts +29 -0
  10. package/dist/components/chat-container.d.ts +19 -0
  11. package/dist/components/chat-scope-picker.d.ts +9 -0
  12. package/dist/components/chat-thread.d.ts +91 -0
  13. package/dist/components/checkpoint.d.ts +20 -0
  14. package/dist/components/choice-card.d.ts +78 -0
  15. package/dist/components/code-block.d.ts +16 -0
  16. package/dist/components/confirm-card.d.ts +57 -0
  17. package/dist/components/context.d.ts +65 -0
  18. package/dist/components/conversation-item.d.ts +8 -0
  19. package/dist/components/conversation-list.d.ts +11 -0
  20. package/dist/components/dismissed-stub.d.ts +22 -0
  21. package/dist/components/embed.d.ts +21 -0
  22. package/dist/components/empty.d.ts +43 -0
  23. package/dist/components/feedback-bar.d.ts +45 -0
  24. package/dist/components/file-tree.d.ts +57 -0
  25. package/dist/components/file-upload.d.ts +16 -0
  26. package/dist/components/form-widgets.d.ts +31 -0
  27. package/dist/components/form.d.ts +103 -0
  28. package/dist/components/image.d.ts +12 -0
  29. package/dist/components/link-preview.d.ts +23 -0
  30. package/dist/components/loader.d.ts +60 -0
  31. package/dist/components/markdown.d.ts +8 -0
  32. package/dist/components/message-skills.d.ts +15 -0
  33. package/dist/components/message.d.ts +111 -0
  34. package/dist/components/model-switcher.d.ts +8 -0
  35. package/dist/components/prompt-input.d.ts +38 -0
  36. package/dist/components/prompt-suggestion.d.ts +13 -0
  37. package/dist/components/reasoning.d.ts +20 -0
  38. package/dist/components/response-compare-types.d.ts +66 -0
  39. package/dist/components/response-compare.d.ts +51 -0
  40. package/dist/components/response-stream.d.ts +14 -0
  41. package/dist/components/scroll-button.d.ts +7 -0
  42. package/dist/components/slash-command.d.ts +15 -0
  43. package/dist/components/source.d.ts +23 -0
  44. package/dist/components/tasks-card.d.ts +72 -0
  45. package/dist/components/text-shimmer.d.ts +9 -0
  46. package/dist/components/thinking-bar.d.ts +10 -0
  47. package/dist/components/toast.d.ts +48 -0
  48. package/dist/components/tool.d.ts +16 -0
  49. package/dist/components/use-card-resolution.d.ts +16 -0
  50. package/dist/components/voice-input.d.ts +7 -0
  51. package/dist/core-AYMC6_lb.js +5874 -12
  52. package/dist/css-M7EaDHN_.js +6 -1
  53. package/dist/custom-elements.json +260 -11
  54. package/dist/elements/artifact.d.ts +1 -0
  55. package/dist/elements/artifact.js +1 -0
  56. package/dist/elements/attachments.d.ts +1 -0
  57. package/dist/elements/attachments.js +1 -0
  58. package/dist/elements/autoloader.d.ts +2 -0
  59. package/dist/elements/autoloader.js +1 -0
  60. package/dist/elements/card.d.ts +1 -0
  61. package/dist/elements/card.js +1 -0
  62. package/dist/elements/cards.d.ts +1 -0
  63. package/dist/elements/cards.js +1 -0
  64. package/dist/elements/chain-of-thought.d.ts +1 -0
  65. package/dist/elements/chain-of-thought.js +1 -0
  66. package/dist/elements/chat-scope-picker.d.ts +1 -0
  67. package/dist/elements/chat-scope-picker.js +1 -0
  68. package/dist/elements/chat-workspace.d.ts +1 -0
  69. package/dist/elements/chat-workspace.js +1 -0
  70. package/dist/elements/chat.d.ts +1 -0
  71. package/dist/elements/chat.js +1 -0
  72. package/dist/elements/checkpoint.d.ts +1 -0
  73. package/dist/elements/checkpoint.js +1 -0
  74. package/dist/elements/choice.d.ts +1 -0
  75. package/dist/elements/choice.js +1 -0
  76. package/dist/elements/chunks/Icon-D_Zmwmwh.js +1 -0
  77. package/dist/elements/chunks/action-icons-PVfIvcuG.js +1 -0
  78. package/dist/elements/chunks/attachments-Dp6vqtrc.js +1 -0
  79. package/dist/elements/chunks/bash-InADTalH.js +1 -0
  80. package/dist/elements/chunks/button-CKyYD9G4.js +1 -0
  81. package/dist/elements/chunks/card-Cjho-PYk.js +1 -0
  82. package/dist/elements/chunks/card-routing-C986KuUA.js +1 -0
  83. package/dist/elements/chunks/chat-thread-RT808iPf.js +1 -0
  84. package/dist/elements/chunks/check-CDYaMYrM.js +1 -0
  85. package/dist/elements/chunks/chevron-down-3SBiq8HF.js +1 -0
  86. package/dist/elements/chunks/chevron-right-k2UFlIVK.js +1 -0
  87. package/dist/elements/chunks/cn-Dogu6PbV.js +1 -0
  88. package/dist/elements/chunks/code-block-BYUmfFSd.js +1 -0
  89. package/dist/elements/chunks/collapsible-BiNwdOn6.js +1 -0
  90. package/dist/elements/chunks/context-18g6oi4J.js +1 -0
  91. package/dist/elements/chunks/conversation-list-D9Y0NVdo.js +1 -0
  92. package/dist/elements/chunks/core-AYMC6_lb.js +12 -0
  93. package/dist/elements/chunks/css-M7EaDHN_.js +1 -0
  94. package/dist/elements/chunks/default-input-CSAoYYMd.js +1 -0
  95. package/dist/elements/chunks/download-D8K0Je8r.js +1 -0
  96. package/dist/elements/chunks/dropdown-bUs2iTia.js +1 -0
  97. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +141 -0
  98. package/dist/elements/chunks/external-link-6mA0wxab.js +1 -0
  99. package/dist/elements/chunks/file-text-DnaYk3Dm.js +1 -0
  100. package/dist/elements/chunks/file-tree-CgtRLlxK.js +1 -0
  101. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +1 -0
  102. package/dist/elements/chunks/github-light-JYsPkUQd.js +1 -0
  103. package/dist/elements/chunks/hover-card-CIGw62gP.js +1 -0
  104. package/dist/elements/chunks/html-CPZ3oZQ7.js +1 -0
  105. package/dist/elements/chunks/index-CTBEJeo6.js +1 -0
  106. package/dist/elements/chunks/javascript-C25yR2R2.js +1 -0
  107. package/dist/elements/chunks/json-DxJze_jm.js +1 -0
  108. package/dist/elements/chunks/link-BlGSeoPy.js +1 -0
  109. package/dist/elements/chunks/loader-DvhR4qa0.js +1 -0
  110. package/dist/elements/chunks/markdown-CMTGx9r8.js +64 -0
  111. package/dist/elements/chunks/message-CDZwXjhL.js +1 -0
  112. package/dist/elements/chunks/message-feedback-CYx81QiA.js +1 -0
  113. package/dist/elements/chunks/model-switcher-O_tUhoOT.js +1 -0
  114. package/dist/elements/chunks/overlay-DydXYMQW.js +1 -0
  115. package/dist/elements/chunks/prompt-suggestion-ldlWV5kd.js +1 -0
  116. package/dist/elements/chunks/reasoning-2PUnHlmr.js +1 -0
  117. package/dist/elements/chunks/resizable-B5WNbSeb.js +1 -0
  118. package/dist/elements/chunks/star-t9tI-P0q.js +1 -0
  119. package/dist/elements/chunks/store-C6rIooZz.js +1 -0
  120. package/dist/elements/chunks/svelte--5p79yCD.js +1 -0
  121. package/dist/elements/chunks/text-shimmer-uh9V_KUh.js +1 -0
  122. package/dist/elements/chunks/textarea-3nm6Ia9U.js +1 -0
  123. package/dist/elements/chunks/thumbs-up-BBtguGld.js +1 -0
  124. package/dist/elements/chunks/toast-store-D8cL3fWl.js +1 -0
  125. package/dist/elements/chunks/tool-GJ3vwp1U.js +1 -0
  126. package/dist/elements/chunks/tooltip-DwKAz4XR.js +1 -0
  127. package/dist/elements/chunks/triangle-alert-Ct1zt2JL.js +1 -0
  128. package/dist/elements/chunks/tsx-B8rCNbgL.js +1 -0
  129. package/dist/elements/chunks/typescript-RycA9KXf.js +1 -0
  130. package/dist/elements/chunks/use-card-resolution-D-_0Alzy.js +1 -0
  131. package/dist/elements/chunks/video-mVYiM99-.js +1 -0
  132. package/dist/elements/chunks/vue-BmIZj4XD.js +1 -0
  133. package/dist/elements/chunks/x-CzxL27Ch.js +1 -0
  134. package/dist/elements/code-block.d.ts +1 -0
  135. package/dist/elements/code-block.js +1 -0
  136. package/dist/elements/compare.d.ts +1 -0
  137. package/dist/elements/compare.js +1 -0
  138. package/dist/elements/confirm-card.d.ts +1 -0
  139. package/dist/elements/confirm-card.js +1 -0
  140. package/dist/elements/context-meter.d.ts +1 -0
  141. package/dist/elements/context-meter.js +1 -0
  142. package/dist/elements/conversation-list.d.ts +1 -0
  143. package/dist/elements/conversation-list.js +1 -0
  144. package/dist/elements/embed.d.ts +1 -0
  145. package/dist/elements/embed.js +1 -0
  146. package/dist/elements/empty.d.ts +1 -0
  147. package/dist/elements/empty.js +1 -0
  148. package/dist/elements/feedback-bar.d.ts +1 -0
  149. package/dist/elements/feedback-bar.js +1 -0
  150. package/dist/elements/file-tree.d.ts +1 -0
  151. package/dist/elements/file-tree.js +1 -0
  152. package/dist/elements/file-upload.d.ts +1 -0
  153. package/dist/elements/file-upload.js +1 -0
  154. package/dist/elements/form.d.ts +1 -0
  155. package/dist/elements/form.js +1 -0
  156. package/dist/elements/image.d.ts +1 -0
  157. package/dist/elements/image.js +1 -0
  158. package/dist/elements/link-preview.d.ts +1 -0
  159. package/dist/elements/link-preview.js +1 -0
  160. package/dist/elements/loader.d.ts +1 -0
  161. package/dist/elements/loader.js +1 -0
  162. package/dist/elements/markdown.d.ts +1 -0
  163. package/dist/elements/markdown.js +1 -0
  164. package/dist/elements/message-skills.d.ts +1 -0
  165. package/dist/elements/message-skills.js +1 -0
  166. package/dist/elements/message.d.ts +1 -0
  167. package/dist/elements/message.js +1 -0
  168. package/dist/elements/model-switcher.d.ts +1 -0
  169. package/dist/elements/model-switcher.js +1 -0
  170. package/dist/elements/popover.d.ts +1 -0
  171. package/dist/elements/popover.js +1 -0
  172. package/dist/elements/prompt-input.d.ts +1 -0
  173. package/dist/elements/prompt-input.js +1 -0
  174. package/dist/elements/prompt-suggestions.d.ts +1 -0
  175. package/dist/elements/prompt-suggestions.js +1 -0
  176. package/dist/elements/reasoning.d.ts +1 -0
  177. package/dist/elements/reasoning.js +1 -0
  178. package/dist/elements/resizable.d.ts +1 -0
  179. package/dist/elements/resizable.js +1 -0
  180. package/dist/elements/response-stream.d.ts +1 -0
  181. package/dist/elements/response-stream.js +14 -0
  182. package/dist/elements/scroll-button.d.ts +1 -0
  183. package/dist/elements/scroll-button.js +1 -0
  184. package/dist/elements/source.d.ts +1 -0
  185. package/dist/elements/source.js +1 -0
  186. package/dist/elements/switch.d.ts +1 -0
  187. package/dist/elements/switch.js +1 -0
  188. package/dist/elements/tasks.d.ts +1 -0
  189. package/dist/elements/tasks.js +1 -0
  190. package/dist/elements/text-shimmer.d.ts +1 -0
  191. package/dist/elements/text-shimmer.js +1 -0
  192. package/dist/elements/thinking-bar.d.ts +1 -0
  193. package/dist/elements/thinking-bar.js +1 -0
  194. package/dist/elements/toast.d.ts +1 -0
  195. package/dist/elements/toast.js +1 -0
  196. package/dist/elements/tool.d.ts +1 -0
  197. package/dist/elements/tool.js +1 -0
  198. package/dist/elements/voice-input.d.ts +1 -0
  199. package/dist/elements/voice-input.js +1 -0
  200. package/dist/elements.d.ts +768 -0
  201. package/dist/engine-javascript-vq0WuIJl.js +2516 -14
  202. package/dist/github-dark-dimmed-DUshB20C.js +4 -1
  203. package/dist/github-light-JYsPkUQd.js +4 -1
  204. package/dist/html-CPZ3oZQ7.js +10 -1
  205. package/dist/index.d.ts +124 -2
  206. package/dist/index.js +15593 -0
  207. package/dist/javascript-C25yR2R2.js +6 -1
  208. package/dist/json-DxJze_jm.js +6 -1
  209. package/dist/kitn-chat.es.js +1 -77
  210. package/dist/llms/llms-full.txt +57 -12
  211. package/dist/llms/llms.txt +3 -3
  212. package/dist/mcp.es.js +2296 -0
  213. package/dist/primitives/card-contract.d.ts +119 -0
  214. package/dist/primitives/card-host.d.ts +12 -0
  215. package/dist/primitives/card-recovery.d.ts +55 -0
  216. package/dist/primitives/card-registry.d.ts +17 -0
  217. package/dist/primitives/card-resolution.d.ts +8 -0
  218. package/dist/primitives/card-routing.d.ts +12 -0
  219. package/dist/primitives/card-validate.d.ts +25 -0
  220. package/dist/primitives/chat-config.d.ts +31 -0
  221. package/dist/primitives/controllable.d.ts +13 -0
  222. package/dist/primitives/embed-providers.d.ts +65 -0
  223. package/dist/primitives/highlighter.d.ts +23 -0
  224. package/dist/primitives/link-preview.d.ts +47 -0
  225. package/dist/primitives/message-feedback.d.ts +35 -0
  226. package/dist/primitives/pdf-preview.d.ts +50 -0
  227. package/dist/primitives/toast-store.d.ts +88 -0
  228. package/dist/primitives/use-auto-resize.d.ts +8 -0
  229. package/dist/primitives/use-resize-observer.d.ts +1 -0
  230. package/dist/primitives/use-stick-to-bottom.d.ts +5 -0
  231. package/dist/primitives/use-text-stream.d.ts +19 -0
  232. package/dist/primitives/use-voice-recorder.d.ts +9 -0
  233. package/dist/react/index.d.ts +1198 -0
  234. package/dist/react/runtime.d.ts +15 -0
  235. package/dist/react.js +275 -0
  236. package/dist/register-JyTglR6m.js +1 -0
  237. package/dist/register-impl-C6wlSsts.js +77 -0
  238. package/dist/remote/host-embed.d.ts +33 -0
  239. package/dist/remote/index.d.ts +2 -0
  240. package/dist/remote/origin.d.ts +5 -0
  241. package/dist/remote/provider-runtime.d.ts +27 -0
  242. package/dist/remote/provider.d.ts +2 -0
  243. package/dist/remote/validate.d.ts +4 -0
  244. package/dist/remote/version.d.ts +4 -0
  245. package/dist/remote/wire.d.ts +53 -0
  246. package/dist/schemas/choice.schema.json +5 -0
  247. package/dist/schemas/tasks.schema.json +5 -0
  248. package/dist/svelte--5p79yCD.js +15 -1
  249. package/dist/tsx-B8rCNbgL.js +6 -1
  250. package/dist/types.d.ts +42 -0
  251. package/dist/typescript-RycA9KXf.js +6 -1
  252. package/dist/ui/action-icons.d.ts +11 -0
  253. package/dist/ui/avatar.d.ts +8 -0
  254. package/dist/ui/badge.d.ts +9 -0
  255. package/dist/ui/button.d.ts +10 -0
  256. package/dist/ui/collapsible.d.ts +27 -0
  257. package/dist/ui/dropdown.d.ts +20 -0
  258. package/dist/ui/hover-card.d.ts +28 -0
  259. package/dist/ui/overlay.d.ts +67 -0
  260. package/dist/ui/popover.d.ts +32 -0
  261. package/dist/ui/resizable.d.ts +76 -0
  262. package/dist/ui/scroll-area.d.ts +5 -0
  263. package/dist/ui/separator.d.ts +5 -0
  264. package/dist/ui/skeleton.d.ts +5 -0
  265. package/dist/ui/switch.d.ts +19 -0
  266. package/dist/ui/textarea.d.ts +6 -0
  267. package/dist/ui/tooltip.d.ts +9 -0
  268. package/dist/utils/cn.d.ts +2 -0
  269. package/dist/vue-BmIZj4XD.js +33 -1
  270. package/frameworks/react/index.tsx +67 -11
  271. package/frameworks/react/runtime.tsx +16 -2
  272. package/llms-full.txt +57 -12
  273. package/llms.txt +3 -3
  274. package/package.json +36 -14
  275. package/src/agent-tooling/archetypes.ts +46 -0
  276. package/src/agent-tooling/integrations/cloudflare.ts +88 -0
  277. package/src/agent-tooling/integrations/langgraph.ts +60 -0
  278. package/src/agent-tooling/integrations/mastra.ts +27 -0
  279. package/src/agent-tooling/integrations/mock.ts +34 -0
  280. package/src/agent-tooling/integrations/ollama.ts +47 -0
  281. package/src/agent-tooling/integrations/openrouter.ts +26 -0
  282. package/src/agent-tooling/integrations/pi.ts +40 -0
  283. package/src/agent-tooling/integrations/pydantic-ai.ts +52 -0
  284. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +44 -0
  285. package/src/agent-tooling/mcp/manifest.ts +128 -0
  286. package/src/agent-tooling/mcp/server.ts +59 -0
  287. package/src/agent-tooling/mcp/stdio.ts +18 -0
  288. package/src/agent-tooling/mcp/tools/debug.ts +489 -0
  289. package/src/agent-tooling/mcp/tools/reference.ts +132 -0
  290. package/src/agent-tooling/mcp/tools/scaffold.ts +1759 -0
  291. package/src/agent-tooling/mcp/tools/theme.ts +340 -0
  292. package/src/agent-tooling/mcp/tools/types.ts +14 -0
  293. package/src/agent-tooling/registry.ts +41 -0
  294. package/src/agent-tooling/types.ts +30 -0
  295. package/src/components/chat-thread.tsx +27 -35
  296. package/src/components/choice-card.tsx +33 -4
  297. package/src/components/component-meta.json +250 -26
  298. package/src/components/confirm-card.tsx +15 -1
  299. package/src/components/dismissed-stub.tsx +80 -0
  300. package/src/components/form.tsx +21 -3
  301. package/src/components/message.tsx +151 -10
  302. package/src/components/response-compare-types.ts +127 -0
  303. package/src/components/response-compare.tsx +431 -0
  304. package/src/components/tasks-card.tsx +33 -3
  305. package/src/components/toast.tsx +398 -0
  306. package/src/components/use-card-resolution.ts +24 -8
  307. package/src/elements/autoloader.ts +89 -0
  308. package/src/elements/chat-types.ts +7 -0
  309. package/src/elements/chat-workspace.tsx +4 -2
  310. package/src/elements/chat.tsx +4 -2
  311. package/src/elements/compare.tsx +88 -0
  312. package/src/elements/compiled.css +1 -1
  313. package/src/elements/define.tsx +7 -1
  314. package/src/elements/element-manifest.json +182 -0
  315. package/src/elements/element-meta.json +191 -68
  316. package/src/elements/element-types.d.ts +43 -6
  317. package/src/elements/framework-usage.json +25 -0
  318. package/src/elements/message.tsx +27 -45
  319. package/src/elements/register-impl.ts +58 -0
  320. package/src/elements/register.ts +26 -51
  321. package/src/elements/remote.tsx +4 -0
  322. package/src/elements/styles.css +19 -0
  323. package/src/elements/toast.tsx +74 -0
  324. package/src/index.ts +30 -0
  325. package/src/primitives/card-contract.ts +14 -5
  326. package/src/primitives/card-recovery.ts +149 -0
  327. package/src/primitives/card-routing.ts +3 -0
  328. package/src/primitives/card-schemas/choice.schema.json +5 -0
  329. package/src/primitives/card-schemas/tasks.schema.json +5 -0
  330. package/src/primitives/message-feedback.ts +123 -0
  331. package/src/primitives/toast-store.ts +235 -0
  332. package/src/ui/tooltip.tsx +8 -2
  333. package/theme.css +4 -2
@@ -5,8 +5,9 @@
5
5
 
6
6
  // Re-exports for `import { … } from '@kitn.ai/ui/elements'`.
7
7
  export type { ChatMessage, ChatMessageAction } from './chat-types';
8
- export { configureCodeHighlighting, isCodeHighlightingEnabled } from '../primitives/highlighter';
9
8
  export type { CodeHighlightingOptions } from '../primitives/highlighter';
9
+ export declare function configureCodeHighlighting(options: CodeHighlightingOptions): void;
10
+ export declare function isCodeHighlightingEnabled(): boolean;
10
11
 
11
12
  export interface KaiArtifactElement extends HTMLElement {
12
13
  /** Color mode (`auto` follows prefers-color-scheme). */
@@ -79,11 +80,11 @@ export interface KaiCardsElement extends HTMLElement {
79
80
  /** Color mode (`auto` follows prefers-color-scheme). */
80
81
  theme?: 'light' | 'dark' | 'auto';
81
82
  /** The stream of card envelopes to render. Set as a JS PROPERTY: `el.cards = [...]`. */
82
- cards?: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } }[];
83
+ cards?: { type: string; id: string; data: unknown; title?: string; resolution?: { kind: "action"; action: string; payload?: unknown; at?: string } | { kind: "submit"; data: unknown; at?: string } | { kind: "dismissed"; at?: string } | { kind: "expired"; reason?: string; at?: string } }[];
83
84
  /** Optional type→tag overrides/additions (merged over the built-ins). Property: `el.types`. Typed as a plain string map (not the `CardTagMap` alias) so the generated React wrapper inlines it instead of emitting an unresolved named type. */
84
85
  types?: Record<string, string>;
85
86
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
86
- policy?: { onSubmit?: (cardId: string, data: unknown) => void; onAction?: (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: (text: string, opts: { mode: "compose" | "send"; context?: unknown; }) => void; onOpen?: (url: string, target: "tab" | "artifact") => void; onState?: (cardId: string, patch: unknown) => void; onDismiss?: (cardId: string) => void; onError?: (cardId: string, message: string) => void; maxSendPromptMode?: "compose" | "send" };
87
+ policy?: { onSubmit?: (cardId: string, data: unknown) => void; onAction?: (cardId: string, action: string, payload?: unknown) => void; onSendPrompt?: (text: string, opts: { mode: "compose" | "send"; context?: unknown; }) => void; onOpen?: (url: string, target: "tab" | "artifact") => void; onState?: (cardId: string, patch: unknown) => void; onDismiss?: (cardId: string) => void; onReopen?: (cardId: string) => void; onError?: (cardId: string, message: string) => void; maxSendPromptMode?: "compose" | "send" };
87
88
  }
88
89
 
89
90
  export interface KaiChainOfThoughtElement extends HTMLElement {
@@ -97,7 +98,7 @@ export interface KaiChatElement extends HTMLElement {
97
98
  /** Color mode (`auto` follows prefers-color-scheme). */
98
99
  theme?: 'light' | 'dark' | 'auto';
99
100
  /** The full message thread to render, newest last. Each entry carries its role, content, and optional reasoning/tools/attachments/actions. Set as a JS property (`el.messages = [...]`). */
100
- messages: { id: string; role: "user" | "assistant"; content: string; reasoning?: undefined | { text: string; label?: undefined | string }; tools?: undefined | { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string }[]; attachments?: undefined | { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[]; actions?: undefined | ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: undefined | string; tooltip?: undefined | string })[]; avatar?: undefined | { src?: undefined | string; fallback?: undefined | string; alt?: undefined | string } }[];
101
+ messages: { id: string; role: "user" | "assistant"; content: string; reasoning?: undefined | { text: string; label?: undefined | string }; tools?: undefined | { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string }[]; attachments?: undefined | { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[]; actions?: undefined | ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: undefined | string; tooltip?: undefined | string })[]; avatar?: undefined | { src?: undefined | string; fallback?: undefined | string; alt?: undefined | string }; feedback?: undefined | "like" | "dislike" }[];
101
102
  /** Controlled value of the input. When set, the host owns the input text and must update it on `kai-value-change`; leave unset for uncontrolled behavior. */
102
103
  value?: string;
103
104
  /** Placeholder text shown in the empty input. */
@@ -185,6 +186,25 @@ export interface KaiCodeBlockElement extends HTMLElement {
185
186
  proseSize?: "xs" | "sm" | "base" | "lg";
186
187
  }
187
188
 
189
+ export interface KaiCompareElement extends HTMLElement {
190
+ /** Color mode (`auto` follows prefers-color-scheme). */
191
+ theme?: 'light' | 'dark' | 'auto';
192
+ /** The compare definition (prompt + the two candidates). Set as a JS PROPERTY: `el.data = { prompt, candidates: [A, B], collapse? }`. Import `ResponseCompareData` from `@kitn.ai/ui` for the full shape. */
193
+ data?: Record<string, unknown>;
194
+ /** Stable id correlating every emitted event. Attribute: `compare-id`. */
195
+ compareId?: string;
196
+ /** Re-hydrate / control the user's pick. Set as a JS PROPERTY: `el.selection = { chosenId, rejectedIds }`. Renders the collapsed winner. */
197
+ selection?: Record<string, unknown>;
198
+ /** Layout: `'auto'` (default — columns when wide, tabs when narrow, by CONTAINER width) | `'columns'` (side-by-side) | `'tabs'` (pills to switch). Attribute: `layout`. */
199
+ layout?: "auto" | "columns" | "tabs";
200
+ /** Prose/text size for the rendered candidates. Attribute: `prose-size`. */
201
+ proseSize?: "xs" | "sm" | "base" | "lg";
202
+ /** Shiki theme for code blocks in the candidates. Attribute: `code-theme`. */
203
+ codeTheme?: string;
204
+ /** Whether code blocks are syntax-highlighted. Attribute: `code-highlight`. */
205
+ codeHighlight?: boolean;
206
+ }
207
+
188
208
  export interface KaiConfirmElement extends HTMLElement {
189
209
  /** Color mode (`auto` follows prefers-color-scheme). */
190
210
  theme?: 'light' | 'dark' | 'auto';
@@ -346,7 +366,7 @@ export interface KaiMessageElement extends HTMLElement {
346
366
  /** Color mode (`auto` follows prefers-color-scheme). */
347
367
  theme?: 'light' | 'dark' | 'auto';
348
368
  /** The full message object. Set as a JS property. */
349
- message?: { id: string; role: "user" | "assistant"; content: string; reasoning?: { text: string; label?: string }; tools?: { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; output?: Record<string, unknown>; toolCallId?: string; errorText?: string }[]; attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string } };
369
+ message?: { id: string; role: "user" | "assistant"; content: string; reasoning?: { text: string; label?: string }; tools?: { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: Record<string, unknown>; output?: Record<string, unknown>; toolCallId?: string; errorText?: string }[]; attachments?: { id: string; type: "file" | "source-document"; filename?: string; mediaType?: string; url?: string; title?: string }[]; actions?: ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: string; tooltip?: string })[]; avatar?: { src?: string; fallback?: string; alt?: string }; feedback?: "like" | "dislike" };
350
370
  /** Convenience for simple cases when not passing a `message` object. */
351
371
  role?: "user" | "assistant";
352
372
  /** Convenience content (used when `message` is not set). */
@@ -601,6 +621,21 @@ export interface KaiThinkingBarElement extends HTMLElement {
601
621
  stopLabel?: string;
602
622
  }
603
623
 
624
+ export interface KaiToastRegionElement extends HTMLElement {
625
+ /** Color mode (`auto` follows prefers-color-scheme). */
626
+ theme?: 'light' | 'dark' | 'auto';
627
+ /** The toasts to render. Newest is shown on top. Set as a JS property (array); pass a new array reference to update. */
628
+ toasts: { id: string; message: string; variant?: undefined | "neutral" | "success"; action?: undefined | { label: string; onAction: () => void | false }; duration?: undefined | number; dismissible?: undefined | boolean; target?: undefined | HTMLElement }[];
629
+ /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
630
+ position?: "top-center" | "top-right" | "top-left" | "bottom-center" | "bottom-right" | "bottom-left";
631
+ /** Max simultaneously-visible toasts; the rest queue. Defaults to `3`. */
632
+ max?: number;
633
+ /** Stacking: 'expanded' (default, full column) | 'collapsed' (Sonner-style pile that expands on hover/focus). Attribute: stack. */
634
+ stack?: "expanded" | "collapsed";
635
+ /** Container element to anchor this region to (JS property). Set by the store for a scoped region; unset = the global viewport region. */
636
+ target?: HTMLElement;
637
+ }
638
+
604
639
  export interface KaiToolElement extends HTMLElement {
605
640
  /** Color mode (`auto` follows prefers-color-scheme). */
606
641
  theme?: 'light' | 'dark' | 'auto';
@@ -629,7 +664,7 @@ export interface KaiWorkspaceElement extends HTMLElement {
629
664
  /** Id of the open conversation, highlighted in the sidebar. */
630
665
  activeId?: string;
631
666
  /** The active conversation's message thread, newest last. Set as a JS property. */
632
- messages: { id: string; role: "user" | "assistant"; content: string; reasoning?: undefined | { text: string; label?: undefined | string }; tools?: undefined | { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string }[]; attachments?: undefined | { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[]; actions?: undefined | ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: undefined | string; tooltip?: undefined | string })[]; avatar?: undefined | { src?: undefined | string; fallback?: undefined | string; alt?: undefined | string } }[];
667
+ messages: { id: string; role: "user" | "assistant"; content: string; reasoning?: undefined | { text: string; label?: undefined | string }; tools?: undefined | { type: string; state: "input-streaming" | "input-available" | "output-available" | "output-error"; input?: undefined | Record<string, unknown>; output?: undefined | Record<string, unknown>; toolCallId?: undefined | string; errorText?: undefined | string }[]; attachments?: undefined | { id: string; type: "file" | "source-document"; filename?: undefined | string; mediaType?: undefined | string; url?: undefined | string; title?: undefined | string }[]; actions?: undefined | ("copy" | "like" | "dislike" | "regenerate" | "edit" | { id: string; label: string; icon?: undefined | string; tooltip?: undefined | string })[]; avatar?: undefined | { src?: undefined | string; fallback?: undefined | string; alt?: undefined | string }; feedback?: undefined | "like" | "dislike" }[];
633
668
  value?: string;
634
669
  placeholder?: string;
635
670
  loading?: boolean;
@@ -671,6 +706,7 @@ declare global {
671
706
  'kai-checkpoint': KaiCheckpointElement;
672
707
  'kai-choice': KaiChoiceElement;
673
708
  'kai-code-block': KaiCodeBlockElement;
709
+ 'kai-compare': KaiCompareElement;
674
710
  'kai-confirm': KaiConfirmElement;
675
711
  'kai-context': KaiContextElement;
676
712
  'kai-conversations': KaiConversationsElement;
@@ -703,6 +739,7 @@ declare global {
703
739
  'kai-tasks': KaiTasksElement;
704
740
  'kai-text-shimmer': KaiTextShimmerElement;
705
741
  'kai-thinking-bar': KaiThinkingBarElement;
742
+ 'kai-toast-region': KaiToastRegionElement;
706
743
  'kai-tool': KaiToolElement;
707
744
  'kai-voice-input': KaiVoiceInputElement;
708
745
  'kai-workspace': KaiWorkspaceElement;
@@ -113,6 +113,18 @@
113
113
  "solid": "import { CodeBlock } from '@kitn.ai/ui';\n\n<CodeBlock\n code={code}\n/>"
114
114
  }
115
115
  },
116
+ {
117
+ "tag": "kai-compare",
118
+ "displayName": "Compare",
119
+ "hasSolid": false,
120
+ "snippets": {
121
+ "html": "<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kitn.ai/ui/dist/kitn-chat.es.js\"></script>\n\n<kai-compare></kai-compare>\n<script type=\"module\">\n const el = document.querySelector('kai-compare');\n el.addEventListener('kai-compare-select', (e) => console.log(e.detail));\n el.addEventListener('kai-error', (e) => console.log(e.detail));\n el.addEventListener('kai-ready', (e) => console.log(e.detail));\n</script>",
122
+ "react": "import { Compare } from '@kitn.ai/ui/react';\n\n<Compare\n onCompareSelect={(e) => console.log(e.detail)}\n onError={(e) => console.log(e.detail)}\n onReady={(e) => console.log(e.detail)}\n/>",
123
+ "vue": "<kai-compare\n @kai-compare-select=\"onCompareSelect\"\n @kai-error=\"onError\"\n @kai-ready=\"onReady\"\n/>",
124
+ "svelte": "<script>\n import '@kitn.ai/ui/elements';\n</script>\n\n<kai-compare\n on:kai-compare-select={onCompareSelect}\n on:kai-error={onError}\n on:kai-ready={onReady}\n></kai-compare>",
125
+ "angular": "<kai-compare\n (kai-compare-select)=\"onCompareSelect($event)\"\n (kai-error)=\"onError($event)\"\n (kai-ready)=\"onReady($event)\"\n></kai-compare>"
126
+ }
127
+ },
116
128
  {
117
129
  "tag": "kai-confirm",
118
130
  "displayName": "Confirm",
@@ -522,6 +534,19 @@
522
534
  "solid": "import { ThinkingBar } from '@kitn.ai/ui';\n\n<ThinkingBar\n onStop={(e) => console.log(e.detail)}\n/>"
523
535
  }
524
536
  },
537
+ {
538
+ "tag": "kai-toast-region",
539
+ "displayName": "ToastRegion",
540
+ "hasSolid": true,
541
+ "snippets": {
542
+ "html": "<script type=\"module\" src=\"https://cdn.jsdelivr.net/npm/@kitn.ai/ui/dist/kitn-chat.es.js\"></script>\n\n<kai-toast-region></kai-toast-region>\n<script type=\"module\">\n const el = document.querySelector('kai-toast-region');\n el.toasts = /* … */;\n el.addEventListener('kai-action', (e) => console.log(e.detail));\n el.addEventListener('kai-dismiss', (e) => console.log(e.detail));\n</script>",
543
+ "react": "import { ToastRegion } from '@kitn.ai/ui/react';\n\n<ToastRegion\n toasts={toasts}\n onAction={(e) => console.log(e.detail)}\n onDismiss={(e) => console.log(e.detail)}\n/>",
544
+ "vue": "<kai-toast-region\n :toasts.prop=\"toasts\"\n @kai-action=\"onAction\"\n @kai-dismiss=\"onDismiss\"\n/>",
545
+ "svelte": "<script>\n import '@kitn.ai/ui/elements';\n</script>\n\n<kai-toast-region\n {toasts}\n on:kai-action={onAction}\n on:kai-dismiss={onDismiss}\n></kai-toast-region>",
546
+ "angular": "<kai-toast-region\n [toasts]=\"toasts\"\n (kai-action)=\"onAction($event)\"\n (kai-dismiss)=\"onDismiss($event)\"\n></kai-toast-region>",
547
+ "solid": "import { ToastRegion } from '@kitn.ai/ui';\n\n<ToastRegion\n toasts={toasts}\n onAction={(e) => console.log(e.detail)}\n onDismiss={(e) => console.log(e.detail)}\n/>"
548
+ }
549
+ },
525
550
  {
526
551
  "tag": "kai-tool",
527
552
  "displayName": "Tool",
@@ -1,10 +1,8 @@
1
- import { For, Show, createSignal, onMount, onCleanup } from 'solid-js';
1
+ import { Show, createSignal, onMount, onCleanup } from 'solid-js';
2
2
  import { defineWebComponent } from './define';
3
3
  import { ChatConfig, useChatConfig, type ProseSize } from '../primitives/chat-config';
4
- import { Message, MessageAvatar, MessageContent, MessageActionBar } from '../components/message';
5
- import { Reasoning, ReasoningTrigger, ReasoningContent } from '../components/reasoning';
6
- import { Tool } from '../components/tool';
7
- import { Attachments, Attachment, AttachmentPreview, AttachmentInfo } from '../components/attachments';
4
+ import { Message, MessageAvatar, MessageBody } from '../components/message';
5
+ import { createMessageFeedback } from '../primitives/message-feedback';
8
6
  import type { ChatMessage } from './chat-types';
9
7
 
10
8
  interface Props extends Record<string, unknown> {
@@ -33,8 +31,10 @@ interface Props extends Record<string, unknown> {
33
31
 
34
32
  /** Events fired by `<kai-message>`. */
35
33
  interface Events {
36
- /** An action button was clicked. `action` is the built-in name or custom id. */
37
- 'kai-message-action': { messageId: string; action: string };
34
+ /** An action button was clicked. `action` is the built-in name or custom id.
35
+ * `state` is present only for the toggleable feedback votes: `'on'` when a
36
+ * like/dislike is set, `'off'` when re-tapped to clear. */
37
+ 'kai-message-action': { messageId: string; action: string; state?: 'on' | 'off' };
38
38
  }
39
39
 
40
40
  /**
@@ -58,6 +58,12 @@ defineWebComponent<Props, Events>('kai-message', {
58
58
  const outer = useChatConfig();
59
59
  const msg = (): ChatMessage =>
60
60
  props.message ?? { id: 'message', role: props.role ?? 'assistant', content: props.content ?? '' };
61
+ // Copy + vote state lives here (above the rendered body) so it survives a
62
+ // re-render when the host swaps in a fresh `message` object during streaming.
63
+ const feedback = createMessageFeedback({
64
+ emit: (detail) => dispatch('kai-message-action', detail),
65
+ target: () => element,
66
+ });
61
67
 
62
68
  // Read declarative <kai-action> children from light DOM.
63
69
  // Shadow DOM with no <slot> suppresses them visually — they're invisible data carriers.
@@ -89,45 +95,21 @@ defineWebComponent<Props, Events>('kai-message', {
89
95
  element.hasAttribute('markdown') || props.markdown === true || props.markdown === false;
90
96
  const useMarkdown = () => (markdownExplicit() ? flag('markdown') : !isUser());
91
97
 
98
+ const mergedActions = () => [...(msg().actions ?? []), ...slottedActions()];
92
99
  const body = () => (
93
- <>
94
- <Show when={msg().reasoning}>
95
- <Reasoning class="mb-2 w-full">
96
- <ReasoningTrigger>{msg().reasoning!.label ?? 'Reasoning'}</ReasoningTrigger>
97
- <ReasoningContent markdown>{msg().reasoning!.text}</ReasoningContent>
98
- </Reasoning>
99
- </Show>
100
- <For each={msg().tools ?? []}>
101
- {(tp) => <Tool toolPart={tp} class="mb-2 w-full" />}
102
- </For>
103
- <Show when={msg().attachments?.length}>
104
- <Attachments variant="inline" class={isUser() ? 'mb-2 justify-end' : 'mb-2'}>
105
- <For each={msg().attachments!}>
106
- {(att) => (
107
- <Attachment data={att}>
108
- <AttachmentPreview />
109
- <AttachmentInfo />
110
- </Attachment>
111
- )}
112
- </For>
113
- </Attachments>
114
- </Show>
115
- <MessageContent
116
- markdown={useMarkdown()}
117
- class={isUser()
118
- ? 'bg-muted text-primary max-w-[85%] rounded-2xl px-4 py-2'
119
- : 'bg-transparent p-0'}
120
- >
121
- {msg().content}
122
- </MessageContent>
123
- <Show when={(msg().actions?.length ?? 0) > 0 || slottedActions().length > 0}>
124
- <MessageActionBar
125
- actions={[...(msg().actions ?? []), ...slottedActions()]}
126
- reveal={reveal()}
127
- onAction={(action) => dispatch('kai-message-action', { messageId: msg().id, action })}
128
- />
129
- </Show>
130
- </>
100
+ <MessageBody
101
+ content={msg().content}
102
+ reasoning={msg().reasoning}
103
+ tools={msg().tools}
104
+ attachments={msg().attachments}
105
+ isUser={isUser()}
106
+ markdown={useMarkdown()}
107
+ actions={mergedActions()}
108
+ actionsReveal={reveal()}
109
+ activeFeedback={feedback.resolveFeedback(msg())}
110
+ copied={feedback.isCopied(msg().id)}
111
+ onAction={(action) => feedback.handleAction(msg(), action)}
112
+ />
131
113
  );
132
114
 
133
115
  // Row carries `group` so a hover-revealed action bar fades in on row hover.
@@ -0,0 +1,58 @@
1
+ // Component registration implementation. Importing this file defines all kitn
2
+ // custom elements as a side effect. It is dynamically imported (browser-only)
3
+ // from ./register.ts so the elements entry is SSR-import-safe — see the comment
4
+ // there. This file is hand-maintained (the generator gen-element-api.mjs SKIPs
5
+ // register.ts AND this file); keep the component import list here in sync.
6
+ import './conversation-list';
7
+ import './prompt-input';
8
+ import './chat';
9
+ import './chat-workspace';
10
+ // Composable leaf elements (spike — see docs/handoff + examples/composable)
11
+ import './thinking-bar';
12
+ import './model-switcher';
13
+ import './attachments';
14
+ // Phase 1 — message-rendering core
15
+ import './message';
16
+ import './markdown';
17
+ import './code-block';
18
+ import './reasoning';
19
+ import './tool';
20
+ // Phase 2 — header / meta
21
+ import './context-meter';
22
+ import './feedback-bar';
23
+ import './chat-scope-picker';
24
+ // Phase 3 — input ecosystem
25
+ // (NB: SlashCommand is context-bound to PromptInput — it observes the input
26
+ // value via usePromptInput() — so it is NOT a standalone element. It will fold
27
+ // into <kai-prompt-input> as a `slash-commands` property in a later pass.)
28
+ import './prompt-suggestions';
29
+ import './file-upload';
30
+ import './voice-input';
31
+ // Phase 4 — indicators & leaves
32
+ import './loader';
33
+ import './text-shimmer';
34
+ import './image';
35
+ import './checkpoint';
36
+ import './message-skills';
37
+ import './source';
38
+ import './response-stream';
39
+ import './empty';
40
+ import './chain-of-thought';
41
+ import './resizable';
42
+ import './file-tree';
43
+ import './artifact';
44
+ import './scroll-button';
45
+ import './popover';
46
+ import './switch';
47
+ import './toast';
48
+ // Generative-UI cards (Card Contract)
49
+ import './card';
50
+ import './form';
51
+ import './link-preview';
52
+ import './embed';
53
+ import './confirm-card';
54
+ import './tasks';
55
+ import './choice';
56
+ import './cards';
57
+ // Dual-response comparison (preference capture)
58
+ import './compare';
@@ -1,56 +1,31 @@
1
1
  // Single entry that registers all kitn custom elements. Importing this file
2
- // (or the built bundle) defines the elements as a side effect.
3
- import './conversation-list';
4
- import './prompt-input';
5
- import './chat';
6
- import './chat-workspace';
7
- // Composable leaf elements (spike see docs/handoff + examples/composable)
8
- import './thinking-bar';
9
- import './model-switcher';
10
- import './attachments';
11
- // Phase 1 message-rendering core
12
- import './message';
13
- import './markdown';
14
- import './code-block';
15
- import './reasoning';
16
- import './tool';
17
- // Phase 2 — header / meta
18
- import './context-meter';
19
- import './feedback-bar';
20
- import './chat-scope-picker';
21
- // Phase 3 — input ecosystem
22
- // (NB: SlashCommand is context-bound to PromptInput — it observes the input
23
- // value via usePromptInput() — so it is NOT a standalone element. It will fold
24
- // into <kai-prompt-input> as a `slash-commands` property in a later pass.)
25
- import './prompt-suggestions';
26
- import './file-upload';
27
- import './voice-input';
28
- // Phase 4 — indicators & leaves
29
- import './loader';
30
- import './text-shimmer';
31
- import './image';
32
- import './checkpoint';
33
- import './message-skills';
34
- import './source';
35
- import './response-stream';
36
- import './empty';
37
- import './chain-of-thought';
38
- import './resizable';
39
- import './file-tree';
40
- import './artifact';
41
- import './scroll-button';
42
- import './popover';
43
- import './switch';
44
- // Generative-UI cards (Card Contract)
45
- import './card';
46
- import './form';
47
- import './link-preview';
48
- import './embed';
49
- import './confirm-card';
50
- import './tasks';
51
- import './choice';
52
- import './cards';
2
+ // (or the built bundle) defines the elements as a side effect — but ONLY in a
3
+ // browser/DOM context.
4
+ //
5
+ // SSR-safe: the kai-* elements bundle Solid's client runtime, which touches
6
+ // `window` at module-eval (delegateEvents(events, doc = window.document)). With
7
+ // no `window`, a static import would throw. So the actual component registration
8
+ // lives in ./register-impl and is loaded behind a browser check + dynamic
9
+ // import() — static ESM imports hoist + evaluate unconditionally, so a bare `if`
10
+ // cannot gate them; a dynamic import() can. On the server this module is inert
11
+ // (no DOM no registration → no throw); in the browser it registers as before.
12
+ //
13
+ // Registration is async (a microtask after this module loads). Consumers that
14
+ // need the upgraded element (e.g. the React runtime) guard with
15
+ // customElements.whenDefined(), which resolves once the impl chunk has run.
16
+ if (typeof window !== 'undefined' && typeof customElements !== 'undefined') {
17
+ void import('./register-impl');
18
+ }
53
19
 
54
20
  export type { ChatMessage, ChatMessageAction } from './chat-types';
55
21
  export { configureCodeHighlighting, isCodeHighlightingEnabled } from '../primitives/highlighter';
56
22
  export type { CodeHighlightingOptions } from '../primitives/highlighter';
23
+
24
+ // Imperative toast API — usable directly from the elements entry so consumers
25
+ // who only import the web-components bundle still get `toast()`. The store is
26
+ // SSR-safe (no DOM touched until the first toast is raised on the client).
27
+ export { toast, configureToasts } from '../primitives/toast-store';
28
+ export type {
29
+ ToastConfig,
30
+ ToastItem, ToastVariant, ToastAction, ToastOptions, ToastHandle,
31
+ } from '../primitives/toast-store';
@@ -81,6 +81,10 @@ function buildWrappedPolicy(element: HTMLElement, userPolicy: CardPolicy | undef
81
81
  emit({ kind: 'dismiss', cardId });
82
82
  userPolicy?.onDismiss?.(cardId);
83
83
  },
84
+ onReopen(cardId) {
85
+ emit({ kind: 'reopen', cardId });
86
+ userPolicy?.onReopen?.(cardId);
87
+ },
84
88
  onError(cardId, message) {
85
89
  emit({ kind: 'error', cardId, message });
86
90
  userPolicy?.onError?.(cardId, message);
@@ -1,6 +1,12 @@
1
1
  /* Element-bundle stylesheet. Compiled to compiled.css and injected into each
2
2
  custom element's shadow root. Mirrors .storybook/styles.css but scans src/. */
3
3
  @import "tailwindcss";
4
+ /* tw-animate-css supplies the animate-in/animate-out + slide/fade/zoom utilities
5
+ the kit uses for every overlay enter/exit (toast, popover, tooltip, dropdown,
6
+ hover-card, the message action-row hide). Without this @import those classes
7
+ compile to nothing and all the animations are silently dead. Compiled into
8
+ compiled.css at build time, so it's a build-time dep only (no consumer import). */
9
+ @import "tw-animate-css";
4
10
  @import "../../theme.css";
5
11
  @plugin "@tailwindcss/typography";
6
12
 
@@ -65,6 +71,19 @@
65
71
  }
66
72
  *::-webkit-scrollbar-thumb:hover { background-color: var(--color-scrollbar-thumb-hover); }
67
73
  *::-webkit-scrollbar-corner { background: transparent; }
74
+
75
+ /* Respect a user's reduced-motion preference: disable the tw-animate-css
76
+ enter/exit keyframe animations (toasts, overlays, cards) so they appear and
77
+ disappear instantly instead of sliding/fading. Targets the utility classes
78
+ tw-animate-css emits (.animate-in / .animate-out) plus any arbitrary
79
+ `animate-[…]` keyframe utility. */
80
+ @media (prefers-reduced-motion: reduce) {
81
+ .animate-in,
82
+ .animate-out,
83
+ [class*="animate-["] {
84
+ animation: none !important;
85
+ }
86
+ }
68
87
  }
69
88
 
70
89
  @layer components {
@@ -0,0 +1,74 @@
1
+ import { defineWebComponent } from './define';
2
+ import { ToastRegion, type ToastDismissReason, type ToastPosition } from '../components/toast';
3
+ import { toast as toastStore, type ToastItem } from '../primitives/toast-store';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** The toasts to render. Newest is shown on top. Set as a JS property (array);
7
+ * pass a new array reference to update. */
8
+ toasts: ToastItem[];
9
+ /** Stack anchor: `'top-center'` (default), `'top-right'`, `'bottom-center'`, … */
10
+ position?: ToastPosition;
11
+ /** Max simultaneously-visible toasts; the rest queue. Defaults to `3`. */
12
+ max?: number;
13
+ /** Stacking: 'expanded' (default, full column) | 'collapsed' (Sonner-style
14
+ * pile that expands on hover/focus). Attribute: stack. */
15
+ stack?: 'expanded' | 'collapsed';
16
+ /** Container element to anchor this region to (JS property). Set by the store
17
+ * for a scoped region; unset = the global viewport region. */
18
+ target?: HTMLElement;
19
+ }
20
+
21
+ interface Events {
22
+ /** A toast left the stack. `reason` is `'timeout' | 'close' | 'action'`. */
23
+ 'kai-dismiss': { id: string; reason: ToastDismissReason };
24
+ /** A toast's action button was pressed. */
25
+ 'kai-action': { id: string; label: string };
26
+ }
27
+
28
+ /**
29
+ * `<kai-toast-region>` — the viewport overlay that renders the toast stack.
30
+ *
31
+ * It is the substrate behind the imperative `toast()` API: the store lazily
32
+ * mounts ONE of these on `document.body` and binds the list to `toasts`. It is
33
+ * also usable declaratively — set `el.toasts = [...]` (a JS property, never an
34
+ * attribute) and listen for `kai-dismiss` / `kai-action`.
35
+ *
36
+ * Because it is a real `kai-*` element it carries its own shadow root + the
37
+ * shared kit stylesheet, so it is viewport-positioned AND kit-styled.
38
+ */
39
+ defineWebComponent<Props, Events>('kai-toast-region', {
40
+ toasts: [],
41
+ position: 'top-center',
42
+ max: 3,
43
+ stack: 'expanded',
44
+ target: undefined,
45
+ }, (props, { dispatch }) => {
46
+ // `max` may arrive as a string attribute (`<kai-toast-region max="2">`).
47
+ const max = () => {
48
+ const raw = props.max as unknown;
49
+ const n = typeof raw === 'string' ? parseInt(raw, 10) : raw;
50
+ return typeof n === 'number' && !Number.isNaN(n) ? n : 3;
51
+ };
52
+
53
+ const remove = (id: string) => {
54
+ // Drop from the shared store (imperative path). For purely-declarative
55
+ // consumers the store simply won't contain the id — the dispatched event
56
+ // is the signal for them to update their own `toasts` array.
57
+ toastStore.dismiss(id);
58
+ };
59
+
60
+ return (
61
+ <ToastRegion
62
+ toasts={props.toasts ?? []}
63
+ position={props.position}
64
+ max={max()}
65
+ stack={props.stack as 'expanded' | 'collapsed' | undefined}
66
+ target={props.target as HTMLElement | undefined}
67
+ onDismiss={(id, reason) => {
68
+ remove(id);
69
+ dispatch('kai-dismiss', { id, reason });
70
+ }}
71
+ onAction={(id, label) => dispatch('kai-action', { id, label })}
72
+ />
73
+ );
74
+ });
package/src/index.ts CHANGED
@@ -18,6 +18,16 @@ export type { CodeHighlightingOptions } from './primitives/highlighter';
18
18
  export { configurePdfPreview, isPdfPreviewEnabled } from './primitives/pdf-preview';
19
19
  export type { PdfPreviewOptions } from './primitives/pdf-preview';
20
20
 
21
+ // Toasts: imperative `toast()` API + the reactive store behind <kai-toast-region>
22
+ export { toast, configureToasts, ensureMounted as ensureToastRegion, getToasts } from './primitives/toast-store';
23
+ export type {
24
+ ToastItem, ToastVariant, ToastAction, ToastOptions, ToastHandle, ToastFn, ToastConfig,
25
+ } from './primitives/toast-store';
26
+ export { Toast, ToastRegion } from './components/toast';
27
+ export type {
28
+ ToastRegionProps, ToastDismissReason, ToastPosition,
29
+ } from './components/toast';
30
+
21
31
  // Card Contract (generative-UI foundation)
22
32
  export { CARD_CONTRACT_VERSION } from './primitives/card-contract';
23
33
  export type {
@@ -27,6 +37,10 @@ export { applyResolution, resolutionFromEvent } from './primitives/card-resoluti
27
37
  export { CardProvider, useCardHost } from './primitives/card-host';
28
38
  export type { CardProviderProps } from './primitives/card-host';
29
39
  export { CARD_EVENT_NAME, emitCardEvent, routeCardEvent, listenForCardEvents } from './primitives/card-routing';
40
+ export { dismissRecovery, defaultIsReopenable } from './primitives/card-recovery';
41
+ export type {
42
+ RecoveryToast, ReopenEnv, DismissRecoveryOptions,
43
+ } from './primitives/card-recovery';
30
44
  export { validateAgainstSchema } from './primitives/card-validate';
31
45
  export type { JsonSchema, ValidationResult } from './primitives/card-validate';
32
46
 
@@ -47,6 +61,8 @@ export type { CardComponent, CardComponentMap, CardTagMap } from './primitives/c
47
61
  // Card: kai-card (base shell) + kai-form (JSON-Schema form renderer)
48
62
  export { Card } from './components/card';
49
63
  export type { CardProps } from './components/card';
64
+ export { DismissedStub, stubIntent } from './components/dismissed-stub';
65
+ export type { DismissedStubProps, DismissedCardType } from './components/dismissed-stub';
50
66
  export { Form, validateForm, buildResult, widgetFor, orderedKeys, coerceValue } from './components/form';
51
67
  export type {
52
68
  FormProps,
@@ -168,6 +184,20 @@ export {
168
184
  } from './components/chat-container';
169
185
  export { Message, MessageAvatar, MessageContent, MessageActions, MessageAction, MessageCopyButton } from './components/message';
170
186
  export type { MessageCopyButtonProps } from './components/message';
187
+ export { ResponseCompare, useResolved } from './components/response-compare';
188
+ export type { ResponseCompareProps, CompareLayout, ResolvedController } from './components/response-compare';
189
+ export {
190
+ normalizeCandidates,
191
+ buildSelection,
192
+ isAnyStreaming,
193
+ } from './components/response-compare';
194
+ export type {
195
+ CompareCandidate,
196
+ ComparePair,
197
+ CompareCollapse,
198
+ ResponseCompareData,
199
+ CompareSelection,
200
+ } from './components/response-compare';
171
201
  export { MessageSkills } from './components/message-skills';
172
202
  export type { Skill as MessageSkill } from './components/message-skills';
173
203
  export {
@@ -38,18 +38,24 @@ export type CardEvent =
38
38
  | { kind: 'resize'; cardId: string; height: number }
39
39
  | { kind: 'state'; cardId: string; patch: unknown }
40
40
  | { kind: 'dismiss'; cardId: string }
41
+ | { kind: 'reopen'; cardId: string }
41
42
  | { kind: 'error'; cardId: string; message: string };
42
43
 
43
44
  export type CardEventKind = CardEvent['kind'];
44
45
 
45
46
  /** How a card was resolved by the user — the re-hydration channel for the chromed
46
- * read-only state. Mirrors the two terminal CardEvents (minus `cardId`): the
47
- * resolution is just the event that resolved the card. `at` is optional ISO-8601
48
- * provenance (data only; never rendered). Additivedoes not bump the contract
49
- * version. */
47
+ * read-only state. The terminal kinds (`action`/`submit`) mirror the two terminal
48
+ * CardEvents (minus `cardId`): the resolution is just the event that resolved the
49
+ * card. `dismissed` is a DEFERRED (non-terminal) statethe user set the card
50
+ * aside; it can be re-opened. `expired` is terminal — a dismissed card the host
51
+ * decided can no longer be re-opened (e.g. the agent proceeded, or it went stale).
52
+ * `at` is optional ISO-8601 provenance (data only; never rendered). Additive —
53
+ * does not bump the contract version. */
50
54
  export type CardResolution =
51
55
  | { kind: 'action'; action: string; payload?: unknown; at?: string }
52
- | { kind: 'submit'; data: unknown; at?: string };
56
+ | { kind: 'submit'; data: unknown; at?: string }
57
+ | { kind: 'dismissed'; at?: string }
58
+ | { kind: 'expired'; reason?: string; at?: string };
53
59
 
54
60
  /** What every card is handed (via native context or the iframe bridge). */
55
61
  export interface CardHost {
@@ -65,6 +71,9 @@ export interface CardPolicy {
65
71
  onOpen?: (url: string, target: 'tab' | 'artifact') => void;
66
72
  onState?: (cardId: string, patch: unknown) => void;
67
73
  onDismiss?: (cardId: string) => void;
74
+ /** A dismissed card asked to be re-opened. The host decides whether it can come
75
+ * back (clear the resolution → live again) or has expired (stamp `expired`). */
76
+ onReopen?: (cardId: string) => void;
68
77
  onError?: (cardId: string, message: string) => void;
69
78
  /** Cap on send-prompt: 'compose' (default) forbids silent sends. 'send' to allow. */
70
79
  maxSendPromptMode?: 'compose' | 'send';