agentgui 1.0.97 → 1.0.99
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/package.json +1 -1
- package/server.js +51 -5
- package/static/index.html +164 -0
- package/static/js/client.js +109 -34
- package/static/js/streaming-renderer.js +14 -7
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -475,10 +475,27 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
475
475
|
let eventCount = 0;
|
|
476
476
|
|
|
477
477
|
const onEvent = (parsed) => {
|
|
478
|
-
|
|
478
|
+
eventCount++;
|
|
479
|
+
|
|
480
|
+
if (parsed.type === 'system') {
|
|
481
|
+
broadcastSync({
|
|
482
|
+
type: 'streaming_progress',
|
|
483
|
+
sessionId,
|
|
484
|
+
conversationId,
|
|
485
|
+
block: {
|
|
486
|
+
type: 'system',
|
|
487
|
+
subtype: parsed.subtype,
|
|
488
|
+
model: parsed.model,
|
|
489
|
+
cwd: parsed.cwd,
|
|
490
|
+
tools: parsed.tools,
|
|
491
|
+
session_id: parsed.session_id
|
|
492
|
+
},
|
|
493
|
+
blockIndex: allBlocks.length,
|
|
494
|
+
timestamp: Date.now()
|
|
495
|
+
});
|
|
496
|
+
} else if (parsed.type === 'assistant' && parsed.message?.content) {
|
|
479
497
|
for (const block of parsed.message.content) {
|
|
480
498
|
allBlocks.push(block);
|
|
481
|
-
eventCount++;
|
|
482
499
|
broadcastSync({
|
|
483
500
|
type: 'streaming_progress',
|
|
484
501
|
sessionId,
|
|
@@ -488,16 +505,45 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
488
505
|
timestamp: Date.now()
|
|
489
506
|
});
|
|
490
507
|
}
|
|
491
|
-
} else if (parsed.type === '
|
|
508
|
+
} else if (parsed.type === 'user' && parsed.message?.content) {
|
|
509
|
+
for (const block of parsed.message.content) {
|
|
510
|
+
if (block.type === 'tool_result') {
|
|
511
|
+
broadcastSync({
|
|
512
|
+
type: 'streaming_progress',
|
|
513
|
+
sessionId,
|
|
514
|
+
conversationId,
|
|
515
|
+
block: {
|
|
516
|
+
type: 'tool_result',
|
|
517
|
+
tool_use_id: block.tool_use_id,
|
|
518
|
+
content: typeof block.content === 'string' ? block.content : JSON.stringify(block.content),
|
|
519
|
+
is_error: block.is_error || false
|
|
520
|
+
},
|
|
521
|
+
blockIndex: allBlocks.length,
|
|
522
|
+
timestamp: Date.now()
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
} else if (parsed.type === 'result') {
|
|
492
527
|
broadcastSync({
|
|
493
528
|
type: 'streaming_progress',
|
|
494
529
|
sessionId,
|
|
495
530
|
conversationId,
|
|
496
|
-
block: {
|
|
497
|
-
|
|
531
|
+
block: {
|
|
532
|
+
type: 'result',
|
|
533
|
+
subtype: parsed.subtype,
|
|
534
|
+
duration_ms: parsed.duration_ms,
|
|
535
|
+
total_cost_usd: parsed.total_cost_usd,
|
|
536
|
+
num_turns: parsed.num_turns,
|
|
537
|
+
is_error: parsed.is_error || false,
|
|
538
|
+
result: parsed.result
|
|
539
|
+
},
|
|
540
|
+
blockIndex: allBlocks.length,
|
|
498
541
|
isResult: true,
|
|
499
542
|
timestamp: Date.now()
|
|
500
543
|
});
|
|
544
|
+
if (parsed.result && allBlocks.length === 0) {
|
|
545
|
+
allBlocks.push({ type: 'text', text: String(parsed.result) });
|
|
546
|
+
}
|
|
501
547
|
}
|
|
502
548
|
};
|
|
503
549
|
|
package/static/index.html
CHANGED
|
@@ -431,6 +431,170 @@
|
|
|
431
431
|
margin: 0.25rem 0;
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
/* --- Streaming block types --- */
|
|
435
|
+
.streaming-block-system {
|
|
436
|
+
padding: 0.5rem 0.75rem;
|
|
437
|
+
margin: 0.25rem 0;
|
|
438
|
+
background: rgba(59,130,246,0.08);
|
|
439
|
+
border: 1px solid rgba(59,130,246,0.2);
|
|
440
|
+
border-radius: 0.375rem;
|
|
441
|
+
font-size: 0.8rem;
|
|
442
|
+
display: flex;
|
|
443
|
+
align-items: center;
|
|
444
|
+
gap: 0.5rem;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.system-model-badge {
|
|
448
|
+
background: var(--color-primary);
|
|
449
|
+
color: white;
|
|
450
|
+
padding: 0.125rem 0.5rem;
|
|
451
|
+
border-radius: 1rem;
|
|
452
|
+
font-size: 0.7rem;
|
|
453
|
+
font-weight: 600;
|
|
454
|
+
white-space: nowrap;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.system-info {
|
|
458
|
+
color: var(--color-text-secondary);
|
|
459
|
+
font-size: 0.75rem;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.streaming-block-tool-use {
|
|
463
|
+
margin: 0.5rem 0;
|
|
464
|
+
border-left: 3px solid #06b6d4;
|
|
465
|
+
background: rgba(6,182,212,0.06);
|
|
466
|
+
border-radius: 0 0.375rem 0.375rem 0;
|
|
467
|
+
overflow: hidden;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.tool-use-header {
|
|
471
|
+
padding: 0.5rem 0.75rem;
|
|
472
|
+
font-weight: 600;
|
|
473
|
+
font-size: 0.85rem;
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: center;
|
|
476
|
+
gap: 0.375rem;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.tool-use-icon {
|
|
480
|
+
font-size: 0.9rem;
|
|
481
|
+
opacity: 0.7;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.tool-use-name {
|
|
485
|
+
color: #0891b2;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
html.dark .tool-use-name { color: #22d3ee; }
|
|
489
|
+
|
|
490
|
+
.tool-input-details {
|
|
491
|
+
border-top: 1px solid rgba(6,182,212,0.15);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.tool-input-summary {
|
|
495
|
+
padding: 0.375rem 0.75rem;
|
|
496
|
+
font-size: 0.75rem;
|
|
497
|
+
color: var(--color-text-secondary);
|
|
498
|
+
cursor: pointer;
|
|
499
|
+
user-select: none;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.tool-input-summary:hover {
|
|
503
|
+
background: rgba(6,182,212,0.08);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.tool-input-pre {
|
|
507
|
+
margin: 0;
|
|
508
|
+
padding: 0.5rem 0.75rem;
|
|
509
|
+
font-size: 0.75rem;
|
|
510
|
+
font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
|
|
511
|
+
background: rgba(0,0,0,0.03);
|
|
512
|
+
overflow-x: auto;
|
|
513
|
+
max-height: 200px;
|
|
514
|
+
overflow-y: auto;
|
|
515
|
+
white-space: pre-wrap;
|
|
516
|
+
word-break: break-all;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
html.dark .tool-input-pre { background: rgba(255,255,255,0.03); }
|
|
520
|
+
|
|
521
|
+
.streaming-block-tool-result {
|
|
522
|
+
margin: 0.25rem 0 0.5rem 0;
|
|
523
|
+
border-radius: 0.375rem;
|
|
524
|
+
background: var(--color-bg-code);
|
|
525
|
+
overflow: hidden;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.tool-result-header {
|
|
529
|
+
padding: 0.375rem 0.75rem;
|
|
530
|
+
font-size: 0.7rem;
|
|
531
|
+
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.tool-result-ok-badge {
|
|
535
|
+
color: #10b981;
|
|
536
|
+
font-weight: 600;
|
|
537
|
+
text-transform: uppercase;
|
|
538
|
+
letter-spacing: 0.05em;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.tool-result-error-badge {
|
|
542
|
+
color: #ef4444;
|
|
543
|
+
font-weight: 600;
|
|
544
|
+
text-transform: uppercase;
|
|
545
|
+
letter-spacing: 0.05em;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
.tool-result-pre {
|
|
549
|
+
margin: 0;
|
|
550
|
+
padding: 0.5rem 0.75rem;
|
|
551
|
+
font-size: 0.75rem;
|
|
552
|
+
font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
|
|
553
|
+
color: #d1d5db;
|
|
554
|
+
overflow-x: auto;
|
|
555
|
+
max-height: 300px;
|
|
556
|
+
overflow-y: auto;
|
|
557
|
+
white-space: pre-wrap;
|
|
558
|
+
word-break: break-all;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.streaming-block-tool-result.tool-result-error {
|
|
562
|
+
border: 1px solid rgba(239,68,68,0.3);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.streaming-block-result {
|
|
566
|
+
padding: 0.5rem 0.75rem;
|
|
567
|
+
margin: 0.5rem 0;
|
|
568
|
+
border-radius: 0.375rem;
|
|
569
|
+
background: rgba(16,185,129,0.08);
|
|
570
|
+
border: 1px solid rgba(16,185,129,0.2);
|
|
571
|
+
font-size: 0.8rem;
|
|
572
|
+
display: flex;
|
|
573
|
+
align-items: center;
|
|
574
|
+
gap: 0.5rem;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.streaming-block-result.result-error {
|
|
578
|
+
background: rgba(239,68,68,0.08);
|
|
579
|
+
border-color: rgba(239,68,68,0.2);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.result-status {
|
|
583
|
+
font-weight: 600;
|
|
584
|
+
color: #10b981;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.result-error .result-status { color: #ef4444; }
|
|
588
|
+
|
|
589
|
+
.result-stats {
|
|
590
|
+
color: var(--color-text-secondary);
|
|
591
|
+
font-size: 0.75rem;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
.streaming-indicator-label {
|
|
595
|
+
white-space: nowrap;
|
|
596
|
+
}
|
|
597
|
+
|
|
434
598
|
/* --- Input area: fixed at bottom --- */
|
|
435
599
|
.input-section {
|
|
436
600
|
flex-shrink: 0;
|
package/static/js/client.js
CHANGED
|
@@ -274,7 +274,7 @@ class AgentGUIClient {
|
|
|
274
274
|
<div class="message-blocks streaming-blocks"></div>
|
|
275
275
|
<div class="streaming-indicator" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0;color:var(--color-text-secondary);font-size:0.875rem;">
|
|
276
276
|
<span class="animate-spin" style="display:inline-block;width:1rem;height:1rem;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;"></span>
|
|
277
|
-
Thinking
|
|
277
|
+
<span class="streaming-indicator-label">Thinking...</span>
|
|
278
278
|
</div>
|
|
279
279
|
`;
|
|
280
280
|
messagesEl.appendChild(streamingDiv);
|
|
@@ -300,37 +300,75 @@ class AgentGUIClient {
|
|
|
300
300
|
if (!blocksEl) return;
|
|
301
301
|
|
|
302
302
|
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
303
|
+
let indicatorText = 'Responding...';
|
|
303
304
|
|
|
304
|
-
if (block.type === '
|
|
305
|
+
if (block.type === 'system') {
|
|
306
|
+
const div = document.createElement('div');
|
|
307
|
+
div.className = 'streaming-block-system';
|
|
308
|
+
const toolCount = block.tools ? block.tools.length : 0;
|
|
309
|
+
div.innerHTML = `<span class="system-model-badge">${this.escapeHtml(block.model || 'unknown')}</span> <span class="system-info">${toolCount} tools available</span>`;
|
|
310
|
+
blocksEl.appendChild(div);
|
|
311
|
+
indicatorText = 'Initializing...';
|
|
312
|
+
} else if (block.type === 'text' && block.text) {
|
|
305
313
|
const existingTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
306
314
|
if (existingTextEl && !data.isResult) {
|
|
307
315
|
existingTextEl.innerHTML = this.renderBlockContent(block);
|
|
308
316
|
} else {
|
|
317
|
+
const prevTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
318
|
+
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
309
319
|
const div = document.createElement('div');
|
|
310
320
|
div.className = 'message-text streaming-text-current';
|
|
311
321
|
div.innerHTML = this.renderBlockContent(block);
|
|
312
322
|
blocksEl.appendChild(div);
|
|
313
323
|
}
|
|
324
|
+
indicatorText = 'Responding...';
|
|
314
325
|
} else if (block.type === 'tool_use') {
|
|
315
326
|
const prevTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
316
327
|
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
317
328
|
|
|
318
329
|
const div = document.createElement('div');
|
|
319
|
-
div.className = '
|
|
320
|
-
div.
|
|
330
|
+
div.className = 'streaming-block-tool-use';
|
|
331
|
+
div.dataset.toolUseId = block.id || '';
|
|
332
|
+
let inputHtml = '';
|
|
333
|
+
if (block.input && Object.keys(block.input).length > 0) {
|
|
334
|
+
const inputStr = JSON.stringify(block.input, null, 2);
|
|
335
|
+
inputHtml = `<details class="tool-input-details"><summary class="tool-input-summary">Input</summary><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></details>`;
|
|
336
|
+
}
|
|
337
|
+
div.innerHTML = `<div class="tool-use-header"><span class="tool-use-icon">⚙</span> <span class="tool-use-name">${this.escapeHtml(block.name || 'unknown')}</span></div>${inputHtml}`;
|
|
321
338
|
blocksEl.appendChild(div);
|
|
339
|
+
indicatorText = `Using ${block.name || 'tool'}...`;
|
|
322
340
|
} else if (block.type === 'tool_result') {
|
|
323
341
|
const div = document.createElement('div');
|
|
324
|
-
div.className = '
|
|
325
|
-
|
|
342
|
+
div.className = 'streaming-block-tool-result' + (block.is_error ? ' tool-result-error' : '');
|
|
343
|
+
const content = block.content || '';
|
|
344
|
+
const displayContent = content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content;
|
|
345
|
+
div.innerHTML = `<div class="tool-result-header">${block.is_error ? '<span class="tool-result-error-badge">Error</span>' : '<span class="tool-result-ok-badge">Result</span>'}</div><pre class="tool-result-pre">${this.escapeHtml(displayContent)}</pre>`;
|
|
326
346
|
blocksEl.appendChild(div);
|
|
347
|
+
indicatorText = 'Processing result...';
|
|
348
|
+
} else if (block.type === 'result') {
|
|
349
|
+
const div = document.createElement('div');
|
|
350
|
+
div.className = 'streaming-block-result' + (block.is_error ? ' result-error' : '');
|
|
351
|
+
const duration = block.duration_ms ? (block.duration_ms / 1000).toFixed(1) + 's' : '';
|
|
352
|
+
const cost = block.total_cost_usd ? '$' + block.total_cost_usd.toFixed(4) : '';
|
|
353
|
+
const turns = block.num_turns ? block.num_turns + ' turns' : '';
|
|
354
|
+
const parts = [duration, cost, turns].filter(Boolean);
|
|
355
|
+
div.innerHTML = `<span class="result-status">${block.is_error ? 'Failed' : 'Complete'}</span>${parts.length ? ' <span class="result-stats">' + parts.join(' / ') + '</span>' : ''}`;
|
|
356
|
+
blocksEl.appendChild(div);
|
|
357
|
+
indicatorText = 'Complete';
|
|
327
358
|
}
|
|
328
359
|
|
|
329
|
-
if (indicator) indicator.querySelector('span:last-child')?.remove();
|
|
330
360
|
if (indicator) {
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
361
|
+
const labelEl = indicator.querySelector('.streaming-indicator-label');
|
|
362
|
+
if (labelEl) {
|
|
363
|
+
labelEl.textContent = indicatorText;
|
|
364
|
+
} else {
|
|
365
|
+
const existingLabel = indicator.querySelector('span:last-child');
|
|
366
|
+
if (existingLabel && !existingLabel.classList.contains('animate-spin')) existingLabel.remove();
|
|
367
|
+
const label = document.createElement('span');
|
|
368
|
+
label.className = 'streaming-indicator-label';
|
|
369
|
+
label.textContent = indicatorText;
|
|
370
|
+
indicator.appendChild(label);
|
|
371
|
+
}
|
|
334
372
|
}
|
|
335
373
|
|
|
336
374
|
this.scrollToBottom();
|
|
@@ -339,10 +377,21 @@ class AgentGUIClient {
|
|
|
339
377
|
renderBlockContent(block) {
|
|
340
378
|
if (block.type === 'text' && block.text) {
|
|
341
379
|
const text = block.text;
|
|
342
|
-
if (
|
|
343
|
-
return text
|
|
380
|
+
if (this.isHtmlContent(text)) {
|
|
381
|
+
return `<div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${text}</div>`;
|
|
382
|
+
}
|
|
383
|
+
const parts = this.parseMarkdownCodeBlocks(text);
|
|
384
|
+
if (parts.length === 1 && parts[0].type === 'text') {
|
|
385
|
+
return this.escapeHtml(text);
|
|
344
386
|
}
|
|
345
|
-
return
|
|
387
|
+
return parts.map(part => {
|
|
388
|
+
if (part.type === 'html') {
|
|
389
|
+
return `<div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${part.content}</div>`;
|
|
390
|
+
} else if (part.type === 'code') {
|
|
391
|
+
return this.renderCodeBlock(part.language, part.code);
|
|
392
|
+
}
|
|
393
|
+
return this.escapeHtml(part.content);
|
|
394
|
+
}).join('');
|
|
346
395
|
}
|
|
347
396
|
return this.escapeHtml(JSON.stringify(block));
|
|
348
397
|
}
|
|
@@ -455,10 +504,12 @@ class AgentGUIClient {
|
|
|
455
504
|
}
|
|
456
505
|
}
|
|
457
506
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
507
|
+
isHtmlContent(text) {
|
|
508
|
+
const openTag = /<(?:div|table|section|article|form|ul|ol|dl|nav|header|footer|main|aside|figure|details|summary|h[1-6])\b[^>]*>/i;
|
|
509
|
+
const closeTag = /<\/(?:div|table|section|article|form|ul|ol|dl|nav|header|footer|main|aside|figure|details|summary|h[1-6])>/i;
|
|
510
|
+
return openTag.test(text) && closeTag.test(text);
|
|
511
|
+
}
|
|
512
|
+
|
|
462
513
|
parseMarkdownCodeBlocks(text) {
|
|
463
514
|
const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
|
|
464
515
|
const parts = [];
|
|
@@ -466,14 +517,13 @@ class AgentGUIClient {
|
|
|
466
517
|
let match;
|
|
467
518
|
|
|
468
519
|
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
469
|
-
// Add text before the code block
|
|
470
520
|
if (match.index > lastIndex) {
|
|
521
|
+
const segment = text.substring(lastIndex, match.index);
|
|
471
522
|
parts.push({
|
|
472
|
-
type: 'text',
|
|
473
|
-
content:
|
|
523
|
+
type: this.isHtmlContent(segment) ? 'html' : 'text',
|
|
524
|
+
content: segment
|
|
474
525
|
});
|
|
475
526
|
}
|
|
476
|
-
// Add the code block
|
|
477
527
|
parts.push({
|
|
478
528
|
type: 'code',
|
|
479
529
|
language: match[1] || 'plain',
|
|
@@ -482,17 +532,16 @@ class AgentGUIClient {
|
|
|
482
532
|
lastIndex = codeBlockRegex.lastIndex;
|
|
483
533
|
}
|
|
484
534
|
|
|
485
|
-
// Add remaining text after last code block
|
|
486
535
|
if (lastIndex < text.length) {
|
|
536
|
+
const segment = text.substring(lastIndex);
|
|
487
537
|
parts.push({
|
|
488
|
-
type: 'text',
|
|
489
|
-
content:
|
|
538
|
+
type: this.isHtmlContent(segment) ? 'html' : 'text',
|
|
539
|
+
content: segment
|
|
490
540
|
});
|
|
491
541
|
}
|
|
492
542
|
|
|
493
|
-
// If no code blocks found, return the text as-is
|
|
494
543
|
if (parts.length === 0) {
|
|
495
|
-
return [{ type: 'text', content: text }];
|
|
544
|
+
return [{ type: this.isHtmlContent(text) ? 'html' : 'text', content: text }];
|
|
496
545
|
}
|
|
497
546
|
|
|
498
547
|
return parts;
|
|
@@ -523,16 +572,20 @@ class AgentGUIClient {
|
|
|
523
572
|
*/
|
|
524
573
|
renderMessageContent(content) {
|
|
525
574
|
if (typeof content === 'string') {
|
|
575
|
+
if (this.isHtmlContent(content)) {
|
|
576
|
+
return `<div class="message-text"><div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${content}</div></div>`;
|
|
577
|
+
}
|
|
526
578
|
return `<div class="message-text">${this.escapeHtml(content)}</div>`;
|
|
527
579
|
} else if (content && typeof content === 'object' && content.type === 'claude_execution') {
|
|
528
580
|
let html = '<div class="message-blocks">';
|
|
529
581
|
if (content.blocks && Array.isArray(content.blocks)) {
|
|
530
582
|
content.blocks.forEach(block => {
|
|
531
583
|
if (block.type === 'text') {
|
|
532
|
-
// Parse markdown code blocks from text
|
|
533
584
|
const parts = this.parseMarkdownCodeBlocks(block.text);
|
|
534
585
|
parts.forEach(part => {
|
|
535
|
-
if (part.type === '
|
|
586
|
+
if (part.type === 'html') {
|
|
587
|
+
html += `<div class="message-text"><div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${part.content}</div></div>`;
|
|
588
|
+
} else if (part.type === 'text') {
|
|
536
589
|
html += `<div class="message-text">${this.escapeHtml(part.content)}</div>`;
|
|
537
590
|
} else if (part.type === 'code') {
|
|
538
591
|
html += this.renderCodeBlock(part.language, part.code);
|
|
@@ -555,7 +608,16 @@ class AgentGUIClient {
|
|
|
555
608
|
html += `<div class="message-code"><pre>${this.escapeHtml(block.code)}</pre></div>`;
|
|
556
609
|
}
|
|
557
610
|
} else if (block.type === 'tool_use') {
|
|
558
|
-
|
|
611
|
+
let inputHtml = '';
|
|
612
|
+
if (block.input && Object.keys(block.input).length > 0) {
|
|
613
|
+
const inputStr = JSON.stringify(block.input, null, 2);
|
|
614
|
+
inputHtml = `<details class="tool-input-details"><summary class="tool-input-summary">Input</summary><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></details>`;
|
|
615
|
+
}
|
|
616
|
+
html += `<div class="streaming-block-tool-use"><div class="tool-use-header"><span class="tool-use-icon">⚙</span> <span class="tool-use-name">${this.escapeHtml(block.name || 'unknown')}</span></div>${inputHtml}</div>`;
|
|
617
|
+
} else if (block.type === 'tool_result') {
|
|
618
|
+
const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
619
|
+
const displayContent = content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content;
|
|
620
|
+
html += `<div class="streaming-block-tool-result${block.is_error ? ' tool-result-error' : ''}"><div class="tool-result-header">${block.is_error ? '<span class="tool-result-error-badge">Error</span>' : '<span class="tool-result-ok-badge">Result</span>'}</div><pre class="tool-result-pre">${this.escapeHtml(displayContent)}</pre></div>`;
|
|
559
621
|
}
|
|
560
622
|
});
|
|
561
623
|
}
|
|
@@ -809,18 +871,22 @@ class AgentGUIClient {
|
|
|
809
871
|
return messages.map(msg => {
|
|
810
872
|
let contentHtml = '';
|
|
811
873
|
|
|
812
|
-
// Handle different content types
|
|
813
874
|
if (typeof msg.content === 'string') {
|
|
814
|
-
|
|
875
|
+
if (this.isHtmlContent(msg.content)) {
|
|
876
|
+
contentHtml = `<div class="message-text"><div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${msg.content}</div></div>`;
|
|
877
|
+
} else {
|
|
878
|
+
contentHtml = `<div class="message-text">${this.escapeHtml(msg.content)}</div>`;
|
|
879
|
+
}
|
|
815
880
|
} else if (msg.content && typeof msg.content === 'object' && msg.content.type === 'claude_execution') {
|
|
816
|
-
// Handle Claude execution blocks
|
|
817
881
|
contentHtml = '<div class="message-blocks">';
|
|
818
882
|
if (msg.content.blocks && Array.isArray(msg.content.blocks)) {
|
|
819
883
|
msg.content.blocks.forEach(block => {
|
|
820
884
|
if (block.type === 'text') {
|
|
821
885
|
const parts = this.parseMarkdownCodeBlocks(block.text);
|
|
822
886
|
parts.forEach(part => {
|
|
823
|
-
if (part.type === '
|
|
887
|
+
if (part.type === 'html') {
|
|
888
|
+
contentHtml += `<div class="message-text"><div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${part.content}</div></div>`;
|
|
889
|
+
} else if (part.type === 'text') {
|
|
824
890
|
contentHtml += `<div class="message-text">${this.escapeHtml(part.content)}</div>`;
|
|
825
891
|
} else if (part.type === 'code') {
|
|
826
892
|
contentHtml += this.renderCodeBlock(part.language, part.code);
|
|
@@ -843,7 +909,16 @@ class AgentGUIClient {
|
|
|
843
909
|
contentHtml += `<div class="message-code"><pre>${this.escapeHtml(block.code)}</pre></div>`;
|
|
844
910
|
}
|
|
845
911
|
} else if (block.type === 'tool_use') {
|
|
846
|
-
|
|
912
|
+
let inputHtml = '';
|
|
913
|
+
if (block.input && Object.keys(block.input).length > 0) {
|
|
914
|
+
const inputStr = JSON.stringify(block.input, null, 2);
|
|
915
|
+
inputHtml = `<details class="tool-input-details"><summary class="tool-input-summary">Input</summary><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></details>`;
|
|
916
|
+
}
|
|
917
|
+
contentHtml += `<div class="streaming-block-tool-use"><div class="tool-use-header"><span class="tool-use-icon">⚙</span> <span class="tool-use-name">${this.escapeHtml(block.name || 'unknown')}</span></div>${inputHtml}</div>`;
|
|
918
|
+
} else if (block.type === 'tool_result') {
|
|
919
|
+
const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
920
|
+
const displayContent = content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content;
|
|
921
|
+
contentHtml += `<div class="streaming-block-tool-result${block.is_error ? ' tool-result-error' : ''}"><div class="tool-result-header">${block.is_error ? '<span class="tool-result-error-badge">Error</span>' : '<span class="tool-result-ok-badge">Result</span>'}</div><pre class="tool-result-pre">${this.escapeHtml(displayContent)}</pre></div>`;
|
|
847
922
|
}
|
|
848
923
|
});
|
|
849
924
|
}
|
|
@@ -569,9 +569,12 @@ class StreamingRenderer {
|
|
|
569
569
|
return div;
|
|
570
570
|
}
|
|
571
571
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
572
|
+
isHtmlContent(text) {
|
|
573
|
+
const openTag = /<(?:div|table|section|article|form|ul|ol|dl|nav|header|footer|main|aside|figure|details|summary|h[1-6])\b[^>]*>/i;
|
|
574
|
+
const closeTag = /<\/(?:div|table|section|article|form|ul|ol|dl|nav|header|footer|main|aside|figure|details|summary|h[1-6])>/i;
|
|
575
|
+
return openTag.test(text) && closeTag.test(text);
|
|
576
|
+
}
|
|
577
|
+
|
|
575
578
|
parseMarkdownCodeBlocks(text) {
|
|
576
579
|
const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
|
|
577
580
|
const parts = [];
|
|
@@ -580,18 +583,20 @@ class StreamingRenderer {
|
|
|
580
583
|
|
|
581
584
|
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
582
585
|
if (match.index > lastIndex) {
|
|
583
|
-
|
|
586
|
+
const segment = text.substring(lastIndex, match.index);
|
|
587
|
+
parts.push({ type: this.isHtmlContent(segment) ? 'html' : 'text', content: segment });
|
|
584
588
|
}
|
|
585
589
|
parts.push({ type: 'code', language: match[1] || 'plain', code: match[2] });
|
|
586
590
|
lastIndex = codeBlockRegex.lastIndex;
|
|
587
591
|
}
|
|
588
592
|
|
|
589
593
|
if (lastIndex < text.length) {
|
|
590
|
-
|
|
594
|
+
const segment = text.substring(lastIndex);
|
|
595
|
+
parts.push({ type: this.isHtmlContent(segment) ? 'html' : 'text', content: segment });
|
|
591
596
|
}
|
|
592
597
|
|
|
593
598
|
if (parts.length === 0) {
|
|
594
|
-
return [{ type: 'text', content: text }];
|
|
599
|
+
return [{ type: this.isHtmlContent(text) ? 'html' : 'text', content: text }];
|
|
595
600
|
}
|
|
596
601
|
|
|
597
602
|
return parts;
|
|
@@ -610,7 +615,9 @@ class StreamingRenderer {
|
|
|
610
615
|
const parts = this.parseMarkdownCodeBlocks(text);
|
|
611
616
|
let html = '';
|
|
612
617
|
parts.forEach(part => {
|
|
613
|
-
if (part.type === '
|
|
618
|
+
if (part.type === 'html') {
|
|
619
|
+
html += `<div class="html-content bg-white dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 overflow-x-auto">${part.content}</div>`;
|
|
620
|
+
} else if (part.type === 'text') {
|
|
614
621
|
html += `<p class="text-sm leading-relaxed">${this.escapeHtml(part.content)}</p>`;
|
|
615
622
|
} else if (part.type === 'code') {
|
|
616
623
|
if (part.language.toLowerCase() === 'html') {
|