@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,71 @@
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-checkpoint': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ /** Render the actual `<kitn-checkpoint>` custom element configured by attributes. */
16
+ function CheckpointElement(props: { label?: string; tooltip?: string; variant?: string; size?: string }) {
17
+ let el: HTMLElement | undefined;
18
+ onMount(() => {
19
+ if (!el) return;
20
+ if (props.label) el.setAttribute('label', props.label);
21
+ if (props.tooltip) el.setAttribute('tooltip', props.tooltip);
22
+ if (props.variant) el.setAttribute('variant', props.variant);
23
+ if (props.size) el.setAttribute('size', props.size);
24
+ el.addEventListener('select', () => console.log('checkpoint selected'));
25
+ });
26
+ return <kitn-checkpoint ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '16px' }} />;
27
+ }
28
+
29
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
30
+ <kitn-checkpoint label="Restore" tooltip="Restore this checkpoint" variant="outline" size="sm"></kitn-checkpoint>
31
+
32
+ <script type="module">
33
+ import '@kitnai/chat/elements'; // registers the custom elements
34
+
35
+ const cp = document.querySelector('kitn-checkpoint');
36
+ // events are CustomEvents on the element (they do not bubble)
37
+ cp.addEventListener('select', () => console.log('restore checkpoint'));
38
+ </script>`;
39
+
40
+ const meta = {
41
+ title: 'Web Components/kitn-checkpoint',
42
+ tags: ['autodocs'],
43
+ parameters: {
44
+ layout: 'fullscreen',
45
+ docs: {
46
+ description: {
47
+ component: [
48
+ '`<kitn-checkpoint>` is the framework-agnostic **web component** for a bookmark/checkpoint button (with an optional tooltip and label), isolated in **Shadow DOM**.',
49
+ '**When to use:** marking a restore point in a conversation in a non-Solid app. In SolidJS, compose the `Checkpoint` primitives.',
50
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set `label`, `tooltip`, `variant` (`ghost` | `default` | `outline`), and `size` via attributes, and listen for the `select` **CustomEvent** on click.",
51
+ 'See the **Code** tab for HTML usage.',
52
+ ].join('\n\n'),
53
+ },
54
+ },
55
+ },
56
+ } satisfies Meta;
57
+
58
+ export default meta;
59
+ type Story = StoryObj;
60
+
61
+ /** A labeled checkpoint button. (Add a `tooltip` attribute for a hover hint — see the Code tab.) */
62
+ export const Labeled: Story = {
63
+ render: () => <CheckpointElement label="Restore" variant="outline" />,
64
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
65
+ };
66
+
67
+ /** Icon-only (no label), using an `icon` size. */
68
+ export const IconOnly: Story = {
69
+ name: 'Icon Only',
70
+ render: () => <CheckpointElement size="icon-sm" />,
71
+ };
@@ -0,0 +1,82 @@
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-code-block': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ const sampleCode = `export function add(a: number, b: number): number {
16
+ return a + b;
17
+ }`;
18
+
19
+ const pythonCode = `def fib(n):
20
+ a, b = 0, 1
21
+ for _ in range(n):
22
+ a, b = b, a + b
23
+ return a`;
24
+
25
+ /** Render the actual `<kitn-code-block>` custom element with a `code` property. */
26
+ function CodeBlockElement(props: { code: string; language?: string }) {
27
+ let el: (HTMLElement & { code?: string }) | undefined;
28
+ onMount(() => {
29
+ if (el) {
30
+ el.code = props.code;
31
+ if (props.language) el.setAttribute('language', props.language);
32
+ }
33
+ });
34
+ return (
35
+ <kitn-code-block
36
+ ref={(e) => (el = e as HTMLElement)}
37
+ style={{ display: 'block', padding: '16px', 'max-width': '720px' }}
38
+ />
39
+ );
40
+ }
41
+
42
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
43
+ <kitn-code-block id="code" language="ts" code-theme="github-dark-dimmed"></kitn-code-block>
44
+
45
+ <script type="module">
46
+ import '@kitnai/chat/elements'; // registers the custom elements
47
+
48
+ const code = document.getElementById('code');
49
+ code.code = 'export function add(a, b) {\\n return a + b;\\n}';
50
+ </script>`;
51
+
52
+ const meta = {
53
+ title: 'Web Components/kitn-code-block',
54
+ tags: ['autodocs'],
55
+ parameters: {
56
+ layout: 'fullscreen',
57
+ docs: {
58
+ description: {
59
+ component: [
60
+ '`<kitn-code-block>` is the framework-agnostic **web component** for a single syntax-highlighted code block, complete with a copy button, isolated in **Shadow DOM**.',
61
+ '**When to use:** dropping a highlighted snippet into a non-Solid app. In SolidJS, compose `CodeBlock` + `CodeBlockCode` directly.',
62
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the source via the `code` **property** (`el.code = '...'`), and pick a grammar with the `language` attribute (defaults to `tsx`). Tune highlighting with `code-theme` / `code-highlight`.",
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
+ /** A TypeScript snippet (the default `tsx` grammar). */
74
+ export const TypeScript: Story = {
75
+ render: () => <CodeBlockElement code={sampleCode} language="ts" />,
76
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
77
+ };
78
+
79
+ /** A different grammar via the `language` attribute. */
80
+ export const Python: Story = {
81
+ render: () => <CodeBlockElement code={pythonCode} language="python" />,
82
+ };
@@ -0,0 +1,85 @@
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
+ interface ContextUsage {
6
+ usedTokens: number;
7
+ maxTokens: number;
8
+ inputTokens?: number;
9
+ outputTokens?: number;
10
+ reasoningTokens?: number;
11
+ cacheTokens?: number;
12
+ estimatedCost?: number;
13
+ }
14
+
15
+ // The web components are custom DOM elements, so declare the tags for JSX.
16
+ declare module 'solid-js' {
17
+ // eslint-disable-next-line @typescript-eslint/no-namespace
18
+ namespace JSX {
19
+ interface IntrinsicElements {
20
+ 'kitn-context-meter': JSX.HTMLAttributes<HTMLElement>;
21
+ }
22
+ }
23
+ }
24
+
25
+ const usage: ContextUsage = {
26
+ usedTokens: 48200,
27
+ maxTokens: 200000,
28
+ inputTokens: 31000,
29
+ outputTokens: 17200,
30
+ reasoningTokens: 4200,
31
+ cacheTokens: 8000,
32
+ estimatedCost: 0.42,
33
+ };
34
+
35
+ /** Render `<kitn-context-meter>` with the `context` set as a JS property. */
36
+ function MeterElement(props: { context: ContextUsage }) {
37
+ let el: (HTMLElement & { context?: ContextUsage }) | undefined;
38
+ onMount(() => {
39
+ if (el) el.context = props.context;
40
+ });
41
+ return (
42
+ <kitn-context-meter ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '40px' }} />
43
+ );
44
+ }
45
+
46
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
47
+ <kitn-context-meter id="ctx"></kitn-context-meter>
48
+
49
+ <script type="module">
50
+ import '@kitnai/chat/elements'; // registers the custom elements
51
+
52
+ document.getElementById('ctx').context = {
53
+ usedTokens: 48200, maxTokens: 200000,
54
+ inputTokens: 31000, outputTokens: 17200,
55
+ reasoningTokens: 4200, cacheTokens: 8000,
56
+ estimatedCost: 0.42,
57
+ };
58
+ </script>`;
59
+
60
+ const meta = {
61
+ title: 'Web Components/kitn-context-meter',
62
+ tags: ['autodocs'],
63
+ parameters: {
64
+ layout: 'fullscreen',
65
+ docs: {
66
+ description: {
67
+ component: [
68
+ '`<kitn-context-meter>` is the framework-agnostic **web component** for a token/context-window usage meter — a compact gauge with a hover-card breakdown (input / output / reasoning / cache + estimated cost) — isolated in **Shadow DOM**.',
69
+ '**When to use:** showing how much of the context window a conversation is using, typically in a chat header. In SolidJS, compose the `Context` primitives.',
70
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, then set the `context` **property** with the usage object. Hover the meter to reveal the breakdown.",
71
+ 'See the **Code** tab for HTML usage.',
72
+ ].join('\n\n'),
73
+ },
74
+ },
75
+ },
76
+ } satisfies Meta;
77
+
78
+ export default meta;
79
+ type Story = StoryObj;
80
+
81
+ /** A meter at ~24% usage; hover to reveal the full breakdown. */
82
+ export const Default: Story = {
83
+ render: () => <MeterElement context={usage} />,
84
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
85
+ };
@@ -0,0 +1,110 @@
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-empty': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
16
+ <kitn-empty
17
+ empty-title="No conversations yet"
18
+ description="Start a new chat to see it appear here."
19
+ >
20
+ <!-- Route 2 slots: your own icon and actions -->
21
+ <svg slot="media" width="28" height="28" viewBox="0 0 24 24" fill="none"
22
+ stroke="currentColor" stroke-width="2">
23
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
24
+ </svg>
25
+
26
+ <!-- Slotted actions are light DOM, so style them with your own page CSS
27
+ (a ghost button with a + icon, here styled inline for portability). -->
28
+ <button
29
+ style="display:inline-flex; align-items:center; gap:.45rem;
30
+ font:500 13.5px/1 system-ui,sans-serif; padding:.5rem .85rem;
31
+ border-radius:9px; border:1px solid var(--color-border, #e5e5e5);
32
+ background:transparent; color:inherit; cursor:pointer;"
33
+ >
34
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none"
35
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
36
+ <path d="M5 12h14" /><path d="M12 5v14" />
37
+ </svg>
38
+ New chat
39
+ </button>
40
+ </kitn-empty>
41
+
42
+ <script type="module">
43
+ import '@kitnai/chat/elements'; // registers the custom elements
44
+ </script>`;
45
+
46
+ const meta = {
47
+ title: 'Web Components/kitn-empty',
48
+ tags: ['autodocs'],
49
+ parameters: {
50
+ layout: 'fullscreen',
51
+ docs: {
52
+ description: {
53
+ component: [
54
+ '`<kitn-empty>` is the framework-agnostic **web component** for an empty-state block — an icon, a title, a description, and actions — isolated in **Shadow DOM**.',
55
+ '**When to use:** placeholder UI for an empty list/thread in a non-Solid app. In SolidJS, compose the `Empty*` primitives.',
56
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set `empty-title` (note `empty-title`, not `title`) and `description` via attributes, and use the **slots** (\"Route 2\") to project your own icon (`slot=\"media\"`) and actions (the default slot).",
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
+ /** Render the actual `<kitn-empty>` custom element with slotted children. */
68
+ function EmptyElement() {
69
+ let el: HTMLElement | undefined;
70
+ onMount(() => {
71
+ if (!el) return;
72
+ el.setAttribute('empty-title', 'No conversations yet');
73
+ el.setAttribute('description', 'Start a new chat to see it appear here.');
74
+ });
75
+ return (
76
+ <kitn-empty ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '24px' }}>
77
+ <svg slot="media" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
78
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
79
+ </svg>
80
+ {/* Slotted actions are light DOM — style them with the host page's own
81
+ CSS. Here a ghost "+ New chat" button, styled inline so it renders
82
+ correctly in any context (mirrors the composable example's pill-btn). */}
83
+ <button
84
+ style={{
85
+ display: 'inline-flex',
86
+ 'align-items': 'center',
87
+ gap: '0.45rem',
88
+ font: '500 13.5px/1 system-ui, sans-serif',
89
+ padding: '0.5rem 0.85rem',
90
+ 'border-radius': '9px',
91
+ border: '1px solid var(--color-border, #e5e5e5)',
92
+ background: 'transparent',
93
+ color: 'inherit',
94
+ cursor: 'pointer',
95
+ }}
96
+ >
97
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
98
+ <path d="M5 12h14" /><path d="M12 5v14" />
99
+ </svg>
100
+ New chat
101
+ </button>
102
+ </kitn-empty>
103
+ );
104
+ }
105
+
106
+ /** An empty state with a slotted icon and an action button. */
107
+ export const Default: Story = {
108
+ render: () => <EmptyElement />,
109
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
110
+ };
@@ -0,0 +1,73 @@
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-feedback-bar': JSX.HTMLAttributes<HTMLElement> & {
10
+ 'bar-title'?: string;
11
+ 'on:helpful'?: (e: CustomEvent) => void;
12
+ 'on:nothelpful'?: (e: CustomEvent) => void;
13
+ 'on:close'?: (e: CustomEvent) => void;
14
+ };
15
+ }
16
+ }
17
+ }
18
+
19
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
20
+ <kitn-feedback-bar bar-title="Was this helpful?"></kitn-feedback-bar>
21
+
22
+ <script type="module">
23
+ import '@kitnai/chat/elements'; // registers the custom elements
24
+
25
+ const bar = document.querySelector('kitn-feedback-bar');
26
+ bar.addEventListener('helpful', () => console.log('👍'));
27
+ bar.addEventListener('nothelpful', () => console.log('👎'));
28
+ bar.addEventListener('close', () => bar.remove());
29
+ </script>`;
30
+
31
+ const meta = {
32
+ title: 'Web Components/kitn-feedback-bar',
33
+ tags: ['autodocs'],
34
+ parameters: {
35
+ layout: 'fullscreen',
36
+ docs: {
37
+ description: {
38
+ component: [
39
+ '`<kitn-feedback-bar>` is the framework-agnostic **web component** for an inline thumbs up/down feedback banner with a dismiss button — isolated in **Shadow DOM**.',
40
+ '**When to use:** collecting a quick reaction after an answer or a completed task. In SolidJS, use the `FeedbackBar` primitive.',
41
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the label via the `bar-title` attribute (`title` is avoided — it's a global HTML attribute), and listen for the `helpful` / `nothelpful` / `close` **CustomEvents**.",
42
+ 'See the **Code** tab for HTML usage.',
43
+ ].join('\n\n'),
44
+ },
45
+ },
46
+ },
47
+ } satisfies Meta;
48
+
49
+ export default meta;
50
+ type Story = StoryObj;
51
+
52
+ /** The default feedback prompt. */
53
+ export const Default: Story = {
54
+ render: () => (
55
+ <div style={{ padding: '24px', 'max-width': '480px' }}>
56
+ <kitn-feedback-bar
57
+ on:helpful={() => console.log('helpful')}
58
+ on:nothelpful={() => console.log('not helpful')}
59
+ on:close={() => console.log('closed')}
60
+ />
61
+ </div>
62
+ ),
63
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
64
+ };
65
+
66
+ /** A custom label via the `bar-title` attribute. */
67
+ export const CustomTitle: Story = {
68
+ render: () => (
69
+ <div style={{ padding: '24px', 'max-width': '480px' }}>
70
+ <kitn-feedback-bar bar-title="Did this answer your question?" />
71
+ </div>
72
+ ),
73
+ };
@@ -0,0 +1,81 @@
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-file-upload': JSX.HTMLAttributes<HTMLElement> & {
10
+ multiple?: boolean | string;
11
+ accept?: string;
12
+ disabled?: boolean | string;
13
+ label?: string;
14
+ 'on:filesadded'?: (e: CustomEvent<{ files: File[] }>) => void;
15
+ };
16
+ }
17
+ }
18
+ }
19
+
20
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
21
+ <kitn-file-upload accept="image/*" label="Drop images here"></kitn-file-upload>
22
+
23
+ <script type="module">
24
+ import '@kitnai/chat/elements'; // registers the custom elements
25
+
26
+ document.querySelector('kitn-file-upload')
27
+ .addEventListener('filesadded', (e) =>
28
+ console.log(e.detail.files.map((f) => f.name)));
29
+ </script>`;
30
+
31
+ const meta = {
32
+ title: 'Web Components/kitn-file-upload',
33
+ tags: ['autodocs'],
34
+ parameters: {
35
+ layout: 'fullscreen',
36
+ docs: {
37
+ description: {
38
+ component: [
39
+ '`<kitn-file-upload>` is the framework-agnostic **web component** for a click / drag-and-drop file dropzone — isolated in **Shadow DOM**.',
40
+ '**When to use:** accepting file or image uploads in a non-Solid app. In SolidJS, compose the `FileUpload` primitives.',
41
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set the `accept` / `multiple` / `label` attributes, and listen for the `filesadded` **CustomEvent** (`e.detail.files` is a `File[]`). The default dropzone label can be replaced with your own markup via the default `<slot>`.",
42
+ 'See the **Code** tab for HTML usage.',
43
+ ].join('\n\n'),
44
+ },
45
+ },
46
+ },
47
+ } satisfies Meta;
48
+
49
+ export default meta;
50
+ type Story = StoryObj;
51
+
52
+ /** A dropzone accepting any files. */
53
+ export const Default: Story = {
54
+ render: () => (
55
+ <div style={{ padding: '24px', 'max-width': '480px' }}>
56
+ <kitn-file-upload
57
+ on:filesadded={(e: CustomEvent<{ files: File[] }>) =>
58
+ console.log(e.detail.files.map((f) => f.name))}
59
+ />
60
+ </div>
61
+ ),
62
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
63
+ };
64
+
65
+ /** Restricted to images, single file, with a custom label. */
66
+ export const ImagesOnly: Story = {
67
+ render: () => (
68
+ <div style={{ padding: '24px', 'max-width': '480px' }}>
69
+ <kitn-file-upload accept="image/*" multiple={false} label="Click or drop an image" />
70
+ </div>
71
+ ),
72
+ };
73
+
74
+ /** A disabled dropzone (non-interactive). */
75
+ export const Disabled: Story = {
76
+ render: () => (
77
+ <div style={{ padding: '24px', 'max-width': '480px' }}>
78
+ <kitn-file-upload disabled label="Uploads are disabled" />
79
+ </div>
80
+ ),
81
+ };
@@ -0,0 +1,70 @@
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-image': JSX.HTMLAttributes<HTMLElement>;
11
+ }
12
+ }
13
+ }
14
+
15
+ // A small inline SVG, base64-encoded — the same trick the composable example uses.
16
+ const sampleSvg =
17
+ '<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96"><rect width="96" height="96" rx="16" fill="#7c3aed"/><text x="48" y="62" font-size="44" text-anchor="middle" fill="white">★</text></svg>';
18
+ const sampleBase64 = btoa(unescape(encodeURIComponent(sampleSvg)));
19
+
20
+ /** Render the actual `<kitn-image>` custom element with base64 + media-type. */
21
+ function ImageElement() {
22
+ let el: (HTMLElement & { base64?: string; alt?: string }) | undefined;
23
+ onMount(() => {
24
+ if (el) {
25
+ el.base64 = sampleBase64;
26
+ el.alt = 'A purple star';
27
+ el.setAttribute('media-type', 'image/svg+xml');
28
+ }
29
+ });
30
+ return <kitn-image ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '16px' }} />;
31
+ }
32
+
33
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
34
+ <kitn-image id="img" media-type="image/png" alt="A chart"></kitn-image>
35
+
36
+ <script type="module">
37
+ import '@kitnai/chat/elements'; // registers the custom elements
38
+
39
+ const img = document.getElementById('img');
40
+ img.base64 = '<...base64 image data...>';
41
+ // or set raw bytes as a property:
42
+ // img.bytes = new Uint8Array([...]);
43
+ </script>`;
44
+
45
+ const meta = {
46
+ title: 'Web Components/kitn-image',
47
+ tags: ['autodocs'],
48
+ parameters: {
49
+ layout: 'fullscreen',
50
+ docs: {
51
+ description: {
52
+ component: [
53
+ '`<kitn-image>` is the framework-agnostic **web component** that renders a base64 or byte-array image, showing a skeleton fallback while it resolves, isolated in **Shadow DOM**.',
54
+ '**When to use:** displaying model-generated or in-memory images (without a hosted URL) in a non-Solid app. In SolidJS, use the `Image` primitive directly.',
55
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, set `base64` (paired with the `media-type` attribute) or set raw `bytes` as a **property**, and add `alt` text.",
56
+ 'See the **Code** tab for HTML usage.',
57
+ ].join('\n\n'),
58
+ },
59
+ },
60
+ },
61
+ } satisfies Meta;
62
+
63
+ export default meta;
64
+ type Story = StoryObj;
65
+
66
+ /** A base64-encoded SVG with a media type and alt text. */
67
+ export const Default: Story = {
68
+ render: () => <ImageElement />,
69
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
70
+ };
@@ -0,0 +1,87 @@
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-loader': JSX.HTMLAttributes<HTMLElement> & {
10
+ variant?: string;
11
+ size?: string;
12
+ text?: string;
13
+ };
14
+ }
15
+ }
16
+ }
17
+
18
+ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
19
+ <kitn-loader variant="dots" size="md"></kitn-loader>
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-loader',
27
+ tags: ['autodocs'],
28
+ parameters: {
29
+ layout: 'fullscreen',
30
+ docs: {
31
+ description: {
32
+ component: [
33
+ '`<kitn-loader>` is the framework-agnostic **web component** for an animated busy indicator — a dozen styles (circular, dots, wave, bars, text-shimmer, …) selected via the `variant` attribute, isolated in **Shadow DOM**.',
34
+ '**When to use:** showing a small "working" indicator anywhere outside the chat thread (toolbars, buttons, panels). In SolidJS, use the `Loader` primitive directly.',
35
+ "**How to use:** register once with `import '@kitnai/chat/elements'`, then set `variant`, `size`, and (for text variants) `text` as plain HTML attributes.",
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
+ const VARIANTS = [
47
+ 'circular', 'classic', 'pulse', 'pulse-dot', 'dots', 'typing',
48
+ 'wave', 'bars', 'terminal', 'text-blink', 'text-shimmer', 'loading-dots',
49
+ ];
50
+
51
+ /** The default circular spinner. */
52
+ export const Default: Story = {
53
+ render: () => (
54
+ <div style={{ padding: '24px' }}>
55
+ <kitn-loader variant="circular" size="md" />
56
+ </div>
57
+ ),
58
+ parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
59
+ };
60
+
61
+ /** Every variant at the medium size, side by side. */
62
+ export const AllVariants: Story = {
63
+ render: () => (
64
+ <div style={{ display: 'flex', 'flex-wrap': 'wrap', gap: '32px', 'align-items': 'center', padding: '24px' }}>
65
+ {VARIANTS.map((v) => (
66
+ <div style={{ display: 'flex', 'flex-direction': 'column', 'align-items': 'center', gap: '8px', 'min-width': '90px' }}>
67
+ <kitn-loader variant={v} size="md" text="Loading" />
68
+ <code style={{ 'font-size': '11px', opacity: 0.6 }}>{v}</code>
69
+ </div>
70
+ ))}
71
+ </div>
72
+ ),
73
+ };
74
+
75
+ /** The three sizes (`sm` / `md` / `lg`) of the dots variant. */
76
+ export const Sizes: Story = {
77
+ render: () => (
78
+ <div style={{ display: 'flex', gap: '32px', 'align-items': 'center', padding: '24px' }}>
79
+ {['sm', 'md', 'lg'].map((s) => (
80
+ <div style={{ display: 'flex', 'flex-direction': 'column', 'align-items': 'center', gap: '8px' }}>
81
+ <kitn-loader variant="dots" size={s} />
82
+ <code style={{ 'font-size': '11px', opacity: 0.6 }}>{s}</code>
83
+ </div>
84
+ ))}
85
+ </div>
86
+ ),
87
+ };