@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,76 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import type { JSX } from 'solid-js';
3
+ import { Textarea } from './textarea';
4
+ import { componentDescription } from '../stories/docs/element-controls';
5
+
6
+ const meta = {
7
+ title: 'Solid (Advanced)/Primitives/Textarea',
8
+ component: Textarea,
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ layout: 'padded',
12
+ docs: {
13
+ description: componentDescription([
14
+ 'A single-line-by-default textarea that auto-grows with its content up to an optional `maxHeight`, after which it scrolls. It is **transparent and borderless by design** — it is meant to drop into a composed input frame that owns the visual boundary and focus ring (the demos below wrap it exactly the way `PromptInput` / `<kai-prompt-input>` does, with `focus-within` on the frame). This is the editable surface behind `PromptInput`.',
15
+ '**When to use:** free-text entry that may span multiple lines — a chat composer, a comment box, an editable note.',
16
+ '**How to use:** drop it inside a framed container and use it like a native `<textarea>` (`value`, `placeholder`, `onInput`, …). Auto-resize is on by default; set `maxHeight` (px) to cap growth, or `autoResize={false}` for a fixed-height field.',
17
+ ]),
18
+ },
19
+ },
20
+ argTypes: {
21
+ placeholder: { control: 'text', description: 'Placeholder text.' },
22
+ maxHeight: { control: 'number', description: 'Max height (px) before the field scrolls instead of growing.' },
23
+ autoResize: { control: 'boolean', description: 'Grow with content. Default true.' },
24
+ class: { control: 'text', description: 'Extra classes.' },
25
+ },
26
+ args: {
27
+ placeholder: 'Ask anything… (Shift+Enter for a newline)',
28
+ maxHeight: 200,
29
+ },
30
+ render: (args) => (
31
+ <Frame>
32
+ <Textarea {...args} class="focus-visible:ring-0" />
33
+ </Frame>
34
+ ),
35
+ } satisfies Meta<typeof Textarea>;
36
+
37
+ /**
38
+ * The composed input frame, mirroring how `PromptInput` wraps the textarea: the
39
+ * FRAME owns the boundary + focus ring (`focus-within`), and the transparent
40
+ * textarea inside has its own ring neutralized so there's no nested box.
41
+ */
42
+ function Frame(props: { children: JSX.Element }) {
43
+ return (
44
+ <div class="w-96 cursor-text rounded-xl border border-border bg-muted/40 p-3 shadow-xs transition-shadow focus-within:border-ring focus-within:ring-2 focus-within:ring-ring">
45
+ {props.children}
46
+ </div>
47
+ );
48
+ }
49
+
50
+ export default meta;
51
+ type Story = StoryObj<typeof meta>;
52
+
53
+ const IMPORT = `import { Textarea } from '@kitn.ai/ui';`;
54
+ const src = (code: string) => ({
55
+ parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
56
+ });
57
+
58
+ /** Type several lines — the field grows until `maxHeight`, then scrolls. Focus the field: the ring is on the frame, not a nested box. */
59
+ export const Playground: Story = {
60
+ ...src(`{/* The frame owns the focus ring; the textarea's own ring is neutralized. */}
61
+ <div class="cursor-text rounded-xl border bg-muted/40 p-3 focus-within:ring-2 focus-within:ring-ring">
62
+ <Textarea placeholder="Ask anything…" maxHeight={200} class="focus-visible:ring-0" />
63
+ </div>`),
64
+ };
65
+
66
+ /** A fixed-height field (auto-resize disabled). */
67
+ export const FixedHeight: Story = {
68
+ render: () => (
69
+ <Frame>
70
+ <Textarea autoResize={false} rows={4} placeholder="Fixed at 4 rows…" class="focus-visible:ring-0" />
71
+ </Frame>
72
+ ),
73
+ ...src(`<div class="cursor-text rounded-xl border bg-muted/40 p-3 focus-within:ring-2 focus-within:ring-ring">
74
+ <Textarea autoResize={false} rows={4} placeholder="Fixed at 4 rows…" class="focus-visible:ring-0" />
75
+ </div>`),
76
+ };
@@ -0,0 +1,21 @@
1
+ import { type JSX, splitProps } from 'solid-js';
2
+ import { cn } from '../utils/cn';
3
+ import { useAutoResize } from '../primitives/use-auto-resize';
4
+
5
+ export interface TextareaProps extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {
6
+ maxHeight?: number;
7
+ autoResize?: boolean;
8
+ }
9
+
10
+ export function Textarea(props: TextareaProps) {
11
+ const [local, rest] = splitProps(props, ['class', 'maxHeight', 'autoResize']);
12
+ const { ref } = useAutoResize({ maxHeight: local.maxHeight });
13
+ return (
14
+ <textarea
15
+ ref={local.autoResize !== false ? ref : undefined}
16
+ class={cn('w-full resize-none rounded-md bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring', local.class)}
17
+ rows={1}
18
+ {...rest}
19
+ />
20
+ );
21
+ }
@@ -0,0 +1,74 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { Tooltip } from './tooltip';
3
+ import { Button } from './button';
4
+ import { componentDescription } from '../stories/docs/element-controls';
5
+
6
+ const meta = {
7
+ title: 'Solid (Advanced)/Primitives/Tooltip',
8
+ component: Tooltip,
9
+ tags: ['autodocs'],
10
+ parameters: {
11
+ layout: 'padded',
12
+ docs: {
13
+ description: componentDescription([
14
+ 'A small floating label that appears on hover/focus of its trigger element, built with a DIY overlay-core implementation (no third-party dependency, no arrow).',
15
+ '**When to use:** to clarify the purpose of icon-only buttons or terse controls — short, supplementary hints that are not essential to complete the action.',
16
+ '**How to use:** wrap a single interactive `children` element and set `content` to the hint text. The child becomes the trigger.',
17
+ '**Placement:** toolbars, message action rows, and any compact icon control where a label would not otherwise fit.',
18
+ ]),
19
+ controls: { exclude: ['use:eventListener'] },
20
+ },
21
+ },
22
+ argTypes: {
23
+ content: {
24
+ control: 'text',
25
+ description: 'Text shown inside the tooltip bubble.',
26
+ },
27
+ children: {
28
+ control: false,
29
+ description: 'The trigger element the tooltip is attached to.',
30
+ },
31
+ class: {
32
+ control: 'text',
33
+ description: 'Extra classes applied to the tooltip content bubble.',
34
+ },
35
+ },
36
+ args: {
37
+ content: 'This is a tooltip',
38
+ children: <Button variant="outline">Hover me</Button>,
39
+ },
40
+ render: (args) => <Tooltip {...args} />,
41
+ } satisfies Meta<typeof Tooltip>;
42
+
43
+ export default meta;
44
+ type Story = StoryObj<typeof meta>;
45
+
46
+ const IMPORT = `import { Tooltip } from '@kitn.ai/ui';`;
47
+ const src = (code: string) => ({
48
+ parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
49
+ });
50
+
51
+ /** Interactive playground — set the tooltip text and hover the trigger. */
52
+ export const Playground: Story = {
53
+ ...src(`<Tooltip content="This is a tooltip">
54
+ <Button variant="outline">Hover me</Button>
55
+ </Tooltip>`),
56
+ };
57
+
58
+ export const OnIconButton: Story = {
59
+ render: () => (
60
+ <Tooltip content="Add new item">
61
+ <Button variant="ghost" size="icon-sm" aria-label="Add new item">
62
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
63
+ <line x1="12" y1="5" x2="12" y2="19" />
64
+ <line x1="5" y1="12" x2="19" y2="12" />
65
+ </svg>
66
+ </Button>
67
+ </Tooltip>
68
+ ),
69
+ ...src(`<Tooltip content="Add new item">
70
+ <Button variant="ghost" size="icon-sm">
71
+ <PlusIcon />
72
+ </Button>
73
+ </Tooltip>`),
74
+ };
@@ -0,0 +1,68 @@
1
+ import { createSignal, createUniqueId, onCleanup, Show, type JSX, splitProps } from 'solid-js';
2
+ import { Portal } from 'solid-js/web';
3
+ import { cn } from '../utils/cn';
4
+ import { useChatConfig } from '../primitives/chat-config';
5
+ import { createPresence, usePosition, useDismiss, As } from './overlay';
6
+
7
+ export interface TooltipProps { content: string; children: JSX.Element; class?: string; openDelay?: number; }
8
+
9
+ export function Tooltip(props: TooltipProps) {
10
+ const [local] = splitProps(props, ['content', 'children', 'class', 'openDelay']);
11
+ const config = useChatConfig();
12
+ const id = createUniqueId();
13
+ const [open, setOpen] = createSignal(false);
14
+ const [triggerEl, setTriggerEl] = createSignal<HTMLElement>();
15
+ const [contentEl, setContentEl] = createSignal<HTMLElement>();
16
+ let timer: number | undefined;
17
+
18
+ const [pointerInside, setPointerInside] = createSignal(false);
19
+ const [focusInside, setFocusInside] = createSignal(false);
20
+
21
+ const show = (delay = 0) => {
22
+ clearTimeout(timer);
23
+ if (delay) timer = window.setTimeout(() => setOpen(true), delay);
24
+ else setOpen(true);
25
+ };
26
+ const hide = () => { clearTimeout(timer); setOpen(false); };
27
+ const maybeHide = () => { if (!pointerInside() && !focusInside()) hide(); };
28
+ onCleanup(() => clearTimeout(timer));
29
+
30
+ const presence = createPresence(open);
31
+ const position = usePosition(triggerEl, contentEl, { placement: 'top', gutter: 6 });
32
+ useDismiss({ enabled: open, onDismiss: hide, refs: () => [triggerEl(), contentEl()] });
33
+
34
+ return (
35
+ <>
36
+ <As
37
+ as="span"
38
+ ref={setTriggerEl}
39
+ aria-describedby={open() ? id : undefined}
40
+ onPointerEnter={() => { setPointerInside(true); show(local.openDelay ?? 600); }}
41
+ onPointerLeave={() => { setPointerInside(false); maybeHide(); }}
42
+ onFocusIn={() => { setFocusInside(true); show(); }}
43
+ onFocusOut={(e: FocusEvent) => { const t = triggerEl(); if (t && t.contains(e.relatedTarget as Node)) return; setFocusInside(false); maybeHide(); }}
44
+ >
45
+ {local.children}
46
+ </As>
47
+ <Show when={presence.present()}>
48
+ <Portal mount={config.portalMount()}>
49
+ <div
50
+ ref={(el) => { setContentEl(el); presence.setRef(el); }}
51
+ id={id}
52
+ role="tooltip"
53
+ data-expanded={presence.state() === 'open' ? '' : undefined}
54
+ data-closed={presence.state() === 'closed' ? '' : undefined}
55
+ style={{ position: 'fixed', left: `${position.pos().x}px`, top: `${position.pos().y}px`, 'pointer-events': 'none', visibility: position.hidden() ? 'hidden' : 'visible' }}
56
+ class={cn(
57
+ 'z-50 rounded-md bg-foreground px-2.5 py-1 text-xs text-background shadow-md',
58
+ 'animate-in fade-in-0 zoom-in-95 data-[closed]:animate-out data-[closed]:fade-out-0 data-[closed]:zoom-out-95',
59
+ local.class,
60
+ )}
61
+ >
62
+ {local.content}
63
+ </div>
64
+ </Portal>
65
+ </Show>
66
+ </>
67
+ );
68
+ }
@@ -0,0 +1,24 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { extendTailwindMerge } from 'tailwind-merge';
3
+
4
+ // The kit defines custom font-size utilities via @theme tokens in theme.css:
5
+ // text-caption / text-meta / text-body / text-title. tailwind-merge has no way
6
+ // to know these are font sizes, so by default it buckets e.g. `text-body` with
7
+ // text COLORS and drops a real color (`text-transparent`, `text-foreground`, …)
8
+ // whenever both appear in the same cn() call — which silently broke TextShimmer
9
+ // inside the web components (the element adds `text-body`, dropping
10
+ // `text-transparent`, so the gradient stayed hidden behind opaque text).
11
+ //
12
+ // Register them in the `font-size` group so they conflict only with other font
13
+ // sizes (text-xs/sm/base/lg/…) and never with text colors.
14
+ const twMerge = extendTailwindMerge({
15
+ extend: {
16
+ classGroups: {
17
+ 'font-size': [{ text: ['caption', 'meta', 'body', 'title'] }],
18
+ },
19
+ },
20
+ });
21
+
22
+ export function cn(...inputs: ClassValue[]) {
23
+ return twMerge(clsx(inputs));
24
+ }
package/theme.css ADDED
@@ -0,0 +1,187 @@
1
+ /* @kitn.ai/ui design system — import after `@import "tailwindcss"`.
2
+ Defines ONLY namespaced (--color-*) tokens, component keyframes, animation
3
+ utilities, and the .chat-markdown styles. Contains NO global base/reset rules,
4
+ so importing it restyles nothing on its own — a host opts in via the kit's
5
+ components + classes. Rebrand by overriding the --color-* values (optionally
6
+ scoped to a subtree). */
7
+ @import "tw-animate-css";
8
+
9
+ @custom-variant dark (&:is(.dark *));
10
+
11
+ @theme {
12
+ --color-background: var(--kai-color-background, hsl(0 0% 100%));
13
+ --color-foreground: var(--kai-color-foreground, hsl(240 10% 3.9%));
14
+ --color-card: var(--kai-color-card, hsl(0 0% 100%));
15
+ --color-card-foreground: var(--kai-color-card-foreground, hsl(240 10% 3.9%));
16
+ --color-popover: var(--kai-color-popover, hsl(0 0% 100%));
17
+ --color-popover-foreground: var(--kai-color-popover-foreground, hsl(240 10% 3.9%));
18
+ --color-primary: var(--kai-color-primary, hsl(240 5.9% 10%));
19
+ --color-primary-foreground: var(--kai-color-primary-foreground, hsl(0 0% 98%));
20
+ --color-secondary: var(--kai-color-secondary, hsl(240 4.8% 95.9%));
21
+ --color-secondary-foreground: var(--kai-color-secondary-foreground, hsl(240 5.9% 10%));
22
+ --color-muted: var(--kai-color-muted, hsl(240 4.8% 95.9%));
23
+ --color-muted-foreground: var(--kai-color-muted-foreground, hsl(240 3.8% 43%));
24
+ --color-accent: var(--kai-color-accent, hsl(240 4.8% 95.9%));
25
+ --color-accent-foreground: var(--kai-color-accent-foreground, hsl(240 5.9% 10%));
26
+ --color-destructive: var(--kai-color-destructive, hsl(0 72% 45%));
27
+ --color-destructive-foreground: var(--kai-color-destructive-foreground, hsl(0 0% 98%));
28
+ --color-border: var(--kai-color-border, hsl(240 5.9% 90%));
29
+ --color-input: var(--kai-color-input, hsl(240 5.9% 90%));
30
+ /* Focus ring — a deliberate blue (not a neutral) so keyboard focus is always
31
+ obvious and on-brand in both modes. Light uses a strong blue; dark uses a
32
+ brighter one for contrast on dark surfaces. Both clear WCAG 2.1 non-text
33
+ contrast (≥3:1) against the kit's backgrounds. Override via --kai-color-ring. */
34
+ --color-ring: var(--kai-color-ring, hsl(217 91% 53%));
35
+ /* Custom scrollbars — thin, rounded, subtle; thumb strengthens on hover. A
36
+ consistent cross-platform look (the kit styles its OWN scroll regions inside
37
+ the shadow root; the track stays transparent so nothing is forced where no
38
+ scrollbar is needed). Override via --kai-color-scrollbar-thumb(-hover). */
39
+ --color-scrollbar-thumb: var(--kai-color-scrollbar-thumb, hsl(240 5% 80%));
40
+ --color-scrollbar-thumb-hover: var(--kai-color-scrollbar-thumb-hover, hsl(240 4% 64%));
41
+ --color-sidebar: var(--kai-color-sidebar, hsl(0 0% 100%));
42
+
43
+ /* Inline `code` accent. Blue text on a translucent blue chip. */
44
+ --color-code-foreground: var(--kai-color-code-foreground, hsl(224.3 76.3% 48%));
45
+
46
+ /* Tool/status chip hues. Each chip is hue text over a 15% translucent fill of
47
+ the SAME hue (set in tool.tsx). The bare hue is too light to reach WCAG AA
48
+ (4.5:1) on the faint fill in LIGHT mode, so the light values are darkened;
49
+ dark mode keeps brighter hues for AA on the dark fill. Override via
50
+ --kai-color-tool-* to retheme. */
51
+ --color-tool-blue: var(--kai-color-tool-blue, hsl(217 91% 38%));
52
+ --color-tool-amber: var(--kai-color-tool-amber, hsl(38 92% 28%));
53
+ --color-tool-green: var(--kai-color-tool-green, hsl(142 71% 26%));
54
+ --color-tool-red: var(--kai-color-tool-red, hsl(0 72% 42%));
55
+
56
+ --radius: var(--kai-radius, 0.6rem);
57
+ --radius-sm: calc(var(--radius) - 4px);
58
+ --radius-md: calc(var(--radius) - 2px);
59
+ --radius-lg: var(--radius);
60
+ --radius-xl: calc(var(--radius) + 4px);
61
+
62
+ /* Typography scale — semantic sizes shared by all components. Each generates a
63
+ Tailwind utility (text-caption / text-meta / text-body / text-title). To
64
+ restyle the kit's typography, override the namespaced --kai-text-* token on
65
+ :root (e.g. `--kai-text-body: 0.9375rem`) — it pierces the Shadow DOM via the
66
+ var() fallback, exactly like the --kai-color-* tokens. The bare --text-*
67
+ names stay internal so a host's own --text-* can't collide. (Message/markdown/
68
+ input reading size also scales with the `proseSize` prop; these tokens cover
69
+ the fixed chrome & controls.) */
70
+ --text-caption: var(--kai-text-caption, 0.6875rem); --text-caption--line-height: 1rem; /* 11px — micro labels, badges, sub-counts */
71
+ --text-meta: var(--kai-text-meta, 0.75rem); --text-meta--line-height: 1.1rem; /* 12px — controls, toggles, switchers, captions */
72
+ --text-body: var(--kai-text-body, 0.875rem); --text-body--line-height: 1.45rem; /* 14px — primary reading text */
73
+ --text-title: var(--kai-text-title, 1rem); --text-title--line-height: 1.5rem; /* 16px — emphasis / headers */
74
+
75
+ @keyframes typing { 0%,100% { transform: translateY(0); opacity: .5 } 50% { transform: translateY(-2px); opacity: 1 } }
76
+ @keyframes loading-dots { 0%,100% { opacity: 0 } 50% { opacity: 1 } }
77
+ @keyframes wave { 0%,100% { transform: scaleY(1) } 50% { transform: scaleY(.6) } }
78
+ @keyframes blink { 0%,100% { opacity: 1 } 50% { opacity: 0 } }
79
+ @keyframes text-blink { 0%,100% { color: var(--color-primary) } 50% { color: var(--color-muted-foreground) } }
80
+ @keyframes bounce-dots { 0%,100% { transform: scale(.8); opacity: .5 } 50% { transform: scale(1.2); opacity: 1 } }
81
+ @keyframes thin-pulse { 0%,100% { transform: scale(.95); opacity: .8 } 50% { transform: scale(1.05); opacity: .4 } }
82
+ @keyframes pulse-dot { 0%,100% { transform: scale(1); opacity: .8 } 50% { transform: scale(1.5); opacity: 1 } }
83
+ @keyframes shimmer-text { 0% { background-position: 150% center } 100% { background-position: -150% center } }
84
+ @keyframes wave-bars { 0%,100% { transform: scaleY(1); opacity: .5 } 50% { transform: scaleY(.6); opacity: 1 } }
85
+ @keyframes shimmer { 0% { background-position: 200% 50% } 100% { background-position: -200% 50% } }
86
+ @keyframes spinner-fade { 0% { opacity: 0 } 100% { opacity: 1 } }
87
+ @keyframes collapsible-down { from { height: 0; opacity: 0 } to { height: var(--kb-collapsible-content-height); opacity: 1 } }
88
+ @keyframes collapsible-up { from { height: var(--kb-collapsible-content-height); opacity: 1 } to { height: 0; opacity: 0 } }
89
+ }
90
+
91
+ .dark {
92
+ --color-background: var(--kai-color-background, hsl(50 2% 9%));
93
+ --color-foreground: var(--kai-color-foreground, hsl(0 0% 98%));
94
+ --color-card: var(--kai-color-card, hsl(45 4% 12%));
95
+ --color-card-foreground: var(--kai-color-card-foreground, hsl(0 0% 98%));
96
+ --color-popover: var(--kai-color-popover, hsl(45 4% 12%));
97
+ --color-popover-foreground: var(--kai-color-popover-foreground, hsl(0 0% 98%));
98
+ --color-primary: var(--kai-color-primary, hsl(0 0% 98%));
99
+ --color-primary-foreground: var(--kai-color-primary-foreground, hsl(45 4% 11%));
100
+ --color-secondary: var(--kai-color-secondary, hsl(45 4% 17%));
101
+ --color-secondary-foreground: var(--kai-color-secondary-foreground, hsl(0 0% 98%));
102
+ --color-muted: var(--kai-color-muted, hsl(45 4% 17%));
103
+ --color-muted-foreground: var(--kai-color-muted-foreground, hsl(45 4% 64%));
104
+ --color-accent: var(--kai-color-accent, hsl(45 4% 17%));
105
+ --color-accent-foreground: var(--kai-color-accent-foreground, hsl(0 0% 98%));
106
+ --color-destructive: var(--kai-color-destructive, hsl(0 62.8% 30.6%));
107
+ --color-destructive-foreground: var(--kai-color-destructive-foreground, hsl(0 0% 98%));
108
+ --color-border: var(--kai-color-border, hsl(45 4% 17%));
109
+ --color-input: var(--kai-color-input, hsl(45 4% 17%));
110
+ --color-ring: var(--kai-color-ring, hsl(217 91% 68%));
111
+ --color-scrollbar-thumb: var(--kai-color-scrollbar-thumb, hsl(45 3% 30%));
112
+ --color-scrollbar-thumb-hover: var(--kai-color-scrollbar-thumb-hover, hsl(45 3% 42%));
113
+ --color-sidebar: var(--kai-color-sidebar, hsl(50 2% 7%));
114
+ --color-code-foreground: var(--kai-color-code-foreground, hsl(213 94% 78%));
115
+
116
+ /* Tool/status chip hues — dark mode. Brighter hues reach AA on the dark fill. */
117
+ --color-tool-blue: var(--kai-color-tool-blue, hsl(217 91% 70%));
118
+ --color-tool-amber: var(--kai-color-tool-amber, hsl(38 92% 50%));
119
+ --color-tool-green: var(--kai-color-tool-green, hsl(142 71% 45%));
120
+ --color-tool-red: var(--kai-color-tool-red, hsl(0 84% 70%));
121
+ }
122
+
123
+ /* Self-contained markdown styling — replaces the typography plugin's `prose`.
124
+ Keyed on --color-* tokens so it themes with the rest of the kit. */
125
+ .chat-markdown { color: var(--color-foreground); line-height: 1.6; }
126
+ .chat-markdown > div:first-child > :first-child { margin-top: 0; }
127
+ .chat-markdown > div:last-child > :last-child { margin-bottom: 0; }
128
+ .chat-markdown p { margin: 0 0 0.75em; }
129
+ .chat-markdown p:last-child { margin-bottom: 0; }
130
+ .chat-markdown h1, .chat-markdown h2, .chat-markdown h3, .chat-markdown h4 {
131
+ margin: 1.2em 0 0.5em; font-weight: 600; line-height: 1.3; color: var(--color-foreground);
132
+ }
133
+ .chat-markdown h1 { font-size: 1.4em; } .chat-markdown h2 { font-size: 1.25em; }
134
+ .chat-markdown h3 { font-size: 1.1em; } .chat-markdown h4 { font-size: 1em; }
135
+ .chat-markdown ul, .chat-markdown ol { margin: 0 0 0.75em; padding-left: 1.4em; }
136
+ .chat-markdown li { margin: 0.2em 0; }
137
+ .chat-markdown ul { list-style: disc; } .chat-markdown ol { list-style: decimal; }
138
+ .chat-markdown a { color: var(--color-primary); text-decoration: underline; text-underline-offset: 2px; }
139
+ .chat-markdown strong { font-weight: 600; color: var(--color-foreground); }
140
+ .chat-markdown em { font-style: italic; }
141
+ .chat-markdown blockquote {
142
+ margin: 0 0 0.75em; padding-left: 0.9em; border-left: 2px solid var(--color-border);
143
+ color: var(--color-muted-foreground);
144
+ }
145
+ .chat-markdown hr { margin: 1em 0; border: 0; border-top: 1px solid var(--color-border); }
146
+ .chat-markdown code:not(pre code) {
147
+ background: color-mix(in oklab, var(--color-code-foreground) 15%, transparent);
148
+ color: var(--color-code-foreground);
149
+ border-radius: 4px; padding: 0.1em 0.35em; font-size: 0.875em;
150
+ font-family: var(--kai-font-code, ui-monospace, "SF Mono", Menlo, monospace);
151
+ }
152
+ .chat-markdown table { width: 100%; border-collapse: collapse; margin: 0 0 0.75em; font-size: 0.9em; }
153
+ .chat-markdown th, .chat-markdown td { border: 1px solid var(--color-border); padding: 0.4em 0.6em; text-align: left; }
154
+ .chat-markdown th { font-weight: 600; background: color-mix(in oklab, var(--color-muted-foreground) 8%, transparent); }
155
+
156
+ /* Themeable elevation. The kit is mostly flat (borders), but a few surfaces lift
157
+ off the page — popovers, dropdowns, hover cards, cards. The shadow COLOR is a
158
+ token (--kai-shadow-color, default near-black); set it transparent for a fully
159
+ flat look, or tint it to match a theme. Defined here (not as a Tailwind shadow
160
+ utility) so the color stays overridable and works in both the web-component and
161
+ SolidJS layers. */
162
+ .kai-elevation {
163
+ box-shadow:
164
+ 0 4px 12px -2px color-mix(in oklab, var(--kai-shadow-color, oklch(0 0 0)) 14%, transparent),
165
+ 0 2px 6px -2px color-mix(in oklab, var(--kai-shadow-color, oklch(0 0 0)) 10%, transparent);
166
+ }
167
+ .kai-elevation-sm {
168
+ box-shadow: 0 1px 2px 0 color-mix(in oklab, var(--kai-shadow-color, oklch(0 0 0)) 8%, transparent);
169
+ }
170
+
171
+ /* Cross-platform thin scrollbar utility (Firefox + WebKit). Used by ScrollArea
172
+ and any scroll region in the SolidJS-component build (web components also get
173
+ this globally inside their shadow root — see src/elements/styles.css). */
174
+ .scrollbar-thin {
175
+ scrollbar-width: thin;
176
+ scrollbar-color: var(--color-scrollbar-thumb) transparent;
177
+ }
178
+ .scrollbar-thin::-webkit-scrollbar { width: 8px; height: 8px; }
179
+ .scrollbar-thin::-webkit-scrollbar-track { background: transparent; }
180
+ .scrollbar-thin::-webkit-scrollbar-thumb {
181
+ background-color: var(--color-scrollbar-thumb);
182
+ border-radius: 9999px;
183
+ border: 2px solid transparent;
184
+ background-clip: padding-box;
185
+ }
186
+ .scrollbar-thin::-webkit-scrollbar-thumb:hover { background-color: var(--color-scrollbar-thumb-hover); }
187
+ .scrollbar-thin::-webkit-scrollbar-corner { background: transparent; }