agentgui 1.0.152 → 1.0.154

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.
@@ -69,6 +69,7 @@ class StreamingRenderer {
69
69
  this.setupDOMObserver();
70
70
  this.setupResizeObserver();
71
71
  this.setupScrollOptimization();
72
+ StreamingRenderer._setupGlobalLazyHL();
72
73
  return this;
73
74
  }
74
75
 
@@ -460,10 +461,7 @@ class StreamingRenderer {
460
461
 
461
462
  const preStyle = "background:#1e293b;padding:1rem;border-radius:0 0 0.375rem 0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155;border-top:none;margin:0";
462
463
  const codeContainer = document.createElement('div');
463
- codeContainer.innerHTML = `<pre style="${preStyle}"><code>${this.escapeHtml(code)}</code></pre>`;
464
- if (typeof hljs !== 'undefined') {
465
- this.lazyHighlight(codeContainer, code);
466
- }
464
+ codeContainer.innerHTML = `<pre style="${preStyle}"><code class="lazy-hl">${this.escapeHtml(code)}</code></pre>`;
467
465
 
468
466
  details.appendChild(summary);
469
467
  details.appendChild(codeContainer);
@@ -702,40 +700,26 @@ class StreamingRenderer {
702
700
  renderBlockToolUse(block, context) {
703
701
  const toolName = block.name || 'unknown';
704
702
  const input = block.input || {};
705
- const shouldFold = toolName.startsWith('mcp__') || toolName === 'Edit';
706
-
707
- if (shouldFold) {
708
- const details = document.createElement('details');
709
- details.className = 'block-tool-use folded-tool';
710
- const summary = document.createElement('summary');
711
- summary.className = 'folded-tool-bar';
712
- const displayName = this.getToolUseDisplayName(toolName);
713
- const titleInfo = this.getToolUseTitle(toolName, input);
714
- summary.innerHTML = `
715
- <span class="folded-tool-icon">${this.getToolIcon(toolName)}</span>
716
- <span class="folded-tool-name">${this.escapeHtml(displayName)}</span>
717
- ${titleInfo ? `<span class="folded-tool-desc">${this.escapeHtml(titleInfo)}</span>` : ''}
718
- `;
719
- details.appendChild(summary);
720
- if (Object.keys(input).length > 0) {
721
- const paramsDiv = document.createElement('div');
722
- paramsDiv.className = 'folded-tool-body';
723
- paramsDiv.innerHTML = this.renderSmartParams(toolName, input);
724
- details.appendChild(paramsDiv);
725
- }
726
- return details;
727
- }
728
703
 
729
- const div = document.createElement('div');
730
- div.className = 'block-tool-use';
731
- div.innerHTML = `
732
- <div class="tool-header">
733
- <span class="tool-icon">${this.getToolIcon(toolName)}</span>
734
- <span class="tool-name"><code>${this.escapeHtml(toolName)}</code></span>
735
- </div>
736
- ${Object.keys(input).length > 0 ? this.renderSmartParams(toolName, input) : ''}
704
+ const details = document.createElement('details');
705
+ details.className = 'block-tool-use folded-tool';
706
+ const summary = document.createElement('summary');
707
+ summary.className = 'folded-tool-bar';
708
+ const displayName = this.getToolUseDisplayName(toolName);
709
+ const titleInfo = this.getToolUseTitle(toolName, input);
710
+ summary.innerHTML = `
711
+ <span class="folded-tool-icon">${this.getToolIcon(toolName)}</span>
712
+ <span class="folded-tool-name">${this.escapeHtml(displayName)}</span>
713
+ ${titleInfo ? `<span class="folded-tool-desc">${this.escapeHtml(titleInfo)}</span>` : ''}
737
714
  `;
738
- return div;
715
+ details.appendChild(summary);
716
+ if (Object.keys(input).length > 0) {
717
+ const paramsDiv = document.createElement('div');
718
+ paramsDiv.className = 'folded-tool-body';
719
+ paramsDiv.innerHTML = this.renderSmartParams(toolName, input);
720
+ details.appendChild(paramsDiv);
721
+ }
722
+ return details;
739
723
  }
740
724
 
741
725
  /**
@@ -820,7 +804,7 @@ class StreamingRenderer {
820
804
  */
821
805
  static renderSmartContentHTML(contentStr, escapeHtml) {
822
806
  const trimmed = contentStr.trim();
823
- const esc = escapeHtml || (t => t.replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c])));
807
+ const esc = escapeHtml || window._escHtml;
824
808
 
825
809
  if (trimmed.startsWith('data:image/')) {
826
810
  return `<div style="padding:0.5rem"><img src="${esc(trimmed)}" style="max-width:100%;max-height:24rem;border-radius:0.375rem" loading="lazy"></div>`;
@@ -1071,18 +1055,33 @@ class StreamingRenderer {
1071
1055
  static renderCodeWithHighlight(code, esc) {
1072
1056
  const preStyle = "background:#1e293b;padding:1rem;border-radius:0 0 0.375rem 0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:#e2e8f0;border:1px solid #334155;border-top:none;margin:0";
1073
1057
  const lineCount = code.split('\n').length;
1074
- const lang = (typeof hljs !== 'undefined') ? (hljs.highlightAuto(code).language || 'code') : 'code';
1075
- const summaryLabel = `${lang} - ${lineCount} line${lineCount !== 1 ? 's' : ''}`;
1076
- let codeHtml;
1077
- if (typeof hljs !== 'undefined') {
1078
- const result = hljs.highlightAuto(code);
1079
- codeHtml = `<pre style="${preStyle}"><code class="hljs">${result.value}</code></pre>`;
1080
- } else {
1081
- codeHtml = `<pre style="${preStyle}">${esc(code)}</pre>`;
1082
- }
1058
+ const summaryLabel = `code - ${lineCount} line${lineCount !== 1 ? 's' : ''}`;
1059
+ const codeHtml = `<pre style="${preStyle}"><code class="lazy-hl">${esc(code)}</code></pre>`;
1083
1060
  return `<details class="collapsible-code"><summary class="collapsible-code-summary">${summaryLabel}</summary>${codeHtml}</details>`;
1084
1061
  }
1085
1062
 
1063
+ static _setupGlobalLazyHL() {
1064
+ if (StreamingRenderer._lazyHLSetup) return;
1065
+ StreamingRenderer._lazyHLSetup = true;
1066
+ const root = document.getElementById('output-scroll') || document.body;
1067
+ root.addEventListener('toggle', (e) => {
1068
+ const details = e.target;
1069
+ if (!details.open || details.tagName !== 'DETAILS') return;
1070
+ const codeEls = details.querySelectorAll('code.lazy-hl');
1071
+ if (codeEls.length === 0) return;
1072
+ if (typeof hljs === 'undefined') return;
1073
+ for (const el of codeEls) {
1074
+ try {
1075
+ const raw = el.textContent;
1076
+ const result = hljs.highlightAuto(raw);
1077
+ el.classList.remove('lazy-hl');
1078
+ el.classList.add('hljs');
1079
+ el.innerHTML = result.value;
1080
+ } catch (_) {}
1081
+ }
1082
+ }, true);
1083
+ }
1084
+
1086
1085
  static getToolDisplayName(toolName) {
1087
1086
  const normalized = toolName.replace(/^mcp__[^_]+__/, '');
1088
1087
  const knownTools = ['Read','Write','Edit','Bash','Glob','Grep','WebFetch','WebSearch','TodoWrite','Task','NotebookEdit'];
@@ -1439,64 +1438,57 @@ class StreamingRenderer {
1439
1438
  * Render file read event
1440
1439
  */
1441
1440
  renderFileRead(event) {
1442
- const div = document.createElement('div');
1443
- div.className = 'event-file-read card mb-3 p-4';
1444
- div.dataset.eventId = event.id || '';
1445
- div.dataset.eventType = 'file_read';
1446
-
1447
1441
  const fileName = event.path ? event.path.split('/').pop() : 'unknown';
1448
- const size = event.size || 0;
1449
- const sizeStr = this.formatFileSize(size);
1450
-
1451
- div.innerHTML = `
1452
- <div class="flex items-start justify-between gap-3 mb-3">
1453
- <div class="flex items-center gap-2 flex-1">
1454
- <svg class="w-4 h-4 text-primary flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
1455
- <path d="M5.5 13a3 3 0 01.369-1.618l1.83-1.83a3 3 0 015.604 0l.83 1.83A3 3 0 0113.5 13H11V9.413l1.293 1.293a1 1 0 001.414-1.414l-3-3a1 1 0 00-1.414 0l-3 3a1 1 0 001.414 1.414L9 9.414V13H5.5z"></path>
1456
- </svg>
1457
- <div class="flex-1 min-w-0">
1458
- <h4 class="font-semibold text-sm truncate">${this.escapeHtml(fileName)}</h4>
1459
- <p class="text-xs text-secondary truncate" title="${this.escapeHtml(event.path || '')}">${this.escapeHtml(event.path || '')}</p>
1460
- </div>
1461
- </div>
1462
- <span class="badge badge-sm flex-shrink-0">${this.escapeHtml(sizeStr)}</span>
1463
- </div>
1464
- ${event.content ? `
1465
- <pre class="bg-gray-50 dark:bg-gray-900 p-3 rounded border text-xs overflow-x-auto"><code>${this.escapeHtml(this.truncateContent(event.content, 500))}</code></pre>
1466
- ` : ''}
1442
+ const details = document.createElement('details');
1443
+ details.className = 'block-tool-use folded-tool';
1444
+ details.dataset.eventId = event.id || '';
1445
+ details.dataset.eventType = 'file_read';
1446
+ const summary = document.createElement('summary');
1447
+ summary.className = 'folded-tool-bar';
1448
+ summary.innerHTML = `
1449
+ <span class="folded-tool-icon">${this.getToolIcon('Read')}</span>
1450
+ <span class="folded-tool-name">Read</span>
1451
+ <span class="folded-tool-desc">${this.escapeHtml(fileName)}</span>
1467
1452
  `;
1468
- return div;
1453
+ details.appendChild(summary);
1454
+ if (event.path || event.content) {
1455
+ const body = document.createElement('div');
1456
+ body.className = 'folded-tool-body';
1457
+ let html = '';
1458
+ if (event.path) html += this.renderFilePath(event.path);
1459
+ if (event.content) {
1460
+ html += `<pre style="background:#1e293b;padding:0.75rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem;line-height:1.5;color:#e2e8f0;margin:0.5rem 0 0 0"><code class="lazy-hl">${this.escapeHtml(this.truncateContent(event.content, 2000))}</code></pre>`;
1461
+ }
1462
+ body.innerHTML = html;
1463
+ details.appendChild(body);
1464
+ }
1465
+ return details;
1469
1466
  }
1470
1467
 
1471
1468
  /**
1472
1469
  * Render file write event
1473
1470
  */
1474
1471
  renderFileWrite(event) {
1475
- const div = document.createElement('div');
1476
- div.className = 'event-file-write card mb-3 p-4 border-l-4 border-yellow-500';
1477
- div.dataset.eventId = event.id || '';
1478
- div.dataset.eventType = 'file_write';
1479
-
1480
1472
  const fileName = event.path ? event.path.split('/').pop() : 'unknown';
1481
- const size = event.size || 0;
1482
- const sizeStr = this.formatFileSize(size);
1483
-
1484
- div.innerHTML = `
1485
- <div class="flex items-start justify-between gap-3 mb-3">
1486
- <div class="flex items-center gap-2 flex-1">
1487
- <svg class="w-4 h-4 text-yellow-600 dark:text-yellow-400 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
1488
- <path d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a1 1 0 110 2h-3a1 1 0 01-1-1v-2a1 1 0 00-1-1H9a1 1 0 00-1 1v2a1 1 0 01-1 1H4a1 1 0 110-2V4z"></path>
1489
- </svg>
1490
- <div class="flex-1 min-w-0">
1491
- <h4 class="font-semibold text-sm truncate">${this.escapeHtml(fileName)}</h4>
1492
- <p class="text-xs text-secondary truncate" title="${this.escapeHtml(event.path || '')}">${this.escapeHtml(event.path || '')}</p>
1493
- </div>
1494
- </div>
1495
- <span class="badge badge-sm bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200 flex-shrink-0">Written</span>
1496
- </div>
1497
- <span class="text-xs text-secondary">${this.escapeHtml(sizeStr)}</span>
1473
+ const details = document.createElement('details');
1474
+ details.className = 'block-tool-use folded-tool';
1475
+ details.dataset.eventId = event.id || '';
1476
+ details.dataset.eventType = 'file_write';
1477
+ const summary = document.createElement('summary');
1478
+ summary.className = 'folded-tool-bar';
1479
+ summary.innerHTML = `
1480
+ <span class="folded-tool-icon">${this.getToolIcon('Write')}</span>
1481
+ <span class="folded-tool-name">Write</span>
1482
+ <span class="folded-tool-desc">${this.escapeHtml(fileName)}</span>
1498
1483
  `;
1499
- return div;
1484
+ details.appendChild(summary);
1485
+ if (event.path) {
1486
+ const body = document.createElement('div');
1487
+ body.className = 'folded-tool-body';
1488
+ body.innerHTML = this.renderFilePath(event.path);
1489
+ details.appendChild(body);
1490
+ }
1491
+ return details;
1500
1492
  }
1501
1493
 
1502
1494
  /**
@@ -1536,57 +1528,64 @@ class StreamingRenderer {
1536
1528
  * Render command execution event
1537
1529
  */
1538
1530
  renderCommand(event) {
1539
- const div = document.createElement('div');
1540
- div.className = 'event-command card mb-3 p-4 font-mono text-sm';
1541
- div.dataset.eventId = event.id || '';
1542
- div.dataset.eventType = 'command_execute';
1543
-
1544
1531
  const command = event.command || '';
1545
1532
  const output = event.output || '';
1546
1533
  const exitCode = event.exitCode !== undefined ? event.exitCode : null;
1534
+ const cmdPreview = command.length > 60 ? command.substring(0, 57) + '...' : command;
1547
1535
 
1548
- div.innerHTML = `
1549
- <div class="bg-gray-900 text-gray-100 p-3 rounded mb-2 overflow-x-auto">
1550
- <div class="text-green-400">$ ${this.escapeHtml(command)}</div>
1551
- </div>
1552
- ${output ? `
1553
- <div class="bg-gray-50 dark:bg-gray-900 p-3 rounded border text-xs overflow-x-auto">
1554
- <pre><code>${this.escapeHtml(this.truncateContent(output, 500))}</code></pre>
1555
- </div>
1556
- ` : ''}
1557
- ${exitCode !== null ? `
1558
- <div class="text-xs mt-2 ${exitCode === 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}">
1559
- Exit code: ${exitCode}
1560
- </div>
1561
- ` : ''}
1536
+ const details = document.createElement('details');
1537
+ details.className = 'block-tool-use folded-tool';
1538
+ details.dataset.eventId = event.id || '';
1539
+ details.dataset.eventType = 'command_execute';
1540
+ const summary = document.createElement('summary');
1541
+ summary.className = 'folded-tool-bar';
1542
+ summary.innerHTML = `
1543
+ <span class="folded-tool-icon">${this.getToolIcon('Bash')}</span>
1544
+ <span class="folded-tool-name">Bash</span>
1545
+ <span class="folded-tool-desc">${this.escapeHtml(cmdPreview)}</span>
1562
1546
  `;
1563
- return div;
1547
+ details.appendChild(summary);
1548
+
1549
+ const body = document.createElement('div');
1550
+ body.className = 'folded-tool-body';
1551
+ let html = `<div class="tool-param-command"><span class="prompt-char">$</span><span class="command-text">${this.escapeHtml(command)}</span></div>`;
1552
+ if (output) {
1553
+ html += `<pre style="background:#1e293b;padding:0.75rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem;line-height:1.5;color:#e2e8f0;margin:0.5rem 0 0 0"><code class="lazy-hl">${this.escapeHtml(this.truncateContent(output, 2000))}</code></pre>`;
1554
+ }
1555
+ if (exitCode !== null && exitCode !== 0) {
1556
+ html += `<div style="margin-top:0.375rem;font-size:0.75rem;color:#ef4444;font-weight:600">Exit code: ${exitCode}</div>`;
1557
+ }
1558
+ body.innerHTML = html;
1559
+ details.appendChild(body);
1560
+ return details;
1564
1561
  }
1565
1562
 
1566
1563
  /**
1567
1564
  * Render error event
1568
1565
  */
1569
1566
  renderError(event) {
1570
- const div = document.createElement('div');
1571
- div.className = 'event-error card mb-3 p-4 bg-red-50 dark:bg-red-900 border-l-4 border-red-500';
1572
- div.dataset.eventId = event.id || '';
1573
- div.dataset.eventType = 'error';
1574
-
1575
1567
  const message = event.message || event.error || 'Unknown error';
1576
1568
  const severity = event.severity || 'error';
1569
+ const msgPreview = message.length > 80 ? message.substring(0, 77) + '...' : message;
1577
1570
 
1578
- div.innerHTML = `
1579
- <div class="flex items-start gap-3">
1580
- <svg class="w-5 h-5 text-red-600 dark:text-red-400 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
1581
- <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path>
1582
- </svg>
1583
- <div class="flex-1">
1584
- <h4 class="font-semibold text-red-900 dark:text-red-200">Error: ${this.escapeHtml(severity)}</h4>
1585
- <p class="text-sm text-red-800 dark:text-red-300 mt-1">${this.escapeHtml(message)}</p>
1586
- </div>
1587
- </div>
1571
+ const details = document.createElement('details');
1572
+ details.className = 'folded-tool folded-tool-error';
1573
+ details.dataset.eventId = event.id || '';
1574
+ details.dataset.eventType = 'error';
1575
+ const summary = document.createElement('summary');
1576
+ summary.className = 'folded-tool-bar';
1577
+ summary.innerHTML = `
1578
+ <span class="folded-tool-icon"><svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/></svg></span>
1579
+ <span class="folded-tool-name">Error</span>
1580
+ <span class="folded-tool-desc">${this.escapeHtml(msgPreview)}</span>
1588
1581
  `;
1589
- return div;
1582
+ details.appendChild(summary);
1583
+
1584
+ const body = document.createElement('div');
1585
+ body.className = 'folded-tool-body';
1586
+ body.innerHTML = `<div style="font-size:0.8rem;white-space:pre-wrap;word-break:break-word;line-height:1.5">${this.escapeHtml(message)}</div>`;
1587
+ details.appendChild(body);
1588
+ return details;
1590
1589
  }
1591
1590
 
1592
1591
  isHtmlContent(text) {
@@ -1806,31 +1805,6 @@ class StreamingRenderer {
1806
1805
  });
1807
1806
  }
1808
1807
 
1809
- lazyHighlight(container, code) {
1810
- if (!this._hlObserver) {
1811
- this._hlObserver = new IntersectionObserver((entries) => {
1812
- for (const entry of entries) {
1813
- if (!entry.isIntersecting) continue;
1814
- const el = entry.target;
1815
- const raw = el._rawCode;
1816
- if (!raw) continue;
1817
- this._hlObserver.unobserve(el);
1818
- try {
1819
- const codeEl = el.querySelector('code');
1820
- if (codeEl && typeof hljs !== 'undefined') {
1821
- const result = hljs.highlightAuto(raw);
1822
- codeEl.classList.add('hljs');
1823
- codeEl.innerHTML = result.value;
1824
- }
1825
- } catch (_) {}
1826
- delete el._rawCode;
1827
- }
1828
- }, { rootMargin: '200px' });
1829
- }
1830
- container._rawCode = code;
1831
- this._hlObserver.observe(container);
1832
- }
1833
-
1834
1808
  updateVirtualScroll() {
1835
1809
  }
1836
1810
 
@@ -1845,9 +1819,7 @@ class StreamingRenderer {
1845
1819
  * HTML escape utility
1846
1820
  */
1847
1821
  escapeHtml(text) {
1848
- const div = document.createElement('div');
1849
- div.textContent = text;
1850
- return div.innerHTML;
1822
+ return window._escHtml(text);
1851
1823
  }
1852
1824
 
1853
1825
  /**
@@ -259,9 +259,7 @@ class SyntaxHighlighter {
259
259
  * HTML escape utility
260
260
  */
261
261
  escapeHtml(text) {
262
- const div = document.createElement('div');
263
- div.textContent = text;
264
- return div.innerHTML;
262
+ return window._escHtml(text);
265
263
  }
266
264
  }
267
265
 
@@ -396,9 +396,7 @@ class UIComponents {
396
396
  * HTML escape utility
397
397
  */
398
398
  static escapeHtml(text) {
399
- const div = document.createElement('div');
400
- div.textContent = text;
401
- return div.innerHTML;
399
+ return window._escHtml(text);
402
400
  }
403
401
 
404
402
  /**