@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/README.md +233 -0
- package/dist/index.cjs +848 -0
- package/dist/index.d.ts +341 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +829 -0
- package/package.json +58 -0
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 };
|