agentgui 1.0.930 → 1.0.932
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/AGENTS.md +35 -12
- package/database.js +31 -2
- package/lib/http-handler.js +11 -25
- package/lib/routes-registry.js +4 -48
- package/lib/server-startup.js +3 -11
- package/lib/ws-setup.js +2 -1
- package/package.json +3 -3
- package/server.js +7 -1
- package/site/app/js/app.js +64 -68
- package/site/app/js/backend.js +1 -1
- package/static/lib/xstate.umd.min.js +1 -1
- package/lib/db-queries-chunks.js +0 -195
- package/lib/db-queries-chunks2.js +0 -82
- package/lib/db-queries-cleanup.js +0 -74
- package/lib/db-queries-del.js +0 -141
- package/lib/db-queries-events.js +0 -68
- package/lib/db-queries-import.js +0 -133
- package/lib/db-queries-messages.js +0 -102
- package/lib/db-queries-sessions.js +0 -112
- package/lib/db-queries-streams.js +0 -100
- package/lib/db-queries.js +0 -89
- package/lib/jsonl-parser.js +0 -190
- package/lib/jsonl-watcher.js +0 -64
- package/lib/routes-agent-actions.js +0 -61
- package/lib/routes-auth-config.js +0 -30
- package/lib/routes-conversations.js +0 -96
- package/lib/routes-debug.js +0 -119
- package/lib/routes-messages.js +0 -139
- package/lib/routes-runs.js +0 -156
- package/lib/routes-scripts.js +0 -135
- package/lib/routes-sessions.js +0 -144
- package/lib/routes-threads.js +0 -100
- package/lib/routes-util.js +0 -110
- package/lib/ws-handlers-conv.js +0 -138
- package/lib/ws-handlers-conv2.js +0 -169
- package/lib/ws-handlers-msg.js +0 -121
- package/lib/ws-handlers-queue.js +0 -56
- package/lib/ws-handlers-run.js +0 -182
- package/lib/ws-handlers-scripts.js +0 -66
- package/lib/ws-handlers-session.js +0 -105
- package/lib/ws-handlers-session2.js +0 -85
- package/lib/ws-legacy-handlers.js +0 -51
- package/static/app.js +0 -261
- package/static/css/app-shell.css +0 -419
- package/static/css/brand-bible.css +0 -591
- package/static/css/colors_and_type.css +0 -568
- package/static/css/gmail-skin.css +0 -663
- package/static/css/main.css +0 -4015
- package/static/css/tools-popup.css +0 -472
- package/static/index.html +0 -418
- package/static/js/agent-auth.js +0 -146
- package/static/js/app-shortcuts.js +0 -30
- package/static/js/audio-recorder-processor.js +0 -18
- package/static/js/client-agents.js +0 -155
- package/static/js/client-cache.js +0 -171
- package/static/js/client-conv.js +0 -198
- package/static/js/client-events.js +0 -164
- package/static/js/client-exec.js +0 -160
- package/static/js/client-helpers.js +0 -199
- package/static/js/client-load.js +0 -175
- package/static/js/client-render.js +0 -132
- package/static/js/client-scroll.js +0 -178
- package/static/js/client-status.js +0 -167
- package/static/js/client-streaming.js +0 -117
- package/static/js/client-streaming2.js +0 -116
- package/static/js/client-streaming3.js +0 -153
- package/static/js/client-streaming4.js +0 -194
- package/static/js/client-ui-controls.js +0 -170
- package/static/js/client-ui.js +0 -128
- package/static/js/client-ui2.js +0 -160
- package/static/js/client-url.js +0 -93
- package/static/js/client-utils.js +0 -174
- package/static/js/client-ws-msg.js +0 -88
- package/static/js/client-ws.js +0 -161
- package/static/js/client.js +0 -145
- package/static/js/codec.js +0 -4
- package/static/js/conv-list-machine.js +0 -145
- package/static/js/conv-list-renderer.js +0 -198
- package/static/js/conv-machine.js +0 -110
- package/static/js/conv-sidebar-actions.js +0 -188
- package/static/js/conv-sidebar-clone.js +0 -91
- package/static/js/conversations.js +0 -116
- package/static/js/dialogs-types.js +0 -111
- package/static/js/dialogs.js +0 -53
- package/static/js/event-filter-config.js +0 -36
- package/static/js/event-processor.js +0 -181
- package/static/js/features.js +0 -187
- package/static/js/image-loader-element.js +0 -76
- package/static/js/image-loader.js +0 -146
- package/static/js/prompt-machine.js +0 -108
- package/static/js/recording-machine.js +0 -49
- package/static/js/script-runner.js +0 -192
- package/static/js/state-barrier.js +0 -105
- package/static/js/streaming-renderer-dispatch.js +0 -144
- package/static/js/streaming-renderer-events.js +0 -163
- package/static/js/streaming-renderer-events2.js +0 -125
- package/static/js/streaming-renderer-params.js +0 -38
- package/static/js/streaming-renderer-render-misc.js +0 -107
- package/static/js/streaming-renderer-render.js +0 -181
- package/static/js/streaming-renderer-render2.js +0 -149
- package/static/js/streaming-renderer-render3.js +0 -142
- package/static/js/streaming-renderer-static.js +0 -181
- package/static/js/streaming-renderer-static2.js +0 -140
- package/static/js/streaming-renderer-stream.js +0 -170
- package/static/js/streaming-renderer-text.js +0 -185
- package/static/js/streaming-renderer-tools.js +0 -189
- package/static/js/streaming-renderer-tools2.js +0 -92
- package/static/js/streaming-renderer.js +0 -200
- package/static/js/syntax-highlighter-render.js +0 -72
- package/static/js/syntax-highlighter.js +0 -131
- package/static/js/terminal-machine.js +0 -51
- package/static/js/terminal.js +0 -178
- package/static/js/ui-components-rendering.js +0 -62
- package/static/js/ui-components.js +0 -88
- package/static/js/websocket-manager.js +0 -107
- package/static/js/ws-client.js +0 -87
- package/static/js/ws-core.js +0 -162
- package/static/js/ws-latency.js +0 -88
- package/static/js/ws-machine.js +0 -68
- package/static/lib/msgpackr.min.js +0 -2
- package/static/theme.js +0 -74
- package/static/vendor/highlight-js.css +0 -10
- package/static/vendor/highlight.min.js +0 -1244
- package/static/vendor/prism-dark.css +0 -129
- package/static/vendor/rippleui.css +0 -35
- package/static/vendor/xterm-addon-fit.min.js +0 -8
- package/static/vendor/xterm.css +0 -8
- package/static/vendor/xterm.min.js +0 -8
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
Object.assign(StreamingRenderer.prototype, {
|
|
2
|
-
autoScroll() {
|
|
3
|
-
if (this._scrollRafPending || this._userScrolledUp) return;
|
|
4
|
-
this._scrollRafPending = true;
|
|
5
|
-
requestAnimationFrame(() => {
|
|
6
|
-
this._scrollRafPending = false;
|
|
7
|
-
if (this.scrollContainer) {
|
|
8
|
-
this._programmaticScroll = true;
|
|
9
|
-
try { this.scrollContainer.scrollTop = this.scrollContainer.scrollHeight; } catch (_) {}
|
|
10
|
-
this._programmaticScroll = false;
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
},
|
|
14
|
-
|
|
15
|
-
resetScrollState() {
|
|
16
|
-
this._userScrolledUp = false;
|
|
17
|
-
},
|
|
18
|
-
|
|
19
|
-
updateDOMNodeCount() {
|
|
20
|
-
this.domNodeCount = this.outputContainer?.querySelectorAll('[data-event-id]').length || 0;
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
escapeHtml(text) {
|
|
24
|
-
return window._escHtml(text);
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
formatFileSize(bytes) {
|
|
28
|
-
if (bytes === 0) return '0 B';
|
|
29
|
-
const k = 1024;
|
|
30
|
-
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
31
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
32
|
-
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
truncateContent(content, maxLength = 200) {
|
|
36
|
-
if (content.length <= maxLength) return content;
|
|
37
|
-
return content.substring(0, maxLength) + '...';
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
clear() {
|
|
41
|
-
if (this.outputContainer) {
|
|
42
|
-
this.outputContainer.innerHTML = '';
|
|
43
|
-
}
|
|
44
|
-
this.eventQueue = [];
|
|
45
|
-
this.eventHistory = [];
|
|
46
|
-
this.domNodeCount = 0;
|
|
47
|
-
this.dedupMap.clear();
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
getMetrics() {
|
|
51
|
-
return {
|
|
52
|
-
...this.performanceMetrics,
|
|
53
|
-
domNodeCount: this.domNodeCount,
|
|
54
|
-
queueLength: this.eventQueue.length,
|
|
55
|
-
historyLength: this.eventHistory.length,
|
|
56
|
-
lastRenderTime: this.lastRenderTime
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
on(event, callback) {
|
|
61
|
-
if (!this.listeners[event]) {
|
|
62
|
-
this.listeners[event] = [];
|
|
63
|
-
}
|
|
64
|
-
this.listeners[event].push(callback);
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
emit(event, data) {
|
|
68
|
-
if (this.listeners[event]) {
|
|
69
|
-
this.listeners[event].forEach(callback => {
|
|
70
|
-
try {
|
|
71
|
-
callback(data);
|
|
72
|
-
} catch (e) {
|
|
73
|
-
console.error('Listener error:', e);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
renderBlockHeader(block, context = {}) {
|
|
80
|
-
if (!block || !block.type) return null;
|
|
81
|
-
|
|
82
|
-
const typeLabel = block.type.charAt(0).toUpperCase() + block.type.slice(1).replace(/_/g, ' ');
|
|
83
|
-
const summary = document.createElement('summary');
|
|
84
|
-
summary.style.cursor = 'pointer';
|
|
85
|
-
summary.style.userSelect = 'none';
|
|
86
|
-
summary.className = 'block-header-summary';
|
|
87
|
-
|
|
88
|
-
let summaryText = typeLabel;
|
|
89
|
-
if (block.type === 'code' && block.language) {
|
|
90
|
-
summaryText += ` (${block.language})`;
|
|
91
|
-
} else if (block.type === 'bash' && block.source) {
|
|
92
|
-
summaryText += ` - ${block.source}`;
|
|
93
|
-
} else if (block.type === 'tool_use' && block.name) {
|
|
94
|
-
summaryText += ` - ${block.name}`;
|
|
95
|
-
} else if (block.type === 'text' && block.text) {
|
|
96
|
-
const preview = block.text.substring(0, 60).replace(/\n/g, ' ');
|
|
97
|
-
summaryText = preview + (block.text.length > 60 ? '...' : '');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
summary.textContent = summaryText;
|
|
101
|
-
|
|
102
|
-
const details = document.createElement('details');
|
|
103
|
-
details.className = `block-${block.type}`;
|
|
104
|
-
details.classList.add(this._getBlockTypeClass(block.type));
|
|
105
|
-
details.setAttribute('data-block-type', block.type);
|
|
106
|
-
details.setAttribute('data-lazy-load', 'pending');
|
|
107
|
-
details.open = block.type === 'success' || (block.type === 'tool_result' && !block.is_error);
|
|
108
|
-
details.appendChild(summary);
|
|
109
|
-
|
|
110
|
-
details.addEventListener('toggle', async (e) => {
|
|
111
|
-
if (details.open && details.getAttribute('data-lazy-load') === 'pending') {
|
|
112
|
-
details.setAttribute('data-lazy-load', 'loading');
|
|
113
|
-
try {
|
|
114
|
-
const body = this.renderBlock(block, context);
|
|
115
|
-
if (body && body !== summary) {
|
|
116
|
-
details.appendChild(body);
|
|
117
|
-
}
|
|
118
|
-
details.setAttribute('data-lazy-load', 'loaded');
|
|
119
|
-
} catch (err) {
|
|
120
|
-
console.error('Failed to lazy-load block:', err);
|
|
121
|
-
details.setAttribute('data-lazy-load', 'failed');
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}, { once: false });
|
|
125
|
-
|
|
126
|
-
return details;
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
destroy() {
|
|
130
|
-
if (this.observer) {
|
|
131
|
-
this.observer.disconnect();
|
|
132
|
-
}
|
|
133
|
-
if (this.resizeObserver) {
|
|
134
|
-
this.resizeObserver.disconnect();
|
|
135
|
-
}
|
|
136
|
-
if (this.batchTimer) {
|
|
137
|
-
clearTimeout(this.batchTimer);
|
|
138
|
-
}
|
|
139
|
-
this.listeners = {};
|
|
140
|
-
this.clear();
|
|
141
|
-
}
|
|
142
|
-
});
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
Object.assign(StreamingRenderer, {
|
|
2
|
-
renderSmartContentHTML(contentStr, escapeHtml, flat = false) {
|
|
3
|
-
const trimmed = contentStr.trim();
|
|
4
|
-
const esc = escapeHtml || window._escHtml;
|
|
5
|
-
|
|
6
|
-
if (trimmed.startsWith('data:image/')) {
|
|
7
|
-
return `<div style="padding:0.5rem"><img src="${esc(trimmed)}" style="max-width:100%;max-height:24rem;border-radius:0.375rem" loading="lazy"></div>`;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
|
11
|
-
try {
|
|
12
|
-
const parsed = JSON.parse(trimmed);
|
|
13
|
-
|
|
14
|
-
if (Array.isArray(parsed) && parsed.length > 0 && parsed[0] && parsed[0].type === 'text') {
|
|
15
|
-
const textParts = parsed.filter(b => b.type === 'text' && b.text);
|
|
16
|
-
if (textParts.length > 0) {
|
|
17
|
-
const combined = textParts.map(b => b.text).join('\n');
|
|
18
|
-
return StreamingRenderer.renderSmartContentHTML(combined, esc, flat);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
23
|
-
const imgParts = parsed.filter(b => b.type === 'image' && b.source && b.source.type === 'base64' && b.source.data);
|
|
24
|
-
if (imgParts.length > 0) {
|
|
25
|
-
return imgParts.map(b => {
|
|
26
|
-
const mime = b.source.media_type || 'image/png';
|
|
27
|
-
return `<div style="padding:0.5rem"><img src="data:${esc(mime)};base64,${b.source.data}" style="max-width:100%;max-height:24rem;border-radius:0.375rem"></div>`;
|
|
28
|
-
}).join('');
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return `<div style="padding:0.5rem 0.75rem">${StreamingRenderer.renderParamsHTML(parsed, 0, esc)}</div>`;
|
|
33
|
-
} catch (e) {
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const lines = trimmed.split('\n');
|
|
38
|
-
const isCatNOutput = lines.length > 1 && lines[0].match(/^\s*\d+→/);
|
|
39
|
-
const isGrepOutput = lines.length > 1 && lines[0].match(/^\s*\d+-/);
|
|
40
|
-
|
|
41
|
-
if (isCatNOutput || isGrepOutput) {
|
|
42
|
-
const cleanedLines = lines.map(line => {
|
|
43
|
-
if (line === '--') return null;
|
|
44
|
-
|
|
45
|
-
const match = line.match(/^\s*\d+[→\-:](.*)/);
|
|
46
|
-
return match ? match[1] : line;
|
|
47
|
-
}).filter(line => line !== null);
|
|
48
|
-
const cleanedContent = cleanedLines.join('\n');
|
|
49
|
-
|
|
50
|
-
return StreamingRenderer.renderCodeWithHighlight(cleanedContent, esc, flat);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const systemReminderPattern = /<system-reminder>([\s\S]*?)<\/system-reminder>/g;
|
|
54
|
-
const systemReminders = [];
|
|
55
|
-
let contentWithoutReminders = trimmed;
|
|
56
|
-
|
|
57
|
-
let reminderMatch;
|
|
58
|
-
while ((reminderMatch = systemReminderPattern.exec(trimmed)) !== null) {
|
|
59
|
-
systemReminders.push(reminderMatch[1].trim());
|
|
60
|
-
contentWithoutReminders = contentWithoutReminders.replace(reminderMatch[0], '');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
contentWithoutReminders = contentWithoutReminders.trim();
|
|
64
|
-
|
|
65
|
-
const successPatterns = [
|
|
66
|
-
/^Success\s+toolu_[\w]+$/m,
|
|
67
|
-
/^The file .* has been (updated|created|modified)/,
|
|
68
|
-
/^Here's the result of running `cat -n`/,
|
|
69
|
-
/^Applied \d+ edits? to/,
|
|
70
|
-
/^\w+ tool completed successfully/
|
|
71
|
-
];
|
|
72
|
-
|
|
73
|
-
const hasSuccessPattern = successPatterns.some(pattern => pattern.test(contentWithoutReminders));
|
|
74
|
-
|
|
75
|
-
if (hasSuccessPattern) {
|
|
76
|
-
const contentLines = contentWithoutReminders.split('\n');
|
|
77
|
-
let successEndIndex = -1;
|
|
78
|
-
let codeStartIndex = -1;
|
|
79
|
-
|
|
80
|
-
for (let i = 0; i < contentLines.length; i++) {
|
|
81
|
-
const line = contentLines[i];
|
|
82
|
-
if (line.match(/^Success\s+toolu_/)) {
|
|
83
|
-
successEndIndex = i;
|
|
84
|
-
for (let j = i + 1; j < contentLines.length; j++) {
|
|
85
|
-
if (contentLines[j].trim() && !contentLines[j].match(/^The file|^Here's the result/)) {
|
|
86
|
-
codeStartIndex = j;
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
break;
|
|
91
|
-
} else if (line.match(/^The file .* has been|^Applied \d+ edits? to|^Replaced|^Created|^Deleted/)) {
|
|
92
|
-
for (let j = i + 1; j < contentLines.length; j++) {
|
|
93
|
-
if (contentLines[j].match(/^Here's the result|^\s*\d+→/)) {
|
|
94
|
-
if (contentLines[j].match(/^Here's the result/)) {
|
|
95
|
-
codeStartIndex = j + 1;
|
|
96
|
-
} else {
|
|
97
|
-
codeStartIndex = j;
|
|
98
|
-
}
|
|
99
|
-
break;
|
|
100
|
-
} else if (contentLines[j].trim() && !contentLines[j].match(/^cat -n|^Running/)) {
|
|
101
|
-
codeStartIndex = j;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (codeStartIndex === -1) {
|
|
106
|
-
codeStartIndex = i + 2;
|
|
107
|
-
}
|
|
108
|
-
successEndIndex = codeStartIndex - 1;
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (codeStartIndex > 0 && codeStartIndex < contentLines.length) {
|
|
114
|
-
const beforeCode = contentLines.slice(0, codeStartIndex).join('\n');
|
|
115
|
-
let codeContent = contentLines.slice(codeStartIndex).join('\n');
|
|
116
|
-
|
|
117
|
-
if (codeContent.match(/^\s*\d+→/m)) {
|
|
118
|
-
const codeLines = codeContent.split('\n');
|
|
119
|
-
codeContent = codeLines.map(line => {
|
|
120
|
-
const match = line.match(/^\s*\d+→(.*)/);
|
|
121
|
-
return match ? match[1] : line;
|
|
122
|
-
}).join('\n');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
let html = '';
|
|
126
|
-
|
|
127
|
-
if (beforeCode.trim()) {
|
|
128
|
-
html += `<div style="color:var(--color-success);font-weight:600;margin-bottom:0.75rem;font-size:0.9rem">${esc(beforeCode.trim())}</div>`;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (codeContent.trim()) {
|
|
132
|
-
html += StreamingRenderer.renderCodeWithHighlight(codeContent, esc, flat);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (systemReminders.length > 0) {
|
|
136
|
-
html += StreamingRenderer.renderSystemReminders(systemReminders, esc);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return html;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (systemReminders.length > 0) {
|
|
144
|
-
let html = '';
|
|
145
|
-
|
|
146
|
-
if (contentWithoutReminders) {
|
|
147
|
-
if (StreamingRenderer.detectCodeContent(contentWithoutReminders)) {
|
|
148
|
-
html += StreamingRenderer.renderCodeWithHighlight(contentWithoutReminders, esc, flat);
|
|
149
|
-
} else {
|
|
150
|
-
html += `<pre class="tool-result-pre">${esc(contentWithoutReminders)}</pre>`;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
html += StreamingRenderer.renderSystemReminders(systemReminders, esc);
|
|
155
|
-
return html;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const allFilePaths = lines.length > 1 && lines.every(l => {
|
|
159
|
-
const t = l.trim();
|
|
160
|
-
return t === '' || t.startsWith('/') || /^[A-Za-z]:[\\\/]/.test(t);
|
|
161
|
-
});
|
|
162
|
-
if (allFilePaths && lines.filter(l => l.trim()).length > 0) {
|
|
163
|
-
const fileHtml = lines.filter(l => l.trim()).map(l => {
|
|
164
|
-
const p = l.trim();
|
|
165
|
-
const parts = pathSplit(p);
|
|
166
|
-
const name = parts.pop();
|
|
167
|
-
const dir = parts.join('/');
|
|
168
|
-
return `<div style="display:flex;align-items:center;gap:0.375rem;padding:0.1875rem 0;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem"><span style="opacity:0.5">📄</span><span style="color:var(--color-text-secondary)">${esc(dir)}/</span><span style="font-weight:600">${esc(name)}</span></div>`;
|
|
169
|
-
}).join('');
|
|
170
|
-
return `<div style="padding:0.625rem 1rem">${fileHtml}</div>`;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const looksLikeCode = StreamingRenderer.detectCodeContent(trimmed);
|
|
174
|
-
if (looksLikeCode) {
|
|
175
|
-
return StreamingRenderer.renderCodeWithHighlight(trimmed, esc, flat);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const displayContent = trimmed.length > 2000 ? trimmed.substring(0, 2000) + '\n... (truncated)' : trimmed;
|
|
179
|
-
return `<pre class="tool-result-pre">${esc(displayContent)}</pre>`;
|
|
180
|
-
}
|
|
181
|
-
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
Object.assign(StreamingRenderer, {
|
|
2
|
-
renderSystemReminders(reminders, esc) {
|
|
3
|
-
if (!reminders || reminders.length === 0) return '';
|
|
4
|
-
|
|
5
|
-
const reminderHtml = reminders.map(reminder => {
|
|
6
|
-
const lines = reminder.split('\n').filter(l => l.trim());
|
|
7
|
-
const formattedLines = lines.map(line => {
|
|
8
|
-
if (line.includes('IMPORTANT:') || line.includes('WARNING:')) {
|
|
9
|
-
return `<div style="font-weight:600;color:var(--color-warning);margin:0.25rem 0">${esc(line)}</div>`;
|
|
10
|
-
}
|
|
11
|
-
return `<div style="margin:0.125rem 0">${esc(line)}</div>`;
|
|
12
|
-
}).join('');
|
|
13
|
-
|
|
14
|
-
return formattedLines;
|
|
15
|
-
}).join('');
|
|
16
|
-
|
|
17
|
-
return `
|
|
18
|
-
<div style="margin-top:1rem;padding:0.75rem;background:var(--color-bg-secondary);border-left:3px solid var(--color-info);border-radius:0.25rem;font-size:0.8rem;color:var(--color-text-secondary)">
|
|
19
|
-
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.5rem">
|
|
20
|
-
<span style="color:var(--color-info)">ℹ</span>
|
|
21
|
-
<span style="font-weight:600;font-size:0.85rem;color:var(--color-text-primary)">System Reminder</span>
|
|
22
|
-
</div>
|
|
23
|
-
${reminderHtml}
|
|
24
|
-
</div>
|
|
25
|
-
`;
|
|
26
|
-
},
|
|
27
|
-
|
|
28
|
-
detectCodeContent(content) {
|
|
29
|
-
const codePatterns = [
|
|
30
|
-
/^\s*(function|const|let|var|class|import|export|async|await)/m, // JavaScript
|
|
31
|
-
/^\s*(def|class|import|from|if __name__|lambda|async def)/m, // Python
|
|
32
|
-
/^\s*(public|private|protected|class|interface|package|import)/m, // Java/TypeScript
|
|
33
|
-
/^\s*(<\?php|namespace|use|trait)/m, // PHP
|
|
34
|
-
/^\s*(#include|int main|void|struct|typedef)/m, // C/C++
|
|
35
|
-
/[{}\[\];()]/, // Brackets and semicolons
|
|
36
|
-
/=>|->|::/, // Arrow functions, pointers
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
return codePatterns.some(pattern => pattern.test(content));
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
renderCodeWithHighlight(code, esc, flat = false) {
|
|
43
|
-
const preStyle = "background:var(--color-bg-code);padding:1rem;border-radius:0.375rem;overflow-x:auto;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.875rem;line-height:1.6;color:var(--color-code-text);border:1px solid var(--color-code-border);margin:0";
|
|
44
|
-
const codeHtml = `<pre style="${preStyle}"><code class="lazy-hl">${esc(code)}</code></pre>`;
|
|
45
|
-
if (flat) return codeHtml;
|
|
46
|
-
const lineCount = code.split('\n').length;
|
|
47
|
-
const summaryLabel = `code - ${lineCount} line${lineCount !== 1 ? 's' : ''}`;
|
|
48
|
-
return `<details class="collapsible-code"><summary class="collapsible-code-summary">${summaryLabel}</summary>${codeHtml}</details>`;
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
_setupGlobalLazyHL() {
|
|
52
|
-
if (StreamingRenderer._lazyHLSetup) return;
|
|
53
|
-
StreamingRenderer._lazyHLSetup = true;
|
|
54
|
-
const root = document.getElementById('output-scroll') || document.body;
|
|
55
|
-
root.addEventListener('toggle', (e) => {
|
|
56
|
-
const details = e.target;
|
|
57
|
-
if (!details.open || details.tagName !== 'DETAILS') return;
|
|
58
|
-
const codeEls = details.querySelectorAll('code.lazy-hl');
|
|
59
|
-
if (codeEls.length === 0) return;
|
|
60
|
-
if (typeof hljs === 'undefined') return;
|
|
61
|
-
for (const el of codeEls) {
|
|
62
|
-
try {
|
|
63
|
-
const raw = el.textContent;
|
|
64
|
-
const result = hljs.highlightAuto(raw);
|
|
65
|
-
el.classList.remove('lazy-hl');
|
|
66
|
-
el.classList.add('hljs');
|
|
67
|
-
el.innerHTML = result.value;
|
|
68
|
-
} catch (_) {}
|
|
69
|
-
}
|
|
70
|
-
}, true);
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
getToolDisplayName(toolName) {
|
|
74
|
-
const normalized = toolName.replace(/^mcp__.*?__/, '');
|
|
75
|
-
const knownTools = ['Read','Write','Edit','Bash','Glob','Grep','WebFetch','WebSearch','TodoWrite','Task','NotebookEdit'];
|
|
76
|
-
if (knownTools.includes(normalized)) return normalized;
|
|
77
|
-
if (toolName.startsWith('mcp__')) {
|
|
78
|
-
const parts = toolName.split('__');
|
|
79
|
-
return parts.length >= 3 ? parts[2] : parts[parts.length - 1];
|
|
80
|
-
}
|
|
81
|
-
return normalized || toolName;
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
getToolTitle(toolName, input) {
|
|
85
|
-
const n = toolName.replace(/^mcp__.*?__/, '');
|
|
86
|
-
if (n === 'Edit' && input.file_path) { const p = pathSplit(input.file_path); const f = p.pop(); const d = p.slice(-2).join('/'); return d ? d+'/'+f : f; }
|
|
87
|
-
if (n === 'Read' && input.file_path) return pathBasename(input.file_path);
|
|
88
|
-
if (n === 'Write' && input.file_path) return pathBasename(input.file_path);
|
|
89
|
-
if ((n === 'Bash' || n === 'bash') && (input.command || input.commands)) { const c = typeof (input.command||input.commands) === 'string' ? (input.command||input.commands) : JSON.stringify(input.command||input.commands); return c.length > 60 ? c.substring(0,57)+'...' : c; }
|
|
90
|
-
if (n === 'Glob' && input.pattern) return input.pattern;
|
|
91
|
-
if (n === 'Grep' && input.pattern) return input.pattern;
|
|
92
|
-
if (n === 'WebFetch' && input.url) { try { return new URL(input.url).hostname; } catch(e) { return input.url.substring(0,40); } }
|
|
93
|
-
if (n === 'WebSearch' && input.query) return input.query.substring(0,50);
|
|
94
|
-
if (input.file_path) return pathBasename(input.file_path);
|
|
95
|
-
if (input.command) { const c = typeof input.command === 'string' ? input.command : JSON.stringify(input.command); return c.length > 50 ? c.substring(0,47)+'...' : c; }
|
|
96
|
-
if (input.query) return input.query.substring(0,50);
|
|
97
|
-
return '';
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
renderParamsHTML(data, depth, esc) {
|
|
101
|
-
if (data === null || data === undefined) return `<span style="color:var(--color-text-secondary);font-style:italic">null</span>`;
|
|
102
|
-
if (typeof data === 'boolean') return `<span style="color:#d97706;font-weight:600">${data}</span>`;
|
|
103
|
-
if (typeof data === 'number') return `<span style="color:#7c3aed;font-weight:600">${data}</span>`;
|
|
104
|
-
|
|
105
|
-
if (typeof data === 'string') {
|
|
106
|
-
if (data.length > 200 && StreamingRenderer.detectCodeContent(data)) {
|
|
107
|
-
const displayData = data.length > 1000 ? data.substring(0, 1000) : data;
|
|
108
|
-
const suffix = data.length > 1000 ? `<div style="font-size:0.7rem;color:var(--color-text-secondary);text-align:center;padding:0.25rem">... ${data.length - 1000} more characters</div>` : '';
|
|
109
|
-
return `<div style="max-height:200px;overflow-y:auto">${StreamingRenderer.renderCodeWithHighlight(displayData, esc, true)}${suffix}</div>`;
|
|
110
|
-
}
|
|
111
|
-
if (data.length > 500) {
|
|
112
|
-
return `<div style="font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto;background:var(--color-bg-code);color:var(--color-code-text);padding:0.5rem;border-radius:0.375rem;line-height:1.5">${esc(data.substring(0, 1000))}${data.length > 1000 ? '\n... (' + (data.length - 1000) + ' more chars)' : ''}</div>`;
|
|
113
|
-
}
|
|
114
|
-
const looksLikePath = /^[A-Za-z]:[\\\/]/.test(data) || data.startsWith('/');
|
|
115
|
-
if (looksLikePath && !data.includes(' ') && data.includes('.')) {
|
|
116
|
-
const parts = pathSplit(data);
|
|
117
|
-
const name = parts.pop();
|
|
118
|
-
const dir = parts.join('/');
|
|
119
|
-
return `<div style="display:flex;align-items:center;gap:0.375rem;font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.8rem"><span style="opacity:0.5">📄</span><span style="color:var(--color-text-secondary)">${esc(dir)}/</span><span style="font-weight:600">${esc(name)}</span></div>`;
|
|
120
|
-
}
|
|
121
|
-
return `<span style="color:var(--color-text-primary)">${esc(data)}</span>`;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (Array.isArray(data)) {
|
|
125
|
-
if (data.length === 0) return `<span style="color:var(--color-text-secondary)">[]</span>`;
|
|
126
|
-
if (data.every(i => typeof i === 'string') && data.length <= 20) {
|
|
127
|
-
return `<div style="display:flex;flex-direction:column;gap:0.125rem;${depth > 0 ? 'padding-left:1rem' : ''}">${data.map((i, idx) => `<div style="display:flex;align-items:center;gap:0.375rem"><span style="color:var(--color-text-secondary);font-size:0.65rem;opacity:0.5">•</span><span style="font-family:'Monaco','Menlo','Ubuntu Mono',monospace;font-size:0.75rem">${esc(i)}</span></div>`).join('')}</div>`;
|
|
128
|
-
}
|
|
129
|
-
return `<div style="display:flex;flex-direction:column;gap:0.25rem;${depth > 0 ? 'padding-left:1rem' : ''}">${data.map((item, i) => `<div style="display:flex;gap:0.5rem;align-items:flex-start"><span style="color:var(--color-text-secondary);font-size:0.7rem;min-width:1.5rem;text-align:right;flex-shrink:0">${i}</span><div style="flex:1;min-width:0">${StreamingRenderer.renderParamsHTML(item, depth + 1, esc)}</div></div>`).join('')}</div>`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (typeof data === 'object') {
|
|
133
|
-
const entries = Object.entries(data);
|
|
134
|
-
if (entries.length === 0) return `<span style="color:var(--color-text-secondary)">{}</span>`;
|
|
135
|
-
return `<div style="display:flex;flex-direction:column;gap:0.375rem;${depth > 0 ? 'padding-left:1rem' : ''}">${entries.map(([k, v]) => `<div style="display:flex;gap:0.5rem;align-items:flex-start"><span style="font-weight:600;font-size:0.75rem;color:#0891b2;flex-shrink:0;min-width:fit-content;font-family:'Monaco','Menlo','Ubuntu Mono',monospace">${esc(k)}</span><div style="flex:1;min-width:0;font-size:0.8rem">${StreamingRenderer.renderParamsHTML(v, depth + 1, esc)}</div></div>`).join('')}</div>`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return `<span>${esc(String(data))}</span>`;
|
|
139
|
-
}
|
|
140
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
Object.assign(StreamingRenderer.prototype, {
|
|
2
|
-
renderStreamingStart(event) {
|
|
3
|
-
const div = document.createElement('div');
|
|
4
|
-
div.className = 'event-streaming-start card mb-3 p-4 alert alert-info';
|
|
5
|
-
div.dataset.eventId = event.id || event.sessionId || '';
|
|
6
|
-
div.dataset.eventType = 'streaming_start';
|
|
7
|
-
|
|
8
|
-
const time = new Date(event.timestamp).toLocaleTimeString();
|
|
9
|
-
div.innerHTML = `
|
|
10
|
-
<div class="flex items-center gap-2">
|
|
11
|
-
<svg class="w-5 h-5 text-info animate-spin" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
12
|
-
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" opacity="0.25"></circle>
|
|
13
|
-
<path d="M4 12a8 8 0 018-8" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path>
|
|
14
|
-
</svg>
|
|
15
|
-
<div class="flex-1">
|
|
16
|
-
<h4 class="font-semibold text-info-content">Streaming Started</h4>
|
|
17
|
-
<p class="text-sm text-info-content/70">${time}</p>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
`;
|
|
21
|
-
return div;
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
renderStreamingProgress(event) {
|
|
25
|
-
if (event.block) {
|
|
26
|
-
return this.renderBlock(event.block, event);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const div = document.createElement('div');
|
|
30
|
-
div.className = 'event-streaming-progress mb-2 p-2';
|
|
31
|
-
div.dataset.eventId = event.id || '';
|
|
32
|
-
div.dataset.eventType = 'streaming_progress';
|
|
33
|
-
|
|
34
|
-
const percentage = event.progress || 0;
|
|
35
|
-
div.innerHTML = `
|
|
36
|
-
<div class="flex items-center gap-2 text-sm">
|
|
37
|
-
<span class="text-secondary">${percentage}%</span>
|
|
38
|
-
<div class="progress progress-primary flex-1">
|
|
39
|
-
<div class="progress-bar" style="width: ${percentage}%"></div>
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
`;
|
|
43
|
-
return div;
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
renderStreamingComplete(event) {
|
|
47
|
-
const div = document.createElement('div');
|
|
48
|
-
div.className = 'event-streaming-complete card mb-3 p-4 alert alert-success rounded-lg';
|
|
49
|
-
div.dataset.eventId = event.id || event.sessionId || '';
|
|
50
|
-
div.dataset.eventType = 'streaming_complete';
|
|
51
|
-
|
|
52
|
-
const time = new Date(event.timestamp).toLocaleTimeString();
|
|
53
|
-
const eventCount = event.eventCount || 0;
|
|
54
|
-
|
|
55
|
-
div.innerHTML = `
|
|
56
|
-
<div class="flex items-start gap-3">
|
|
57
|
-
<div class="flex-shrink-0 mt-0.5">
|
|
58
|
-
<svg class="w-6 h-6 text-success animate-bounce" fill="currentColor" viewBox="0 0 20 20">
|
|
59
|
-
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
|
|
60
|
-
</svg>
|
|
61
|
-
</div>
|
|
62
|
-
<div class="flex-1">
|
|
63
|
-
<h4 class="font-bold text-lg text-success-content">✨ Execution Complete</h4>
|
|
64
|
-
<div class="mt-2 grid grid-cols-2 gap-3 text-sm">
|
|
65
|
-
<div>
|
|
66
|
-
<span class="text-success font-semibold">${eventCount}</span>
|
|
67
|
-
<span class="text-success/70">events processed</span>
|
|
68
|
-
</div>
|
|
69
|
-
<div class="text-right">
|
|
70
|
-
<span class="text-success/70">${time}</span>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
`;
|
|
76
|
-
return div;
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
detectBase64Image(content) {
|
|
80
|
-
if (!content || typeof content !== 'string') return null;
|
|
81
|
-
const trimmed = content.trim();
|
|
82
|
-
const signatures = {
|
|
83
|
-
'png': /^iVBORw0KGgo/,
|
|
84
|
-
'jpeg': /^\/9j\/4AAQ/,
|
|
85
|
-
'webp': /^UklGRi/,
|
|
86
|
-
'gif': /^R0lGODlh/
|
|
87
|
-
};
|
|
88
|
-
for (const [type, pattern] of Object.entries(signatures)) {
|
|
89
|
-
if (pattern.test(trimmed)) {
|
|
90
|
-
return { type, isBase64: true, data: trimmed };
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return null;
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
renderFileRead(event) {
|
|
97
|
-
const fileName = event.path ? event.path.split('/').pop() : 'unknown';
|
|
98
|
-
const details = document.createElement('details');
|
|
99
|
-
details.className = 'block-tool-use folded-tool';
|
|
100
|
-
details.classList.add(this._getBlockTypeClass('tool_use'));
|
|
101
|
-
details.classList.add(this._getToolColorClass('Read'));
|
|
102
|
-
details.dataset.eventId = event.id || '';
|
|
103
|
-
details.dataset.eventType = 'file_read';
|
|
104
|
-
const summary = document.createElement('summary');
|
|
105
|
-
summary.className = 'folded-tool-bar';
|
|
106
|
-
summary.innerHTML = `
|
|
107
|
-
<span class="folded-tool-icon">${this.getToolIcon('Read')}</span>
|
|
108
|
-
<span class="folded-tool-name">Read</span>
|
|
109
|
-
<span class="folded-tool-desc">${this.escapeHtml(fileName)}</span>
|
|
110
|
-
`;
|
|
111
|
-
details.appendChild(summary);
|
|
112
|
-
if (event.path || event.content) {
|
|
113
|
-
const body = document.createElement('div');
|
|
114
|
-
body.className = 'folded-tool-body';
|
|
115
|
-
let html = '';
|
|
116
|
-
if (event.path) html += this.renderFilePath(event.path);
|
|
117
|
-
if (event.content) {
|
|
118
|
-
let base64Data = null;
|
|
119
|
-
let mimeType = event.media_type || 'application/octet-stream';
|
|
120
|
-
if (typeof event.content === 'string') {
|
|
121
|
-
const imageInfo = this.detectBase64Image(event.content);
|
|
122
|
-
if (imageInfo) {
|
|
123
|
-
base64Data = imageInfo.data;
|
|
124
|
-
mimeType = imageInfo.type === 'jpeg' ? 'image/jpeg' : `image/${imageInfo.type}`;
|
|
125
|
-
}
|
|
126
|
-
} else if (typeof event.content === 'object' && event.content !== null) {
|
|
127
|
-
if (event.content.source?.type === 'base64' && event.content.source?.data) {
|
|
128
|
-
base64Data = event.content.source.data;
|
|
129
|
-
} else if (event.content.type === 'base64' && event.content.data) {
|
|
130
|
-
base64Data = event.content.data;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (base64Data) {
|
|
134
|
-
html += `<div style="padding:0.5rem;display:flex;flex-direction:column;gap:0.5rem"><img src="data:${mimeType};base64,${this.escapeHtml(base64Data)}" style="max-width:100%;max-height:600px;border-radius:0.375rem;border:1px solid var(--color-code-border)" loading="lazy"><div style="font-size:0.7rem;color:var(--color-text-secondary);font-family:'Monaco','Menlo','Ubuntu Mono',monospace;word-break:break-all">${this.escapeHtml(event.path)}</div></div>`;
|
|
135
|
-
} else {
|
|
136
|
-
const contentStr = typeof event.content === 'string' ? event.content : JSON.stringify(event.content, null, 2);
|
|
137
|
-
html += `<pre style="background:var(--color-bg-code);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:var(--color-code-text);margin:0.5rem 0 0 0"><code class="lazy-hl">${this.escapeHtml(this.truncateContent(contentStr, 2000))}</code></pre>`;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
body.innerHTML = html;
|
|
141
|
-
details.appendChild(body);
|
|
142
|
-
}
|
|
143
|
-
return details;
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
renderFileWrite(event) {
|
|
147
|
-
const fileName = event.path ? event.path.split('/').pop() : 'unknown';
|
|
148
|
-
const details = document.createElement('details');
|
|
149
|
-
details.className = 'block-tool-use folded-tool';
|
|
150
|
-
details.classList.add(this._getBlockTypeClass('tool_use'));
|
|
151
|
-
details.classList.add(this._getToolColorClass('Write'));
|
|
152
|
-
details.dataset.eventId = event.id || '';
|
|
153
|
-
details.dataset.eventType = 'file_write';
|
|
154
|
-
const summary = document.createElement('summary');
|
|
155
|
-
summary.className = 'folded-tool-bar';
|
|
156
|
-
summary.innerHTML = `
|
|
157
|
-
<span class="folded-tool-icon">${this.getToolIcon('Write')}</span>
|
|
158
|
-
<span class="folded-tool-name">Write</span>
|
|
159
|
-
<span class="folded-tool-desc">${this.escapeHtml(fileName)}</span>
|
|
160
|
-
`;
|
|
161
|
-
details.appendChild(summary);
|
|
162
|
-
if (event.path) {
|
|
163
|
-
const body = document.createElement('div');
|
|
164
|
-
body.className = 'folded-tool-body';
|
|
165
|
-
body.innerHTML = this.renderFilePath(event.path);
|
|
166
|
-
details.appendChild(body);
|
|
167
|
-
}
|
|
168
|
-
return details;
|
|
169
|
-
}
|
|
170
|
-
});
|