@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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +314 -0
  3. package/dist/bash-InADTalH.js +6 -0
  4. package/dist/core-AYMC6_lb.js +5874 -0
  5. package/dist/engine-javascript-vq0WuIJl.js +2643 -0
  6. package/dist/github-dark-dimmed-DUshB20C.js +4 -0
  7. package/dist/github-light-JYsPkUQd.js +4 -0
  8. package/dist/javascript-C25yR2R2.js +6 -0
  9. package/dist/json-DxJze_jm.js +6 -0
  10. package/dist/kitn-chat.es.js +6632 -0
  11. package/dist/tsx-B8rCNbgL.js +6 -0
  12. package/dist/typescript-RycA9KXf.js +6 -0
  13. package/package.json +80 -0
  14. package/src/components/attachments.stories.tsx +304 -0
  15. package/src/components/attachments.tsx +394 -0
  16. package/src/components/chain-of-thought.stories.tsx +212 -0
  17. package/src/components/chain-of-thought.tsx +139 -0
  18. package/src/components/chat-container.stories.tsx +188 -0
  19. package/src/components/chat-container.tsx +78 -0
  20. package/src/components/chat-scope-picker.tsx +47 -0
  21. package/src/components/checkpoint.stories.tsx +103 -0
  22. package/src/components/checkpoint.tsx +81 -0
  23. package/src/components/code-block.stories.tsx +151 -0
  24. package/src/components/code-block.tsx +99 -0
  25. package/src/components/context.stories.tsx +180 -0
  26. package/src/components/context.tsx +323 -0
  27. package/src/components/conversation-item.stories.tsx +126 -0
  28. package/src/components/conversation-item.tsx +18 -0
  29. package/src/components/conversation-list.stories.tsx +134 -0
  30. package/src/components/conversation-list.tsx +100 -0
  31. package/src/components/empty.stories.tsx +435 -0
  32. package/src/components/empty.tsx +166 -0
  33. package/src/components/feedback-bar.stories.tsx +101 -0
  34. package/src/components/feedback-bar.tsx +58 -0
  35. package/src/components/file-upload.stories.tsx +157 -0
  36. package/src/components/file-upload.tsx +161 -0
  37. package/src/components/image.stories.tsx +90 -0
  38. package/src/components/image.tsx +67 -0
  39. package/src/components/loader.stories.tsx +182 -0
  40. package/src/components/loader.tsx +333 -0
  41. package/src/components/markdown.stories.tsx +181 -0
  42. package/src/components/markdown.tsx +81 -0
  43. package/src/components/message-narrow.stories.tsx +330 -0
  44. package/src/components/message-skills.stories.tsx +212 -0
  45. package/src/components/message-skills.tsx +36 -0
  46. package/src/components/message.stories.tsx +282 -0
  47. package/src/components/message.tsx +149 -0
  48. package/src/components/model-switcher.stories.tsx +98 -0
  49. package/src/components/model-switcher.tsx +36 -0
  50. package/src/components/prompt-input.stories.tsx +223 -0
  51. package/src/components/prompt-input.tsx +190 -0
  52. package/src/components/prompt-suggestion.stories.tsx +143 -0
  53. package/src/components/prompt-suggestion.tsx +115 -0
  54. package/src/components/reasoning.stories.tsx +141 -0
  55. package/src/components/reasoning.tsx +157 -0
  56. package/src/components/response-stream.tsx +103 -0
  57. package/src/components/scroll-button.stories.tsx +101 -0
  58. package/src/components/scroll-button.tsx +33 -0
  59. package/src/components/slash-command.stories.tsx +164 -0
  60. package/src/components/slash-command.tsx +223 -0
  61. package/src/components/source.stories.tsx +125 -0
  62. package/src/components/source.tsx +129 -0
  63. package/src/components/text-shimmer.stories.tsx +88 -0
  64. package/src/components/text-shimmer.tsx +37 -0
  65. package/src/components/thinking-bar.stories.tsx +88 -0
  66. package/src/components/thinking-bar.tsx +50 -0
  67. package/src/components/tool.stories.tsx +154 -0
  68. package/src/components/tool.tsx +173 -0
  69. package/src/components/voice-input.stories.tsx +84 -0
  70. package/src/components/voice-input.tsx +103 -0
  71. package/src/elements/chat-types.ts +14 -0
  72. package/src/elements/chat.tsx +111 -0
  73. package/src/elements/compiled.css +2 -0
  74. package/src/elements/conversation-list.tsx +26 -0
  75. package/src/elements/css.ts +5 -0
  76. package/src/elements/default-input.tsx +53 -0
  77. package/src/elements/define.tsx +54 -0
  78. package/src/elements/kitn-chat.stories.tsx +105 -0
  79. package/src/elements/kitn-conversation-list.stories.tsx +177 -0
  80. package/src/elements/kitn-prompt-input.stories.tsx +123 -0
  81. package/src/elements/prompt-input.tsx +39 -0
  82. package/src/elements/register.ts +9 -0
  83. package/src/elements/styles.css +12 -0
  84. package/src/index.ts +128 -0
  85. package/src/primitives/chat-config.tsx +76 -0
  86. package/src/primitives/highlighter.ts +150 -0
  87. package/src/primitives/use-auto-resize.ts +31 -0
  88. package/src/primitives/use-stick-to-bottom.ts +43 -0
  89. package/src/primitives/use-text-stream.ts +112 -0
  90. package/src/primitives/use-voice-recorder.ts +50 -0
  91. package/src/stories/chat-panel-layout.stories.tsx +144 -0
  92. package/src/stories/chat-scene.tsx +570 -0
  93. package/src/stories/checkpoint-restore.stories.tsx +224 -0
  94. package/src/stories/context-usage.stories.tsx +155 -0
  95. package/src/stories/conversation-with-reasoning.stories.tsx +151 -0
  96. package/src/stories/conversation-with-sources.stories.tsx +165 -0
  97. package/src/stories/docs/GettingStarted.mdx +76 -0
  98. package/src/stories/docs/Installation.mdx +48 -0
  99. package/src/stories/docs/Integrations.mdx +110 -0
  100. package/src/stories/docs/Introduction.mdx +29 -0
  101. package/src/stories/docs/Theming.mdx +87 -0
  102. package/src/stories/docs/theme-editor/canvas.tsx +32 -0
  103. package/src/stories/docs/theme-editor/inspector.tsx +66 -0
  104. package/src/stories/docs/theme-editor/presets.test.ts +32 -0
  105. package/src/stories/docs/theme-editor/presets.ts +64 -0
  106. package/src/stories/docs/theme-editor/theme-css.test.ts +19 -0
  107. package/src/stories/docs/theme-editor/theme-css.ts +15 -0
  108. package/src/stories/docs/theme-editor/theme-editor.tsx +145 -0
  109. package/src/stories/docs/theme-tokens.tsx +174 -0
  110. package/src/stories/full-chat.stories.tsx +18 -0
  111. package/src/stories/message-actions.stories.tsx +167 -0
  112. package/src/stories/prompt-input-variants.stories.tsx +179 -0
  113. package/src/stories/streaming-response.stories.tsx +234 -0
  114. package/src/stories/theme-editor.stories.tsx +16 -0
  115. package/src/stories/token-reference.stories.tsx +18 -0
  116. package/src/types.ts +41 -0
  117. package/src/ui/avatar.stories.tsx +104 -0
  118. package/src/ui/avatar.tsx +23 -0
  119. package/src/ui/badge.stories.tsx +87 -0
  120. package/src/ui/badge.tsx +21 -0
  121. package/src/ui/button.stories.tsx +146 -0
  122. package/src/ui/button.tsx +37 -0
  123. package/src/ui/collapsible.tsx +14 -0
  124. package/src/ui/dialog.tsx +21 -0
  125. package/src/ui/dropdown.tsx +26 -0
  126. package/src/ui/hover-card.tsx +48 -0
  127. package/src/ui/resizable.stories.tsx +171 -0
  128. package/src/ui/resizable.tsx +219 -0
  129. package/src/ui/scroll-area.tsx +13 -0
  130. package/src/ui/separator.stories.tsx +82 -0
  131. package/src/ui/separator.tsx +10 -0
  132. package/src/ui/skeleton.stories.tsx +338 -0
  133. package/src/ui/skeleton.tsx +16 -0
  134. package/src/ui/textarea.tsx +21 -0
  135. package/src/ui/tooltip.stories.tsx +75 -0
  136. package/src/ui/tooltip.tsx +22 -0
  137. package/src/utils/cn.ts +6 -0
  138. 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
+ };