@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.
- package/LICENSE +21 -0
- package/README.md +394 -0
- package/dist/bash-InADTalH.js +1 -0
- package/dist/core-AYMC6_lb.js +12 -0
- package/dist/css-M7EaDHN_.js +1 -0
- package/dist/custom-elements.json +4739 -0
- package/dist/engine-javascript-vq0WuIJl.js +141 -0
- package/dist/github-dark-dimmed-DUshB20C.js +1 -0
- package/dist/github-light-JYsPkUQd.js +1 -0
- package/dist/host-embed.d.ts +33 -0
- package/dist/html-CPZ3oZQ7.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/javascript-C25yR2R2.js +1 -0
- package/dist/json-DxJze_jm.js +1 -0
- package/dist/kitn-chat-provider.d.ts +2 -0
- package/dist/kitn-chat-provider.es.js +240 -0
- package/dist/kitn-chat.es.js +77 -0
- package/dist/llms/llms-full.txt +1018 -0
- package/dist/llms/llms.txt +104 -0
- package/dist/origin.d.ts +5 -0
- package/dist/provider-runtime.d.ts +27 -0
- package/dist/schemas/card-envelope.schema.json +14 -0
- package/dist/schemas/card-event.schema.json +12 -0
- package/dist/schemas/choice.schema.json +70 -0
- package/dist/schemas/confirm.schema.json +65 -0
- package/dist/schemas/embed.schema.json +65 -0
- package/dist/schemas/form.result.schema.json +7 -0
- package/dist/schemas/form.schema.json +33 -0
- package/dist/schemas/link.schema.json +56 -0
- package/dist/schemas/tasks.result.schema.json +16 -0
- package/dist/schemas/tasks.schema.json +78 -0
- package/dist/svelte--5p79yCD.js +1 -0
- package/dist/theme.tokens.css +176 -0
- package/dist/tsx-B8rCNbgL.js +1 -0
- package/dist/typescript-RycA9KXf.js +1 -0
- package/dist/validate.d.ts +4 -0
- package/dist/version.d.ts +4 -0
- package/dist/vue-BmIZj4XD.js +1 -0
- package/dist/wire.d.ts +53 -0
- package/frameworks/react/index.tsx +948 -0
- package/frameworks/react/runtime.tsx +94 -0
- package/llms-full.txt +1018 -0
- package/llms.txt +104 -0
- package/package.json +141 -0
- package/src/components/artifact.stories.tsx +138 -0
- package/src/components/artifact.tsx +743 -0
- package/src/components/attachments.stories.tsx +303 -0
- package/src/components/attachments.test.tsx +64 -0
- package/src/components/attachments.tsx +399 -0
- package/src/components/card-fallback.tsx +28 -0
- package/src/components/card-renderer.tsx +52 -0
- package/src/components/card.tsx +110 -0
- package/src/components/chain-of-thought.stories.tsx +211 -0
- package/src/components/chain-of-thought.tsx +139 -0
- package/src/components/chat-container.stories.tsx +187 -0
- package/src/components/chat-container.tsx +82 -0
- package/src/components/chat-scope-picker.tsx +47 -0
- package/src/components/chat-thread.test.tsx +58 -0
- package/src/components/chat-thread.tsx +249 -0
- package/src/components/checkpoint.stories.tsx +102 -0
- package/src/components/checkpoint.tsx +88 -0
- package/src/components/choice-card.tsx +569 -0
- package/src/components/code-block.stories.tsx +150 -0
- package/src/components/code-block.tsx +105 -0
- package/src/components/component-meta.json +3735 -0
- package/src/components/confirm-card.stories.tsx +74 -0
- package/src/components/confirm-card.tsx +315 -0
- package/src/components/context.stories.tsx +229 -0
- package/src/components/context.test.tsx +133 -0
- package/src/components/context.tsx +363 -0
- package/src/components/conversation-item.stories.tsx +125 -0
- package/src/components/conversation-item.tsx +16 -0
- package/src/components/conversation-list.stories.tsx +133 -0
- package/src/components/conversation-list.tsx +101 -0
- package/src/components/default-input.test.tsx +111 -0
- package/src/components/embed.tsx +196 -0
- package/src/components/empty.stories.tsx +434 -0
- package/src/components/empty.tsx +166 -0
- package/src/components/feedback-bar.stories.tsx +138 -0
- package/src/components/feedback-bar.test.tsx +103 -0
- package/src/components/feedback-bar.tsx +166 -0
- package/src/components/file-tree.stories.tsx +73 -0
- package/src/components/file-tree.tsx +383 -0
- package/src/components/file-upload.stories.tsx +156 -0
- package/src/components/file-upload.tsx +161 -0
- package/src/components/form-widgets.tsx +461 -0
- package/src/components/form.tsx +873 -0
- package/src/components/image.stories.tsx +89 -0
- package/src/components/image.tsx +67 -0
- package/src/components/link-preview.tsx +194 -0
- package/src/components/loader.stories.tsx +181 -0
- package/src/components/loader.tsx +333 -0
- package/src/components/markdown.stories.tsx +180 -0
- package/src/components/markdown.tsx +81 -0
- package/src/components/message-action-bar.test.tsx +128 -0
- package/src/components/message-narrow.stories.tsx +329 -0
- package/src/components/message-skills.stories.tsx +211 -0
- package/src/components/message-skills.tsx +36 -0
- package/src/components/message.stories.tsx +281 -0
- package/src/components/message.tsx +227 -0
- package/src/components/model-switcher.stories.tsx +123 -0
- package/src/components/model-switcher.test.tsx +65 -0
- package/src/components/model-switcher.tsx +87 -0
- package/src/components/prompt-input.stories.tsx +222 -0
- package/src/components/prompt-input.tsx +211 -0
- package/src/components/prompt-suggestion.stories.tsx +142 -0
- package/src/components/prompt-suggestion.tsx +115 -0
- package/src/components/reasoning.stories.tsx +140 -0
- package/src/components/reasoning.tsx +156 -0
- package/src/components/resizable.test.tsx +226 -0
- package/src/components/response-stream.tsx +103 -0
- package/src/components/scroll-button.stories.tsx +100 -0
- package/src/components/scroll-button.tsx +34 -0
- package/src/components/slash-command.stories.tsx +163 -0
- package/src/components/slash-command.tsx +232 -0
- package/src/components/source-list.test.tsx +152 -0
- package/src/components/source.stories.tsx +124 -0
- package/src/components/source.tsx +129 -0
- package/src/components/tasks-card.stories.tsx +78 -0
- package/src/components/tasks-card.tsx +432 -0
- package/src/components/text-shimmer.stories.tsx +87 -0
- package/src/components/text-shimmer.tsx +37 -0
- package/src/components/thinking-bar.stories.tsx +87 -0
- package/src/components/thinking-bar.tsx +50 -0
- package/src/components/tool.stories.tsx +153 -0
- package/src/components/tool.tsx +184 -0
- package/src/components/use-card-resolution.ts +28 -0
- package/src/components/voice-input.stories.tsx +83 -0
- package/src/components/voice-input.tsx +107 -0
- package/src/elements/artifact.stories.tsx +506 -0
- package/src/elements/artifact.tsx +137 -0
- package/src/elements/attachments.stories.tsx +190 -0
- package/src/elements/attachments.tsx +136 -0
- package/src/elements/card.stories.tsx +113 -0
- package/src/elements/card.tsx +40 -0
- package/src/elements/cards.stories.tsx +59 -0
- package/src/elements/cards.tsx +97 -0
- package/src/elements/catalog.stories.tsx +491 -0
- package/src/elements/chain-of-thought.declarative.test.tsx +153 -0
- package/src/elements/chain-of-thought.stories.tsx +113 -0
- package/src/elements/chain-of-thought.tsx +92 -0
- package/src/elements/chat-scope-picker.stories.tsx +73 -0
- package/src/elements/chat-scope-picker.tsx +36 -0
- package/src/elements/chat-types.ts +42 -0
- package/src/elements/chat-workspace.stories.tsx +288 -0
- package/src/elements/chat-workspace.tsx +161 -0
- package/src/elements/chat.stories.tsx +151 -0
- package/src/elements/chat.tsx +80 -0
- package/src/elements/checkpoint.stories.tsx +72 -0
- package/src/elements/checkpoint.tsx +47 -0
- package/src/elements/choice.stories.tsx +240 -0
- package/src/elements/choice.tsx +48 -0
- package/src/elements/code-block.stories.tsx +83 -0
- package/src/elements/code-block.tsx +42 -0
- package/src/elements/compiled.css +2 -0
- package/src/elements/composed-shell.stories.tsx +316 -0
- package/src/elements/confirm-card.stories.tsx +227 -0
- package/src/elements/confirm-card.tsx +51 -0
- package/src/elements/context-meter.stories.tsx +151 -0
- package/src/elements/context-meter.tsx +121 -0
- package/src/elements/conversation-list.declarative.test.tsx +137 -0
- package/src/elements/conversation-list.stories.tsx +243 -0
- package/src/elements/conversation-list.tsx +80 -0
- package/src/elements/css.ts +5 -0
- package/src/elements/default-input.tsx +247 -0
- package/src/elements/define.tsx +171 -0
- package/src/elements/element-meta.json +3314 -0
- package/src/elements/element-types.d.ts +710 -0
- package/src/elements/embed.stories.tsx +213 -0
- package/src/elements/embed.tsx +35 -0
- package/src/elements/empty.stories.tsx +111 -0
- package/src/elements/empty.tsx +29 -0
- package/src/elements/feedback-bar.stories.tsx +114 -0
- package/src/elements/feedback-bar.tsx +61 -0
- package/src/elements/file-tree.stories.tsx +134 -0
- package/src/elements/file-tree.tsx +52 -0
- package/src/elements/file-upload.stories.tsx +82 -0
- package/src/elements/file-upload.tsx +44 -0
- package/src/elements/form.stories.tsx +248 -0
- package/src/elements/form.tsx +43 -0
- package/src/elements/framework-usage.json +563 -0
- package/src/elements/image.stories.tsx +71 -0
- package/src/elements/image.tsx +32 -0
- package/src/elements/link-preview.stories.tsx +209 -0
- package/src/elements/link-preview.tsx +34 -0
- package/src/elements/loader.stories.tsx +88 -0
- package/src/elements/loader.tsx +25 -0
- package/src/elements/markdown.stories.tsx +76 -0
- package/src/elements/markdown.tsx +38 -0
- package/src/elements/message-skills.declarative.test.tsx +104 -0
- package/src/elements/message-skills.stories.tsx +114 -0
- package/src/elements/message-skills.tsx +85 -0
- package/src/elements/message.stories.tsx +218 -0
- package/src/elements/message.tsx +162 -0
- package/src/elements/model-switcher.declarative.test.tsx +130 -0
- package/src/elements/model-switcher.stories.tsx +198 -0
- package/src/elements/model-switcher.tsx +93 -0
- package/src/elements/popover.stories.tsx +80 -0
- package/src/elements/popover.tsx +59 -0
- package/src/elements/prompt-input-slash-command.test.tsx +128 -0
- package/src/elements/prompt-input-stoppable.test.tsx +88 -0
- package/src/elements/prompt-input-toolbar-actions.test.tsx +120 -0
- package/src/elements/prompt-input.stories.tsx +331 -0
- package/src/elements/prompt-input.tsx +185 -0
- package/src/elements/prompt-suggestions.declarative.test.tsx +164 -0
- package/src/elements/prompt-suggestions.stories.tsx +213 -0
- package/src/elements/prompt-suggestions.tsx +94 -0
- package/src/elements/reasoning.stories.tsx +77 -0
- package/src/elements/reasoning.tsx +50 -0
- package/src/elements/register.ts +56 -0
- package/src/elements/remote.stories.tsx +314 -0
- package/src/elements/remote.tsx +221 -0
- package/src/elements/resizable.d.ts +27 -0
- package/src/elements/resizable.stories.tsx +426 -0
- package/src/elements/resizable.tsx +553 -0
- package/src/elements/response-stream.stories.tsx +79 -0
- package/src/elements/response-stream.tsx +40 -0
- package/src/elements/scroll-button.stories.tsx +202 -0
- package/src/elements/scroll-button.test.tsx +148 -0
- package/src/elements/scroll-button.tsx +126 -0
- package/src/elements/source-list.stories.tsx +143 -0
- package/src/elements/source.stories.tsx +88 -0
- package/src/elements/source.tsx +120 -0
- package/src/elements/styles.css +199 -0
- package/src/elements/switch.stories.tsx +56 -0
- package/src/elements/switch.tsx +43 -0
- package/src/elements/tasks.stories.tsx +237 -0
- package/src/elements/tasks.tsx +46 -0
- package/src/elements/text-shimmer.stories.tsx +64 -0
- package/src/elements/text-shimmer.tsx +28 -0
- package/src/elements/thinking-bar.stories.tsx +72 -0
- package/src/elements/thinking-bar.tsx +34 -0
- package/src/elements/tool.stories.tsx +89 -0
- package/src/elements/tool.tsx +23 -0
- package/src/elements/voice-input.stories.tsx +88 -0
- package/src/elements/voice-input.tsx +41 -0
- package/src/index.ts +253 -0
- package/src/primitives/card-contract.ts +71 -0
- package/src/primitives/card-host.tsx +35 -0
- package/src/primitives/card-registry.tsx +67 -0
- package/src/primitives/card-resolution.ts +36 -0
- package/src/primitives/card-routing.ts +79 -0
- package/src/primitives/card-schemas/card-envelope.schema.json +14 -0
- package/src/primitives/card-schemas/card-event.schema.json +12 -0
- package/src/primitives/card-schemas/choice.schema.json +70 -0
- package/src/primitives/card-schemas/confirm.schema.json +65 -0
- package/src/primitives/card-schemas/embed.schema.json +65 -0
- package/src/primitives/card-schemas/form.result.schema.json +7 -0
- package/src/primitives/card-schemas/form.schema.json +33 -0
- package/src/primitives/card-schemas/link.schema.json +56 -0
- package/src/primitives/card-schemas/tasks.result.schema.json +16 -0
- package/src/primitives/card-schemas/tasks.schema.json +78 -0
- package/src/primitives/card-validate.ts +95 -0
- package/src/primitives/chat-config.tsx +76 -0
- package/src/primitives/controllable.test.ts +47 -0
- package/src/primitives/controllable.ts +21 -0
- package/src/primitives/embed-providers.ts +254 -0
- package/src/primitives/highlighter.ts +157 -0
- package/src/primitives/link-preview.ts +87 -0
- package/src/primitives/pdf-preview.ts +121 -0
- package/src/primitives/use-auto-resize.ts +31 -0
- package/src/primitives/use-resize-observer.ts +12 -0
- package/src/primitives/use-stick-to-bottom.ts +43 -0
- package/src/primitives/use-text-stream.ts +112 -0
- package/src/primitives/use-voice-recorder.ts +50 -0
- package/src/remote/host-embed.ts +345 -0
- package/src/remote/index.ts +2 -0
- package/src/remote/origin.ts +30 -0
- package/src/remote/provider-runtime.ts +259 -0
- package/src/remote/provider.ts +2 -0
- package/src/remote/validate.ts +22 -0
- package/src/remote/version.ts +12 -0
- package/src/remote/wire.ts +48 -0
- package/src/stories/chat-panel-layout.stories.tsx +145 -0
- package/src/stories/chat-scene.tsx +571 -0
- package/src/stories/checkpoint-restore.stories.tsx +256 -0
- package/src/stories/context-usage.stories.tsx +212 -0
- package/src/stories/conversation-with-reasoning.stories.tsx +182 -0
- package/src/stories/conversation-with-sources.stories.tsx +471 -0
- package/src/stories/docs/Accessibility.mdx +119 -0
- package/src/stories/docs/ChildElements.mdx +269 -0
- package/src/stories/docs/ForAIAgents.mdx +162 -0
- package/src/stories/docs/GettingStarted.mdx +65 -0
- package/src/stories/docs/Installation.mdx +83 -0
- package/src/stories/docs/Introduction.mdx +50 -0
- package/src/stories/docs/SolidJsAdvanced.mdx +17 -0
- package/src/stories/docs/Theming.mdx +85 -0
- package/src/stories/docs/element-controls.ts +121 -0
- package/src/stories/docs/frameworks/Angular.mdx +245 -0
- package/src/stories/docs/frameworks/Html.mdx +227 -0
- package/src/stories/docs/frameworks/Overview.mdx +47 -0
- package/src/stories/docs/frameworks/React.mdx +227 -0
- package/src/stories/docs/frameworks/Solid.mdx +173 -0
- package/src/stories/docs/frameworks/Svelte.mdx +194 -0
- package/src/stories/docs/frameworks/Vue.mdx +238 -0
- package/src/stories/docs/generative-ui-overview.mdx +186 -0
- package/src/stories/docs/recipes/SpeechToText.mdx +7 -0
- package/src/stories/docs/recipes/Streaming.mdx +68 -0
- package/src/stories/docs/recipes/TextToSpeech.mdx +35 -0
- package/src/stories/docs/theme-editor/canvas.tsx +32 -0
- package/src/stories/docs/theme-editor/inspector.tsx +66 -0
- package/src/stories/docs/theme-editor/presets.test.ts +32 -0
- package/src/stories/docs/theme-editor/presets.ts +64 -0
- package/src/stories/docs/theme-editor/theme-css.test.ts +19 -0
- package/src/stories/docs/theme-editor/theme-css.ts +15 -0
- package/src/stories/docs/theme-editor/theme-editor.tsx +146 -0
- package/src/stories/docs/theme-tokens.tsx +174 -0
- package/src/stories/examples/ChoosingComponents.mdx +106 -0
- package/src/stories/examples/sample-data.ts +79 -0
- package/src/stories/examples/usage/checkpoint-restore.ts +156 -0
- package/src/stories/examples/usage/context-usage.ts +591 -0
- package/src/stories/examples/usage/conversation-with-reasoning.ts +224 -0
- package/src/stories/examples/usage/conversation-with-sources.ts +589 -0
- package/src/stories/examples/usage/empty-state.ts +144 -0
- package/src/stories/examples/usage/full-chat-app.ts +277 -0
- package/src/stories/examples/usage/index.ts +63 -0
- package/src/stories/examples/usage/message-actions.ts +704 -0
- package/src/stories/examples/usage/pattern-centered-conversation.ts +446 -0
- package/src/stories/examples/usage/pattern-chat-panel-layout.ts +442 -0
- package/src/stories/examples/usage/pattern-docked-widget.ts +479 -0
- package/src/stories/examples/usage/prompt-input-variants.ts +1356 -0
- package/src/stories/examples/usage/streaming-response.ts +687 -0
- package/src/stories/examples/usage/types.ts +33 -0
- package/src/stories/full-chat.stories.tsx +18 -0
- package/src/stories/message-actions.stories.tsx +230 -0
- package/src/stories/pattern-centered-conversation.stories.tsx +93 -0
- package/src/stories/pattern-docked-widget.stories.tsx +93 -0
- package/src/stories/pattern-empty-state.stories.tsx +76 -0
- package/src/stories/prompt-input-variants.stories.tsx +456 -0
- package/src/stories/streaming-response.stories.tsx +438 -0
- package/src/stories/theme-editor.stories.tsx +16 -0
- package/src/stories/token-reference.stories.tsx +18 -0
- package/src/stories/typography.stories.tsx +78 -0
- package/src/types.ts +47 -0
- package/src/ui/action-icons.ts +52 -0
- package/src/ui/avatar.stories.tsx +103 -0
- package/src/ui/avatar.tsx +23 -0
- package/src/ui/badge.stories.tsx +86 -0
- package/src/ui/badge.tsx +21 -0
- package/src/ui/button.stories.tsx +145 -0
- package/src/ui/button.tsx +38 -0
- package/src/ui/collapsible.stories.tsx +69 -0
- package/src/ui/collapsible.tsx +125 -0
- package/src/ui/dropdown.stories.tsx +59 -0
- package/src/ui/dropdown.tsx +196 -0
- package/src/ui/hover-card.stories.tsx +77 -0
- package/src/ui/hover-card.test.tsx +30 -0
- package/src/ui/hover-card.tsx +172 -0
- package/src/ui/overlay.stories.tsx +115 -0
- package/src/ui/overlay.tsx +158 -0
- package/src/ui/popover.stories.tsx +81 -0
- package/src/ui/popover.test.tsx +99 -0
- package/src/ui/popover.tsx +92 -0
- package/src/ui/resizable.stories.tsx +236 -0
- package/src/ui/resizable.tsx +576 -0
- package/src/ui/scroll-area.stories.tsx +50 -0
- package/src/ui/scroll-area.tsx +15 -0
- package/src/ui/separator.stories.tsx +81 -0
- package/src/ui/separator.tsx +10 -0
- package/src/ui/skeleton.stories.tsx +337 -0
- package/src/ui/skeleton.tsx +16 -0
- package/src/ui/switch.stories.tsx +51 -0
- package/src/ui/switch.test.tsx +59 -0
- package/src/ui/switch.tsx +62 -0
- package/src/ui/textarea.stories.tsx +76 -0
- package/src/ui/textarea.tsx +21 -0
- package/src/ui/tooltip.stories.tsx +74 -0
- package/src/ui/tooltip.tsx +68 -0
- package/src/utils/cn.ts +24 -0
- package/theme.css +187 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Meta, Canvas } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as TokenRef from '../token-reference.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Theming/Overview" />
|
|
5
|
+
|
|
6
|
+
# Theming
|
|
7
|
+
|
|
8
|
+
The kit's appearance is driven entirely by **CSS custom properties** (`--color-*` design tokens) defined in `theme.css`. Override them to rebrand everything — buttons, bubbles, borders, the sidebar, code blocks — without touching component code.
|
|
9
|
+
|
|
10
|
+
## Overriding tokens
|
|
11
|
+
|
|
12
|
+
Because inherited CSS pierces the Shadow DOM boundary, setting tokens on `:root` re-themes both the SolidJS components and the web components:
|
|
13
|
+
|
|
14
|
+
```css
|
|
15
|
+
:root {
|
|
16
|
+
--color-background: #0f0f0f;
|
|
17
|
+
--color-foreground: #f5f5f5;
|
|
18
|
+
--color-primary: #7c3aed;
|
|
19
|
+
--color-primary-foreground: #ffffff;
|
|
20
|
+
--color-muted: #1e1e1e;
|
|
21
|
+
--color-border: #2a2a2a;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Scope the same variables to a parent element instead of `:root` to theme just one subtree. The tokens follow the **shadcn/ui convention**: they're *semantic* (`--color-primary`, not `--color-blue`) and come in **`X` / `X-foreground` pairs** — a surface color and the text color that sits on it.
|
|
26
|
+
|
|
27
|
+
## Live editor
|
|
28
|
+
|
|
29
|
+
Edit every token for **light and dark** modes and watch a real chat UI re-skin live — pick a preset, tweak swatches, then **Copy CSS** for a paste-ready `:root` + `.dark` block.
|
|
30
|
+
|
|
31
|
+
<a
|
|
32
|
+
href="?path=/story/theming-editor--editor"
|
|
33
|
+
style={{
|
|
34
|
+
display: 'inline-block',
|
|
35
|
+
background: 'var(--color-primary, #18181b)',
|
|
36
|
+
borderRadius: 'var(--radius-md, 0.5rem)',
|
|
37
|
+
padding: '0.4rem 0.85rem',
|
|
38
|
+
textDecoration: 'none',
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
<span style={{
|
|
42
|
+
color: 'var(--color-primary-foreground, #fafafa)',
|
|
43
|
+
fontWeight: 600,
|
|
44
|
+
fontSize: '0.8125rem',
|
|
45
|
+
lineHeight: 1.3,
|
|
46
|
+
}}>Open the theme editor</span>
|
|
47
|
+
</a>
|
|
48
|
+
|
|
49
|
+
_(Opens full-screen — it needs more room than this docs column.)_
|
|
50
|
+
|
|
51
|
+
## Token reference
|
|
52
|
+
|
|
53
|
+
A complete, auto-generated reference of every token you can override — generated live from the loaded CSS, so it never drifts from `theme.css`:
|
|
54
|
+
|
|
55
|
+
<Canvas of={TokenRef.TokenReference} />
|
|
56
|
+
|
|
57
|
+
## Dark mode
|
|
58
|
+
|
|
59
|
+
The kit ships light and dark token sets. Toggle dark mode by adding the `dark` class to a root element (this Storybook's toolbar does exactly that):
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
document.documentElement.classList.toggle('dark');
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Appearance settings
|
|
66
|
+
|
|
67
|
+
`ChatConfig` (SolidJS) and matching properties on `<kai-chat>` control non-color appearance:
|
|
68
|
+
|
|
69
|
+
| Setting | Values | Purpose |
|
|
70
|
+
| --- | --- | --- |
|
|
71
|
+
| `proseSize` | `xs` · `sm` · `base` · `lg` | Text/markdown sizing |
|
|
72
|
+
| `codeTheme` | any Shiki theme name | Syntax-highlight theme for code blocks |
|
|
73
|
+
| `codeHighlight` | `true` / `false` | Turn code highlighting off entirely (renders plain text, loads no Shiki) |
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<ChatConfig proseSize="base" codeTheme="github-dark-dimmed">
|
|
77
|
+
{/* ... */}
|
|
78
|
+
</ChatConfig>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<kai-chat prose-size="base" code-theme="github-dark-dimmed"></kai-chat>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
That's the whole theming surface — tokens for color, `ChatConfig` for sizing and code. Everything else is encapsulated.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import meta from '../../elements/element-meta.json';
|
|
2
|
+
|
|
3
|
+
type Prop = { name: string; type: string; default?: string; scalar: boolean };
|
|
4
|
+
type ElementMeta = { tag: string; props: Prop[] };
|
|
5
|
+
const all = meta as unknown as ElementMeta[];
|
|
6
|
+
|
|
7
|
+
// A small, consistent pointer at the top of every Web Component's Docs tab,
|
|
8
|
+
// directing readers to the dedicated API tab (which holds the full generated
|
|
9
|
+
// spec). Keeps the Docs tab focused on examples while keeping the spec one click
|
|
10
|
+
// away — and discoverable, since everyone reads the description first.
|
|
11
|
+
const API_POINTER =
|
|
12
|
+
'> **Full API reference** — every property, event, default and token is on the **API** tab above.';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build a Storybook `docs.description` from an element's intro paragraphs, with
|
|
16
|
+
* the API-tab pointer prepended. The generated spec itself lives in the **API**
|
|
17
|
+
* tab (see `.storybook/api-tab.tsx`), not inline here.
|
|
18
|
+
* (`tag` is kept in the signature so callers don't churn if we re-inline later.)
|
|
19
|
+
*/
|
|
20
|
+
export function specDescription(_tag: string, paragraphs: string[]): { component: string } {
|
|
21
|
+
return { component: [API_POINTER, ...paragraphs].join('\n\n') };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The Components/UI sibling of API_POINTER — same idea, component vocabulary
|
|
25
|
+
// (props/callbacks/slots rather than properties/events/attributes).
|
|
26
|
+
const COMPONENT_POINTER =
|
|
27
|
+
'> **Full API reference** — every prop, callback, slot and token is on the **API** tab above.';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build a Storybook `docs.description` for a SolidJS/UI component story, with the
|
|
31
|
+
* API-tab pointer prepended. The generated spec (props/callbacks/slots/tokens)
|
|
32
|
+
* lives on the **API** tab (see `.storybook/api-tab.tsx`), from
|
|
33
|
+
* `src/components/component-meta.json`.
|
|
34
|
+
*/
|
|
35
|
+
export function componentDescription(paragraphs: string[]): { component: string } {
|
|
36
|
+
return { component: [COMPONENT_POINTER, ...paragraphs].join('\n\n') };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parse a type string from element-meta.json into meaningful parts.
|
|
41
|
+
*
|
|
42
|
+
* The meta generator writes types like:
|
|
43
|
+
* - `"undefined | false | true"` for booleans (NOT "boolean")
|
|
44
|
+
* - `"undefined | \"preview\" | \"code\""` for enums (double-quoted, with leading undefined)
|
|
45
|
+
*
|
|
46
|
+
* We normalise by splitting on `|`, trimming, and dropping `undefined` / `null`.
|
|
47
|
+
*/
|
|
48
|
+
const normaliseParts = (type: string): string[] =>
|
|
49
|
+
type
|
|
50
|
+
.split('|')
|
|
51
|
+
.map((s) => s.trim())
|
|
52
|
+
.filter((s) => s !== 'undefined' && s !== 'null');
|
|
53
|
+
|
|
54
|
+
const enumValues = (parts: string[]): string[] | null => {
|
|
55
|
+
// Accept both single-quoted ('foo') and double-quoted ("foo") string literals.
|
|
56
|
+
if (parts.length > 0 && parts.every((p) => /^(['"])[^'"]*\1$/.test(p)))
|
|
57
|
+
return parts.map((p) => p.slice(1, -1));
|
|
58
|
+
return null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const isBooleanParts = (parts: string[], rawType: string): boolean => {
|
|
62
|
+
// Explicit boolean keyword (future-proof)
|
|
63
|
+
if (/\bboolean\b/.test(rawType)) return true;
|
|
64
|
+
// element-meta encodes booleans as "undefined | false | true"
|
|
65
|
+
const sorted = [...parts].sort().join(',');
|
|
66
|
+
return sorted === 'false,true';
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Storybook argTypes for an element's props (theme select, booleans, text,
|
|
71
|
+
* number, object/JSON for complex types, and no control for functions).
|
|
72
|
+
*
|
|
73
|
+
* Classification order:
|
|
74
|
+
* 1. enum → select
|
|
75
|
+
* 2. boolean → boolean
|
|
76
|
+
* 3. number → number
|
|
77
|
+
* 4. contains `=>` (function / function-bearing object) → control: false
|
|
78
|
+
* 5. not all-string after normalisation (arrays, records, generics…) → object
|
|
79
|
+
* 6. all-string → text
|
|
80
|
+
*/
|
|
81
|
+
export function argTypesFor(tag: string): Record<string, unknown> {
|
|
82
|
+
const el = all.find((e) => e.tag === tag);
|
|
83
|
+
if (!el) return {};
|
|
84
|
+
const out: Record<string, unknown> = {};
|
|
85
|
+
for (const p of el.props) {
|
|
86
|
+
const parts = normaliseParts(p.type);
|
|
87
|
+
|
|
88
|
+
// 1–3: scalar classifications (enums, booleans, numbers only appear in scalar props)
|
|
89
|
+
if (p.scalar) {
|
|
90
|
+
const values = enumValues(parts);
|
|
91
|
+
if (values) {
|
|
92
|
+
out[p.name] = { control: { type: 'select' }, options: values };
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (isBooleanParts(parts, p.type)) {
|
|
96
|
+
out[p.name] = { control: 'boolean' };
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (/\bnumber\b/.test(p.type)) {
|
|
100
|
+
out[p.name] = { control: 'number' };
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 4. Function / function-bearing object: contains `=>` in raw type string
|
|
106
|
+
if (p.type.includes('=>')) {
|
|
107
|
+
out[p.name] = { control: false };
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 5. Complex/object: parts are not all exactly the string "string"
|
|
112
|
+
if (!parts.every((part) => part === 'string')) {
|
|
113
|
+
out[p.name] = { control: 'object' };
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 6. Pure string
|
|
118
|
+
out[p.name] = { control: 'text' };
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Docs/Frameworks/Angular" />
|
|
4
|
+
|
|
5
|
+
# Angular
|
|
6
|
+
|
|
7
|
+
Angular needs **no wrappers** — its property binding (`[prop]="value"`) assigns the DOM **property**
|
|
8
|
+
directly, so arrays and objects pass through unstringified. Register the elements once, allow the
|
|
9
|
+
custom tags with `CUSTOM_ELEMENTS_SCHEMA`, and handle events with `(event)="handler($event)"`.
|
|
10
|
+
|
|
11
|
+
There are **two ways to build with the kit**, and you can mix them:
|
|
12
|
+
|
|
13
|
+
1. **`<kai-chat>`** — the batteries-included shell: a whole chat experience in one tag. Fastest start.
|
|
14
|
+
2. **Compose the individual elements** (`<kai-conversations>`, `<kai-markdown>`, `<kai-artifact>`, …)
|
|
15
|
+
into your own layout when you want full control.
|
|
16
|
+
|
|
17
|
+
Both are shown below.
|
|
18
|
+
|
|
19
|
+
## Install & setup
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i @kitn.ai/ui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Register the custom elements once as a static side-effect import in `main.ts`, **before**
|
|
26
|
+
`bootstrapApplication(...)`. Then add `CUSTOM_ELEMENTS_SCHEMA` to each standalone component that
|
|
27
|
+
uses `<kai-*>` tags:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
// main.ts — import BEFORE bootstrapApplication so elements are defined first
|
|
31
|
+
import '@kitn.ai/ui/elements';
|
|
32
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
33
|
+
import { AppComponent } from './app/app.component';
|
|
34
|
+
import { appConfig } from './app/app.config';
|
|
35
|
+
|
|
36
|
+
bootstrapApplication(AppComponent, appConfig);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// app.component.ts
|
|
41
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
42
|
+
|
|
43
|
+
@Component({
|
|
44
|
+
selector: 'app-root',
|
|
45
|
+
standalone: true,
|
|
46
|
+
templateUrl: './app.component.html',
|
|
47
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA], // required: allows <kai-*> tags in the template
|
|
48
|
+
})
|
|
49
|
+
export class AppComponent { /* … */ }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **`@kitn.ai/ui/elements`** — the side-effect import that registers every `<kai-*>` element
|
|
53
|
+
globally. Import it once, in `main.ts`.
|
|
54
|
+
- **`CUSTOM_ELEMENTS_SCHEMA`** — tells the Angular compiler to accept unknown element names (the
|
|
55
|
+
`<kai-*>` tags). Add it to every standalone component whose template uses them.
|
|
56
|
+
- No CSS to import: each element is styled inside its own Shadow DOM. Only pull in
|
|
57
|
+
`@kitn.ai/ui/theme.css` if you want to override design tokens (see **Theming**).
|
|
58
|
+
|
|
59
|
+
## Quick start — the all-in-one shell
|
|
60
|
+
|
|
61
|
+
`<kai-chat>` is **transport-agnostic**: give it a `messages` array, handle the `submit` event, and
|
|
62
|
+
stream your model's reply back into state. You own the request; the component owns the UI.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// app.component.ts
|
|
66
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
67
|
+
|
|
68
|
+
type Message = { id: string; role: 'user' | 'assistant'; content: string };
|
|
69
|
+
|
|
70
|
+
@Component({
|
|
71
|
+
selector: 'app-root',
|
|
72
|
+
standalone: true,
|
|
73
|
+
templateUrl: './app.component.html',
|
|
74
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
75
|
+
})
|
|
76
|
+
export class AppComponent {
|
|
77
|
+
messages: Message[] = [
|
|
78
|
+
{ id: '1', role: 'assistant', content: 'Hello! How can I help?' },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
async onSubmit(e: Event) {
|
|
82
|
+
const { value } = (e as CustomEvent<{ value: string }>).detail;
|
|
83
|
+
// Reassign a NEW array so Angular's change detection picks it up.
|
|
84
|
+
const history = [...this.messages, { id: crypto.randomUUID(), role: 'user' as const, content: value }];
|
|
85
|
+
this.messages = history;
|
|
86
|
+
|
|
87
|
+
const aid = crypto.randomUUID();
|
|
88
|
+
this.messages = [...history, { id: aid, role: 'assistant' as const, content: '' }];
|
|
89
|
+
|
|
90
|
+
let answer = '';
|
|
91
|
+
for await (const token of streamFromYourAPI(history)) {
|
|
92
|
+
answer += token;
|
|
93
|
+
// Reassign a new array — and a new object for the message being streamed.
|
|
94
|
+
this.messages = this.messages.map((m) =>
|
|
95
|
+
m.id === aid ? { ...m, content: answer } : m
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<!-- app.component.html -->
|
|
104
|
+
<!-- The elements are display:block and fill their container — lay them out with flex
|
|
105
|
+
and let the element grow with flex:1 rather than hard-coding a height. -->
|
|
106
|
+
<div style="display: flex; flex-direction: column; height: 100dvh">
|
|
107
|
+
<kai-chat
|
|
108
|
+
[messages]="messages"
|
|
109
|
+
(kai-submit)="onSubmit($event)"
|
|
110
|
+
style="flex: 1; min-height: 0"
|
|
111
|
+
></kai-chat>
|
|
112
|
+
</div>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Go further — compose the pieces
|
|
116
|
+
|
|
117
|
+
`<kai-chat>` is one option, not the only one. You can assemble your own layout from individual
|
|
118
|
+
elements. Here is a multi-conversation shell — a `<kai-conversations>` sidebar next to the
|
|
119
|
+
`<kai-chat>` thread:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
// workspace.component.ts
|
|
123
|
+
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
|
124
|
+
|
|
125
|
+
@Component({
|
|
126
|
+
selector: 'app-workspace',
|
|
127
|
+
standalone: true,
|
|
128
|
+
templateUrl: './workspace.component.html',
|
|
129
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
130
|
+
})
|
|
131
|
+
export class WorkspaceComponent {
|
|
132
|
+
conversations = myConversations;
|
|
133
|
+
activeId = this.conversations[0]?.id;
|
|
134
|
+
messages = loadMessages(this.activeId);
|
|
135
|
+
|
|
136
|
+
onConversationSelect(e: Event) {
|
|
137
|
+
this.activeId = (e as CustomEvent<{ id: string }>).detail.id;
|
|
138
|
+
this.messages = loadMessages(this.activeId);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
onNewChat() {
|
|
142
|
+
startNewConversation();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
onSubmit(e: Event) {
|
|
146
|
+
const { value } = (e as CustomEvent<{ value: string }>).detail;
|
|
147
|
+
sendMessage(value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<!-- workspace.component.html -->
|
|
154
|
+
<!-- Lay panels out with flex: the sidebar is fixed-width, the thread takes the rest
|
|
155
|
+
with flex:1. The elements fill whatever box you give them. -->
|
|
156
|
+
<div style="display: flex; height: 100dvh">
|
|
157
|
+
<kai-conversations
|
|
158
|
+
[conversations]="conversations"
|
|
159
|
+
[activeId]="activeId"
|
|
160
|
+
(kai-conversation-select)="onConversationSelect($event)"
|
|
161
|
+
(kai-new-chat)="onNewChat()"
|
|
162
|
+
style="width: 300px; flex-shrink: 0"
|
|
163
|
+
></kai-conversations>
|
|
164
|
+
|
|
165
|
+
<kai-chat
|
|
166
|
+
[messages]="messages"
|
|
167
|
+
(kai-submit)="onSubmit($event)"
|
|
168
|
+
style="flex: 1; min-width: 0"
|
|
169
|
+
></kai-chat>
|
|
170
|
+
</div>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Make the panels resizable
|
|
174
|
+
|
|
175
|
+
Want a draggable divider between the sidebar and the thread? Wrap the panels in `<kai-resizable>`
|
|
176
|
+
with one `<kai-resizable-item>` each — the handles are inserted for you (up to 3 panels). Each item
|
|
177
|
+
takes a `size` (px or `%`) plus optional `min`/`max`; listen for `(change)` (`$event.detail.sizes`)
|
|
178
|
+
to persist the layout.
|
|
179
|
+
|
|
180
|
+
```html
|
|
181
|
+
<div style="display: flex; flex-direction: column; height: 100dvh">
|
|
182
|
+
<kai-resizable
|
|
183
|
+
orientation="horizontal"
|
|
184
|
+
(kai-change)="onResize($event)"
|
|
185
|
+
style="flex: 1; min-height: 0"
|
|
186
|
+
>
|
|
187
|
+
<kai-resizable-item size="25%" min="200px">
|
|
188
|
+
<kai-conversations
|
|
189
|
+
[conversations]="conversations"
|
|
190
|
+
[activeId]="activeId"
|
|
191
|
+
(kai-conversation-select)="onConversationSelect($event)"
|
|
192
|
+
></kai-conversations>
|
|
193
|
+
</kai-resizable-item>
|
|
194
|
+
<kai-resizable-item>
|
|
195
|
+
<kai-chat [messages]="messages" (kai-submit)="onSubmit($event)"></kai-chat>
|
|
196
|
+
</kai-resizable-item>
|
|
197
|
+
</kai-resizable>
|
|
198
|
+
</div>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
onResize(e: Event) {
|
|
203
|
+
const { sizes } = (e as CustomEvent<{ sizes: string[] }>).detail;
|
|
204
|
+
// persist sizes to localStorage, a service, etc.
|
|
205
|
+
console.log('panel sizes:', sizes);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
You can also drop **standalone display elements** anywhere in your own UI — `<kai-markdown>`,
|
|
210
|
+
`<kai-code-block>`, `<kai-artifact>`, `<kai-reasoning>`, `<kai-tool>` — to render rich AI content
|
|
211
|
+
without adopting the whole chat. Each fills its container and is controlled via `[prop]` bindings
|
|
212
|
+
and `(event)` listeners.
|
|
213
|
+
|
|
214
|
+
> **See it all assembled:** **[Examples → Full Chat App](?path=/story/examples-full-chat-app--default)**
|
|
215
|
+
> wires a sidebar, threaded markdown, reasoning, a tool call, a model switcher, a context meter, and
|
|
216
|
+
> a rich prompt input into one screen — a working reference to crib from.
|
|
217
|
+
|
|
218
|
+
> **Find every element:** browse the **Components** section in the sidebar. Each element's **API**
|
|
219
|
+
> tab lists its props, events, and copy-paste usage for Angular (and every other framework).
|
|
220
|
+
|
|
221
|
+
## Props & events
|
|
222
|
+
|
|
223
|
+
The rule for all elements: **rich data goes in as properties, interactions come out as events.**
|
|
224
|
+
Angular's `[prop]="…"` binding writes to the element's DOM property (not an HTML attribute), so
|
|
225
|
+
arrays and objects pass through unstringified — no adapter or wrapper needed.
|
|
226
|
+
|
|
227
|
+
`(event)="handler($event)"` listens for a DOM CustomEvent. `$event` is the `CustomEvent` itself;
|
|
228
|
+
read the payload from `$event.detail`:
|
|
229
|
+
|
|
230
|
+
| Element | Property binding | Event | `$event.detail` |
|
|
231
|
+
|---|---|---|---|
|
|
232
|
+
| `kai-chat` | `[messages]="messages"` | `(submit)` | `{ value: string }` |
|
|
233
|
+
| `kai-conversations` | `[conversations]="…"` | `(conversationselect)` | `{ id: string }` |
|
|
234
|
+
| `kai-conversations` | `[groups]="…"` | `(newchat)` | — |
|
|
235
|
+
| `kai-conversations` | — | `(togglesidebar)` | — |
|
|
236
|
+
| `kai-resizable` | `orientation="horizontal"` | `(change)` | `{ sizes: string[] }` |
|
|
237
|
+
| `kai-resizable-item` | `size="25%"` `min="200px"` `max="…"` | — | — |
|
|
238
|
+
|
|
239
|
+
> **Scalar attributes** like `orientation="horizontal"` or `theme="dark"` can be plain HTML
|
|
240
|
+
> attributes (no brackets). Use `[prop]="…"` only when passing non-string values (arrays, objects,
|
|
241
|
+
> booleans) or Angular expressions.
|
|
242
|
+
|
|
243
|
+
> **`CUSTOM_ELEMENTS_SCHEMA` is required** in every standalone component (or NgModule) whose
|
|
244
|
+
> template contains `<kai-*>` tags. Without it, the Angular compiler rejects unknown element names at
|
|
245
|
+
> build time.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
|
|
3
|
+
<Meta title="Docs/Frameworks/HTML" />
|
|
4
|
+
|
|
5
|
+
# HTML
|
|
6
|
+
|
|
7
|
+
The kit ships as standard **custom elements** (`kai-*` tags) — no framework required. Import the
|
|
8
|
+
bundle once as a side-effect and every element is registered globally, ready to drop into any HTML
|
|
9
|
+
page or templating system.
|
|
10
|
+
|
|
11
|
+
There are **two ways to build with the kit**, and you can mix them:
|
|
12
|
+
|
|
13
|
+
1. **`<kai-chat>`** — the batteries-included shell: a whole chat experience in one tag. Fastest start.
|
|
14
|
+
2. **Compose the individual elements** (`<kai-conversations>`, `<kai-markdown>`, `<kai-artifact>`, …)
|
|
15
|
+
into your own layout when you want full control.
|
|
16
|
+
|
|
17
|
+
Both are shown below.
|
|
18
|
+
|
|
19
|
+
## Install & setup
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm i @kitn.ai/ui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Register the custom elements once (a side-effect import), then use the tags anywhere in your HTML:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
// Once, near your app entry — registers all <kai-*> elements globally.
|
|
29
|
+
import '@kitn.ai/ui/elements';
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- No CSS to import: each element is styled inside its own Shadow DOM. Only pull in
|
|
33
|
+
`@kitn.ai/ui/theme.css` if you want to override design tokens (see **Theming**).
|
|
34
|
+
|
|
35
|
+
### No-build / CDN option
|
|
36
|
+
|
|
37
|
+
No bundler? Load the module directly from a CDN and you're done:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<script type="module">
|
|
41
|
+
import 'https://cdn.jsdelivr.net/npm/@kitn.ai/ui/dist/kitn-chat.es.js';
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every `<kai-*>` element is then available as a global custom element — no install, no build step.
|
|
46
|
+
|
|
47
|
+
## Quick start — the all-in-one shell
|
|
48
|
+
|
|
49
|
+
`<kai-chat>` is **transport-agnostic**: give it a `messages` array, handle the `submit` event, and
|
|
50
|
+
stream your model's reply back into the property. You own the request; the element owns the UI.
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<!DOCTYPE html>
|
|
54
|
+
<html>
|
|
55
|
+
<head>
|
|
56
|
+
<!-- optional: only needed to override design tokens -->
|
|
57
|
+
<link rel="stylesheet" href="./node_modules/@kitn.ai/ui/theme.css" />
|
|
58
|
+
<style>
|
|
59
|
+
html, body { margin: 0; height: 100%; }
|
|
60
|
+
.app { display: flex; flex-direction: column; height: 100dvh; }
|
|
61
|
+
</style>
|
|
62
|
+
</head>
|
|
63
|
+
<body>
|
|
64
|
+
<!-- Put the element in a flex parent and give it flex: 1.
|
|
65
|
+
The elements are display:block and fill their box. -->
|
|
66
|
+
<div class="app">
|
|
67
|
+
<kai-chat id="chat" style="flex: 1; min-height: 0;"></kai-chat>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<script type="module">
|
|
71
|
+
import '@kitn.ai/ui/elements';
|
|
72
|
+
|
|
73
|
+
const chat = document.getElementById('chat');
|
|
74
|
+
|
|
75
|
+
// Arrays and objects → JS properties (never attributes)
|
|
76
|
+
chat.messages = [
|
|
77
|
+
{ id: '1', role: 'assistant', content: 'Hello! How can I help?', actions: ['copy', 'like', 'dislike'] },
|
|
78
|
+
];
|
|
79
|
+
chat.suggestions = ['Summarize the chat', 'Start fresh'];
|
|
80
|
+
|
|
81
|
+
// Interactions → addEventListener (CustomEvents, they don't bubble)
|
|
82
|
+
chat.addEventListener('kai-submit', async (e) => {
|
|
83
|
+
const text = e.detail.value;
|
|
84
|
+
|
|
85
|
+
// Append user message — always assign a NEW array to trigger a re-render
|
|
86
|
+
const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', content: text }];
|
|
87
|
+
chat.messages = history;
|
|
88
|
+
chat.loading = true;
|
|
89
|
+
|
|
90
|
+
// Stream into an empty assistant placeholder
|
|
91
|
+
const aid = crypto.randomUUID();
|
|
92
|
+
chat.messages = [...history, { id: aid, role: 'assistant', content: '' }];
|
|
93
|
+
|
|
94
|
+
let answer = '';
|
|
95
|
+
for await (const token of streamFromYourAPI(history)) {
|
|
96
|
+
answer += token;
|
|
97
|
+
// Replace the placeholder with a new object each chunk
|
|
98
|
+
chat.messages = chat.messages.map((m) =>
|
|
99
|
+
m.id === aid ? { ...m, content: answer } : m
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
chat.loading = false;
|
|
103
|
+
});
|
|
104
|
+
</script>
|
|
105
|
+
</body>
|
|
106
|
+
</html>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> **Reactivity:** always assign a **new array** (and a new object for any message you change).
|
|
110
|
+
> Mutating an existing object in place will not trigger a re-render.
|
|
111
|
+
|
|
112
|
+
## Go further — compose the pieces
|
|
113
|
+
|
|
114
|
+
`<kai-chat>` is one option, not the only one. Every element can be placed independently, so you can
|
|
115
|
+
assemble your own layout. Here's a multi-conversation shell — a `<kai-conversations>` sidebar next
|
|
116
|
+
to the `<kai-chat>` thread:
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<style>
|
|
120
|
+
html, body { margin: 0; height: 100%; }
|
|
121
|
+
.workspace { display: flex; height: 100dvh; }
|
|
122
|
+
</style>
|
|
123
|
+
|
|
124
|
+
<div class="workspace">
|
|
125
|
+
<kai-conversations id="sidebar" style="width: 300px; flex-shrink: 0;"></kai-conversations>
|
|
126
|
+
<kai-chat id="chat" style="flex: 1; min-width: 0;"></kai-chat>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<script type="module">
|
|
130
|
+
import '@kitn.ai/ui/elements';
|
|
131
|
+
|
|
132
|
+
const sidebar = document.getElementById('sidebar');
|
|
133
|
+
const chat = document.getElementById('chat');
|
|
134
|
+
|
|
135
|
+
// Rich data → JS properties
|
|
136
|
+
sidebar.conversations = myConversations;
|
|
137
|
+
chat.messages = loadMessages(myConversations[0]?.id);
|
|
138
|
+
|
|
139
|
+
sidebar.addEventListener('kai-conversation-select', (e) => {
|
|
140
|
+
chat.messages = loadMessages(e.detail.id);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
sidebar.addEventListener('kai-new-chat', () => startNewConversation());
|
|
144
|
+
|
|
145
|
+
chat.addEventListener('kai-submit', (e) => sendMessage(e.detail.value));
|
|
146
|
+
</script>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Make the panels resizable
|
|
150
|
+
|
|
151
|
+
Want a draggable divider between the sidebar and the thread? Wrap the panels in `<kai-resizable>`
|
|
152
|
+
with one `<kai-resizable-item>` each — the handles are inserted for you (up to 3 panels). Each item
|
|
153
|
+
takes a `size` (px or `%`) plus optional `min`/`max`; listen for `change` (`detail.sizes`) to
|
|
154
|
+
persist the layout.
|
|
155
|
+
|
|
156
|
+
```html
|
|
157
|
+
<style>
|
|
158
|
+
html, body { margin: 0; height: 100%; }
|
|
159
|
+
.app { display: flex; flex-direction: column; height: 100dvh; }
|
|
160
|
+
</style>
|
|
161
|
+
|
|
162
|
+
<div class="app">
|
|
163
|
+
<kai-resizable orientation="horizontal" style="flex: 1; min-height: 0;">
|
|
164
|
+
<kai-resizable-item size="25%" min="200px">
|
|
165
|
+
<kai-conversations id="sidebar"></kai-conversations>
|
|
166
|
+
</kai-resizable-item>
|
|
167
|
+
<kai-resizable-item>
|
|
168
|
+
<kai-chat id="chat"></kai-chat>
|
|
169
|
+
</kai-resizable-item>
|
|
170
|
+
</kai-resizable>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<script type="module">
|
|
174
|
+
import '@kitn.ai/ui/elements';
|
|
175
|
+
|
|
176
|
+
const resizable = document.querySelector('kai-resizable');
|
|
177
|
+
resizable.addEventListener('kai-change', (e) => {
|
|
178
|
+
console.log('panel sizes:', e.detail.sizes);
|
|
179
|
+
// persist to localStorage, etc.
|
|
180
|
+
});
|
|
181
|
+
</script>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
You can also drop **standalone display elements** anywhere in your own page — `<kai-markdown>`,
|
|
185
|
+
`<kai-code-block>`, `<kai-artifact>` — to render rich AI content without adopting the whole chat.
|
|
186
|
+
Each element fills its container and is controlled via properties and events.
|
|
187
|
+
|
|
188
|
+
> **See it all assembled:** **[Examples → Full Chat App](?path=/story/examples-full-chat-app--default)**
|
|
189
|
+
> wires a sidebar, threaded markdown, a model switcher, a context meter, and a rich prompt input
|
|
190
|
+
> into one screen — a working reference to crib from.
|
|
191
|
+
|
|
192
|
+
> **Find every element:** browse the **Components** section in the sidebar. Each element's **API**
|
|
193
|
+
> tab lists its props, events, and copy-paste usage for HTML (and every other framework).
|
|
194
|
+
|
|
195
|
+
## Props & events
|
|
196
|
+
|
|
197
|
+
The rule for all elements: **rich data goes in as JS properties, interactions come out as events.**
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
// ✅ Arrays and objects — always set as a JS property
|
|
201
|
+
el.messages = [{ id: '1', role: 'assistant', content: 'Hi' }];
|
|
202
|
+
el.conversations = myConversations;
|
|
203
|
+
|
|
204
|
+
// ✅ Events — addEventListener on the element (CustomEvents, they don't bubble)
|
|
205
|
+
el.addEventListener('kai-submit', (e) => console.log(e.detail.value));
|
|
206
|
+
el.addEventListener('kai-conversation-select', (e) => console.log(e.detail.id));
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Scalar attributes** (strings, booleans, numbers) can go directly in HTML markup:
|
|
210
|
+
|
|
211
|
+
```html
|
|
212
|
+
<!-- theme, placeholder, and loading are scalars → safe as attributes -->
|
|
213
|
+
<kai-chat
|
|
214
|
+
theme="dark"
|
|
215
|
+
placeholder="Ask anything…"
|
|
216
|
+
></kai-chat>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Key events by element:
|
|
220
|
+
|
|
221
|
+
| Element | Event | `detail` |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `kai-chat` | `submit` | `{ value: string }` |
|
|
224
|
+
| `kai-conversations` | `conversationselect` | `{ id: string }` |
|
|
225
|
+
| `kai-conversations` | `newchat` | — |
|
|
226
|
+
| `kai-conversations` | `togglesidebar` | — |
|
|
227
|
+
| `kai-resizable` | `change` | `{ sizes: string[] }` |
|