agentgui 1.0.150 → 1.0.152

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/static/index.html CHANGED
@@ -7,10 +7,14 @@
7
7
  <title>AgentGUI</title>
8
8
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='20' fill='%233b82f6'/%3E%3Ctext x='50' y='68' font-size='50' font-family='sans-serif' font-weight='bold' fill='white' text-anchor='middle'%3EG%3C/text%3E%3C/svg%3E">
9
9
 
10
+ <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
11
+ <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
10
12
  <link href="https://cdn.jsdelivr.net/npm/rippleui@1.12.1/dist/css/styles.css" rel="stylesheet">
11
- <link href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-dark.css" rel="stylesheet">
12
- <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" rel="stylesheet">
13
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
13
+ <link rel="preload" href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-dark.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
14
+ <noscript><link href="https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-dark.css" rel="stylesheet"></noscript>
15
+ <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
16
+ <noscript><link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" rel="stylesheet"></noscript>
17
+ <script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
14
18
 
15
19
  <style>
16
20
  *, *::before, *::after { box-sizing: border-box; }
@@ -1158,6 +1162,19 @@
1158
1162
  font-size: 0.9rem;
1159
1163
  }
1160
1164
 
1165
+ .block-text + .block-text {
1166
+ margin-top: -0.75rem;
1167
+ padding-top: 0;
1168
+ border-top-left-radius: 0;
1169
+ border-top-right-radius: 0;
1170
+ }
1171
+
1172
+ .block-text:has(+ .block-text) {
1173
+ margin-bottom: 0;
1174
+ border-bottom-left-radius: 0;
1175
+ border-bottom-right-radius: 0;
1176
+ }
1177
+
1161
1178
  .block-code {
1162
1179
  margin-bottom: 0.75rem;
1163
1180
  border-radius: 0.5rem;
@@ -2060,16 +2077,16 @@
2060
2077
  </div>
2061
2078
  </div>
2062
2079
 
2063
- <script src="/gm/js/event-processor.js"></script>
2064
- <script src="/gm/js/streaming-renderer.js"></script>
2065
- <script src="/gm/js/websocket-manager.js"></script>
2066
- <script src="/gm/js/event-filter.js"></script>
2067
- <script src="/gm/js/syntax-highlighter.js"></script>
2068
- <script src="/gm/js/ui-components.js"></script>
2069
- <script src="/gm/js/conversations.js"></script>
2070
- <script src="/gm/js/client.js"></script>
2080
+ <script defer src="/gm/js/event-processor.js"></script>
2081
+ <script defer src="/gm/js/streaming-renderer.js"></script>
2082
+ <script defer src="/gm/js/websocket-manager.js"></script>
2083
+ <script defer src="/gm/js/event-filter.js"></script>
2084
+ <script defer src="/gm/js/syntax-highlighter.js"></script>
2085
+ <script defer src="/gm/js/ui-components.js"></script>
2086
+ <script defer src="/gm/js/conversations.js"></script>
2087
+ <script defer src="/gm/js/client.js"></script>
2071
2088
  <script type="module" src="/gm/js/voice.js"></script>
2072
- <script src="/gm/js/features.js"></script>
2089
+ <script defer src="/gm/js/features.js"></script>
2073
2090
 
2074
2091
  <script>
2075
2092
  const savedTheme = localStorage.getItem('theme') || 'light';
@@ -544,12 +544,13 @@ class AgentGUIClient {
544
544
  }
545
545
 
546
546
  scrollToBottom() {
547
- const scrollContainer = document.getElementById('output-scroll');
548
- if (scrollContainer) {
549
- requestAnimationFrame(() => {
550
- scrollContainer.scrollTop = scrollContainer.scrollHeight;
551
- });
552
- }
547
+ if (this._scrollRafPending) return;
548
+ this._scrollRafPending = true;
549
+ requestAnimationFrame(() => {
550
+ this._scrollRafPending = false;
551
+ const scrollContainer = document.getElementById('output-scroll');
552
+ if (scrollContainer) scrollContainer.scrollTop = scrollContainer.scrollHeight;
553
+ });
553
554
  }
554
555
 
555
556
  handleStreamingError(data) {
@@ -135,12 +135,17 @@ class StreamingRenderer {
135
135
  const lastTime = this.dedupMap.get(key);
136
136
  const now = Date.now();
137
137
 
138
- // Deduplicate within 100ms window
139
138
  if (lastTime && (now - lastTime) < 100) {
140
139
  return true;
141
140
  }
142
141
 
143
142
  this.dedupMap.set(key, now);
143
+ if (this.dedupMap.size > 5000) {
144
+ const cutoff = now - 1000;
145
+ for (const [k, t] of this.dedupMap) {
146
+ if (t < cutoff) this.dedupMap.delete(k);
147
+ }
148
+ }
144
149
  return false;
145
150
  }
146
151
 
@@ -358,17 +363,25 @@ class StreamingRenderer {
358
363
  * Render text block with semantic HTML
359
364
  */
360
365
  renderBlockText(block, context) {
361
- const div = document.createElement('div');
362
- div.className = 'block-text';
363
-
364
366
  const text = block.text || '';
365
- if (this.containsHtmlTags(text)) {
366
- div.innerHTML = this.sanitizeHtml(text);
367
- div.classList.add('html-content');
368
- } else {
369
- div.innerHTML = this.parseAndRenderMarkdown(text);
367
+ const isHtml = this.containsHtmlTags(text);
368
+ const cached = this.renderCache.get(text);
369
+ const html = cached || (isHtml ? this.sanitizeHtml(text) : this.parseAndRenderMarkdown(text));
370
+
371
+ if (!cached && this.renderCache.size < 2000) {
372
+ this.renderCache.set(text, html);
373
+ }
374
+
375
+ const lastChild = this.outputContainer && this.outputContainer.lastElementChild;
376
+ if (lastChild && lastChild.classList.contains('block-text') && !isHtml && !lastChild.classList.contains('html-content')) {
377
+ lastChild.innerHTML += html;
378
+ return null;
370
379
  }
371
380
 
381
+ const div = document.createElement('div');
382
+ div.className = 'block-text';
383
+ if (isHtml) div.classList.add('html-content');
384
+ div.innerHTML = html;
372
385
  return div;
373
386
  }
374
387
 
@@ -447,11 +460,9 @@ class StreamingRenderer {
447
460
 
448
461
  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";
449
462
  const codeContainer = document.createElement('div');
463
+ codeContainer.innerHTML = `<pre style="${preStyle}"><code>${this.escapeHtml(code)}</code></pre>`;
450
464
  if (typeof hljs !== 'undefined') {
451
- const result = hljs.highlightAuto(code);
452
- codeContainer.innerHTML = `<pre style="${preStyle}"><code class="hljs">${result.value}</code></pre>`;
453
- } else {
454
- codeContainer.innerHTML = `<pre style="${preStyle}"><code>${this.escapeHtml(code)}</code></pre>`;
465
+ this.lazyHighlight(codeContainer, code);
455
466
  }
456
467
 
457
468
  details.appendChild(summary);
@@ -1785,13 +1796,39 @@ class StreamingRenderer {
1785
1796
  * Auto-scroll to bottom of container
1786
1797
  */
1787
1798
  autoScroll() {
1788
- if (this.scrollContainer) {
1789
- try {
1790
- this.scrollContainer.scrollTop = this.scrollContainer.scrollHeight;
1791
- } catch (e) {
1792
- // Ignore scroll errors
1799
+ if (this._scrollRafPending) return;
1800
+ this._scrollRafPending = true;
1801
+ requestAnimationFrame(() => {
1802
+ this._scrollRafPending = false;
1803
+ if (this.scrollContainer) {
1804
+ try { this.scrollContainer.scrollTop = this.scrollContainer.scrollHeight; } catch (_) {}
1793
1805
  }
1806
+ });
1807
+ }
1808
+
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' });
1794
1829
  }
1830
+ container._rawCode = code;
1831
+ this._hlObserver.observe(container);
1795
1832
  }
1796
1833
 
1797
1834
  updateVirtualScroll() {
@@ -137,26 +137,23 @@ class WebSocketManager {
137
137
  */
138
138
  onMessage(event) {
139
139
  try {
140
- const data = JSON.parse(event.data);
141
- this.stats.totalMessagesReceived++;
142
-
143
- // Handle pong response
144
- if (data.type === 'pong') {
145
- const requestId = data.requestId;
146
- if (requestId && this.requestMap.has(requestId)) {
147
- const request = this.requestMap.get(requestId);
148
- request.resolve({ latency: Date.now() - request.sentTime });
149
- this.requestMap.delete(requestId);
140
+ const parsed = JSON.parse(event.data);
141
+ const messages = Array.isArray(parsed) ? parsed : [parsed];
142
+ this.stats.totalMessagesReceived += messages.length;
143
+
144
+ for (const data of messages) {
145
+ if (data.type === 'pong') {
146
+ const requestId = data.requestId;
147
+ if (requestId && this.requestMap.has(requestId)) {
148
+ const request = this.requestMap.get(requestId);
149
+ request.resolve({ latency: Date.now() - request.sentTime });
150
+ this.requestMap.delete(requestId);
151
+ }
152
+ continue;
150
153
  }
151
- return;
152
- }
153
-
154
- // Route message to listeners
155
- this.emit('message', data);
156
154
 
157
- // Route by type
158
- if (data.type) {
159
- this.emit(`message:${data.type}`, data);
155
+ this.emit('message', data);
156
+ if (data.type) this.emit(`message:${data.type}`, data);
160
157
  }
161
158
  } catch (error) {
162
159
  console.error('WebSocket message parse error:', error);
@@ -234,15 +231,7 @@ class WebSocketManager {
234
231
  * Start heartbeat/keepalive
235
232
  */
236
233
  startHeartbeat() {
237
- if (this.heartbeatTimer) {
238
- clearTimeout(this.heartbeatTimer);
239
- }
240
-
241
- this.heartbeatTimer = setInterval(() => {
242
- if (this.isConnected) {
243
- this.ping();
244
- }
245
- }, this.config.heartbeatInterval);
234
+ if (this.heartbeatTimer) clearTimeout(this.heartbeatTimer);
246
235
  }
247
236
 
248
237
  /**