@v0-sdk/react 0.5.1 → 3.0.0-canary.9ae7b76

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.cjs CHANGED
@@ -1,1253 +1,3 @@
1
- Object.defineProperty(exports, '__esModule', { value: true });
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./request-C1Ues3qu.cjs`);require(`./swr.cjs`);let t=require(`v0/browser`);function n(e){let t=[];for(let n of e.attachments??[])t.push({type:`file`,url:n.url,mediaType:n.contentType||`application/octet-stream`,...n.name?{filename:n.name}:{}});return e.parts.forEach((n,r)=>{switch(n.type){case`text`:t.push({type:`text`,text:n.text,state:n.finishedAt||e.finishReason!==null?`done`:`streaming`});break;case`thinking`:t.push({type:`reasoning`,text:n.text,state:n.finishedAt||e.finishReason!==null?`done`:`streaming`});break;default:{let{type:i,...s}=n;t.push({type:`data-v0-${i}`,id:o(e.id,r),data:a(s)})}}}),{id:e.id,role:e.role,metadata:i(e),parts:t}}function r(e,t={}){return(t.newestFirst===!1?e:[...e].reverse()).map(n)}function i(e){let{role:t,content:n,parts:r,...i}=e;return a(i)}function a(e){return e instanceof Date?e.toISOString():Array.isArray(e)?e.map(e=>a(e)):typeof e==`object`&&e?Object.fromEntries(Object.entries(e).map(([e,t])=>[e,a(t)])):e}function o(e,t){return`${e}:part:${t}`}var s=class{previous;metadata;metadataJson;started=!1;finished=!1;activeText=new Map;textPartAttempts=new Map;constructor(e){this.previous=e,this.metadata=e?i(e):void 0}push(e,t=e.status===`done`){let n=l(e,this.previous);if(!n||this.finished)return[];let r=[];this.metadata=u(e,n,this.metadata);let i=JSON.stringify(this.metadata),a=i!==this.metadataJson;if(this.started?a&&(r.push({type:`message-metadata`,messageMetadata:this.metadata}),this.metadataJson=i):(r.push({type:`start`,messageId:n.id,messageMetadata:this.metadata}),this.started=!0,this.metadataJson=i),n.parts.forEach((e,t)=>{let i=this.previous?.parts[t];switch(e.type){case`text`:case`thinking`:r.push(...this.updateTextPart(n,e,i,t));break;default:{let a=d(e);(i?.type!==e.type||JSON.stringify(a)!==JSON.stringify(d(i)))&&r.push({type:`data-v0-${e.type}`,id:o(n.id,t),data:a})}}}),this.previous=n,t){for(let{id:e,type:t}of this.activeText.values())r.push({type:t===`text`?`text-end`:`reasoning-end`,id:e});this.activeText.clear(),r.push({type:`finish`,...n.finishReason?{finishReason:n.finishReason}:{},messageMetadata:this.metadata}),this.finished=!0}return r}updateTextPart(e,t,n,r){let i=[],a=n?.type===t.type?n.text:``,s=t.text.startsWith(a)?t.text.slice(a.length):``,c=this.activeText.get(r);if(c&&c.type!==t.type&&(i.push({type:c.type===`text`?`text-end`:`reasoning-end`,id:c.id}),this.activeText.delete(r),c=void 0),(!n||s)&&!c){let a=this.textPartAttempts.get(r)??0,s=a===0?n?.type===t.type?`:continuation`:``:`:continuation:${a}`,l=`${o(e.id,r)}${s}`;this.textPartAttempts.set(r,a+1),c={id:l,type:t.type},this.activeText.set(r,c),i.push({type:t.type===`text`?`text-start`:`reasoning-start`,id:l})}return s&&c&&i.push({type:t.type===`text`?`text-delta`:`reasoning-delta`,id:c.id,delta:s}),c&&t.finishedAt&&(i.push({type:t.type===`text`?`text-end`:`reasoning-end`,id:c.id}),this.activeText.delete(r)),i}};function c(e,t={}){let n=new s(t.seed),r,i=!1,a=!1,o=()=>{a||(a=!0,t.onClose?.())};return new ReadableStream({async start(a){try{for(r=e.stream[Symbol.asyncIterator]();!i;){let e=await r.next();if(e.done)break;t.onUpdate?.(e.value);for(let t of n.push(e.value))a.enqueue(t)}if(!i){let r=await e.final;t.onUpdate?.(r);for(let e of n.push(r,!0))a.enqueue(e);a.close()}}catch(e){i||a.error(e)}finally{o()}},async cancel(e){i=!0,t.abort?.(e),await r?.return?.(),o()}})}function l(e,t){if(e.message)return{id:e.message.id,chatId:e.message.chatId,parts:e.parts,finishReason:e.message.finishReason};if(!t&&e.chat&&(e.event.object===`message.parts.chunk`||e.event.object===`message.usage`))return f(e);if(t&&e.status===`done`||t&&(e.event.object===`message.parts.chunk`||e.event.object===`message.usage`))return{...t,parts:e.parts}}function u(e,t,n){return e.message?{...i(e.message),...e.usage?{usage:e.usage}:{}}:{...n,id:t.id,chatId:e.chat?.id??n?.chatId??t.chatId,...e.usage?{usage:e.usage}:{}}}function d(e){if(!e)return;let{type:t,...n}=e;return a(n)}function f(e){return{id:e.event.id,chatId:e.chat.id,parts:e.parts,finishReason:null}}function p(e){let t=e[0];return t?.role===`assistant`&&t.finishReason===null}function m(e){return p(e)?e[0]:void 0}function h(e,t){let n=new Set(e.map(e=>e.id));return[...r(t).filter(e=>!n.has(e.id)),...e]}const g={id:`ai-sdk.stream`,method:`POST`,response:`stream`};var _=class{currentChatId;reconnecting=!1;seed;options;constructor(e){this.options=e,this.currentChatId=e.chatId??e.messages?.[0]?.chatId,this.seed=m(e.messages??[])}get chatId(){return this.currentChatId}async sendMessages(n){if(n.trigger===`regenerate-message`)throw Error(`V0Transport does not support regeneration because the v0 API has no regeneration operation`);let r=[...n.messages].reverse().find(e=>e.role===`user`);if(!r)throw Error(`V0Transport requires a user message to submit`);let i=r.parts.filter(e=>e.type===`text`).map(e=>e.text).join(`
2
2
 
