@v0-sdk/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,829 @@
1
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
+ import React, { useContext, createContext, useState } from 'react';
3
+
4
+ /**
5
+ * Generic math renderer component
6
+ * Renders plain math content by default - consumers should provide their own math rendering
7
+ */ function MathPart({ content, inline = false, className = '', children }) {
8
+ // If children provided, use that (allows complete customization)
9
+ if (children) {
10
+ return /*#__PURE__*/ jsx(Fragment, {
11
+ children: children
12
+ });
13
+ }
14
+ // Simple fallback - just render plain math content
15
+ const Element = inline ? 'span' : 'div';
16
+ return /*#__PURE__*/ jsx(Element, {
17
+ className: className,
18
+ "data-math-inline": inline,
19
+ children: content
20
+ });
21
+ }
22
+
23
+ /**
24
+ * Generic code block component
25
+ * Renders plain code by default - consumers should provide their own styling and highlighting
26
+ */ function CodeBlock({ language, code, className = '', children }) {
27
+ // If children provided, use that (allows complete customization)
28
+ if (children) {
29
+ return /*#__PURE__*/ jsx(Fragment, {
30
+ children: children
31
+ });
32
+ }
33
+ // Simple fallback - just render plain code
34
+ return /*#__PURE__*/ jsx("pre", {
35
+ className: className,
36
+ "data-language": language,
37
+ children: /*#__PURE__*/ jsx("code", {
38
+ children: code
39
+ })
40
+ });
41
+ }
42
+
43
+ // Context for providing custom icon implementation
44
+ const IconContext = /*#__PURE__*/ createContext(null);
45
+ /**
46
+ * Generic icon component that can be customized by consumers.
47
+ * By default, renders a simple fallback. Consumers should provide
48
+ * their own icon implementation via context or props.
49
+ */ function Icon(props) {
50
+ const CustomIcon = useContext(IconContext);
51
+ // Use custom icon implementation if provided via context
52
+ if (CustomIcon) {
53
+ return /*#__PURE__*/ jsx(CustomIcon, {
54
+ ...props
55
+ });
56
+ }
57
+ // Fallback implementation - consumers should override this
58
+ return /*#__PURE__*/ jsx("span", {
59
+ className: props.className,
60
+ "data-icon": props.name,
61
+ suppressHydrationWarning: true,
62
+ "aria-label": props.name.replace('-', ' '),
63
+ children: getIconFallback(props.name)
64
+ });
65
+ }
66
+ function getIconFallback(name) {
67
+ const iconMap = {
68
+ 'chevron-right': '▶',
69
+ 'chevron-down': '▼',
70
+ search: '🔍',
71
+ folder: '📁',
72
+ settings: '⚙️',
73
+ 'file-text': '📄',
74
+ brain: '🧠',
75
+ wrench: '🔧'
76
+ };
77
+ return iconMap[name] || '•';
78
+ }
79
+
80
+ /**
81
+ * Generic code project block component
82
+ * Renders a collapsible code project with basic structure - consumers provide styling
83
+ */ function CodeProjectPart({ title, filename, code, language = 'typescript', collapsed: initialCollapsed = true, className, children, iconRenderer }) {
84
+ const [collapsed, setCollapsed] = useState(initialCollapsed);
85
+ // If children provided, use that (allows complete customization)
86
+ if (children) {
87
+ return /*#__PURE__*/ jsx(Fragment, {
88
+ children: children
89
+ });
90
+ }
91
+ return /*#__PURE__*/ jsxs("div", {
92
+ className: className,
93
+ "data-component": "code-project-block",
94
+ children: [
95
+ /*#__PURE__*/ jsxs("button", {
96
+ onClick: ()=>setCollapsed(!collapsed),
97
+ "data-expanded": !collapsed,
98
+ children: [
99
+ /*#__PURE__*/ jsxs("div", {
100
+ "data-header": true,
101
+ children: [
102
+ iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
103
+ name: 'folder'
104
+ }) : /*#__PURE__*/ jsx(Icon, {
105
+ name: "folder"
106
+ }),
107
+ /*#__PURE__*/ jsx("span", {
108
+ "data-title": true,
109
+ children: title || 'Code Project'
110
+ })
111
+ ]
112
+ }),
113
+ /*#__PURE__*/ jsx("span", {
114
+ "data-version": true,
115
+ children: "v1"
116
+ })
117
+ ]
118
+ }),
119
+ !collapsed && /*#__PURE__*/ jsxs("div", {
120
+ "data-content": true,
121
+ children: [
122
+ /*#__PURE__*/ jsxs("div", {
123
+ "data-file-list": true,
124
+ children: [
125
+ /*#__PURE__*/ jsxs("div", {
126
+ "data-file": true,
127
+ "data-active": true,
128
+ children: [
129
+ iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
130
+ name: 'file-text'
131
+ }) : /*#__PURE__*/ jsx(Icon, {
132
+ name: "file-text"
133
+ }),
134
+ /*#__PURE__*/ jsx("span", {
135
+ "data-filename": true,
136
+ children: filename
137
+ }),
138
+ /*#__PURE__*/ jsx("span", {
139
+ "data-filepath": true,
140
+ children: "app/page.tsx"
141
+ })
142
+ ]
143
+ }),
144
+ /*#__PURE__*/ jsxs("div", {
145
+ "data-file": true,
146
+ children: [
147
+ iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
148
+ name: 'file-text'
149
+ }) : /*#__PURE__*/ jsx(Icon, {
150
+ name: "file-text"
151
+ }),
152
+ /*#__PURE__*/ jsx("span", {
153
+ "data-filename": true,
154
+ children: "layout.tsx"
155
+ }),
156
+ /*#__PURE__*/ jsx("span", {
157
+ "data-filepath": true,
158
+ children: "app/layout.tsx"
159
+ })
160
+ ]
161
+ }),
162
+ /*#__PURE__*/ jsxs("div", {
163
+ "data-file": true,
164
+ children: [
165
+ iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
166
+ name: 'file-text'
167
+ }) : /*#__PURE__*/ jsx(Icon, {
168
+ name: "file-text"
169
+ }),
170
+ /*#__PURE__*/ jsx("span", {
171
+ "data-filename": true,
172
+ children: "globals.css"
173
+ }),
174
+ /*#__PURE__*/ jsx("span", {
175
+ "data-filepath": true,
176
+ children: "app/globals.css"
177
+ })
178
+ ]
179
+ })
180
+ ]
181
+ }),
182
+ code && /*#__PURE__*/ jsx(CodeBlock, {
183
+ language: language,
184
+ code: code
185
+ })
186
+ ]
187
+ })
188
+ ]
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Generic thinking section component
194
+ * Renders a collapsible section with basic structure - consumers provide styling
195
+ */ function ThinkingSection({ title, duration, thought, collapsed: initialCollapsed = true, onCollapse, className, children, iconRenderer, brainIcon, chevronRightIcon, chevronDownIcon }) {
196
+ const [internalCollapsed, setInternalCollapsed] = useState(initialCollapsed);
197
+ const collapsed = onCollapse ? initialCollapsed : internalCollapsed;
198
+ const handleCollapse = onCollapse || (()=>setInternalCollapsed(!internalCollapsed));
199
+ // If children provided, use that (allows complete customization)
200
+ if (children) {
201
+ return /*#__PURE__*/ jsx(Fragment, {
202
+ children: children
203
+ });
204
+ }
205
+ return /*#__PURE__*/ jsxs("div", {
206
+ className: className,
207
+ "data-component": "thinking-section",
208
+ children: [
209
+ /*#__PURE__*/ jsxs("button", {
210
+ onClick: handleCollapse,
211
+ "data-expanded": !collapsed,
212
+ "data-button": true,
213
+ children: [
214
+ /*#__PURE__*/ jsx("div", {
215
+ "data-icon-container": true,
216
+ children: collapsed ? /*#__PURE__*/ jsxs(Fragment, {
217
+ children: [
218
+ brainIcon || (iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
219
+ name: 'brain'
220
+ }) : /*#__PURE__*/ jsx(Icon, {
221
+ name: "brain"
222
+ })),
223
+ chevronRightIcon || (iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
224
+ name: 'chevron-right'
225
+ }) : /*#__PURE__*/ jsx(Icon, {
226
+ name: "chevron-right"
227
+ }))
228
+ ]
229
+ }) : chevronDownIcon || (iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
230
+ name: 'chevron-down'
231
+ }) : /*#__PURE__*/ jsx(Icon, {
232
+ name: "chevron-down"
233
+ }))
234
+ }),
235
+ /*#__PURE__*/ jsxs("span", {
236
+ "data-title": true,
237
+ children: [
238
+ title || 'Thinking',
239
+ duration && ` for ${Math.round(duration)}s`
240
+ ]
241
+ })
242
+ ]
243
+ }),
244
+ !collapsed && thought && /*#__PURE__*/ jsx("div", {
245
+ "data-content": true,
246
+ children: /*#__PURE__*/ jsx("div", {
247
+ "data-thought-container": true,
248
+ children: thought.split('\n\n').map((paragraph, index)=>/*#__PURE__*/ jsx("div", {
249
+ "data-paragraph": true,
250
+ children: paragraph
251
+ }, index))
252
+ })
253
+ })
254
+ ]
255
+ });
256
+ }
257
+
258
+ function getTypeIcon(type, title, iconRenderer) {
259
+ // Check title content for specific cases
260
+ if (title?.includes('No issues found')) {
261
+ return iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
262
+ name: 'wrench'
263
+ }) : /*#__PURE__*/ jsx(Icon, {
264
+ name: "wrench"
265
+ });
266
+ }
267
+ if (title?.includes('Analyzed codebase')) {
268
+ return iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
269
+ name: 'search'
270
+ }) : /*#__PURE__*/ jsx(Icon, {
271
+ name: "search"
272
+ });
273
+ }
274
+ // Fallback to type-based icons
275
+ switch(type){
276
+ case 'task-search-web-v1':
277
+ return iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
278
+ name: 'search'
279
+ }) : /*#__PURE__*/ jsx(Icon, {
280
+ name: "search"
281
+ });
282
+ case 'task-search-repo-v1':
283
+ return iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
284
+ name: 'folder'
285
+ }) : /*#__PURE__*/ jsx(Icon, {
286
+ name: "folder"
287
+ });
288
+ case 'task-diagnostics-v1':
289
+ return iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
290
+ name: 'settings'
291
+ }) : /*#__PURE__*/ jsx(Icon, {
292
+ name: "settings"
293
+ });
294
+ default:
295
+ return iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
296
+ name: 'wrench'
297
+ }) : /*#__PURE__*/ jsx(Icon, {
298
+ name: "wrench"
299
+ });
300
+ }
301
+ }
302
+ function renderTaskPart(part, index, iconRenderer) {
303
+ if (part.type === 'search-web') {
304
+ if (part.status === 'searching') {
305
+ return /*#__PURE__*/ jsx("div", {
306
+ children: `Searching "${part.query}"`
307
+ }, index);
308
+ }
309
+ if (part.status === 'analyzing') {
310
+ return /*#__PURE__*/ jsx("div", {
311
+ children: `Analyzing ${part.count} results...`
312
+ }, index);
313
+ }
314
+ if (part.status === 'complete' && part.answer) {
315
+ return /*#__PURE__*/ jsxs("div", {
316
+ children: [
317
+ /*#__PURE__*/ jsx("p", {
318
+ children: part.answer
319
+ }),
320
+ part.sources && part.sources.length > 0 && /*#__PURE__*/ jsx("div", {
321
+ children: part.sources.map((source, sourceIndex)=>/*#__PURE__*/ jsx("a", {
322
+ href: source.url,
323
+ target: "_blank",
324
+ rel: "noopener noreferrer",
325
+ children: source.title
326
+ }, sourceIndex))
327
+ })
328
+ ]
329
+ }, index);
330
+ }
331
+ }
332
+ if (part.type === 'search-repo') {
333
+ if (part.status === 'searching') {
334
+ return /*#__PURE__*/ jsx("div", {
335
+ children: `Searching "${part.query}"`
336
+ }, index);
337
+ }
338
+ if (part.status === 'reading' && part.files) {
339
+ return /*#__PURE__*/ jsxs("div", {
340
+ children: [
341
+ /*#__PURE__*/ jsx("span", {
342
+ children: "Reading files"
343
+ }),
344
+ part.files.map((file, fileIndex)=>/*#__PURE__*/ jsxs("span", {
345
+ children: [
346
+ iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
347
+ name: 'file-text'
348
+ }) : /*#__PURE__*/ jsx(Icon, {
349
+ name: "file-text"
350
+ }),
351
+ ' ',
352
+ file
353
+ ]
354
+ }, fileIndex))
355
+ ]
356
+ }, index);
357
+ }
358
+ }
359
+ if (part.type === 'diagnostics') {
360
+ if (part.status === 'checking') {
361
+ return /*#__PURE__*/ jsx("div", {
362
+ children: "Checking for issues..."
363
+ }, index);
364
+ }
365
+ if (part.status === 'complete' && part.issues === 0) {
366
+ return /*#__PURE__*/ jsx("div", {
367
+ children: "✅ No issues found"
368
+ }, index);
369
+ }
370
+ }
371
+ return /*#__PURE__*/ jsx("div", {
372
+ children: JSON.stringify(part)
373
+ }, index);
374
+ }
375
+ /**
376
+ * Generic task section component
377
+ * Renders a collapsible task section with basic structure - consumers provide styling
378
+ */ function TaskSection({ title, type, parts = [], collapsed: initialCollapsed = true, onCollapse, className, children, iconRenderer, taskIcon, chevronRightIcon, chevronDownIcon }) {
379
+ const [internalCollapsed, setInternalCollapsed] = useState(initialCollapsed);
380
+ const collapsed = onCollapse ? initialCollapsed : internalCollapsed;
381
+ const handleCollapse = onCollapse || (()=>setInternalCollapsed(!internalCollapsed));
382
+ // If children provided, use that (allows complete customization)
383
+ if (children) {
384
+ return /*#__PURE__*/ jsx(Fragment, {
385
+ children: children
386
+ });
387
+ }
388
+ // Count meaningful parts (parts that would render something)
389
+ const meaningfulParts = parts.filter((part)=>{
390
+ // Check if the part would render meaningful content
391
+ if (part.type === 'search-web') {
392
+ return part.status === 'searching' || part.status === 'analyzing' || part.status === 'complete' && part.answer;
393
+ }
394
+ if (part.type === 'starting-repo-search' && part.query) return true;
395
+ if (part.type === 'select-files' && part.filePaths?.length > 0) return true;
396
+ if (part.type === 'starting-web-search' && part.query) return true;
397
+ if (part.type === 'got-results' && part.count) return true;
398
+ if (part.type === 'finished-web-search' && part.answer) return true;
399
+ if (part.type === 'diagnostics-passed') return true;
400
+ if (part.type === 'fetching-diagnostics') return true;
401
+ // Add more meaningful part types as needed
402
+ return false;
403
+ });
404
+ // If there's only one meaningful part, show just the content without the collapsible wrapper
405
+ if (meaningfulParts.length === 1) {
406
+ return /*#__PURE__*/ jsx("div", {
407
+ className: className,
408
+ "data-component": "task-section-inline",
409
+ children: /*#__PURE__*/ jsx("div", {
410
+ "data-part": true,
411
+ children: renderTaskPart(meaningfulParts[0], 0, iconRenderer)
412
+ })
413
+ });
414
+ }
415
+ return /*#__PURE__*/ jsxs("div", {
416
+ className: className,
417
+ "data-component": "task-section",
418
+ children: [
419
+ /*#__PURE__*/ jsxs("button", {
420
+ onClick: handleCollapse,
421
+ "data-expanded": !collapsed,
422
+ "data-button": true,
423
+ children: [
424
+ /*#__PURE__*/ jsxs("div", {
425
+ "data-icon-container": true,
426
+ children: [
427
+ /*#__PURE__*/ jsx("div", {
428
+ "data-task-icon": true,
429
+ children: taskIcon || getTypeIcon(type, title, iconRenderer)
430
+ }),
431
+ collapsed ? chevronRightIcon || (iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
432
+ name: 'chevron-right'
433
+ }) : /*#__PURE__*/ jsx(Icon, {
434
+ name: "chevron-right"
435
+ })) : chevronDownIcon || (iconRenderer ? /*#__PURE__*/ React.createElement(iconRenderer, {
436
+ name: 'chevron-down'
437
+ }) : /*#__PURE__*/ jsx(Icon, {
438
+ name: "chevron-down"
439
+ }))
440
+ ]
441
+ }),
442
+ /*#__PURE__*/ jsx("span", {
443
+ "data-title": true,
444
+ children: title || 'Task'
445
+ })
446
+ ]
447
+ }),
448
+ !collapsed && /*#__PURE__*/ jsx("div", {
449
+ "data-content": true,
450
+ children: /*#__PURE__*/ jsx("div", {
451
+ "data-parts-container": true,
452
+ children: parts.map((part, index)=>/*#__PURE__*/ jsx("div", {
453
+ "data-part": true,
454
+ children: renderTaskPart(part, index, iconRenderer)
455
+ }, index))
456
+ })
457
+ })
458
+ ]
459
+ });
460
+ }
461
+
462
+ function ContentPartRenderer({ part, iconRenderer, thinkingSectionRenderer, taskSectionRenderer, brainIcon, chevronRightIcon, chevronDownIcon, searchIcon, folderIcon, settingsIcon, wrenchIcon }) {
463
+ if (!part) return null;
464
+ const { type, parts = [], ...metadata } = part;
465
+ switch(type){
466
+ case 'task-thinking-v1':
467
+ {
468
+ const thinkingPart = parts.find((p)=>p.type === 'thinking-end');
469
+ const ThinkingComponent = thinkingSectionRenderer || ThinkingSection;
470
+ const [collapsed, setCollapsed] = useState(true);
471
+ return /*#__PURE__*/ jsx(ThinkingComponent, {
472
+ title: "Thought",
473
+ duration: thinkingPart?.duration,
474
+ thought: thinkingPart?.thought,
475
+ collapsed: collapsed,
476
+ onCollapse: ()=>setCollapsed(!collapsed),
477
+ brainIcon: brainIcon,
478
+ chevronRightIcon: chevronRightIcon,
479
+ chevronDownIcon: chevronDownIcon
480
+ });
481
+ }
482
+ case 'task-search-web-v1':
483
+ {
484
+ const TaskComponent = taskSectionRenderer || TaskSection;
485
+ const [collapsed, setCollapsed] = useState(true);
486
+ return /*#__PURE__*/ jsx(TaskComponent, {
487
+ title: metadata.taskNameComplete || metadata.taskNameActive,
488
+ type: type,
489
+ parts: parts,
490
+ collapsed: collapsed,
491
+ onCollapse: ()=>setCollapsed(!collapsed),
492
+ taskIcon: searchIcon,
493
+ chevronRightIcon: chevronRightIcon,
494
+ chevronDownIcon: chevronDownIcon
495
+ });
496
+ }
497
+ case 'task-search-repo-v1':
498
+ {
499
+ const TaskComponent = taskSectionRenderer || TaskSection;
500
+ const [collapsed, setCollapsed] = useState(true);
501
+ return /*#__PURE__*/ jsx(TaskComponent, {
502
+ title: metadata.taskNameComplete || metadata.taskNameActive,
503
+ type: type,
504
+ parts: parts,
505
+ collapsed: collapsed,
506
+ onCollapse: ()=>setCollapsed(!collapsed),
507
+ taskIcon: folderIcon,
508
+ chevronRightIcon: chevronRightIcon,
509
+ chevronDownIcon: chevronDownIcon
510
+ });
511
+ }
512
+ case 'task-diagnostics-v1':
513
+ {
514
+ const TaskComponent = taskSectionRenderer || TaskSection;
515
+ const [collapsed, setCollapsed] = useState(true);
516
+ return /*#__PURE__*/ jsx(TaskComponent, {
517
+ title: metadata.taskNameComplete || metadata.taskNameActive,
518
+ type: type,
519
+ parts: parts,
520
+ collapsed: collapsed,
521
+ onCollapse: ()=>setCollapsed(!collapsed),
522
+ taskIcon: settingsIcon,
523
+ chevronRightIcon: chevronRightIcon,
524
+ chevronDownIcon: chevronDownIcon
525
+ });
526
+ }
527
+ case 'task-start-v1':
528
+ // Usually just indicates task start - can be hidden or show as status
529
+ return null;
530
+ default:
531
+ return /*#__PURE__*/ jsxs("div", {
532
+ "data-unknown-part-type": type,
533
+ children: [
534
+ "Unknown part type: ",
535
+ type
536
+ ]
537
+ });
538
+ }
539
+ }
540
+
541
+ // Utility function to merge class names
542
+ function cn(...classes) {
543
+ return classes.filter(Boolean).join(' ');
544
+ }
545
+
546
+ // Helper function to render HTML elements with component or className config
547
+ function renderHtmlElement(tagName, key, props, children, className, componentOrConfig, components) {
548
+ if (typeof componentOrConfig === 'function') {
549
+ const Component = componentOrConfig;
550
+ return /*#__PURE__*/ jsx(Component, {
551
+ ...props,
552
+ className: className,
553
+ children: renderChildren(children, key, components)
554
+ }, key);
555
+ } else if (componentOrConfig && typeof componentOrConfig === 'object') {
556
+ const mergedClassName = cn(className, componentOrConfig.className);
557
+ return /*#__PURE__*/ React.createElement(tagName, {
558
+ key,
559
+ ...props,
560
+ className: mergedClassName
561
+ }, renderChildren(children, key, components));
562
+ } else {
563
+ return /*#__PURE__*/ React.createElement(tagName, {
564
+ key,
565
+ ...props,
566
+ className
567
+ }, renderChildren(children, key, components));
568
+ }
569
+ }
570
+ /**
571
+ * Core renderer component for v0 Platform API message content
572
+ */ function MessageImpl({ content, messageId = 'unknown', role: _role = 'assistant', streaming: _streaming = false, isLastMessage: _isLastMessage = false, className, components, renderers }) {
573
+ if (!Array.isArray(content)) {
574
+ console.warn('MessageContent: content must be an array (MessageBinaryFormat)');
575
+ return null;
576
+ }
577
+ // Merge components and renderers (backward compatibility)
578
+ const mergedComponents = {
579
+ ...components,
580
+ // Map legacy renderers to new component names
581
+ ...renderers?.CodeBlock && {
582
+ CodeBlock: renderers.CodeBlock
583
+ },
584
+ ...renderers?.MathRenderer && {
585
+ MathPart: renderers.MathRenderer
586
+ },
587
+ ...renderers?.MathPart && {
588
+ MathPart: renderers.MathPart
589
+ },
590
+ ...renderers?.Icon && {
591
+ Icon: renderers.Icon
592
+ }
593
+ };
594
+ const elements = content.map(([type, ...data], index)=>{
595
+ const key = `${messageId}-${index}`;
596
+ // Markdown/text content (type 0)
597
+ if (type === 0) {
598
+ const markdownData = data[0];
599
+ if (!Array.isArray(markdownData)) {
600
+ return null;
601
+ }
602
+ return /*#__PURE__*/ jsx("div", {
603
+ children: markdownData.map((item, mdIndex)=>{
604
+ const mdKey = `${key}-md-${mdIndex}`;
605
+ return renderMarkdownElement(item, mdKey, mergedComponents);
606
+ })
607
+ }, key);
608
+ }
609
+ // Code block (type 1)
610
+ if (type === 1) {
611
+ const [language, code] = data;
612
+ const CodeBlockComponent = mergedComponents?.CodeBlock || CodeBlock;
613
+ return /*#__PURE__*/ jsx(CodeBlockComponent, {
614
+ language: language || 'text',
615
+ code: code || ''
616
+ }, key);
617
+ }
618
+ // Math (type 2 for inline, type 3 for block)
619
+ if (type === 2 || type === 3) {
620
+ const mathContent = data[0] || '';
621
+ const MathPartComponent = mergedComponents?.MathPart || MathPart;
622
+ return /*#__PURE__*/ jsx(MathPartComponent, {
623
+ content: mathContent,
624
+ inline: type === 2
625
+ }, key);
626
+ }
627
+ // Unknown type - render as text for debugging
628
+ return /*#__PURE__*/ jsxs("div", {
629
+ children: [
630
+ "[Unknown content type: ",
631
+ type,
632
+ "]"
633
+ ]
634
+ }, key);
635
+ });
636
+ return /*#__PURE__*/ jsx("div", {
637
+ className: className,
638
+ children: elements
639
+ });
640
+ }
641
+ function renderMarkdownElement(item, key, components) {
642
+ if (typeof item === 'string') {
643
+ return /*#__PURE__*/ jsx("span", {
644
+ children: item
645
+ }, key);
646
+ }
647
+ if (Array.isArray(item)) {
648
+ const [tagName, props, ...children] = item;
649
+ // Handle special v0 Platform API elements
650
+ if (tagName === 'AssistantMessageContentPart') {
651
+ return /*#__PURE__*/ jsx(ContentPartRenderer, {
652
+ part: props.part,
653
+ iconRenderer: components?.Icon,
654
+ thinkingSectionRenderer: components?.ThinkingSection,
655
+ taskSectionRenderer: components?.TaskSection
656
+ }, key);
657
+ }
658
+ if (tagName === 'Codeblock') {
659
+ const CustomCodeProjectPart = components?.CodeProjectPart;
660
+ const CodeProjectComponent = CustomCodeProjectPart || CodeProjectPart;
661
+ return /*#__PURE__*/ jsx(CodeProjectComponent, {
662
+ language: props.lang,
663
+ code: children[0],
664
+ iconRenderer: components?.Icon
665
+ }, key);
666
+ }
667
+ if (tagName === 'text') {
668
+ return /*#__PURE__*/ jsx("span", {
669
+ children: children[0]
670
+ }, key);
671
+ }
672
+ // Handle standard markdown elements
673
+ const className = props?.className;
674
+ switch(tagName){
675
+ case 'p':
676
+ {
677
+ const componentOrConfig = components?.p;
678
+ if (typeof componentOrConfig === 'function') {
679
+ const Component = componentOrConfig;
680
+ return /*#__PURE__*/ jsx(Component, {
681
+ ...props,
682
+ className: className,
683
+ children: renderChildren(children, key, components)
684
+ }, key);
685
+ } else if (componentOrConfig && typeof componentOrConfig === 'object') {
686
+ const mergedClassName = cn(className, componentOrConfig.className);
687
+ return /*#__PURE__*/ jsx("p", {
688
+ ...props,
689
+ className: mergedClassName,
690
+ children: renderChildren(children, key, components)
691
+ }, key);
692
+ } else {
693
+ return /*#__PURE__*/ jsx("p", {
694
+ ...props,
695
+ className: className,
696
+ children: renderChildren(children, key, components)
697
+ }, key);
698
+ }
699
+ }
700
+ case 'h1':
701
+ return renderHtmlElement('h1', key, props, children, className, components?.h1, components);
702
+ case 'h2':
703
+ return renderHtmlElement('h2', key, props, children, className, components?.h2, components);
704
+ case 'h3':
705
+ return renderHtmlElement('h3', key, props, children, className, components?.h3, components);
706
+ case 'h4':
707
+ return renderHtmlElement('h4', key, props, children, className, components?.h4, components);
708
+ case 'h5':
709
+ return renderHtmlElement('h5', key, props, children, className, components?.h5, components);
710
+ case 'h6':
711
+ return renderHtmlElement('h6', key, props, children, className, components?.h6, components);
712
+ case 'ul':
713
+ return renderHtmlElement('ul', key, props, children, className, components?.ul, components);
714
+ case 'ol':
715
+ return renderHtmlElement('ol', key, props, children, className, components?.ol, components);
716
+ case 'li':
717
+ return renderHtmlElement('li', key, props, children, className, components?.li, components);
718
+ case 'blockquote':
719
+ return renderHtmlElement('blockquote', key, props, children, className, components?.blockquote, components);
720
+ case 'code':
721
+ return renderHtmlElement('code', key, props, children, className, components?.code, components);
722
+ case 'pre':
723
+ return renderHtmlElement('pre', key, props, children, className, components?.pre, components);
724
+ case 'strong':
725
+ return renderHtmlElement('strong', key, props, children, className, components?.strong, components);
726
+ case 'em':
727
+ return renderHtmlElement('em', key, props, children, className, components?.em, components);
728
+ case 'a':
729
+ {
730
+ const componentOrConfig = components?.a;
731
+ if (typeof componentOrConfig === 'function') {
732
+ const Component = componentOrConfig;
733
+ return /*#__PURE__*/ jsx(Component, {
734
+ ...props,
735
+ className: className,
736
+ target: "_blank",
737
+ rel: "noopener noreferrer",
738
+ children: renderChildren(children, key, components)
739
+ }, key);
740
+ } else if (componentOrConfig && typeof componentOrConfig === 'object') {
741
+ const mergedClassName = cn(className, componentOrConfig.className);
742
+ return /*#__PURE__*/ jsx("a", {
743
+ ...props,
744
+ className: mergedClassName,
745
+ target: "_blank",
746
+ rel: "noopener noreferrer",
747
+ children: renderChildren(children, key, components)
748
+ }, key);
749
+ } else {
750
+ return /*#__PURE__*/ jsx("a", {
751
+ ...props,
752
+ className: className,
753
+ target: "_blank",
754
+ rel: "noopener noreferrer",
755
+ children: renderChildren(children, key, components)
756
+ }, key);
757
+ }
758
+ }
759
+ case 'br':
760
+ return /*#__PURE__*/ jsx("br", {}, key);
761
+ case 'hr':
762
+ {
763
+ const componentOrConfig = components?.hr;
764
+ if (typeof componentOrConfig === 'function') {
765
+ const Component = componentOrConfig;
766
+ return /*#__PURE__*/ jsx(Component, {
767
+ ...props,
768
+ className: className
769
+ }, key);
770
+ } else if (componentOrConfig && typeof componentOrConfig === 'object') {
771
+ const mergedClassName = cn(className, componentOrConfig.className);
772
+ return /*#__PURE__*/ jsx("hr", {
773
+ ...props,
774
+ className: mergedClassName
775
+ }, key);
776
+ } else {
777
+ return /*#__PURE__*/ jsx("hr", {
778
+ ...props,
779
+ className: className
780
+ }, key);
781
+ }
782
+ }
783
+ case 'div':
784
+ return renderHtmlElement('div', key, props, children, className, components?.div, components);
785
+ case 'span':
786
+ return renderHtmlElement('span', key, props, children, className, components?.span, components);
787
+ default:
788
+ return /*#__PURE__*/ jsx("span", {
789
+ children: renderChildren(children, key, components)
790
+ }, key);
791
+ }
792
+ }
793
+ return null;
794
+ }
795
+ function renderChildren(children, parentKey, components) {
796
+ return children.map((child, index)=>{
797
+ const key = `${parentKey}-child-${index}`;
798
+ return renderMarkdownElement(child, key, components);
799
+ }).filter(Boolean);
800
+ }
801
+ /**
802
+ * Main component for rendering v0 Platform API message content
803
+ *
804
+ * @example
805
+ * ```tsx
806
+ * import { Message } from '@v0-sdk/react'
807
+ *
808
+ * function MyComponent({ apiResponse }) {
809
+ * const content = JSON.parse(apiResponse.content)
810
+ *
811
+ * return (
812
+ * <Message
813
+ * content={content}
814
+ * messageId={apiResponse.id}
815
+ * role={apiResponse.role}
816
+ * className="space-y-4"
817
+ * components={{
818
+ * p: ({ children, ...props }) => <p className="mb-4" {...props}>{children}</p>,
819
+ * h1: ({ children, ...props }) => <h1 className="mb-4 text-2xl font-bold" {...props}>{children}</h1>,
820
+ * CodeBlock: MyCustomCodeBlock,
821
+ * MathPart: MyCustomMathRenderer,
822
+ * }}
823
+ * />
824
+ * )
825
+ * }
826
+ * ```
827
+ */ const Message = /*#__PURE__*/ React.memo(MessageImpl);
828
+
829
+ export { ContentPartRenderer as AssistantMessageContentPart, CodeBlock, CodeProjectPart as CodeProjectBlock, CodeProjectPart, ContentPartRenderer, Icon, MathPart, MathPart as MathRenderer, Message, Message as MessageContent, Message as MessageRenderer, TaskSection, ThinkingSection, Message as V0MessageRenderer };