agentgui 1.0.555 → 1.0.556
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/static/js/client.js +19 -11
- package/static/js/streaming-renderer.js +53 -0
package/package.json
CHANGED
package/static/js/client.js
CHANGED
|
@@ -2637,26 +2637,34 @@ class AgentGUIClient {
|
|
|
2637
2637
|
|
|
2638
2638
|
const blocksEl = messageDiv.querySelector('.message-blocks');
|
|
2639
2639
|
const blockFrag = document.createDocumentFragment();
|
|
2640
|
+
const toolResultBlocks = new Map();
|
|
2641
|
+
|
|
2640
2642
|
sessionChunkList.forEach(chunk => {
|
|
2641
2643
|
if (!chunk.block?.type) return;
|
|
2642
2644
|
if (chunk.block.type === 'tool_result') {
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
lastInFrag.classList.remove('has-success', 'has-error');
|
|
2646
|
-
lastInFrag.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
2647
|
-
const parentIsOpen = lastInFrag.hasAttribute('open');
|
|
2648
|
-
const contextWithParent = { ...chunk, parentIsOpen };
|
|
2649
|
-
const element = this.renderer.renderBlock(chunk.block, contextWithParent, blockFrag);
|
|
2650
|
-
if (element) { lastInFrag.appendChild(element); }
|
|
2651
|
-
return;
|
|
2652
|
-
}
|
|
2645
|
+
toolResultBlocks.set(chunk.id, chunk);
|
|
2646
|
+
return;
|
|
2653
2647
|
}
|
|
2654
|
-
const element = this.renderer.
|
|
2648
|
+
const element = this.renderer.renderBlockHeader(chunk.block, chunk);
|
|
2655
2649
|
if (!element) return;
|
|
2656
2650
|
blockFrag.appendChild(element);
|
|
2657
2651
|
});
|
|
2652
|
+
|
|
2658
2653
|
blocksEl.appendChild(blockFrag);
|
|
2659
2654
|
|
|
2655
|
+
toolResultBlocks.forEach((chunk, chunkId) => {
|
|
2656
|
+
const lastBlock = blocksEl.lastElementChild;
|
|
2657
|
+
if (lastBlock?.classList?.contains('block-type-tool_use')) {
|
|
2658
|
+
lastBlock.classList.remove('has-success', 'has-error');
|
|
2659
|
+
lastBlock.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
2660
|
+
const contextWithParent = { ...chunk, parentIsOpen: lastBlock.hasAttribute('open') };
|
|
2661
|
+
const element = this.renderer.renderBlock(chunk.block, contextWithParent);
|
|
2662
|
+
if (element && element !== blockFrag.lastElementChild) {
|
|
2663
|
+
lastBlock.appendChild(element);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
});
|
|
2667
|
+
|
|
2660
2668
|
if (isCurrentActiveSession) {
|
|
2661
2669
|
const indicatorDiv = document.createElement('div');
|
|
2662
2670
|
indicatorDiv.className = 'streaming-indicator';
|
|
@@ -2154,6 +2154,59 @@ class StreamingRenderer {
|
|
|
2154
2154
|
}
|
|
2155
2155
|
}
|
|
2156
2156
|
|
|
2157
|
+
/**
|
|
2158
|
+
* Render block header with lazy-loading placeholder for body
|
|
2159
|
+
* Returns a <details> element with just the summary, body content deferred
|
|
2160
|
+
*/
|
|
2161
|
+
renderBlockHeader(block, context = {}) {
|
|
2162
|
+
if (!block || !block.type) return null;
|
|
2163
|
+
|
|
2164
|
+
const typeLabel = block.type.charAt(0).toUpperCase() + block.type.slice(1).replace(/_/g, ' ');
|
|
2165
|
+
const summary = document.createElement('summary');
|
|
2166
|
+
summary.style.cursor = 'pointer';
|
|
2167
|
+
summary.style.userSelect = 'none';
|
|
2168
|
+
summary.className = 'block-header-summary';
|
|
2169
|
+
|
|
2170
|
+
let summaryText = typeLabel;
|
|
2171
|
+
if (block.type === 'code' && block.language) {
|
|
2172
|
+
summaryText += ` (${block.language})`;
|
|
2173
|
+
} else if (block.type === 'bash' && block.source) {
|
|
2174
|
+
summaryText += ` - ${block.source}`;
|
|
2175
|
+
} else if (block.type === 'tool_use' && block.name) {
|
|
2176
|
+
summaryText += ` - ${block.name}`;
|
|
2177
|
+
} else if (block.type === 'text' && block.text) {
|
|
2178
|
+
const preview = block.text.substring(0, 60).replace(/\n/g, ' ');
|
|
2179
|
+
summaryText = preview + (block.text.length > 60 ? '...' : '');
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
summary.textContent = summaryText;
|
|
2183
|
+
|
|
2184
|
+
const details = document.createElement('details');
|
|
2185
|
+
details.className = `block-type-${block.type}`;
|
|
2186
|
+
details.setAttribute('data-block-type', block.type);
|
|
2187
|
+
details.setAttribute('data-lazy-load', 'pending');
|
|
2188
|
+
details.appendChild(summary);
|
|
2189
|
+
|
|
2190
|
+
// Attach lazy loader on first open
|
|
2191
|
+
details.addEventListener('toggle', async (e) => {
|
|
2192
|
+
if (details.open && details.getAttribute('data-lazy-load') === 'pending') {
|
|
2193
|
+
details.setAttribute('data-lazy-load', 'loading');
|
|
2194
|
+
try {
|
|
2195
|
+
const body = this.renderBlock(block, context);
|
|
2196
|
+
if (body && body !== summary) {
|
|
2197
|
+
details.appendChild(body);
|
|
2198
|
+
}
|
|
2199
|
+
details.setAttribute('data-lazy-load', 'loaded');
|
|
2200
|
+
} catch (err) {
|
|
2201
|
+
console.error('Failed to lazy-load block:', err);
|
|
2202
|
+
details.setAttribute('data-lazy-load', 'failed');
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
}, { once: false });
|
|
2206
|
+
|
|
2207
|
+
return details;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2157
2210
|
/**
|
|
2158
2211
|
* Cleanup resources
|
|
2159
2212
|
*/
|