@kitn.ai/ui 0.14.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 (370) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +394 -0
  3. package/dist/bash-InADTalH.js +1 -0
  4. package/dist/core-AYMC6_lb.js +12 -0
  5. package/dist/css-M7EaDHN_.js +1 -0
  6. package/dist/custom-elements.json +4739 -0
  7. package/dist/engine-javascript-vq0WuIJl.js +141 -0
  8. package/dist/github-dark-dimmed-DUshB20C.js +1 -0
  9. package/dist/github-light-JYsPkUQd.js +1 -0
  10. package/dist/host-embed.d.ts +33 -0
  11. package/dist/html-CPZ3oZQ7.js +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/javascript-C25yR2R2.js +1 -0
  14. package/dist/json-DxJze_jm.js +1 -0
  15. package/dist/kitn-chat-provider.d.ts +2 -0
  16. package/dist/kitn-chat-provider.es.js +240 -0
  17. package/dist/kitn-chat.es.js +77 -0
  18. package/dist/llms/llms-full.txt +1018 -0
  19. package/dist/llms/llms.txt +104 -0
  20. package/dist/origin.d.ts +5 -0
  21. package/dist/provider-runtime.d.ts +27 -0
  22. package/dist/schemas/card-envelope.schema.json +14 -0
  23. package/dist/schemas/card-event.schema.json +12 -0
  24. package/dist/schemas/choice.schema.json +70 -0
  25. package/dist/schemas/confirm.schema.json +65 -0
  26. package/dist/schemas/embed.schema.json +65 -0
  27. package/dist/schemas/form.result.schema.json +7 -0
  28. package/dist/schemas/form.schema.json +33 -0
  29. package/dist/schemas/link.schema.json +56 -0
  30. package/dist/schemas/tasks.result.schema.json +16 -0
  31. package/dist/schemas/tasks.schema.json +78 -0
  32. package/dist/svelte--5p79yCD.js +1 -0
  33. package/dist/theme.tokens.css +176 -0
  34. package/dist/tsx-B8rCNbgL.js +1 -0
  35. package/dist/typescript-RycA9KXf.js +1 -0
  36. package/dist/validate.d.ts +4 -0
  37. package/dist/version.d.ts +4 -0
  38. package/dist/vue-BmIZj4XD.js +1 -0
  39. package/dist/wire.d.ts +53 -0
  40. package/frameworks/react/index.tsx +948 -0
  41. package/frameworks/react/runtime.tsx +94 -0
  42. package/llms-full.txt +1018 -0
  43. package/llms.txt +104 -0
  44. package/package.json +141 -0
  45. package/src/components/artifact.stories.tsx +138 -0
  46. package/src/components/artifact.tsx +743 -0
  47. package/src/components/attachments.stories.tsx +303 -0
  48. package/src/components/attachments.test.tsx +64 -0
  49. package/src/components/attachments.tsx +399 -0
  50. package/src/components/card-fallback.tsx +28 -0
  51. package/src/components/card-renderer.tsx +52 -0
  52. package/src/components/card.tsx +110 -0
  53. package/src/components/chain-of-thought.stories.tsx +211 -0
  54. package/src/components/chain-of-thought.tsx +139 -0
  55. package/src/components/chat-container.stories.tsx +187 -0
  56. package/src/components/chat-container.tsx +82 -0
  57. package/src/components/chat-scope-picker.tsx +47 -0
  58. package/src/components/chat-thread.test.tsx +58 -0
  59. package/src/components/chat-thread.tsx +249 -0
  60. package/src/components/checkpoint.stories.tsx +102 -0
  61. package/src/components/checkpoint.tsx +88 -0
  62. package/src/components/choice-card.tsx +569 -0
  63. package/src/components/code-block.stories.tsx +150 -0
  64. package/src/components/code-block.tsx +105 -0
  65. package/src/components/component-meta.json +3735 -0
  66. package/src/components/confirm-card.stories.tsx +74 -0
  67. package/src/components/confirm-card.tsx +315 -0
  68. package/src/components/context.stories.tsx +229 -0
  69. package/src/components/context.test.tsx +133 -0
  70. package/src/components/context.tsx +363 -0
  71. package/src/components/conversation-item.stories.tsx +125 -0
  72. package/src/components/conversation-item.tsx +16 -0
  73. package/src/components/conversation-list.stories.tsx +133 -0
  74. package/src/components/conversation-list.tsx +101 -0
  75. package/src/components/default-input.test.tsx +111 -0
  76. package/src/components/embed.tsx +196 -0
  77. package/src/components/empty.stories.tsx +434 -0
  78. package/src/components/empty.tsx +166 -0
  79. package/src/components/feedback-bar.stories.tsx +138 -0
  80. package/src/components/feedback-bar.test.tsx +103 -0
  81. package/src/components/feedback-bar.tsx +166 -0
  82. package/src/components/file-tree.stories.tsx +73 -0
  83. package/src/components/file-tree.tsx +383 -0
  84. package/src/components/file-upload.stories.tsx +156 -0
  85. package/src/components/file-upload.tsx +161 -0
  86. package/src/components/form-widgets.tsx +461 -0
  87. package/src/components/form.tsx +873 -0
  88. package/src/components/image.stories.tsx +89 -0
  89. package/src/components/image.tsx +67 -0
  90. package/src/components/link-preview.tsx +194 -0
  91. package/src/components/loader.stories.tsx +181 -0
  92. package/src/components/loader.tsx +333 -0
  93. package/src/components/markdown.stories.tsx +180 -0
  94. package/src/components/markdown.tsx +81 -0
  95. package/src/components/message-action-bar.test.tsx +128 -0
  96. package/src/components/message-narrow.stories.tsx +329 -0
  97. package/src/components/message-skills.stories.tsx +211 -0
  98. package/src/components/message-skills.tsx +36 -0
  99. package/src/components/message.stories.tsx +281 -0
  100. package/src/components/message.tsx +227 -0
  101. package/src/components/model-switcher.stories.tsx +123 -0
  102. package/src/components/model-switcher.test.tsx +65 -0
  103. package/src/components/model-switcher.tsx +87 -0
  104. package/src/components/prompt-input.stories.tsx +222 -0
  105. package/src/components/prompt-input.tsx +211 -0
  106. package/src/components/prompt-suggestion.stories.tsx +142 -0
  107. package/src/components/prompt-suggestion.tsx +115 -0
  108. package/src/components/reasoning.stories.tsx +140 -0
  109. package/src/components/reasoning.tsx +156 -0
  110. package/src/components/resizable.test.tsx +226 -0
  111. package/src/components/response-stream.tsx +103 -0
  112. package/src/components/scroll-button.stories.tsx +100 -0
  113. package/src/components/scroll-button.tsx +34 -0
  114. package/src/components/slash-command.stories.tsx +163 -0
  115. package/src/components/slash-command.tsx +232 -0
  116. package/src/components/source-list.test.tsx +152 -0
  117. package/src/components/source.stories.tsx +124 -0
  118. package/src/components/source.tsx +129 -0
  119. package/src/components/tasks-card.stories.tsx +78 -0
  120. package/src/components/tasks-card.tsx +432 -0
  121. package/src/components/text-shimmer.stories.tsx +87 -0
  122. package/src/components/text-shimmer.tsx +37 -0
  123. package/src/components/thinking-bar.stories.tsx +87 -0
  124. package/src/components/thinking-bar.tsx +50 -0
  125. package/src/components/tool.stories.tsx +153 -0
  126. package/src/components/tool.tsx +184 -0
  127. package/src/components/use-card-resolution.ts +28 -0
  128. package/src/components/voice-input.stories.tsx +83 -0
  129. package/src/components/voice-input.tsx +107 -0
  130. package/src/elements/artifact.stories.tsx +506 -0
  131. package/src/elements/artifact.tsx +137 -0
  132. package/src/elements/attachments.stories.tsx +190 -0
  133. package/src/elements/attachments.tsx +136 -0
  134. package/src/elements/card.stories.tsx +113 -0
  135. package/src/elements/card.tsx +40 -0
  136. package/src/elements/cards.stories.tsx +59 -0
  137. package/src/elements/cards.tsx +97 -0
  138. package/src/elements/catalog.stories.tsx +491 -0
  139. package/src/elements/chain-of-thought.declarative.test.tsx +153 -0
  140. package/src/elements/chain-of-thought.stories.tsx +113 -0
  141. package/src/elements/chain-of-thought.tsx +92 -0
  142. package/src/elements/chat-scope-picker.stories.tsx +73 -0
  143. package/src/elements/chat-scope-picker.tsx +36 -0
  144. package/src/elements/chat-types.ts +42 -0
  145. package/src/elements/chat-workspace.stories.tsx +288 -0
  146. package/src/elements/chat-workspace.tsx +161 -0
  147. package/src/elements/chat.stories.tsx +151 -0
  148. package/src/elements/chat.tsx +80 -0
  149. package/src/elements/checkpoint.stories.tsx +72 -0
  150. package/src/elements/checkpoint.tsx +47 -0
  151. package/src/elements/choice.stories.tsx +240 -0
  152. package/src/elements/choice.tsx +48 -0
  153. package/src/elements/code-block.stories.tsx +83 -0
  154. package/src/elements/code-block.tsx +42 -0
  155. package/src/elements/compiled.css +2 -0
  156. package/src/elements/composed-shell.stories.tsx +316 -0
  157. package/src/elements/confirm-card.stories.tsx +227 -0
  158. package/src/elements/confirm-card.tsx +51 -0
  159. package/src/elements/context-meter.stories.tsx +151 -0
  160. package/src/elements/context-meter.tsx +121 -0
  161. package/src/elements/conversation-list.declarative.test.tsx +137 -0
  162. package/src/elements/conversation-list.stories.tsx +243 -0
  163. package/src/elements/conversation-list.tsx +80 -0
  164. package/src/elements/css.ts +5 -0
  165. package/src/elements/default-input.tsx +247 -0
  166. package/src/elements/define.tsx +171 -0
  167. package/src/elements/element-meta.json +3314 -0
  168. package/src/elements/element-types.d.ts +710 -0
  169. package/src/elements/embed.stories.tsx +213 -0
  170. package/src/elements/embed.tsx +35 -0
  171. package/src/elements/empty.stories.tsx +111 -0
  172. package/src/elements/empty.tsx +29 -0
  173. package/src/elements/feedback-bar.stories.tsx +114 -0
  174. package/src/elements/feedback-bar.tsx +61 -0
  175. package/src/elements/file-tree.stories.tsx +134 -0
  176. package/src/elements/file-tree.tsx +52 -0
  177. package/src/elements/file-upload.stories.tsx +82 -0
  178. package/src/elements/file-upload.tsx +44 -0
  179. package/src/elements/form.stories.tsx +248 -0
  180. package/src/elements/form.tsx +43 -0
  181. package/src/elements/framework-usage.json +563 -0
  182. package/src/elements/image.stories.tsx +71 -0
  183. package/src/elements/image.tsx +32 -0
  184. package/src/elements/link-preview.stories.tsx +209 -0
  185. package/src/elements/link-preview.tsx +34 -0
  186. package/src/elements/loader.stories.tsx +88 -0
  187. package/src/elements/loader.tsx +25 -0
  188. package/src/elements/markdown.stories.tsx +76 -0
  189. package/src/elements/markdown.tsx +38 -0
  190. package/src/elements/message-skills.declarative.test.tsx +104 -0
  191. package/src/elements/message-skills.stories.tsx +114 -0
  192. package/src/elements/message-skills.tsx +85 -0
  193. package/src/elements/message.stories.tsx +218 -0
  194. package/src/elements/message.tsx +162 -0
  195. package/src/elements/model-switcher.declarative.test.tsx +130 -0
  196. package/src/elements/model-switcher.stories.tsx +198 -0
  197. package/src/elements/model-switcher.tsx +93 -0
  198. package/src/elements/popover.stories.tsx +80 -0
  199. package/src/elements/popover.tsx +59 -0
  200. package/src/elements/prompt-input-slash-command.test.tsx +128 -0
  201. package/src/elements/prompt-input-stoppable.test.tsx +88 -0
  202. package/src/elements/prompt-input-toolbar-actions.test.tsx +120 -0
  203. package/src/elements/prompt-input.stories.tsx +331 -0
  204. package/src/elements/prompt-input.tsx +185 -0
  205. package/src/elements/prompt-suggestions.declarative.test.tsx +164 -0
  206. package/src/elements/prompt-suggestions.stories.tsx +213 -0
  207. package/src/elements/prompt-suggestions.tsx +94 -0
  208. package/src/elements/reasoning.stories.tsx +77 -0
  209. package/src/elements/reasoning.tsx +50 -0
  210. package/src/elements/register.ts +56 -0
  211. package/src/elements/remote.stories.tsx +314 -0
  212. package/src/elements/remote.tsx +221 -0
  213. package/src/elements/resizable.d.ts +27 -0
  214. package/src/elements/resizable.stories.tsx +426 -0
  215. package/src/elements/resizable.tsx +553 -0
  216. package/src/elements/response-stream.stories.tsx +79 -0
  217. package/src/elements/response-stream.tsx +40 -0
  218. package/src/elements/scroll-button.stories.tsx +202 -0
  219. package/src/elements/scroll-button.test.tsx +148 -0
  220. package/src/elements/scroll-button.tsx +126 -0
  221. package/src/elements/source-list.stories.tsx +143 -0
  222. package/src/elements/source.stories.tsx +88 -0
  223. package/src/elements/source.tsx +120 -0
  224. package/src/elements/styles.css +199 -0
  225. package/src/elements/switch.stories.tsx +56 -0
  226. package/src/elements/switch.tsx +43 -0
  227. package/src/elements/tasks.stories.tsx +237 -0
  228. package/src/elements/tasks.tsx +46 -0
  229. package/src/elements/text-shimmer.stories.tsx +64 -0
  230. package/src/elements/text-shimmer.tsx +28 -0
  231. package/src/elements/thinking-bar.stories.tsx +72 -0
  232. package/src/elements/thinking-bar.tsx +34 -0
  233. package/src/elements/tool.stories.tsx +89 -0
  234. package/src/elements/tool.tsx +23 -0
  235. package/src/elements/voice-input.stories.tsx +88 -0
  236. package/src/elements/voice-input.tsx +41 -0
  237. package/src/index.ts +253 -0
  238. package/src/primitives/card-contract.ts +71 -0
  239. package/src/primitives/card-host.tsx +35 -0
  240. package/src/primitives/card-registry.tsx +67 -0
  241. package/src/primitives/card-resolution.ts +36 -0
  242. package/src/primitives/card-routing.ts +79 -0
  243. package/src/primitives/card-schemas/card-envelope.schema.json +14 -0
  244. package/src/primitives/card-schemas/card-event.schema.json +12 -0
  245. package/src/primitives/card-schemas/choice.schema.json +70 -0
  246. package/src/primitives/card-schemas/confirm.schema.json +65 -0
  247. package/src/primitives/card-schemas/embed.schema.json +65 -0
  248. package/src/primitives/card-schemas/form.result.schema.json +7 -0
  249. package/src/primitives/card-schemas/form.schema.json +33 -0
  250. package/src/primitives/card-schemas/link.schema.json +56 -0
  251. package/src/primitives/card-schemas/tasks.result.schema.json +16 -0
  252. package/src/primitives/card-schemas/tasks.schema.json +78 -0
  253. package/src/primitives/card-validate.ts +95 -0
  254. package/src/primitives/chat-config.tsx +76 -0
  255. package/src/primitives/controllable.test.ts +47 -0
  256. package/src/primitives/controllable.ts +21 -0
  257. package/src/primitives/embed-providers.ts +254 -0
  258. package/src/primitives/highlighter.ts +157 -0
  259. package/src/primitives/link-preview.ts +87 -0
  260. package/src/primitives/pdf-preview.ts +121 -0
  261. package/src/primitives/use-auto-resize.ts +31 -0
  262. package/src/primitives/use-resize-observer.ts +12 -0
  263. package/src/primitives/use-stick-to-bottom.ts +43 -0
  264. package/src/primitives/use-text-stream.ts +112 -0
  265. package/src/primitives/use-voice-recorder.ts +50 -0
  266. package/src/remote/host-embed.ts +345 -0
  267. package/src/remote/index.ts +2 -0
  268. package/src/remote/origin.ts +30 -0
  269. package/src/remote/provider-runtime.ts +259 -0
  270. package/src/remote/provider.ts +2 -0
  271. package/src/remote/validate.ts +22 -0
  272. package/src/remote/version.ts +12 -0
  273. package/src/remote/wire.ts +48 -0
  274. package/src/stories/chat-panel-layout.stories.tsx +145 -0
  275. package/src/stories/chat-scene.tsx +571 -0
  276. package/src/stories/checkpoint-restore.stories.tsx +256 -0
  277. package/src/stories/context-usage.stories.tsx +212 -0
  278. package/src/stories/conversation-with-reasoning.stories.tsx +182 -0
  279. package/src/stories/conversation-with-sources.stories.tsx +471 -0
  280. package/src/stories/docs/Accessibility.mdx +119 -0
  281. package/src/stories/docs/ChildElements.mdx +269 -0
  282. package/src/stories/docs/ForAIAgents.mdx +162 -0
  283. package/src/stories/docs/GettingStarted.mdx +65 -0
  284. package/src/stories/docs/Installation.mdx +83 -0
  285. package/src/stories/docs/Introduction.mdx +50 -0
  286. package/src/stories/docs/SolidJsAdvanced.mdx +17 -0
  287. package/src/stories/docs/Theming.mdx +85 -0
  288. package/src/stories/docs/element-controls.ts +121 -0
  289. package/src/stories/docs/frameworks/Angular.mdx +245 -0
  290. package/src/stories/docs/frameworks/Html.mdx +227 -0
  291. package/src/stories/docs/frameworks/Overview.mdx +47 -0
  292. package/src/stories/docs/frameworks/React.mdx +227 -0
  293. package/src/stories/docs/frameworks/Solid.mdx +173 -0
  294. package/src/stories/docs/frameworks/Svelte.mdx +194 -0
  295. package/src/stories/docs/frameworks/Vue.mdx +238 -0
  296. package/src/stories/docs/generative-ui-overview.mdx +186 -0
  297. package/src/stories/docs/recipes/SpeechToText.mdx +7 -0
  298. package/src/stories/docs/recipes/Streaming.mdx +68 -0
  299. package/src/stories/docs/recipes/TextToSpeech.mdx +35 -0
  300. package/src/stories/docs/theme-editor/canvas.tsx +32 -0
  301. package/src/stories/docs/theme-editor/inspector.tsx +66 -0
  302. package/src/stories/docs/theme-editor/presets.test.ts +32 -0
  303. package/src/stories/docs/theme-editor/presets.ts +64 -0
  304. package/src/stories/docs/theme-editor/theme-css.test.ts +19 -0
  305. package/src/stories/docs/theme-editor/theme-css.ts +15 -0
  306. package/src/stories/docs/theme-editor/theme-editor.tsx +146 -0
  307. package/src/stories/docs/theme-tokens.tsx +174 -0
  308. package/src/stories/examples/ChoosingComponents.mdx +106 -0
  309. package/src/stories/examples/sample-data.ts +79 -0
  310. package/src/stories/examples/usage/checkpoint-restore.ts +156 -0
  311. package/src/stories/examples/usage/context-usage.ts +591 -0
  312. package/src/stories/examples/usage/conversation-with-reasoning.ts +224 -0
  313. package/src/stories/examples/usage/conversation-with-sources.ts +589 -0
  314. package/src/stories/examples/usage/empty-state.ts +144 -0
  315. package/src/stories/examples/usage/full-chat-app.ts +277 -0
  316. package/src/stories/examples/usage/index.ts +63 -0
  317. package/src/stories/examples/usage/message-actions.ts +704 -0
  318. package/src/stories/examples/usage/pattern-centered-conversation.ts +446 -0
  319. package/src/stories/examples/usage/pattern-chat-panel-layout.ts +442 -0
  320. package/src/stories/examples/usage/pattern-docked-widget.ts +479 -0
  321. package/src/stories/examples/usage/prompt-input-variants.ts +1356 -0
  322. package/src/stories/examples/usage/streaming-response.ts +687 -0
  323. package/src/stories/examples/usage/types.ts +33 -0
  324. package/src/stories/full-chat.stories.tsx +18 -0
  325. package/src/stories/message-actions.stories.tsx +230 -0
  326. package/src/stories/pattern-centered-conversation.stories.tsx +93 -0
  327. package/src/stories/pattern-docked-widget.stories.tsx +93 -0
  328. package/src/stories/pattern-empty-state.stories.tsx +76 -0
  329. package/src/stories/prompt-input-variants.stories.tsx +456 -0
  330. package/src/stories/streaming-response.stories.tsx +438 -0
  331. package/src/stories/theme-editor.stories.tsx +16 -0
  332. package/src/stories/token-reference.stories.tsx +18 -0
  333. package/src/stories/typography.stories.tsx +78 -0
  334. package/src/types.ts +47 -0
  335. package/src/ui/action-icons.ts +52 -0
  336. package/src/ui/avatar.stories.tsx +103 -0
  337. package/src/ui/avatar.tsx +23 -0
  338. package/src/ui/badge.stories.tsx +86 -0
  339. package/src/ui/badge.tsx +21 -0
  340. package/src/ui/button.stories.tsx +145 -0
  341. package/src/ui/button.tsx +38 -0
  342. package/src/ui/collapsible.stories.tsx +69 -0
  343. package/src/ui/collapsible.tsx +125 -0
  344. package/src/ui/dropdown.stories.tsx +59 -0
  345. package/src/ui/dropdown.tsx +196 -0
  346. package/src/ui/hover-card.stories.tsx +77 -0
  347. package/src/ui/hover-card.test.tsx +30 -0
  348. package/src/ui/hover-card.tsx +172 -0
  349. package/src/ui/overlay.stories.tsx +115 -0
  350. package/src/ui/overlay.tsx +158 -0
  351. package/src/ui/popover.stories.tsx +81 -0
  352. package/src/ui/popover.test.tsx +99 -0
  353. package/src/ui/popover.tsx +92 -0
  354. package/src/ui/resizable.stories.tsx +236 -0
  355. package/src/ui/resizable.tsx +576 -0
  356. package/src/ui/scroll-area.stories.tsx +50 -0
  357. package/src/ui/scroll-area.tsx +15 -0
  358. package/src/ui/separator.stories.tsx +81 -0
  359. package/src/ui/separator.tsx +10 -0
  360. package/src/ui/skeleton.stories.tsx +337 -0
  361. package/src/ui/skeleton.tsx +16 -0
  362. package/src/ui/switch.stories.tsx +51 -0
  363. package/src/ui/switch.test.tsx +59 -0
  364. package/src/ui/switch.tsx +62 -0
  365. package/src/ui/textarea.stories.tsx +76 -0
  366. package/src/ui/textarea.tsx +21 -0
  367. package/src/ui/tooltip.stories.tsx +74 -0
  368. package/src/ui/tooltip.tsx +68 -0
  369. package/src/utils/cn.ts +24 -0
  370. package/theme.css +187 -0
