@v0-sdk/react 0.2.0 → 0.3.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 +180 -382
- package/dist/index.cjs +744 -563
- package/dist/index.d.ts +221 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +736 -565
- package/package.json +3 -5
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
2
|
|
|
3
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
4
3
|
var React = require('react');
|
|
5
4
|
var jsondiffpatch = require('jsondiffpatch');
|
|
6
5
|
|
|
@@ -27,48 +26,80 @@ function _interopNamespace(e) {
|
|
|
27
26
|
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
28
27
|
var jsondiffpatch__namespace = /*#__PURE__*/_interopNamespace(jsondiffpatch);
|
|
29
28
|
|
|
29
|
+
// Headless hook for code block data
|
|
30
|
+
function useCodeBlock(props) {
|
|
31
|
+
const lines = props.code.split('\n');
|
|
32
|
+
return {
|
|
33
|
+
language: props.language,
|
|
34
|
+
code: props.code,
|
|
35
|
+
filename: props.filename,
|
|
36
|
+
lines,
|
|
37
|
+
lineCount: lines.length
|
|
38
|
+
};
|
|
39
|
+
}
|
|
30
40
|
/**
|
|
31
41
|
* Generic code block component
|
|
32
42
|
* Renders plain code by default - consumers should provide their own styling and highlighting
|
|
33
|
-
|
|
43
|
+
*
|
|
44
|
+
* For headless usage, use the useCodeBlock hook instead.
|
|
45
|
+
*/ function CodeBlock({ language, code, className = '', children, filename }) {
|
|
34
46
|
// If children provided, use that (allows complete customization)
|
|
35
47
|
if (children) {
|
|
36
|
-
return
|
|
37
|
-
children: children
|
|
38
|
-
});
|
|
48
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, children);
|
|
39
49
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
children: /*#__PURE__*/ jsxRuntime.jsx("code", {
|
|
45
|
-
children: code
|
|
46
|
-
})
|
|
50
|
+
const codeBlockData = useCodeBlock({
|
|
51
|
+
language,
|
|
52
|
+
code,
|
|
53
|
+
filename
|
|
47
54
|
});
|
|
55
|
+
// Simple fallback - just render plain code
|
|
56
|
+
// Uses React.createElement for maximum compatibility across environments
|
|
57
|
+
return React__default.default.createElement('pre', {
|
|
58
|
+
className,
|
|
59
|
+
'data-language': codeBlockData.language,
|
|
60
|
+
...filename && {
|
|
61
|
+
'data-filename': filename
|
|
62
|
+
}
|
|
63
|
+
}, React__default.default.createElement('code', {}, codeBlockData.code));
|
|
48
64
|
}
|
|
49
65
|
|
|
50
66
|
// Context for providing custom icon implementation
|
|
51
|
-
const IconContext =
|
|
67
|
+
const IconContext = React.createContext(null);
|
|
68
|
+
// Headless hook for icon data
|
|
69
|
+
function useIcon(props) {
|
|
70
|
+
return {
|
|
71
|
+
name: props.name,
|
|
72
|
+
fallback: getIconFallback(props.name),
|
|
73
|
+
ariaLabel: props.name.replace('-', ' ')
|
|
74
|
+
};
|
|
75
|
+
}
|
|
52
76
|
/**
|
|
53
77
|
* Generic icon component that can be customized by consumers.
|
|
54
78
|
* By default, renders a simple fallback. Consumers should provide
|
|
55
79
|
* their own icon implementation via context or props.
|
|
80
|
+
*
|
|
81
|
+
* For headless usage, use the useIcon hook instead.
|
|
56
82
|
*/ function Icon(props) {
|
|
57
83
|
const CustomIcon = React.useContext(IconContext);
|
|
58
84
|
// Use custom icon implementation if provided via context
|
|
59
85
|
if (CustomIcon) {
|
|
60
|
-
return
|
|
61
|
-
...props
|
|
62
|
-
});
|
|
86
|
+
return React__default.default.createElement(CustomIcon, props);
|
|
63
87
|
}
|
|
88
|
+
const iconData = useIcon(props);
|
|
64
89
|
// Fallback implementation - consumers should override this
|
|
65
|
-
|
|
90
|
+
// This uses minimal DOM-specific attributes for maximum compatibility
|
|
91
|
+
return React__default.default.createElement('span', {
|
|
66
92
|
className: props.className,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
'data-icon': iconData.name,
|
|
94
|
+
'aria-label': iconData.ariaLabel
|
|
95
|
+
}, iconData.fallback);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Provider for custom icon implementation
|
|
99
|
+
*/ function IconProvider({ children, component }) {
|
|
100
|
+
return React__default.default.createElement(IconContext.Provider, {
|
|
101
|
+
value: component
|
|
102
|
+
}, children);
|
|
72
103
|
}
|
|
73
104
|
function getIconFallback(name) {
|
|
74
105
|
const iconMap = {
|
|
@@ -84,314 +115,291 @@ function getIconFallback(name) {
|
|
|
84
115
|
return iconMap[name] || '•';
|
|
85
116
|
}
|
|
86
117
|
|
|
118
|
+
// Headless hook for code project
|
|
119
|
+
function useCodeProject({ title, filename, code, language = 'typescript', collapsed: initialCollapsed = true }) {
|
|
120
|
+
const [collapsed, setCollapsed] = React.useState(initialCollapsed);
|
|
121
|
+
// Mock file structure - in a real implementation this could be dynamic
|
|
122
|
+
const files = [
|
|
123
|
+
{
|
|
124
|
+
name: filename || 'page.tsx',
|
|
125
|
+
path: 'app/page.tsx',
|
|
126
|
+
active: true
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'layout.tsx',
|
|
130
|
+
path: 'app/layout.tsx',
|
|
131
|
+
active: false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'globals.css',
|
|
135
|
+
path: 'app/globals.css',
|
|
136
|
+
active: false
|
|
137
|
+
}
|
|
138
|
+
];
|
|
139
|
+
return {
|
|
140
|
+
data: {
|
|
141
|
+
title: title || 'Code Project',
|
|
142
|
+
filename,
|
|
143
|
+
code,
|
|
144
|
+
language,
|
|
145
|
+
collapsed,
|
|
146
|
+
files
|
|
147
|
+
},
|
|
148
|
+
collapsed,
|
|
149
|
+
toggleCollapsed: ()=>setCollapsed(!collapsed)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
87
152
|
/**
|
|
88
153
|
* Generic code project block component
|
|
89
154
|
* Renders a collapsible code project with basic structure - consumers provide styling
|
|
155
|
+
*
|
|
156
|
+
* For headless usage, use the useCodeProject hook instead.
|
|
90
157
|
*/ function CodeProjectPart({ title, filename, code, language = 'typescript', collapsed: initialCollapsed = true, className, children, iconRenderer }) {
|
|
91
|
-
const
|
|
158
|
+
const { data, collapsed, toggleCollapsed } = useCodeProject({
|
|
159
|
+
title,
|
|
160
|
+
filename,
|
|
161
|
+
code,
|
|
162
|
+
language,
|
|
163
|
+
collapsed: initialCollapsed
|
|
164
|
+
});
|
|
92
165
|
// If children provided, use that (allows complete customization)
|
|
93
166
|
if (children) {
|
|
94
|
-
return
|
|
95
|
-
children: children
|
|
96
|
-
});
|
|
167
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, children);
|
|
97
168
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
name: 'file-text'
|
|
138
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
139
|
-
name: "file-text"
|
|
140
|
-
}),
|
|
141
|
-
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
142
|
-
"data-filename": true,
|
|
143
|
-
children: filename
|
|
144
|
-
}),
|
|
145
|
-
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
146
|
-
"data-filepath": true,
|
|
147
|
-
children: "app/page.tsx"
|
|
148
|
-
})
|
|
149
|
-
]
|
|
150
|
-
}),
|
|
151
|
-
/*#__PURE__*/ jsxRuntime.jsxs("div", {
|
|
152
|
-
"data-file": true,
|
|
153
|
-
children: [
|
|
154
|
-
iconRenderer ? /*#__PURE__*/ React__default.default.createElement(iconRenderer, {
|
|
155
|
-
name: 'file-text'
|
|
156
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
157
|
-
name: "file-text"
|
|
158
|
-
}),
|
|
159
|
-
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
160
|
-
"data-filename": true,
|
|
161
|
-
children: "layout.tsx"
|
|
162
|
-
}),
|
|
163
|
-
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
164
|
-
"data-filepath": true,
|
|
165
|
-
children: "app/layout.tsx"
|
|
166
|
-
})
|
|
167
|
-
]
|
|
168
|
-
}),
|
|
169
|
-
/*#__PURE__*/ jsxRuntime.jsxs("div", {
|
|
170
|
-
"data-file": true,
|
|
171
|
-
children: [
|
|
172
|
-
iconRenderer ? /*#__PURE__*/ React__default.default.createElement(iconRenderer, {
|
|
173
|
-
name: 'file-text'
|
|
174
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
175
|
-
name: "file-text"
|
|
176
|
-
}),
|
|
177
|
-
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
178
|
-
"data-filename": true,
|
|
179
|
-
children: "globals.css"
|
|
180
|
-
}),
|
|
181
|
-
/*#__PURE__*/ jsxRuntime.jsx("span", {
|
|
182
|
-
"data-filepath": true,
|
|
183
|
-
children: "app/globals.css"
|
|
184
|
-
})
|
|
185
|
-
]
|
|
186
|
-
})
|
|
187
|
-
]
|
|
188
|
-
}),
|
|
189
|
-
code && /*#__PURE__*/ jsxRuntime.jsx(CodeBlock, {
|
|
190
|
-
language: language,
|
|
191
|
-
code: code
|
|
192
|
-
})
|
|
193
|
-
]
|
|
194
|
-
})
|
|
195
|
-
]
|
|
196
|
-
});
|
|
169
|
+
// Uses React.createElement for maximum compatibility across environments
|
|
170
|
+
return React__default.default.createElement('div', {
|
|
171
|
+
className,
|
|
172
|
+
'data-component': 'code-project-block'
|
|
173
|
+
}, React__default.default.createElement('button', {
|
|
174
|
+
onClick: toggleCollapsed,
|
|
175
|
+
'data-expanded': !collapsed
|
|
176
|
+
}, React__default.default.createElement('div', {
|
|
177
|
+
'data-header': true
|
|
178
|
+
}, iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
179
|
+
name: 'folder'
|
|
180
|
+
}) : React__default.default.createElement(Icon, {
|
|
181
|
+
name: 'folder'
|
|
182
|
+
}), React__default.default.createElement('span', {
|
|
183
|
+
'data-title': true
|
|
184
|
+
}, data.title)), React__default.default.createElement('span', {
|
|
185
|
+
'data-version': true
|
|
186
|
+
}, 'v1')), !collapsed ? React__default.default.createElement('div', {
|
|
187
|
+
'data-content': true
|
|
188
|
+
}, React__default.default.createElement('div', {
|
|
189
|
+
'data-file-list': true
|
|
190
|
+
}, data.files.map((file, index)=>React__default.default.createElement('div', {
|
|
191
|
+
key: index,
|
|
192
|
+
'data-file': true,
|
|
193
|
+
...file.active && {
|
|
194
|
+
'data-active': true
|
|
195
|
+
}
|
|
196
|
+
}, iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
197
|
+
name: 'file-text'
|
|
198
|
+
}) : React__default.default.createElement(Icon, {
|
|
199
|
+
name: 'file-text'
|
|
200
|
+
}), React__default.default.createElement('span', {
|
|
201
|
+
'data-filename': true
|
|
202
|
+
}, file.name), React__default.default.createElement('span', {
|
|
203
|
+
'data-filepath': true
|
|
204
|
+
}, file.path)))), data.code ? React__default.default.createElement(CodeBlock, {
|
|
205
|
+
language: data.language,
|
|
206
|
+
code: data.code
|
|
207
|
+
}) : null) : null);
|
|
197
208
|
}
|
|
198
209
|
|
|
210
|
+
// Headless hook for thinking section
|
|
211
|
+
function useThinkingSection({ title, duration, thought, collapsed: initialCollapsed = true, onCollapse }) {
|
|
212
|
+
const [internalCollapsed, setInternalCollapsed] = React.useState(initialCollapsed);
|
|
213
|
+
const collapsed = onCollapse ? initialCollapsed : internalCollapsed;
|
|
214
|
+
const handleCollapse = onCollapse || (()=>setInternalCollapsed(!internalCollapsed));
|
|
215
|
+
const paragraphs = thought ? thought.split('\n\n') : [];
|
|
216
|
+
const formattedDuration = duration ? `${Math.round(duration)}s` : undefined;
|
|
217
|
+
return {
|
|
218
|
+
data: {
|
|
219
|
+
title: title || 'Thinking',
|
|
220
|
+
duration,
|
|
221
|
+
thought,
|
|
222
|
+
collapsed,
|
|
223
|
+
paragraphs,
|
|
224
|
+
formattedDuration
|
|
225
|
+
},
|
|
226
|
+
collapsed,
|
|
227
|
+
handleCollapse
|
|
228
|
+
};
|
|
229
|
+
}
|
|
199
230
|
/**
|
|
200
231
|
* Generic thinking section component
|
|
201
232
|
* Renders a collapsible section with basic structure - consumers provide styling
|
|
233
|
+
*
|
|
234
|
+
* For headless usage, use the useThinkingSection hook instead.
|
|
202
235
|
*/ function ThinkingSection({ title, duration, thought, collapsed: initialCollapsed = true, onCollapse, className, children, iconRenderer, brainIcon, chevronRightIcon, chevronDownIcon }) {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
236
|
+
const { data, collapsed, handleCollapse } = useThinkingSection({
|
|
237
|
+
title,
|
|
238
|
+
duration,
|
|
239
|
+
thought,
|
|
240
|
+
collapsed: initialCollapsed,
|
|
241
|
+
onCollapse
|
|
242
|
+
});
|
|
206
243
|
// If children provided, use that (allows complete customization)
|
|
207
244
|
if (children) {
|
|
208
|
-
return
|
|
209
|
-
children: children
|
|
210
|
-
});
|
|
245
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, children);
|
|
211
246
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
children: [
|
|
245
|
-
title || 'Thinking',
|
|
246
|
-
duration && ` for ${Math.round(duration)}s`
|
|
247
|
-
]
|
|
248
|
-
})
|
|
249
|
-
]
|
|
250
|
-
}),
|
|
251
|
-
!collapsed && thought && /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
252
|
-
"data-content": true,
|
|
253
|
-
children: /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
254
|
-
"data-thought-container": true,
|
|
255
|
-
children: thought.split('\n\n').map((paragraph, index)=>/*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
256
|
-
"data-paragraph": true,
|
|
257
|
-
children: paragraph
|
|
258
|
-
}, index))
|
|
259
|
-
})
|
|
260
|
-
})
|
|
261
|
-
]
|
|
262
|
-
});
|
|
247
|
+
// Uses React.createElement for maximum compatibility across environments
|
|
248
|
+
return React__default.default.createElement('div', {
|
|
249
|
+
className,
|
|
250
|
+
'data-component': 'thinking-section'
|
|
251
|
+
}, React__default.default.createElement('button', {
|
|
252
|
+
onClick: handleCollapse,
|
|
253
|
+
'data-expanded': !collapsed,
|
|
254
|
+
'data-button': true
|
|
255
|
+
}, React__default.default.createElement('div', {
|
|
256
|
+
'data-icon-container': true
|
|
257
|
+
}, collapsed ? React__default.default.createElement(React__default.default.Fragment, {}, brainIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
258
|
+
name: 'brain'
|
|
259
|
+
}) : React__default.default.createElement(Icon, {
|
|
260
|
+
name: 'brain'
|
|
261
|
+
})), chevronRightIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
262
|
+
name: 'chevron-right'
|
|
263
|
+
}) : React__default.default.createElement(Icon, {
|
|
264
|
+
name: 'chevron-right'
|
|
265
|
+
}))) : chevronDownIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
266
|
+
name: 'chevron-down'
|
|
267
|
+
}) : React__default.default.createElement(Icon, {
|
|
268
|
+
name: 'chevron-down'
|
|
269
|
+
}))), React__default.default.createElement('span', {
|
|
270
|
+
'data-title': true
|
|
271
|
+
}, data.title + (data.formattedDuration ? ` for ${data.formattedDuration}` : ''))), !collapsed && data.thought ? React__default.default.createElement('div', {
|
|
272
|
+
'data-content': true
|
|
273
|
+
}, React__default.default.createElement('div', {
|
|
274
|
+
'data-thought-container': true
|
|
275
|
+
}, data.paragraphs.map((paragraph, index)=>React__default.default.createElement('div', {
|
|
276
|
+
key: index,
|
|
277
|
+
'data-paragraph': true
|
|
278
|
+
}, paragraph)))) : null);
|
|
263
279
|
}
|
|
264
280
|
|
|
265
|
-
function getTypeIcon(type, title
|
|
281
|
+
function getTypeIcon(type, title) {
|
|
266
282
|
// Check title content for specific cases
|
|
267
283
|
if (title?.includes('No issues found')) {
|
|
268
|
-
return
|
|
269
|
-
name: 'wrench'
|
|
270
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
271
|
-
name: "wrench"
|
|
272
|
-
});
|
|
284
|
+
return 'wrench';
|
|
273
285
|
}
|
|
274
286
|
if (title?.includes('Analyzed codebase')) {
|
|
275
|
-
return
|
|
276
|
-
name: 'search'
|
|
277
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
278
|
-
name: "search"
|
|
279
|
-
});
|
|
287
|
+
return 'search';
|
|
280
288
|
}
|
|
281
289
|
// Fallback to type-based icons
|
|
282
290
|
switch(type){
|
|
283
291
|
case 'task-search-web-v1':
|
|
284
|
-
return
|
|
285
|
-
name: 'search'
|
|
286
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
287
|
-
name: "search"
|
|
288
|
-
});
|
|
292
|
+
return 'search';
|
|
289
293
|
case 'task-search-repo-v1':
|
|
290
|
-
return
|
|
291
|
-
name: 'folder'
|
|
292
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
293
|
-
name: "folder"
|
|
294
|
-
});
|
|
294
|
+
return 'folder';
|
|
295
295
|
case 'task-diagnostics-v1':
|
|
296
|
-
return
|
|
297
|
-
name: 'settings'
|
|
298
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
299
|
-
name: "settings"
|
|
300
|
-
});
|
|
296
|
+
return 'settings';
|
|
301
297
|
default:
|
|
302
|
-
return
|
|
303
|
-
name: 'wrench'
|
|
304
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
305
|
-
name: "wrench"
|
|
306
|
-
});
|
|
298
|
+
return 'wrench';
|
|
307
299
|
}
|
|
308
300
|
}
|
|
309
|
-
function
|
|
301
|
+
function processTaskPart(part, index) {
|
|
302
|
+
const baseData = {
|
|
303
|
+
type: part.type,
|
|
304
|
+
status: part.status,
|
|
305
|
+
content: null
|
|
306
|
+
};
|
|
310
307
|
if (part.type === 'search-web') {
|
|
311
308
|
if (part.status === 'searching') {
|
|
312
|
-
return
|
|
313
|
-
|
|
314
|
-
|
|
309
|
+
return {
|
|
310
|
+
...baseData,
|
|
311
|
+
isSearching: true,
|
|
312
|
+
query: part.query,
|
|
313
|
+
content: `Searching "${part.query}"`
|
|
314
|
+
};
|
|
315
315
|
}
|
|
316
316
|
if (part.status === 'analyzing') {
|
|
317
|
-
return
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
return {
|
|
318
|
+
...baseData,
|
|
319
|
+
isAnalyzing: true,
|
|
320
|
+
count: part.count,
|
|
321
|
+
content: `Analyzing ${part.count} results...`
|
|
322
|
+
};
|
|
320
323
|
}
|
|
321
324
|
if (part.status === 'complete' && part.answer) {
|
|
322
|
-
return
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
href: source.url,
|
|
330
|
-
target: "_blank",
|
|
331
|
-
rel: "noopener noreferrer",
|
|
332
|
-
children: source.title
|
|
333
|
-
}, sourceIndex))
|
|
334
|
-
})
|
|
335
|
-
]
|
|
336
|
-
}, index);
|
|
325
|
+
return {
|
|
326
|
+
...baseData,
|
|
327
|
+
isComplete: true,
|
|
328
|
+
answer: part.answer,
|
|
329
|
+
sources: part.sources,
|
|
330
|
+
content: part.answer
|
|
331
|
+
};
|
|
337
332
|
}
|
|
338
333
|
}
|
|
339
334
|
if (part.type === 'search-repo') {
|
|
340
335
|
if (part.status === 'searching') {
|
|
341
|
-
return
|
|
342
|
-
|
|
343
|
-
|
|
336
|
+
return {
|
|
337
|
+
...baseData,
|
|
338
|
+
isSearching: true,
|
|
339
|
+
query: part.query,
|
|
340
|
+
content: `Searching "${part.query}"`
|
|
341
|
+
};
|
|
344
342
|
}
|
|
345
343
|
if (part.status === 'reading' && part.files) {
|
|
346
|
-
return
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
part.files.map((file, fileIndex)=>/*#__PURE__*/ jsxRuntime.jsxs("span", {
|
|
352
|
-
children: [
|
|
353
|
-
iconRenderer ? /*#__PURE__*/ React__default.default.createElement(iconRenderer, {
|
|
354
|
-
name: 'file-text'
|
|
355
|
-
}) : /*#__PURE__*/ jsxRuntime.jsx(Icon, {
|
|
356
|
-
name: "file-text"
|
|
357
|
-
}),
|
|
358
|
-
' ',
|
|
359
|
-
file
|
|
360
|
-
]
|
|
361
|
-
}, fileIndex))
|
|
362
|
-
]
|
|
363
|
-
}, index);
|
|
344
|
+
return {
|
|
345
|
+
...baseData,
|
|
346
|
+
files: part.files,
|
|
347
|
+
content: 'Reading files'
|
|
348
|
+
};
|
|
364
349
|
}
|
|
365
350
|
}
|
|
366
351
|
if (part.type === 'diagnostics') {
|
|
367
352
|
if (part.status === 'checking') {
|
|
368
|
-
return
|
|
369
|
-
|
|
370
|
-
|
|
353
|
+
return {
|
|
354
|
+
...baseData,
|
|
355
|
+
content: 'Checking for issues...'
|
|
356
|
+
};
|
|
371
357
|
}
|
|
372
358
|
if (part.status === 'complete' && part.issues === 0) {
|
|
373
|
-
return
|
|
374
|
-
|
|
375
|
-
|
|
359
|
+
return {
|
|
360
|
+
...baseData,
|
|
361
|
+
isComplete: true,
|
|
362
|
+
issues: part.issues,
|
|
363
|
+
content: '✅ No issues found'
|
|
364
|
+
};
|
|
376
365
|
}
|
|
377
366
|
}
|
|
378
|
-
return
|
|
379
|
-
|
|
380
|
-
|
|
367
|
+
return {
|
|
368
|
+
...baseData,
|
|
369
|
+
content: JSON.stringify(part)
|
|
370
|
+
};
|
|
381
371
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
372
|
+
function renderTaskPartContent(partData, index, iconRenderer) {
|
|
373
|
+
if (partData.type === 'search-web' && partData.isComplete && partData.sources) {
|
|
374
|
+
return React__default.default.createElement('div', {
|
|
375
|
+
key: index
|
|
376
|
+
}, React__default.default.createElement('p', {}, partData.content), partData.sources.length > 0 ? React__default.default.createElement('div', {}, partData.sources.map((source, sourceIndex)=>React__default.default.createElement('a', {
|
|
377
|
+
key: sourceIndex,
|
|
378
|
+
href: source.url,
|
|
379
|
+
target: '_blank',
|
|
380
|
+
rel: 'noopener noreferrer'
|
|
381
|
+
}, source.title))) : null);
|
|
382
|
+
}
|
|
383
|
+
if (partData.type === 'search-repo' && partData.files) {
|
|
384
|
+
return React__default.default.createElement('div', {
|
|
385
|
+
key: index
|
|
386
|
+
}, React__default.default.createElement('span', {}, partData.content), partData.files.map((file, fileIndex)=>React__default.default.createElement('span', {
|
|
387
|
+
key: fileIndex
|
|
388
|
+
}, iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
389
|
+
name: 'file-text'
|
|
390
|
+
}) : React__default.default.createElement(Icon, {
|
|
391
|
+
name: 'file-text'
|
|
392
|
+
}), ' ', file)));
|
|
393
|
+
}
|
|
394
|
+
return React__default.default.createElement('div', {
|
|
395
|
+
key: index
|
|
396
|
+
}, partData.content);
|
|
397
|
+
}
|
|
398
|
+
// Headless hook for task section
|
|
399
|
+
function useTaskSection({ title, type, parts = [], collapsed: initialCollapsed = true, onCollapse }) {
|
|
386
400
|
const [internalCollapsed, setInternalCollapsed] = React.useState(initialCollapsed);
|
|
387
401
|
const collapsed = onCollapse ? initialCollapsed : internalCollapsed;
|
|
388
402
|
const handleCollapse = onCollapse || (()=>setInternalCollapsed(!internalCollapsed));
|
|
389
|
-
// If children provided, use that (allows complete customization)
|
|
390
|
-
if (children) {
|
|
391
|
-
return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
|
|
392
|
-
children: children
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
403
|
// Count meaningful parts (parts that would render something)
|
|
396
404
|
const meaningfulParts = parts.filter((part)=>{
|
|
397
405
|
// Check if the part would render meaningful content
|
|
@@ -405,174 +413,216 @@ function renderTaskPart(part, index, iconRenderer) {
|
|
|
405
413
|
if (part.type === 'finished-web-search' && part.answer) return true;
|
|
406
414
|
if (part.type === 'diagnostics-passed') return true;
|
|
407
415
|
if (part.type === 'fetching-diagnostics') return true;
|
|
408
|
-
// Add more meaningful part types as needed
|
|
409
416
|
return false;
|
|
410
417
|
});
|
|
418
|
+
const processedParts = parts.map(processTaskPart);
|
|
419
|
+
return {
|
|
420
|
+
data: {
|
|
421
|
+
title: title || 'Task',
|
|
422
|
+
type,
|
|
423
|
+
parts,
|
|
424
|
+
collapsed,
|
|
425
|
+
meaningfulParts,
|
|
426
|
+
shouldShowCollapsible: meaningfulParts.length > 1,
|
|
427
|
+
iconName: getTypeIcon(type, title)
|
|
428
|
+
},
|
|
429
|
+
collapsed,
|
|
430
|
+
handleCollapse,
|
|
431
|
+
processedParts
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Generic task section component
|
|
436
|
+
* Renders a collapsible task section with basic structure - consumers provide styling
|
|
437
|
+
*
|
|
438
|
+
* For headless usage, use the useTaskSection hook instead.
|
|
439
|
+
*/ function TaskSection({ title, type, parts = [], collapsed: initialCollapsed = true, onCollapse, className, children, iconRenderer, taskIcon, chevronRightIcon, chevronDownIcon }) {
|
|
440
|
+
const { data, collapsed, handleCollapse, processedParts } = useTaskSection({
|
|
441
|
+
title,
|
|
442
|
+
type,
|
|
443
|
+
parts,
|
|
444
|
+
collapsed: initialCollapsed,
|
|
445
|
+
onCollapse
|
|
446
|
+
});
|
|
447
|
+
// If children provided, use that (allows complete customization)
|
|
448
|
+
if (children) {
|
|
449
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, children);
|
|
450
|
+
}
|
|
411
451
|
// If there's only one meaningful part, show just the content without the collapsible wrapper
|
|
412
|
-
if (meaningfulParts.length === 1) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
});
|
|
452
|
+
if (!data.shouldShowCollapsible && data.meaningfulParts.length === 1) {
|
|
453
|
+
const partData = processTaskPart(data.meaningfulParts[0]);
|
|
454
|
+
return React__default.default.createElement('div', {
|
|
455
|
+
className,
|
|
456
|
+
'data-component': 'task-section-inline'
|
|
457
|
+
}, React__default.default.createElement('div', {
|
|
458
|
+
'data-part': true
|
|
459
|
+
}, renderTaskPartContent(partData, 0, iconRenderer)));
|
|
421
460
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
"data-content": true,
|
|
457
|
-
children: /*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
458
|
-
"data-parts-container": true,
|
|
459
|
-
children: parts.map((part, index)=>/*#__PURE__*/ jsxRuntime.jsx("div", {
|
|
460
|
-
"data-part": true,
|
|
461
|
-
children: renderTaskPart(part, index, iconRenderer)
|
|
462
|
-
}, index))
|
|
463
|
-
})
|
|
464
|
-
})
|
|
465
|
-
]
|
|
466
|
-
});
|
|
461
|
+
// Uses React.createElement for maximum compatibility across environments
|
|
462
|
+
return React__default.default.createElement('div', {
|
|
463
|
+
className,
|
|
464
|
+
'data-component': 'task-section'
|
|
465
|
+
}, React__default.default.createElement('button', {
|
|
466
|
+
onClick: handleCollapse,
|
|
467
|
+
'data-expanded': !collapsed,
|
|
468
|
+
'data-button': true
|
|
469
|
+
}, React__default.default.createElement('div', {
|
|
470
|
+
'data-icon-container': true
|
|
471
|
+
}, React__default.default.createElement('div', {
|
|
472
|
+
'data-task-icon': true
|
|
473
|
+
}, taskIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
474
|
+
name: data.iconName
|
|
475
|
+
}) : React__default.default.createElement(Icon, {
|
|
476
|
+
name: data.iconName
|
|
477
|
+
}))), collapsed ? chevronRightIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
478
|
+
name: 'chevron-right'
|
|
479
|
+
}) : React__default.default.createElement(Icon, {
|
|
480
|
+
name: 'chevron-right'
|
|
481
|
+
})) : chevronDownIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
|
|
482
|
+
name: 'chevron-down'
|
|
483
|
+
}) : React__default.default.createElement(Icon, {
|
|
484
|
+
name: 'chevron-down'
|
|
485
|
+
}))), React__default.default.createElement('span', {
|
|
486
|
+
'data-title': true
|
|
487
|
+
}, data.title)), !collapsed ? React__default.default.createElement('div', {
|
|
488
|
+
'data-content': true
|
|
489
|
+
}, React__default.default.createElement('div', {
|
|
490
|
+
'data-parts-container': true
|
|
491
|
+
}, processedParts.map((partData, index)=>React__default.default.createElement('div', {
|
|
492
|
+
key: index,
|
|
493
|
+
'data-part': true
|
|
494
|
+
}, renderTaskPartContent(partData, index, iconRenderer))))) : null);
|
|
467
495
|
}
|
|
468
496
|
|
|
469
|
-
|
|
470
|
-
|
|
497
|
+
// Headless hook for content part
|
|
498
|
+
function useContentPart(part) {
|
|
499
|
+
if (!part) {
|
|
500
|
+
return {
|
|
501
|
+
type: '',
|
|
502
|
+
parts: [],
|
|
503
|
+
metadata: {},
|
|
504
|
+
componentType: null
|
|
505
|
+
};
|
|
506
|
+
}
|
|
471
507
|
const { type, parts = [], ...metadata } = part;
|
|
508
|
+
let componentType = 'unknown';
|
|
509
|
+
let title;
|
|
510
|
+
let iconName;
|
|
511
|
+
let thinkingData;
|
|
472
512
|
switch(type){
|
|
473
513
|
case 'task-thinking-v1':
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
collapsed: collapsed,
|
|
483
|
-
onCollapse: ()=>setCollapsed(!collapsed),
|
|
484
|
-
brainIcon: brainIcon,
|
|
485
|
-
chevronRightIcon: chevronRightIcon,
|
|
486
|
-
chevronDownIcon: chevronDownIcon
|
|
487
|
-
});
|
|
488
|
-
}
|
|
514
|
+
componentType = 'thinking';
|
|
515
|
+
title = 'Thought';
|
|
516
|
+
const thinkingPart = parts.find((p)=>p.type === 'thinking-end');
|
|
517
|
+
thinkingData = {
|
|
518
|
+
duration: thinkingPart?.duration,
|
|
519
|
+
thought: thinkingPart?.thought
|
|
520
|
+
};
|
|
521
|
+
break;
|
|
489
522
|
case 'task-search-web-v1':
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
title: metadata.taskNameComplete || metadata.taskNameActive,
|
|
495
|
-
type: type,
|
|
496
|
-
parts: parts,
|
|
497
|
-
collapsed: collapsed,
|
|
498
|
-
onCollapse: ()=>setCollapsed(!collapsed),
|
|
499
|
-
taskIcon: searchIcon,
|
|
500
|
-
chevronRightIcon: chevronRightIcon,
|
|
501
|
-
chevronDownIcon: chevronDownIcon
|
|
502
|
-
});
|
|
503
|
-
}
|
|
523
|
+
componentType = 'task';
|
|
524
|
+
title = metadata.taskNameComplete || metadata.taskNameActive;
|
|
525
|
+
iconName = 'search';
|
|
526
|
+
break;
|
|
504
527
|
case 'task-search-repo-v1':
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
title: metadata.taskNameComplete || metadata.taskNameActive,
|
|
510
|
-
type: type,
|
|
511
|
-
parts: parts,
|
|
512
|
-
collapsed: collapsed,
|
|
513
|
-
onCollapse: ()=>setCollapsed(!collapsed),
|
|
514
|
-
taskIcon: folderIcon,
|
|
515
|
-
chevronRightIcon: chevronRightIcon,
|
|
516
|
-
chevronDownIcon: chevronDownIcon
|
|
517
|
-
});
|
|
518
|
-
}
|
|
528
|
+
componentType = 'task';
|
|
529
|
+
title = metadata.taskNameComplete || metadata.taskNameActive;
|
|
530
|
+
iconName = 'folder';
|
|
531
|
+
break;
|
|
519
532
|
case 'task-diagnostics-v1':
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
title: metadata.taskNameComplete || metadata.taskNameActive,
|
|
525
|
-
type: type,
|
|
526
|
-
parts: parts,
|
|
527
|
-
collapsed: collapsed,
|
|
528
|
-
onCollapse: ()=>setCollapsed(!collapsed),
|
|
529
|
-
taskIcon: settingsIcon,
|
|
530
|
-
chevronRightIcon: chevronRightIcon,
|
|
531
|
-
chevronDownIcon: chevronDownIcon
|
|
532
|
-
});
|
|
533
|
-
}
|
|
533
|
+
componentType = 'task';
|
|
534
|
+
title = metadata.taskNameComplete || metadata.taskNameActive;
|
|
535
|
+
iconName = 'settings';
|
|
536
|
+
break;
|
|
534
537
|
case 'task-read-file-v1':
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
title: metadata.taskNameComplete || metadata.taskNameActive || 'Reading file',
|
|
540
|
-
type: type,
|
|
541
|
-
parts: parts,
|
|
542
|
-
collapsed: collapsed,
|
|
543
|
-
onCollapse: ()=>setCollapsed(!collapsed),
|
|
544
|
-
taskIcon: folderIcon,
|
|
545
|
-
chevronRightIcon: chevronRightIcon,
|
|
546
|
-
chevronDownIcon: chevronDownIcon
|
|
547
|
-
});
|
|
548
|
-
}
|
|
538
|
+
componentType = 'task';
|
|
539
|
+
title = metadata.taskNameComplete || metadata.taskNameActive || 'Reading file';
|
|
540
|
+
iconName = 'folder';
|
|
541
|
+
break;
|
|
549
542
|
case 'task-coding-v1':
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
title: metadata.taskNameComplete || metadata.taskNameActive || 'Coding',
|
|
555
|
-
type: type,
|
|
556
|
-
parts: parts,
|
|
557
|
-
collapsed: collapsed,
|
|
558
|
-
onCollapse: ()=>setCollapsed(!collapsed),
|
|
559
|
-
taskIcon: wrenchIcon,
|
|
560
|
-
chevronRightIcon: chevronRightIcon,
|
|
561
|
-
chevronDownIcon: chevronDownIcon
|
|
562
|
-
});
|
|
563
|
-
}
|
|
543
|
+
componentType = 'task';
|
|
544
|
+
title = metadata.taskNameComplete || metadata.taskNameActive || 'Coding';
|
|
545
|
+
iconName = 'wrench';
|
|
546
|
+
break;
|
|
564
547
|
case 'task-start-v1':
|
|
565
|
-
// Usually just indicates task start - can be hidden
|
|
566
|
-
|
|
548
|
+
componentType = null; // Usually just indicates task start - can be hidden
|
|
549
|
+
break;
|
|
567
550
|
default:
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
551
|
+
componentType = 'unknown';
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
type,
|
|
556
|
+
parts,
|
|
557
|
+
metadata,
|
|
558
|
+
componentType,
|
|
559
|
+
title,
|
|
560
|
+
iconName,
|
|
561
|
+
thinkingData
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Content part renderer that handles different types of v0 API content parts
|
|
566
|
+
*
|
|
567
|
+
* For headless usage, use the useContentPart hook instead.
|
|
568
|
+
*/ function ContentPartRenderer({ part, iconRenderer, thinkingSectionRenderer, taskSectionRenderer, brainIcon, chevronRightIcon, chevronDownIcon, searchIcon, folderIcon, settingsIcon, wrenchIcon }) {
|
|
569
|
+
const contentData = useContentPart(part);
|
|
570
|
+
if (!contentData.componentType) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
if (contentData.componentType === 'thinking') {
|
|
574
|
+
const ThinkingComponent = thinkingSectionRenderer || ThinkingSection;
|
|
575
|
+
const [collapsed, setCollapsed] = React.useState(true);
|
|
576
|
+
return React__default.default.createElement(ThinkingComponent, {
|
|
577
|
+
title: contentData.title,
|
|
578
|
+
duration: contentData.thinkingData?.duration,
|
|
579
|
+
thought: contentData.thinkingData?.thought,
|
|
580
|
+
collapsed,
|
|
581
|
+
onCollapse: ()=>setCollapsed(!collapsed),
|
|
582
|
+
brainIcon,
|
|
583
|
+
chevronRightIcon,
|
|
584
|
+
chevronDownIcon
|
|
585
|
+
});
|
|
575
586
|
}
|
|
587
|
+
if (contentData.componentType === 'task') {
|
|
588
|
+
const TaskComponent = taskSectionRenderer || TaskSection;
|
|
589
|
+
const [collapsed, setCollapsed] = React.useState(true);
|
|
590
|
+
// Map icon names to icon components
|
|
591
|
+
let taskIcon;
|
|
592
|
+
switch(contentData.iconName){
|
|
593
|
+
case 'search':
|
|
594
|
+
taskIcon = searchIcon;
|
|
595
|
+
break;
|
|
596
|
+
case 'folder':
|
|
597
|
+
taskIcon = folderIcon;
|
|
598
|
+
break;
|
|
599
|
+
case 'settings':
|
|
600
|
+
taskIcon = settingsIcon;
|
|
601
|
+
break;
|
|
602
|
+
case 'wrench':
|
|
603
|
+
taskIcon = wrenchIcon;
|
|
604
|
+
break;
|
|
605
|
+
default:
|
|
606
|
+
taskIcon = undefined;
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
return React__default.default.createElement(TaskComponent, {
|
|
610
|
+
title: contentData.title,
|
|
611
|
+
type: contentData.type,
|
|
612
|
+
parts: contentData.parts,
|
|
613
|
+
collapsed,
|
|
614
|
+
onCollapse: ()=>setCollapsed(!collapsed),
|
|
615
|
+
taskIcon,
|
|
616
|
+
chevronRightIcon,
|
|
617
|
+
chevronDownIcon
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
if (contentData.componentType === 'unknown') {
|
|
621
|
+
return React__default.default.createElement('div', {
|
|
622
|
+
'data-unknown-part-type': contentData.type
|
|
623
|
+
}, `Unknown part type: ${contentData.type}`);
|
|
624
|
+
}
|
|
625
|
+
return null;
|
|
576
626
|
}
|
|
577
627
|
|
|
578
628
|
// Utility function to merge class names
|
|
@@ -580,11 +630,17 @@ function cn(...classes) {
|
|
|
580
630
|
return classes.filter(Boolean).join(' ');
|
|
581
631
|
}
|
|
582
632
|
|
|
583
|
-
//
|
|
584
|
-
function
|
|
633
|
+
// Headless hook for processing message content
|
|
634
|
+
function useMessage({ content, messageId = 'unknown', role = 'assistant', streaming = false, isLastMessage = false, components, renderers }) {
|
|
585
635
|
if (!Array.isArray(content)) {
|
|
586
636
|
console.warn('MessageContent: content must be an array (MessageBinaryFormat)');
|
|
587
|
-
return
|
|
637
|
+
return {
|
|
638
|
+
elements: [],
|
|
639
|
+
messageId,
|
|
640
|
+
role,
|
|
641
|
+
streaming,
|
|
642
|
+
isLastMessage
|
|
643
|
+
};
|
|
588
644
|
}
|
|
589
645
|
// Merge components and renderers (backward compatibility)
|
|
590
646
|
const mergedComponents = {
|
|
@@ -608,10 +664,7 @@ function MessageImpl({ content, messageId = 'unknown', role: _role = 'assistant'
|
|
|
608
664
|
const key = `${messageId}-${index}`;
|
|
609
665
|
// Markdown data (type 0) - this is the main content
|
|
610
666
|
if (type === 0) {
|
|
611
|
-
return
|
|
612
|
-
data: data,
|
|
613
|
-
components: mergedComponents
|
|
614
|
-
}, key);
|
|
667
|
+
return processElements(data, key, mergedComponents);
|
|
615
668
|
}
|
|
616
669
|
// Metadata (type 1) - extract context but don't render
|
|
617
670
|
if (type === 1) {
|
|
@@ -621,33 +674,40 @@ function MessageImpl({ content, messageId = 'unknown', role: _role = 'assistant'
|
|
|
621
674
|
}
|
|
622
675
|
// Other types - v0 doesn't handle these in the main renderer
|
|
623
676
|
return null;
|
|
624
|
-
});
|
|
625
|
-
return
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
677
|
+
}).filter(Boolean);
|
|
678
|
+
return {
|
|
679
|
+
elements,
|
|
680
|
+
messageId,
|
|
681
|
+
role,
|
|
682
|
+
streaming,
|
|
683
|
+
isLastMessage
|
|
684
|
+
};
|
|
629
685
|
}
|
|
630
|
-
//
|
|
631
|
-
function
|
|
686
|
+
// Process elements into headless data structure
|
|
687
|
+
function processElements(data, keyPrefix, components) {
|
|
632
688
|
// Handle case where data might not be an array due to streaming/patching
|
|
633
689
|
if (!Array.isArray(data)) {
|
|
634
690
|
return null;
|
|
635
691
|
}
|
|
636
|
-
const
|
|
637
|
-
const key =
|
|
638
|
-
return
|
|
639
|
-
}).filter(Boolean)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
692
|
+
const children = data.map((item, index)=>{
|
|
693
|
+
const key = `${keyPrefix}-${index}`;
|
|
694
|
+
return processElement(item, key, components);
|
|
695
|
+
}).filter(Boolean);
|
|
696
|
+
return {
|
|
697
|
+
type: 'component',
|
|
698
|
+
key: keyPrefix,
|
|
699
|
+
data: 'elements',
|
|
700
|
+
children
|
|
701
|
+
};
|
|
644
702
|
}
|
|
645
|
-
//
|
|
646
|
-
function
|
|
703
|
+
// Process individual elements into headless data structure
|
|
704
|
+
function processElement(element, key, components) {
|
|
647
705
|
if (typeof element === 'string') {
|
|
648
|
-
return
|
|
649
|
-
|
|
650
|
-
|
|
706
|
+
return {
|
|
707
|
+
type: 'text',
|
|
708
|
+
key,
|
|
709
|
+
data: element
|
|
710
|
+
};
|
|
651
711
|
}
|
|
652
712
|
if (!Array.isArray(element)) {
|
|
653
713
|
return null;
|
|
@@ -658,69 +718,144 @@ function renderElement(element, key, components) {
|
|
|
658
718
|
}
|
|
659
719
|
// Handle special v0 Platform API elements
|
|
660
720
|
if (tagName === 'AssistantMessageContentPart') {
|
|
661
|
-
return
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
721
|
+
return {
|
|
722
|
+
type: 'content-part',
|
|
723
|
+
key,
|
|
724
|
+
data: {
|
|
725
|
+
part: props.part,
|
|
726
|
+
iconRenderer: components?.Icon,
|
|
727
|
+
thinkingSectionRenderer: components?.ThinkingSection,
|
|
728
|
+
taskSectionRenderer: components?.TaskSection
|
|
729
|
+
}
|
|
730
|
+
};
|
|
667
731
|
}
|
|
668
732
|
if (tagName === 'Codeblock') {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
733
|
+
return {
|
|
734
|
+
type: 'code-project',
|
|
735
|
+
key,
|
|
736
|
+
data: {
|
|
737
|
+
language: props.lang,
|
|
738
|
+
code: children[0],
|
|
739
|
+
iconRenderer: components?.Icon,
|
|
740
|
+
customRenderer: components?.CodeProjectPart
|
|
741
|
+
}
|
|
742
|
+
};
|
|
676
743
|
}
|
|
677
744
|
if (tagName === 'text') {
|
|
678
|
-
return
|
|
679
|
-
|
|
680
|
-
|
|
745
|
+
return {
|
|
746
|
+
type: 'text',
|
|
747
|
+
key,
|
|
748
|
+
data: children[0] || ''
|
|
749
|
+
};
|
|
681
750
|
}
|
|
682
|
-
//
|
|
683
|
-
const
|
|
751
|
+
// Process children
|
|
752
|
+
const processedChildren = children.map((child, childIndex)=>{
|
|
684
753
|
const childKey = `${key}-child-${childIndex}`;
|
|
685
|
-
return
|
|
754
|
+
return processElement(child, childKey, components);
|
|
686
755
|
}).filter(Boolean);
|
|
687
756
|
// Handle standard HTML elements
|
|
688
|
-
const className = props?.className;
|
|
689
757
|
const componentOrConfig = components?.[tagName];
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
758
|
+
return {
|
|
759
|
+
type: 'html',
|
|
760
|
+
key,
|
|
761
|
+
data: {
|
|
762
|
+
tagName,
|
|
763
|
+
props,
|
|
764
|
+
componentOrConfig
|
|
765
|
+
},
|
|
766
|
+
children: processedChildren
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
// Default JSX renderer for backward compatibility
|
|
770
|
+
function MessageRenderer({ messageData, className }) {
|
|
771
|
+
const renderElement = (element)=>{
|
|
772
|
+
switch(element.type){
|
|
773
|
+
case 'text':
|
|
774
|
+
return React__default.default.createElement('span', {
|
|
775
|
+
key: element.key
|
|
776
|
+
}, element.data);
|
|
777
|
+
case 'content-part':
|
|
778
|
+
return React__default.default.createElement(ContentPartRenderer, {
|
|
779
|
+
key: element.key,
|
|
780
|
+
part: element.data.part,
|
|
781
|
+
iconRenderer: element.data.iconRenderer,
|
|
782
|
+
thinkingSectionRenderer: element.data.thinkingSectionRenderer,
|
|
783
|
+
taskSectionRenderer: element.data.taskSectionRenderer
|
|
784
|
+
});
|
|
785
|
+
case 'code-project':
|
|
786
|
+
const CustomCodeProjectPart = element.data.customRenderer;
|
|
787
|
+
const CodeProjectComponent = CustomCodeProjectPart || CodeProjectPart;
|
|
788
|
+
return React__default.default.createElement(CodeProjectComponent, {
|
|
789
|
+
key: element.key,
|
|
790
|
+
language: element.data.language,
|
|
791
|
+
code: element.data.code,
|
|
792
|
+
iconRenderer: element.data.iconRenderer
|
|
793
|
+
});
|
|
794
|
+
case 'html':
|
|
795
|
+
const { tagName, props, componentOrConfig } = element.data;
|
|
796
|
+
const renderedChildren = element.children?.map(renderElement);
|
|
797
|
+
if (typeof componentOrConfig === 'function') {
|
|
798
|
+
const Component = componentOrConfig;
|
|
799
|
+
return React__default.default.createElement(Component, {
|
|
800
|
+
key: element.key,
|
|
801
|
+
...props,
|
|
802
|
+
className: props?.className
|
|
803
|
+
}, renderedChildren);
|
|
804
|
+
} else if (componentOrConfig && typeof componentOrConfig === 'object') {
|
|
805
|
+
const mergedClassName = cn(props?.className, componentOrConfig.className);
|
|
806
|
+
return React__default.default.createElement(tagName, {
|
|
807
|
+
key: element.key,
|
|
808
|
+
...props,
|
|
809
|
+
className: mergedClassName
|
|
810
|
+
}, renderedChildren);
|
|
811
|
+
} else {
|
|
812
|
+
// Default HTML element rendering
|
|
813
|
+
const elementProps = {
|
|
814
|
+
key: element.key,
|
|
815
|
+
...props
|
|
816
|
+
};
|
|
817
|
+
if (props?.className) {
|
|
818
|
+
elementProps.className = props.className;
|
|
819
|
+
}
|
|
820
|
+
// Special handling for links
|
|
821
|
+
if (tagName === 'a') {
|
|
822
|
+
elementProps.target = '_blank';
|
|
823
|
+
elementProps.rel = 'noopener noreferrer';
|
|
824
|
+
}
|
|
825
|
+
return React__default.default.createElement(tagName, elementProps, renderedChildren);
|
|
826
|
+
}
|
|
827
|
+
case 'component':
|
|
828
|
+
return React__default.default.createElement(React__default.default.Fragment, {
|
|
829
|
+
key: element.key
|
|
830
|
+
}, element.children?.map(renderElement));
|
|
831
|
+
default:
|
|
832
|
+
return null;
|
|
717
833
|
}
|
|
718
|
-
|
|
719
|
-
|
|
834
|
+
};
|
|
835
|
+
return React__default.default.createElement('div', {
|
|
836
|
+
className
|
|
837
|
+
}, messageData.elements.map(renderElement));
|
|
838
|
+
}
|
|
839
|
+
// Simplified renderer that matches v0's exact approach (backward compatibility)
|
|
840
|
+
function MessageImpl({ content, messageId = 'unknown', role = 'assistant', streaming = false, isLastMessage = false, className, components, renderers }) {
|
|
841
|
+
const messageData = useMessage({
|
|
842
|
+
content,
|
|
843
|
+
messageId,
|
|
844
|
+
role,
|
|
845
|
+
streaming,
|
|
846
|
+
isLastMessage,
|
|
847
|
+
components,
|
|
848
|
+
renderers
|
|
849
|
+
});
|
|
850
|
+
return React__default.default.createElement(MessageRenderer, {
|
|
851
|
+
messageData,
|
|
852
|
+
className
|
|
853
|
+
});
|
|
720
854
|
}
|
|
721
855
|
/**
|
|
722
856
|
* Main component for rendering v0 Platform API message content
|
|
723
|
-
|
|
857
|
+
* This is a backward-compatible JSX renderer. For headless usage, use the useMessage hook.
|
|
858
|
+
*/ const Message = React__default.default.memo(MessageImpl);
|
|
724
859
|
|
|
725
860
|
const jdf = jsondiffpatch__namespace.create({});
|
|
726
861
|
// Exact copy of the patch function from v0/chat/lib/diffpatch.ts
|
|
@@ -780,7 +915,6 @@ class StreamStateManager {
|
|
|
780
915
|
this.processStream = async (stream, options = {})=>{
|
|
781
916
|
// Prevent processing the same stream multiple times
|
|
782
917
|
if (this.processedStreams.has(stream)) {
|
|
783
|
-
console.log('Stream already processed, skipping');
|
|
784
918
|
return;
|
|
785
919
|
}
|
|
786
920
|
// Handle locked streams gracefully
|
|
@@ -835,13 +969,11 @@ class StreamStateManager {
|
|
|
835
969
|
while(true){
|
|
836
970
|
const { done, value } = await reader.read();
|
|
837
971
|
if (done) {
|
|
838
|
-
console.log('Stream reading completed');
|
|
839
972
|
break;
|
|
840
973
|
}
|
|
841
974
|
const chunk = decoder.decode(value, {
|
|
842
975
|
stream: true
|
|
843
976
|
});
|
|
844
|
-
console.log('Received raw chunk:', chunk);
|
|
845
977
|
buffer += chunk;
|
|
846
978
|
const lines = buffer.split('\n');
|
|
847
979
|
buffer = lines.pop() || '';
|
|
@@ -849,13 +981,11 @@ class StreamStateManager {
|
|
|
849
981
|
if (line.trim() === '') {
|
|
850
982
|
continue;
|
|
851
983
|
}
|
|
852
|
-
console.log('Processing line:', line);
|
|
853
984
|
// Handle SSE format (data: ...)
|
|
854
985
|
let jsonData;
|
|
855
986
|
if (line.startsWith('data: ')) {
|
|
856
987
|
jsonData = line.slice(6); // Remove "data: " prefix
|
|
857
988
|
if (jsonData === '[DONE]') {
|
|
858
|
-
console.log('Stream marked as done via SSE');
|
|
859
989
|
this.setComplete(true);
|
|
860
990
|
options.onComplete?.(currentContent);
|
|
861
991
|
return;
|
|
@@ -867,28 +997,21 @@ class StreamStateManager {
|
|
|
867
997
|
try {
|
|
868
998
|
// Parse the JSON data
|
|
869
999
|
const parsedData = JSON.parse(jsonData);
|
|
870
|
-
console.log('Parsed data:', JSON.stringify(parsedData, null, 2));
|
|
871
1000
|
// Handle v0 streaming format
|
|
872
1001
|
if (parsedData.type === 'connected') {
|
|
873
|
-
console.log('Stream connected');
|
|
874
1002
|
continue;
|
|
875
1003
|
} else if (parsedData.type === 'done') {
|
|
876
|
-
console.log('Stream marked as done');
|
|
877
1004
|
this.setComplete(true);
|
|
878
1005
|
options.onComplete?.(currentContent);
|
|
879
1006
|
return;
|
|
880
|
-
} else if (parsedData.object
|
|
881
|
-
// Handle
|
|
882
|
-
console.log('Received chat data:', parsedData.id);
|
|
1007
|
+
} else if (parsedData.object && parsedData.object.startsWith('chat')) {
|
|
1008
|
+
// Handle chat metadata messages (chat, chat.title, chat.name, etc.)
|
|
883
1009
|
options.onChatData?.(parsedData);
|
|
884
1010
|
continue;
|
|
885
1011
|
} else if (parsedData.delta) {
|
|
886
1012
|
// Apply the delta using jsondiffpatch
|
|
887
|
-
console.log('Applying delta to content:', JSON.stringify(currentContent, null, 2));
|
|
888
|
-
console.log('Delta:', JSON.stringify(parsedData.delta, null, 2));
|
|
889
1013
|
const patchedContent = patch(currentContent, parsedData.delta);
|
|
890
1014
|
currentContent = Array.isArray(patchedContent) ? patchedContent : [];
|
|
891
|
-
console.log('Patched content result:', JSON.stringify(currentContent, null, 2));
|
|
892
1015
|
this.updateContent(currentContent);
|
|
893
1016
|
options.onChunk?.(currentContent);
|
|
894
1017
|
}
|
|
@@ -921,16 +1044,39 @@ class StreamStateManager {
|
|
|
921
1044
|
if (stream !== lastStreamRef.current) {
|
|
922
1045
|
lastStreamRef.current = stream;
|
|
923
1046
|
if (stream) {
|
|
924
|
-
console.log('New stream detected, starting processing');
|
|
925
1047
|
manager.processStream(stream, options);
|
|
926
1048
|
}
|
|
927
1049
|
}
|
|
928
1050
|
return state;
|
|
929
1051
|
}
|
|
930
1052
|
|
|
1053
|
+
// Headless hook for streaming message
|
|
1054
|
+
function useStreamingMessageData({ stream, messageId = 'unknown', role = 'assistant', components, renderers, onChunk, onComplete, onError, onChatData }) {
|
|
1055
|
+
const streamingState = useStreamingMessage(stream, {
|
|
1056
|
+
onChunk,
|
|
1057
|
+
onComplete,
|
|
1058
|
+
onError,
|
|
1059
|
+
onChatData
|
|
1060
|
+
});
|
|
1061
|
+
const messageData = streamingState.content.length > 0 ? useMessage({
|
|
1062
|
+
content: streamingState.content,
|
|
1063
|
+
messageId,
|
|
1064
|
+
role,
|
|
1065
|
+
streaming: streamingState.isStreaming,
|
|
1066
|
+
isLastMessage: true,
|
|
1067
|
+
components,
|
|
1068
|
+
renderers
|
|
1069
|
+
}) : null;
|
|
1070
|
+
return {
|
|
1071
|
+
...streamingState,
|
|
1072
|
+
messageData
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
931
1075
|
/**
|
|
932
1076
|
* Component for rendering streaming message content from v0 API
|
|
933
1077
|
*
|
|
1078
|
+
* For headless usage, use the useStreamingMessageData hook instead.
|
|
1079
|
+
*
|
|
934
1080
|
* @example
|
|
935
1081
|
* ```tsx
|
|
936
1082
|
* import { v0 } from 'v0-sdk'
|
|
@@ -955,81 +1101,106 @@ class StreamStateManager {
|
|
|
955
1101
|
* stream={stream}
|
|
956
1102
|
* messageId="demo-message"
|
|
957
1103
|
* role="assistant"
|
|
958
|
-
* onComplete={(content) =>
|
|
959
|
-
* onChatData={(chatData) =>
|
|
1104
|
+
* onComplete={(content) => handleCompletion(content)}
|
|
1105
|
+
* onChatData={(chatData) => handleChatData(chatData)}
|
|
960
1106
|
* />
|
|
961
1107
|
* )}
|
|
962
1108
|
* </div>
|
|
963
1109
|
* )
|
|
964
1110
|
* }
|
|
965
1111
|
* ```
|
|
966
|
-
*/ function StreamingMessage({ stream, showLoadingIndicator = true, loadingComponent, errorComponent, onChunk, onComplete, onError, onChatData, ...messageProps }) {
|
|
967
|
-
const
|
|
1112
|
+
*/ function StreamingMessage({ stream, showLoadingIndicator = true, loadingComponent, errorComponent, onChunk, onComplete, onError, onChatData, className, ...messageProps }) {
|
|
1113
|
+
const streamingData = useStreamingMessageData({
|
|
1114
|
+
stream,
|
|
968
1115
|
onChunk,
|
|
969
1116
|
onComplete,
|
|
970
1117
|
onError,
|
|
971
|
-
onChatData
|
|
1118
|
+
onChatData,
|
|
1119
|
+
...messageProps
|
|
972
1120
|
});
|
|
973
1121
|
// Handle error state
|
|
974
|
-
if (error) {
|
|
1122
|
+
if (streamingData.error) {
|
|
975
1123
|
if (errorComponent) {
|
|
976
|
-
return
|
|
977
|
-
children: errorComponent(error)
|
|
978
|
-
});
|
|
1124
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, errorComponent(streamingData.error));
|
|
979
1125
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1126
|
+
// Fallback error component using React.createElement for compatibility
|
|
1127
|
+
return React__default.default.createElement('div', {
|
|
1128
|
+
className: 'text-red-500 p-4 border border-red-200 rounded',
|
|
1129
|
+
style: {
|
|
1130
|
+
color: 'red',
|
|
1131
|
+
padding: '1rem',
|
|
1132
|
+
border: '1px solid #fecaca',
|
|
1133
|
+
borderRadius: '0.375rem'
|
|
1134
|
+
}
|
|
1135
|
+
}, `Error: ${streamingData.error}`);
|
|
987
1136
|
}
|
|
988
1137
|
// Handle loading state
|
|
989
|
-
if (showLoadingIndicator && isStreaming && content.length === 0) {
|
|
1138
|
+
if (showLoadingIndicator && streamingData.isStreaming && streamingData.content.length === 0) {
|
|
990
1139
|
if (loadingComponent) {
|
|
991
|
-
return
|
|
992
|
-
children: loadingComponent
|
|
993
|
-
});
|
|
1140
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, loadingComponent);
|
|
994
1141
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1142
|
+
// Fallback loading component using React.createElement for compatibility
|
|
1143
|
+
return React__default.default.createElement('div', {
|
|
1144
|
+
className: 'flex items-center space-x-2 text-gray-500',
|
|
1145
|
+
style: {
|
|
1146
|
+
display: 'flex',
|
|
1147
|
+
alignItems: 'center',
|
|
1148
|
+
gap: '0.5rem',
|
|
1149
|
+
color: '#6b7280'
|
|
1150
|
+
}
|
|
1151
|
+
}, React__default.default.createElement('div', {
|
|
1152
|
+
className: 'animate-spin h-4 w-4 border-2 border-gray-300 border-t-gray-600 rounded-full',
|
|
1153
|
+
style: {
|
|
1154
|
+
animation: 'spin 1s linear infinite',
|
|
1155
|
+
height: '1rem',
|
|
1156
|
+
width: '1rem',
|
|
1157
|
+
border: '2px solid #d1d5db',
|
|
1158
|
+
borderTopColor: '#4b5563',
|
|
1159
|
+
borderRadius: '50%'
|
|
1160
|
+
}
|
|
1161
|
+
}), React__default.default.createElement('span', {}, 'Loading...'));
|
|
1006
1162
|
}
|
|
1007
1163
|
// Render the message content
|
|
1008
|
-
return
|
|
1164
|
+
return React__default.default.createElement(Message, {
|
|
1009
1165
|
...messageProps,
|
|
1010
|
-
content: content,
|
|
1011
|
-
streaming: isStreaming,
|
|
1012
|
-
isLastMessage: true
|
|
1166
|
+
content: streamingData.content,
|
|
1167
|
+
streaming: streamingData.isStreaming,
|
|
1168
|
+
isLastMessage: true,
|
|
1169
|
+
className
|
|
1013
1170
|
});
|
|
1014
1171
|
}
|
|
1015
1172
|
|
|
1173
|
+
// Headless hook for math data
|
|
1174
|
+
function useMath(props) {
|
|
1175
|
+
return {
|
|
1176
|
+
content: props.content,
|
|
1177
|
+
inline: props.inline ?? false,
|
|
1178
|
+
displayMode: props.displayMode ?? !props.inline,
|
|
1179
|
+
processedContent: props.content
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1016
1182
|
/**
|
|
1017
1183
|
* Generic math renderer component
|
|
1018
1184
|
* Renders plain math content by default - consumers should provide their own math rendering
|
|
1019
|
-
|
|
1185
|
+
*
|
|
1186
|
+
* For headless usage, use the useMath hook instead.
|
|
1187
|
+
*/ function MathPart({ content, inline = false, className = '', children, displayMode }) {
|
|
1020
1188
|
// If children provided, use that (allows complete customization)
|
|
1021
1189
|
if (children) {
|
|
1022
|
-
return
|
|
1023
|
-
children: children
|
|
1024
|
-
});
|
|
1190
|
+
return React__default.default.createElement(React__default.default.Fragment, {}, children);
|
|
1025
1191
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
"data-math-inline": inline,
|
|
1031
|
-
children: content
|
|
1192
|
+
const mathData = useMath({
|
|
1193
|
+
content,
|
|
1194
|
+
inline,
|
|
1195
|
+
displayMode
|
|
1032
1196
|
});
|
|
1197
|
+
// Simple fallback - just render plain math content
|
|
1198
|
+
// Uses React.createElement for maximum compatibility across environments
|
|
1199
|
+
return React__default.default.createElement(mathData.inline ? 'span' : 'div', {
|
|
1200
|
+
className,
|
|
1201
|
+
'data-math-inline': mathData.inline,
|
|
1202
|
+
'data-math-display': mathData.displayMode
|
|
1203
|
+
}, mathData.processedContent);
|
|
1033
1204
|
}
|
|
1034
1205
|
|
|
1035
1206
|
exports.AssistantMessageContentPart = ContentPartRenderer;
|
|
@@ -1038,6 +1209,7 @@ exports.CodeProjectBlock = CodeProjectPart;
|
|
|
1038
1209
|
exports.CodeProjectPart = CodeProjectPart;
|
|
1039
1210
|
exports.ContentPartRenderer = ContentPartRenderer;
|
|
1040
1211
|
exports.Icon = Icon;
|
|
1212
|
+
exports.IconProvider = IconProvider;
|
|
1041
1213
|
exports.MathPart = MathPart;
|
|
1042
1214
|
exports.MathRenderer = MathPart;
|
|
1043
1215
|
exports.Message = Message;
|
|
@@ -1047,4 +1219,13 @@ exports.StreamingMessage = StreamingMessage;
|
|
|
1047
1219
|
exports.TaskSection = TaskSection;
|
|
1048
1220
|
exports.ThinkingSection = ThinkingSection;
|
|
1049
1221
|
exports.V0MessageRenderer = Message;
|
|
1222
|
+
exports.useCodeBlock = useCodeBlock;
|
|
1223
|
+
exports.useCodeProject = useCodeProject;
|
|
1224
|
+
exports.useContentPart = useContentPart;
|
|
1225
|
+
exports.useIcon = useIcon;
|
|
1226
|
+
exports.useMath = useMath;
|
|
1227
|
+
exports.useMessage = useMessage;
|
|
1050
1228
|
exports.useStreamingMessage = useStreamingMessage;
|
|
1229
|
+
exports.useStreamingMessageData = useStreamingMessageData;
|
|
1230
|
+
exports.useTaskSection = useTaskSection;
|
|
1231
|
+
exports.useThinkingSection = useThinkingSection;
|