|
146 | 146 | : tableState.streamingRows[row.ID]?.includes(col.id) && !value |
147 | 147 | ? 'thinking' |
148 | 148 | : 'answer', |
149 | | - message: { content: value, chunks, fileUrl: tableState.rowThumbs[row.ID]?.[col.id]?.url }, |
| 149 | + message: { |
| 150 | + content: value, |
| 151 | + error: cell.error ?? null, |
| 152 | + chunks, |
| 153 | + fileUrl: tableState.rowThumbs[row.ID]?.[col.id]?.url |
| 154 | + }, |
150 | 155 | reasoningContent: cell.reasoning_content ?? null, |
151 | 156 | reasoningTime: cell.reasoning_time ?? null, |
152 | 157 | expandChunk: null, |
|
238 | 243 | </button> |
239 | 244 | </div> |
240 | 245 |
|
241 | | - {#if showOutputDetails.activeTab !== 'image'} |
242 | | - <div |
243 | | - data-testid="output-details-tabs" |
244 | | - style="grid-template-columns: repeat({tabItems.length}, minmax(6rem, 1fr));" |
245 | | - class="relative grid w-fit items-end overflow-auto border-b border-[#F2F4F7] text-xs sm:text-sm" |
246 | | - > |
247 | | - {#each tabItems as { id, title, condition }} |
248 | | - {#if condition} |
249 | | - <button |
250 | | - onclick={() => (showOutputDetails.activeTab = id)} |
251 | | - class="px-0 py-2 font-medium sm:px-4 {showOutputDetails.activeTab === id |
252 | | - ? 'text-[#344054]' |
253 | | - : 'text-[#98A2B3]'} text-center transition-colors" |
254 | | - > |
255 | | - {title} |
256 | | - </button> |
257 | | - {/if} |
258 | | - {/each} |
259 | | - |
260 | | - <div |
261 | | - style="width: {(1 / tabItems.length) * 100}%; left: {tabHighlightPos}%;" |
262 | | - class="absolute bottom-0 h-[3px] bg-secondary transition-[left]" |
263 | | - ></div> |
264 | | - </div> |
265 | | - {/if} |
266 | | - |
267 | | - {#if showOutputDetails.activeTab === 'image'} |
268 | | - <div class="flex h-1 grow flex-col gap-2 p-3"> |
269 | | - <img src={showOutputDetails.message?.fileUrl} alt="" class="max-h-[45vh] object-contain" /> |
270 | | - |
| 246 | + {#if showOutputDetails.message?.error} |
| 247 | + <div class="flex h-1 grow flex-col gap-2 overflow-auto px-8 py-4"> |
271 | 248 | <p |
272 | | - title={showOutputDetails.message?.content.split('/').pop()} |
273 | | - class="break-all rounded text-sm text-[#667085]" |
| 249 | + class="response-message flex max-w-full flex-col gap-4 whitespace-pre-line text-sm text-[#D92D20]" |
274 | 250 | > |
275 | | - {showOutputDetails.message?.content.split('/').pop()} |
| 251 | + {typeof showOutputDetails.message.error === 'string' |
| 252 | + ? showOutputDetails.message.error |
| 253 | + : showOutputDetails.message.error?.message |
| 254 | + ? String(showOutputDetails.message.error.message) |
| 255 | + : 'Error'} |
276 | 256 | </p> |
| 257 | + </div> |
| 258 | + {:else} |
| 259 | + {#if showOutputDetails.activeTab !== 'image'} |
| 260 | + <div |
| 261 | + data-testid="output-details-tabs" |
| 262 | + style="grid-template-columns: repeat({tabItems.length}, minmax(6rem, 1fr));" |
| 263 | + class="relative grid w-fit items-end overflow-auto border-b border-[#F2F4F7] text-xs sm:text-sm" |
| 264 | + > |
| 265 | + {#each tabItems as { id, title, condition }} |
| 266 | + {#if condition} |
| 267 | + <button |
| 268 | + onclick={() => (showOutputDetails.activeTab = id)} |
| 269 | + class="px-0 py-2 font-medium sm:px-4 {showOutputDetails.activeTab === id |
| 270 | + ? 'text-[#344054]' |
| 271 | + : 'text-[#98A2B3]'} text-center transition-colors" |
| 272 | + > |
| 273 | + {title} |
| 274 | + </button> |
| 275 | + {/if} |
| 276 | + {/each} |
| 277 | + |
| 278 | + <div |
| 279 | + style="width: {(1 / tabItems.length) * 100}%; left: {tabHighlightPos}%;" |
| 280 | + class="absolute bottom-0 h-[3px] bg-secondary transition-[left]" |
| 281 | + ></div> |
| 282 | + </div> |
| 283 | + {/if} |
277 | 284 |
|
278 | | - <div class="flex gap-1"> |
279 | | - <Button |
280 | | - variant="ghost" |
281 | | - title="Download file" |
282 | | - onclick={() => getRawFile(showOutputDetails.message?.content ?? '')} |
283 | | - class="h-8 w-max gap-2 rounded-md border border-[#E4E7EC] bg-white px-2 font-normal text-[#667085] shadow-[0px_1px_3px_0px_rgba(16,24,40,0.1)] hover:bg-[#F9FAFB] hover:text-[#667085]" |
| 285 | + {#if showOutputDetails.activeTab === 'image'} |
| 286 | + <div class="flex h-1 grow flex-col gap-2 p-3"> |
| 287 | + <img |
| 288 | + src={showOutputDetails.message?.fileUrl} |
| 289 | + alt="" |
| 290 | + class="max-h-[45vh] object-contain" |
| 291 | + /> |
| 292 | + |
| 293 | + <p |
| 294 | + title={showOutputDetails.message?.content.split('/').pop()} |
| 295 | + class="break-all rounded text-sm text-[#667085]" |
284 | 296 | > |
285 | | - <ArrowDownToLine class="h-3.5 w-3.5" /> |
286 | | - Download |
287 | | - </Button> |
| 297 | + {showOutputDetails.message?.content.split('/').pop()} |
| 298 | + </p> |
288 | 299 |
|
289 | | - {#if showOutputDetails.activeCell?.rowID} |
| 300 | + <div class="flex gap-1"> |
290 | 301 | <Button |
291 | 302 | variant="ghost" |
292 | | - title="Delete file" |
293 | | - onclick={() => { |
294 | | - tableState.deletingFile = { |
295 | | - rowID: showOutputDetails.activeCell?.rowID ?? '', |
296 | | - columnID: showOutputDetails.activeCell?.columnID ?? '', |
297 | | - fileUri: showOutputDetails.message?.content ?? '' |
298 | | - }; |
299 | | - }} |
300 | | - class="h-8 w-max gap-2 rounded-md border border-[#E4E7EC] bg-white px-2 font-normal text-[#F04438] shadow-[0px_1px_3px_0px_rgba(16,24,40,0.1)] hover:bg-[#F9FAFB] hover:text-[#F04438]" |
| 303 | + title="Download file" |
| 304 | + onclick={() => getRawFile(showOutputDetails.message?.content ?? '')} |
| 305 | + class="h-8 w-max gap-2 rounded-md border border-[#E4E7EC] bg-white px-2 font-normal text-[#667085] shadow-[0px_1px_3px_0px_rgba(16,24,40,0.1)] hover:bg-[#F9FAFB] hover:text-[#667085]" |
301 | 306 | > |
302 | | - <Trash2 class="h-3.5 w-3.5" /> |
303 | | - Delete |
| 307 | + <ArrowDownToLine class="h-3.5 w-3.5" /> |
| 308 | + Download |
304 | 309 | </Button> |
305 | | - {/if} |
306 | | - </div> |
307 | | - </div> |
308 | | - {:else if showOutputDetails.activeTab === 'answer'} |
309 | | - {@const rawHtml = converter |
310 | | - .makeHtml(showOutputDetails.message?.content ?? '') |
311 | | - .replaceAll(chatCitationPattern, (match, word) => |
312 | | - citationReplacer( |
313 | | - match, |
314 | | - word, |
315 | | - showOutputDetails.activeCell?.columnID ?? '', |
316 | | - showOutputDetails.activeCell?.rowID ?? '', |
317 | | - showOutputDetails.message?.chunks ?? [] |
318 | | - ) |
319 | | - )} |
320 | 310 |
|
321 | | - <div class="flex h-1 grow flex-col gap-2 overflow-auto px-8 py-4"> |
322 | | - <p class="response-message flex max-w-full flex-col gap-4 whitespace-pre-line text-sm"> |
323 | | - {@html rawHtml} |
324 | | - </p> |
325 | | - </div> |
326 | | - {:else if showOutputDetails.activeTab === 'thinking'} |
327 | | - {@const rawHtml = converter.makeHtml(showOutputDetails.reasoningContent ?? '')} |
328 | | - <div class="flex h-1 grow flex-col gap-2 overflow-auto px-8 py-4"> |
329 | | - {#if showOutputDetails.reasoningTime} |
330 | | - <div class="mb-2 flex select-none items-center gap-2 self-start text-sm text-[#667085]"> |
331 | | - <Sparkle size={16} /> |
332 | | - Thought for {showOutputDetails.reasoningTime.toFixed()} second{Number( |
333 | | - showOutputDetails.reasoningTime.toFixed() |
334 | | - ) > 1 |
335 | | - ? 's' |
336 | | - : ''} |
| 311 | + {#if showOutputDetails.activeCell?.rowID} |
| 312 | + <Button |
| 313 | + variant="ghost" |
| 314 | + title="Delete file" |
| 315 | + onclick={() => { |
| 316 | + tableState.deletingFile = { |
| 317 | + rowID: showOutputDetails.activeCell?.rowID ?? '', |
| 318 | + columnID: showOutputDetails.activeCell?.columnID ?? '', |
| 319 | + fileUri: showOutputDetails.message?.content ?? '' |
| 320 | + }; |
| 321 | + }} |
| 322 | + class="h-8 w-max gap-2 rounded-md border border-[#E4E7EC] bg-white px-2 font-normal text-[#F04438] shadow-[0px_1px_3px_0px_rgba(16,24,40,0.1)] hover:bg-[#F9FAFB] hover:text-[#F04438]" |
| 323 | + > |
| 324 | + <Trash2 class="h-3.5 w-3.5" /> |
| 325 | + Delete |
| 326 | + </Button> |
| 327 | + {/if} |
337 | 328 | </div> |
338 | | - {/if} |
| 329 | + </div> |
| 330 | + {:else if showOutputDetails.activeTab === 'answer'} |
| 331 | + {@const rawHtml = converter |
| 332 | + .makeHtml(showOutputDetails.message?.content ?? '') |
| 333 | + .replaceAll(chatCitationPattern, (match, word) => |
| 334 | + citationReplacer( |
| 335 | + match, |
| 336 | + word, |
| 337 | + showOutputDetails.activeCell?.columnID ?? '', |
| 338 | + showOutputDetails.activeCell?.rowID ?? '', |
| 339 | + showOutputDetails.message?.chunks ?? [] |
| 340 | + ) |
| 341 | + )} |
| 342 | + |
| 343 | + <div class="flex h-1 grow flex-col gap-2 overflow-auto px-8 py-4"> |
| 344 | + <p class="response-message flex max-w-full flex-col gap-4 whitespace-pre-line text-sm"> |
| 345 | + {@html rawHtml} |
| 346 | + </p> |
| 347 | + </div> |
| 348 | + {:else if showOutputDetails.activeTab === 'thinking'} |
| 349 | + {@const rawHtml = converter.makeHtml(showOutputDetails.reasoningContent ?? '')} |
| 350 | + <div class="flex h-1 grow flex-col gap-2 overflow-auto px-8 py-4"> |
| 351 | + {#if showOutputDetails.reasoningTime} |
| 352 | + <div class="mb-2 flex select-none items-center gap-2 self-start text-sm text-[#667085]"> |
| 353 | + <Sparkle size={16} /> |
| 354 | + Thought for {showOutputDetails.reasoningTime.toFixed()} second{Number( |
| 355 | + showOutputDetails.reasoningTime.toFixed() |
| 356 | + ) > 1 |
| 357 | + ? 's' |
| 358 | + : ''} |
| 359 | + </div> |
| 360 | + {/if} |
339 | 361 |
|
340 | | - <p |
341 | | - class="response-message flex max-w-full flex-col gap-4 whitespace-pre-line text-sm text-[#475467]" |
342 | | - > |
343 | | - {@html rawHtml} |
344 | | - </p> |
345 | | - </div> |
346 | | - {:else if showOutputDetails.activeTab === 'references'} |
347 | | - <References bind:showReferences={showOutputDetails} /> |
| 362 | + <p |
| 363 | + class="response-message flex max-w-full flex-col gap-4 whitespace-pre-line text-sm text-[#475467]" |
| 364 | + > |
| 365 | + {@html rawHtml} |
| 366 | + </p> |
| 367 | + </div> |
| 368 | + {:else if showOutputDetails.activeTab === 'references'} |
| 369 | + <References bind:showReferences={showOutputDetails} /> |
| 370 | + {/if} |
348 | 371 | {/if} |
349 | 372 | </div> |
350 | 373 | </div> |
0 commit comments