3
- var React = require('react');
4
- var jsondiffpatch = require('jsondiffpatch');
5
-
6
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
-
8
- function _interopNamespace(e) {
9
- if (e && e.__esModule) return e;
10
- var n = Object.create(null);
11
- if (e) {
12
- Object.keys(e).forEach(function (k) {
13
- if (k !== 'default') {
14
- var d = Object.getOwnPropertyDescriptor(e, k);
15
- Object.defineProperty(n, k, d.get ? d : {
16
- enumerable: true,
17
- get: function () { return e[k]; }
18
- });
19
- }
20
- });
21
- }
22
- n.default = e;
23
- return n;
24
- }
25
-
26
- var React__default = /*#__PURE__*/_interopDefault(React);
27
- var jsondiffpatch__namespace = /*#__PURE__*/_interopNamespace(jsondiffpatch);
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
- }
40
- /**
41
- * Generic code block component
42
- * Renders plain code by default - consumers should provide their own styling and highlighting
43
- *
44
- * For headless usage, use the useCodeBlock hook instead.
45
- */ function CodeBlock({ language, code, className = '', children, filename }) {
46
- // If children provided, use that (allows complete customization)
47
- if (children) {
48
- return React__default.default.createElement(React__default.default.Fragment, {}, children);
49
- }
50
- const codeBlockData = useCodeBlock({
51
- language,
52
- code,
53
- filename
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));
64
- }
65
-
66
- // Context for providing custom icon implementation
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
- }
76
- /**
77
- * Generic icon component that can be customized by consumers.
78
- * By default, renders a simple fallback. Consumers should provide
79
- * their own icon implementation via context or props.
80
- *
81
- * For headless usage, use the useIcon hook instead.
82
- */ function Icon(props) {
83
- const CustomIcon = React.useContext(IconContext);
84
- // Use custom icon implementation if provided via context
85
- if (CustomIcon) {
86
- return React__default.default.createElement(CustomIcon, props);
87
- }
88
- const iconData = useIcon(props);
89
- // Fallback implementation - consumers should override this
90
- // This uses minimal DOM-specific attributes for maximum compatibility
91
- return React__default.default.createElement('span', {
92
- className: props.className,
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);
103
- }
104
- function getIconFallback(name) {
105
- const iconMap = {
106
- 'chevron-right': '▶',
107
- 'chevron-down': '▼',
108
- search: '🔍',
109
- folder: '📁',
110
- settings: '⚙️',
111
- 'file-text': '📄',
112
- brain: '🧠',
113
- wrench: '🔧'
114
- };
115
- return iconMap[name] || '•';
116
- }
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
- }
152
- /**
153
- * Generic code project block component
154
- * Renders a collapsible code project with basic structure - consumers provide styling
155
- *
156
- * For headless usage, use the useCodeProject hook instead.
157
- */ function CodeProjectPart({ title, filename, code, language = 'typescript', collapsed: initialCollapsed = true, className, children, iconRenderer }) {
158
- const { data, collapsed, toggleCollapsed } = useCodeProject({
159
- title,
160
- filename,
161
- code,
162
- language,
163
- collapsed: initialCollapsed
164
- });
165
- // If children provided, use that (allows complete customization)
166
- if (children) {
167
- return React__default.default.createElement(React__default.default.Fragment, {}, children);
168
- }
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);
208
- }
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
- }
230
- /**
231
- * Generic thinking section component
232
- * Renders a collapsible section with basic structure - consumers provide styling
233
- *
234
- * For headless usage, use the useThinkingSection hook instead.
235
- */ function ThinkingSection({ title, duration, thought, collapsed: initialCollapsed = true, onCollapse, className, children, iconRenderer, brainIcon, chevronRightIcon, chevronDownIcon }) {
236
- const { data, collapsed, handleCollapse } = useThinkingSection({
237
- title,
238
- duration,
239
- thought,
240
- collapsed: initialCollapsed,
241
- onCollapse
242
- });
243
- // If children provided, use that (allows complete customization)
244
- if (children) {
245
- return React__default.default.createElement(React__default.default.Fragment, {}, children);
246
- }
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);
279
- }
280
-
281
- function getTypeIcon(type, title) {
282
- // Check title content for specific cases
283
- if (title?.includes('No issues found')) {
284
- return 'wrench';
285
- }
286
- if (title?.includes('Analyzed codebase')) {
287
- return 'search';
288
- }
289
- // Fallback to type-based icons
290
- switch(type){
291
- case 'task-search-web-v1':
292
- return 'search';
293
- case 'task-search-repo-v1':
294
- return 'folder';
295
- case 'task-diagnostics-v1':
296
- return 'settings';
297
- case 'task-generate-design-inspiration-v1':
298
- return 'wrench';
299
- case 'task-read-file-v1':
300
- return 'folder';
301
- case 'task-coding-v1':
302
- return 'wrench';
303
- default:
304
- return 'wrench';
305
- }
306
- }
307
- function processTaskPart(part, index) {
308
- const baseData = {
309
- type: part.type,
310
- status: part.status,
311
- content: null
312
- };
313
- if (part.type === 'search-web') {
314
- if (part.status === 'searching') {
315
- return {
316
- ...baseData,
317
- isSearching: true,
318
- query: part.query,
319
- content: `Searching "${part.query}"`
320
- };
321
- }
322
- if (part.status === 'analyzing') {
323
- return {
324
- ...baseData,
325
- isAnalyzing: true,
326
- count: part.count,
327
- content: `Analyzing ${part.count} results...`
328
- };
329
- }
330
- if (part.status === 'complete' && part.answer) {
331
- return {
332
- ...baseData,
333
- isComplete: true,
334
- answer: part.answer,
335
- sources: part.sources,
336
- content: part.answer
337
- };
338
- }
339
- }
340
- if (part.type === 'search-repo') {
341
- if (part.status === 'searching') {
342
- return {
343
- ...baseData,
344
- isSearching: true,
345
- query: part.query,
346
- content: `Searching "${part.query}"`
347
- };
348
- }
349
- if (part.status === 'reading' && part.files) {
350
- return {
351
- ...baseData,
352
- files: part.files,
353
- content: 'Reading files'
354
- };
355
- }
356
- }
357
- if (part.type === 'diagnostics') {
358
- if (part.status === 'checking') {
359
- return {
360
- ...baseData,
361
- content: 'Checking for issues...'
362
- };
363
- }
364
- if (part.status === 'complete' && part.issues === 0) {
365
- return {
366
- ...baseData,
367
- isComplete: true,
368
- issues: part.issues,
369
- content: '✅ No issues found'
370
- };
371
- }
372
- }
373
- return {
374
- ...baseData,
375
- content: JSON.stringify(part)
376
- };
377
- }
378
- function renderTaskPartContent(partData, index, iconRenderer) {
379
- if (partData.type === 'search-web' && partData.isComplete && partData.sources) {
380
- return React__default.default.createElement('div', {
381
- key: index
382
- }, 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', {
383
- key: sourceIndex,
384
- href: source.url,
385
- target: '_blank',
386
- rel: 'noopener noreferrer'
387
- }, source.title))) : null);
388
- }
389
- if (partData.type === 'search-repo' && partData.files) {
390
- return React__default.default.createElement('div', {
391
- key: index
392
- }, React__default.default.createElement('span', {}, partData.content), partData.files.map((file, fileIndex)=>React__default.default.createElement('span', {
393
- key: fileIndex
394
- }, iconRenderer ? React__default.default.createElement(iconRenderer, {
395
- name: 'file-text'
396
- }) : React__default.default.createElement(Icon, {
397
- name: 'file-text'
398
- }), ' ', file)));
399
- }
400
- return React__default.default.createElement('div', {
401
- key: index
402
- }, partData.content);
403
- }
404
- // Headless hook for task section
405
- function useTaskSection({ title, type, parts = [], collapsed: initialCollapsed = true, onCollapse }) {
406
- const [internalCollapsed, setInternalCollapsed] = React.useState(initialCollapsed);
407
- const collapsed = onCollapse ? initialCollapsed : internalCollapsed;
408
- const handleCollapse = onCollapse || (()=>setInternalCollapsed(!internalCollapsed));
409
- // Count meaningful parts (parts that would render something)
410
- const meaningfulParts = parts.filter((part)=>{
411
- // Check if the part would render meaningful content
412
- if (part.type === 'search-web') {
413
- return part.status === 'searching' || part.status === 'analyzing' || part.status === 'complete' && part.answer;
414
- }
415
- if (part.type === 'starting-repo-search' && part.query) return true;
416
- if (part.type === 'select-files' && part.filePaths?.length > 0) return true;
417
- if (part.type === 'starting-web-search' && part.query) return true;
418
- if (part.type === 'got-results' && part.count) return true;
419
- if (part.type === 'finished-web-search' && part.answer) return true;
420
- if (part.type === 'diagnostics-passed') return true;
421
- if (part.type === 'fetching-diagnostics') return true;
422
- return false;
423
- });
424
- const processedParts = parts.map(processTaskPart);
425
- return {
426
- data: {
427
- title: title || 'Task',
428
- type,
429
- parts,
430
- collapsed,
431
- meaningfulParts,
432
- shouldShowCollapsible: meaningfulParts.length > 1,
433
- iconName: getTypeIcon(type, title)
434
- },
435
- collapsed,
436
- handleCollapse,
437
- processedParts
438
- };
439
- }
440
- /**
441
- * Generic task section component
442
- * Renders a collapsible task section with basic structure - consumers provide styling
443
- *
444
- * For headless usage, use the useTaskSection hook instead.
445
- */ function TaskSection({ title, type, parts = [], collapsed: initialCollapsed = true, onCollapse, className, children, iconRenderer, taskIcon, chevronRightIcon, chevronDownIcon }) {
446
- const { data, collapsed, handleCollapse, processedParts } = useTaskSection({
447
- title,
448
- type,
449
- parts,
450
- collapsed: initialCollapsed,
451
- onCollapse
452
- });
453
- // If children provided, use that (allows complete customization)
454
- if (children) {
455
- return React__default.default.createElement(React__default.default.Fragment, {}, children);
456
- }
457
- // If there's only one meaningful part, show just the content without the collapsible wrapper
458
- if (!data.shouldShowCollapsible && data.meaningfulParts.length === 1) {
459
- const partData = processTaskPart(data.meaningfulParts[0]);
460
- return React__default.default.createElement('div', {
461
- className,
462
- 'data-component': 'task-section-inline'
463
- }, React__default.default.createElement('div', {
464
- 'data-part': true
465
- }, renderTaskPartContent(partData, 0, iconRenderer)));
466
- }
467
- // Uses React.createElement for maximum compatibility across environments
468
- return React__default.default.createElement('div', {
469
- className,
470
- 'data-component': 'task-section'
471
- }, React__default.default.createElement('button', {
472
- onClick: handleCollapse,
473
- 'data-expanded': !collapsed,
474
- 'data-button': true
475
- }, React__default.default.createElement('div', {
476
- 'data-icon-container': true
477
- }, React__default.default.createElement('div', {
478
- 'data-task-icon': true
479
- }, taskIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
480
- name: data.iconName
481
- }) : React__default.default.createElement(Icon, {
482
- name: data.iconName
483
- }))), collapsed ? chevronRightIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
484
- name: 'chevron-right'
485
- }) : React__default.default.createElement(Icon, {
486
- name: 'chevron-right'
487
- })) : chevronDownIcon || (iconRenderer ? React__default.default.createElement(iconRenderer, {
488
- name: 'chevron-down'
489
- }) : React__default.default.createElement(Icon, {
490
- name: 'chevron-down'
491
- }))), React__default.default.createElement('span', {
492
- 'data-title': true
493
- }, data.title)), !collapsed ? React__default.default.createElement('div', {
494
- 'data-content': true
495
- }, React__default.default.createElement('div', {
496
- 'data-parts-container': true
497
- }, processedParts.map((partData, index)=>React__default.default.createElement('div', {
498
- key: index,
499
- 'data-part': true
500
- }, renderTaskPartContent(partData, index, iconRenderer))))) : null);
501
- }
502
-
503
- // Headless hook for content part
504
- function useContentPart(part) {
505
- if (!part) {
506
- return {
507
- type: '',
508
- parts: [],
509
- metadata: {},
510
- componentType: null
511
- };
512
- }
513
- const { type, parts = [], ...metadata } = part;
514
- let componentType = 'unknown';
515
- let title;
516
- let iconName;
517
- let thinkingData;
518
- switch(type){
519
- case 'task-thinking-v1':
520
- componentType = 'thinking';
521
- title = 'Thought';
522
- const thinkingPart = parts.find((p)=>p.type === 'thinking-end');
523
- thinkingData = {
524
- duration: thinkingPart?.duration,
525
- thought: thinkingPart?.thought
526
- };
527
- break;
528
- case 'task-search-web-v1':
529
- componentType = 'task';
530
- title = metadata.taskNameComplete || metadata.taskNameActive;
531
- iconName = 'search';
532
- break;
533
- case 'task-search-repo-v1':
534
- componentType = 'task';
535
- title = metadata.taskNameComplete || metadata.taskNameActive;
536
- iconName = 'folder';
537
- break;
538
- case 'task-diagnostics-v1':
539
- componentType = 'task';
540
- title = metadata.taskNameComplete || metadata.taskNameActive;
541
- iconName = 'settings';
542
- break;
543
- case 'task-read-file-v1':
544
- componentType = 'task';
545
- title = metadata.taskNameComplete || metadata.taskNameActive || 'Reading file';
546
- iconName = 'folder';
547
- break;
548
- case 'task-coding-v1':
549
- componentType = 'task';
550
- title = metadata.taskNameComplete || metadata.taskNameActive || 'Coding';
551
- iconName = 'wrench';
552
- break;
553
- case 'task-start-v1':
554
- componentType = null; // Usually just indicates task start - can be hidden
555
- break;
556
- case 'task-generate-design-inspiration-v1':
557
- componentType = 'task';
558
- title = metadata.taskNameComplete || metadata.taskNameActive || 'Generating Design Inspiration';
559
- iconName = 'wrench';
560
- break;
561
- // Handle any other task-*-v1 patterns that might be added in the future
562
- default:
563
- // Check if it's a task type we haven't explicitly handled yet
564
- if (type && typeof type === 'string' && type.startsWith('task-') && type.endsWith('-v1')) {
565
- componentType = 'task';
566
- // Generate a readable title from the task type
567
- const taskName = type.replace('task-', '').replace('-v1', '').split('-').map((word)=>word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
568
- title = metadata.taskNameComplete || metadata.taskNameActive || taskName;
569
- iconName = 'wrench'; // Default icon for unknown task types
570
- } else {
571
- componentType = 'unknown';
572
- }
573
- break;
574
- }
575
- return {
576
- type,
577
- parts,
578
- metadata,
579
- componentType,
580
- title,
581
- iconName,
582
- thinkingData
583
- };
584
- }
585
- /**
586
- * Content part renderer that handles different types of v0 API content parts
587
- *
588
- * For headless usage, use the useContentPart hook instead.
589
- */ function ContentPartRenderer({ part, iconRenderer, thinkingSectionRenderer, taskSectionRenderer, brainIcon, chevronRightIcon, chevronDownIcon, searchIcon, folderIcon, settingsIcon, wrenchIcon }) {
590
- const contentData = useContentPart(part);
591
- if (!contentData.componentType) {
592
- return null;
593
- }
594
- if (contentData.componentType === 'thinking') {
595
- const ThinkingComponent = thinkingSectionRenderer || ThinkingSection;
596
- const [collapsed, setCollapsed] = React.useState(true);
597
- return React__default.default.createElement(ThinkingComponent, {
598
- title: contentData.title,
599
- duration: contentData.thinkingData?.duration,
600
- thought: contentData.thinkingData?.thought,
601
- collapsed,
602
- onCollapse: ()=>setCollapsed(!collapsed),
603
- brainIcon,
604
- chevronRightIcon,
605
- chevronDownIcon
606
- });
607
- }
608
- if (contentData.componentType === 'task') {
609
- const TaskComponent = taskSectionRenderer || TaskSection;
610
- const [collapsed, setCollapsed] = React.useState(true);
611
- // Map icon names to icon components
612
- let taskIcon;
613
- switch(contentData.iconName){
614
- case 'search':
615
- taskIcon = searchIcon;
616
- break;
617
- case 'folder':
618
- taskIcon = folderIcon;
619
- break;
620
- case 'settings':
621
- taskIcon = settingsIcon;
622
- break;
623
- case 'wrench':
624
- taskIcon = wrenchIcon;
625
- break;
626
- default:
627
- taskIcon = undefined;
628
- break;
629
- }
630
- return React__default.default.createElement(TaskComponent, {
631
- title: contentData.title,
632
- type: contentData.type,
633
- parts: contentData.parts,
634
- collapsed,
635
- onCollapse: ()=>setCollapsed(!collapsed),
636
- taskIcon,
637
- chevronRightIcon,
638
- chevronDownIcon
639
- });
640
- }
641
- if (contentData.componentType === 'unknown') {
642
- return React__default.default.createElement('div', {
643
- 'data-unknown-part-type': contentData.type
644
- }, `Unknown part type: ${contentData.type}`);
645
- }
646
- return null;
647
- }
648
-
649
- // Utility function to merge class names
650
- function cn(...classes) {
651
- return classes.filter(Boolean).join(' ');
652
- }
653
-
654
- // Headless hook for processing message content
655
- function useMessage({ content, messageId = 'unknown', role = 'assistant', streaming = false, isLastMessage = false, components, renderers }) {
656
- if (!Array.isArray(content)) {
657
- console.warn('MessageContent: content must be an array (MessageBinaryFormat)');
658
- return {
659
- elements: [],
660
- messageId,
661
- role,
662
- streaming,
663
- isLastMessage
664
- };
665
- }
666
- // Merge components and renderers (backward compatibility)
667
- const mergedComponents = {
668
- ...components,
669
- // Map legacy renderers to new component names
670
- ...renderers?.CodeBlock && {
671
- CodeBlock: renderers.CodeBlock
672
- },
673
- ...renderers?.MathRenderer && {
674
- MathPart: renderers.MathRenderer
675
- },
676
- ...renderers?.MathPart && {
677
- MathPart: renderers.MathPart
678
- },
679
- ...renderers?.Icon && {
680
- Icon: renderers.Icon
681
- }
682
- };
683
- // Process content exactly like v0's Renderer component
684
- const elements = content.map(([type, data], index)=>{
685
- const key = `${messageId}-${index}`;
686
- // Markdown data (type 0) - this is the main content
687
- if (type === 0) {
688
- return processElements(data, key, mergedComponents);
689
- }
690
- // Metadata (type 1) - extract context but don't render
691
- if (type === 1) {
692
- // In the future, we could extract sources/context here like v0 does
693
- // For now, just return null like v0's renderer
694
- return null;
695
- }
696
- // Other types - v0 doesn't handle these in the main renderer
697
- return null;
698
- }).filter(Boolean);
699
- return {
700
- elements,
701
- messageId,
702
- role,
703
- streaming,
704
- isLastMessage
705
- };
706
- }
707
- // Process elements into headless data structure
708
- function processElements(data, keyPrefix, components) {
709
- // Handle case where data might not be an array due to streaming/patching
710
- if (!Array.isArray(data)) {
711
- return null;
712
- }
713
- const children = data.map((item, index)=>{
714
- const key = `${keyPrefix}-${index}`;
715
- return processElement(item, key, components);
716
- }).filter(Boolean);
717
- return {
718
- type: 'component',
719
- key: keyPrefix,
720
- data: 'elements',
721
- children
722
- };
723
- }
724
- // Process individual elements into headless data structure
725
- function processElement(element, key, components) {
726
- if (typeof element === 'string') {
727
- return {
728
- type: 'text',
729
- key,
730
- data: element
731
- };
732
- }
733
- if (!Array.isArray(element)) {
734
- return null;
735
- }
736
- const [tagName, props, ...children] = element;
737
- if (!tagName) {
738
- return null;
739
- }
740
- // Handle special v0 Platform API elements
741
- if (tagName === 'AssistantMessageContentPart') {
742
- return {
743
- type: 'content-part',
744
- key,
745
- data: {
746
- part: props.part,
747
- iconRenderer: components?.Icon,
748
- thinkingSectionRenderer: components?.ThinkingSection,
749
- taskSectionRenderer: components?.TaskSection
750
- }
751
- };
752
- }
753
- if (tagName === 'Codeblock') {
754
- return {
755
- type: 'code-project',
756
- key,
757
- data: {
758
- language: props.lang,
759
- code: children[0],
760
- iconRenderer: components?.Icon,
761
- customRenderer: components?.CodeProjectPart
762
- }
763
- };
764
- }
765
- if (tagName === 'text') {
766
- return {
767
- type: 'text',
768
- key,
769
- data: children[0] || ''
770
- };
771
- }
772
- // Process children
773
- const processedChildren = children.map((child, childIndex)=>{
774
- const childKey = `${key}-child-${childIndex}`;
775
- return processElement(child, childKey, components);
776
- }).filter(Boolean);
777
- // Handle standard HTML elements
778
- const componentOrConfig = components?.[tagName];
779
- return {
780
- type: 'html',
781
- key,
782
- data: {
783
- tagName,
784
- props,
785
- componentOrConfig
786
- },
787
- children: processedChildren
788
- };
789
- }
790
- // Default JSX renderer for backward compatibility
791
- function MessageRenderer({ messageData, className }) {
792
- const renderElement = (element)=>{
793
- switch(element.type){
794
- case 'text':
795
- return React__default.default.createElement('span', {
796
- key: element.key
797
- }, element.data);
798
- case 'content-part':
799
- return React__default.default.createElement(ContentPartRenderer, {
800
- key: element.key,
801
- part: element.data.part,
802
- iconRenderer: element.data.iconRenderer,
803
- thinkingSectionRenderer: element.data.thinkingSectionRenderer,
804
- taskSectionRenderer: element.data.taskSectionRenderer
805
- });
806
- case 'code-project':
807
- const CustomCodeProjectPart = element.data.customRenderer;
808
- const CodeProjectComponent = CustomCodeProjectPart || CodeProjectPart;
809
- return React__default.default.createElement(CodeProjectComponent, {
810
- key: element.key,
811
- language: element.data.language,
812
- code: element.data.code,
813
- iconRenderer: element.data.iconRenderer
814
- });
815
- case 'html':
816
- const { tagName, props, componentOrConfig } = element.data;
817
- const renderedChildren = element.children?.map(renderElement);
818
- if (typeof componentOrConfig === 'function') {
819
- const Component = componentOrConfig;
820
- return React__default.default.createElement(Component, {
821
- key: element.key,
822
- ...props,
823
- className: props?.className
824
- }, renderedChildren);
825
- } else if (componentOrConfig && typeof componentOrConfig === 'object') {
826
- const mergedClassName = cn(props?.className, componentOrConfig.className);
827
- const elementProps = {
828
- key: element.key,
829
- ...props,
830
- className: mergedClassName
831
- };
832
- return renderedChildren?.length ? React__default.default.createElement(tagName, elementProps, renderedChildren) : React__default.default.createElement(tagName, elementProps);
833
- } else {
834
- // Default HTML element rendering
835
- const elementProps = {
836
- key: element.key,
837
- ...props
838
- };
839
- if (props?.className) {
840
- elementProps.className = props.className;
841
- }
842
- // Special handling for links
843
- if (tagName === 'a') {
844
- elementProps.target = '_blank';
845
- elementProps.rel = 'noopener noreferrer';
846
- }
847
- return renderedChildren?.length ? React__default.default.createElement(tagName, elementProps, renderedChildren) : React__default.default.createElement(tagName, elementProps);
848
- }
849
- case 'component':
850
- return React__default.default.createElement(React__default.default.Fragment, {
851
- key: element.key
852
- }, element.children?.map(renderElement));
853
- default:
854
- return null;
855
- }
856
- };
857
- return React__default.default.createElement('div', {
858
- className
859
- }, messageData.elements.map(renderElement));
860
- }
861
- // Simplified renderer that matches v0's exact approach (backward compatibility)
862
- function MessageImpl({ content, messageId = 'unknown', role = 'assistant', streaming = false, isLastMessage = false, className, components, renderers }) {
863
- const messageData = useMessage({
864
- content,
865
- messageId,
866
- role,
867
- streaming,
868
- isLastMessage,
869
- components,
870
- renderers
871
- });
872
- return React__default.default.createElement(MessageRenderer, {
873
- messageData,
874
- className
875
- });
876
- }
877
- /**
878
- * Main component for rendering v0 Platform API message content
879
- * This is a backward-compatible JSX renderer. For headless usage, use the useMessage hook.
880
- */ const Message = React__default.default.memo(MessageImpl);
881
-
882
- const jdf = jsondiffpatch__namespace.create({});
883
- // Exact copy of the patch function from v0/chat/lib/diffpatch.ts
884
- function patch(original, delta) {
885
- const newObj = jdf.clone(original);
886
- // Check for our customized delta
887
- if (Array.isArray(delta) && delta[1] === 9 && delta[2] === 9) {
888
- // Get the path to the modified element
889
- const indexes = delta[0].slice(0, -1);
890
- // Get the string to be appended
891
- const value = delta[0].slice(-1);
892
- let obj = newObj;
893
- for (const index of indexes){
894
- if (typeof obj[index] === 'string') {
895
- obj[index] += value;
896
- return newObj;
897
- }
898
- obj = obj[index];
899
- }
900
- }
901
- // If not custom delta, apply standard jsondiffpatch-ing
902
- jdf.patch(newObj, delta);
903
- return newObj;
904
- }
905
- // Stream state manager - isolated from React lifecycle
906
- class StreamStateManager {
907
- constructor(){
908
- this.content = [];
909
- this.isStreaming = false;
910
- this.isComplete = false;
911
- this.callbacks = new Set();
912
- this.processedStreams = new WeakSet();
913
- this.cachedState = null;
914
- this.subscribe = (callback)=>{
915
- this.callbacks.add(callback);
916
- return ()=>{
917
- this.callbacks.delete(callback);
918
- };
919
- };
920
- this.notifySubscribers = ()=>{
921
- // Invalidate cached state when state changes
922
- this.cachedState = null;
923
- this.callbacks.forEach((callback)=>callback());
924
- };
925
- this.getState = ()=>{
926
- // Return cached state to prevent infinite re-renders
927
- if (this.cachedState === null) {
928
- this.cachedState = {
929
- content: this.content,
930
- isStreaming: this.isStreaming,
931
- error: this.error,
932
- isComplete: this.isComplete
933
- };
934
- }
935
- return this.cachedState;
936
- };
937
- this.processStream = async (stream, options = {})=>{
938
- // Prevent processing the same stream multiple times
939
- if (this.processedStreams.has(stream)) {
940
- return;
941
- }
942
- // Handle locked streams gracefully
943
- if (stream.locked) {
944
- console.warn('Stream is locked, cannot process');
945
- return;
946
- }
947
- this.processedStreams.add(stream);
948
- this.reset();
949
- this.setStreaming(true);
950
- try {
951
- await this.readStream(stream, options);
952
- } catch (err) {
953
- const errorMessage = err instanceof Error ? err.message : 'Unknown streaming error';
954
- this.setError(errorMessage);
955
- options.onError?.(errorMessage);
956
- } finally{
957
- this.setStreaming(false);
958
- }
959
- };
960
- this.reset = ()=>{
961
- this.content = [];
962
- this.isStreaming = false;
963
- this.error = undefined;
964
- this.isComplete = false;
965
- this.notifySubscribers();
966
- };
967
- this.setStreaming = (streaming)=>{
968
- this.isStreaming = streaming;
969
- this.notifySubscribers();
970
- };
971
- this.setError = (error)=>{
972
- this.error = error;
973
- this.notifySubscribers();
974
- };
975
- this.setComplete = (complete)=>{
976
- this.isComplete = complete;
977
- this.notifySubscribers();
978
- };
979
- this.updateContent = (newContent)=>{
980
- this.content = [
981
- ...newContent
982
- ];
983
- this.notifySubscribers();
984
- };
985
- this.readStream = async (stream, options)=>{
986
- const reader = stream.getReader();
987
- const decoder = new TextDecoder();
988
- let buffer = '';
989
- let currentContent = [];
990
- try {
991
- while(true){
992
- const { done, value } = await reader.read();
993
- if (done) {
994
- break;
995
- }
996
- const chunk = decoder.decode(value, {
997
- stream: true
998
- });
999
- buffer += chunk;
1000
- const lines = buffer.split('\n');
1001
- buffer = lines.pop() || '';
1002
- for (const line of lines){
1003
- if (line.trim() === '') {
1004
- continue;
1005
- }
1006
- // Handle SSE format (data: ...)
1007
- let jsonData;
1008
- if (line.startsWith('data: ')) {
1009
- jsonData = line.slice(6); // Remove "data: " prefix
1010
- if (jsonData === '[DONE]') {
1011
- this.setComplete(true);
1012
- options.onComplete?.(currentContent);
1013
- return;
1014
- }
1015
- } else {
1016
- // Handle raw JSON lines (fallback)
1017
- jsonData = line;
1018
- }
1019
- try {
1020
- // Parse the JSON data
1021
- const parsedData = JSON.parse(jsonData);
1022
- // Handle v0 streaming format
1023
- if (parsedData.type === 'connected') {
1024
- continue;
1025
- } else if (parsedData.type === 'done') {
1026
- this.setComplete(true);
1027
- options.onComplete?.(currentContent);
1028
- return;
1029
- } else if (parsedData.object && parsedData.object.startsWith('chat')) {
1030
- // Handle chat metadata messages (chat, chat.title, chat.name, etc.)
1031
- options.onChatData?.(parsedData);
1032
- continue;
1033
- } else if (parsedData.delta) {
1034
- // Apply the delta using jsondiffpatch
1035
- const patchedContent = patch(currentContent, parsedData.delta);
1036
- currentContent = Array.isArray(patchedContent) ? patchedContent : [];
1037
- this.updateContent(currentContent);
1038
- options.onChunk?.(currentContent);
1039
- }
1040
- } catch (e) {
1041
- console.warn('Failed to parse streaming data:', line, e);
1042
- }
1043
- }
1044
- }
1045
- this.setComplete(true);
1046
- options.onComplete?.(currentContent);
1047
- } finally{
1048
- reader.releaseLock();
1049
- }
1050
- };
1051
- }
1052
- }
1053
- /**
1054
- * Hook for handling streaming message content from v0 API using useSyncExternalStore
1055
- */ function useStreamingMessage(stream, options = {}) {
1056
- // Create a stable stream manager instance
1057
- const managerRef = React.useRef(null);
1058
- if (!managerRef.current) {
1059
- managerRef.current = new StreamStateManager();
1060
- }
1061
- const manager = managerRef.current;
1062
- // Subscribe to state changes using useSyncExternalStore
1063
- const state = React.useSyncExternalStore(manager.subscribe, manager.getState, manager.getState);
1064
- // Process stream when it changes
1065
- const lastStreamRef = React.useRef(null);
1066
- if (stream !== lastStreamRef.current) {
1067
- lastStreamRef.current = stream;
1068
- if (stream) {
1069
- manager.processStream(stream, options);
1070
- }
1071
- }
1072
- return state;
1073
- }
1074
-
1075
- // Headless hook for streaming message
1076
- function useStreamingMessageData({ stream, messageId = 'unknown', role = 'assistant', components, renderers, onChunk, onComplete, onError, onChatData }) {
1077
- const streamingState = useStreamingMessage(stream, {
1078
- onChunk,
1079
- onComplete,
1080
- onError,
1081
- onChatData
1082
- });
1083
- const messageData = streamingState.content.length > 0 ? useMessage({
1084
- content: streamingState.content,
1085
- messageId,
1086
- role,
1087
- streaming: streamingState.isStreaming,
1088
- isLastMessage: true,
1089
- components,
1090
- renderers
1091
- }) : null;
1092
- return {
1093
- ...streamingState,
1094
- messageData
1095
- };
1096
- }
1097
- /**
1098
- * Component for rendering streaming message content from v0 API
1099
- *
1100
- * For headless usage, use the useStreamingMessageData hook instead.
1101
- *
1102
- * @example
1103
- * ```tsx
1104
- * import { v0 } from 'v0-sdk'
1105
- * import { StreamingMessage } from '@v0-sdk/react'
1106
- *
1107
- * function ChatDemo() {
1108
- * const [stream, setStream] = useState<ReadableStream<Uint8Array> | null>(null)
1109
- *
1110
- * const handleSubmit = async () => {
1111
- * const response = await v0.chats.create({
1112
- * message: 'Create a button component',
1113
- * responseMode: 'experimental_stream'
1114
- * })
1115
- * setStream(response)
1116
- * }
1117
- *
1118
- * return (
1119
- * <div>
1120
- * <button onClick={handleSubmit}>Send Message</button>
1121
- * {stream && (
1122
- * <StreamingMessage
1123
- * stream={stream}
1124
- * messageId="demo-message"
1125
- * role="assistant"
1126
- * onComplete={(content) => handleCompletion(content)}
1127
- * onChatData={(chatData) => handleChatData(chatData)}
1128
- * />
1129
- * )}
1130
- * </div>
1131
- * )
1132
- * }
1133
- * ```
1134
- */ function StreamingMessage({ stream, showLoadingIndicator = true, loadingComponent, errorComponent, onChunk, onComplete, onError, onChatData, className, ...messageProps }) {
1135
- const streamingData = useStreamingMessageData({
1136
- stream,
1137
- onChunk,
1138
- onComplete,
1139
- onError,
1140
- onChatData,
1141
- ...messageProps
1142
- });
1143
- // Handle error state
1144
- if (streamingData.error) {
1145
- if (errorComponent) {
1146
- return React__default.default.createElement(React__default.default.Fragment, {}, errorComponent(streamingData.error));
1147
- }
1148
- // Fallback error component using React.createElement for compatibility
1149
- return React__default.default.createElement('div', {
1150
- className: 'text-red-500 p-4 border border-red-200 rounded',
1151
- style: {
1152
- color: 'red',
1153
- padding: '1rem',
1154
- border: '1px solid #fecaca',
1155
- borderRadius: '0.375rem'
1156
- }
1157
- }, `Error: ${streamingData.error}`);
1158
- }
1159
- // Handle loading state
1160
- if (showLoadingIndicator && streamingData.isStreaming && streamingData.content.length === 0) {
1161
- if (loadingComponent) {
1162
- return React__default.default.createElement(React__default.default.Fragment, {}, loadingComponent);
1163
- }
1164
- // Fallback loading component using React.createElement for compatibility
1165
- return React__default.default.createElement('div', {
1166
- className: 'flex items-center space-x-2 text-gray-500',
1167
- style: {
1168
- display: 'flex',
1169
- alignItems: 'center',
1170
- gap: '0.5rem',
1171
- color: '#6b7280'
1172
- }
1173
- }, React__default.default.createElement('div', {
1174
- className: 'animate-spin h-4 w-4 border-2 border-gray-300 border-t-gray-600 rounded-full',
1175
- style: {
1176
- animation: 'spin 1s linear infinite',
1177
- height: '1rem',
1178
- width: '1rem',
1179
- border: '2px solid #d1d5db',
1180
- borderTopColor: '#4b5563',
1181
- borderRadius: '50%'
1182
- }
1183
- }), React__default.default.createElement('span', {}, 'Loading...'));
1184
- }
1185
- // Render the message content
1186
- return React__default.default.createElement(Message, {
1187
- ...messageProps,
1188
- content: streamingData.content,
1189
- streaming: streamingData.isStreaming,
1190
- isLastMessage: true,
1191
- className
1192
- });
1193
- }
1194
-
1195
- // Headless hook for math data
1196
- function useMath(props) {
1197
- return {
1198
- content: props.content,
1199
- inline: props.inline ?? false,
1200
- displayMode: props.displayMode ?? !props.inline,
1201
- processedContent: props.content
1202
- };
1203
- }
1204
- /**
1205
- * Generic math renderer component
1206
- * Renders plain math content by default - consumers should provide their own math rendering
1207
- *
1208
- * For headless usage, use the useMath hook instead.
1209
- */ function MathPart({ content, inline = false, className = '', children, displayMode }) {
1210
- // If children provided, use that (allows complete customization)
1211
- if (children) {
1212
- return React__default.default.createElement(React__default.default.Fragment, {}, children);
1213
- }
1214
- const mathData = useMath({
1215
- content,
1216
- inline,
1217
- displayMode
1218
- });
1219
- // Simple fallback - just render plain math content
1220
- // Uses React.createElement for maximum compatibility across environments
1221
- return React__default.default.createElement(mathData.inline ? 'span' : 'div', {
1222
- className,
1223
- 'data-math-inline': mathData.inline,
1224
- 'data-math-display': mathData.displayMode
1225
- }, mathData.processedContent);
1226
- }
1227
-
1228
- exports.AssistantMessageContentPart = ContentPartRenderer;
1229
- exports.CodeBlock = CodeBlock;
1230
- exports.CodeProjectBlock = CodeProjectPart;
1231
- exports.CodeProjectPart = CodeProjectPart;
1232
- exports.ContentPartRenderer = ContentPartRenderer;
1233
- exports.Icon = Icon;
1234
- exports.IconProvider = IconProvider;
1235
- exports.MathPart = MathPart;
1236
- exports.MathRenderer = MathPart;
1237
- exports.Message = Message;
1238
- exports.MessageContent = Message;
1239
- exports.MessageRenderer = Message;
1240
- exports.StreamingMessage = StreamingMessage;
1241
- exports.TaskSection = TaskSection;
1242
- exports.ThinkingSection = ThinkingSection;
1243
- exports.V0MessageRenderer = Message;
1244
- exports.useCodeBlock = useCodeBlock;
1245
- exports.useCodeProject = useCodeProject;
1246
- exports.useContentPart = useContentPart;
1247
- exports.useIcon = useIcon;
1248
- exports.useMath = useMath;
1249
- exports.useMessage = useMessage;
1250
- exports.useStreamingMessage = useStreamingMessage;
1251
- exports.useStreamingMessageData = useStreamingMessageData;
1252
- exports.useTaskSection = useTaskSection;
1253
- exports.useThinkingSection = useThinkingSection;
3
+ `),a=r.parts.filter(e=>e.type===`file`).map(e=>({url:e.url})),o=x(n.body)?n.body:{},s=new AbortController,l=y(this.options.request,n.headers,b(this.options.request?.signal,n.abortSignal,s.signal));return c((0,t.readV0Stream)(await e.n(this.currentChatId?v(this.options.urls.send,this.currentChatId):this.options.urls.create,g,this.currentChatId?{...this.options.send,...o,message:i,...a.length?{attachments:a}:{}}:{...this.options.create,...o,message:i,...a.length?{attachments:a}:{}},l)),{onUpdate:e=>this.captureChatId(e),abort:e=>s.abort(e)})}async reconnectToStream(n){if(this.reconnecting)return null;let r=this.currentChatId??n.chatId;if(!r||!this.seed)return null;this.reconnecting=!0;let i=new AbortController;try{let a=await e.n(v(this.options.urls.resume,r),g,void 0,y(this.options.request,n.headers,b(this.options.request?.signal,i.signal)));return a.status===204?(this.reconnecting=!1,null):c((0,t.readV0Stream)(a),{seed:this.seed,onUpdate:e=>this.captureChatId(e),abort:e=>i.abort(e),onClose:()=>{this.reconnecting=!1}})}catch(e){throw this.reconnecting=!1,e}}captureChatId(e){let t=e.chat?.id??e.message?.chatId;!t||t===this.currentChatId||(this.currentChatId=t,this.options.onChatCreated?.(t))}};function v(e,t){return typeof e==`function`?e(t):e}function y(e,t,n){let r=new Headers(e?.headers);return new Headers(t).forEach((e,t)=>r.set(t,e)),{...e,headers:r,signal:n}}function b(...e){let t=e.filter(e=>e!=null);if(t.length===1)return t[0];let n=new AbortController,r=e=>{n.signal.aborted||n.abort(e.reason)};for(let e of t){if(e.aborted){r(e);break}e.addEventListener(`abort`,()=>r(e),{once:!0})}return n.signal}function x(e){return typeof e==`object`&&!!e}exports.V0ResponseError=e.t,exports.V0SnapshotChunkReducer=s,exports.V0Transport=_,exports.getResumableV0Assistant=m,exports.getV0PartId=o,exports.prependV0UIMessageHistory=h,exports.serializeDates=a,exports.shouldResumeV0Chat=p,exports.toV0UIMessage=n,exports.toV0UIMessageMetadata=i,exports.toV0UIMessages=r,exports.v0StreamToUIMessageStream=c;