agentgui 1.0.620 → 1.0.622
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/README.md +7 -11
- package/docs/index.html +31 -29
- package/docs/screenshot-conversation.png +0 -0
- package/docs/screenshot-dark-conversation.png +0 -0
- package/docs/screenshot-dark.png +0 -0
- package/docs/screenshot-files.png +0 -0
- package/docs/screenshot-light.png +0 -0
- package/docs/screenshot-terminal.png +0 -0
- package/docs/screenshot-tools.png +0 -0
- package/lib/claude-runner.js +29 -1
- package/lib/ws-handlers-session.js +3 -50
- package/lib/ws-handlers-util.js +37 -1
- package/package.json +1 -1
- package/static/index.html +0 -2
- package/static/js/client.js +99 -18
- package/docs/screenshot-main.png +0 -0
- package/docs/screenshot-tools-popup.png +0 -0
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Multi-agent GUI client for AI coding agents with real-time streaming, WebSocket
|
|
|
26
26
|
| Kiro CLI | ACP | - |
|
|
27
27
|
| fast-agent | ACP | - |
|
|
28
28
|
|
|
29
|
-

|
|
30
30
|
|
|
31
31
|
## Why AgentGUI?
|
|
32
32
|
|
|
@@ -50,17 +50,13 @@ Modern AI coding requires juggling multiple agents, each in their own terminal.
|
|
|
50
50
|
|
|
51
51
|
### Screenshots
|
|
52
52
|
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|  |  |
|
|
56
56
|
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|  |  |
|
|
57
|
+
| Active Conversation |
|
|
58
|
+
|---------------------|
|
|
59
|
+
|  |
|
|
64
60
|
|
|
65
61
|
## Quick Start
|
|
66
62
|
|
package/docs/index.html
CHANGED
|
@@ -193,21 +193,27 @@
|
|
|
193
193
|
/* Screenshots */
|
|
194
194
|
.screenshot-gallery {
|
|
195
195
|
display: grid;
|
|
196
|
-
grid-template-columns: repeat(
|
|
197
|
-
gap:
|
|
196
|
+
grid-template-columns: repeat(3, 1fr);
|
|
197
|
+
gap: 1.5rem;
|
|
198
198
|
margin-top: 3rem;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
.screenshot-gallery.two-col {
|
|
202
|
+
grid-template-columns: repeat(2, 1fr);
|
|
203
|
+
margin-top: 1.5rem;
|
|
204
|
+
}
|
|
205
|
+
|
|
201
206
|
.screenshot-item {
|
|
202
207
|
border-radius: 12px;
|
|
203
208
|
overflow: hidden;
|
|
204
|
-
box-shadow: 0 4px
|
|
209
|
+
box-shadow: 0 4px 24px rgba(0,0,0,0.12);
|
|
205
210
|
transition: all 0.3s ease;
|
|
211
|
+
background: #1f2937;
|
|
206
212
|
}
|
|
207
213
|
|
|
208
214
|
.screenshot-item:hover {
|
|
209
|
-
transform:
|
|
210
|
-
box-shadow: 0
|
|
215
|
+
transform: translateY(-4px);
|
|
216
|
+
box-shadow: 0 12px 40px rgba(0,0,0,0.2);
|
|
211
217
|
}
|
|
212
218
|
|
|
213
219
|
.screenshot-item img {
|
|
@@ -217,11 +223,13 @@
|
|
|
217
223
|
}
|
|
218
224
|
|
|
219
225
|
.screenshot-caption {
|
|
220
|
-
padding: 1rem;
|
|
221
|
-
background: #
|
|
226
|
+
padding: 0.875rem 1rem;
|
|
227
|
+
background: #1f2937;
|
|
222
228
|
text-align: center;
|
|
223
229
|
font-weight: 600;
|
|
224
|
-
color: #
|
|
230
|
+
color: #e5e7eb;
|
|
231
|
+
font-size: 0.9rem;
|
|
232
|
+
letter-spacing: 0.02em;
|
|
225
233
|
}
|
|
226
234
|
|
|
227
235
|
/* Code Block */
|
|
@@ -321,7 +329,8 @@
|
|
|
321
329
|
grid-template-columns: 1fr;
|
|
322
330
|
}
|
|
323
331
|
|
|
324
|
-
.screenshot-gallery
|
|
332
|
+
.screenshot-gallery,
|
|
333
|
+
.screenshot-gallery.two-col {
|
|
325
334
|
grid-template-columns: 1fr;
|
|
326
335
|
}
|
|
327
336
|
}
|
|
@@ -332,7 +341,7 @@
|
|
|
332
341
|
<section class="hero">
|
|
333
342
|
<div class="hero-content">
|
|
334
343
|
<h1>AgentGUI</h1>
|
|
335
|
-
<p>Multi-agent GUI for
|
|
344
|
+
<p>Multi-agent GUI for Claude Code, Gemini CLI, OpenCode & more. Real-time streaming, dark mode, voice, and SQLite persistence.</p>
|
|
336
345
|
|
|
337
346
|
<div class="badges">
|
|
338
347
|
<img src="https://img.shields.io/npm/v/agentgui?color=brightgreen" alt="npm version">
|
|
@@ -443,33 +452,26 @@
|
|
|
443
452
|
|
|
444
453
|
<div class="screenshot-gallery">
|
|
445
454
|
<div class="screenshot-item">
|
|
446
|
-
<img src="screenshot-
|
|
447
|
-
<div class="screenshot-caption">
|
|
455
|
+
<img src="screenshot-light.png" alt="Welcome Screen - Light Mode">
|
|
456
|
+
<div class="screenshot-caption">Welcome Screen · Light Mode</div>
|
|
448
457
|
</div>
|
|
449
|
-
|
|
450
458
|
<div class="screenshot-item">
|
|
451
|
-
<img src="screenshot-
|
|
452
|
-
<div class="screenshot-caption">
|
|
459
|
+
<img src="screenshot-dark.png" alt="Welcome Screen - Dark Mode">
|
|
460
|
+
<div class="screenshot-caption">Welcome Screen · Dark Mode</div>
|
|
453
461
|
</div>
|
|
454
|
-
|
|
455
|
-
<div class="screenshot-item">
|
|
456
|
-
<img src="screenshot-files.png" alt="File Browser">
|
|
457
|
-
<div class="screenshot-caption">File Browser</div>
|
|
458
|
-
</div>
|
|
459
|
-
|
|
460
462
|
<div class="screenshot-item">
|
|
461
|
-
<img src="screenshot-
|
|
462
|
-
<div class="screenshot-caption">
|
|
463
|
+
<img src="screenshot-dark-conversation.png" alt="Live Streaming">
|
|
464
|
+
<div class="screenshot-caption">Live Agent Streaming</div>
|
|
463
465
|
</div>
|
|
464
|
-
|
|
466
|
+
</div>
|
|
467
|
+
<div class="screenshot-gallery two-col">
|
|
465
468
|
<div class="screenshot-item">
|
|
466
|
-
<img src="screenshot-
|
|
467
|
-
<div class="screenshot-caption">
|
|
469
|
+
<img src="screenshot-conversation.png" alt="Tool Use & Code Output">
|
|
470
|
+
<div class="screenshot-caption">Tool Use & Code Output</div>
|
|
468
471
|
</div>
|
|
469
|
-
|
|
470
472
|
<div class="screenshot-item">
|
|
471
|
-
<img src="screenshot-
|
|
472
|
-
<div class="screenshot-caption">
|
|
473
|
+
<img src="screenshot-tools.png" alt="Tools & Extensions">
|
|
474
|
+
<div class="screenshot-caption">Tools & Extensions Manager</div>
|
|
473
475
|
</div>
|
|
474
476
|
</div>
|
|
475
477
|
</div>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/claude-runner.js
CHANGED
|
@@ -115,9 +115,10 @@ class AgentRunner {
|
|
|
115
115
|
}, timeout);
|
|
116
116
|
|
|
117
117
|
// Write to stdin if supported
|
|
118
|
+
// Don't close stdin - keep it open for steering/injection during execution
|
|
118
119
|
if (this.supportsStdin) {
|
|
119
120
|
proc.stdin.write(prompt);
|
|
120
|
-
|
|
121
|
+
// Don't call stdin.end() - agents need open stdin for session/prompt steering
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
proc.stdout.on('error', () => {});
|
|
@@ -1189,6 +1190,33 @@ export async function runClaudeWithStreaming(prompt, cwd, agentId = 'claude-code
|
|
|
1189
1190
|
if (!enhancedConfig.systemPrompt) {
|
|
1190
1191
|
enhancedConfig.systemPrompt = '';
|
|
1191
1192
|
}
|
|
1193
|
+
|
|
1194
|
+
// Append communication guidelines for all agents
|
|
1195
|
+
const communicationGuidelines = `
|
|
1196
|
+
COMMUNICATION STYLE: Minimize output. Only inform the user about:
|
|
1197
|
+
- Critical errors that block work
|
|
1198
|
+
- User needs to know info (e.g., "port in use", "authentication failed", "file not found")
|
|
1199
|
+
- Action required from user
|
|
1200
|
+
- Important decisions that affect their work
|
|
1201
|
+
|
|
1202
|
+
DO NOT output:
|
|
1203
|
+
- Progress updates ("doing X now", "completed Y", "searching for...")
|
|
1204
|
+
- Verbose summaries of what was done
|
|
1205
|
+
- Status checks or verification messages
|
|
1206
|
+
- Detailed explanations unless asked
|
|
1207
|
+
- "Working on...", "Looking for...", step-by-step progress
|
|
1208
|
+
|
|
1209
|
+
INSTEAD:
|
|
1210
|
+
- Run tools silently
|
|
1211
|
+
- Show results only when relevant
|
|
1212
|
+
- Be conversational and direct
|
|
1213
|
+
- Let code/output speak for itself
|
|
1214
|
+
`;
|
|
1215
|
+
|
|
1216
|
+
if (!enhancedConfig.systemPrompt.includes('COMMUNICATION STYLE')) {
|
|
1217
|
+
enhancedConfig.systemPrompt = communicationGuidelines + enhancedConfig.systemPrompt;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1192
1220
|
if (agentId && agentId !== 'claude-code') {
|
|
1193
1221
|
const displayAgentId = agentId.split('-·-')[0];
|
|
1194
1222
|
const agentPrefix = `use ${displayAgentId} subagent to. `;
|
|
@@ -107,55 +107,8 @@ export function register(router, deps) {
|
|
|
107
107
|
protocol: a.protocol || 'unknown', description: a.description || '', status: 'available'
|
|
108
108
|
}))
|
|
109
109
|
}));
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const agent = discoveredAgents.find(x => x.id === p.id);
|
|
113
|
-
if (p.id === 'claude-code') {
|
|
114
|
-
// Directly enumerate skills from ~/.claude/skills directory
|
|
115
|
-
// Don't try to run 'claude agents list' as it gets intercepted by the running Claude Code instance
|
|
116
|
-
const skillsDir = path.join(os.homedir(), '.claude', 'skills');
|
|
117
|
-
try {
|
|
118
|
-
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
119
|
-
const skills = entries
|
|
120
|
-
.filter(e => e.isDirectory() || e.isSymbolicLink())
|
|
121
|
-
.map(e => {
|
|
122
|
-
// Try to read SKILL.md to get the actual name
|
|
123
|
-
const md = path.join(skillsDir, e.name, 'SKILL.md');
|
|
124
|
-
let name = e.name;
|
|
125
|
-
try {
|
|
126
|
-
const content = fs.readFileSync(md, 'utf-8');
|
|
127
|
-
const m = content.match(/^name:\s*(.+)$/m);
|
|
128
|
-
if (m) name = m[1].trim();
|
|
129
|
-
} catch (_) {
|
|
130
|
-
// Fallback: format the directory name as the skill name
|
|
131
|
-
name = e.name
|
|
132
|
-
.split(/[\-_]/)
|
|
133
|
-
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
134
|
-
.join(' ');
|
|
135
|
-
}
|
|
136
|
-
return { id: e.name, name };
|
|
137
|
-
})
|
|
138
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
139
|
-
|
|
140
|
-
if (skills.length > 0) {
|
|
141
|
-
console.log(`[agent.subagents] Found ${skills.length} Claude Code skills:`, skills.map(s => s.name).join(', '));
|
|
142
|
-
return { subAgents: skills };
|
|
143
|
-
}
|
|
144
|
-
return { subAgents: [] };
|
|
145
|
-
} catch (err) {
|
|
146
|
-
console.error(`[agent.subagents] Error reading skills directory:`, err.message);
|
|
147
|
-
return { subAgents: [] };
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (!agent || agent.protocol !== 'acp') return { subAgents: [] };
|
|
151
|
-
const port = await ensureRunning(p.id);
|
|
152
|
-
if (!port) return { subAgents: [] };
|
|
153
|
-
try {
|
|
154
|
-
const data = await acpFetch(port, '/agent');
|
|
155
|
-
const list = Array.isArray(data) ? data : (data?.agents || []);
|
|
156
|
-
return { subAgents: list.map(a => ({ id: a.name || a.agent_id || a.id, name: a.name || a.agent_id || a.id })) };
|
|
157
|
-
} catch (_) { return { subAgents: [] }; }
|
|
158
|
-
});
|
|
110
|
+
// Note: agent.subagents is handled by ws-handlers-util.js which is registered after this file
|
|
111
|
+
// Keeping this note to avoid duplicate handler registration
|
|
159
112
|
|
|
160
113
|
router.handle('agent.get', (p) => {
|
|
161
114
|
const a = discoveredAgents.find(x => x.id === p.id);
|
|
@@ -173,7 +126,7 @@ export function register(router, deps) {
|
|
|
173
126
|
const cached = modelCache.get(p.id);
|
|
174
127
|
if (cached && (Date.now() - cached.ts) < 300000) return { models: cached.models };
|
|
175
128
|
let models = [];
|
|
176
|
-
if (p.id === 'claude-code') {
|
|
129
|
+
if (p.id === 'claude-code' || p.id === 'cli-claude') {
|
|
177
130
|
models = [
|
|
178
131
|
{ id: 'haiku', label: 'Haiku' },
|
|
179
132
|
{ id: 'sonnet', label: 'Sonnet' },
|
package/lib/ws-handlers-util.js
CHANGED
|
@@ -274,9 +274,45 @@ export function register(router, deps) {
|
|
|
274
274
|
const { id } = p;
|
|
275
275
|
if (!id) err(400, 'Missing agent id');
|
|
276
276
|
|
|
277
|
+
// For Claude Code, read skills from ~/.claude/skills directory
|
|
278
|
+
if (id === 'cli-claude') {
|
|
279
|
+
const skillsDir = path.join(os.homedir(), '.claude', 'skills');
|
|
280
|
+
try {
|
|
281
|
+
const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
282
|
+
const skills = entries
|
|
283
|
+
.filter(e => e.isDirectory() || e.isSymbolicLink())
|
|
284
|
+
.map(e => {
|
|
285
|
+
// Try to read SKILL.md to get the actual name
|
|
286
|
+
const md = path.join(skillsDir, e.name, 'SKILL.md');
|
|
287
|
+
let name = e.name;
|
|
288
|
+
try {
|
|
289
|
+
const content = fs.readFileSync(md, 'utf-8');
|
|
290
|
+
const m = content.match(/^name:\s*(.+)$/m);
|
|
291
|
+
if (m) name = m[1].trim();
|
|
292
|
+
} catch (_) {
|
|
293
|
+
// Fallback: format the directory name as the skill name
|
|
294
|
+
name = e.name
|
|
295
|
+
.split(/[\-_]/)
|
|
296
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
297
|
+
.join(' ');
|
|
298
|
+
}
|
|
299
|
+
return { id: e.name, name };
|
|
300
|
+
})
|
|
301
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
302
|
+
|
|
303
|
+
if (skills.length > 0) {
|
|
304
|
+
console.log(`[agent.subagents] Found ${skills.length} Claude Code skills:`, skills.map(s => s.name).join(', '));
|
|
305
|
+
return { subAgents: skills };
|
|
306
|
+
}
|
|
307
|
+
return { subAgents: [] };
|
|
308
|
+
} catch (err) {
|
|
309
|
+
console.error(`[agent.subagents] Error reading skills directory:`, err.message);
|
|
310
|
+
return { subAgents: [] };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
277
314
|
// Map CLI agent IDs to their corresponding plugin sub-agents
|
|
278
315
|
const subAgentMap = {
|
|
279
|
-
'cli-claude': [{ id: 'gm-cc', name: 'GM Claude' }],
|
|
280
316
|
'cli-opencode': [{ id: 'gm-oc', name: 'GM OpenCode' }],
|
|
281
317
|
'cli-gemini': [{ id: 'gm-gc', name: 'GM Gemini' }],
|
|
282
318
|
'cli-kilo': [{ id: 'gm-kilo', name: 'GM Kilo' }],
|
package/package.json
CHANGED
package/static/index.html
CHANGED
package/static/js/client.js
CHANGED
|
@@ -283,11 +283,17 @@ class AgentGUIClient {
|
|
|
283
283
|
const latestConv = this.state.conversations[0];
|
|
284
284
|
console.log('Loading latest conversation instead:', latestConv.id);
|
|
285
285
|
return this.loadConversationMessages(latestConv.id);
|
|
286
|
+
} else {
|
|
287
|
+
// No conversations available - show welcome screen
|
|
288
|
+
this._showWelcomeScreen();
|
|
286
289
|
}
|
|
287
290
|
}).finally(() => {
|
|
288
291
|
this._isLoadingConversation = false;
|
|
289
292
|
});
|
|
290
293
|
}
|
|
294
|
+
} else {
|
|
295
|
+
// No conversation in URL - show welcome screen
|
|
296
|
+
this._showWelcomeScreen();
|
|
291
297
|
}
|
|
292
298
|
}
|
|
293
299
|
|
|
@@ -599,8 +605,7 @@ class AgentGUIClient {
|
|
|
599
605
|
this.updateUrlForConversation(null);
|
|
600
606
|
this.stopChunkPolling();
|
|
601
607
|
this.enableControls();
|
|
602
|
-
|
|
603
|
-
if (outputEl) outputEl.innerHTML = '';
|
|
608
|
+
this._showWelcomeScreen();
|
|
604
609
|
if (this.ui.messageInput) {
|
|
605
610
|
this.ui.messageInput.value = '';
|
|
606
611
|
this.ui.messageInput.style.height = 'auto';
|
|
@@ -904,18 +909,30 @@ class AgentGUIClient {
|
|
|
904
909
|
console.warn('Failed to load prior messages for streaming view:', e);
|
|
905
910
|
}
|
|
906
911
|
}
|
|
907
|
-
|
|
908
|
-
streamingDiv
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
<
|
|
915
|
-
<
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
912
|
+
let streamingDiv = document.getElementById(`streaming-${data.sessionId}`);
|
|
913
|
+
if (!streamingDiv) {
|
|
914
|
+
streamingDiv = document.createElement('div');
|
|
915
|
+
streamingDiv.className = 'message message-assistant streaming-message';
|
|
916
|
+
streamingDiv.id = `streaming-${data.sessionId}`;
|
|
917
|
+
streamingDiv.innerHTML = `
|
|
918
|
+
<div class="message-role">Assistant</div>
|
|
919
|
+
<div class="message-blocks streaming-blocks"></div>
|
|
920
|
+
<div class="streaming-indicator" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0;color:var(--color-text-secondary);font-size:0.875rem;">
|
|
921
|
+
<span class="animate-spin" style="display:inline-block;width:1rem;height:1rem;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;"></span>
|
|
922
|
+
<span class="streaming-indicator-label">Thinking...</span>
|
|
923
|
+
</div>
|
|
924
|
+
`;
|
|
925
|
+
messagesEl.appendChild(streamingDiv);
|
|
926
|
+
} else {
|
|
927
|
+
// Reuse existing div - ensure streaming class and single indicator
|
|
928
|
+
streamingDiv.classList.add('streaming-message');
|
|
929
|
+
streamingDiv.querySelectorAll('.streaming-indicator').forEach(ind => ind.remove());
|
|
930
|
+
const indDiv = document.createElement('div');
|
|
931
|
+
indDiv.className = 'streaming-indicator';
|
|
932
|
+
indDiv.style = 'display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0;color:var(--color-text-secondary);font-size:0.875rem;';
|
|
933
|
+
indDiv.innerHTML = `<span class="animate-spin" style="display:inline-block;width:1rem;height:1rem;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;"></span><span class="streaming-indicator-label">Thinking...</span>`;
|
|
934
|
+
streamingDiv.appendChild(indDiv);
|
|
935
|
+
}
|
|
919
936
|
this.scrollToBottom(true);
|
|
920
937
|
}
|
|
921
938
|
|
|
@@ -1144,10 +1161,13 @@ class AgentGUIClient {
|
|
|
1144
1161
|
if (queueEl) queueEl.remove();
|
|
1145
1162
|
|
|
1146
1163
|
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
1164
|
+
// Remove ALL streaming indicators from the entire messages container
|
|
1165
|
+
const outputEl2 = document.getElementById('output');
|
|
1166
|
+
if (outputEl2) {
|
|
1167
|
+
outputEl2.querySelectorAll('.streaming-indicator').forEach(ind => ind.remove());
|
|
1168
|
+
}
|
|
1147
1169
|
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
1148
1170
|
if (streamingEl) {
|
|
1149
|
-
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
1150
|
-
if (indicator) indicator.remove();
|
|
1151
1171
|
streamingEl.classList.remove('streaming-message');
|
|
1152
1172
|
const prevTextEl = streamingEl.querySelector('.streaming-text-current');
|
|
1153
1173
|
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
@@ -1860,6 +1880,54 @@ class AgentGUIClient {
|
|
|
1860
1880
|
});
|
|
1861
1881
|
}
|
|
1862
1882
|
|
|
1883
|
+
/**
|
|
1884
|
+
* Show native loading spinner on document element
|
|
1885
|
+
*/
|
|
1886
|
+
showLoadingSpinner() {
|
|
1887
|
+
document.documentElement.style.pointerEvents = 'auto';
|
|
1888
|
+
// Show native CSS loading indicator (not removing, just visual cue)
|
|
1889
|
+
const indicator = document.querySelector('[data-model-dl-indicator]');
|
|
1890
|
+
if (indicator && !indicator.classList.contains('visible')) {
|
|
1891
|
+
indicator.classList.add('visible');
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
/**
|
|
1896
|
+
* Hide native loading spinner
|
|
1897
|
+
*/
|
|
1898
|
+
hideLoadingSpinner() {
|
|
1899
|
+
const indicator = document.querySelector('[data-model-dl-indicator]');
|
|
1900
|
+
if (indicator && indicator.classList.contains('visible')) {
|
|
1901
|
+
indicator.classList.remove('visible');
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
/**
|
|
1906
|
+
* Show welcome screen when no conversation is selected
|
|
1907
|
+
*/
|
|
1908
|
+
_showWelcomeScreen() {
|
|
1909
|
+
const outputEl = document.getElementById('output');
|
|
1910
|
+
if (!outputEl) return;
|
|
1911
|
+
outputEl.innerHTML = `
|
|
1912
|
+
<div style="display:flex;align-items:center;justify-content:center;height:100%;flex-direction:column;gap:2rem;padding:2rem;">
|
|
1913
|
+
<div style="text-align:center;">
|
|
1914
|
+
<h1 style="margin:0;font-size:2.5rem;color:var(--color-text-primary);">Welcome to AgentGUI</h1>
|
|
1915
|
+
<p style="margin:1rem 0 0 0;font-size:1.1rem;color:var(--color-text-secondary);">Start a new conversation or select one from the sidebar</p>
|
|
1916
|
+
</div>
|
|
1917
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:1rem;max-width:600px;">
|
|
1918
|
+
<div style="padding:1.5rem;border-radius:0.5rem;background:var(--color-bg-secondary);border:1px solid var(--color-border);">
|
|
1919
|
+
<h3 style="margin:0 0 0.5rem 0;color:var(--color-primary);">New Conversation</h3>
|
|
1920
|
+
<p style="margin:0;font-size:0.9rem;color:var(--color-text-secondary);">Create a new chat with any AI agent</p>
|
|
1921
|
+
</div>
|
|
1922
|
+
<div style="padding:1.5rem;border-radius:0.5rem;background:var(--color-bg-secondary);border:1px solid var(--color-border);">
|
|
1923
|
+
<h3 style="margin:0 0 0.5rem 0;color:var(--color-primary);">Available Agents</h3>
|
|
1924
|
+
<p style="margin:0;font-size:0.9rem;color:var(--color-text-secondary);">Claude Code, Gemini, OpenCode, and more</p>
|
|
1925
|
+
</div>
|
|
1926
|
+
</div>
|
|
1927
|
+
</div>
|
|
1928
|
+
`;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1863
1931
|
_showSkeletonLoading(conversationId) {
|
|
1864
1932
|
const outputEl = document.getElementById('output');
|
|
1865
1933
|
if (!outputEl) return;
|
|
@@ -1883,6 +1951,8 @@ class AgentGUIClient {
|
|
|
1883
1951
|
</div>
|
|
1884
1952
|
</div>
|
|
1885
1953
|
`;
|
|
1954
|
+
// Keep loading spinner visible during hydration
|
|
1955
|
+
this.showLoadingSpinner();
|
|
1886
1956
|
}
|
|
1887
1957
|
|
|
1888
1958
|
async streamToConversation(conversationId, prompt, agentId, model, subAgent) {
|
|
@@ -2248,10 +2318,14 @@ class AgentGUIClient {
|
|
|
2248
2318
|
.map(a => `<option value="${a.id}">${a.name.split(/[\s\-]+/)[0]}</option>`)
|
|
2249
2319
|
.join('');
|
|
2250
2320
|
this.ui.agentSelector.style.display = 'inline-block';
|
|
2321
|
+
console.log(`[Agent Selector] Loaded ${subAgents.length} sub-agents for ${cliAgentId}`);
|
|
2251
2322
|
this.loadModelsForAgent(cliAgentId);
|
|
2323
|
+
} else {
|
|
2324
|
+
console.log(`[Agent Selector] No sub-agents found for ${cliAgentId}`);
|
|
2252
2325
|
}
|
|
2253
|
-
} catch (
|
|
2326
|
+
} catch (err) {
|
|
2254
2327
|
// No sub-agents available for this CLI tool — keep hidden
|
|
2328
|
+
console.warn(`[Agent Selector] Failed to load sub-agents for ${cliAgentId}:`, err.message);
|
|
2255
2329
|
}
|
|
2256
2330
|
}
|
|
2257
2331
|
|
|
@@ -2722,6 +2796,8 @@ class AgentGUIClient {
|
|
|
2722
2796
|
this.conversationCache.delete(conversationId);
|
|
2723
2797
|
this.syncPromptState(conversationId);
|
|
2724
2798
|
this.restoreScrollPosition(conversationId);
|
|
2799
|
+
// Hydration complete - hide loading spinner
|
|
2800
|
+
this.hideLoadingSpinner();
|
|
2725
2801
|
// Prompt state is immutable: computed from shouldResumeStreaming via syncPromptState
|
|
2726
2802
|
// Do not call enableControls/disableControls here - prompt state is determined by streaming status
|
|
2727
2803
|
return;
|
|
@@ -2734,7 +2810,9 @@ class AgentGUIClient {
|
|
|
2734
2810
|
|
|
2735
2811
|
let fullData;
|
|
2736
2812
|
try {
|
|
2737
|
-
|
|
2813
|
+
// Load only recent chunks initially (lazy load older ones)
|
|
2814
|
+
// Use chunkLimit of 50 to make page load faster
|
|
2815
|
+
fullData = await window.wsClient.rpc('conv.full', { id: conversationId, chunkLimit: 50 });
|
|
2738
2816
|
} catch (e) {
|
|
2739
2817
|
if (e.code === 404) {
|
|
2740
2818
|
console.warn('Conversation no longer exists:', conversationId);
|
|
@@ -2967,9 +3045,12 @@ class AgentGUIClient {
|
|
|
2967
3045
|
|
|
2968
3046
|
this.restoreScrollPosition(conversationId);
|
|
2969
3047
|
this.setupScrollUpDetection(conversationId);
|
|
3048
|
+
// Hydration complete - hide loading spinner
|
|
3049
|
+
this.hideLoadingSpinner();
|
|
2970
3050
|
}
|
|
2971
3051
|
} catch (error) {
|
|
2972
3052
|
if (error.name === 'AbortError') return;
|
|
3053
|
+
this.hideLoadingSpinner();
|
|
2973
3054
|
console.error('Failed to load conversation messages:', error);
|
|
2974
3055
|
// Resume from last successful conversation if available, or fall back to any available conversation
|
|
2975
3056
|
const fallbackConv = prevConversationId ? prevConversationId : availableFallback?.id;
|
package/docs/screenshot-main.png
DELETED
|
Binary file
|
|
Binary file
|