@kitnai/chat 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +50 -0
  2. package/dist/custom-elements.json +125 -73
  3. package/dist/kitn-chat.es.js +1 -1
  4. package/dist/llms/llms-full.txt +24 -24
  5. package/dist/theme.tokens.css +25 -1
  6. package/frameworks/react/index.tsx +46 -24
  7. package/llms-full.txt +24 -24
  8. package/package.json +1 -1
  9. package/src/elements/chat-workspace.tsx +28 -2
  10. package/src/elements/chat.tsx +22 -1
  11. package/src/elements/compiled.css +1 -1
  12. package/src/elements/conversation-list.tsx +10 -1
  13. package/src/elements/element-meta.json +2003 -0
  14. package/src/elements/kitn-attachments.stories.tsx +9 -0
  15. package/src/elements/kitn-chain-of-thought.stories.tsx +9 -0
  16. package/src/elements/kitn-chat-scope-picker.stories.tsx +9 -0
  17. package/src/elements/kitn-chat-workspace.stories.tsx +64 -14
  18. package/src/elements/kitn-chat.stories.tsx +58 -5
  19. package/src/elements/kitn-checkpoint.stories.tsx +9 -0
  20. package/src/elements/kitn-code-block.stories.tsx +9 -0
  21. package/src/elements/kitn-context-meter.stories.tsx +9 -0
  22. package/src/elements/kitn-conversation-list.stories.tsx +31 -10
  23. package/src/elements/kitn-empty.stories.tsx +9 -0
  24. package/src/elements/kitn-feedback-bar.stories.tsx +9 -0
  25. package/src/elements/kitn-file-upload.stories.tsx +9 -0
  26. package/src/elements/kitn-image.stories.tsx +9 -0
  27. package/src/elements/kitn-loader.stories.tsx +9 -0
  28. package/src/elements/kitn-markdown.stories.tsx +9 -0
  29. package/src/elements/kitn-message-skills.stories.tsx +9 -0
  30. package/src/elements/kitn-message.stories.tsx +9 -0
  31. package/src/elements/kitn-model-switcher.stories.tsx +9 -0
  32. package/src/elements/kitn-prompt-input.stories.tsx +35 -5
  33. package/src/elements/kitn-prompt-suggestions.stories.tsx +9 -0
  34. package/src/elements/kitn-reasoning.stories.tsx +9 -0
  35. package/src/elements/kitn-response-stream.stories.tsx +9 -0
  36. package/src/elements/kitn-source-list.stories.tsx +9 -0
  37. package/src/elements/kitn-source.stories.tsx +9 -0
  38. package/src/elements/kitn-text-shimmer.stories.tsx +9 -0
  39. package/src/elements/kitn-thinking-bar.stories.tsx +9 -0
  40. package/src/elements/kitn-tool.stories.tsx +9 -0
  41. package/src/elements/kitn-voice-input.stories.tsx +9 -0
  42. package/src/elements/prompt-input.tsx +2 -2
  43. package/src/elements/styles.css +21 -0
  44. package/src/stories/docs/element-controls.ts +28 -0
  45. package/src/stories/docs/element-spec.tsx +86 -0
  46. package/src/ui/scroll-area.tsx +1 -1
  47. package/theme.css +25 -1
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  // The web components are custom DOM elements, so declare the tags for JSX.
6
8
  declare module 'solid-js' {
@@ -49,6 +51,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
49
51
  const meta = {
50
52
  title: 'Web Components/kitn-message-skills',
51
53
  tags: ['autodocs'],
54
+ argTypes: argTypesFor('kitn-message-skills'),
52
55
  parameters: {
53
56
  layout: 'fullscreen',
54
57
  docs: {
@@ -67,6 +70,12 @@ const meta = {
67
70
  export default meta;
68
71
  type Story = StoryObj;
69
72
 
73
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
74
+ export const API: Story = {
75
+ render: () => <ElementSpec tag="kitn-message-skills" />,
76
+ parameters: { layout: 'padded' },
77
+ };
78
+
70
79
  /** Two active-skill badges. */
71
80
  export const Default: Story = {
72
81
  render: () => <MessageSkillsElement skills={sampleSkills} />,
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
4
  import type { ChatMessage } from './chat-types';
5
+ import { ElementSpec } from '../stories/docs/element-spec';
6
+ import { argTypesFor } from '../stories/docs/element-controls';
5
7
 
6
8
  // The web components are custom DOM elements, so declare the tags for JSX.
7
9
  declare module 'solid-js' {
@@ -75,6 +77,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
75
77
  const meta = {
76
78
  title: 'Web Components/kitn-message',
77
79
  tags: ['autodocs'],
80
+ argTypes: argTypesFor('kitn-message'),
78
81
  parameters: {
79
82
  layout: 'fullscreen',
80
83
  docs: {
@@ -93,6 +96,12 @@ const meta = {
93
96
  export default meta;
94
97
  type Story = StoryObj;
95
98
 
99
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
100
+ export const API: Story = {
101
+ render: () => <ElementSpec tag="kitn-message" />,
102
+ parameters: { layout: 'padded' },
103
+ };
104
+
96
105
  /** A rich assistant message: markdown, reasoning, a tool call, an attachment, and actions. */
97
106
  export const Assistant: Story = {
98
107
  render: () => <MessageElement message={assistantMessage} />,
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
4
  import type { ModelOption } from '../types';
5
+ import { ElementSpec } from '../stories/docs/element-spec';
6
+ import { argTypesFor } from '../stories/docs/element-controls';
5
7
 
6
8
  // The web components are custom DOM elements, so declare the tags for JSX.
7
9
  declare module 'solid-js' {
@@ -55,6 +57,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
55
57
  const meta = {
56
58
  title: 'Web Components/kitn-model-switcher',
57
59
  tags: ['autodocs'],
60
+ argTypes: argTypesFor('kitn-model-switcher'),
58
61
  parameters: {
59
62
  layout: 'fullscreen',
60
63
  docs: {
@@ -73,6 +76,12 @@ const meta = {
73
76
  export default meta;
74
77
  type Story = StoryObj;
75
78
 
79
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
80
+ export const API: Story = {
81
+ render: () => <ElementSpec tag="kitn-model-switcher" />,
82
+ parameters: { layout: 'padded' },
83
+ };
84
+
76
85
  /** A three-model picker; selecting updates the trigger label. */
77
86
  export const Default: Story = {
78
87
  render: () => <SwitcherElement models={models} current="opus" />,
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers <kitn-chat>, <kitn-conversation-list>, <kitn-prompt-input>
4
4
  import type { AttachmentData } from '../components/attachments';
5
+ import { ElementSpec } from '../stories/docs/element-spec';
6
+ import { argTypesFor } from '../stories/docs/element-controls';
5
7
 
6
8
  // The web components are custom DOM elements, so declare the tags for JSX.
7
9
  declare module 'solid-js' {
@@ -41,15 +43,27 @@ interface PromptInputEl extends HTMLElement {
41
43
  }
42
44
 
43
45
  /** Live demo of the actual `<kitn-prompt-input>` custom element (Shadow DOM and all). */
44
- function PromptInputElement(props?: { search?: boolean; voice?: boolean; attachments?: AttachmentData[] }) {
46
+ function PromptInputElement(props: { search?: boolean; voice?: boolean; attachments?: AttachmentData[]; args?: Record<string, unknown> }) {
45
47
  let el: PromptInputEl | undefined;
46
48
  onMount(() => {
47
49
  if (!el) return;
50
+ // Default fixed data
48
51
  el.placeholder = 'Ask anything...';
49
52
  el.suggestions = sampleSuggestions;
50
- if (props?.search) el.setAttribute('search', '');
51
- if (props?.voice) el.setAttribute('voice', '');
52
- if (props?.attachments) el.attachments = props.attachments;
53
+ if (props.search) el.setAttribute('search', '');
54
+ if (props.voice) el.setAttribute('voice', '');
55
+ if (props.attachments) el.attachments = props.attachments;
56
+ // Scalar args from Controls
57
+ const args = props.args;
58
+ if (args) {
59
+ const scalarNames = [
60
+ 'value', 'placeholder', 'disabled', 'loading', 'suggestionMode',
61
+ 'slashCompact', 'search', 'voice',
62
+ ];
63
+ for (const name of scalarNames) {
64
+ if (name in args) (el as unknown as Record<string, unknown>)[name] = args[name];
65
+ }
66
+ }
53
67
  el.addEventListener('search', () => console.log('search clicked'));
54
68
  el.addEventListener('voice', () => console.log('voice clicked'));
55
69
  });
@@ -108,6 +122,7 @@ function Composer() {
108
122
  const meta = {
109
123
  title: 'Web Components/kitn-prompt-input',
110
124
  tags: ['autodocs'],
125
+ argTypes: argTypesFor('kitn-prompt-input'),
111
126
  parameters: {
112
127
  layout: 'fullscreen',
113
128
  docs: {
@@ -129,7 +144,16 @@ type Story = StoryObj;
129
144
 
130
145
  /** The element used the plain-HTML / any-framework way. */
131
146
  export const Default: Story = {
132
- render: () => <PromptInputElement />,
147
+ args: {
148
+ placeholder: 'Send a message...',
149
+ disabled: false,
150
+ loading: false,
151
+ suggestionMode: 'submit',
152
+ slashCompact: false,
153
+ search: false,
154
+ voice: false,
155
+ },
156
+ render: (args: Record<string, unknown>) => <PromptInputElement args={args} />,
133
157
  parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
134
158
  };
135
159
 
@@ -179,3 +203,9 @@ export const WithAttachments: Story = {
179
203
  render: () => <PromptInputElement voice attachments={sampleAttachments} />,
180
204
  parameters: { docs: { source: { code: ATTACHMENTS_SNIPPET, language: 'html' } } },
181
205
  };
206
+
207
+ /** Full generated API reference — properties, events, tokens, and the SolidJS components this element is composed from. */
208
+ export const API: Story = {
209
+ render: () => <ElementSpec tag="kitn-prompt-input" />,
210
+ parameters: { layout: 'padded' },
211
+ };
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  type Item = string | { label: string; value?: string };
6
8
 
@@ -62,6 +64,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
62
64
  const meta = {
63
65
  title: 'Web Components/kitn-prompt-suggestions',
64
66
  tags: ['autodocs'],
67
+ argTypes: argTypesFor('kitn-prompt-suggestions'),
65
68
  parameters: {
66
69
  layout: 'fullscreen',
67
70
  docs: {
@@ -80,6 +83,12 @@ const meta = {
80
83
  export default meta;
81
84
  type Story = StoryObj;
82
85
 
86
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
87
+ export const API: Story = {
88
+ render: () => <ElementSpec tag="kitn-prompt-suggestions" />,
89
+ parameters: { layout: 'padded' },
90
+ };
91
+
83
92
  /** Default outline pills, wrapping in a row. */
84
93
  export const Default: Story = {
85
94
  render: () => <SuggestionsElement suggestions={suggestions} variant="outline" />,
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  // The web components are custom DOM elements, so declare the tags for JSX.
6
8
  declare module 'solid-js' {
@@ -46,6 +48,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
46
48
  const meta = {
47
49
  title: 'Web Components/kitn-reasoning',
48
50
  tags: ['autodocs'],
51
+ argTypes: argTypesFor('kitn-reasoning'),
49
52
  parameters: {
50
53
  layout: 'fullscreen',
51
54
  docs: {
@@ -64,6 +67,12 @@ const meta = {
64
67
  export default meta;
65
68
  type Story = StoryObj;
66
69
 
70
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
71
+ export const API: Story = {
72
+ render: () => <ElementSpec tag="kitn-reasoning" />,
73
+ parameters: { layout: 'padded' },
74
+ };
75
+
67
76
  /** A collapsed reasoning block (the trigger toggles it). */
68
77
  export const Default: Story = {
69
78
  render: () => <ReasoningElement text={sampleText} />,
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  // The web components are custom DOM elements, so declare the tags for JSX.
6
8
  declare module 'solid-js' {
@@ -49,6 +51,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
49
51
  const meta = {
50
52
  title: 'Web Components/kitn-response-stream',
51
53
  tags: ['autodocs'],
54
+ argTypes: argTypesFor('kitn-response-stream'),
52
55
  parameters: {
53
56
  layout: 'fullscreen',
54
57
  docs: {
@@ -67,6 +70,12 @@ const meta = {
67
70
  export default meta;
68
71
  type Story = StoryObj;
69
72
 
73
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
74
+ export const API: Story = {
75
+ render: () => <ElementSpec tag="kitn-response-stream" />,
76
+ parameters: { layout: 'padded' },
77
+ };
78
+
70
79
  /** Typewriter reveal (the default). */
71
80
  export const Typewriter: Story = {
72
81
  render: () => <StreamElement text={STREAM_TEXT} mode="typewriter" speed={20} />,
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  // The web components are custom DOM elements, so declare the tags for JSX.
6
8
  declare module 'solid-js' {
@@ -52,6 +54,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
52
54
  const meta = {
53
55
  title: 'Web Components/kitn-source-list',
54
56
  tags: ['autodocs'],
57
+ argTypes: argTypesFor('kitn-source-list'),
55
58
  parameters: {
56
59
  layout: 'fullscreen',
57
60
  docs: {
@@ -70,6 +73,12 @@ const meta = {
70
73
  export default meta;
71
74
  type Story = StoryObj;
72
75
 
76
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
77
+ export const API: Story = {
78
+ render: () => <ElementSpec tag="kitn-source-list" />,
79
+ parameters: { layout: 'padded' },
80
+ };
81
+
73
82
  /** Two citations rendered as a wrapped list with favicons. */
74
83
  export const Default: Story = {
75
84
  render: () => <SourceListElement />,
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  // The web components are custom DOM elements, so declare the tags for JSX.
6
8
  declare module 'solid-js' {
@@ -48,6 +50,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
48
50
  const meta = {
49
51
  title: 'Web Components/kitn-source',
50
52
  tags: ['autodocs'],
53
+ argTypes: argTypesFor('kitn-source'),
51
54
  parameters: {
52
55
  layout: 'fullscreen',
53
56
  docs: {
@@ -66,6 +69,12 @@ const meta = {
66
69
  export default meta;
67
70
  type Story = StoryObj;
68
71
 
72
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
73
+ export const API: Story = {
74
+ render: () => <ElementSpec tag="kitn-source" />,
75
+ parameters: { layout: 'padded' },
76
+ };
77
+
69
78
  /** A citation with a custom label, hover headline/description, and favicon. */
70
79
  export const Default: Story = {
71
80
  render: () => (
@@ -1,5 +1,7 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import './register'; // side effect: registers the custom elements
3
+ import { ElementSpec } from '../stories/docs/element-spec';
4
+ import { argTypesFor } from '../stories/docs/element-controls';
3
5
 
4
6
  // The web components are custom DOM elements, so declare the tags for JSX.
5
7
  declare module 'solid-js' {
@@ -25,6 +27,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
25
27
  const meta = {
26
28
  title: 'Web Components/kitn-text-shimmer',
27
29
  tags: ['autodocs'],
30
+ argTypes: argTypesFor('kitn-text-shimmer'),
28
31
  parameters: {
29
32
  layout: 'fullscreen',
30
33
  docs: {
@@ -43,6 +46,12 @@ const meta = {
43
46
  export default meta;
44
47
  type Story = StoryObj;
45
48
 
49
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
50
+ export const API: Story = {
51
+ render: () => <ElementSpec tag="kitn-text-shimmer" />,
52
+ parameters: { layout: 'padded' },
53
+ };
54
+
46
55
  /** Default shimmer. */
47
56
  export const Default: Story = {
48
57
  render: () => (
@@ -1,5 +1,7 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import './register'; // side effect: registers the custom elements
3
+ import { ElementSpec } from '../stories/docs/element-spec';
4
+ import { argTypesFor } from '../stories/docs/element-controls';
3
5
 
4
6
  // The web components are custom DOM elements, so declare the tags for JSX.
5
7
  declare module 'solid-js' {
@@ -29,6 +31,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
29
31
  const meta = {
30
32
  title: 'Web Components/kitn-thinking-bar',
31
33
  tags: ['autodocs'],
34
+ argTypes: argTypesFor('kitn-thinking-bar'),
32
35
  parameters: {
33
36
  layout: 'fullscreen',
34
37
  docs: {
@@ -47,6 +50,12 @@ const meta = {
47
50
  export default meta;
48
51
  type Story = StoryObj;
49
52
 
53
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
54
+ export const API: Story = {
55
+ render: () => <ElementSpec tag="kitn-thinking-bar" />,
56
+ parameters: { layout: 'padded' },
57
+ };
58
+
50
59
  /** A plain thinking indicator. */
51
60
  export const Default: Story = {
52
61
  render: () => (
@@ -2,6 +2,8 @@ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
4
  import type { ToolPart } from '../components/tool';
5
+ import { ElementSpec } from '../stories/docs/element-spec';
6
+ import { argTypesFor } from '../stories/docs/element-controls';
5
7
 
6
8
  // The web components are custom DOM elements, so declare the tags for JSX.
7
9
  declare module 'solid-js' {
@@ -58,6 +60,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
58
60
  const meta = {
59
61
  title: 'Web Components/kitn-tool',
60
62
  tags: ['autodocs'],
63
+ argTypes: argTypesFor('kitn-tool'),
61
64
  parameters: {
62
65
  layout: 'fullscreen',
63
66
  docs: {
@@ -76,6 +79,12 @@ const meta = {
76
79
  export default meta;
77
80
  type Story = StoryObj;
78
81
 
82
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
83
+ export const API: Story = {
84
+ render: () => <ElementSpec tag="kitn-tool" />,
85
+ parameters: { layout: 'padded' },
86
+ };
87
+
79
88
  /** A completed call with input and output, started expanded. */
80
89
  export const Completed: Story = {
81
90
  render: () => <ToolElement tool={completedTool} open />,
@@ -1,6 +1,8 @@
1
1
  import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
2
  import { onMount } from 'solid-js';
3
3
  import './register'; // side effect: registers the custom elements
4
+ import { ElementSpec } from '../stories/docs/element-spec';
5
+ import { argTypesFor } from '../stories/docs/element-controls';
4
6
 
5
7
  // The web components are custom DOM elements, so declare the tags for JSX.
6
8
  declare module 'solid-js' {
@@ -57,6 +59,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
57
59
  const meta = {
58
60
  title: 'Web Components/kitn-voice-input',
59
61
  tags: ['autodocs'],
62
+ argTypes: argTypesFor('kitn-voice-input'),
60
63
  parameters: {
61
64
  layout: 'fullscreen',
62
65
  docs: {
@@ -75,6 +78,12 @@ const meta = {
75
78
  export default meta;
76
79
  type Story = StoryObj;
77
80
 
81
+ /** Full generated API reference — properties, events, tokens, and composed-from. */
82
+ export const API: Story = {
83
+ render: () => <ElementSpec tag="kitn-voice-input" />,
84
+ parameters: { layout: 'padded' },
85
+ };
86
+
78
87
  /** A working mic button wired to a stub transcriber. */
79
88
  export const Default: Story = {
80
89
  render: () => <VoiceElement />,
@@ -51,9 +51,9 @@ interface Events {
51
51
  /** A slash command was chosen from the palette. */
52
52
  slashselect: { command: SlashCommandItem };
53
53
  /** The Search (Globe) toolbar button was clicked. */
54
- search: undefined;
54
+ search: Record<string, never>;
55
55
  /** The Voice (Mic) toolbar button was clicked. */
56
- voice: undefined;
56
+ voice: Record<string, never>;
57
57
  }
58
58
 
59
59
  defineKitnElement<Props, Events>('kitn-prompt-input', {
@@ -23,4 +23,25 @@
23
23
  outline: 2px solid var(--color-ring);
24
24
  outline-offset: 2px;
25
25
  }
26
+
27
+ /* Consistent cross-platform scrollbars for EVERY scroll region inside the
28
+ shadow root (message list, conversation list, menus, hover cards, code
29
+ blocks, the prompt textarea …). Thin + rounded + subtle, themed via tokens
30
+ for light/dark, thumb stronger on hover. Track stays transparent and
31
+ overflow is untouched, so no scrollbar is forced where none is needed.
32
+ Scoped to the shadow root, so it never restyles the host page. */
33
+ * {
34
+ scrollbar-width: thin;
35
+ scrollbar-color: var(--color-scrollbar-thumb) transparent;
36
+ }
37
+ *::-webkit-scrollbar { width: 8px; height: 8px; }
38
+ *::-webkit-scrollbar-track { background: transparent; }
39
+ *::-webkit-scrollbar-thumb {
40
+ background-color: var(--color-scrollbar-thumb);
41
+ border-radius: 9999px;
42
+ border: 2px solid transparent;
43
+ background-clip: padding-box;
44
+ }
45
+ *::-webkit-scrollbar-thumb:hover { background-color: var(--color-scrollbar-thumb-hover); }
46
+ *::-webkit-scrollbar-corner { background: transparent; }
26
47
  }
@@ -0,0 +1,28 @@
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
+ const enumValues = (type: string): string[] | null => {
8
+ // string-literal unions like "'light' | 'dark' | 'auto'"
9
+ const parts = type.split('|').map((s) => s.trim());
10
+ if (parts.length > 1 && parts.every((p) => /^'[^']*'$/.test(p))) return parts.map((p) => p.slice(1, -1));
11
+ return null;
12
+ };
13
+
14
+ /** Storybook argTypes for an element's scalar props (theme select, booleans, text, number). */
15
+ export function argTypesFor(tag: string): Record<string, unknown> {
16
+ const el = all.find((e) => e.tag === tag);
17
+ if (!el) return {};
18
+ const out: Record<string, unknown> = {};
19
+ for (const p of el.props) {
20
+ if (!p.scalar) continue;
21
+ const values = enumValues(p.type);
22
+ if (values) out[p.name] = { control: 'select', options: values };
23
+ else if (/boolean/.test(p.type)) out[p.name] = { control: 'boolean' };
24
+ else if (/number/.test(p.type)) out[p.name] = { control: 'number' };
25
+ else out[p.name] = { control: 'text' };
26
+ }
27
+ return out;
28
+ }
@@ -0,0 +1,86 @@
1
+ import { For, Show } from 'solid-js';
2
+ import meta from '../../elements/element-meta.json';
3
+
4
+ type Prop = { name: string; type: string; default?: string; scalar: boolean; description: string };
5
+ type Event = { name: string; detail: string | null; description: string };
6
+ type Composed = { name: string; group: string; storyId?: string };
7
+ type ElementMeta = { tag: string; className: string; props: Prop[]; events: Event[]; composedFrom: Composed[]; tokens: string[] };
8
+
9
+ const all = meta as unknown as ElementMeta[];
10
+
11
+ const th = 'text-left font-semibold px-2 py-1.5 border-b border-border text-foreground';
12
+ const td = 'px-2 py-1.5 border-b border-border align-top text-muted-foreground';
13
+ const code = 'font-mono text-[0.85em] text-code-foreground';
14
+
15
+ export function ElementSpec(props: { tag: string }) {
16
+ const el = () => all.find((e) => e.tag === props.tag);
17
+ return (
18
+ <Show when={el()} fallback={<p>Unknown element: {props.tag}</p>}>
19
+ {(e) => (
20
+ <div class="text-sm space-y-6">
21
+ <section>
22
+ <h3 class="text-title font-semibold text-foreground mb-2">Properties</h3>
23
+ <table class="w-full border-collapse">
24
+ <thead><tr><th class={th}>Property</th><th class={th}>Attribute</th><th class={th}>Type / values</th><th class={th}>Default</th></tr></thead>
25
+ <tbody>
26
+ <For each={e().props}>{(p) => (
27
+ <tr>
28
+ <td class={td}><span class={code}>{p.name}</span></td>
29
+ <td class={td}>{p.scalar ? <span class={code}>{kebab(p.name)}</span> : <span class="opacity-50">— (property only)</span>}</td>
30
+ <td class={td}><span class={code}>{p.type}</span></td>
31
+ <td class={td}>{p.default ? <span class={code}>{p.default}</span> : '—'}</td>
32
+ </tr>
33
+ )}</For>
34
+ </tbody>
35
+ </table>
36
+ </section>
37
+
38
+ <Show when={e().events.length}>
39
+ <section>
40
+ <h3 class="text-title font-semibold text-foreground mb-2">Events</h3>
41
+ <p class="text-muted-foreground mb-2 text-xs">Non-bubbling <span class={code}>CustomEvent</span>s on the element; the payload is on <span class={code}>event.detail</span>.</p>
42
+ <table class="w-full border-collapse">
43
+ <thead><tr><th class={th}>Event</th><th class={th}>detail</th><th class={th}>Description</th></tr></thead>
44
+ <tbody>
45
+ <For each={e().events}>{(ev) => (
46
+ <tr>
47
+ <td class={td}><span class={code}>{ev.name}</span></td>
48
+ <td class={td}><span class={code}>{detailText(ev.detail)}</span></td>
49
+ <td class={td}>{ev.description}</td>
50
+ </tr>
51
+ )}</For>
52
+ </tbody>
53
+ </table>
54
+ </section>
55
+ </Show>
56
+
57
+ <Show when={e().composedFrom.length}>
58
+ <section>
59
+ <h3 class="text-title font-semibold text-foreground mb-2">Composed from</h3>
60
+ <p class="text-muted-foreground mb-2 text-xs">This element wraps these SolidJS components:</p>
61
+ <div class="flex flex-wrap gap-2">
62
+ <For each={e().composedFrom}>{(c) => (
63
+ <a class="rounded-md bg-muted px-2 py-1 text-xs text-foreground hover:bg-accent no-underline" href={`?path=/docs/${c.storyId}`}>
64
+ {c.group}/{c.name}
65
+ </a>
66
+ )}</For>
67
+ </div>
68
+ </section>
69
+ </Show>
70
+
71
+ <section>
72
+ <h3 class="text-title font-semibold text-foreground mb-2">Theming</h3>
73
+ <p class="text-muted-foreground text-xs">
74
+ Themed by the global design tokens — override any <span class={code}>--color-*</span> token to rebrand (see the <a href="?path=/docs/theming-token-reference--docs" class="text-primary">Token Reference</a>).
75
+ <Show when={e().tokens.length}>{' '}This element also reads:{' '}<For each={e().tokens}>{(t, i) => <><span class={code}>{t}</span>{i() < e().tokens.length - 1 ? ', ' : ''}</>}</For>.</Show>
76
+ </p>
77
+ </section>
78
+ </div>
79
+ )}
80
+ </Show>
81
+ );
82
+ }
83
+
84
+ function kebab(name: string) { return name.replace(/([A-Z])/g, '-$1').toLowerCase(); }
85
+ // Payloadless events (no detail, or an empty `Record<string, never>`) render as a dash.
86
+ function detailText(detail: string | null) { return !detail || detail === 'Record<string, never>' ? '—' : detail; }
@@ -6,7 +6,7 @@ export interface ScrollAreaProps extends JSX.HTMLAttributes<HTMLDivElement> { ch
6
6
  export function ScrollArea(props: ScrollAreaProps) {
7
7
  const [local, rest] = splitProps(props, ['children', 'class']);
8
8
  return (
9
- <div class={cn('overflow-y-auto scrollbar-thin scrollbar-thumb-muted scrollbar-track-transparent', local.class)} {...rest}>
9
+ <div class={cn('overflow-y-auto scrollbar-thin', local.class)} {...rest}>
10
10
  {local.children}
11
11
  </div>
12
12
  );
package/theme.css CHANGED
@@ -32,6 +32,12 @@
32
32
  brighter one for contrast on dark surfaces. Both clear WCAG 2.1 non-text
33
33
  contrast (≥3:1) against the kit's backgrounds. Override via --kitn-color-ring. */
34
34
  --color-ring: var(--kitn-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 --kitn-color-scrollbar-thumb(-hover). */
39
+ --color-scrollbar-thumb: var(--kitn-color-scrollbar-thumb, hsl(240 5% 80%));
40
+ --color-scrollbar-thumb-hover: var(--kitn-color-scrollbar-thumb-hover, hsl(240 4% 64%));
35
41
  --color-sidebar: var(--kitn-color-sidebar, hsl(0 0% 100%));
36
42
 
37
43
  /* Inline `code` accent. Blue text on a translucent blue chip. */
@@ -102,6 +108,8 @@
102
108
  --color-border: var(--kitn-color-border, hsl(45 4% 17%));
103
109
  --color-input: var(--kitn-color-input, hsl(45 4% 17%));
104
110
  --color-ring: var(--kitn-color-ring, hsl(217 91% 68%));
111
+ --color-scrollbar-thumb: var(--kitn-color-scrollbar-thumb, hsl(45 3% 30%));
112
+ --color-scrollbar-thumb-hover: var(--kitn-color-scrollbar-thumb-hover, hsl(45 3% 42%));
105
113
  --color-sidebar: var(--kitn-color-sidebar, hsl(50 2% 7%));
106
114
  --color-code-foreground: var(--kitn-color-code-foreground, hsl(213 94% 78%));
107
115
 
@@ -145,4 +153,20 @@
145
153
  .chat-markdown th, .chat-markdown td { border: 1px solid var(--color-border); padding: 0.4em 0.6em; text-align: left; }
146
154
  .chat-markdown th { font-weight: 600; background: color-mix(in oklab, var(--color-muted-foreground) 8%, transparent); }
147
155
 
148
- .scrollbar-thin { scrollbar-width: thin; scrollbar-color: var(--color-muted) transparent; }
156
+ /* Cross-platform thin scrollbar utility (Firefox + WebKit). Used by ScrollArea
157
+ and any scroll region in the SolidJS-component build (web components also get
158
+ this globally inside their shadow root — see src/elements/styles.css). */
159
+ .scrollbar-thin {
160
+ scrollbar-width: thin;
161
+ scrollbar-color: var(--color-scrollbar-thumb) transparent;
162
+ }
163
+ .scrollbar-thin::-webkit-scrollbar { width: 8px; height: 8px; }
164
+ .scrollbar-thin::-webkit-scrollbar-track { background: transparent; }
165
+ .scrollbar-thin::-webkit-scrollbar-thumb {
166
+ background-color: var(--color-scrollbar-thumb);
167
+ border-radius: 9999px;
168
+ border: 2px solid transparent;
169
+ background-clip: padding-box;
170
+ }
171
+ .scrollbar-thin::-webkit-scrollbar-thumb:hover { background-color: var(--color-scrollbar-thumb-hover); }
172
+ .scrollbar-thin::-webkit-scrollbar-corner { background: transparent; }