@@ -0,0 +1,211 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import {
3
+ ChainOfThought,
4
+ ChainOfThoughtStep,
5
+ ChainOfThoughtTrigger,
6
+ ChainOfThoughtContent,
7
+ ChainOfThoughtItem,
8
+ } from './chain-of-thought';
9
+ import { componentDescription } from '../stories/docs/element-controls';
10
+
11
+ const meta = {
12
+ title: 'Solid (Advanced)/Elements/ChainOfThought',
13
+ component: ChainOfThought,
14
+ tags: ['autodocs'],
15
+ parameters: {
16
+ layout: 'padded',
17
+ docs: {
18
+ description: componentDescription([
19
+ 'A vertical, collapsible timeline that reveals an agent\'s reasoning as a series of connected steps. Composed of `ChainOfThought` (root) wrapping `ChainOfThoughtStep` items, each with a `ChainOfThoughtTrigger`, `ChainOfThoughtContent`, and one or more `ChainOfThoughtItem`s.',
20
+ '**When to use:** to surface multi-step reasoning, tool calls, or research progress behind an assistant response, letting users expand each step on demand.',
21
+ '**How to use:** map each reasoning step to a `ChainOfThoughtStep` (mark the final one with `isLast`); put the summary in `ChainOfThoughtTrigger` and the detail inside `ChainOfThoughtContent` / `ChainOfThoughtItem`.',
22
+ '**Placement:** above or within an assistant message, typically before the final answer.',
23
+ ]),
24
+ controls: { exclude: ['use:eventListener'] },
25
+ },
26
+ },
27
+ argTypes: {
28
+ children: {
29
+ control: false,
30
+ description: 'The `ChainOfThoughtStep` items that make up the timeline.',
31
+ },
32
+ class: {
33
+ control: 'text',
34
+ description: 'Extra classes for the root wrapper.',
35
+ },
36
+ },
37
+ args: {},
38
+ render: (args) => (
39
+ <ChainOfThought {...args}>
40
+ <ChainOfThoughtStep>
41
+ <ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
42
+ <ChainOfThoughtContent>
43
+ <ChainOfThoughtItem>
44
+ The user is asking about reactive programming concepts in SolidJS.
45
+ </ChainOfThoughtItem>
46
+ </ChainOfThoughtContent>
47
+ </ChainOfThoughtStep>
48
+ <ChainOfThoughtStep isLast>
49
+ <ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
50
+ <ChainOfThoughtContent>
51
+ <ChainOfThoughtItem>
52
+ Combining insights from documentation and examples into a clear answer.
53
+ </ChainOfThoughtItem>
54
+ </ChainOfThoughtContent>
55
+ </ChainOfThoughtStep>
56
+ </ChainOfThought>
57
+ ),
58
+ } satisfies Meta<typeof ChainOfThought>;
59
+
60
+ export default meta;
61
+ type Story = StoryObj<typeof meta>;
62
+
63
+ const IMPORT = `import {
64
+ ChainOfThought, ChainOfThoughtStep, ChainOfThoughtTrigger,
65
+ ChainOfThoughtContent, ChainOfThoughtItem,
66
+ } from '@kitn.ai/ui';`;
67
+ const src = (code: string) => ({
68
+ parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
69
+ });
70
+
71
+ /** Interactive playground — expand the steps to reveal each reasoning item. */
72
+ export const Playground: Story = {
73
+ ...src(`<ChainOfThought>
74
+ <ChainOfThoughtStep>
75
+ <ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
76
+ <ChainOfThoughtContent>
77
+ <ChainOfThoughtItem>The user is asking about SolidJS reactivity.</ChainOfThoughtItem>
78
+ </ChainOfThoughtContent>
79
+ </ChainOfThoughtStep>
80
+ <ChainOfThoughtStep isLast>
81
+ <ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
82
+ <ChainOfThoughtContent>
83
+ <ChainOfThoughtItem>Combining insights into a clear answer.</ChainOfThoughtItem>
84
+ </ChainOfThoughtContent>
85
+ </ChainOfThoughtStep>
86
+ </ChainOfThought>`),
87
+ };
88
+
89
+ export const SingleStep: Story = {
90
+ render: () => (
91
+ <ChainOfThought>
92
+ <ChainOfThoughtStep isLast>
93
+ <ChainOfThoughtTrigger>Analyzing the question</ChainOfThoughtTrigger>
94
+ <ChainOfThoughtContent>
95
+ <ChainOfThoughtItem>
96
+ Breaking down the user's query into key concepts and identifying relevant knowledge areas.
97
+ </ChainOfThoughtItem>
98
+ </ChainOfThoughtContent>
99
+ </ChainOfThoughtStep>
100
+ </ChainOfThought>
101
+ ),
102
+ ...src(`<ChainOfThought>
103
+ <ChainOfThoughtStep isLast>
104
+ <ChainOfThoughtTrigger>Analyzing the question</ChainOfThoughtTrigger>
105
+ <ChainOfThoughtContent>
106
+ <ChainOfThoughtItem>Breaking down the query into key concepts.</ChainOfThoughtItem>
107
+ </ChainOfThoughtContent>
108
+ </ChainOfThoughtStep>
109
+ </ChainOfThought>`),
110
+ };
111
+
112
+ export const MultipleSteps: Story = {
113
+ render: () => (
114
+ <ChainOfThought>
115
+ <ChainOfThoughtStep>
116
+ <ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
117
+ <ChainOfThoughtContent>
118
+ <ChainOfThoughtItem>
119
+ The user is asking about reactive programming concepts in SolidJS.
120
+ </ChainOfThoughtItem>
121
+ </ChainOfThoughtContent>
122
+ </ChainOfThoughtStep>
123
+ <ChainOfThoughtStep>
124
+ <ChainOfThoughtTrigger>Searching knowledge base</ChainOfThoughtTrigger>
125
+ <ChainOfThoughtContent>
126
+ <ChainOfThoughtItem>
127
+ Found 3 relevant documents about SolidJS signals and reactivity.
128
+ </ChainOfThoughtItem>
129
+ </ChainOfThoughtContent>
130
+ </ChainOfThoughtStep>
131
+ <ChainOfThoughtStep isLast>
132
+ <ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
133
+ <ChainOfThoughtContent>
134
+ <ChainOfThoughtItem>
135
+ Combining insights from documentation and examples to create a comprehensive answer.
136
+ </ChainOfThoughtItem>
137
+ </ChainOfThoughtContent>
138
+ </ChainOfThoughtStep>
139
+ </ChainOfThought>
140
+ ),
141
+ ...src(`<ChainOfThought>
142
+ <ChainOfThoughtStep>
143
+ <ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
144
+ <ChainOfThoughtContent>
145
+ <ChainOfThoughtItem>The user is asking about SolidJS reactivity.</ChainOfThoughtItem>
146
+ </ChainOfThoughtContent>
147
+ </ChainOfThoughtStep>
148
+ <ChainOfThoughtStep>
149
+ <ChainOfThoughtTrigger>Searching knowledge base</ChainOfThoughtTrigger>
150
+ <ChainOfThoughtContent>
151
+ <ChainOfThoughtItem>Found 3 relevant documents.</ChainOfThoughtItem>
152
+ </ChainOfThoughtContent>
153
+ </ChainOfThoughtStep>
154
+ <ChainOfThoughtStep isLast>
155
+ <ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
156
+ <ChainOfThoughtContent>
157
+ <ChainOfThoughtItem>Combining insights into a clear answer.</ChainOfThoughtItem>
158
+ </ChainOfThoughtContent>
159
+ </ChainOfThoughtStep>
160
+ </ChainOfThought>`),
161
+ };
162
+
163
+ export const WithCustomIcons: Story = {
164
+ render: () => (
165
+ <ChainOfThought>
166
+ <ChainOfThoughtStep>
167
+ <ChainOfThoughtTrigger
168
+ leftIcon={
169
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
170
+ <circle cx="11" cy="11" r="8" />
171
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
172
+ </svg>
173
+ }
174
+ >
175
+ Searching documents
176
+ </ChainOfThoughtTrigger>
177
+ <ChainOfThoughtContent>
178
+ <ChainOfThoughtItem>Found 5 relevant results.</ChainOfThoughtItem>
179
+ </ChainOfThoughtContent>
180
+ </ChainOfThoughtStep>
181
+ <ChainOfThoughtStep isLast>
182
+ <ChainOfThoughtTrigger
183
+ leftIcon={
184
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
185
+ <polyline points="20 6 9 17 4 12" />
186
+ </svg>
187
+ }
188
+ >
189
+ Analysis complete
190
+ </ChainOfThoughtTrigger>
191
+ <ChainOfThoughtContent>
192
+ <ChainOfThoughtItem>All sources have been analyzed and synthesized.</ChainOfThoughtItem>
193
+ </ChainOfThoughtContent>
194
+ </ChainOfThoughtStep>
195
+ </ChainOfThought>
196
+ ),
197
+ ...src(`<ChainOfThought>
198
+ <ChainOfThoughtStep>
199
+ <ChainOfThoughtTrigger leftIcon={<SearchIcon />}>Searching documents</ChainOfThoughtTrigger>
200
+ <ChainOfThoughtContent>
201
+ <ChainOfThoughtItem>Found 5 relevant results.</ChainOfThoughtItem>
202
+ </ChainOfThoughtContent>
203
+ </ChainOfThoughtStep>
204
+ <ChainOfThoughtStep isLast>
205
+ <ChainOfThoughtTrigger leftIcon={<CheckIcon />}>Analysis complete</ChainOfThoughtTrigger>
206
+ <ChainOfThoughtContent>
207
+ <ChainOfThoughtItem>All sources analyzed and synthesized.</ChainOfThoughtItem>
208
+ </ChainOfThoughtContent>
209
+ </ChainOfThoughtStep>
210
+ </ChainOfThought>`),
211
+ };
@@ -0,0 +1,139 @@
1
+ import { type JSX, splitProps, children as resolveChildren, For, Show } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../ui/collapsible';
4
+ import { ChevronDown, Circle } from 'lucide-solid';
5
+
6
+ // --- ChainOfThoughtItem ---
7
+
8
+ export interface ChainOfThoughtItemProps extends JSX.HTMLAttributes<HTMLDivElement> {
9
+ children: JSX.Element;
10
+ }
11
+
12
+ function ChainOfThoughtItem(props: ChainOfThoughtItemProps) {
13
+ const [local, rest] = splitProps(props, ['children', 'class']);
14
+ return (
15
+ <div class={cn('text-muted-foreground text-sm', local.class)} {...rest}>
16
+ {local.children}
17
+ </div>
18
+ );
19
+ }
20
+
21
+ // --- ChainOfThoughtTrigger ---
22
+
23
+ export interface ChainOfThoughtTriggerProps {
24
+ children: JSX.Element;
25
+ class?: string;
26
+ leftIcon?: JSX.Element;
27
+ swapIconOnHover?: boolean;
28
+ }
29
+
30
+ function ChainOfThoughtTrigger(props: ChainOfThoughtTriggerProps) {
31
+ const swapOnHover = () => props.swapIconOnHover ?? true;
32
+
33
+ return (
34
+ <CollapsibleTrigger
35
+ class={cn(
36
+ 'group text-muted-foreground hover:text-foreground flex cursor-pointer items-center justify-start gap-1 text-left text-meta transition-colors',
37
+ props.class
38
+ )}
39
+ >
40
+ <div class="flex items-center gap-2">
41
+ <Show
42
+ when={props.leftIcon}
43
+ fallback={
44
+ <span class="relative inline-flex size-4 items-center justify-center">
45
+ <Circle class="size-2 fill-current" />
46
+ </span>
47
+ }
48
+ >
49
+ <span class="relative inline-flex size-4 items-center justify-center">
50
+ <span
51
+ class={cn(
52
+ 'transition-opacity',
53
+ swapOnHover() && 'group-hover:opacity-0'
54
+ )}
55
+ >
56
+ {props.leftIcon}
57
+ </span>
58
+ <Show when={swapOnHover()}>
59
+ <ChevronDown class="absolute size-4 opacity-0 transition-opacity group-hover:opacity-100 group-data-[state=open]:rotate-180" />
60
+ </Show>
61
+ </span>
62
+ </Show>
63
+ <span>{props.children}</span>
64
+ </div>
65
+ <Show when={!props.leftIcon}>
66
+ <ChevronDown class="size-4 transition-transform group-data-[state=open]:rotate-180" />
67
+ </Show>
68
+ </CollapsibleTrigger>
69
+ );
70
+ }
71
+
72
+ // --- ChainOfThoughtContent ---
73
+
74
+ export interface ChainOfThoughtContentProps {
75
+ children: JSX.Element;
76
+ class?: string;
77
+ }
78
+
79
+ function ChainOfThoughtContent(props: ChainOfThoughtContentProps) {
80
+ return (
81
+ <CollapsibleContent
82
+ class={cn(
83
+ 'text-popover-foreground data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down overflow-hidden',
84
+ props.class
85
+ )}
86
+ >
87
+ <div class="grid grid-cols-[min-content_minmax(0,1fr)] gap-x-4">
88
+ <div class="bg-primary/20 ml-1.75 h-full w-px group-data-[last=true]:hidden" />
89
+ <div class="ml-1.75 h-full w-px bg-transparent group-data-[last=false]:hidden" />
90
+ <div class="mt-2 space-y-2">{props.children}</div>
91
+ </div>
92
+ </CollapsibleContent>
93
+ );
94
+ }
95
+
96
+ // --- ChainOfThought (Root) ---
97
+
98
+ export interface ChainOfThoughtProps {
99
+ children: JSX.Element;
100
+ class?: string;
101
+ }
102
+
103
+ function ChainOfThought(props: ChainOfThoughtProps) {
104
+ return (
105
+ <div class={cn('space-y-0', props.class)}>
106
+ {props.children}
107
+ </div>
108
+ );
109
+ }
110
+
111
+ // --- ChainOfThoughtStep ---
112
+
113
+ export interface ChainOfThoughtStepProps {
114
+ children: JSX.Element;
115
+ class?: string;
116
+ isLast?: boolean;
117
+ }
118
+
119
+ function ChainOfThoughtStep(props: ChainOfThoughtStepProps) {
120
+ return (
121
+ <Collapsible
122
+ class={cn('group', props.class)}
123
+ data-last={props.isLast ?? false}
124
+ >
125
+ {props.children}
126
+ <div class="flex justify-start group-data-[last=true]:hidden">
127
+ <div class="bg-primary/20 ml-1.75 h-4 w-px" />
128
+ </div>
129
+ </Collapsible>
130
+ );
131
+ }
132
+
133
+ export {
134
+ ChainOfThought,
135
+ ChainOfThoughtStep,
136
+ ChainOfThoughtTrigger,
137
+ ChainOfThoughtContent,
138
+ ChainOfThoughtItem,
139
+ };
@@ -0,0 +1,187 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { For } from 'solid-js';
3
+ import { ChatContainerRoot, ChatContainerContent, ChatContainerScrollAnchor } from './chat-container';
4
+ import { Message, MessageAvatar, MessageContent } from './message';
5
+ import { componentDescription } from '../stories/docs/element-controls';
6
+
7
+ const sampleMessages = [
8
+ { role: 'user', content: 'What is SolidJS?' },
9
+ { role: 'assistant', content: '**SolidJS** is a declarative, efficient, and flexible JavaScript library for building user interfaces. Unlike React, it uses fine-grained reactivity with no Virtual DOM, resulting in excellent performance.' },
10
+ { role: 'user', content: 'How does reactivity work in SolidJS?' },
11
+ { role: 'assistant', content: `SolidJS uses **signals** as its core reactive primitive. Here's how it works:
12
+
13
+ 1. **Signals** -- Store reactive values that track their dependencies
14
+ 2. **Effects** -- Side effects that re-run when their signal dependencies change
15
+ 3. **Memos** -- Derived values that cache their results
16
+
17
+ Unlike React's useState, SolidJS signals are getter/setter pairs that update only the specific DOM nodes that depend on them.` },
18
+ { role: 'user', content: 'Can you show me an example?' },
19
+ { role: 'assistant', content: `Here's a simple counter example:
20
+
21
+ \`\`\`typescript
22
+ import { createSignal } from 'solid-js';
23
+
24
+ function Counter() {
25
+ const [count, setCount] = createSignal(0);
26
+ return (
27
+ <button onClick={() => setCount(c => c + 1)}>
28
+ Count: {count()}
29
+ </button>
30
+ );
31
+ }
32
+ \`\`\`
33
+
34
+ Notice that \`count\` is called as a function -- this is how SolidJS tracks which parts of the UI depend on which signals.` },
35
+ ];
36
+
37
+ const meta = {
38
+ title: 'Solid (Advanced)/Elements/ChatContainer',
39
+ component: ChatContainerRoot,
40
+ tags: ['autodocs'],
41
+ parameters: {
42
+ layout: 'padded',
43
+ docs: {
44
+ description: componentDescription([
45
+ 'A scrollable message viewport that automatically sticks to the bottom as new content streams in. Composed of `ChatContainerRoot` (the scroll region), `ChatContainerContent` (the message stack), and `ChatContainerScrollAnchor` (the stick-to-bottom target).',
46
+ '**When to use:** as the conversation transcript region of a chat UI, where messages append over time and the view should follow the latest output unless the user scrolls up.',
47
+ '**How to use:** wrap your message list in `ChatContainerRoot`, place messages inside `ChatContainerContent`, and end with `ChatContainerScrollAnchor`. Give the root a fixed height so it can scroll.',
48
+ '**Placement:** the central pane of a chat layout, between the header and the prompt input.',
49
+ ]),
50
+ controls: { exclude: ['use:eventListener'] },
51
+ },
52
+ },
53
+ argTypes: {
54
+ children: {
55
+ control: false,
56
+ description: 'The container content — typically `ChatContainerContent` with messages.',
57
+ },
58
+ class: {
59
+ control: 'text',
60
+ description: 'Extra classes for the scroll region (set a height so it can scroll).',
61
+ },
62
+ },
63
+ args: {
64
+ class: 'h-full flex-col p-4',
65
+ },
66
+ render: (args) => (
67
+ <div class="h-[500px] w-full max-w-2xl border border-border rounded-lg overflow-hidden">
68
+ <ChatContainerRoot {...args}>
69
+ <ChatContainerContent class="space-y-4">
70
+ <For each={sampleMessages}>
71
+ {(msg) => (
72
+ <Message>
73
+ <MessageAvatar src="" fallback={msg.role === 'user' ? 'U' : 'AI'} alt={msg.role} />
74
+ <MessageContent markdown={msg.role === 'assistant'}>{msg.content}</MessageContent>
75
+ </Message>
76
+ )}
77
+ </For>
78
+ <ChatContainerScrollAnchor />
79
+ </ChatContainerContent>
80
+ </ChatContainerRoot>
81
+ </div>
82
+ ),
83
+ } satisfies Meta<typeof ChatContainerRoot>;
84
+
85
+ export default meta;
86
+ type Story = StoryObj<typeof meta>;
87
+
88
+ const IMPORT = `import {
89
+ ChatContainerRoot, ChatContainerContent, ChatContainerScrollAnchor,
90
+ Message, MessageAvatar, MessageContent,
91
+ } from '@kitn.ai/ui';`;
92
+ const src = (code: string) => ({
93
+ parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
94
+ });
95
+
96
+ /** Interactive playground — a full transcript inside the stick-to-bottom container. */
97
+ export const Playground: Story = {
98
+ ...src(`<div class="h-[500px]">
99
+ <ChatContainerRoot class="h-full flex-col p-4">
100
+ <ChatContainerContent class="space-y-4">
101
+ <For each={messages}>
102
+ {(msg) => (
103
+ <Message>
104
+ <MessageAvatar src="" fallback={msg.role === 'user' ? 'U' : 'AI'} alt={msg.role} />
105
+ <MessageContent markdown={msg.role === 'assistant'}>{msg.content}</MessageContent>
106
+ </Message>
107
+ )}
108
+ </For>
109
+ <ChatContainerScrollAnchor />
110
+ </ChatContainerContent>
111
+ </ChatContainerRoot>
112
+ </div>`),
113
+ };
114
+
115
+ export const FullChat: Story = {
116
+ render: () => (
117
+ <div class="h-[500px] w-full max-w-2xl border border-border rounded-lg overflow-hidden">
118
+ <ChatContainerRoot class="h-full flex-col p-4">
119
+ <ChatContainerContent class="space-y-4">
120
+ <For each={sampleMessages}>
121
+ {(msg) => (
122
+ <Message>
123
+ <MessageAvatar src="" fallback={msg.role === 'user' ? 'U' : 'AI'} alt={msg.role} />
124
+ <MessageContent markdown={msg.role === 'assistant'}>{msg.content}</MessageContent>
125
+ </Message>
126
+ )}
127
+ </For>
128
+ <ChatContainerScrollAnchor />
129
+ </ChatContainerContent>
130
+ </ChatContainerRoot>
131
+ </div>
132
+ ),
133
+ ...src(`<ChatContainerRoot class="h-full flex-col p-4">
134
+ <ChatContainerContent class="space-y-4">
135
+ <For each={messages}>
136
+ {(msg) => (
137
+ <Message>
138
+ <MessageAvatar src="" fallback={msg.role === 'user' ? 'U' : 'AI'} alt={msg.role} />
139
+ <MessageContent markdown={msg.role === 'assistant'}>{msg.content}</MessageContent>
140
+ </Message>
141
+ )}
142
+ </For>
143
+ <ChatContainerScrollAnchor />
144
+ </ChatContainerContent>
145
+ </ChatContainerRoot>`),
146
+ };
147
+
148
+ export const LongConversation: Story = {
149
+ render: () => {
150
+ const manyMessages = Array.from({ length: 20 }, (_, i) => ({
151
+ role: i % 2 === 0 ? 'user' : 'assistant',
152
+ content: i % 2 === 0
153
+ ? `This is user message number ${Math.floor(i / 2) + 1}. It asks a question about the topic.`
154
+ : `This is the assistant's response to message ${Math.floor(i / 2) + 1}. It provides a detailed explanation with relevant examples and context.`,
155
+ }));
156
+ return (
157
+ <div class="h-[400px] w-full max-w-2xl border border-border rounded-lg overflow-hidden">
158
+ <ChatContainerRoot class="h-full flex-col p-4">
159
+ <ChatContainerContent class="space-y-4">
160
+ <For each={manyMessages}>
161
+ {(msg) => (
162
+ <Message>
163
+ <MessageAvatar src="" fallback={msg.role === 'user' ? 'U' : 'AI'} alt={msg.role} />
164
+ <MessageContent>{msg.content}</MessageContent>
165
+ </Message>
166
+ )}
167
+ </For>
168
+ <ChatContainerScrollAnchor />
169
+ </ChatContainerContent>
170
+ </ChatContainerRoot>
171
+ </div>
172
+ );
173
+ },
174
+ ...src(`<ChatContainerRoot class="h-full flex-col p-4">
175
+ <ChatContainerContent class="space-y-4">
176
+ <For each={manyMessages}>
177
+ {(msg) => (
178
+ <Message>
179
+ <MessageAvatar src="" fallback={msg.role === 'user' ? 'U' : 'AI'} alt={msg.role} />
180
+ <MessageContent>{msg.content}</MessageContent>
181
+ </Message>
182
+ )}
183
+ </For>
184
+ <ChatContainerScrollAnchor />
185
+ </ChatContainerContent>
186
+ </ChatContainerRoot>`),
187
+ };
@@ -0,0 +1,82 @@
1
+ import { type JSX, splitProps, createContext, useContext } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { useStickToBottom } from '../primitives/use-stick-to-bottom';
4
+
5
+ interface ChatContainerContextValue {
6
+ isAtBottom: () => boolean;
7
+ scrollToBottom: (behavior?: ScrollBehavior) => void;
8
+ }
9
+
10
+ const ChatContainerContext = createContext<ChatContainerContextValue>();
11
+
12
+ export function useChatContainer() {
13
+ const ctx = useContext(ChatContainerContext);
14
+ if (!ctx) throw new Error('useChatContainer must be used within ChatContainer');
15
+ return ctx;
16
+ }
17
+
18
+ // --- ChatContainerRoot ---
19
+
20
+ export interface ChatContainerRootProps extends JSX.HTMLAttributes<HTMLDivElement> {
21
+ children: JSX.Element;
22
+ }
23
+
24
+ function ChatContainerRoot(props: ChatContainerRootProps) {
25
+ const [local, rest] = splitProps(props, ['children', 'class']);
26
+ const { ref, isAtBottom, scrollToBottom } = useStickToBottom();
27
+ return (
28
+ <ChatContainerContext.Provider value={{ isAtBottom, scrollToBottom }}>
29
+ <div
30
+ ref={ref}
31
+ class={cn('flex flex-col overflow-y-auto', local.class)}
32
+ role="log"
33
+ // Keyboard users must be able to scroll the conversation even when no
34
+ // message contains a focusable control (WCAG 2.1.1 — axe
35
+ // `scrollable-region-focusable`).
36
+ tabindex={0}
37
+ {...rest}
38
+ >
39
+ {local.children}
40
+ </div>
41
+ </ChatContainerContext.Provider>
42
+ );
43
+ }
44
+
45
+ // --- ChatContainerContent ---
46
+
47
+ export interface ChatContainerContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
48
+ children: JSX.Element;
49
+ }
50
+
51
+ function ChatContainerContent(props: ChatContainerContentProps) {
52
+ const [local, rest] = splitProps(props, ['children', 'class']);
53
+ return (
54
+ <div class={cn('flex w-full flex-col', local.class)} {...rest}>
55
+ {local.children}
56
+ </div>
57
+ );
58
+ }
59
+
60
+ // --- ChatContainerScrollAnchor ---
61
+
62
+ export interface ChatContainerScrollAnchorProps extends JSX.HTMLAttributes<HTMLDivElement> {
63
+ ref?: HTMLDivElement | ((el: HTMLDivElement) => void);
64
+ }
65
+
66
+ function ChatContainerScrollAnchor(props: ChatContainerScrollAnchorProps) {
67
+ const [local, rest] = splitProps(props, ['class']);
68
+ return (
69
+ <div
70
+ class={cn('h-px w-full shrink-0 scroll-mt-4', local.class)}
71
+ aria-hidden="true"
72
+ {...rest}
73
+ />
74
+ );
75
+ }
76
+
77
+ export {
78
+ ChatContainerRoot as ChatContainer,
79
+ ChatContainerRoot,
80
+ ChatContainerContent,
81
+ ChatContainerScrollAnchor,
82
+ };
@@ -0,0 +1,47 @@
1
+ import { splitProps, Show, For } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '../ui/dropdown';
4
+ import { Button } from '../ui/button';
5
+ import type { SearchFilters } from '../types';
6
+
7
+ export interface ChatScopePickerProps {
8
+ currentLabel: string;
9
+ onScopeChange: (filters: SearchFilters | undefined) => void;
10
+ availableAuthors?: string[];
11
+ availableTags?: string[];
12
+ class?: string;
13
+ }
14
+
15
+ export function ChatScopePicker(props: ChatScopePickerProps) {
16
+ const [local] = splitProps(props, ['currentLabel', 'onScopeChange', 'availableAuthors', 'availableTags', 'class']);
17
+ return (
18
+ <Dropdown>
19
+ <DropdownTrigger as={(triggerProps: any) => (
20
+ <Button variant="ghost" size="sm" class={cn('gap-1 text-xs', local.class)} {...triggerProps}>
21
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
22
+ {local.currentLabel}
23
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
24
+ </Button>
25
+ )} />
26
+ <DropdownContent class="min-w-[180px]">
27
+ <DropdownItem onSelect={() => local.onScopeChange(undefined)}>All Content</DropdownItem>
28
+ <Show when={local.availableAuthors?.length}>
29
+ <div class="px-2 py-1 text-[10px] uppercase tracking-wider text-muted-foreground">Authors</div>
30
+ <For each={local.availableAuthors}>
31
+ {(author) => (
32
+ <DropdownItem onSelect={() => local.onScopeChange({ authors: [author] })}>{author}</DropdownItem>
33
+ )}
34
+ </For>
35
+ </Show>
36
+ <Show when={local.availableTags?.length}>
37
+ <div class="px-2 py-1 text-[10px] uppercase tracking-wider text-muted-foreground">Tags</div>
38
+ <For each={local.availableTags}>
39
+ {(tag) => (
40
+ <DropdownItem onSelect={() => local.onScopeChange({ tags: [tag] })}>{tag}</DropdownItem>
41
+ )}
42
+ </For>
43
+ </Show>
44
+ </DropdownContent>
45
+ </Dropdown>
46
+ );
47
+ }