@kitnai/chat 0.3.1 → 0.5.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 (119) hide show
  1. package/README.md +35 -5
  2. package/dist/custom-elements.json +2969 -0
  3. package/dist/kitn-chat.es.js +52 -39
  4. package/dist/llms/llms-full.txt +718 -0
  5. package/dist/llms/llms.txt +104 -0
  6. package/dist/theme.tokens.css +137 -0
  7. package/frameworks/react/index.tsx +584 -0
  8. package/frameworks/react/runtime.tsx +94 -0
  9. package/llms-full.txt +718 -0
  10. package/llms.txt +104 -0
  11. package/package.json +53 -6
  12. package/src/components/attachments.tsx +4 -2
  13. package/src/components/chain-of-thought.tsx +1 -1
  14. package/src/components/chat-scope-picker.tsx +2 -2
  15. package/src/components/chat-thread.tsx +217 -0
  16. package/src/components/checkpoint.tsx +7 -3
  17. package/src/components/context.tsx +14 -18
  18. package/src/components/conversation-item.tsx +1 -1
  19. package/src/components/conversation-list.tsx +5 -4
  20. package/src/components/message-skills.tsx +1 -1
  21. package/src/components/message.tsx +1 -0
  22. package/src/components/model-switcher.tsx +3 -3
  23. package/src/components/prompt-input.tsx +20 -2
  24. package/src/components/reasoning.tsx +2 -2
  25. package/src/components/scroll-button.tsx +1 -0
  26. package/src/components/slash-command.tsx +17 -8
  27. package/src/components/source.tsx +2 -2
  28. package/src/components/thinking-bar.tsx +2 -2
  29. package/src/components/tool.tsx +17 -6
  30. package/src/components/voice-input.tsx +5 -1
  31. package/src/elements/attachments.tsx +132 -0
  32. package/src/elements/chain-of-thought.tsx +45 -0
  33. package/src/elements/chat-scope-picker.tsx +36 -0
  34. package/src/elements/chat-workspace.tsx +122 -0
  35. package/src/elements/chat.tsx +31 -228
  36. package/src/elements/checkpoint.tsx +43 -0
  37. package/src/elements/code-block.tsx +42 -0
  38. package/src/elements/compiled.css +1 -1
  39. package/src/elements/context-meter.tsx +71 -0
  40. package/src/elements/conversation-list.tsx +6 -0
  41. package/src/elements/default-input.tsx +22 -1
  42. package/src/elements/define.tsx +98 -12
  43. package/src/elements/element-types.d.ts +444 -0
  44. package/src/elements/empty.tsx +29 -0
  45. package/src/elements/feedback-bar.tsx +33 -0
  46. package/src/elements/file-upload.tsx +44 -0
  47. package/src/elements/image.tsx +32 -0
  48. package/src/elements/kitn-attachments.stories.tsx +181 -0
  49. package/src/elements/kitn-chain-of-thought.stories.tsx +75 -0
  50. package/src/elements/kitn-chat-scope-picker.stories.tsx +72 -0
  51. package/src/elements/kitn-chat-workspace.stories.tsx +195 -0
  52. package/src/elements/kitn-checkpoint.stories.tsx +71 -0
  53. package/src/elements/kitn-code-block.stories.tsx +82 -0
  54. package/src/elements/kitn-context-meter.stories.tsx +85 -0
  55. package/src/elements/kitn-empty.stories.tsx +110 -0
  56. package/src/elements/kitn-feedback-bar.stories.tsx +73 -0
  57. package/src/elements/kitn-file-upload.stories.tsx +81 -0
  58. package/src/elements/kitn-image.stories.tsx +70 -0
  59. package/src/elements/kitn-loader.stories.tsx +87 -0
  60. package/src/elements/kitn-markdown.stories.tsx +75 -0
  61. package/src/elements/kitn-message-skills.stories.tsx +74 -0
  62. package/src/elements/kitn-message.stories.tsx +105 -0
  63. package/src/elements/kitn-model-switcher.stories.tsx +80 -0
  64. package/src/elements/kitn-prompt-input.stories.tsx +74 -16
  65. package/src/elements/kitn-prompt-suggestions.stories.tsx +157 -0
  66. package/src/elements/kitn-reasoning.stories.tsx +76 -0
  67. package/src/elements/kitn-response-stream.stories.tsx +79 -0
  68. package/src/elements/kitn-source-list.stories.tsx +77 -0
  69. package/src/elements/kitn-source.stories.tsx +87 -0
  70. package/src/elements/kitn-text-shimmer.stories.tsx +63 -0
  71. package/src/elements/kitn-thinking-bar.stories.tsx +72 -0
  72. package/src/elements/kitn-tool.stories.tsx +88 -0
  73. package/src/elements/kitn-voice-input.stories.tsx +87 -0
  74. package/src/elements/loader.tsx +25 -0
  75. package/src/elements/markdown.tsx +38 -0
  76. package/src/elements/message-skills.tsx +22 -0
  77. package/src/elements/message.tsx +125 -0
  78. package/src/elements/model-switcher.tsx +35 -0
  79. package/src/elements/prompt-input.tsx +83 -7
  80. package/src/elements/prompt-suggestions.tsx +58 -0
  81. package/src/elements/reasoning.tsx +50 -0
  82. package/src/elements/register.ts +32 -0
  83. package/src/elements/response-stream.tsx +40 -0
  84. package/src/elements/source.tsx +67 -0
  85. package/src/elements/styles.css +14 -0
  86. package/src/elements/text-shimmer.tsx +28 -0
  87. package/src/elements/thinking-bar.tsx +34 -0
  88. package/src/elements/tool.tsx +23 -0
  89. package/src/elements/voice-input.tsx +41 -0
  90. package/src/index.ts +0 -1
  91. package/src/primitives/chat-config.tsx +3 -3
  92. package/src/stories/docs/Accessibility.mdx +119 -0
  93. package/src/stories/docs/ForAIAgents.mdx +93 -0
  94. package/src/stories/docs/GettingStarted.mdx +2 -2
  95. package/src/stories/docs/Installation.mdx +29 -2
  96. package/src/stories/docs/Integrations.mdx +417 -15
  97. package/src/stories/docs/Introduction.mdx +17 -8
  98. package/src/stories/docs/Theming.mdx +1 -1
  99. package/src/stories/pattern-centered-conversation.stories.tsx +93 -0
  100. package/src/stories/pattern-docked-widget.stories.tsx +93 -0
  101. package/src/stories/pattern-empty-state.stories.tsx +76 -0
  102. package/src/stories/typography.stories.tsx +78 -0
  103. package/src/ui/button.tsx +1 -1
  104. package/src/ui/collapsible.stories.tsx +70 -0
  105. package/src/ui/collapsible.tsx +119 -8
  106. package/src/ui/dropdown.stories.tsx +60 -0
  107. package/src/ui/dropdown.tsx +177 -12
  108. package/src/ui/hover-card.stories.tsx +78 -0
  109. package/src/ui/hover-card.tsx +147 -26
  110. package/src/ui/overlay.stories.tsx +115 -0
  111. package/src/ui/overlay.tsx +151 -0
  112. package/src/ui/scroll-area.stories.tsx +51 -0
  113. package/src/ui/textarea.stories.tsx +77 -0
  114. package/src/ui/textarea.tsx +1 -1
  115. package/src/ui/tooltip.stories.tsx +1 -1
  116. package/src/ui/tooltip.tsx +59 -13
  117. package/src/utils/cn.ts +19 -1
  118. package/theme.css +76 -43
  119. package/src/ui/dialog.tsx +0 -21
