@kitnai/chat 0.1.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/LICENSE +21 -0
- package/README.md +314 -0
- package/dist/bash-InADTalH.js +6 -0
- package/dist/core-AYMC6_lb.js +5874 -0
- package/dist/engine-javascript-vq0WuIJl.js +2643 -0
- package/dist/github-dark-dimmed-DUshB20C.js +4 -0
- package/dist/github-light-JYsPkUQd.js +4 -0
- package/dist/javascript-C25yR2R2.js +6 -0
- package/dist/json-DxJze_jm.js +6 -0
- package/dist/kitn-chat.es.js +6632 -0
- package/dist/tsx-B8rCNbgL.js +6 -0
- package/dist/typescript-RycA9KXf.js +6 -0
- package/package.json +80 -0
- package/src/components/attachments.stories.tsx +304 -0
- package/src/components/attachments.tsx +394 -0
- package/src/components/chain-of-thought.stories.tsx +212 -0
- package/src/components/chain-of-thought.tsx +139 -0
- package/src/components/chat-container.stories.tsx +188 -0
- package/src/components/chat-container.tsx +78 -0
- package/src/components/chat-scope-picker.tsx +47 -0
- package/src/components/checkpoint.stories.tsx +103 -0
- package/src/components/checkpoint.tsx +81 -0
- package/src/components/code-block.stories.tsx +151 -0
- package/src/components/code-block.tsx +99 -0
- package/src/components/context.stories.tsx +180 -0
- package/src/components/context.tsx +323 -0
- package/src/components/conversation-item.stories.tsx +126 -0
- package/src/components/conversation-item.tsx +18 -0
- package/src/components/conversation-list.stories.tsx +134 -0
- package/src/components/conversation-list.tsx +100 -0
- package/src/components/empty.stories.tsx +435 -0
- package/src/components/empty.tsx +166 -0
- package/src/components/feedback-bar.stories.tsx +101 -0
- package/src/components/feedback-bar.tsx +58 -0
- package/src/components/file-upload.stories.tsx +157 -0
- package/src/components/file-upload.tsx +161 -0
- package/src/components/image.stories.tsx +90 -0
- package/src/components/image.tsx +67 -0
- package/src/components/loader.stories.tsx +182 -0
- package/src/components/loader.tsx +333 -0
- package/src/components/markdown.stories.tsx +181 -0
- package/src/components/markdown.tsx +81 -0
- package/src/components/message-narrow.stories.tsx +330 -0
- package/src/components/message-skills.stories.tsx +212 -0
- package/src/components/message-skills.tsx +36 -0
- package/src/components/message.stories.tsx +282 -0
- package/src/components/message.tsx +149 -0
- package/src/components/model-switcher.stories.tsx +98 -0
- package/src/components/model-switcher.tsx +36 -0
- package/src/components/prompt-input.stories.tsx +223 -0
- package/src/components/prompt-input.tsx +190 -0
- package/src/components/prompt-suggestion.stories.tsx +143 -0
- package/src/components/prompt-suggestion.tsx +115 -0
- package/src/components/reasoning.stories.tsx +141 -0
- package/src/components/reasoning.tsx +157 -0
- package/src/components/response-stream.tsx +103 -0
- package/src/components/scroll-button.stories.tsx +101 -0
- package/src/components/scroll-button.tsx +33 -0
- package/src/components/slash-command.stories.tsx +164 -0
- package/src/components/slash-command.tsx +223 -0
- package/src/components/source.stories.tsx +125 -0
- package/src/components/source.tsx +129 -0
- package/src/components/text-shimmer.stories.tsx +88 -0
- package/src/components/text-shimmer.tsx +37 -0
- package/src/components/thinking-bar.stories.tsx +88 -0
- package/src/components/thinking-bar.tsx +50 -0
- package/src/components/tool.stories.tsx +154 -0
- package/src/components/tool.tsx +173 -0
- package/src/components/voice-input.stories.tsx +84 -0
- package/src/components/voice-input.tsx +103 -0
- package/src/elements/chat-types.ts +14 -0
- package/src/elements/chat.tsx +111 -0
- package/src/elements/compiled.css +2 -0
- package/src/elements/conversation-list.tsx +26 -0
- package/src/elements/css.ts +5 -0
- package/src/elements/default-input.tsx +53 -0
- package/src/elements/define.tsx +54 -0
- package/src/elements/kitn-chat.stories.tsx +105 -0
- package/src/elements/kitn-conversation-list.stories.tsx +177 -0
- package/src/elements/kitn-prompt-input.stories.tsx +123 -0
- package/src/elements/prompt-input.tsx +39 -0
- package/src/elements/register.ts +9 -0
- package/src/elements/styles.css +12 -0
- package/src/index.ts +128 -0
- package/src/primitives/chat-config.tsx +76 -0
- package/src/primitives/highlighter.ts +150 -0
- package/src/primitives/use-auto-resize.ts +31 -0
- package/src/primitives/use-stick-to-bottom.ts +43 -0
- package/src/primitives/use-text-stream.ts +112 -0
- package/src/primitives/use-voice-recorder.ts +50 -0
- package/src/stories/chat-panel-layout.stories.tsx +144 -0
- package/src/stories/chat-scene.tsx +570 -0
- package/src/stories/checkpoint-restore.stories.tsx +224 -0
- package/src/stories/context-usage.stories.tsx +155 -0
- package/src/stories/conversation-with-reasoning.stories.tsx +151 -0
- package/src/stories/conversation-with-sources.stories.tsx +165 -0
- package/src/stories/docs/GettingStarted.mdx +76 -0
- package/src/stories/docs/Installation.mdx +48 -0
- package/src/stories/docs/Integrations.mdx +110 -0
- package/src/stories/docs/Introduction.mdx +29 -0
- package/src/stories/docs/Theming.mdx +87 -0
- package/src/stories/docs/theme-editor/canvas.tsx +32 -0
- package/src/stories/docs/theme-editor/inspector.tsx +66 -0
- package/src/stories/docs/theme-editor/presets.test.ts +32 -0
- package/src/stories/docs/theme-editor/presets.ts +64 -0
- package/src/stories/docs/theme-editor/theme-css.test.ts +19 -0
- package/src/stories/docs/theme-editor/theme-css.ts +15 -0
- package/src/stories/docs/theme-editor/theme-editor.tsx +145 -0
- package/src/stories/docs/theme-tokens.tsx +174 -0
- package/src/stories/full-chat.stories.tsx +18 -0
- package/src/stories/message-actions.stories.tsx +167 -0
- package/src/stories/prompt-input-variants.stories.tsx +179 -0
- package/src/stories/streaming-response.stories.tsx +234 -0
- package/src/stories/theme-editor.stories.tsx +16 -0
- package/src/stories/token-reference.stories.tsx +18 -0
- package/src/types.ts +41 -0
- package/src/ui/avatar.stories.tsx +104 -0
- package/src/ui/avatar.tsx +23 -0
- package/src/ui/badge.stories.tsx +87 -0
- package/src/ui/badge.tsx +21 -0
- package/src/ui/button.stories.tsx +146 -0
- package/src/ui/button.tsx +37 -0
- package/src/ui/collapsible.tsx +14 -0
- package/src/ui/dialog.tsx +21 -0
- package/src/ui/dropdown.tsx +26 -0
- package/src/ui/hover-card.tsx +48 -0
- package/src/ui/resizable.stories.tsx +171 -0
- package/src/ui/resizable.tsx +219 -0
- package/src/ui/scroll-area.tsx +13 -0
- package/src/ui/separator.stories.tsx +82 -0
- package/src/ui/separator.tsx +10 -0
- package/src/ui/skeleton.stories.tsx +338 -0
- package/src/ui/skeleton.tsx +16 -0
- package/src/ui/textarea.tsx +21 -0
- package/src/ui/tooltip.stories.tsx +75 -0
- package/src/ui/tooltip.tsx +22 -0
- package/src/utils/cn.ts +6 -0
- package/theme.css +115 -0
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { type JSX, createContext, useContext, splitProps, Show } from 'solid-js';
|
|
2
|
+
import { cn } from '../utils/cn';
|
|
3
|
+
import { Button } from '../ui/button';
|
|
4
|
+
import { HoverCardRoot, HoverCardTrigger, HoverCardContent } from '../ui/hover-card';
|
|
5
|
+
import {
|
|
6
|
+
FileText,
|
|
7
|
+
Globe,
|
|
8
|
+
Image as ImageIcon,
|
|
9
|
+
Music2,
|
|
10
|
+
Paperclip,
|
|
11
|
+
Video,
|
|
12
|
+
X,
|
|
13
|
+
} from 'lucide-solid';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Types
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
export interface AttachmentData {
|
|
20
|
+
id: string;
|
|
21
|
+
type: 'file' | 'source-document';
|
|
22
|
+
filename?: string;
|
|
23
|
+
mediaType?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type AttachmentMediaCategory = 'image' | 'video' | 'audio' | 'document' | 'source' | 'unknown';
|
|
29
|
+
export type AttachmentVariant = 'grid' | 'inline' | 'list';
|
|
30
|
+
|
|
31
|
+
const mediaCategoryIcons: Record<AttachmentMediaCategory, typeof ImageIcon> = {
|
|
32
|
+
audio: Music2,
|
|
33
|
+
document: FileText,
|
|
34
|
+
image: ImageIcon,
|
|
35
|
+
source: Globe,
|
|
36
|
+
unknown: Paperclip,
|
|
37
|
+
video: Video,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Utility Functions
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
export const getMediaCategory = (data: AttachmentData): AttachmentMediaCategory => {
|
|
45
|
+
if (data.type === 'source-document') {
|
|
46
|
+
return 'source';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const mediaType = data.mediaType ?? '';
|
|
50
|
+
|
|
51
|
+
if (mediaType.startsWith('image/')) return 'image';
|
|
52
|
+
if (mediaType.startsWith('video/')) return 'video';
|
|
53
|
+
if (mediaType.startsWith('audio/')) return 'audio';
|
|
54
|
+
if (mediaType.startsWith('application/') || mediaType.startsWith('text/')) return 'document';
|
|
55
|
+
|
|
56
|
+
return 'unknown';
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const getAttachmentLabel = (data: AttachmentData): string => {
|
|
60
|
+
if (data.type === 'source-document') {
|
|
61
|
+
return data.title || data.filename || 'Source';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const category = getMediaCategory(data);
|
|
65
|
+
return data.filename || (category === 'image' ? 'Image' : 'Attachment');
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// Contexts
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
interface AttachmentsContextValue {
|
|
73
|
+
variant: AttachmentVariant;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const AttachmentsContext = createContext<AttachmentsContextValue>();
|
|
77
|
+
|
|
78
|
+
interface AttachmentContextValue {
|
|
79
|
+
data: AttachmentData;
|
|
80
|
+
mediaCategory: AttachmentMediaCategory;
|
|
81
|
+
onRemove?: () => void;
|
|
82
|
+
variant: AttachmentVariant;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const AttachmentContext = createContext<AttachmentContextValue>();
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Hooks
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
export const useAttachmentsContext = () =>
|
|
92
|
+
useContext(AttachmentsContext) ?? { variant: 'grid' as const };
|
|
93
|
+
|
|
94
|
+
export const useAttachmentContext = () => {
|
|
95
|
+
const ctx = useContext(AttachmentContext);
|
|
96
|
+
if (!ctx) {
|
|
97
|
+
throw new Error('Attachment components must be used within <Attachment>');
|
|
98
|
+
}
|
|
99
|
+
return ctx;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Attachments - Container
|
|
104
|
+
// ============================================================================
|
|
105
|
+
|
|
106
|
+
export interface AttachmentsProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
107
|
+
variant?: AttachmentVariant;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function Attachments(props: AttachmentsProps) {
|
|
111
|
+
const [local, rest] = splitProps(props, ['variant', 'class', 'children']);
|
|
112
|
+
const variant = () => local.variant ?? 'grid';
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<AttachmentsContext.Provider value={{ get variant() { return variant(); } }}>
|
|
116
|
+
<div
|
|
117
|
+
class={cn(
|
|
118
|
+
'flex items-start',
|
|
119
|
+
variant() === 'list' ? 'flex-col gap-2' : 'flex-wrap gap-2',
|
|
120
|
+
variant() === 'grid' && 'w-fit',
|
|
121
|
+
local.class,
|
|
122
|
+
)}
|
|
123
|
+
{...rest}
|
|
124
|
+
>
|
|
125
|
+
{local.children}
|
|
126
|
+
</div>
|
|
127
|
+
</AttachmentsContext.Provider>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ============================================================================
|
|
132
|
+
// Attachment - Item
|
|
133
|
+
// ============================================================================
|
|
134
|
+
|
|
135
|
+
export interface AttachmentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
136
|
+
data: AttachmentData;
|
|
137
|
+
onRemove?: () => void;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function Attachment(props: AttachmentProps) {
|
|
141
|
+
const [local, rest] = splitProps(props, ['data', 'onRemove', 'class', 'children']);
|
|
142
|
+
const { variant } = useAttachmentsContext();
|
|
143
|
+
const mediaCategory = () => getMediaCategory(local.data);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<AttachmentContext.Provider
|
|
147
|
+
value={{
|
|
148
|
+
get data() { return local.data; },
|
|
149
|
+
get mediaCategory() { return mediaCategory(); },
|
|
150
|
+
get onRemove() { return local.onRemove; },
|
|
151
|
+
get variant() { return variant; },
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
<div
|
|
155
|
+
class={cn(
|
|
156
|
+
'group relative',
|
|
157
|
+
variant === 'grid' && 'size-24 overflow-hidden rounded-lg',
|
|
158
|
+
variant === 'inline' && [
|
|
159
|
+
'flex h-8 cursor-pointer select-none items-center gap-1.5',
|
|
160
|
+
'rounded-md bg-muted/50 px-1.5',
|
|
161
|
+
'font-medium text-sm transition-all',
|
|
162
|
+
'hover:bg-muted',
|
|
163
|
+
],
|
|
164
|
+
variant === 'list' && [
|
|
165
|
+
'flex w-full items-center gap-3 rounded-lg bg-muted/30 p-3',
|
|
166
|
+
'hover:bg-muted/50',
|
|
167
|
+
],
|
|
168
|
+
local.class,
|
|
169
|
+
)}
|
|
170
|
+
{...rest}
|
|
171
|
+
>
|
|
172
|
+
{local.children}
|
|
173
|
+
</div>
|
|
174
|
+
</AttachmentContext.Provider>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// AttachmentPreview - Media preview
|
|
180
|
+
// ============================================================================
|
|
181
|
+
|
|
182
|
+
export interface AttachmentPreviewProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
183
|
+
fallbackIcon?: JSX.Element;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function AttachmentPreview(props: AttachmentPreviewProps) {
|
|
187
|
+
const [local, rest] = splitProps(props, ['fallbackIcon', 'class']);
|
|
188
|
+
const ctx = useAttachmentContext();
|
|
189
|
+
|
|
190
|
+
const iconSize = () => ctx.variant === 'inline' ? 'size-3' : 'size-4';
|
|
191
|
+
|
|
192
|
+
const renderIcon = (Icon: typeof ImageIcon) => (
|
|
193
|
+
<Icon class={cn(iconSize(), 'text-muted-foreground')} />
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const renderContent = () => {
|
|
197
|
+
if (ctx.mediaCategory === 'image' && ctx.data.type === 'file' && ctx.data.url) {
|
|
198
|
+
return ctx.variant === 'grid' ? (
|
|
199
|
+
<img
|
|
200
|
+
alt={ctx.data.filename || 'Image'}
|
|
201
|
+
class="size-full object-cover"
|
|
202
|
+
height={96}
|
|
203
|
+
src={ctx.data.url}
|
|
204
|
+
width={96}
|
|
205
|
+
/>
|
|
206
|
+
) : (
|
|
207
|
+
<img
|
|
208
|
+
alt={ctx.data.filename || 'Image'}
|
|
209
|
+
class="size-full rounded object-cover"
|
|
210
|
+
height={20}
|
|
211
|
+
src={ctx.data.url}
|
|
212
|
+
width={20}
|
|
213
|
+
/>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (ctx.mediaCategory === 'video' && ctx.data.type === 'file' && ctx.data.url) {
|
|
218
|
+
return <video class="size-full object-cover" muted src={ctx.data.url} />;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const Icon = mediaCategoryIcons[ctx.mediaCategory];
|
|
222
|
+
return local.fallbackIcon ?? renderIcon(Icon);
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<div
|
|
227
|
+
class={cn(
|
|
228
|
+
'flex shrink-0 items-center justify-center overflow-hidden',
|
|
229
|
+
ctx.variant === 'grid' && 'size-full bg-muted',
|
|
230
|
+
ctx.variant === 'inline' && 'size-5 rounded bg-background',
|
|
231
|
+
ctx.variant === 'list' && 'size-12 rounded bg-muted',
|
|
232
|
+
local.class,
|
|
233
|
+
)}
|
|
234
|
+
{...rest}
|
|
235
|
+
>
|
|
236
|
+
{renderContent()}
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// AttachmentInfo - Name and type display
|
|
243
|
+
// ============================================================================
|
|
244
|
+
|
|
245
|
+
export interface AttachmentInfoProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
246
|
+
showMediaType?: boolean;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function AttachmentInfo(props: AttachmentInfoProps) {
|
|
250
|
+
const [local, rest] = splitProps(props, ['showMediaType', 'class']);
|
|
251
|
+
const ctx = useAttachmentContext();
|
|
252
|
+
const label = () => getAttachmentLabel(ctx.data);
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
<Show when={ctx.variant !== 'grid'}>
|
|
256
|
+
<div class={cn('min-w-0 flex-1', local.class)} {...rest}>
|
|
257
|
+
<span class="block truncate">{label()}</span>
|
|
258
|
+
<Show when={local.showMediaType && ctx.data.mediaType}>
|
|
259
|
+
<span class="block truncate text-muted-foreground text-xs">
|
|
260
|
+
{ctx.data.mediaType}
|
|
261
|
+
</span>
|
|
262
|
+
</Show>
|
|
263
|
+
</div>
|
|
264
|
+
</Show>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ============================================================================
|
|
269
|
+
// AttachmentRemove - Remove button
|
|
270
|
+
// ============================================================================
|
|
271
|
+
|
|
272
|
+
export interface AttachmentRemoveProps {
|
|
273
|
+
label?: string;
|
|
274
|
+
class?: string;
|
|
275
|
+
children?: JSX.Element;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function AttachmentRemove(props: AttachmentRemoveProps) {
|
|
279
|
+
const ctx = useAttachmentContext();
|
|
280
|
+
|
|
281
|
+
const handleClick = (e: MouseEvent) => {
|
|
282
|
+
e.stopPropagation();
|
|
283
|
+
ctx.onRemove?.();
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<Show when={ctx.onRemove}>
|
|
288
|
+
<Button
|
|
289
|
+
aria-label={props.label ?? 'Remove'}
|
|
290
|
+
class={cn(
|
|
291
|
+
ctx.variant === 'grid' && [
|
|
292
|
+
'absolute top-2 right-2 size-6 rounded-full p-0',
|
|
293
|
+
'bg-background/80 backdrop-blur-sm',
|
|
294
|
+
'opacity-0 transition-opacity group-hover:opacity-100',
|
|
295
|
+
'hover:bg-background',
|
|
296
|
+
'[&>svg]:size-3',
|
|
297
|
+
],
|
|
298
|
+
ctx.variant === 'inline' && [
|
|
299
|
+
'size-5 rounded p-0',
|
|
300
|
+
'opacity-0 transition-opacity group-hover:opacity-100',
|
|
301
|
+
'[&>svg]:size-2.5',
|
|
302
|
+
],
|
|
303
|
+
ctx.variant === 'list' && ['size-8 shrink-0 rounded p-0', '[&>svg]:size-4'],
|
|
304
|
+
props.class,
|
|
305
|
+
)}
|
|
306
|
+
onClick={handleClick}
|
|
307
|
+
type="button"
|
|
308
|
+
variant="ghost"
|
|
309
|
+
>
|
|
310
|
+
{props.children ?? <X />}
|
|
311
|
+
<span class="sr-only">{props.label ?? 'Remove'}</span>
|
|
312
|
+
</Button>
|
|
313
|
+
</Show>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// AttachmentHoverCard - Hover preview
|
|
319
|
+
// ============================================================================
|
|
320
|
+
|
|
321
|
+
export interface AttachmentHoverCardProps {
|
|
322
|
+
children: JSX.Element;
|
|
323
|
+
openDelay?: number;
|
|
324
|
+
closeDelay?: number;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function AttachmentHoverCard(props: AttachmentHoverCardProps) {
|
|
328
|
+
return (
|
|
329
|
+
<HoverCardRoot
|
|
330
|
+
openDelay={props.openDelay ?? 0}
|
|
331
|
+
closeDelay={props.closeDelay ?? 0}
|
|
332
|
+
>
|
|
333
|
+
{props.children}
|
|
334
|
+
</HoverCardRoot>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export interface AttachmentHoverCardTriggerProps {
|
|
339
|
+
children: JSX.Element;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function AttachmentHoverCardTrigger(props: AttachmentHoverCardTriggerProps) {
|
|
343
|
+
return <HoverCardTrigger>{props.children}</HoverCardTrigger>;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export interface AttachmentHoverCardContentProps {
|
|
347
|
+
children: JSX.Element;
|
|
348
|
+
class?: string;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function AttachmentHoverCardContent(props: AttachmentHoverCardContentProps) {
|
|
352
|
+
return (
|
|
353
|
+
<HoverCardContent class={cn('w-auto p-2', props.class)}>
|
|
354
|
+
{props.children}
|
|
355
|
+
</HoverCardContent>
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ============================================================================
|
|
360
|
+
// AttachmentEmpty - Empty state
|
|
361
|
+
// ============================================================================
|
|
362
|
+
|
|
363
|
+
export interface AttachmentEmptyProps extends JSX.HTMLAttributes<HTMLDivElement> {}
|
|
364
|
+
|
|
365
|
+
function AttachmentEmpty(props: AttachmentEmptyProps) {
|
|
366
|
+
const [local, rest] = splitProps(props, ['class', 'children']);
|
|
367
|
+
return (
|
|
368
|
+
<div
|
|
369
|
+
class={cn(
|
|
370
|
+
'flex items-center justify-center p-4 text-muted-foreground text-sm',
|
|
371
|
+
local.class,
|
|
372
|
+
)}
|
|
373
|
+
{...rest}
|
|
374
|
+
>
|
|
375
|
+
{local.children ?? 'No attachments'}
|
|
376
|
+
</div>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ============================================================================
|
|
381
|
+
// Exports
|
|
382
|
+
// ============================================================================
|
|
383
|
+
|
|
384
|
+
export {
|
|
385
|
+
Attachments,
|
|
386
|
+
Attachment,
|
|
387
|
+
AttachmentPreview,
|
|
388
|
+
AttachmentInfo,
|
|
389
|
+
AttachmentRemove,
|
|
390
|
+
AttachmentHoverCard,
|
|
391
|
+
AttachmentHoverCardTrigger,
|
|
392
|
+
AttachmentHoverCardContent,
|
|
393
|
+
AttachmentEmpty,
|
|
394
|
+
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from 'storybook-solidjs-vite';
|
|
2
|
+
import {
|
|
3
|
+
ChainOfThought,
|
|
4
|
+
ChainOfThoughtStep,
|
|
5
|
+
ChainOfThoughtTrigger,
|
|
6
|
+
ChainOfThoughtContent,
|
|
7
|
+
ChainOfThoughtItem,
|
|
8
|
+
} from './chain-of-thought';
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
title: 'Components/ChainOfThought',
|
|
12
|
+
component: ChainOfThought,
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
parameters: {
|
|
15
|
+
layout: 'padded',
|
|
16
|
+
docs: {
|
|
17
|
+
description: {
|
|
18
|
+
component: [
|
|
19
|
+
'A vertical, collapsible timeline that reveals an agent\'s reasoning as a series of connected steps. Composed of `ChainOfThought` (root) wrapping `ChainOfThoughtStep` items, each with a `ChainOfThoughtTrigger`, `ChainOfThoughtContent`, and one or more `ChainOfThoughtItem`s.',
|
|
20
|
+
'**When to use:** to surface multi-step reasoning, tool calls, or research progress behind an assistant response, letting users expand each step on demand.',
|
|
21
|
+
'**How to use:** map each reasoning step to a `ChainOfThoughtStep` (mark the final one with `isLast`); put the summary in `ChainOfThoughtTrigger` and the detail inside `ChainOfThoughtContent` / `ChainOfThoughtItem`.',
|
|
22
|
+
'**Placement:** above or within an assistant message, typically before the final answer.',
|
|
23
|
+
].join('\n\n'),
|
|
24
|
+
},
|
|
25
|
+
controls: { exclude: ['use:eventListener'] },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
argTypes: {
|
|
29
|
+
children: {
|
|
30
|
+
control: false,
|
|
31
|
+
description: 'The `ChainOfThoughtStep` items that make up the timeline.',
|
|
32
|
+
},
|
|
33
|
+
class: {
|
|
34
|
+
control: 'text',
|
|
35
|
+
description: 'Extra classes for the root wrapper.',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
args: {},
|
|
39
|
+
render: (args) => (
|
|
40
|
+
<ChainOfThought {...args}>
|
|
41
|
+
<ChainOfThoughtStep>
|
|
42
|
+
<ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
|
|
43
|
+
<ChainOfThoughtContent>
|
|
44
|
+
<ChainOfThoughtItem>
|
|
45
|
+
The user is asking about reactive programming concepts in SolidJS.
|
|
46
|
+
</ChainOfThoughtItem>
|
|
47
|
+
</ChainOfThoughtContent>
|
|
48
|
+
</ChainOfThoughtStep>
|
|
49
|
+
<ChainOfThoughtStep isLast>
|
|
50
|
+
<ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
|
|
51
|
+
<ChainOfThoughtContent>
|
|
52
|
+
<ChainOfThoughtItem>
|
|
53
|
+
Combining insights from documentation and examples into a clear answer.
|
|
54
|
+
</ChainOfThoughtItem>
|
|
55
|
+
</ChainOfThoughtContent>
|
|
56
|
+
</ChainOfThoughtStep>
|
|
57
|
+
</ChainOfThought>
|
|
58
|
+
),
|
|
59
|
+
} satisfies Meta<typeof ChainOfThought>;
|
|
60
|
+
|
|
61
|
+
export default meta;
|
|
62
|
+
type Story = StoryObj<typeof meta>;
|
|
63
|
+
|
|
64
|
+
const IMPORT = `import {
|
|
65
|
+
ChainOfThought, ChainOfThoughtStep, ChainOfThoughtTrigger,
|
|
66
|
+
ChainOfThoughtContent, ChainOfThoughtItem,
|
|
67
|
+
} from '@kitnai/chat';`;
|
|
68
|
+
const src = (code: string) => ({
|
|
69
|
+
parameters: { docs: { source: { code: `${IMPORT}\n\n${code}`, language: 'tsx' } } },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** Interactive playground — expand the steps to reveal each reasoning item. */
|
|
73
|
+
export const Playground: Story = {
|
|
74
|
+
...src(`<ChainOfThought>
|
|
75
|
+
<ChainOfThoughtStep>
|
|
76
|
+
<ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
|
|
77
|
+
<ChainOfThoughtContent>
|
|
78
|
+
<ChainOfThoughtItem>The user is asking about SolidJS reactivity.</ChainOfThoughtItem>
|
|
79
|
+
</ChainOfThoughtContent>
|
|
80
|
+
</ChainOfThoughtStep>
|
|
81
|
+
<ChainOfThoughtStep isLast>
|
|
82
|
+
<ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
|
|
83
|
+
<ChainOfThoughtContent>
|
|
84
|
+
<ChainOfThoughtItem>Combining insights into a clear answer.</ChainOfThoughtItem>
|
|
85
|
+
</ChainOfThoughtContent>
|
|
86
|
+
</ChainOfThoughtStep>
|
|
87
|
+
</ChainOfThought>`),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const SingleStep: Story = {
|
|
91
|
+
render: () => (
|
|
92
|
+
<ChainOfThought>
|
|
93
|
+
<ChainOfThoughtStep isLast>
|
|
94
|
+
<ChainOfThoughtTrigger>Analyzing the question</ChainOfThoughtTrigger>
|
|
95
|
+
<ChainOfThoughtContent>
|
|
96
|
+
<ChainOfThoughtItem>
|
|
97
|
+
Breaking down the user's query into key concepts and identifying relevant knowledge areas.
|
|
98
|
+
</ChainOfThoughtItem>
|
|
99
|
+
</ChainOfThoughtContent>
|
|
100
|
+
</ChainOfThoughtStep>
|
|
101
|
+
</ChainOfThought>
|
|
102
|
+
),
|
|
103
|
+
...src(`<ChainOfThought>
|
|
104
|
+
<ChainOfThoughtStep isLast>
|
|
105
|
+
<ChainOfThoughtTrigger>Analyzing the question</ChainOfThoughtTrigger>
|
|
106
|
+
<ChainOfThoughtContent>
|
|
107
|
+
<ChainOfThoughtItem>Breaking down the query into key concepts.</ChainOfThoughtItem>
|
|
108
|
+
</ChainOfThoughtContent>
|
|
109
|
+
</ChainOfThoughtStep>
|
|
110
|
+
</ChainOfThought>`),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const MultipleSteps: Story = {
|
|
114
|
+
render: () => (
|
|
115
|
+
<ChainOfThought>
|
|
116
|
+
<ChainOfThoughtStep>
|
|
117
|
+
<ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
|
|
118
|
+
<ChainOfThoughtContent>
|
|
119
|
+
<ChainOfThoughtItem>
|
|
120
|
+
The user is asking about reactive programming concepts in SolidJS.
|
|
121
|
+
</ChainOfThoughtItem>
|
|
122
|
+
</ChainOfThoughtContent>
|
|
123
|
+
</ChainOfThoughtStep>
|
|
124
|
+
<ChainOfThoughtStep>
|
|
125
|
+
<ChainOfThoughtTrigger>Searching knowledge base</ChainOfThoughtTrigger>
|
|
126
|
+
<ChainOfThoughtContent>
|
|
127
|
+
<ChainOfThoughtItem>
|
|
128
|
+
Found 3 relevant documents about SolidJS signals and reactivity.
|
|
129
|
+
</ChainOfThoughtItem>
|
|
130
|
+
</ChainOfThoughtContent>
|
|
131
|
+
</ChainOfThoughtStep>
|
|
132
|
+
<ChainOfThoughtStep isLast>
|
|
133
|
+
<ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
|
|
134
|
+
<ChainOfThoughtContent>
|
|
135
|
+
<ChainOfThoughtItem>
|
|
136
|
+
Combining insights from documentation and examples to create a comprehensive answer.
|
|
137
|
+
</ChainOfThoughtItem>
|
|
138
|
+
</ChainOfThoughtContent>
|
|
139
|
+
</ChainOfThoughtStep>
|
|
140
|
+
</ChainOfThought>
|
|
141
|
+
),
|
|
142
|
+
...src(`<ChainOfThought>
|
|
143
|
+
<ChainOfThoughtStep>
|
|
144
|
+
<ChainOfThoughtTrigger>Understanding the question</ChainOfThoughtTrigger>
|
|
145
|
+
<ChainOfThoughtContent>
|
|
146
|
+
<ChainOfThoughtItem>The user is asking about SolidJS reactivity.</ChainOfThoughtItem>
|
|
147
|
+
</ChainOfThoughtContent>
|
|
148
|
+
</ChainOfThoughtStep>
|
|
149
|
+
<ChainOfThoughtStep>
|
|
150
|
+
<ChainOfThoughtTrigger>Searching knowledge base</ChainOfThoughtTrigger>
|
|
151
|
+
<ChainOfThoughtContent>
|
|
152
|
+
<ChainOfThoughtItem>Found 3 relevant documents.</ChainOfThoughtItem>
|
|
153
|
+
</ChainOfThoughtContent>
|
|
154
|
+
</ChainOfThoughtStep>
|
|
155
|
+
<ChainOfThoughtStep isLast>
|
|
156
|
+
<ChainOfThoughtTrigger>Formulating response</ChainOfThoughtTrigger>
|
|
157
|
+
<ChainOfThoughtContent>
|
|
158
|
+
<ChainOfThoughtItem>Combining insights into a clear answer.</ChainOfThoughtItem>
|
|
159
|
+
</ChainOfThoughtContent>
|
|
160
|
+
</ChainOfThoughtStep>
|
|
161
|
+
</ChainOfThought>`),
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const WithCustomIcons: Story = {
|
|
165
|
+
render: () => (
|
|
166
|
+
<ChainOfThought>
|
|
167
|
+
<ChainOfThoughtStep>
|
|
168
|
+
<ChainOfThoughtTrigger
|
|
169
|
+
leftIcon={
|
|
170
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
171
|
+
<circle cx="11" cy="11" r="8" />
|
|
172
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
173
|
+
</svg>
|
|
174
|
+
}
|
|
175
|
+
>
|
|
176
|
+
Searching documents
|
|
177
|
+
</ChainOfThoughtTrigger>
|
|
178
|
+
<ChainOfThoughtContent>
|
|
179
|
+
<ChainOfThoughtItem>Found 5 relevant results.</ChainOfThoughtItem>
|
|
180
|
+
</ChainOfThoughtContent>
|
|
181
|
+
</ChainOfThoughtStep>
|
|
182
|
+
<ChainOfThoughtStep isLast>
|
|
183
|
+
<ChainOfThoughtTrigger
|
|
184
|
+
leftIcon={
|
|
185
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
186
|
+
<polyline points="20 6 9 17 4 12" />
|
|
187
|
+
</svg>
|
|
188
|
+
}
|
|
189
|
+
>
|
|
190
|
+
Analysis complete
|
|
191
|
+
</ChainOfThoughtTrigger>
|
|
192
|
+
<ChainOfThoughtContent>
|
|
193
|
+
<ChainOfThoughtItem>All sources have been analyzed and synthesized.</ChainOfThoughtItem>
|
|
194
|
+
</ChainOfThoughtContent>
|
|
195
|
+
</ChainOfThoughtStep>
|
|
196
|
+
</ChainOfThought>
|
|
197
|
+
),
|
|
198
|
+
...src(`<ChainOfThought>
|
|
199
|
+
<ChainOfThoughtStep>
|
|
200
|
+
<ChainOfThoughtTrigger leftIcon={<SearchIcon />}>Searching documents</ChainOfThoughtTrigger>
|
|
201
|
+
<ChainOfThoughtContent>
|
|
202
|
+
<ChainOfThoughtItem>Found 5 relevant results.</ChainOfThoughtItem>
|
|
203
|
+
</ChainOfThoughtContent>
|
|
204
|
+
</ChainOfThoughtStep>
|
|
205
|
+
<ChainOfThoughtStep isLast>
|
|
206
|
+
<ChainOfThoughtTrigger leftIcon={<CheckIcon />}>Analysis complete</ChainOfThoughtTrigger>
|
|
207
|
+
<ChainOfThoughtContent>
|
|
208
|
+
<ChainOfThoughtItem>All sources analyzed and synthesized.</ChainOfThoughtItem>
|
|
209
|
+
</ChainOfThoughtContent>
|
|
210
|
+
</ChainOfThoughtStep>
|
|
211
|
+
</ChainOfThought>`),
|
|
212
|
+
};
|