@kitnai/chat 0.3.1 → 0.4.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.
- package/README.md +11 -0
- package/dist/custom-elements.json +2494 -0
- package/dist/kitn-chat.es.js +52 -39
- package/dist/llms/llms-full.txt +667 -0
- package/dist/llms/llms.txt +104 -0
- package/dist/theme.tokens.css +133 -0
- package/frameworks/react/index.tsx +530 -0
- package/frameworks/react/runtime.tsx +94 -0
- package/llms-full.txt +667 -0
- package/llms.txt +104 -0
- package/package.json +34 -5
- package/src/components/attachments.tsx +4 -2
- package/src/components/chain-of-thought.tsx +1 -1
- package/src/components/chat-scope-picker.tsx +2 -2
- package/src/components/checkpoint.tsx +7 -3
- package/src/components/context.tsx +14 -18
- package/src/components/conversation-item.tsx +1 -1
- package/src/components/conversation-list.tsx +5 -4
- package/src/components/message-skills.tsx +1 -1
- package/src/components/message.tsx +1 -0
- package/src/components/model-switcher.tsx +3 -3
- package/src/components/prompt-input.tsx +15 -2
- package/src/components/reasoning.tsx +2 -2
- package/src/components/scroll-button.tsx +1 -0
- package/src/components/slash-command.tsx +17 -8
- package/src/components/source.tsx +2 -2
- package/src/components/thinking-bar.tsx +2 -2
- package/src/components/tool.tsx +17 -6
- package/src/components/voice-input.tsx +5 -1
- package/src/elements/attachments.tsx +132 -0
- package/src/elements/chain-of-thought.tsx +45 -0
- package/src/elements/chat-scope-picker.tsx +36 -0
- package/src/elements/chat.tsx +51 -7
- package/src/elements/checkpoint.tsx +43 -0
- package/src/elements/code-block.tsx +42 -0
- package/src/elements/compiled.css +1 -1
- package/src/elements/context-meter.tsx +71 -0
- package/src/elements/conversation-list.tsx +6 -0
- package/src/elements/default-input.tsx +22 -1
- package/src/elements/define.tsx +97 -11
- package/src/elements/element-types.d.ts +404 -0
- package/src/elements/empty.tsx +29 -0
- package/src/elements/feedback-bar.tsx +33 -0
- package/src/elements/file-upload.tsx +44 -0
- package/src/elements/image.tsx +32 -0
- package/src/elements/kitn-attachments.stories.tsx +181 -0
- package/src/elements/kitn-chain-of-thought.stories.tsx +75 -0
- package/src/elements/kitn-chat-scope-picker.stories.tsx +72 -0
- package/src/elements/kitn-checkpoint.stories.tsx +71 -0
- package/src/elements/kitn-code-block.stories.tsx +82 -0
- package/src/elements/kitn-context-meter.stories.tsx +85 -0
- package/src/elements/kitn-empty.stories.tsx +110 -0
- package/src/elements/kitn-feedback-bar.stories.tsx +73 -0
- package/src/elements/kitn-file-upload.stories.tsx +81 -0
- package/src/elements/kitn-image.stories.tsx +70 -0
- package/src/elements/kitn-loader.stories.tsx +87 -0
- package/src/elements/kitn-markdown.stories.tsx +75 -0
- package/src/elements/kitn-message-skills.stories.tsx +74 -0
- package/src/elements/kitn-message.stories.tsx +105 -0
- package/src/elements/kitn-model-switcher.stories.tsx +80 -0
- package/src/elements/kitn-prompt-input.stories.tsx +74 -16
- package/src/elements/kitn-prompt-suggestions.stories.tsx +157 -0
- package/src/elements/kitn-reasoning.stories.tsx +76 -0
- package/src/elements/kitn-response-stream.stories.tsx +79 -0
- package/src/elements/kitn-source-list.stories.tsx +77 -0
- package/src/elements/kitn-source.stories.tsx +87 -0
- package/src/elements/kitn-text-shimmer.stories.tsx +63 -0
- package/src/elements/kitn-thinking-bar.stories.tsx +72 -0
- package/src/elements/kitn-tool.stories.tsx +88 -0
- package/src/elements/kitn-voice-input.stories.tsx +87 -0
- package/src/elements/loader.tsx +25 -0
- package/src/elements/markdown.tsx +38 -0
- package/src/elements/message-skills.tsx +22 -0
- package/src/elements/message.tsx +125 -0
- package/src/elements/model-switcher.tsx +35 -0
- package/src/elements/prompt-input.tsx +83 -7
- package/src/elements/prompt-suggestions.tsx +58 -0
- package/src/elements/reasoning.tsx +50 -0
- package/src/elements/register.ts +31 -0
- package/src/elements/response-stream.tsx +40 -0
- package/src/elements/source.tsx +67 -0
- package/src/elements/text-shimmer.tsx +28 -0
- package/src/elements/thinking-bar.tsx +34 -0
- package/src/elements/tool.tsx +23 -0
- package/src/elements/voice-input.tsx +41 -0
- package/src/index.ts +0 -1
- package/src/primitives/chat-config.tsx +2 -2
- package/src/stories/docs/Accessibility.mdx +119 -0
- package/src/stories/docs/ForAIAgents.mdx +93 -0
- package/src/stories/docs/GettingStarted.mdx +2 -2
- package/src/stories/docs/Installation.mdx +2 -2
- package/src/stories/docs/Integrations.mdx +415 -15
- package/src/stories/docs/Introduction.mdx +5 -5
- package/src/stories/docs/Theming.mdx +1 -1
- package/src/stories/typography.stories.tsx +78 -0
- package/src/ui/button.tsx +1 -1
- package/src/ui/collapsible.tsx +119 -8
- package/src/ui/dropdown.tsx +177 -12
- package/src/ui/hover-card.tsx +147 -26
- package/src/ui/overlay.tsx +151 -0
- package/src/ui/textarea.tsx +1 -1
- package/src/ui/tooltip.stories.tsx +1 -1
- package/src/ui/tooltip.tsx +59 -13
- package/src/utils/cn.ts +19 -1
- package/theme.css +72 -43
- package/src/ui/dialog.tsx +0 -21
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { defineKitnElement } from './define';
|
|
2
|
+
import { FileUpload, FileUploadTrigger } from '../components/file-upload';
|
|
3
|
+
import { Upload } from 'lucide-solid';
|
|
4
|
+
|
|
5
|
+
interface Props extends Record<string, unknown> {
|
|
6
|
+
/** Allow selecting multiple files (default true). */
|
|
7
|
+
multiple?: boolean;
|
|
8
|
+
/** `accept` attribute for the file picker (e.g. `image/*`). */
|
|
9
|
+
accept?: string;
|
|
10
|
+
/** Disable the dropzone — no clicking, no drag-and-drop. */
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Default dropzone label (overridable via the default slot). */
|
|
13
|
+
label?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Events fired by `<kitn-file-upload>`. */
|
|
17
|
+
interface Events {
|
|
18
|
+
/** Files were picked or dropped. */
|
|
19
|
+
filesadded: { files: File[] };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* `<kitn-file-upload>` — a click/drag-drop dropzone. Emits `filesadded`. The
|
|
24
|
+
* default dropzone label can be replaced with your own markup via the default
|
|
25
|
+
* `<slot>` (a "Route 2" custom-content slot).
|
|
26
|
+
*/
|
|
27
|
+
defineKitnElement<Props, Events>('kitn-file-upload', {
|
|
28
|
+
multiple: true,
|
|
29
|
+
accept: undefined,
|
|
30
|
+
disabled: false,
|
|
31
|
+
label: 'Click or drop files to upload',
|
|
32
|
+
}, (props, { dispatch, flag }) => (
|
|
33
|
+
<FileUpload
|
|
34
|
+
multiple={flag('multiple')}
|
|
35
|
+
accept={props.accept}
|
|
36
|
+
disabled={flag('disabled')}
|
|
37
|
+
onFilesAdded={(files) => dispatch('filesadded', { files })}
|
|
38
|
+
>
|
|
39
|
+
<FileUploadTrigger class="border-border bg-muted/30 hover:bg-muted/60 text-muted-foreground flex w-full cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border border-dashed px-6 py-8 text-sm transition-colors">
|
|
40
|
+
<Upload class="size-5" />
|
|
41
|
+
<slot>{props.label}</slot>
|
|
42
|
+
</FileUploadTrigger>
|
|
43
|
+
</FileUpload>
|
|
44
|
+
));
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineKitnElement } from './define';
|
|
2
|
+
import { Image } from '../components/image';
|
|
3
|
+
|
|
4
|
+
interface Props extends Record<string, unknown> {
|
|
5
|
+
/** Base64-encoded image data (pair with `media-type`). */
|
|
6
|
+
base64?: string;
|
|
7
|
+
/** Raw image bytes (set as a JS property). */
|
|
8
|
+
bytes?: Uint8Array;
|
|
9
|
+
/** Alt text. */
|
|
10
|
+
alt?: string;
|
|
11
|
+
/** MIME type (default `image/png`). */
|
|
12
|
+
mediaType?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `<kitn-image>` — renders a base64 or byte-array image with a skeleton
|
|
17
|
+
* fallback while it resolves. `base64`/`alt`/`media-type` via attributes;
|
|
18
|
+
* `bytes` via property.
|
|
19
|
+
*/
|
|
20
|
+
defineKitnElement<Props>('kitn-image', {
|
|
21
|
+
base64: undefined,
|
|
22
|
+
bytes: undefined,
|
|
23
|
+
alt: '',
|
|
24
|
+
mediaType: undefined,
|
|
25
|
+
}, (props) => (
|
|
26
|
+
<Image
|
|
27
|
+
alt={props.alt ?? ''}
|
|
28
|
+
base64={props.base64}
|
|
29
|
+
uint8Array={props.bytes}
|
|
30
|
+
mediaType={props.mediaType}
|
|
31
|
+
/>
|
|
32
|
+
));
|
|
@@ -0,0 +1,181 @@
|
|
|
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 { AttachmentData } from '../components/attachments';
|
|
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-attachments': JSX.HTMLAttributes<HTMLElement> & {
|
|
12
|
+
variant?: string;
|
|
13
|
+
'hover-card'?: boolean | string;
|
|
14
|
+
removable?: boolean | string;
|
|
15
|
+
'show-media-type'?: boolean | string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Mixed file/source items (icons + labels) — mirrors Components/Attachments.
|
|
22
|
+
const sampleItems: AttachmentData[] = [
|
|
23
|
+
{
|
|
24
|
+
id: '1',
|
|
25
|
+
type: 'file',
|
|
26
|
+
filename: 'mountain-landscape.jpg',
|
|
27
|
+
mediaType: 'image/jpeg',
|
|
28
|
+
url: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: '2',
|
|
32
|
+
type: 'file',
|
|
33
|
+
filename: 'sunset-beach.png',
|
|
34
|
+
mediaType: 'image/png',
|
|
35
|
+
url: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=400&h=400&fit=crop',
|
|
36
|
+
},
|
|
37
|
+
{ id: '3', type: 'file', filename: 'architecture-report.pdf', mediaType: 'application/pdf' },
|
|
38
|
+
{ id: '4', type: 'file', filename: 'demo-recording.mp4', mediaType: 'video/mp4' },
|
|
39
|
+
{ id: '5', type: 'file', filename: 'podcast-episode.mp3', mediaType: 'audio/mpeg' },
|
|
40
|
+
{ id: '6', type: 'source-document', filename: 'SolidJS Documentation', title: 'SolidJS Reactivity Guide', url: 'https://solidjs.com/docs' },
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Image-only items — used for the grid thumbnails + hover-card preview stories.
|
|
44
|
+
const imageItems: AttachmentData[] = [
|
|
45
|
+
{
|
|
46
|
+
id: 'img-1',
|
|
47
|
+
type: 'file',
|
|
48
|
+
filename: 'mountain-landscape.jpg',
|
|
49
|
+
mediaType: 'image/jpeg',
|
|
50
|
+
url: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=400&fit=crop',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'img-2',
|
|
54
|
+
type: 'file',
|
|
55
|
+
filename: 'sunset-beach.png',
|
|
56
|
+
mediaType: 'image/png',
|
|
57
|
+
url: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=400&h=400&fit=crop',
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/** Render `<kitn-attachments>` with an `items` property and the given flags. */
|
|
62
|
+
function AttachmentsElement(props: {
|
|
63
|
+
items: AttachmentData[];
|
|
64
|
+
variant?: string;
|
|
65
|
+
removable?: boolean;
|
|
66
|
+
hoverCard?: boolean;
|
|
67
|
+
showMediaType?: boolean;
|
|
68
|
+
}) {
|
|
69
|
+
let el: (HTMLElement & { items?: AttachmentData[] }) | undefined;
|
|
70
|
+
onMount(() => {
|
|
71
|
+
if (!el) return;
|
|
72
|
+
el.items = props.items;
|
|
73
|
+
if (props.variant) el.setAttribute('variant', props.variant);
|
|
74
|
+
if (props.hoverCard) el.setAttribute('hover-card', '');
|
|
75
|
+
if (props.showMediaType) el.setAttribute('show-media-type', '');
|
|
76
|
+
if (props.removable) {
|
|
77
|
+
el.setAttribute('removable', '');
|
|
78
|
+
el.addEventListener('remove', ((e: CustomEvent<{ id: string }>) => {
|
|
79
|
+
el!.items = (el!.items ?? []).filter((x) => x.id !== e.detail.id);
|
|
80
|
+
}) as EventListener);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
return (
|
|
84
|
+
<kitn-attachments
|
|
85
|
+
ref={(e) => (el = e as HTMLElement)}
|
|
86
|
+
style={{ display: 'block', padding: '24px', 'max-width': '720px' }}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
92
|
+
<kitn-attachments id="att" variant="grid" removable></kitn-attachments>
|
|
93
|
+
|
|
94
|
+
<script type="module">
|
|
95
|
+
import '@kitnai/chat/elements'; // registers the custom elements
|
|
96
|
+
|
|
97
|
+
const att = document.getElementById('att');
|
|
98
|
+
att.items = [
|
|
99
|
+
{ id: '1', type: 'file', filename: 'mountain-landscape.jpg',
|
|
100
|
+
mediaType: 'image/jpeg', url: 'https://.../mountain.jpg' },
|
|
101
|
+
{ id: '2', type: 'file', filename: 'spec.pdf', mediaType: 'application/pdf' },
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
// events are CustomEvents on the element (they do not bubble)
|
|
105
|
+
att.addEventListener('remove', (e) => {
|
|
106
|
+
att.items = att.items.filter((x) => x.id !== e.detail.id);
|
|
107
|
+
});
|
|
108
|
+
</script>`;
|
|
109
|
+
|
|
110
|
+
const HOVER_SNIPPET = `<!-- inline/list chips with a hover-card image preview -->
|
|
111
|
+
<kitn-attachments id="att" variant="inline" hover-card></kitn-attachments>
|
|
112
|
+
|
|
113
|
+
<script type="module">
|
|
114
|
+
import '@kitnai/chat/elements';
|
|
115
|
+
|
|
116
|
+
const att = document.getElementById('att');
|
|
117
|
+
// image attachments show their thumbnail in the hover card on hover;
|
|
118
|
+
// non-image attachments show their label + media type instead.
|
|
119
|
+
att.items = [
|
|
120
|
+
{ id: '1', type: 'file', filename: 'mountain-landscape.jpg',
|
|
121
|
+
mediaType: 'image/jpeg', url: 'https://.../mountain.jpg' },
|
|
122
|
+
];
|
|
123
|
+
</script>`;
|
|
124
|
+
|
|
125
|
+
const meta = {
|
|
126
|
+
title: 'Web Components/kitn-attachments',
|
|
127
|
+
tags: ['autodocs'],
|
|
128
|
+
parameters: {
|
|
129
|
+
layout: 'fullscreen',
|
|
130
|
+
docs: {
|
|
131
|
+
description: {
|
|
132
|
+
component: [
|
|
133
|
+
'`<kitn-attachments>` is the framework-agnostic **web component** for a set of file/source attachments, and the exemplar for the "collapse a compound primitive to ONE configurable element" pattern: the sub-parts the SolidJS layer composes become attributes here. Isolated in **Shadow DOM**.',
|
|
134
|
+
'**When to use:** rendering attachment chips/tiles in a non-Solid app. In SolidJS, compose the `Attachment*` primitives for fully custom layouts.',
|
|
135
|
+
"**How to use:** register once with `import '@kitnai/chat/elements'`, set the data via the `items` **property**, pick a layout with `variant` (`grid` | `inline` | `list`), add `removable` to get per-item remove buttons (emits a `remove` **CustomEvent** with `{ id }`), and `hover-card` for inline/list previews (image attachments preview their thumbnail).",
|
|
136
|
+
'See the **Code** tab for HTML usage.',
|
|
137
|
+
].join('\n\n'),
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
} satisfies Meta;
|
|
142
|
+
|
|
143
|
+
export default meta;
|
|
144
|
+
type Story = StoryObj;
|
|
145
|
+
|
|
146
|
+
/** Visual tiles (the default `grid` variant) — image attachments render as real
|
|
147
|
+
* `<img>` thumbnails; non-image files fall back to a media-type icon. */
|
|
148
|
+
export const Grid: Story = {
|
|
149
|
+
render: () => <AttachmentsElement items={sampleItems} variant="grid" />,
|
|
150
|
+
parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** Icon + label chips (`inline`) — files shown as a small preview + filename. */
|
|
154
|
+
export const Inline: Story = {
|
|
155
|
+
render: () => <AttachmentsElement items={sampleItems} variant="inline" />,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/** Detailed rows (`list`) with the media type beneath each filename. */
|
|
159
|
+
export const List: Story = {
|
|
160
|
+
render: () => <AttachmentsElement items={sampleItems} variant="list" showMediaType />,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/** Removable rows — clicking the remove button fires a `remove` event. */
|
|
164
|
+
export const RemovableList: Story = {
|
|
165
|
+
name: 'Removable List',
|
|
166
|
+
render: () => <AttachmentsElement items={sampleItems} variant="list" removable />,
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** Image thumbnails in a grid (real `<img>` previews). */
|
|
170
|
+
export const ImageGrid: Story = {
|
|
171
|
+
name: 'Image Grid',
|
|
172
|
+
render: () => <AttachmentsElement items={imageItems} variant="grid" />,
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/** Inline chips with a hover-card preview — hover an image chip to see its
|
|
176
|
+
* thumbnail enlarge in the hover card. */
|
|
177
|
+
export const WithHoverCard: Story = {
|
|
178
|
+
name: 'With Hover Card',
|
|
179
|
+
render: () => <AttachmentsElement items={imageItems} variant="inline" hoverCard />,
|
|
180
|
+
parameters: { docs: { source: { code: HOVER_SNIPPET, language: 'html' } } },
|
|
181
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
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 Step {
|
|
6
|
+
label: string;
|
|
7
|
+
content?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// The web components are custom DOM elements, so declare the tags for JSX.
|
|
11
|
+
declare module 'solid-js' {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
13
|
+
namespace JSX {
|
|
14
|
+
interface IntrinsicElements {
|
|
15
|
+
'kitn-chain-of-thought': JSX.HTMLAttributes<HTMLElement>;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const steps: Step[] = [
|
|
21
|
+
{ label: 'Understand the request', content: 'The user wants a composable set of web components.' },
|
|
22
|
+
{ label: 'Design the API', content: 'Route 1: variant + flags + events; rich data via properties.' },
|
|
23
|
+
{ label: 'Build & verify' },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/** Render `<kitn-chain-of-thought>` with the `steps` set as a JS property. */
|
|
27
|
+
function CotElement(props: { steps: Step[] }) {
|
|
28
|
+
let el: (HTMLElement & { steps?: Step[] }) | undefined;
|
|
29
|
+
onMount(() => {
|
|
30
|
+
if (el) el.steps = props.steps;
|
|
31
|
+
});
|
|
32
|
+
return (
|
|
33
|
+
<kitn-chain-of-thought ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '24px', 'max-width': '560px' }} />
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
38
|
+
<kitn-chain-of-thought id="cot"></kitn-chain-of-thought>
|
|
39
|
+
|
|
40
|
+
<script type="module">
|
|
41
|
+
import '@kitnai/chat/elements'; // registers the custom elements
|
|
42
|
+
|
|
43
|
+
document.getElementById('cot').steps = [
|
|
44
|
+
{ label: 'Understand the request', content: 'The user wants a composable set.' },
|
|
45
|
+
{ label: 'Design the API', content: 'Route 1: variant + flags + events.' },
|
|
46
|
+
{ label: 'Build & verify' },
|
|
47
|
+
];
|
|
48
|
+
</script>`;
|
|
49
|
+
|
|
50
|
+
const meta = {
|
|
51
|
+
title: 'Web Components/kitn-chain-of-thought',
|
|
52
|
+
tags: ['autodocs'],
|
|
53
|
+
parameters: {
|
|
54
|
+
layout: 'fullscreen',
|
|
55
|
+
docs: {
|
|
56
|
+
description: {
|
|
57
|
+
component: [
|
|
58
|
+
'`<kitn-chain-of-thought>` is the framework-agnostic **web component** for step-by-step reasoning — a connected list of steps, each with optional collapsible detail — isolated in **Shadow DOM**. The compound primitive collapses to a single `steps` data model (Route 1).',
|
|
59
|
+
'**When to use:** surfacing an agent\'s plan or reasoning trace in a non-Solid app. In SolidJS, compose the `ChainOfThought` primitives for finer control.',
|
|
60
|
+
"**How to use:** register once with `import '@kitnai/chat/elements'`, then set the `steps` **property** — an array of `{ label, content? }`. Steps with `content` become expandable.",
|
|
61
|
+
'See the **Code** tab for HTML usage.',
|
|
62
|
+
].join('\n\n'),
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
} satisfies Meta;
|
|
67
|
+
|
|
68
|
+
export default meta;
|
|
69
|
+
type Story = StoryObj;
|
|
70
|
+
|
|
71
|
+
/** A three-step reasoning trace; the last step has no detail. */
|
|
72
|
+
export const Default: Story = {
|
|
73
|
+
render: () => <CotElement steps={steps} />,
|
|
74
|
+
parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
|
|
75
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
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-chat-scope-picker': JSX.HTMLAttributes<HTMLElement> & {
|
|
11
|
+
'current-label'?: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Render `<kitn-chat-scope-picker>` with author/tag options set as properties. */
|
|
18
|
+
function ScopePickerElement(props: { authors: string[]; tags: string[] }) {
|
|
19
|
+
let el: (HTMLElement & { availableAuthors?: string[]; availableTags?: string[] }) | undefined;
|
|
20
|
+
onMount(() => {
|
|
21
|
+
if (!el) return;
|
|
22
|
+
el.availableAuthors = props.authors;
|
|
23
|
+
el.availableTags = props.tags;
|
|
24
|
+
el.addEventListener('scopechange', (e) => {
|
|
25
|
+
const ev = e as CustomEvent<{ filters: unknown }>;
|
|
26
|
+
console.log('scopechange', ev.detail.filters ?? 'all');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
return (
|
|
30
|
+
<kitn-chat-scope-picker ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '40px' }} />
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
35
|
+
<kitn-chat-scope-picker id="scope"></kitn-chat-scope-picker>
|
|
36
|
+
|
|
37
|
+
<script type="module">
|
|
38
|
+
import '@kitnai/chat/elements'; // registers the custom elements
|
|
39
|
+
|
|
40
|
+
const scope = document.getElementById('scope');
|
|
41
|
+
scope.availableAuthors = ['Rob', 'Alex'];
|
|
42
|
+
scope.availableTags = ['design', 'api'];
|
|
43
|
+
// undefined filters means "All Content"
|
|
44
|
+
scope.addEventListener('scopechange', (e) => console.log(e.detail.filters ?? 'all'));
|
|
45
|
+
</script>`;
|
|
46
|
+
|
|
47
|
+
const meta = {
|
|
48
|
+
title: 'Web Components/kitn-chat-scope-picker',
|
|
49
|
+
tags: ['autodocs'],
|
|
50
|
+
parameters: {
|
|
51
|
+
layout: 'fullscreen',
|
|
52
|
+
docs: {
|
|
53
|
+
description: {
|
|
54
|
+
component: [
|
|
55
|
+
'`<kitn-chat-scope-picker>` is the framework-agnostic **web component** for scoping a chat by author or tag — a dropdown that emits the chosen filters — isolated in **Shadow DOM**.',
|
|
56
|
+
'**When to use:** letting users narrow a conversation/search to a subset of content. In SolidJS, use the `ChatScopePicker` primitive.',
|
|
57
|
+
"**How to use:** register once with `import '@kitnai/chat/elements'`, set the `availableAuthors` / `availableTags` **properties** (and optionally `current-label`), and listen for the `scopechange` **CustomEvent** (`undefined` filters = \"All Content\").",
|
|
58
|
+
'See the **Code** tab for HTML usage.',
|
|
59
|
+
].join('\n\n'),
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
} satisfies Meta;
|
|
64
|
+
|
|
65
|
+
export default meta;
|
|
66
|
+
type Story = StoryObj;
|
|
67
|
+
|
|
68
|
+
/** Authors and tags available as scope filters. */
|
|
69
|
+
export const Default: Story = {
|
|
70
|
+
render: () => <ScopePickerElement authors={['Rob', 'Alex']} tags={['design', 'api']} />,
|
|
71
|
+
parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
|
|
72
|
+
};
|
|
@@ -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
|
+
};
|