@@ -0,0 +1,76 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kitn-reasoning': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ const sampleText =
16
+ 'First I parse the request, then I plan the steps, then I execute and verify each one before responding.';
17
+
18
+ /** Render the actual `<kitn-reasoning>` custom element with a `text` property. */
19
+ function ReasoningElement(props: { text: string; streaming?: boolean }) {
20
+ let el: (HTMLElement & { text?: string; streaming?: boolean }) | undefined;
21
+ onMount(() => {
22
+ if (el) {
23
+ el.text = props.text;
24
+ if (props.streaming) el.streaming = true;
25
+ }
26
+ });
27
+ return (
28
+ <kitn-reasoning ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
29
+ );
30
+ }
31
+
32
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
33
+ <kitn-reasoning id="reason" label="Reasoning"></kitn-reasoning>
34
+
35
+ <script type="module">
36
+ import '@kitnai/chat/elements'; // registers the custom elements
37
+
38
+ const reason = document.getElementById('reason');
39
+ reason.text = 'First I parse the request, then I plan the steps, then I execute.';
40
+ // reason.streaming = true; // auto-expands while a thought streams in
41
+
42
+ // events are CustomEvents on the element (they do not bubble)
43
+ reason.addEventListener('openchange', (e) => console.log('open:', e.detail.open));
44
+ </script>`;
45
+
46
+ const meta = {
47
+ title: 'Web Components/kitn-reasoning',
48
+ tags: ['autodocs'],
49
+ parameters: {
50
+ layout: 'fullscreen',
51
+ docs: {
52
+ description: {
53
+ component: [
54
+ '`<kitn-reasoning>` is the framework-agnostic **web component** for a collapsible reasoning/thinking block that auto-expands while a thought is `streaming`, isolated in **Shadow DOM**.',
55
+ '**When to use:** surfacing model chain-of-thought in a non-Solid app. In SolidJS, compose the `Reasoning` primitives directly.',
56
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the body via the `text` **property**, set the `streaming` flag while it streams in, optionally drive the controlled `open` property, and listen for the `openchange` **CustomEvent**.",
57
+ 'See the **Code** tab for HTML usage.',
58
+ ].join('\n\n'),
59
+ },
60
+ },
61
+ },
62
+ } satisfies Meta;
63
+
64
+ export default meta;
65
+ type Story = StoryObj;
66
+
67
+ /** A collapsed reasoning block (the trigger toggles it). */
68
+ export const Default: Story = {
69
+ render: () => <ReasoningElement text={sampleText} />,
70
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
71
+ };
72
+
73
+ /** With `streaming` set, the block auto-expands. */
74
+ export const Streaming: Story = {
75
+ render: () => <ReasoningElement text={sampleText} streaming />,
76
+ };
@@ -0,0 +1,79 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kitn-response-stream': JSX.HTMLAttributes<HTMLElement> & {
11
+ mode?: string;
12
+ speed?: number;
13
+ };
14
+ }
15
+ }
16
+ }
17
+
18
+ const STREAM_TEXT =
19
+ "This text reveals with a typewriter animation, streamed character by character — exactly how you'd render a live assistant reply.";
20
+
21
+ /** Render `<kitn-response-stream>` with the `text` set as a JS property. */
22
+ function StreamElement(props: { text: string; mode?: string; speed?: number }) {
23
+ let el: (HTMLElement & { text?: string }) | undefined;
24
+ onMount(() => {
25
+ if (el) el.text = props.text;
26
+ });
27
+ return (
28
+ <kitn-response-stream
29
+ ref={(e) => (el = e as HTMLElement)}
30
+ mode={props.mode}
31
+ speed={props.speed}
32
+ style={{ display: 'block', padding: '24px', 'max-width': '640px', 'line-height': 1.6 }}
33
+ />
34
+ );
35
+ }
36
+
37
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
38
+ <kitn-response-stream id="stream" mode="typewriter" speed="20"></kitn-response-stream>
39
+
40
+ <script type="module">
41
+ import '@kitnai/chat/elements'; // registers the custom elements
42
+
43
+ const stream = document.getElementById('stream');
44
+ // text can be a string, or an AsyncIterable<string> for live streaming
45
+ stream.text = "Hello, this reveals one character at a time…";
46
+ stream.addEventListener('complete', () => console.log('done'));
47
+ </script>`;
48
+
49
+ const meta = {
50
+ title: 'Web Components/kitn-response-stream',
51
+ tags: ['autodocs'],
52
+ parameters: {
53
+ layout: 'fullscreen',
54
+ docs: {
55
+ description: {
56
+ component: [
57
+ '`<kitn-response-stream>` is the framework-agnostic **web component** that reveals text with a typewriter or fade animation — the building block for streamed assistant replies, isolated in **Shadow DOM**.',
58
+ '**When to use:** animating a response as it arrives. Pass a finished string to replay an animation, or an `AsyncIterable<string>` to drive it from a live stream. In SolidJS, use the `ResponseStream` primitive.',
59
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the `text` **property** (string or async iterable), tune `mode` (`typewriter` / `fade`) and `speed`, and listen for the `complete` **CustomEvent**.",
60
+ 'See the **Code** tab for HTML usage.',
61
+ ].join('\n\n'),
62
+ },
63
+ },
64
+ },
65
+ } satisfies Meta;
66
+
67
+ export default meta;
68
+ type Story = StoryObj;
69
+
70
+ /** Typewriter reveal (the default). */
71
+ export const Typewriter: Story = {
72
+ render: () => <StreamElement text={STREAM_TEXT} mode="typewriter" speed={20} />,
73
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
74
+ };
75
+
76
+ /** Fade-in reveal, segment by segment. */
77
+ export const Fade: Story = {
78
+ render: () => <StreamElement text={STREAM_TEXT} mode="fade" speed={10} />,
79
+ };
@@ -0,0 +1,77 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kitn-source-list': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ interface SourceItem {
16
+ href: string;
17
+ title?: string;
18
+ description?: string;
19
+ label?: string;
20
+ showFavicon?: boolean;
21
+ }
22
+
23
+ const sampleSources: SourceItem[] = [
24
+ { href: 'https://kitn.dev', title: 'kitn — the kit', description: 'Composable SolidJS + web-component chat UI.', showFavicon: true },
25
+ { href: 'https://solidjs.com', title: 'SolidJS', description: 'A reactive UI library.', showFavicon: true },
26
+ ];
27
+
28
+ /** Render the actual `<kitn-source-list>` custom element with a `sources` property. */
29
+ function SourceListElement() {
30
+ let el: (HTMLElement & { sources?: SourceItem[] }) | undefined;
31
+ onMount(() => {
32
+ if (el) el.sources = sampleSources;
33
+ });
34
+ return (
35
+ <kitn-source-list ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
36
+ );
37
+ }
38
+
39
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
40
+ <kitn-source-list id="srcs" show-favicon></kitn-source-list>
41
+
42
+ <script type="module">
43
+ import '@kitnai/chat/elements'; // registers the custom elements
44
+
45
+ const srcs = document.getElementById('srcs');
46
+ srcs.sources = [
47
+ { href: 'https://kitn.dev', title: 'kitn — the kit', description: 'Composable chat UI.' },
48
+ { href: 'https://solidjs.com', title: 'SolidJS', description: 'A reactive UI library.' },
49
+ ];
50
+ </script>`;
51
+
52
+ const meta = {
53
+ title: 'Web Components/kitn-source-list',
54
+ tags: ['autodocs'],
55
+ parameters: {
56
+ layout: 'fullscreen',
57
+ docs: {
58
+ description: {
59
+ component: [
60
+ '`<kitn-source-list>` is the framework-agnostic **web component** for a wrapped row of citation links (each with its own hover-card preview), isolated in **Shadow DOM**.',
61
+ '**When to use:** showing the sources behind an assistant answer in a non-Solid app. For a single citation, use `<kitn-source>`; in SolidJS, compose `SourceList` + `Source`.',
62
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the data via the `sources` **property** (each item: `href`, `title`, `description`, `label`, `showFavicon`), and set `show-favicon` to enable favicons for all items (a per-item `showFavicon` overrides it).",
63
+ 'See the **Code** tab for HTML usage.',
64
+ ].join('\n\n'),
65
+ },
66
+ },
67
+ },
68
+ } satisfies Meta;
69
+
70
+ export default meta;
71
+ type Story = StoryObj;
72
+
73
+ /** Two citations rendered as a wrapped list with favicons. */
74
+ export const Default: Story = {
75
+ render: () => <SourceListElement />,
76
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
77
+ };
@@ -0,0 +1,87 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kitn-source': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ /** Render the actual `<kitn-source>` custom element configured by attributes. */
16
+ function SourceElement(props: {
17
+ href: string;
18
+ label?: string;
19
+ headline?: string;
20
+ description?: string;
21
+ showFavicon?: boolean;
22
+ }) {
23
+ let el: HTMLElement | undefined;
24
+ onMount(() => {
25
+ if (!el) return;
26
+ el.setAttribute('href', props.href);
27
+ if (props.label) el.setAttribute('label', props.label);
28
+ if (props.headline) el.setAttribute('headline', props.headline);
29
+ if (props.description) el.setAttribute('description', props.description);
30
+ if (props.showFavicon) el.setAttribute('show-favicon', '');
31
+ });
32
+ return <kitn-source ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '16px' }} />;
33
+ }
34
+
35
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
36
+ <kitn-source
37
+ href="https://kitn.dev"
38
+ label="kitn"
39
+ headline="kitn — the kit"
40
+ description="Composable SolidJS + web-component chat UI."
41
+ show-favicon
42
+ ></kitn-source>
43
+
44
+ <script type="module">
45
+ import '@kitnai/chat/elements'; // registers the custom elements
46
+ </script>`;
47
+
48
+ const meta = {
49
+ title: 'Web Components/kitn-source',
50
+ tags: ['autodocs'],
51
+ parameters: {
52
+ layout: 'fullscreen',
53
+ docs: {
54
+ description: {
55
+ component: [
56
+ '`<kitn-source>` is the framework-agnostic **web component** for a single citation link with a hover-card preview, isolated in **Shadow DOM**.',
57
+ '**When to use:** inlining a single source citation in a non-Solid app. For multiple sources, use `<kitn-source-list>`; in SolidJS, compose the `Source` primitives.',
58
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, then set `href` (the link, also the default label/favicon source), `label`, `headline` (the hover headline — note `headline`, not `title`), `description`, and the `show-favicon` flag via attributes.",
59
+ 'See the **Code** tab for HTML usage.',
60
+ ].join('\n\n'),
61
+ },
62
+ },
63
+ },
64
+ } satisfies Meta;
65
+
66
+ export default meta;
67
+ type Story = StoryObj;
68
+
69
+ /** A citation with a custom label, hover headline/description, and favicon. */
70
+ export const Default: Story = {
71
+ render: () => (
72
+ <SourceElement
73
+ href="https://kitn.dev"
74
+ label="kitn"
75
+ headline="kitn — the kit"
76
+ description="Composable SolidJS + web-component chat UI."
77
+ showFavicon
78
+ />
79
+ ),
80
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
81
+ };
82
+
83
+ /** With no `label`, the trigger falls back to the domain. */
84
+ export const DomainLabel: Story = {
85
+ name: 'Domain Label',
86
+ render: () => <SourceElement href="https://solidjs.com" description="A reactive UI library." />,
87
+ };
@@ -0,0 +1,63 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import './register'; // side effect: registers the custom elements
3
+
4
+ // The web components are custom DOM elements, so declare the tags for JSX.
5
+ declare module 'solid-js' {
6
+ // eslint-disable-next-line @typescript-eslint/no-namespace
7
+ namespace JSX {
8
+ interface IntrinsicElements {
9
+ 'kitn-text-shimmer': JSX.HTMLAttributes<HTMLElement> & {
10
+ text?: string;
11
+ duration?: number;
12
+ spread?: number;
13
+ };
14
+ }
15
+ }
16
+ }
17
+
18
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
19
+ <kitn-text-shimmer text="Thinking…" duration="3" spread="20"></kitn-text-shimmer>
20
+
21
+ <script type="module">
22
+ import '@kitnai/chat/elements'; // registers the custom elements
23
+ </script>`;
24
+
25
+ const meta = {
26
+ title: 'Web Components/kitn-text-shimmer',
27
+ tags: ['autodocs'],
28
+ parameters: {
29
+ layout: 'fullscreen',
30
+ docs: {
31
+ description: {
32
+ component: [
33
+ '`<kitn-text-shimmer>` is the framework-agnostic **web component** for animated shimmering text — a gradient sweep across a label, isolated in **Shadow DOM**.',
34
+ '**When to use:** signalling a quiet, in-progress state ("Thinking…", "Generating…") inline. In SolidJS, use the `TextShimmer` primitive.',
35
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the `text` attribute, and tune `duration` (seconds) and `spread` (gradient width, 5–45).",
36
+ 'See the **Code** tab for HTML usage.',
37
+ ].join('\n\n'),
38
+ },
39
+ },
40
+ },
41
+ } satisfies Meta;
42
+
43
+ export default meta;
44
+ type Story = StoryObj;
45
+
46
+ /** Default shimmer. */
47
+ export const Default: Story = {
48
+ render: () => (
49
+ <div style={{ padding: '24px', 'font-size': '18px' }}>
50
+ <kitn-text-shimmer text="Thinking…" />
51
+ </div>
52
+ ),
53
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
54
+ };
55
+
56
+ /** Faster sweep with a wider gradient spread. */
57
+ export const Tuned: Story = {
58
+ render: () => (
59
+ <div style={{ padding: '24px', 'font-size': '18px' }}>
60
+ <kitn-text-shimmer text="Generating response…" duration={2} spread={35} />
61
+ </div>
62
+ ),
63
+ };
@@ -0,0 +1,72 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import './register'; // side effect: registers the custom elements
3
+
4
+ // The web components are custom DOM elements, so declare the tags for JSX.
5
+ declare module 'solid-js' {
6
+ // eslint-disable-next-line @typescript-eslint/no-namespace
7
+ namespace JSX {
8
+ interface IntrinsicElements {
9
+ 'kitn-thinking-bar': JSX.HTMLAttributes<HTMLElement> & {
10
+ text?: string;
11
+ stoppable?: boolean | string;
12
+ 'stop-label'?: string;
13
+ 'on:stop'?: (e: CustomEvent) => void;
14
+ };
15
+ }
16
+ }
17
+ }
18
+
19
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
20
+ <kitn-thinking-bar text="Thinking" stoppable stop-label="Answer now"></kitn-thinking-bar>
21
+
22
+ <script type="module">
23
+ import '@kitnai/chat/elements'; // registers the custom elements
24
+
25
+ document.querySelector('kitn-thinking-bar')
26
+ .addEventListener('stop', () => console.log('user asked to stop'));
27
+ </script>`;
28
+
29
+ const meta = {
30
+ title: 'Web Components/kitn-thinking-bar',
31
+ tags: ['autodocs'],
32
+ parameters: {
33
+ layout: 'fullscreen',
34
+ docs: {
35
+ description: {
36
+ component: [
37
+ '`<kitn-thinking-bar>` is the framework-agnostic **web component** for an animated "thinking" indicator with an optional stop affordance — a pure leaf element isolated in **Shadow DOM**. (`<kitn-chat>` does not surface this; compose it yourself.)',
38
+ '**When to use:** showing that the assistant is reasoning, optionally letting the user interrupt with "Answer now". In SolidJS, use the `ThinkingBar` primitive.',
39
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the `text`/`stop-label` attributes, add the `stoppable` flag to show the stop button, and listen for the `stop` **CustomEvent**.",
40
+ 'See the **Code** tab for HTML usage.',
41
+ ].join('\n\n'),
42
+ },
43
+ },
44
+ },
45
+ } satisfies Meta;
46
+
47
+ export default meta;
48
+ type Story = StoryObj;
49
+
50
+ /** A plain thinking indicator. */
51
+ export const Default: Story = {
52
+ render: () => (
53
+ <div style={{ padding: '24px' }}>
54
+ <kitn-thinking-bar text="Thinking" />
55
+ </div>
56
+ ),
57
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
58
+ };
59
+
60
+ /** Stoppable — shows an "Answer now" affordance that fires a `stop` event. */
61
+ export const Stoppable: Story = {
62
+ render: () => (
63
+ <div style={{ padding: '24px' }}>
64
+ <kitn-thinking-bar
65
+ text="Reasoning"
66
+ stoppable
67
+ stop-label="Answer now"
68
+ on:stop={() => console.log('stop')}
69
+ />
70
+ </div>
71
+ ),
72
+ };
@@ -0,0 +1,88 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+ import type { ToolPart } from '../components/tool';
5
+
6
+ // The web components are custom DOM elements, so declare the tags for JSX.
7
+ declare module 'solid-js' {
8
+ // eslint-disable-next-line @typescript-eslint/no-namespace
9
+ namespace JSX {
10
+ interface IntrinsicElements {
11
+ 'kitn-tool': JSX.HTMLAttributes<HTMLElement>;
12
+ }
13
+ }
14
+ }
15
+
16
+ const completedTool: ToolPart = {
17
+ type: 'database_query',
18
+ state: 'output-available',
19
+ input: { table: 'users', limit: 10 },
20
+ output: { rows: 10, ms: 42 },
21
+ };
22
+
23
+ const runningTool: ToolPart = {
24
+ type: 'search',
25
+ state: 'input-available',
26
+ input: { query: 'kitn docs' },
27
+ };
28
+
29
+ /** Render the actual `<kitn-tool>` custom element with a `tool` property. */
30
+ function ToolElement(props: { tool: ToolPart; open?: boolean }) {
31
+ let el: (HTMLElement & { tool?: ToolPart; open?: boolean }) | undefined;
32
+ onMount(() => {
33
+ if (el) {
34
+ el.tool = props.tool;
35
+ if (props.open) el.open = true;
36
+ }
37
+ });
38
+ return (
39
+ <kitn-tool ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
40
+ );
41
+ }
42
+
43
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
44
+ <kitn-tool id="tool" open></kitn-tool>
45
+
46
+ <script type="module">
47
+ import '@kitnai/chat/elements'; // registers the custom elements
48
+
49
+ const tool = document.getElementById('tool');
50
+ tool.tool = {
51
+ type: 'database_query',
52
+ state: 'output-available',
53
+ input: { table: 'users', limit: 10 },
54
+ output: { rows: 10, ms: 42 },
55
+ };
56
+ </script>`;
57
+
58
+ const meta = {
59
+ title: 'Web Components/kitn-tool',
60
+ tags: ['autodocs'],
61
+ parameters: {
62
+ layout: 'fullscreen',
63
+ docs: {
64
+ description: {
65
+ component: [
66
+ '`<kitn-tool>` is the framework-agnostic **web component** for a single tool-call panel — a collapsible input/output inspector with a state badge — isolated in **Shadow DOM**.',
67
+ '**When to use:** rendering an agent/tool-call trace in a non-Solid app. In SolidJS, use the `Tool` primitive directly.',
68
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the call via the `tool` **property** (`el.tool = {...}`), and add the `open` flag to start it expanded.",
69
+ 'See the **Code** tab for HTML usage.',
70
+ ].join('\n\n'),
71
+ },
72
+ },
73
+ },
74
+ } satisfies Meta;
75
+
76
+ export default meta;
77
+ type Story = StoryObj;
78
+
79
+ /** A completed call with input and output, started expanded. */
80
+ export const Completed: Story = {
81
+ render: () => <ToolElement tool={completedTool} open />,
82
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
83
+ };
84
+
85
+ /** A call still awaiting output (collapsed). */
86
+ export const Running: Story = {
87
+ render: () => <ToolElement tool={runningTool} />,
88
+ };
@@ -0,0 +1,87 @@
1
+ import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
+ import { onMount } from 'solid-js';
3
+ import './register'; // side effect: registers the custom elements
4
+
5
+ // The web components are custom DOM elements, so declare the tags for JSX.
6
+ declare module 'solid-js' {
7
+ // eslint-disable-next-line @typescript-eslint/no-namespace
8
+ namespace JSX {
9
+ interface IntrinsicElements {
10
+ 'kitn-voice-input': JSX.HTMLAttributes<HTMLElement> & {
11
+ disabled?: boolean | string;
12
+ };
13
+ }
14
+ }
15
+ }
16
+
17
+ /** Render `<kitn-voice-input>` with a stub `transcribe` function-property. */
18
+ function VoiceElement(props: { disabled?: boolean }) {
19
+ let el: (HTMLElement & { transcribe?: (audio: Blob) => Promise<string> }) | undefined;
20
+ onMount(() => {
21
+ if (!el) return;
22
+ // transcribe MUST be set as a JS property — a value-returning callback
23
+ // can't be modelled as an attribute.
24
+ el.transcribe = async () => {
25
+ await new Promise((r) => setTimeout(r, 400));
26
+ return 'transcribed text';
27
+ };
28
+ el.addEventListener('transcription', (e) => {
29
+ const ev = e as CustomEvent<{ text: string }>;
30
+ console.log('transcription', ev.detail.text);
31
+ });
32
+ });
33
+ return (
34
+ <kitn-voice-input
35
+ ref={(e) => (el = e as HTMLElement)}
36
+ disabled={props.disabled ? true : undefined}
37
+ style={{ display: 'inline-block', padding: '40px' }}
38
+ />
39
+ );
40
+ }
41
+
42
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
43
+ <kitn-voice-input id="voice"></kitn-voice-input>
44
+
45
+ <script type="module">
46
+ import '@kitnai/chat/elements'; // registers the custom elements
47
+
48
+ const voice = document.getElementById('voice');
49
+ // transcribe is a FUNCTION property — your async transcriber
50
+ voice.transcribe = async (blob) => {
51
+ const text = await myTranscriptionApi(blob);
52
+ return text;
53
+ };
54
+ voice.addEventListener('transcription', (e) => console.log(e.detail.text));
55
+ </script>`;
56
+
57
+ const meta = {
58
+ title: 'Web Components/kitn-voice-input',
59
+ tags: ['autodocs'],
60
+ parameters: {
61
+ layout: 'fullscreen',
62
+ docs: {
63
+ description: {
64
+ component: [
65
+ '`<kitn-voice-input>` is the framework-agnostic **web component** for a mic button that records and transcribes audio — isolated in **Shadow DOM**. It is the canonical **function-property** element.',
66
+ '**When to use:** adding voice dictation to an input in a non-Solid app. In SolidJS, use the `VoiceInput` primitive.',
67
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, then set the `transcribe` **function property** (`el.transcribe = async blob => '...'`) — a value-returning callback can't be modelled as an event. It also emits `audiocaptured` (raw blob) and `transcription` (text) **CustomEvents**.",
68
+ 'See the **Code** tab for HTML usage.',
69
+ ].join('\n\n'),
70
+ },
71
+ },
72
+ },
73
+ } satisfies Meta;
74
+
75
+ export default meta;
76
+ type Story = StoryObj;
77
+
78
+ /** A working mic button wired to a stub transcriber. */
79
+ export const Default: Story = {
80
+ render: () => <VoiceElement />,
81
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
82
+ };
83
+
84
+ /** A disabled mic button (non-interactive). */
85
+ export const Disabled: Story = {
86
+ render: () => <VoiceElement disabled />,
87
+ };
@@ -0,0 +1,25 @@
1
+ import { defineKitnElement } from './define';
2
+ import { Loader, type LoaderVariant, type LoaderSize } from '../components/loader';
3
+
4
+ interface Props extends Record<string, unknown> {
5
+ /** The animation style: `'circular' | 'classic' | 'pulse' | 'pulse-dot' |
6
+ * 'dots' | 'typing' | 'wave' | 'bars' | 'terminal' | 'text-blink' |
7
+ * 'text-shimmer' | 'loading-dots'`. Defaults to `'circular'`. */
8
+ variant?: LoaderVariant;
9
+ /** Loader size: `'sm' | 'md' | 'lg'`. Defaults to `'md'`. */
10
+ size?: LoaderSize;
11
+ /** Label for the text-based variants. */
12
+ text?: string;
13
+ }
14
+
15
+ /**
16
+ * `<kitn-loader>` — an animated loader. `variant` selects the style (circular,
17
+ * dots, wave, text-shimmer, …); `size` and `text` are attributes.
18
+ */
19
+ defineKitnElement<Props>('kitn-loader', {
20
+ variant: 'circular',
21
+ size: 'md',
22
+ text: undefined,
23
+ }, (props) => (
24
+ <Loader variant={props.variant} size={props.size} text={props.text} />
25
+ ));