agentgui 1.0.554 → 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 +36 -12
- package/static/js/streaming-renderer.js +53 -0
package/package.json
CHANGED
package/static/js/client.js
CHANGED
|
@@ -1179,9 +1179,11 @@ class AgentGUIClient {
|
|
|
1179
1179
|
outputEl.appendChild(queueEl);
|
|
1180
1180
|
}
|
|
1181
1181
|
|
|
1182
|
+
const isStreaming = this.state.streamingConversations.has(conversationId);
|
|
1182
1183
|
queueEl.innerHTML = queue.map((q, i) => `
|
|
1183
1184
|
<div class="queue-item" data-message-id="${q.messageId}" style="padding:0.5rem 1rem;margin:0.5rem 0;border-radius:0.375rem;background:var(--color-warning);color:#000;font-size:0.875rem;display:flex;align-items:center;gap:0.5rem;">
|
|
1184
1185
|
<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${i + 1}. ${this.escapeHtml(q.content)}</span>
|
|
1186
|
+
${isStreaming ? `<button class="queue-steer-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:#06b6d4;border:1px solid #0891b2;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;color:#fff;">Steer</button>` : ''}
|
|
1185
1187
|
<button class="queue-edit-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:transparent;border:1px solid #000;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;">Edit</button>
|
|
1186
1188
|
<button class="queue-delete-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:transparent;border:1px solid #000;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;">Delete</button>
|
|
1187
1189
|
</div>
|
|
@@ -1190,7 +1192,21 @@ class AgentGUIClient {
|
|
|
1190
1192
|
if (!queueEl._listenersAttached) {
|
|
1191
1193
|
queueEl._listenersAttached = true;
|
|
1192
1194
|
queueEl.addEventListener('click', async (e) => {
|
|
1193
|
-
if (e.target.classList.contains('queue-
|
|
1195
|
+
if (e.target.classList.contains('queue-steer-btn')) {
|
|
1196
|
+
const index = parseInt(e.target.dataset.index);
|
|
1197
|
+
const q = queue[index];
|
|
1198
|
+
try {
|
|
1199
|
+
const data = await window.wsClient.rpc('conv.steer', { id: conversationId, content: q.content });
|
|
1200
|
+
console.log('Steer response:', data);
|
|
1201
|
+
if (data.ok && data.steered) {
|
|
1202
|
+
// Remove from queue after successful steer
|
|
1203
|
+
await window.wsClient.rpc('q.del', { id: conversationId, messageId: q.messageId });
|
|
1204
|
+
}
|
|
1205
|
+
} catch (err) {
|
|
1206
|
+
console.error('Failed to steer:', err);
|
|
1207
|
+
this.showError('Failed to steer: ' + err.message);
|
|
1208
|
+
}
|
|
1209
|
+
} else if (e.target.classList.contains('queue-delete-btn')) {
|
|
1194
1210
|
const index = parseInt(e.target.dataset.index);
|
|
1195
1211
|
const msgId = queue[index].messageId;
|
|
1196
1212
|
if (await window.UIDialog.confirm('Delete this queued message?', 'Delete Message')) {
|
|
@@ -2621,26 +2637,34 @@ class AgentGUIClient {
|
|
|
2621
2637
|
|
|
2622
2638
|
const blocksEl = messageDiv.querySelector('.message-blocks');
|
|
2623
2639
|
const blockFrag = document.createDocumentFragment();
|
|
2640
|
+
const toolResultBlocks = new Map();
|
|
2641
|
+
|
|
2624
2642
|
sessionChunkList.forEach(chunk => {
|
|
2625
2643
|
if (!chunk.block?.type) return;
|
|
2626
2644
|
if (chunk.block.type === 'tool_result') {
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
lastInFrag.classList.remove('has-success', 'has-error');
|
|
2630
|
-
lastInFrag.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
2631
|
-
const parentIsOpen = lastInFrag.hasAttribute('open');
|
|
2632
|
-
const contextWithParent = { ...chunk, parentIsOpen };
|
|
2633
|
-
const element = this.renderer.renderBlock(chunk.block, contextWithParent, blockFrag);
|
|
2634
|
-
if (element) { lastInFrag.appendChild(element); }
|
|
2635
|
-
return;
|
|
2636
|
-
}
|
|
2645
|
+
toolResultBlocks.set(chunk.id, chunk);
|
|
2646
|
+
return;
|
|
2637
2647
|
}
|
|
2638
|
-
const element = this.renderer.
|
|
2648
|
+
const element = this.renderer.renderBlockHeader(chunk.block, chunk);
|
|
2639
2649
|
if (!element) return;
|
|
2640
2650
|
blockFrag.appendChild(element);
|
|
2641
2651
|
});
|
|
2652
|
+
|
|
2642
2653
|
blocksEl.appendChild(blockFrag);
|
|
2643
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
|
+
|
|
2644
2668
|
if (isCurrentActiveSession) {
|
|
2645
2669
|
const indicatorDiv = document.createElement('div');
|
|
2646
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
|
*/
|