@v0-sdk/react 0.2.1 → 0.3.1
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 +198 -376
- package/dist/index.cjs +763 -548
- package/dist/index.d.ts +219 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +755 -550
- package/package.json +11 -11
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import React$1 from 'react';
|
|
3
2
|
|
|
4
3
|
/**
|
|
@@ -177,9 +176,28 @@ interface MessageProps {
|
|
|
177
176
|
type MessageRendererProps = MessageProps;
|
|
178
177
|
type V0MessageRendererProps = MessageProps;
|
|
179
178
|
|
|
180
|
-
|
|
179
|
+
interface MessageData {
|
|
180
|
+
elements: MessageElement[];
|
|
181
|
+
messageId: string;
|
|
182
|
+
role: string;
|
|
183
|
+
streaming: boolean;
|
|
184
|
+
isLastMessage: boolean;
|
|
185
|
+
}
|
|
186
|
+
interface MessageElement {
|
|
187
|
+
type: 'text' | 'html' | 'component' | 'content-part' | 'code-project';
|
|
188
|
+
key: string;
|
|
189
|
+
data: any;
|
|
190
|
+
props?: Record<string, any>;
|
|
191
|
+
children?: MessageElement[];
|
|
192
|
+
}
|
|
193
|
+
declare function useMessage({ content, messageId, role, streaming, isLastMessage, components, renderers, }: Omit<MessageProps, 'className'>): MessageData;
|
|
194
|
+
declare function MessageImpl({ content, messageId, role, streaming, isLastMessage, className, components, renderers, }: MessageProps): React$1.FunctionComponentElement<{
|
|
195
|
+
messageData: MessageData;
|
|
196
|
+
className?: string;
|
|
197
|
+
}>;
|
|
181
198
|
/**
|
|
182
199
|
* Main component for rendering v0 Platform API message content
|
|
200
|
+
* This is a backward-compatible JSX renderer. For headless usage, use the useMessage hook.
|
|
183
201
|
*/
|
|
184
202
|
declare const Message: React$1.MemoExoticComponent<typeof MessageImpl>;
|
|
185
203
|
|
|
@@ -218,9 +236,15 @@ interface StreamingMessageProps extends Omit<MessageProps, 'content' | 'streamin
|
|
|
218
236
|
*/
|
|
219
237
|
errorComponent?: (error: string) => React$1.ReactNode;
|
|
220
238
|
}
|
|
239
|
+
interface StreamingMessageData extends StreamingMessageState {
|
|
240
|
+
messageData: MessageData | null;
|
|
241
|
+
}
|
|
242
|
+
declare function useStreamingMessageData({ stream, messageId, role, components, renderers, onChunk, onComplete, onError, onChatData, }: Omit<StreamingMessageProps, 'className' | 'showLoadingIndicator' | 'loadingComponent' | 'errorComponent'>): StreamingMessageData;
|
|
221
243
|
/**
|
|
222
244
|
* Component for rendering streaming message content from v0 API
|
|
223
245
|
*
|
|
246
|
+
* For headless usage, use the useStreamingMessageData hook instead.
|
|
247
|
+
*
|
|
224
248
|
* @example
|
|
225
249
|
* ```tsx
|
|
226
250
|
* import { v0 } from 'v0-sdk'
|
|
@@ -254,18 +278,53 @@ interface StreamingMessageProps extends Omit<MessageProps, 'content' | 'streamin
|
|
|
254
278
|
* }
|
|
255
279
|
* ```
|
|
256
280
|
*/
|
|
257
|
-
declare function StreamingMessage({ stream, showLoadingIndicator, loadingComponent, errorComponent, onChunk, onComplete, onError, onChatData, ...messageProps }: StreamingMessageProps):
|
|
281
|
+
declare function StreamingMessage({ stream, showLoadingIndicator, loadingComponent, errorComponent, onChunk, onComplete, onError, onChatData, className, ...messageProps }: StreamingMessageProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
|
|
282
|
+
className: string;
|
|
283
|
+
style: {
|
|
284
|
+
color: "red";
|
|
285
|
+
padding: string;
|
|
286
|
+
border: string;
|
|
287
|
+
borderRadius: string;
|
|
288
|
+
};
|
|
289
|
+
}, HTMLElement> | React$1.DetailedReactHTMLElement<{
|
|
290
|
+
className: string;
|
|
291
|
+
style: {
|
|
292
|
+
display: "flex";
|
|
293
|
+
alignItems: "center";
|
|
294
|
+
gap: string;
|
|
295
|
+
color: "#6b7280";
|
|
296
|
+
};
|
|
297
|
+
}, HTMLElement> | React$1.FunctionComponentElement<MessageProps>;
|
|
258
298
|
|
|
259
299
|
interface IconProps {
|
|
260
300
|
name: 'chevron-right' | 'chevron-down' | 'search' | 'folder' | 'settings' | 'file-text' | 'brain' | 'wrench';
|
|
261
301
|
className?: string;
|
|
262
302
|
}
|
|
303
|
+
interface IconData {
|
|
304
|
+
name: IconProps['name'];
|
|
305
|
+
fallback: string;
|
|
306
|
+
ariaLabel: string;
|
|
307
|
+
}
|
|
308
|
+
declare function useIcon(props: IconProps): IconData;
|
|
263
309
|
/**
|
|
264
310
|
* Generic icon component that can be customized by consumers.
|
|
265
311
|
* By default, renders a simple fallback. Consumers should provide
|
|
266
312
|
* their own icon implementation via context or props.
|
|
313
|
+
*
|
|
314
|
+
* For headless usage, use the useIcon hook instead.
|
|
315
|
+
*/
|
|
316
|
+
declare function Icon(props: IconProps): React$1.ReactElement<IconProps, string | React$1.JSXElementConstructor<any>> | React$1.DetailedReactHTMLElement<{
|
|
317
|
+
className: string;
|
|
318
|
+
'data-icon': "search" | "brain" | "chevron-right" | "chevron-down" | "folder" | "settings" | "file-text" | "wrench";
|
|
319
|
+
'aria-label': string;
|
|
320
|
+
}, HTMLElement>;
|
|
321
|
+
/**
|
|
322
|
+
* Provider for custom icon implementation
|
|
267
323
|
*/
|
|
268
|
-
declare function
|
|
324
|
+
declare function IconProvider({ children, component, }: {
|
|
325
|
+
children: React$1.ReactNode;
|
|
326
|
+
component: React$1.ComponentType<IconProps>;
|
|
327
|
+
}): React$1.FunctionComponentElement<React$1.ProviderProps<React$1.ComponentType<IconProps>>>;
|
|
269
328
|
|
|
270
329
|
interface ThinkingSectionProps {
|
|
271
330
|
title?: string;
|
|
@@ -280,11 +339,29 @@ interface ThinkingSectionProps {
|
|
|
280
339
|
chevronRightIcon?: React$1.ReactNode;
|
|
281
340
|
chevronDownIcon?: React$1.ReactNode;
|
|
282
341
|
}
|
|
342
|
+
interface ThinkingSectionData {
|
|
343
|
+
title: string;
|
|
344
|
+
duration?: number;
|
|
345
|
+
thought?: string;
|
|
346
|
+
collapsed: boolean;
|
|
347
|
+
paragraphs: string[];
|
|
348
|
+
formattedDuration?: string;
|
|
349
|
+
}
|
|
350
|
+
declare function useThinkingSection({ title, duration, thought, collapsed: initialCollapsed, onCollapse, }: Omit<ThinkingSectionProps, 'className' | 'children' | 'iconRenderer' | 'brainIcon' | 'chevronRightIcon' | 'chevronDownIcon'>): {
|
|
351
|
+
data: ThinkingSectionData;
|
|
352
|
+
collapsed: boolean;
|
|
353
|
+
handleCollapse: () => void;
|
|
354
|
+
};
|
|
283
355
|
/**
|
|
284
356
|
* Generic thinking section component
|
|
285
357
|
* Renders a collapsible section with basic structure - consumers provide styling
|
|
358
|
+
*
|
|
359
|
+
* For headless usage, use the useThinkingSection hook instead.
|
|
286
360
|
*/
|
|
287
|
-
declare function ThinkingSection({ title, duration, thought, collapsed: initialCollapsed, onCollapse, className, children, iconRenderer, brainIcon, chevronRightIcon, chevronDownIcon, }: ThinkingSectionProps):
|
|
361
|
+
declare function ThinkingSection({ title, duration, thought, collapsed: initialCollapsed, onCollapse, className, children, iconRenderer, brainIcon, chevronRightIcon, chevronDownIcon, }: ThinkingSectionProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
|
|
362
|
+
className: string;
|
|
363
|
+
'data-component': string;
|
|
364
|
+
}, HTMLElement>;
|
|
288
365
|
|
|
289
366
|
interface TaskSectionProps {
|
|
290
367
|
title?: string;
|
|
@@ -299,11 +376,48 @@ interface TaskSectionProps {
|
|
|
299
376
|
chevronRightIcon?: React$1.ReactNode;
|
|
300
377
|
chevronDownIcon?: React$1.ReactNode;
|
|
301
378
|
}
|
|
379
|
+
interface TaskSectionData {
|
|
380
|
+
title: string;
|
|
381
|
+
type?: string;
|
|
382
|
+
parts: any[];
|
|
383
|
+
collapsed: boolean;
|
|
384
|
+
meaningfulParts: any[];
|
|
385
|
+
shouldShowCollapsible: boolean;
|
|
386
|
+
iconName: IconProps['name'];
|
|
387
|
+
}
|
|
388
|
+
interface TaskPartData {
|
|
389
|
+
type: string;
|
|
390
|
+
status?: string;
|
|
391
|
+
content: React$1.ReactNode;
|
|
392
|
+
isSearching?: boolean;
|
|
393
|
+
isAnalyzing?: boolean;
|
|
394
|
+
isComplete?: boolean;
|
|
395
|
+
query?: string;
|
|
396
|
+
count?: number;
|
|
397
|
+
answer?: string;
|
|
398
|
+
sources?: Array<{
|
|
399
|
+
url: string;
|
|
400
|
+
title: string;
|
|
401
|
+
}>;
|
|
402
|
+
files?: string[];
|
|
403
|
+
issues?: number;
|
|
404
|
+
}
|
|
405
|
+
declare function useTaskSection({ title, type, parts, collapsed: initialCollapsed, onCollapse, }: Omit<TaskSectionProps, 'className' | 'children' | 'iconRenderer' | 'taskIcon' | 'chevronRightIcon' | 'chevronDownIcon'>): {
|
|
406
|
+
data: TaskSectionData;
|
|
407
|
+
collapsed: boolean;
|
|
408
|
+
handleCollapse: () => void;
|
|
409
|
+
processedParts: TaskPartData[];
|
|
410
|
+
};
|
|
302
411
|
/**
|
|
303
412
|
* Generic task section component
|
|
304
413
|
* Renders a collapsible task section with basic structure - consumers provide styling
|
|
414
|
+
*
|
|
415
|
+
* For headless usage, use the useTaskSection hook instead.
|
|
305
416
|
*/
|
|
306
|
-
declare function TaskSection({ title, type, parts, collapsed: initialCollapsed, onCollapse, className, children, iconRenderer, taskIcon, chevronRightIcon, chevronDownIcon, }: TaskSectionProps):
|
|
417
|
+
declare function TaskSection({ title, type, parts, collapsed: initialCollapsed, onCollapse, className, children, iconRenderer, taskIcon, chevronRightIcon, chevronDownIcon, }: TaskSectionProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
|
|
418
|
+
className: string;
|
|
419
|
+
'data-component': string;
|
|
420
|
+
}, HTMLElement>;
|
|
307
421
|
|
|
308
422
|
interface CodeProjectPartProps {
|
|
309
423
|
title?: string;
|
|
@@ -315,11 +429,33 @@ interface CodeProjectPartProps {
|
|
|
315
429
|
children?: React$1.ReactNode;
|
|
316
430
|
iconRenderer?: React$1.ComponentType<IconProps>;
|
|
317
431
|
}
|
|
432
|
+
interface CodeProjectData {
|
|
433
|
+
title: string;
|
|
434
|
+
filename?: string;
|
|
435
|
+
code?: string;
|
|
436
|
+
language: string;
|
|
437
|
+
collapsed: boolean;
|
|
438
|
+
files: Array<{
|
|
439
|
+
name: string;
|
|
440
|
+
path: string;
|
|
441
|
+
active: boolean;
|
|
442
|
+
}>;
|
|
443
|
+
}
|
|
444
|
+
declare function useCodeProject({ title, filename, code, language, collapsed: initialCollapsed, }: Omit<CodeProjectPartProps, 'className' | 'children' | 'iconRenderer'>): {
|
|
445
|
+
data: CodeProjectData;
|
|
446
|
+
collapsed: boolean;
|
|
447
|
+
toggleCollapsed: () => void;
|
|
448
|
+
};
|
|
318
449
|
/**
|
|
319
450
|
* Generic code project block component
|
|
320
451
|
* Renders a collapsible code project with basic structure - consumers provide styling
|
|
452
|
+
*
|
|
453
|
+
* For headless usage, use the useCodeProject hook instead.
|
|
321
454
|
*/
|
|
322
|
-
declare function CodeProjectPart({ title, filename, code, language, collapsed: initialCollapsed, className, children, iconRenderer, }: CodeProjectPartProps):
|
|
455
|
+
declare function CodeProjectPart({ title, filename, code, language, collapsed: initialCollapsed, className, children, iconRenderer, }: CodeProjectPartProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
|
|
456
|
+
className: string;
|
|
457
|
+
'data-component': string;
|
|
458
|
+
}, HTMLElement>;
|
|
323
459
|
|
|
324
460
|
interface ContentPartRendererProps {
|
|
325
461
|
part: any;
|
|
@@ -329,6 +465,7 @@ interface ContentPartRendererProps {
|
|
|
329
465
|
duration?: number;
|
|
330
466
|
thought?: string;
|
|
331
467
|
collapsed?: boolean;
|
|
468
|
+
onCollapse?: () => void;
|
|
332
469
|
className?: string;
|
|
333
470
|
children?: React$1.ReactNode;
|
|
334
471
|
brainIcon?: React$1.ReactNode;
|
|
@@ -340,6 +477,7 @@ interface ContentPartRendererProps {
|
|
|
340
477
|
type?: string;
|
|
341
478
|
parts?: any[];
|
|
342
479
|
collapsed?: boolean;
|
|
480
|
+
onCollapse?: () => void;
|
|
343
481
|
className?: string;
|
|
344
482
|
children?: React$1.ReactNode;
|
|
345
483
|
taskIcon?: React$1.ReactNode;
|
|
@@ -354,7 +492,49 @@ interface ContentPartRendererProps {
|
|
|
354
492
|
settingsIcon?: React$1.ReactNode;
|
|
355
493
|
wrenchIcon?: React$1.ReactNode;
|
|
356
494
|
}
|
|
357
|
-
|
|
495
|
+
interface ContentPartData {
|
|
496
|
+
type: string;
|
|
497
|
+
parts: any[];
|
|
498
|
+
metadata: Record<string, any>;
|
|
499
|
+
componentType: 'thinking' | 'task' | 'unknown' | null;
|
|
500
|
+
title?: string;
|
|
501
|
+
iconName?: IconProps['name'];
|
|
502
|
+
thinkingData?: {
|
|
503
|
+
duration?: number;
|
|
504
|
+
thought?: string;
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
declare function useContentPart(part: any): ContentPartData;
|
|
508
|
+
/**
|
|
509
|
+
* Content part renderer that handles different types of v0 API content parts
|
|
510
|
+
*
|
|
511
|
+
* For headless usage, use the useContentPart hook instead.
|
|
512
|
+
*/
|
|
513
|
+
declare function ContentPartRenderer({ part, iconRenderer, thinkingSectionRenderer, taskSectionRenderer, brainIcon, chevronRightIcon, chevronDownIcon, searchIcon, folderIcon, settingsIcon, wrenchIcon, }: ContentPartRendererProps): React$1.ReactElement<{
|
|
514
|
+
title?: string;
|
|
515
|
+
duration?: number;
|
|
516
|
+
thought?: string;
|
|
517
|
+
collapsed?: boolean;
|
|
518
|
+
onCollapse?: () => void;
|
|
519
|
+
className?: string;
|
|
520
|
+
children?: React$1.ReactNode;
|
|
521
|
+
brainIcon?: React$1.ReactNode;
|
|
522
|
+
chevronRightIcon?: React$1.ReactNode;
|
|
523
|
+
chevronDownIcon?: React$1.ReactNode;
|
|
524
|
+
}, string | React$1.JSXElementConstructor<any>> | React$1.ReactElement<{
|
|
525
|
+
title?: string;
|
|
526
|
+
type?: string;
|
|
527
|
+
parts?: any[];
|
|
528
|
+
collapsed?: boolean;
|
|
529
|
+
onCollapse?: () => void;
|
|
530
|
+
className?: string;
|
|
531
|
+
children?: React$1.ReactNode;
|
|
532
|
+
taskIcon?: React$1.ReactNode;
|
|
533
|
+
chevronRightIcon?: React$1.ReactNode;
|
|
534
|
+
chevronDownIcon?: React$1.ReactNode;
|
|
535
|
+
}, string | React$1.JSXElementConstructor<any>> | React$1.ReactElement<{
|
|
536
|
+
'data-unknown-part-type': string;
|
|
537
|
+
}, string | React$1.JSXElementConstructor<any>>;
|
|
358
538
|
|
|
359
539
|
interface MathPartProps {
|
|
360
540
|
content: string;
|
|
@@ -363,11 +543,24 @@ interface MathPartProps {
|
|
|
363
543
|
children?: React$1.ReactNode;
|
|
364
544
|
displayMode?: boolean;
|
|
365
545
|
}
|
|
546
|
+
interface MathData {
|
|
547
|
+
content: string;
|
|
548
|
+
inline: boolean;
|
|
549
|
+
displayMode: boolean;
|
|
550
|
+
processedContent: string;
|
|
551
|
+
}
|
|
552
|
+
declare function useMath(props: Omit<MathPartProps, 'className' | 'children'>): MathData;
|
|
366
553
|
/**
|
|
367
554
|
* Generic math renderer component
|
|
368
555
|
* Renders plain math content by default - consumers should provide their own math rendering
|
|
556
|
+
*
|
|
557
|
+
* For headless usage, use the useMath hook instead.
|
|
369
558
|
*/
|
|
370
|
-
declare function MathPart({ content, inline, className, children, }: MathPartProps):
|
|
559
|
+
declare function MathPart({ content, inline, className, children, displayMode, }: MathPartProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
|
|
560
|
+
className: string;
|
|
561
|
+
'data-math-inline': boolean;
|
|
562
|
+
'data-math-display': boolean;
|
|
563
|
+
}, HTMLElement>;
|
|
371
564
|
|
|
372
565
|
interface CodeBlockProps {
|
|
373
566
|
language: string;
|
|
@@ -376,12 +569,26 @@ interface CodeBlockProps {
|
|
|
376
569
|
children?: React$1.ReactNode;
|
|
377
570
|
filename?: string;
|
|
378
571
|
}
|
|
572
|
+
interface CodeBlockData {
|
|
573
|
+
language: string;
|
|
574
|
+
code: string;
|
|
575
|
+
filename?: string;
|
|
576
|
+
lines: string[];
|
|
577
|
+
lineCount: number;
|
|
578
|
+
}
|
|
579
|
+
declare function useCodeBlock(props: Omit<CodeBlockProps, 'className' | 'children'>): CodeBlockData;
|
|
379
580
|
/**
|
|
380
581
|
* Generic code block component
|
|
381
582
|
* Renders plain code by default - consumers should provide their own styling and highlighting
|
|
583
|
+
*
|
|
584
|
+
* For headless usage, use the useCodeBlock hook instead.
|
|
382
585
|
*/
|
|
383
|
-
declare function CodeBlock({ language, code, className, children, }: CodeBlockProps):
|
|
586
|
+
declare function CodeBlock({ language, code, className, children, filename, }: CodeBlockProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
|
|
587
|
+
'data-filename': string;
|
|
588
|
+
className: string;
|
|
589
|
+
'data-language': string;
|
|
590
|
+
}, HTMLElement>;
|
|
384
591
|
|
|
385
|
-
export { ContentPartRenderer as AssistantMessageContentPart, CodeBlock, CodeProjectPart as CodeProjectBlock, CodeProjectPart, ContentPartRenderer, Icon, MathPart, MathPart as MathRenderer, Message, Message as MessageContent, Message as MessageRenderer, StreamingMessage, TaskSection, ThinkingSection, Message as V0MessageRenderer, useStreamingMessage };
|
|
386
|
-
export type { CodeBlockProps, CodeProjectPartProps as CodeProjectBlockProps, CodeProjectPartProps, ContentPartRendererProps, IconProps, MathPartProps, MathPartProps as MathRendererProps, MessageBinaryFormat, MessageBinaryFormatRow, MessageProps, MessageRendererProps, StreamingMessageProps, StreamingMessageState, TaskSectionProps, ThinkingSectionProps, UseStreamingMessageOptions, V0MessageRendererProps };
|
|
592
|
+
export { ContentPartRenderer as AssistantMessageContentPart, CodeBlock, CodeProjectPart as CodeProjectBlock, CodeProjectPart, ContentPartRenderer, Icon, IconProvider, MathPart, MathPart as MathRenderer, Message, Message as MessageContent, Message as MessageRenderer, StreamingMessage, TaskSection, ThinkingSection, Message as V0MessageRenderer, useCodeBlock, useCodeProject, useContentPart, useIcon, useMath, useMessage, useStreamingMessage, useStreamingMessageData, useTaskSection, useThinkingSection };
|
|
593
|
+
export type { CodeBlockData, CodeBlockProps, CodeProjectPartProps as CodeProjectBlockProps, CodeProjectData, CodeProjectPartProps, ContentPartData, ContentPartRendererProps, IconData, IconProps, MathData, MathPartProps, MathPartProps as MathRendererProps, MessageBinaryFormat, MessageBinaryFormatRow, MessageData, MessageElement, MessageProps, MessageRendererProps, StreamingMessageData, StreamingMessageProps, StreamingMessageState, TaskPartData, TaskSectionData, TaskSectionProps, ThinkingSectionData, ThinkingSectionProps, UseStreamingMessageOptions, V0MessageRendererProps };
|
|
387
594
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sources":["../src/types.ts","../src/components/message.tsx","../src/hooks/use-streaming-message.tsx","../src/components/streaming-message.tsx","../src/components/icon.tsx","../src/components/thinking-section.tsx","../src/components/task-section.tsx","../src/components/code-project-part.tsx","../src/components/content-part-renderer.tsx","../src/components/math-part.tsx","../src/components/code-block.tsx"],"sourcesContent":["/**\n * Binary format for message content as returned by the v0 Platform API\n * Each row is a tuple where the first element is the type and the rest are data\n */\nexport type MessageBinaryFormat = [number, ...any[]][]\n\n/**\n * Individual row in the message binary format\n */\nexport type MessageBinaryFormatRow = MessageBinaryFormat[number]\n\n/**\n * Props for the Message component\n */\nexport interface MessageProps {\n /**\n * The parsed content from the v0 Platform API\n * This should be the JSON.parsed value of the 'content' field from API responses\n */\n content: MessageBinaryFormat\n\n /**\n * Optional message ID for tracking purposes\n */\n messageId?: string\n\n /**\n * Role of the message sender\n */\n role?: 'user' | 'assistant' | 'system' | 'tool'\n\n /**\n * Whether the message is currently being streamed\n */\n streaming?: boolean\n\n /**\n * Whether this is the last message in the conversation\n */\n isLastMessage?: boolean\n\n /**\n * Custom className for styling the root container\n */\n className?: string\n\n /**\n * Custom component renderers (react-markdown style)\n * Override specific components by name\n * Can be either a React component or an object with className for simple styling\n */\n components?: {\n // Content components\n CodeBlock?: React.ComponentType<{\n language: string\n code: string\n className?: string\n }>\n MathPart?: React.ComponentType<{\n content: string\n inline?: boolean\n className?: string\n }>\n CodeProjectPart?: React.ComponentType<{\n title?: string\n filename?: string\n code?: string\n language?: string\n collapsed?: boolean\n className?: string\n }>\n ThinkingSection?: React.ComponentType<{\n title?: string\n duration?: number\n thought?: string\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n TaskSection?: React.ComponentType<{\n title?: string\n type?: string\n parts?: any[]\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n taskIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n Icon?: React.ComponentType<{\n name:\n | 'chevron-right'\n | 'chevron-down'\n | 'search'\n | 'folder'\n | 'settings'\n | 'file-text'\n | 'brain'\n | 'wrench'\n className?: string\n }>\n\n // HTML elements (react-markdown style)\n // Can be either a React component or an object with className\n p?:\n | React.ComponentType<React.HTMLAttributes<HTMLParagraphElement>>\n | { className?: string }\n h1?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h2?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h3?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h4?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h5?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h6?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n ul?:\n | React.ComponentType<React.HTMLAttributes<HTMLUListElement>>\n | { className?: string }\n ol?:\n | React.ComponentType<React.HTMLAttributes<HTMLOListElement>>\n | { className?: string }\n li?:\n | React.ComponentType<React.HTMLAttributes<HTMLLIElement>>\n | { className?: string }\n blockquote?:\n | React.ComponentType<React.HTMLAttributes<HTMLQuoteElement>>\n | { className?: string }\n code?:\n | React.ComponentType<React.HTMLAttributes<HTMLElement>>\n | { className?: string }\n pre?:\n | React.ComponentType<React.HTMLAttributes<HTMLPreElement>>\n | { className?: string }\n strong?:\n | React.ComponentType<React.HTMLAttributes<HTMLElement>>\n | { className?: string }\n em?:\n | React.ComponentType<React.HTMLAttributes<HTMLElement>>\n | { className?: string }\n a?:\n | React.ComponentType<React.AnchorHTMLAttributes<HTMLAnchorElement>>\n | { className?: string }\n hr?:\n | React.ComponentType<React.HTMLAttributes<HTMLHRElement>>\n | { className?: string }\n div?:\n | React.ComponentType<React.HTMLAttributes<HTMLDivElement>>\n | { className?: string }\n span?:\n | React.ComponentType<React.HTMLAttributes<HTMLSpanElement>>\n | { className?: string }\n }\n\n /**\n * @deprecated Use `components` instead. Will be removed in next major version.\n */\n renderers?: {\n CodeBlock?: React.ComponentType<{\n language: string\n code: string\n className?: string\n }>\n MathRenderer?: React.ComponentType<{\n content: string\n inline?: boolean\n className?: string\n }>\n MathPart?: React.ComponentType<{\n content: string\n inline?: boolean\n className?: string\n }>\n Icon?: React.ComponentType<{\n name:\n | 'chevron-right'\n | 'chevron-down'\n | 'search'\n | 'folder'\n | 'settings'\n | 'file-text'\n | 'brain'\n | 'wrench'\n className?: string\n }>\n }\n}\n\n// Backward compatibility exports\nexport type MessageRendererProps = MessageProps\nexport type V0MessageRendererProps = MessageProps\n// Note: MessageStyles/MessageRendererStyles/V0MessageRendererStyles removed as styles prop is no longer supported\n","import React from 'react'\nimport { MessageProps } from '../types'\nimport { MathPart } from './math-part'\nimport { CodeBlock } from './code-block'\nimport { CodeProjectPart } from './code-project-part'\nimport { ContentPartRenderer } from './content-part-renderer'\nimport { cn } from '../utils/cn'\n\n// Simplified renderer that matches v0's exact approach\nfunction MessageImpl({\n content,\n messageId = 'unknown',\n role: _role = 'assistant',\n streaming: _streaming = false,\n isLastMessage: _isLastMessage = false,\n className,\n components,\n renderers, // deprecated\n}: MessageProps) {\n if (!Array.isArray(content)) {\n console.warn(\n 'MessageContent: content must be an array (MessageBinaryFormat)',\n )\n return null\n }\n\n // Merge components and renderers (backward compatibility)\n const mergedComponents = {\n ...components,\n // Map legacy renderers to new component names\n ...(renderers?.CodeBlock && { CodeBlock: renderers.CodeBlock }),\n ...(renderers?.MathRenderer && { MathPart: renderers.MathRenderer }),\n ...(renderers?.MathPart && { MathPart: renderers.MathPart }),\n ...(renderers?.Icon && { Icon: renderers.Icon }),\n }\n\n // Process content exactly like v0's Renderer component\n const elements = content.map(([type, data], index) => {\n const key = `${messageId}-${index}`\n\n // Markdown data (type 0) - this is the main content\n if (type === 0) {\n return <Elements key={key} data={data} components={mergedComponents} />\n }\n\n // Metadata (type 1) - extract context but don't render\n if (type === 1) {\n // In the future, we could extract sources/context here like v0 does\n // For now, just return null like v0's renderer\n return null\n }\n\n // Other types - v0 doesn't handle these in the main renderer\n return null\n })\n\n return <div className={className}>{elements}</div>\n}\n\n// This component handles the markdown data array (equivalent to v0's Elements component)\nfunction Elements({\n data,\n components,\n}: {\n data: any\n components?: MessageProps['components']\n}) {\n // Handle case where data might not be an array due to streaming/patching\n if (!Array.isArray(data)) {\n return null\n }\n\n const renderedElements = data\n .map((item, index) => {\n const key = `element-${index}`\n return renderElement(item, key, components)\n })\n .filter(Boolean) // Filter out null/undefined elements\n\n return <>{renderedElements}</>\n}\n\n// Render individual elements (equivalent to v0's element rendering logic)\nfunction renderElement(\n element: any,\n key: string,\n components?: MessageProps['components'],\n): React.ReactNode {\n if (typeof element === 'string') {\n return <span key={key}>{element}</span>\n }\n\n if (!Array.isArray(element)) {\n return null\n }\n\n const [tagName, props, ...children] = element\n\n if (!tagName) {\n return null\n }\n\n // Handle special v0 Platform API elements\n if (tagName === 'AssistantMessageContentPart') {\n return (\n <ContentPartRenderer\n key={key}\n part={props.part}\n iconRenderer={components?.Icon}\n thinkingSectionRenderer={components?.ThinkingSection}\n taskSectionRenderer={components?.TaskSection}\n />\n )\n }\n\n if (tagName === 'Codeblock') {\n const CustomCodeProjectPart = components?.CodeProjectPart\n const CodeProjectComponent = CustomCodeProjectPart || CodeProjectPart\n return (\n <CodeProjectComponent\n key={key}\n language={props.lang}\n code={children[0]}\n iconRenderer={components?.Icon}\n />\n )\n }\n\n if (tagName === 'text') {\n return <span key={key}>{children[0] || ''}</span>\n }\n\n // Render children\n const renderedChildren = children\n .map((child, childIndex) => {\n const childKey = `${key}-child-${childIndex}`\n return renderElement(child, childKey, components)\n })\n .filter(Boolean)\n\n // Handle standard HTML elements\n const className = props?.className\n const componentOrConfig = components?.[tagName as keyof typeof components]\n\n if (typeof componentOrConfig === 'function') {\n const Component = componentOrConfig\n return (\n <Component key={key} {...props} className={className}>\n {renderedChildren}\n </Component>\n )\n } else if (componentOrConfig && typeof componentOrConfig === 'object') {\n const mergedClassName = cn(className, componentOrConfig.className)\n return React.createElement(\n tagName,\n { key, ...props, className: mergedClassName },\n renderedChildren,\n )\n } else {\n // Default HTML element rendering\n const elementProps: Record<string, any> = { key, ...props }\n if (className) {\n elementProps.className = className\n }\n\n // Special handling for links\n if (tagName === 'a') {\n elementProps.target = '_blank'\n elementProps.rel = 'noopener noreferrer'\n }\n\n return React.createElement(tagName, elementProps, renderedChildren)\n }\n}\n\n/**\n * Main component for rendering v0 Platform API message content\n */\nexport const Message = React.memo(MessageImpl)\n","import { useRef, useSyncExternalStore } from 'react'\nimport { MessageBinaryFormat } from '../types'\nimport * as jsondiffpatch from 'jsondiffpatch'\n\nconst jdf = jsondiffpatch.create({})\n\n// Exact copy of the patch function from v0/chat/lib/diffpatch.ts\nfunction patch(original: any, delta: any) {\n const newObj = jdf.clone(original)\n\n // Check for our customized delta\n if (Array.isArray(delta) && delta[1] === 9 && delta[2] === 9) {\n // Get the path to the modified element\n const indexes = delta[0].slice(0, -1)\n // Get the string to be appended\n const value = delta[0].slice(-1)\n let obj = newObj as any\n for (const index of indexes) {\n if (typeof obj[index] === 'string') {\n obj[index] += value\n return newObj\n }\n obj = obj[index]\n }\n }\n\n // If not custom delta, apply standard jsondiffpatch-ing\n jdf.patch(newObj, delta)\n return newObj\n}\n\nexport interface StreamingMessageState {\n content: MessageBinaryFormat\n isStreaming: boolean\n error?: string\n isComplete: boolean\n}\n\nexport interface UseStreamingMessageOptions {\n onChunk?: (chunk: MessageBinaryFormat) => void\n onComplete?: (finalContent: MessageBinaryFormat) => void\n onError?: (error: string) => void\n onChatData?: (chatData: any) => void\n}\n\n// Stream state manager - isolated from React lifecycle\nclass StreamStateManager {\n private content: MessageBinaryFormat = []\n private isStreaming: boolean = false\n private error?: string\n private isComplete: boolean = false\n private callbacks = new Set<() => void>()\n private processedStreams = new WeakSet<ReadableStream<Uint8Array>>()\n private cachedState: StreamingMessageState | null = null\n\n subscribe = (callback: () => void) => {\n this.callbacks.add(callback)\n return () => {\n this.callbacks.delete(callback)\n }\n }\n\n private notifySubscribers = () => {\n // Invalidate cached state when state changes\n this.cachedState = null\n this.callbacks.forEach((callback) => callback())\n }\n\n getState = (): StreamingMessageState => {\n // Return cached state to prevent infinite re-renders\n if (this.cachedState === null) {\n this.cachedState = {\n content: this.content,\n isStreaming: this.isStreaming,\n error: this.error,\n isComplete: this.isComplete,\n }\n }\n return this.cachedState\n }\n\n processStream = async (\n stream: ReadableStream<Uint8Array>,\n options: UseStreamingMessageOptions = {},\n ): Promise<void> => {\n // Prevent processing the same stream multiple times\n if (this.processedStreams.has(stream)) {\n return\n }\n\n // Handle locked streams gracefully\n if (stream.locked) {\n console.warn('Stream is locked, cannot process')\n return\n }\n\n this.processedStreams.add(stream)\n this.reset()\n this.setStreaming(true)\n\n try {\n await this.readStream(stream, options)\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Unknown streaming error'\n this.setError(errorMessage)\n options.onError?.(errorMessage)\n } finally {\n this.setStreaming(false)\n }\n }\n\n private reset = () => {\n this.content = []\n this.isStreaming = false\n this.error = undefined\n this.isComplete = false\n this.notifySubscribers()\n }\n\n private setStreaming = (streaming: boolean) => {\n this.isStreaming = streaming\n this.notifySubscribers()\n }\n\n private setError = (error: string) => {\n this.error = error\n this.notifySubscribers()\n }\n\n private setComplete = (complete: boolean) => {\n this.isComplete = complete\n this.notifySubscribers()\n }\n\n private updateContent = (newContent: MessageBinaryFormat) => {\n this.content = [...newContent]\n this.notifySubscribers()\n }\n\n private readStream = async (\n stream: ReadableStream<Uint8Array>,\n options: UseStreamingMessageOptions,\n ): Promise<void> => {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentContent: MessageBinaryFormat = []\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n break\n }\n\n const chunk = decoder.decode(value, { stream: true })\n buffer += chunk\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n // Handle SSE format (data: ...)\n let jsonData: string\n if (line.startsWith('data: ')) {\n jsonData = line.slice(6) // Remove \"data: \" prefix\n if (jsonData === '[DONE]') {\n this.setComplete(true)\n options.onComplete?.(currentContent)\n return\n }\n } else {\n // Handle raw JSON lines (fallback)\n jsonData = line\n }\n\n try {\n // Parse the JSON data\n const parsedData = JSON.parse(jsonData)\n\n // Handle v0 streaming format\n if (parsedData.type === 'connected') {\n continue\n } else if (parsedData.type === 'done') {\n this.setComplete(true)\n options.onComplete?.(currentContent)\n return\n } else if (parsedData.object === 'chat' && parsedData.id) {\n // Handle the initial chat data message\n options.onChatData?.(parsedData)\n continue\n } else if (parsedData.delta) {\n // Apply the delta using jsondiffpatch\n const patchedContent = patch(currentContent, parsedData.delta)\n currentContent = Array.isArray(patchedContent)\n ? (patchedContent as MessageBinaryFormat)\n : []\n\n this.updateContent(currentContent)\n options.onChunk?.(currentContent)\n }\n } catch (e) {\n console.warn('Failed to parse streaming data:', line, e)\n }\n }\n }\n\n this.setComplete(true)\n options.onComplete?.(currentContent)\n } finally {\n reader.releaseLock()\n }\n }\n}\n\n/**\n * Hook for handling streaming message content from v0 API using useSyncExternalStore\n */\nexport function useStreamingMessage(\n stream: ReadableStream<Uint8Array> | null,\n options: UseStreamingMessageOptions = {},\n): StreamingMessageState {\n // Create a stable stream manager instance\n const managerRef = useRef<StreamStateManager | null>(null)\n if (!managerRef.current) {\n managerRef.current = new StreamStateManager()\n }\n\n const manager = managerRef.current\n\n // Subscribe to state changes using useSyncExternalStore\n const state = useSyncExternalStore(\n manager.subscribe,\n manager.getState,\n manager.getState,\n )\n\n // Process stream when it changes\n const lastStreamRef = useRef<ReadableStream<Uint8Array> | null>(null)\n\n if (stream !== lastStreamRef.current) {\n lastStreamRef.current = stream\n if (stream) {\n manager.processStream(stream, options)\n }\n }\n\n return state\n}\n","import React from 'react'\nimport { Message } from './message'\nimport {\n useStreamingMessage,\n UseStreamingMessageOptions,\n} from '../hooks/use-streaming-message'\nimport { MessageProps } from '../types'\n\nexport interface StreamingMessageProps\n extends Omit<MessageProps, 'content' | 'streaming' | 'isLastMessage'>,\n UseStreamingMessageOptions {\n /**\n * The streaming response from v0.chats.create() with responseMode: 'experimental_stream'\n */\n stream: ReadableStream<Uint8Array> | null\n\n /**\n * Show a loading indicator while no content has been received yet\n */\n showLoadingIndicator?: boolean\n\n /**\n * Custom loading component\n */\n loadingComponent?: React.ReactNode\n\n /**\n * Custom error component\n */\n errorComponent?: (error: string) => React.ReactNode\n}\n\n/**\n * Component for rendering streaming message content from v0 API\n *\n * @example\n * ```tsx\n * import { v0 } from 'v0-sdk'\n * import { StreamingMessage } from '@v0-sdk/react'\n *\n * function ChatDemo() {\n * const [stream, setStream] = useState<ReadableStream<Uint8Array> | null>(null)\n *\n * const handleSubmit = async () => {\n * const response = await v0.chats.create({\n * message: 'Create a button component',\n * responseMode: 'experimental_stream'\n * })\n * setStream(response)\n * }\n *\n * return (\n * <div>\n * <button onClick={handleSubmit}>Send Message</button>\n * {stream && (\n * <StreamingMessage\n * stream={stream}\n * messageId=\"demo-message\"\n * role=\"assistant\"\n * onComplete={(content) => handleCompletion(content)}\n * onChatData={(chatData) => handleChatData(chatData)}\n * />\n * )}\n * </div>\n * )\n * }\n * ```\n */\nexport function StreamingMessage({\n stream,\n showLoadingIndicator = true,\n loadingComponent,\n errorComponent,\n onChunk,\n onComplete,\n onError,\n onChatData,\n ...messageProps\n}: StreamingMessageProps) {\n const { content, isStreaming, error, isComplete } = useStreamingMessage(\n stream,\n {\n onChunk,\n onComplete,\n onError,\n onChatData,\n },\n )\n\n // Handle error state\n if (error) {\n if (errorComponent) {\n return <>{errorComponent(error)}</>\n }\n return (\n <div className=\"text-red-500 p-4 border border-red-200 rounded\">\n Error: {error}\n </div>\n )\n }\n\n // Handle loading state\n if (showLoadingIndicator && isStreaming && content.length === 0) {\n if (loadingComponent) {\n return <>{loadingComponent}</>\n }\n return (\n <div className=\"flex items-center space-x-2 text-gray-500\">\n <div className=\"animate-spin h-4 w-4 border-2 border-gray-300 border-t-gray-600 rounded-full\"></div>\n <span>Loading...</span>\n </div>\n )\n }\n\n // Render the message content\n return (\n <Message\n {...messageProps}\n content={content}\n streaming={isStreaming}\n isLastMessage={true}\n />\n )\n}\n","import React, { createContext, useContext } from 'react'\n\n// Context for providing custom icon implementation\nconst IconContext = createContext<React.ComponentType<IconProps> | null>(null)\n\nexport interface IconProps {\n name:\n | 'chevron-right'\n | 'chevron-down'\n | 'search'\n | 'folder'\n | 'settings'\n | 'file-text'\n | 'brain'\n | 'wrench'\n className?: string\n}\n\n/**\n * Generic icon component that can be customized by consumers.\n * By default, renders a simple fallback. Consumers should provide\n * their own icon implementation via context or props.\n */\nexport function Icon(props: IconProps) {\n const CustomIcon = useContext(IconContext)\n\n // Use custom icon implementation if provided via context\n if (CustomIcon) {\n return <CustomIcon {...props} />\n }\n\n // Fallback implementation - consumers should override this\n return (\n <span\n className={props.className}\n data-icon={props.name}\n suppressHydrationWarning\n aria-label={props.name.replace('-', ' ')}\n >\n {getIconFallback(props.name)}\n </span>\n )\n}\n\n/**\n * Provider for custom icon implementation\n */\nexport function IconProvider({\n children,\n component,\n}: {\n children: React.ReactNode\n component: React.ComponentType<IconProps>\n}) {\n return (\n <IconContext.Provider value={component}>{children}</IconContext.Provider>\n )\n}\n\nfunction getIconFallback(name: string): string {\n const iconMap: Record<string, string> = {\n 'chevron-right': '▶',\n 'chevron-down': '▼',\n search: '🔍',\n folder: '📁',\n settings: '⚙️',\n 'file-text': '📄',\n brain: '🧠',\n wrench: '🔧',\n }\n return iconMap[name] || '•'\n}\n","import React, { useState } from 'react'\nimport { Icon, IconProps } from './icon'\n\nexport interface ThinkingSectionProps {\n title?: string\n duration?: number\n thought?: string\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n iconRenderer?: React.ComponentType<IconProps>\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n}\n\n/**\n * Generic thinking section component\n * Renders a collapsible section with basic structure - consumers provide styling\n */\nexport function ThinkingSection({\n title,\n duration,\n thought,\n collapsed: initialCollapsed = true,\n onCollapse,\n className,\n children,\n iconRenderer,\n brainIcon,\n chevronRightIcon,\n chevronDownIcon,\n}: ThinkingSectionProps) {\n const [internalCollapsed, setInternalCollapsed] = useState(initialCollapsed)\n const collapsed = onCollapse ? initialCollapsed : internalCollapsed\n const handleCollapse =\n onCollapse || (() => setInternalCollapsed(!internalCollapsed))\n\n // If children provided, use that (allows complete customization)\n if (children) {\n return <>{children}</>\n }\n\n return (\n <div className={className} data-component=\"thinking-section\">\n <button onClick={handleCollapse} data-expanded={!collapsed} data-button>\n <div data-icon-container>\n {collapsed ? (\n <>\n {brainIcon ||\n (iconRenderer ? (\n React.createElement(iconRenderer, { name: 'brain' })\n ) : (\n <Icon name=\"brain\" />\n ))}\n {chevronRightIcon ||\n (iconRenderer ? (\n React.createElement(iconRenderer, { name: 'chevron-right' })\n ) : (\n <Icon name=\"chevron-right\" />\n ))}\n </>\n ) : (\n chevronDownIcon ||\n (iconRenderer ? (\n React.createElement(iconRenderer, { name: 'chevron-down' })\n ) : (\n <Icon name=\"chevron-down\" />\n ))\n )}\n </div>\n <span data-title>\n {title || 'Thinking'}\n {duration && ` for ${Math.round(duration)}s`}\n </span>\n </button>\n {!collapsed && thought && (\n <div data-content>\n <div data-thought-container>\n {thought.split('\\n\\n').map((paragraph, index) => (\n <div key={index} data-paragraph>\n {paragraph}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )\n}\n","import React, { useState } from 'react'\nimport { Icon, IconProps } from './icon'\n\nexport interface TaskSectionProps {\n title?: string\n type?: string\n parts?: any[]\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n iconRenderer?: React.ComponentType<IconProps>\n taskIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n}\n\nfunction getTypeIcon(\n type?: string,\n title?: string,\n iconRenderer?: React.ComponentType<IconProps>,\n) {\n // Check title content for specific cases\n if (title?.includes('No issues found')) {\n return iconRenderer ? (\n React.createElement(iconRenderer, { name: 'wrench' })\n ) : (\n <Icon name=\"wrench\" />\n )\n }\n if (title?.includes('Analyzed codebase')) {\n return iconRenderer ? (\n React.createElement(iconRenderer, { name: 'search' })\n ) : (\n <Icon name=\"search\" />\n )\n }\n\n // Fallback to type-based icons\n switch (type) {\n case 'task-search-web-v1':\n return iconRenderer ? (\n React.createElement(iconRenderer, { name: 'search' })\n ) : (\n <Icon name=\"search\" />\n )\n case 'task-search-repo-v1':\n return iconRenderer ? (\n React.createElement(iconRenderer, { name: 'folder' })\n ) : (\n <Icon name=\"folder\" />\n )\n case 'task-diagnostics-v1':\n return iconRenderer ? (\n React.createElement(iconRenderer, { name: 'settings' })\n ) : (\n <Icon name=\"settings\" />\n )\n default:\n return iconRenderer ? (\n React.createElement(iconRenderer, { name: 'wrench' })\n ) : (\n <Icon name=\"wrench\" />\n )\n }\n}\n\nfunction renderTaskPart(\n part: any,\n index: number,\n iconRenderer?: React.ComponentType<IconProps>,\n) {\n if (part.type === 'search-web') {\n if (part.status === 'searching') {\n return <div key={index}>{`Searching \"${part.query}\"`}</div>\n }\n if (part.status === 'analyzing') {\n return <div key={index}>{`Analyzing ${part.count} results...`}</div>\n }\n if (part.status === 'complete' && part.answer) {\n return (\n <div key={index}>\n <p>{part.answer}</p>\n {part.sources && part.sources.length > 0 && (\n <div>\n {part.sources.map((source: any, sourceIndex: number) => (\n <a\n key={sourceIndex}\n href={source.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {source.title}\n </a>\n ))}\n </div>\n )}\n </div>\n )\n }\n }\n\n if (part.type === 'search-repo') {\n if (part.status === 'searching') {\n return <div key={index}>{`Searching \"${part.query}\"`}</div>\n }\n if (part.status === 'reading' && part.files) {\n return (\n <div key={index}>\n <span>Reading files</span>\n {part.files.map((file: string, fileIndex: number) => (\n <span key={fileIndex}>\n {iconRenderer ? (\n React.createElement(iconRenderer, { name: 'file-text' })\n ) : (\n <Icon name=\"file-text\" />\n )}{' '}\n {file}\n </span>\n ))}\n </div>\n )\n }\n }\n\n if (part.type === 'diagnostics') {\n if (part.status === 'checking') {\n return <div key={index}>Checking for issues...</div>\n }\n if (part.status === 'complete' && part.issues === 0) {\n return <div key={index}>✅ No issues found</div>\n }\n }\n\n return <div key={index}>{JSON.stringify(part)}</div>\n}\n\n/**\n * Generic task section component\n * Renders a collapsible task section with basic structure - consumers provide styling\n */\nexport function TaskSection({\n title,\n type,\n parts = [],\n collapsed: initialCollapsed = true,\n onCollapse,\n className,\n children,\n iconRenderer,\n taskIcon,\n chevronRightIcon,\n chevronDownIcon,\n}: TaskSectionProps) {\n const [internalCollapsed, setInternalCollapsed] = useState(initialCollapsed)\n const collapsed = onCollapse ? initialCollapsed : internalCollapsed\n const handleCollapse =\n onCollapse || (() => setInternalCollapsed(!internalCollapsed))\n\n // If children provided, use that (allows complete customization)\n if (children) {\n return <>{children}</>\n }\n\n // Count meaningful parts (parts that would render something)\n const meaningfulParts = parts.filter((part) => {\n // Check if the part would render meaningful content\n if (part.type === 'search-web') {\n return (\n part.status === 'searching' ||\n part.status === 'analyzing' ||\n (part.status === 'complete' && part.answer)\n )\n }\n if (part.type === 'starting-repo-search' && part.query) return true\n if (part.type === 'select-files' && part.filePaths?.length > 0) return true\n if (part.type === 'starting-web-search' && part.query) return true\n if (part.type === 'got-results' && part.count) return true\n if (part.type === 'finished-web-search' && part.answer) return true\n if (part.type === 'diagnostics-passed') return true\n if (part.type === 'fetching-diagnostics') return true\n // Add more meaningful part types as needed\n return false\n })\n\n // If there's only one meaningful part, show just the content without the collapsible wrapper\n if (meaningfulParts.length === 1) {\n return (\n <div className={className} data-component=\"task-section-inline\">\n <div data-part>\n {renderTaskPart(meaningfulParts[0], 0, iconRenderer)}\n </div>\n </div>\n )\n }\n\n return (\n <div className={className} data-component=\"task-section\">\n <button onClick={handleCollapse} data-expanded={!collapsed} data-button>\n <div data-icon-container>\n <div data-task-icon>\n {taskIcon || getTypeIcon(type, title, iconRenderer)}\n </div>\n {collapsed\n ? chevronRightIcon ||\n (iconRenderer ? (\n React.createElement(iconRenderer, { name: 'chevron-right' })\n ) : (\n <Icon name=\"chevron-right\" />\n ))\n : chevronDownIcon ||\n (iconRenderer ? (\n React.createElement(iconRenderer, { name: 'chevron-down' })\n ) : (\n <Icon name=\"chevron-down\" />\n ))}\n </div>\n <span data-title>{title || 'Task'}</span>\n </button>\n {!collapsed && (\n <div data-content>\n <div data-parts-container>\n {parts.map((part, index) => (\n <div key={index} data-part>\n {renderTaskPart(part, index, iconRenderer)}\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )\n}\n","import React, { useState } from 'react'\nimport { CodeBlock } from './code-block'\nimport { Icon, IconProps } from './icon'\n\nexport interface CodeProjectPartProps {\n title?: string\n filename?: string\n code?: string\n language?: string\n collapsed?: boolean\n className?: string\n children?: React.ReactNode\n iconRenderer?: React.ComponentType<IconProps>\n}\n\n/**\n * Generic code project block component\n * Renders a collapsible code project with basic structure - consumers provide styling\n */\nexport function CodeProjectPart({\n title,\n filename,\n code,\n language = 'typescript',\n collapsed: initialCollapsed = true,\n className,\n children,\n iconRenderer,\n}: CodeProjectPartProps) {\n const [collapsed, setCollapsed] = useState(initialCollapsed)\n\n // If children provided, use that (allows complete customization)\n if (children) {\n return <>{children}</>\n }\n\n return (\n <div className={className} data-component=\"code-project-block\">\n <button\n onClick={() => setCollapsed(!collapsed)}\n data-expanded={!collapsed}\n >\n <div data-header>\n {iconRenderer ? (\n React.createElement(iconRenderer, { name: 'folder' })\n ) : (\n <Icon name=\"folder\" />\n )}\n <span data-title>{title || 'Code Project'}</span>\n </div>\n <span data-version>v1</span>\n </button>\n {!collapsed && (\n <div data-content>\n <div data-file-list>\n <div data-file data-active>\n {iconRenderer ? (\n React.createElement(iconRenderer, { name: 'file-text' })\n ) : (\n <Icon name=\"file-text\" />\n )}\n <span data-filename>{filename}</span>\n <span data-filepath>app/page.tsx</span>\n </div>\n {/* Additional files could be added here */}\n <div data-file>\n {iconRenderer ? (\n React.createElement(iconRenderer, { name: 'file-text' })\n ) : (\n <Icon name=\"file-text\" />\n )}\n <span data-filename>layout.tsx</span>\n <span data-filepath>app/layout.tsx</span>\n </div>\n <div data-file>\n {iconRenderer ? (\n React.createElement(iconRenderer, { name: 'file-text' })\n ) : (\n <Icon name=\"file-text\" />\n )}\n <span data-filename>globals.css</span>\n <span data-filepath>app/globals.css</span>\n </div>\n </div>\n {code && <CodeBlock language={language} code={code} />}\n </div>\n )}\n </div>\n )\n}\n","import React, { useState } from 'react'\nimport { ThinkingSection } from './thinking-section'\nimport { TaskSection } from './task-section'\nimport { IconProps } from './icon'\n\nexport interface ContentPartRendererProps {\n part: any\n iconRenderer?: React.ComponentType<IconProps>\n thinkingSectionRenderer?: React.ComponentType<{\n title?: string\n duration?: number\n thought?: string\n collapsed?: boolean\n className?: string\n children?: React.ReactNode\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n taskSectionRenderer?: React.ComponentType<{\n title?: string\n type?: string\n parts?: any[]\n collapsed?: boolean\n className?: string\n children?: React.ReactNode\n taskIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n // Individual icon props for direct icon usage\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n searchIcon?: React.ReactNode\n folderIcon?: React.ReactNode\n settingsIcon?: React.ReactNode\n wrenchIcon?: React.ReactNode\n}\n\nexport function ContentPartRenderer({\n part,\n iconRenderer,\n thinkingSectionRenderer,\n taskSectionRenderer,\n brainIcon,\n chevronRightIcon,\n chevronDownIcon,\n searchIcon,\n folderIcon,\n settingsIcon,\n wrenchIcon,\n}: ContentPartRendererProps) {\n if (!part) return null\n\n const { type, parts = [], ...metadata } = part\n\n switch (type) {\n case 'task-thinking-v1': {\n const thinkingPart = parts.find((p: any) => p.type === 'thinking-end')\n const ThinkingComponent = thinkingSectionRenderer || ThinkingSection\n const [collapsed, setCollapsed] = useState(true)\n\n return (\n <ThinkingComponent\n title=\"Thought\"\n duration={thinkingPart?.duration}\n thought={thinkingPart?.thought}\n collapsed={collapsed}\n onCollapse={() => setCollapsed(!collapsed)}\n brainIcon={brainIcon}\n chevronRightIcon={chevronRightIcon}\n chevronDownIcon={chevronDownIcon}\n />\n )\n }\n\n case 'task-search-web-v1': {\n const TaskComponent = taskSectionRenderer || TaskSection\n const [collapsed, setCollapsed] = useState(true)\n\n return (\n <TaskComponent\n title={metadata.taskNameComplete || metadata.taskNameActive}\n type={type}\n parts={parts}\n collapsed={collapsed}\n onCollapse={() => setCollapsed(!collapsed)}\n taskIcon={searchIcon}\n chevronRightIcon={chevronRightIcon}\n chevronDownIcon={chevronDownIcon}\n />\n )\n }\n\n case 'task-search-repo-v1': {\n const TaskComponent = taskSectionRenderer || TaskSection\n const [collapsed, setCollapsed] = useState(true)\n\n return (\n <TaskComponent\n title={metadata.taskNameComplete || metadata.taskNameActive}\n type={type}\n parts={parts}\n collapsed={collapsed}\n onCollapse={() => setCollapsed(!collapsed)}\n taskIcon={folderIcon}\n chevronRightIcon={chevronRightIcon}\n chevronDownIcon={chevronDownIcon}\n />\n )\n }\n\n case 'task-diagnostics-v1': {\n const TaskComponent = taskSectionRenderer || TaskSection\n const [collapsed, setCollapsed] = useState(true)\n\n return (\n <TaskComponent\n title={metadata.taskNameComplete || metadata.taskNameActive}\n type={type}\n parts={parts}\n collapsed={collapsed}\n onCollapse={() => setCollapsed(!collapsed)}\n taskIcon={settingsIcon}\n chevronRightIcon={chevronRightIcon}\n chevronDownIcon={chevronDownIcon}\n />\n )\n }\n\n case 'task-read-file-v1': {\n const TaskComponent = taskSectionRenderer || TaskSection\n const [collapsed, setCollapsed] = useState(true)\n\n return (\n <TaskComponent\n title={\n metadata.taskNameComplete ||\n metadata.taskNameActive ||\n 'Reading file'\n }\n type={type}\n parts={parts}\n collapsed={collapsed}\n onCollapse={() => setCollapsed(!collapsed)}\n taskIcon={folderIcon}\n chevronRightIcon={chevronRightIcon}\n chevronDownIcon={chevronDownIcon}\n />\n )\n }\n\n case 'task-coding-v1': {\n const TaskComponent = taskSectionRenderer || TaskSection\n const [collapsed, setCollapsed] = useState(true)\n\n return (\n <TaskComponent\n title={\n metadata.taskNameComplete || metadata.taskNameActive || 'Coding'\n }\n type={type}\n parts={parts}\n collapsed={collapsed}\n onCollapse={() => setCollapsed(!collapsed)}\n taskIcon={wrenchIcon}\n chevronRightIcon={chevronRightIcon}\n chevronDownIcon={chevronDownIcon}\n />\n )\n }\n\n case 'task-start-v1':\n // Usually just indicates task start - can be hidden or show as status\n return null\n\n default:\n return <div data-unknown-part-type={type}>Unknown part type: {type}</div>\n }\n}\n","import React from 'react'\n\nexport interface MathPartProps {\n content: string\n inline?: boolean\n className?: string\n children?: React.ReactNode\n displayMode?: boolean\n}\n\n/**\n * Generic math renderer component\n * Renders plain math content by default - consumers should provide their own math rendering\n */\nexport function MathPart({\n content,\n inline = false,\n className = '',\n children,\n}: MathPartProps) {\n // If children provided, use that (allows complete customization)\n if (children) {\n return <>{children}</>\n }\n\n // Simple fallback - just render plain math content\n const Element = inline ? 'span' : 'div'\n\n return (\n <Element className={className} data-math-inline={inline}>\n {content}\n </Element>\n )\n}\n","import React from 'react'\n\nexport interface CodeBlockProps {\n language: string\n code: string\n className?: string\n children?: React.ReactNode\n filename?: string\n}\n\n/**\n * Generic code block component\n * Renders plain code by default - consumers should provide their own styling and highlighting\n */\nexport function CodeBlock({\n language,\n code,\n className = '',\n children,\n}: CodeBlockProps) {\n // If children provided, use that (allows complete customization)\n if (children) {\n return <>{children}</>\n }\n\n // Simple fallback - just render plain code\n return (\n <pre className={className} data-language={language}>\n <code>{code}</code>\n </pre>\n )\n}\n"],"names":[],"mappings":";;;AAAA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACA;;AC5KP;AACA;AACA;AACA;AACO;;ACLA;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACbA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACxDA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACRA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACjBA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACjBA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACdA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;AClCA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACXA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;;"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sources":["../src/types.ts","../src/components/message.ts","../src/hooks/use-streaming-message.ts","../src/components/streaming-message.ts","../src/components/icon.ts","../src/components/thinking-section.ts","../src/components/task-section.ts","../src/components/code-project-part.ts","../src/components/content-part-renderer.ts","../src/components/math-part.ts","../src/components/code-block.ts"],"sourcesContent":["/**\n * Binary format for message content as returned by the v0 Platform API\n * Each row is a tuple where the first element is the type and the rest are data\n */\nexport type MessageBinaryFormat = [number, ...any[]][]\n\n/**\n * Individual row in the message binary format\n */\nexport type MessageBinaryFormatRow = MessageBinaryFormat[number]\n\n/**\n * Props for the Message component\n */\nexport interface MessageProps {\n /**\n * The parsed content from the v0 Platform API\n * This should be the JSON.parsed value of the 'content' field from API responses\n */\n content: MessageBinaryFormat\n\n /**\n * Optional message ID for tracking purposes\n */\n messageId?: string\n\n /**\n * Role of the message sender\n */\n role?: 'user' | 'assistant' | 'system' | 'tool'\n\n /**\n * Whether the message is currently being streamed\n */\n streaming?: boolean\n\n /**\n * Whether this is the last message in the conversation\n */\n isLastMessage?: boolean\n\n /**\n * Custom className for styling the root container\n */\n className?: string\n\n /**\n * Custom component renderers (react-markdown style)\n * Override specific components by name\n * Can be either a React component or an object with className for simple styling\n */\n components?: {\n // Content components\n CodeBlock?: React.ComponentType<{\n language: string\n code: string\n className?: string\n }>\n MathPart?: React.ComponentType<{\n content: string\n inline?: boolean\n className?: string\n }>\n CodeProjectPart?: React.ComponentType<{\n title?: string\n filename?: string\n code?: string\n language?: string\n collapsed?: boolean\n className?: string\n }>\n ThinkingSection?: React.ComponentType<{\n title?: string\n duration?: number\n thought?: string\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n TaskSection?: React.ComponentType<{\n title?: string\n type?: string\n parts?: any[]\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n taskIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n Icon?: React.ComponentType<{\n name:\n | 'chevron-right'\n | 'chevron-down'\n | 'search'\n | 'folder'\n | 'settings'\n | 'file-text'\n | 'brain'\n | 'wrench'\n className?: string\n }>\n\n // HTML elements (react-markdown style)\n // Can be either a React component or an object with className\n p?:\n | React.ComponentType<React.HTMLAttributes<HTMLParagraphElement>>\n | { className?: string }\n h1?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h2?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h3?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h4?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h5?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n h6?:\n | React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>>\n | { className?: string }\n ul?:\n | React.ComponentType<React.HTMLAttributes<HTMLUListElement>>\n | { className?: string }\n ol?:\n | React.ComponentType<React.HTMLAttributes<HTMLOListElement>>\n | { className?: string }\n li?:\n | React.ComponentType<React.HTMLAttributes<HTMLLIElement>>\n | { className?: string }\n blockquote?:\n | React.ComponentType<React.HTMLAttributes<HTMLQuoteElement>>\n | { className?: string }\n code?:\n | React.ComponentType<React.HTMLAttributes<HTMLElement>>\n | { className?: string }\n pre?:\n | React.ComponentType<React.HTMLAttributes<HTMLPreElement>>\n | { className?: string }\n strong?:\n | React.ComponentType<React.HTMLAttributes<HTMLElement>>\n | { className?: string }\n em?:\n | React.ComponentType<React.HTMLAttributes<HTMLElement>>\n | { className?: string }\n a?:\n | React.ComponentType<React.AnchorHTMLAttributes<HTMLAnchorElement>>\n | { className?: string }\n hr?:\n | React.ComponentType<React.HTMLAttributes<HTMLHRElement>>\n | { className?: string }\n div?:\n | React.ComponentType<React.HTMLAttributes<HTMLDivElement>>\n | { className?: string }\n span?:\n | React.ComponentType<React.HTMLAttributes<HTMLSpanElement>>\n | { className?: string }\n }\n\n /**\n * @deprecated Use `components` instead. Will be removed in next major version.\n */\n renderers?: {\n CodeBlock?: React.ComponentType<{\n language: string\n code: string\n className?: string\n }>\n MathRenderer?: React.ComponentType<{\n content: string\n inline?: boolean\n className?: string\n }>\n MathPart?: React.ComponentType<{\n content: string\n inline?: boolean\n className?: string\n }>\n Icon?: React.ComponentType<{\n name:\n | 'chevron-right'\n | 'chevron-down'\n | 'search'\n | 'folder'\n | 'settings'\n | 'file-text'\n | 'brain'\n | 'wrench'\n className?: string\n }>\n }\n}\n\n// Backward compatibility exports\nexport type MessageRendererProps = MessageProps\nexport type V0MessageRendererProps = MessageProps\n// Note: MessageStyles/MessageRendererStyles/V0MessageRendererStyles removed as styles prop is no longer supported\n","import React from 'react'\nimport { MessageProps } from '../types'\nimport { MathPart } from './math-part'\nimport { CodeBlock } from './code-block'\nimport { CodeProjectPart } from './code-project-part'\nimport { ContentPartRenderer } from './content-part-renderer'\nimport { cn } from '../utils/cn'\n\n// Headless message data structure\nexport interface MessageData {\n elements: MessageElement[]\n messageId: string\n role: string\n streaming: boolean\n isLastMessage: boolean\n}\n\nexport interface MessageElement {\n type: 'text' | 'html' | 'component' | 'content-part' | 'code-project'\n key: string\n data: any\n props?: Record<string, any>\n children?: MessageElement[]\n}\n\n// Headless hook for processing message content\nexport function useMessage({\n content,\n messageId = 'unknown',\n role = 'assistant',\n streaming = false,\n isLastMessage = false,\n components,\n renderers, // deprecated\n}: Omit<MessageProps, 'className'>): MessageData {\n if (!Array.isArray(content)) {\n console.warn(\n 'MessageContent: content must be an array (MessageBinaryFormat)',\n )\n return {\n elements: [],\n messageId,\n role,\n streaming,\n isLastMessage,\n }\n }\n\n // Merge components and renderers (backward compatibility)\n const mergedComponents = {\n ...components,\n // Map legacy renderers to new component names\n ...(renderers?.CodeBlock && { CodeBlock: renderers.CodeBlock }),\n ...(renderers?.MathRenderer && { MathPart: renderers.MathRenderer }),\n ...(renderers?.MathPart && { MathPart: renderers.MathPart }),\n ...(renderers?.Icon && { Icon: renderers.Icon }),\n }\n\n // Process content exactly like v0's Renderer component\n const elements = content\n .map(([type, data], index) => {\n const key = `${messageId}-${index}`\n\n // Markdown data (type 0) - this is the main content\n if (type === 0) {\n return processElements(data, key, mergedComponents)\n }\n\n // Metadata (type 1) - extract context but don't render\n if (type === 1) {\n // In the future, we could extract sources/context here like v0 does\n // For now, just return null like v0's renderer\n return null\n }\n\n // Other types - v0 doesn't handle these in the main renderer\n return null\n })\n .filter(Boolean) as MessageElement[]\n\n return {\n elements,\n messageId,\n role,\n streaming,\n isLastMessage,\n }\n}\n\n// Process elements into headless data structure\nfunction processElements(\n data: any,\n keyPrefix: string,\n components?: MessageProps['components'],\n): MessageElement | null {\n // Handle case where data might not be an array due to streaming/patching\n if (!Array.isArray(data)) {\n return null\n }\n\n const children = data\n .map((item, index) => {\n const key = `${keyPrefix}-${index}`\n return processElement(item, key, components)\n })\n .filter(Boolean) as MessageElement[]\n\n return {\n type: 'component',\n key: keyPrefix,\n data: 'elements',\n children,\n }\n}\n\n// Process individual elements into headless data structure\nfunction processElement(\n element: any,\n key: string,\n components?: MessageProps['components'],\n): MessageElement | null {\n if (typeof element === 'string') {\n return {\n type: 'text',\n key,\n data: element,\n }\n }\n\n if (!Array.isArray(element)) {\n return null\n }\n\n const [tagName, props, ...children] = element\n\n if (!tagName) {\n return null\n }\n\n // Handle special v0 Platform API elements\n if (tagName === 'AssistantMessageContentPart') {\n return {\n type: 'content-part',\n key,\n data: {\n part: props.part,\n iconRenderer: components?.Icon,\n thinkingSectionRenderer: components?.ThinkingSection,\n taskSectionRenderer: components?.TaskSection,\n },\n }\n }\n\n if (tagName === 'Codeblock') {\n return {\n type: 'code-project',\n key,\n data: {\n language: props.lang,\n code: children[0],\n iconRenderer: components?.Icon,\n customRenderer: components?.CodeProjectPart,\n },\n }\n }\n\n if (tagName === 'text') {\n return {\n type: 'text',\n key,\n data: children[0] || '',\n }\n }\n\n // Process children\n const processedChildren = children\n .map((child, childIndex) => {\n const childKey = `${key}-child-${childIndex}`\n return processElement(child, childKey, components)\n })\n .filter(Boolean) as MessageElement[]\n\n // Handle standard HTML elements\n const componentOrConfig = components?.[tagName as keyof typeof components]\n\n return {\n type: 'html',\n key,\n data: {\n tagName,\n props,\n componentOrConfig,\n },\n children: processedChildren,\n }\n}\n\n// Default JSX renderer for backward compatibility\nfunction MessageRenderer({\n messageData,\n className,\n}: {\n messageData: MessageData\n className?: string\n}) {\n const renderElement = (element: MessageElement): React.ReactNode => {\n switch (element.type) {\n case 'text':\n return React.createElement('span', { key: element.key }, element.data)\n\n case 'content-part':\n return React.createElement(ContentPartRenderer, {\n key: element.key,\n part: element.data.part,\n iconRenderer: element.data.iconRenderer,\n thinkingSectionRenderer: element.data.thinkingSectionRenderer,\n taskSectionRenderer: element.data.taskSectionRenderer,\n })\n\n case 'code-project':\n const CustomCodeProjectPart = element.data.customRenderer\n const CodeProjectComponent = CustomCodeProjectPart || CodeProjectPart\n return React.createElement(CodeProjectComponent, {\n key: element.key,\n language: element.data.language,\n code: element.data.code,\n iconRenderer: element.data.iconRenderer,\n })\n\n case 'html':\n const { tagName, props, componentOrConfig } = element.data\n const renderedChildren = element.children?.map(renderElement)\n\n if (typeof componentOrConfig === 'function') {\n const Component = componentOrConfig\n return React.createElement(\n Component,\n {\n key: element.key,\n ...props,\n className: props?.className,\n },\n renderedChildren,\n )\n } else if (componentOrConfig && typeof componentOrConfig === 'object') {\n const mergedClassName = cn(\n props?.className,\n componentOrConfig.className,\n )\n return React.createElement(\n tagName,\n { key: element.key, ...props, className: mergedClassName },\n renderedChildren,\n )\n } else {\n // Default HTML element rendering\n const elementProps: Record<string, any> = {\n key: element.key,\n ...props,\n }\n if (props?.className) {\n elementProps.className = props.className\n }\n\n // Special handling for links\n if (tagName === 'a') {\n elementProps.target = '_blank'\n elementProps.rel = 'noopener noreferrer'\n }\n\n return React.createElement(tagName, elementProps, renderedChildren)\n }\n\n case 'component':\n return React.createElement(\n React.Fragment,\n { key: element.key },\n element.children?.map(renderElement),\n )\n\n default:\n return null\n }\n }\n\n return React.createElement(\n 'div',\n { className },\n messageData.elements.map(renderElement),\n )\n}\n\n// Simplified renderer that matches v0's exact approach (backward compatibility)\nfunction MessageImpl({\n content,\n messageId = 'unknown',\n role = 'assistant',\n streaming = false,\n isLastMessage = false,\n className,\n components,\n renderers, // deprecated\n}: MessageProps) {\n const messageData = useMessage({\n content,\n messageId,\n role,\n streaming,\n isLastMessage,\n components,\n renderers,\n })\n\n return React.createElement(MessageRenderer, { messageData, className })\n}\n\n/**\n * Main component for rendering v0 Platform API message content\n * This is a backward-compatible JSX renderer. For headless usage, use the useMessage hook.\n */\nexport const Message = React.memo(MessageImpl)\n","import { useRef, useSyncExternalStore } from 'react'\nimport { MessageBinaryFormat } from '../types'\nimport * as jsondiffpatch from 'jsondiffpatch'\n\nconst jdf = jsondiffpatch.create({})\n\n// Exact copy of the patch function from v0/chat/lib/diffpatch.ts\nfunction patch(original: any, delta: any) {\n const newObj = jdf.clone(original)\n\n // Check for our customized delta\n if (Array.isArray(delta) && delta[1] === 9 && delta[2] === 9) {\n // Get the path to the modified element\n const indexes = delta[0].slice(0, -1)\n // Get the string to be appended\n const value = delta[0].slice(-1)\n let obj = newObj as any\n for (const index of indexes) {\n if (typeof obj[index] === 'string') {\n obj[index] += value\n return newObj\n }\n obj = obj[index]\n }\n }\n\n // If not custom delta, apply standard jsondiffpatch-ing\n jdf.patch(newObj, delta)\n return newObj\n}\n\nexport interface StreamingMessageState {\n content: MessageBinaryFormat\n isStreaming: boolean\n error?: string\n isComplete: boolean\n}\n\nexport interface UseStreamingMessageOptions {\n onChunk?: (chunk: MessageBinaryFormat) => void\n onComplete?: (finalContent: MessageBinaryFormat) => void\n onError?: (error: string) => void\n onChatData?: (chatData: any) => void\n}\n\n// Stream state manager - isolated from React lifecycle\nclass StreamStateManager {\n private content: MessageBinaryFormat = []\n private isStreaming: boolean = false\n private error?: string\n private isComplete: boolean = false\n private callbacks = new Set<() => void>()\n private processedStreams = new WeakSet<ReadableStream<Uint8Array>>()\n private cachedState: StreamingMessageState | null = null\n\n subscribe = (callback: () => void) => {\n this.callbacks.add(callback)\n return () => {\n this.callbacks.delete(callback)\n }\n }\n\n private notifySubscribers = () => {\n // Invalidate cached state when state changes\n this.cachedState = null\n this.callbacks.forEach((callback) => callback())\n }\n\n getState = (): StreamingMessageState => {\n // Return cached state to prevent infinite re-renders\n if (this.cachedState === null) {\n this.cachedState = {\n content: this.content,\n isStreaming: this.isStreaming,\n error: this.error,\n isComplete: this.isComplete,\n }\n }\n return this.cachedState\n }\n\n processStream = async (\n stream: ReadableStream<Uint8Array>,\n options: UseStreamingMessageOptions = {},\n ): Promise<void> => {\n // Prevent processing the same stream multiple times\n if (this.processedStreams.has(stream)) {\n return\n }\n\n // Handle locked streams gracefully\n if (stream.locked) {\n console.warn('Stream is locked, cannot process')\n return\n }\n\n this.processedStreams.add(stream)\n this.reset()\n this.setStreaming(true)\n\n try {\n await this.readStream(stream, options)\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Unknown streaming error'\n this.setError(errorMessage)\n options.onError?.(errorMessage)\n } finally {\n this.setStreaming(false)\n }\n }\n\n private reset = () => {\n this.content = []\n this.isStreaming = false\n this.error = undefined\n this.isComplete = false\n this.notifySubscribers()\n }\n\n private setStreaming = (streaming: boolean) => {\n this.isStreaming = streaming\n this.notifySubscribers()\n }\n\n private setError = (error: string) => {\n this.error = error\n this.notifySubscribers()\n }\n\n private setComplete = (complete: boolean) => {\n this.isComplete = complete\n this.notifySubscribers()\n }\n\n private updateContent = (newContent: MessageBinaryFormat) => {\n this.content = [...newContent]\n this.notifySubscribers()\n }\n\n private readStream = async (\n stream: ReadableStream<Uint8Array>,\n options: UseStreamingMessageOptions,\n ): Promise<void> => {\n const reader = stream.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentContent: MessageBinaryFormat = []\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n break\n }\n\n const chunk = decoder.decode(value, { stream: true })\n buffer += chunk\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n if (line.trim() === '') {\n continue\n }\n\n // Handle SSE format (data: ...)\n let jsonData: string\n if (line.startsWith('data: ')) {\n jsonData = line.slice(6) // Remove \"data: \" prefix\n if (jsonData === '[DONE]') {\n this.setComplete(true)\n options.onComplete?.(currentContent)\n return\n }\n } else {\n // Handle raw JSON lines (fallback)\n jsonData = line\n }\n\n try {\n // Parse the JSON data\n const parsedData = JSON.parse(jsonData)\n\n // Handle v0 streaming format\n if (parsedData.type === 'connected') {\n continue\n } else if (parsedData.type === 'done') {\n this.setComplete(true)\n options.onComplete?.(currentContent)\n return\n } else if (\n parsedData.object &&\n parsedData.object.startsWith('chat')\n ) {\n // Handle chat metadata messages (chat, chat.title, chat.name, etc.)\n options.onChatData?.(parsedData)\n continue\n } else if (parsedData.delta) {\n // Apply the delta using jsondiffpatch\n const patchedContent = patch(currentContent, parsedData.delta)\n currentContent = Array.isArray(patchedContent)\n ? (patchedContent as MessageBinaryFormat)\n : []\n\n this.updateContent(currentContent)\n options.onChunk?.(currentContent)\n }\n } catch (e) {\n console.warn('Failed to parse streaming data:', line, e)\n }\n }\n }\n\n this.setComplete(true)\n options.onComplete?.(currentContent)\n } finally {\n reader.releaseLock()\n }\n }\n}\n\n/**\n * Hook for handling streaming message content from v0 API using useSyncExternalStore\n */\nexport function useStreamingMessage(\n stream: ReadableStream<Uint8Array> | null,\n options: UseStreamingMessageOptions = {},\n): StreamingMessageState {\n // Create a stable stream manager instance\n const managerRef = useRef<StreamStateManager | null>(null)\n if (!managerRef.current) {\n managerRef.current = new StreamStateManager()\n }\n\n const manager = managerRef.current\n\n // Subscribe to state changes using useSyncExternalStore\n const state = useSyncExternalStore(\n manager.subscribe,\n manager.getState,\n manager.getState,\n )\n\n // Process stream when it changes\n const lastStreamRef = useRef<ReadableStream<Uint8Array> | null>(null)\n\n if (stream !== lastStreamRef.current) {\n lastStreamRef.current = stream\n if (stream) {\n manager.processStream(stream, options)\n }\n }\n\n return state\n}\n","import React from 'react'\nimport { Message, useMessage, MessageData } from './message'\nimport {\n useStreamingMessage,\n UseStreamingMessageOptions,\n StreamingMessageState,\n} from '../hooks/use-streaming-message'\nimport { MessageProps } from '../types'\n\nexport interface StreamingMessageProps\n extends Omit<MessageProps, 'content' | 'streaming' | 'isLastMessage'>,\n UseStreamingMessageOptions {\n /**\n * The streaming response from v0.chats.create() with responseMode: 'experimental_stream'\n */\n stream: ReadableStream<Uint8Array> | null\n\n /**\n * Show a loading indicator while no content has been received yet\n */\n showLoadingIndicator?: boolean\n\n /**\n * Custom loading component\n */\n loadingComponent?: React.ReactNode\n\n /**\n * Custom error component\n */\n errorComponent?: (error: string) => React.ReactNode\n}\n\n// Headless streaming message data\nexport interface StreamingMessageData extends StreamingMessageState {\n messageData: MessageData | null\n}\n\n// Headless hook for streaming message\nexport function useStreamingMessageData({\n stream,\n messageId = 'unknown',\n role = 'assistant',\n components,\n renderers,\n onChunk,\n onComplete,\n onError,\n onChatData,\n}: Omit<\n StreamingMessageProps,\n 'className' | 'showLoadingIndicator' | 'loadingComponent' | 'errorComponent'\n>): StreamingMessageData {\n const streamingState = useStreamingMessage(stream, {\n onChunk,\n onComplete,\n onError,\n onChatData,\n })\n\n const messageData =\n streamingState.content.length > 0\n ? useMessage({\n content: streamingState.content,\n messageId,\n role,\n streaming: streamingState.isStreaming,\n isLastMessage: true,\n components,\n renderers,\n })\n : null\n\n return {\n ...streamingState,\n messageData,\n }\n}\n\n/**\n * Component for rendering streaming message content from v0 API\n *\n * For headless usage, use the useStreamingMessageData hook instead.\n *\n * @example\n * ```tsx\n * import { v0 } from 'v0-sdk'\n * import { StreamingMessage } from '@v0-sdk/react'\n *\n * function ChatDemo() {\n * const [stream, setStream] = useState<ReadableStream<Uint8Array> | null>(null)\n *\n * const handleSubmit = async () => {\n * const response = await v0.chats.create({\n * message: 'Create a button component',\n * responseMode: 'experimental_stream'\n * })\n * setStream(response)\n * }\n *\n * return (\n * <div>\n * <button onClick={handleSubmit}>Send Message</button>\n * {stream && (\n * <StreamingMessage\n * stream={stream}\n * messageId=\"demo-message\"\n * role=\"assistant\"\n * onComplete={(content) => handleCompletion(content)}\n * onChatData={(chatData) => handleChatData(chatData)}\n * />\n * )}\n * </div>\n * )\n * }\n * ```\n */\nexport function StreamingMessage({\n stream,\n showLoadingIndicator = true,\n loadingComponent,\n errorComponent,\n onChunk,\n onComplete,\n onError,\n onChatData,\n className,\n ...messageProps\n}: StreamingMessageProps) {\n const streamingData = useStreamingMessageData({\n stream,\n onChunk,\n onComplete,\n onError,\n onChatData,\n ...messageProps,\n })\n\n // Handle error state\n if (streamingData.error) {\n if (errorComponent) {\n return React.createElement(\n React.Fragment,\n {},\n errorComponent(streamingData.error),\n )\n }\n // Fallback error component using React.createElement for compatibility\n return React.createElement(\n 'div',\n {\n className: 'text-red-500 p-4 border border-red-200 rounded',\n style: {\n color: 'red',\n padding: '1rem',\n border: '1px solid #fecaca',\n borderRadius: '0.375rem',\n },\n },\n `Error: ${streamingData.error}`,\n )\n }\n\n // Handle loading state\n if (\n showLoadingIndicator &&\n streamingData.isStreaming &&\n streamingData.content.length === 0\n ) {\n if (loadingComponent) {\n return React.createElement(React.Fragment, {}, loadingComponent)\n }\n // Fallback loading component using React.createElement for compatibility\n return React.createElement(\n 'div',\n {\n className: 'flex items-center space-x-2 text-gray-500',\n style: {\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n color: '#6b7280',\n },\n },\n React.createElement('div', {\n className:\n 'animate-spin h-4 w-4 border-2 border-gray-300 border-t-gray-600 rounded-full',\n style: {\n animation: 'spin 1s linear infinite',\n height: '1rem',\n width: '1rem',\n border: '2px solid #d1d5db',\n borderTopColor: '#4b5563',\n borderRadius: '50%',\n },\n }),\n React.createElement('span', {}, 'Loading...'),\n )\n }\n\n // Render the message content\n return React.createElement(Message, {\n ...messageProps,\n content: streamingData.content,\n streaming: streamingData.isStreaming,\n isLastMessage: true,\n className,\n })\n}\n","import React, { createContext, useContext } from 'react'\n\n// Context for providing custom icon implementation\nconst IconContext = createContext<React.ComponentType<IconProps> | null>(null)\n\nexport interface IconProps {\n name:\n | 'chevron-right'\n | 'chevron-down'\n | 'search'\n | 'folder'\n | 'settings'\n | 'file-text'\n | 'brain'\n | 'wrench'\n className?: string\n}\n\n// Headless icon data\nexport interface IconData {\n name: IconProps['name']\n fallback: string\n ariaLabel: string\n}\n\n// Headless hook for icon data\nexport function useIcon(props: IconProps): IconData {\n return {\n name: props.name,\n fallback: getIconFallback(props.name),\n ariaLabel: props.name.replace('-', ' '),\n }\n}\n\n/**\n * Generic icon component that can be customized by consumers.\n * By default, renders a simple fallback. Consumers should provide\n * their own icon implementation via context or props.\n *\n * For headless usage, use the useIcon hook instead.\n */\nexport function Icon(props: IconProps) {\n const CustomIcon = useContext(IconContext)\n\n // Use custom icon implementation if provided via context\n if (CustomIcon) {\n return React.createElement(CustomIcon, props)\n }\n\n const iconData = useIcon(props)\n\n // Fallback implementation - consumers should override this\n // This uses minimal DOM-specific attributes for maximum compatibility\n return React.createElement(\n 'span',\n {\n className: props.className,\n 'data-icon': iconData.name,\n 'aria-label': iconData.ariaLabel,\n // Note: suppressHydrationWarning removed for React Native compatibility\n },\n iconData.fallback,\n )\n}\n\n/**\n * Provider for custom icon implementation\n */\nexport function IconProvider({\n children,\n component,\n}: {\n children: React.ReactNode\n component: React.ComponentType<IconProps>\n}) {\n return React.createElement(\n IconContext.Provider,\n { value: component },\n children,\n )\n}\n\nfunction getIconFallback(name: string): string {\n const iconMap: Record<string, string> = {\n 'chevron-right': '▶',\n 'chevron-down': '▼',\n search: '🔍',\n folder: '📁',\n settings: '⚙️',\n 'file-text': '📄',\n brain: '🧠',\n wrench: '🔧',\n }\n return iconMap[name] || '•'\n}\n","import React, { useState } from 'react'\nimport { Icon, IconProps } from './icon'\n\nexport interface ThinkingSectionProps {\n title?: string\n duration?: number\n thought?: string\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n iconRenderer?: React.ComponentType<IconProps>\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n}\n\n// Headless thinking section data\nexport interface ThinkingSectionData {\n title: string\n duration?: number\n thought?: string\n collapsed: boolean\n paragraphs: string[]\n formattedDuration?: string\n}\n\n// Headless hook for thinking section\nexport function useThinkingSection({\n title,\n duration,\n thought,\n collapsed: initialCollapsed = true,\n onCollapse,\n}: Omit<\n ThinkingSectionProps,\n | 'className'\n | 'children'\n | 'iconRenderer'\n | 'brainIcon'\n | 'chevronRightIcon'\n | 'chevronDownIcon'\n>): {\n data: ThinkingSectionData\n collapsed: boolean\n handleCollapse: () => void\n} {\n const [internalCollapsed, setInternalCollapsed] = useState(initialCollapsed)\n const collapsed = onCollapse ? initialCollapsed : internalCollapsed\n const handleCollapse =\n onCollapse || (() => setInternalCollapsed(!internalCollapsed))\n\n const paragraphs = thought ? thought.split('\\n\\n') : []\n const formattedDuration = duration ? `${Math.round(duration)}s` : undefined\n\n return {\n data: {\n title: title || 'Thinking',\n duration,\n thought,\n collapsed,\n paragraphs,\n formattedDuration,\n },\n collapsed,\n handleCollapse,\n }\n}\n\n/**\n * Generic thinking section component\n * Renders a collapsible section with basic structure - consumers provide styling\n *\n * For headless usage, use the useThinkingSection hook instead.\n */\nexport function ThinkingSection({\n title,\n duration,\n thought,\n collapsed: initialCollapsed = true,\n onCollapse,\n className,\n children,\n iconRenderer,\n brainIcon,\n chevronRightIcon,\n chevronDownIcon,\n}: ThinkingSectionProps) {\n const { data, collapsed, handleCollapse } = useThinkingSection({\n title,\n duration,\n thought,\n collapsed: initialCollapsed,\n onCollapse,\n })\n\n // If children provided, use that (allows complete customization)\n if (children) {\n return React.createElement(React.Fragment, {}, children)\n }\n\n // Uses React.createElement for maximum compatibility across environments\n return React.createElement(\n 'div',\n {\n className,\n 'data-component': 'thinking-section',\n },\n React.createElement(\n 'button',\n {\n onClick: handleCollapse,\n 'data-expanded': !collapsed,\n 'data-button': true,\n },\n React.createElement(\n 'div',\n { 'data-icon-container': true },\n collapsed\n ? React.createElement(\n React.Fragment,\n {},\n brainIcon ||\n (iconRenderer\n ? React.createElement(iconRenderer, { name: 'brain' })\n : React.createElement(Icon, { name: 'brain' })),\n chevronRightIcon ||\n (iconRenderer\n ? React.createElement(iconRenderer, { name: 'chevron-right' })\n : React.createElement(Icon, { name: 'chevron-right' })),\n )\n : chevronDownIcon ||\n (iconRenderer\n ? React.createElement(iconRenderer, { name: 'chevron-down' })\n : React.createElement(Icon, { name: 'chevron-down' })),\n ),\n React.createElement(\n 'span',\n { 'data-title': true },\n data.title +\n (data.formattedDuration ? ` for ${data.formattedDuration}` : ''),\n ),\n ),\n !collapsed && data.thought\n ? React.createElement(\n 'div',\n { 'data-content': true },\n React.createElement(\n 'div',\n { 'data-thought-container': true },\n data.paragraphs.map((paragraph, index) =>\n React.createElement(\n 'div',\n { key: index, 'data-paragraph': true },\n paragraph,\n ),\n ),\n ),\n )\n : null,\n )\n}\n","import React, { useState } from 'react'\nimport { Icon, IconProps } from './icon'\n\nexport interface TaskSectionProps {\n title?: string\n type?: string\n parts?: any[]\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n iconRenderer?: React.ComponentType<IconProps>\n taskIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n}\n\n// Headless task section data\nexport interface TaskSectionData {\n title: string\n type?: string\n parts: any[]\n collapsed: boolean\n meaningfulParts: any[]\n shouldShowCollapsible: boolean\n iconName: IconProps['name']\n}\n\n// Headless task part data\nexport interface TaskPartData {\n type: string\n status?: string\n content: React.ReactNode\n isSearching?: boolean\n isAnalyzing?: boolean\n isComplete?: boolean\n query?: string\n count?: number\n answer?: string\n sources?: Array<{ url: string; title: string }>\n files?: string[]\n issues?: number\n}\n\nfunction getTypeIcon(type?: string, title?: string): IconProps['name'] {\n // Check title content for specific cases\n if (title?.includes('No issues found')) {\n return 'wrench'\n }\n if (title?.includes('Analyzed codebase')) {\n return 'search'\n }\n\n // Fallback to type-based icons\n switch (type) {\n case 'task-search-web-v1':\n return 'search'\n case 'task-search-repo-v1':\n return 'folder'\n case 'task-diagnostics-v1':\n return 'settings'\n case 'task-generate-design-inspiration-v1':\n return 'wrench'\n case 'task-read-file-v1':\n return 'folder'\n case 'task-coding-v1':\n return 'wrench'\n default:\n return 'wrench'\n }\n}\n\nfunction processTaskPart(part: any, index: number): TaskPartData {\n const baseData: TaskPartData = {\n type: part.type,\n status: part.status,\n content: null,\n }\n\n if (part.type === 'search-web') {\n if (part.status === 'searching') {\n return {\n ...baseData,\n isSearching: true,\n query: part.query,\n content: `Searching \"${part.query}\"`,\n }\n }\n if (part.status === 'analyzing') {\n return {\n ...baseData,\n isAnalyzing: true,\n count: part.count,\n content: `Analyzing ${part.count} results...`,\n }\n }\n if (part.status === 'complete' && part.answer) {\n return {\n ...baseData,\n isComplete: true,\n answer: part.answer,\n sources: part.sources,\n content: part.answer,\n }\n }\n }\n\n if (part.type === 'search-repo') {\n if (part.status === 'searching') {\n return {\n ...baseData,\n isSearching: true,\n query: part.query,\n content: `Searching \"${part.query}\"`,\n }\n }\n if (part.status === 'reading' && part.files) {\n return {\n ...baseData,\n files: part.files,\n content: 'Reading files',\n }\n }\n }\n\n if (part.type === 'diagnostics') {\n if (part.status === 'checking') {\n return {\n ...baseData,\n content: 'Checking for issues...',\n }\n }\n if (part.status === 'complete' && part.issues === 0) {\n return {\n ...baseData,\n isComplete: true,\n issues: part.issues,\n content: '✅ No issues found',\n }\n }\n }\n\n return {\n ...baseData,\n content: JSON.stringify(part),\n }\n}\n\nfunction renderTaskPartContent(\n partData: TaskPartData,\n index: number,\n iconRenderer?: React.ComponentType<IconProps>,\n): React.ReactNode {\n if (\n partData.type === 'search-web' &&\n partData.isComplete &&\n partData.sources\n ) {\n return React.createElement(\n 'div',\n { key: index },\n React.createElement('p', {}, partData.content),\n partData.sources.length > 0\n ? React.createElement(\n 'div',\n {},\n partData.sources.map((source, sourceIndex) =>\n React.createElement(\n 'a',\n {\n key: sourceIndex,\n href: source.url,\n target: '_blank',\n rel: 'noopener noreferrer',\n },\n source.title,\n ),\n ),\n )\n : null,\n )\n }\n\n if (partData.type === 'search-repo' && partData.files) {\n return React.createElement(\n 'div',\n { key: index },\n React.createElement('span', {}, partData.content),\n partData.files.map((file, fileIndex) =>\n React.createElement(\n 'span',\n { key: fileIndex },\n iconRenderer\n ? React.createElement(iconRenderer, { name: 'file-text' })\n : React.createElement(Icon, { name: 'file-text' }),\n ' ',\n file,\n ),\n ),\n )\n }\n\n return React.createElement('div', { key: index }, partData.content)\n}\n\n// Headless hook for task section\nexport function useTaskSection({\n title,\n type,\n parts = [],\n collapsed: initialCollapsed = true,\n onCollapse,\n}: Omit<\n TaskSectionProps,\n | 'className'\n | 'children'\n | 'iconRenderer'\n | 'taskIcon'\n | 'chevronRightIcon'\n | 'chevronDownIcon'\n>): {\n data: TaskSectionData\n collapsed: boolean\n handleCollapse: () => void\n processedParts: TaskPartData[]\n} {\n const [internalCollapsed, setInternalCollapsed] = useState(initialCollapsed)\n const collapsed = onCollapse ? initialCollapsed : internalCollapsed\n const handleCollapse =\n onCollapse || (() => setInternalCollapsed(!internalCollapsed))\n\n // Count meaningful parts (parts that would render something)\n const meaningfulParts = parts.filter((part) => {\n // Check if the part would render meaningful content\n if (part.type === 'search-web') {\n return (\n part.status === 'searching' ||\n part.status === 'analyzing' ||\n (part.status === 'complete' && part.answer)\n )\n }\n if (part.type === 'starting-repo-search' && part.query) return true\n if (part.type === 'select-files' && part.filePaths?.length > 0) return true\n if (part.type === 'starting-web-search' && part.query) return true\n if (part.type === 'got-results' && part.count) return true\n if (part.type === 'finished-web-search' && part.answer) return true\n if (part.type === 'diagnostics-passed') return true\n if (part.type === 'fetching-diagnostics') return true\n return false\n })\n\n const processedParts = parts.map(processTaskPart)\n\n return {\n data: {\n title: title || 'Task',\n type,\n parts,\n collapsed,\n meaningfulParts,\n shouldShowCollapsible: meaningfulParts.length > 1,\n iconName: getTypeIcon(type, title),\n },\n collapsed,\n handleCollapse,\n processedParts,\n }\n}\n\n/**\n * Generic task section component\n * Renders a collapsible task section with basic structure - consumers provide styling\n *\n * For headless usage, use the useTaskSection hook instead.\n */\nexport function TaskSection({\n title,\n type,\n parts = [],\n collapsed: initialCollapsed = true,\n onCollapse,\n className,\n children,\n iconRenderer,\n taskIcon,\n chevronRightIcon,\n chevronDownIcon,\n}: TaskSectionProps) {\n const { data, collapsed, handleCollapse, processedParts } = useTaskSection({\n title,\n type,\n parts,\n collapsed: initialCollapsed,\n onCollapse,\n })\n\n // If children provided, use that (allows complete customization)\n if (children) {\n return React.createElement(React.Fragment, {}, children)\n }\n\n // If there's only one meaningful part, show just the content without the collapsible wrapper\n if (!data.shouldShowCollapsible && data.meaningfulParts.length === 1) {\n const partData = processTaskPart(data.meaningfulParts[0], 0)\n return React.createElement(\n 'div',\n {\n className,\n 'data-component': 'task-section-inline',\n },\n React.createElement(\n 'div',\n { 'data-part': true },\n renderTaskPartContent(partData, 0, iconRenderer),\n ),\n )\n }\n\n // Uses React.createElement for maximum compatibility across environments\n return React.createElement(\n 'div',\n {\n className,\n 'data-component': 'task-section',\n },\n React.createElement(\n 'button',\n {\n onClick: handleCollapse,\n 'data-expanded': !collapsed,\n 'data-button': true,\n },\n React.createElement(\n 'div',\n { 'data-icon-container': true },\n React.createElement(\n 'div',\n { 'data-task-icon': true },\n taskIcon ||\n (iconRenderer\n ? React.createElement(iconRenderer, { name: data.iconName })\n : React.createElement(Icon, { name: data.iconName })),\n ),\n collapsed\n ? chevronRightIcon ||\n (iconRenderer\n ? React.createElement(iconRenderer, { name: 'chevron-right' })\n : React.createElement(Icon, { name: 'chevron-right' }))\n : chevronDownIcon ||\n (iconRenderer\n ? React.createElement(iconRenderer, { name: 'chevron-down' })\n : React.createElement(Icon, { name: 'chevron-down' })),\n ),\n React.createElement('span', { 'data-title': true }, data.title),\n ),\n !collapsed\n ? React.createElement(\n 'div',\n { 'data-content': true },\n React.createElement(\n 'div',\n { 'data-parts-container': true },\n processedParts.map((partData, index) =>\n React.createElement(\n 'div',\n { key: index, 'data-part': true },\n renderTaskPartContent(partData, index, iconRenderer),\n ),\n ),\n ),\n )\n : null,\n )\n}\n","import React, { useState } from 'react'\nimport { CodeBlock } from './code-block'\nimport { Icon, IconProps } from './icon'\n\nexport interface CodeProjectPartProps {\n title?: string\n filename?: string\n code?: string\n language?: string\n collapsed?: boolean\n className?: string\n children?: React.ReactNode\n iconRenderer?: React.ComponentType<IconProps>\n}\n\n// Headless code project data\nexport interface CodeProjectData {\n title: string\n filename?: string\n code?: string\n language: string\n collapsed: boolean\n files: Array<{\n name: string\n path: string\n active: boolean\n }>\n}\n\n// Headless hook for code project\nexport function useCodeProject({\n title,\n filename,\n code,\n language = 'typescript',\n collapsed: initialCollapsed = true,\n}: Omit<CodeProjectPartProps, 'className' | 'children' | 'iconRenderer'>): {\n data: CodeProjectData\n collapsed: boolean\n toggleCollapsed: () => void\n} {\n const [collapsed, setCollapsed] = useState(initialCollapsed)\n\n // Mock file structure - in a real implementation this could be dynamic\n const files = [\n {\n name: filename || 'page.tsx',\n path: 'app/page.tsx',\n active: true,\n },\n {\n name: 'layout.tsx',\n path: 'app/layout.tsx',\n active: false,\n },\n {\n name: 'globals.css',\n path: 'app/globals.css',\n active: false,\n },\n ]\n\n return {\n data: {\n title: title || 'Code Project',\n filename,\n code,\n language,\n collapsed,\n files,\n },\n collapsed,\n toggleCollapsed: () => setCollapsed(!collapsed),\n }\n}\n\n/**\n * Generic code project block component\n * Renders a collapsible code project with basic structure - consumers provide styling\n *\n * For headless usage, use the useCodeProject hook instead.\n */\nexport function CodeProjectPart({\n title,\n filename,\n code,\n language = 'typescript',\n collapsed: initialCollapsed = true,\n className,\n children,\n iconRenderer,\n}: CodeProjectPartProps) {\n const { data, collapsed, toggleCollapsed } = useCodeProject({\n title,\n filename,\n code,\n language,\n collapsed: initialCollapsed,\n })\n\n // If children provided, use that (allows complete customization)\n if (children) {\n return React.createElement(React.Fragment, {}, children)\n }\n\n // Uses React.createElement for maximum compatibility across environments\n return React.createElement(\n 'div',\n {\n className,\n 'data-component': 'code-project-block',\n },\n React.createElement(\n 'button',\n {\n onClick: toggleCollapsed,\n 'data-expanded': !collapsed,\n },\n React.createElement(\n 'div',\n { 'data-header': true },\n iconRenderer\n ? React.createElement(iconRenderer, { name: 'folder' })\n : React.createElement(Icon, { name: 'folder' }),\n React.createElement('span', { 'data-title': true }, data.title),\n ),\n React.createElement('span', { 'data-version': true }, 'v1'),\n ),\n !collapsed\n ? React.createElement(\n 'div',\n { 'data-content': true },\n React.createElement(\n 'div',\n { 'data-file-list': true },\n data.files.map((file, index) =>\n React.createElement(\n 'div',\n {\n key: index,\n 'data-file': true,\n ...(file.active && { 'data-active': true }),\n },\n iconRenderer\n ? React.createElement(iconRenderer, { name: 'file-text' })\n : React.createElement(Icon, { name: 'file-text' }),\n React.createElement(\n 'span',\n { 'data-filename': true },\n file.name,\n ),\n React.createElement(\n 'span',\n { 'data-filepath': true },\n file.path,\n ),\n ),\n ),\n ),\n data.code\n ? React.createElement(CodeBlock, {\n language: data.language,\n code: data.code,\n })\n : null,\n )\n : null,\n )\n}\n","import React, { useState } from 'react'\nimport { ThinkingSection } from './thinking-section'\nimport { TaskSection } from './task-section'\nimport { IconProps } from './icon'\n\nexport interface ContentPartRendererProps {\n part: any\n iconRenderer?: React.ComponentType<IconProps>\n thinkingSectionRenderer?: React.ComponentType<{\n title?: string\n duration?: number\n thought?: string\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n taskSectionRenderer?: React.ComponentType<{\n title?: string\n type?: string\n parts?: any[]\n collapsed?: boolean\n onCollapse?: () => void\n className?: string\n children?: React.ReactNode\n taskIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n }>\n // Individual icon props for direct icon usage\n brainIcon?: React.ReactNode\n chevronRightIcon?: React.ReactNode\n chevronDownIcon?: React.ReactNode\n searchIcon?: React.ReactNode\n folderIcon?: React.ReactNode\n settingsIcon?: React.ReactNode\n wrenchIcon?: React.ReactNode\n}\n\n// Headless content part data\nexport interface ContentPartData {\n type: string\n parts: any[]\n metadata: Record<string, any>\n componentType: 'thinking' | 'task' | 'unknown' | null\n title?: string\n iconName?: IconProps['name']\n thinkingData?: {\n duration?: number\n thought?: string\n }\n}\n\n// Headless hook for content part\nexport function useContentPart(part: any): ContentPartData {\n if (!part) {\n return {\n type: '',\n parts: [],\n metadata: {},\n componentType: null,\n }\n }\n\n const { type, parts = [], ...metadata } = part\n\n let componentType: ContentPartData['componentType'] = 'unknown'\n let title: string | undefined\n let iconName: IconProps['name'] | undefined\n let thinkingData: ContentPartData['thinkingData']\n\n switch (type) {\n case 'task-thinking-v1':\n componentType = 'thinking'\n title = 'Thought'\n const thinkingPart = parts.find((p: any) => p.type === 'thinking-end')\n thinkingData = {\n duration: thinkingPart?.duration,\n thought: thinkingPart?.thought,\n }\n break\n\n case 'task-search-web-v1':\n componentType = 'task'\n title = metadata.taskNameComplete || metadata.taskNameActive\n iconName = 'search'\n break\n\n case 'task-search-repo-v1':\n componentType = 'task'\n title = metadata.taskNameComplete || metadata.taskNameActive\n iconName = 'folder'\n break\n\n case 'task-diagnostics-v1':\n componentType = 'task'\n title = metadata.taskNameComplete || metadata.taskNameActive\n iconName = 'settings'\n break\n\n case 'task-read-file-v1':\n componentType = 'task'\n title =\n metadata.taskNameComplete || metadata.taskNameActive || 'Reading file'\n iconName = 'folder'\n break\n\n case 'task-coding-v1':\n componentType = 'task'\n title = metadata.taskNameComplete || metadata.taskNameActive || 'Coding'\n iconName = 'wrench'\n break\n\n case 'task-start-v1':\n componentType = null // Usually just indicates task start - can be hidden\n break\n\n case 'task-generate-design-inspiration-v1':\n componentType = 'task'\n title =\n metadata.taskNameComplete ||\n metadata.taskNameActive ||\n 'Generating Design Inspiration'\n iconName = 'wrench'\n break\n\n // Handle any other task-*-v1 patterns that might be added in the future\n default:\n // Check if it's a task type we haven't explicitly handled yet\n if (\n type &&\n typeof type === 'string' &&\n type.startsWith('task-') &&\n type.endsWith('-v1')\n ) {\n componentType = 'task'\n // Generate a readable title from the task type\n const taskName = type\n .replace('task-', '')\n .replace('-v1', '')\n .split('-')\n .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ')\n title = metadata.taskNameComplete || metadata.taskNameActive || taskName\n iconName = 'wrench' // Default icon for unknown task types\n } else {\n componentType = 'unknown'\n }\n break\n }\n\n return {\n type,\n parts,\n metadata,\n componentType,\n title,\n iconName,\n thinkingData,\n }\n}\n\n/**\n * Content part renderer that handles different types of v0 API content parts\n *\n * For headless usage, use the useContentPart hook instead.\n */\nexport function ContentPartRenderer({\n part,\n iconRenderer,\n thinkingSectionRenderer,\n taskSectionRenderer,\n brainIcon,\n chevronRightIcon,\n chevronDownIcon,\n searchIcon,\n folderIcon,\n settingsIcon,\n wrenchIcon,\n}: ContentPartRendererProps) {\n const contentData = useContentPart(part)\n\n if (!contentData.componentType) {\n return null\n }\n\n if (contentData.componentType === 'thinking') {\n const ThinkingComponent = thinkingSectionRenderer || ThinkingSection\n const [collapsed, setCollapsed] = useState(true)\n\n return React.createElement(ThinkingComponent, {\n title: contentData.title,\n duration: contentData.thinkingData?.duration,\n thought: contentData.thinkingData?.thought,\n collapsed,\n onCollapse: () => setCollapsed(!collapsed),\n brainIcon,\n chevronRightIcon,\n chevronDownIcon,\n })\n }\n\n if (contentData.componentType === 'task') {\n const TaskComponent = taskSectionRenderer || TaskSection\n const [collapsed, setCollapsed] = useState(true)\n\n // Map icon names to icon components\n let taskIcon: React.ReactNode\n switch (contentData.iconName) {\n case 'search':\n taskIcon = searchIcon\n break\n case 'folder':\n taskIcon = folderIcon\n break\n case 'settings':\n taskIcon = settingsIcon\n break\n case 'wrench':\n taskIcon = wrenchIcon\n break\n default:\n taskIcon = undefined\n break\n }\n\n return React.createElement(TaskComponent, {\n title: contentData.title,\n type: contentData.type,\n parts: contentData.parts,\n collapsed,\n onCollapse: () => setCollapsed(!collapsed),\n taskIcon,\n chevronRightIcon,\n chevronDownIcon,\n })\n }\n\n if (contentData.componentType === 'unknown') {\n return React.createElement(\n 'div',\n { 'data-unknown-part-type': contentData.type },\n `Unknown part type: ${contentData.type}`,\n )\n }\n\n return null\n}\n","import React from 'react'\n\nexport interface MathPartProps {\n content: string\n inline?: boolean\n className?: string\n children?: React.ReactNode\n displayMode?: boolean\n}\n\n// Headless math data\nexport interface MathData {\n content: string\n inline: boolean\n displayMode: boolean\n processedContent: string\n}\n\n// Headless hook for math data\nexport function useMath(\n props: Omit<MathPartProps, 'className' | 'children'>,\n): MathData {\n return {\n content: props.content,\n inline: props.inline ?? false,\n displayMode: props.displayMode ?? !props.inline,\n processedContent: props.content, // Could be enhanced with math processing\n }\n}\n\n/**\n * Generic math renderer component\n * Renders plain math content by default - consumers should provide their own math rendering\n *\n * For headless usage, use the useMath hook instead.\n */\nexport function MathPart({\n content,\n inline = false,\n className = '',\n children,\n displayMode,\n}: MathPartProps) {\n // If children provided, use that (allows complete customization)\n if (children) {\n return React.createElement(React.Fragment, {}, children)\n }\n\n const mathData = useMath({ content, inline, displayMode })\n\n // Simple fallback - just render plain math content\n // Uses React.createElement for maximum compatibility across environments\n return React.createElement(\n mathData.inline ? 'span' : 'div',\n {\n className,\n 'data-math-inline': mathData.inline,\n 'data-math-display': mathData.displayMode,\n },\n mathData.processedContent,\n )\n}\n","import React from 'react'\n\nexport interface CodeBlockProps {\n language: string\n code: string\n className?: string\n children?: React.ReactNode\n filename?: string\n}\n\n// Headless code block data\nexport interface CodeBlockData {\n language: string\n code: string\n filename?: string\n lines: string[]\n lineCount: number\n}\n\n// Headless hook for code block data\nexport function useCodeBlock(\n props: Omit<CodeBlockProps, 'className' | 'children'>,\n): CodeBlockData {\n const lines = props.code.split('\\n')\n\n return {\n language: props.language,\n code: props.code,\n filename: props.filename,\n lines,\n lineCount: lines.length,\n }\n}\n\n/**\n * Generic code block component\n * Renders plain code by default - consumers should provide their own styling and highlighting\n *\n * For headless usage, use the useCodeBlock hook instead.\n */\nexport function CodeBlock({\n language,\n code,\n className = '',\n children,\n filename,\n}: CodeBlockProps) {\n // If children provided, use that (allows complete customization)\n if (children) {\n return React.createElement(React.Fragment, {}, children)\n }\n\n const codeBlockData = useCodeBlock({ language, code, filename })\n\n // Simple fallback - just render plain code\n // Uses React.createElement for maximum compatibility across environments\n return React.createElement(\n 'pre',\n {\n className,\n 'data-language': codeBlockData.language,\n ...(filename && { 'data-filename': filename }),\n },\n React.createElement('code', {}, codeBlockData.code),\n )\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACA;;AC5KA;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACxBA;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;;ACZA;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC/EO;AACP;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;AC3BO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;ACnCO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;ACtDO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;;ACpCO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AC9EO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;;ACxBO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;AACA;AACA;AACO;AACP;AACA;AACA;AACA;;;"}
|