agentgui 1.0.94 → 1.0.96
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/.prd +6 -0
- package/database.js +43 -1
- package/lib/claude-runner.js +24 -22
- package/package.json +1 -1
- package/server.js +133 -146
- package/static/index.html +422 -977
- package/static/js/client.js +229 -144
- package/static/js/features.js +43 -25
- package/static/js/websocket-manager.js +1 -1
package/static/js/client.js
CHANGED
|
@@ -160,6 +160,12 @@ class AgentGUIClient {
|
|
|
160
160
|
this.startExecution();
|
|
161
161
|
}
|
|
162
162
|
});
|
|
163
|
+
|
|
164
|
+
this.ui.messageInput.addEventListener('input', () => {
|
|
165
|
+
const el = this.ui.messageInput;
|
|
166
|
+
el.style.height = 'auto';
|
|
167
|
+
el.style.height = Math.min(el.scrollHeight, 150) + 'px';
|
|
168
|
+
});
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
// Setup theme toggle
|
|
@@ -193,66 +199,42 @@ class AgentGUIClient {
|
|
|
193
199
|
}
|
|
194
200
|
}
|
|
195
201
|
|
|
196
|
-
/**
|
|
197
|
-
* Handle incoming WebSocket message
|
|
198
|
-
*/
|
|
199
202
|
handleWebSocketMessage(data) {
|
|
200
203
|
try {
|
|
201
|
-
// Route by message type
|
|
202
204
|
switch (data.type) {
|
|
203
205
|
case 'streaming_start':
|
|
204
206
|
this.handleStreamingStart(data);
|
|
205
207
|
break;
|
|
206
|
-
|
|
207
208
|
case 'streaming_progress':
|
|
208
|
-
this.
|
|
209
|
+
this.handleStreamingProgress(data);
|
|
209
210
|
break;
|
|
210
|
-
|
|
211
211
|
case 'streaming_complete':
|
|
212
212
|
this.handleStreamingComplete(data);
|
|
213
213
|
break;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
case 'file_write':
|
|
217
|
-
case 'command_execute':
|
|
218
|
-
case 'git_status':
|
|
219
|
-
case 'error':
|
|
220
|
-
case 'text_block':
|
|
221
|
-
case 'code_block':
|
|
222
|
-
case 'thinking_block':
|
|
223
|
-
case 'tool_use':
|
|
224
|
-
this.queueEvent(data);
|
|
214
|
+
case 'streaming_error':
|
|
215
|
+
this.handleStreamingError(data);
|
|
225
216
|
break;
|
|
226
|
-
|
|
227
217
|
case 'conversation_created':
|
|
228
218
|
this.handleConversationCreated(data);
|
|
229
219
|
break;
|
|
230
|
-
|
|
231
220
|
case 'message_created':
|
|
232
221
|
this.handleMessageCreated(data);
|
|
233
222
|
break;
|
|
234
|
-
|
|
223
|
+
case 'queue_status':
|
|
224
|
+
this.handleQueueStatus(data);
|
|
225
|
+
break;
|
|
235
226
|
default:
|
|
236
|
-
|
|
227
|
+
break;
|
|
237
228
|
}
|
|
238
229
|
} catch (error) {
|
|
239
230
|
console.error('Message handling error:', error);
|
|
240
231
|
}
|
|
241
232
|
}
|
|
242
233
|
|
|
243
|
-
/**
|
|
244
|
-
* Queue event for rendering
|
|
245
|
-
*/
|
|
246
234
|
queueEvent(data) {
|
|
247
235
|
try {
|
|
248
|
-
// Process event
|
|
249
236
|
const processed = this.eventProcessor.processEvent(data);
|
|
250
237
|
if (!processed) return;
|
|
251
|
-
|
|
252
|
-
// Queue for rendering
|
|
253
|
-
this.renderer.queueEvent(processed);
|
|
254
|
-
|
|
255
|
-
// Track session events
|
|
256
238
|
if (data.sessionId && this.state.currentSession?.id === data.sessionId) {
|
|
257
239
|
this.state.sessionEvents.push(processed);
|
|
258
240
|
}
|
|
@@ -261,9 +243,6 @@ class AgentGUIClient {
|
|
|
261
243
|
}
|
|
262
244
|
}
|
|
263
245
|
|
|
264
|
-
/**
|
|
265
|
-
* Handle streaming start
|
|
266
|
-
*/
|
|
267
246
|
handleStreamingStart(data) {
|
|
268
247
|
console.log('Streaming started:', data);
|
|
269
248
|
this.state.isStreaming = true;
|
|
@@ -273,60 +252,148 @@ class AgentGUIClient {
|
|
|
273
252
|
agentId: data.agentId,
|
|
274
253
|
startTime: Date.now()
|
|
275
254
|
};
|
|
276
|
-
this.state.currentConversation = { id: data.conversationId };
|
|
277
255
|
this.state.sessionEvents = [];
|
|
256
|
+
this.state.streamingBlocks = [];
|
|
278
257
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
window.conversationManager.select(data.conversationId);
|
|
258
|
+
if (this.wsManager.isConnected) {
|
|
259
|
+
this.wsManager.subscribeToSession(data.sessionId);
|
|
282
260
|
}
|
|
283
261
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
262
|
+
const outputEl = document.getElementById('output');
|
|
263
|
+
if (outputEl) {
|
|
264
|
+
let messagesEl = outputEl.querySelector('.conversation-messages');
|
|
265
|
+
if (!messagesEl) {
|
|
266
|
+
outputEl.innerHTML = '<div class="conversation-messages"></div>';
|
|
267
|
+
messagesEl = outputEl.querySelector('.conversation-messages');
|
|
290
268
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
269
|
+
const streamingDiv = document.createElement('div');
|
|
270
|
+
streamingDiv.className = 'message message-assistant streaming-message';
|
|
271
|
+
streamingDiv.id = `streaming-${data.sessionId}`;
|
|
272
|
+
streamingDiv.innerHTML = `
|
|
273
|
+
<div class="message-role">Assistant</div>
|
|
274
|
+
<div class="message-blocks streaming-blocks"></div>
|
|
275
|
+
<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;">
|
|
276
|
+
<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>
|
|
277
|
+
Thinking...
|
|
278
|
+
</div>
|
|
279
|
+
`;
|
|
280
|
+
messagesEl.appendChild(streamingDiv);
|
|
281
|
+
this.scrollToBottom();
|
|
282
|
+
}
|
|
303
283
|
|
|
304
284
|
this.disableControls();
|
|
305
285
|
this.emit('streaming:start', data);
|
|
306
286
|
}
|
|
307
287
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
288
|
+
handleStreamingProgress(data) {
|
|
289
|
+
if (!data.block) return;
|
|
290
|
+
|
|
291
|
+
const block = data.block;
|
|
292
|
+
if (!this.state.streamingBlocks) this.state.streamingBlocks = [];
|
|
293
|
+
this.state.streamingBlocks.push(block);
|
|
294
|
+
|
|
295
|
+
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
296
|
+
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
297
|
+
if (!streamingEl) return;
|
|
298
|
+
|
|
299
|
+
const blocksEl = streamingEl.querySelector('.streaming-blocks');
|
|
300
|
+
if (!blocksEl) return;
|
|
301
|
+
|
|
302
|
+
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
303
|
+
|
|
304
|
+
if (block.type === 'text' && block.text) {
|
|
305
|
+
const existingTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
306
|
+
if (existingTextEl && !data.isResult) {
|
|
307
|
+
existingTextEl.innerHTML = this.renderBlockContent(block);
|
|
308
|
+
} else {
|
|
309
|
+
const div = document.createElement('div');
|
|
310
|
+
div.className = 'message-text streaming-text-current';
|
|
311
|
+
div.innerHTML = this.renderBlockContent(block);
|
|
312
|
+
blocksEl.appendChild(div);
|
|
313
|
+
}
|
|
314
|
+
} else if (block.type === 'tool_use') {
|
|
315
|
+
const prevTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
316
|
+
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
317
|
+
|
|
318
|
+
const div = document.createElement('div');
|
|
319
|
+
div.className = 'message-tool';
|
|
320
|
+
div.textContent = `[Tool: ${block.name || 'unknown'}]`;
|
|
321
|
+
blocksEl.appendChild(div);
|
|
322
|
+
} else if (block.type === 'tool_result') {
|
|
323
|
+
const div = document.createElement('div');
|
|
324
|
+
div.className = 'message-text';
|
|
325
|
+
div.innerHTML = `<em style="color:var(--color-text-secondary)">${this.escapeHtml(String(block.result || '').substring(0, 500))}</em>`;
|
|
326
|
+
blocksEl.appendChild(div);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (indicator) indicator.querySelector('span:last-child')?.remove();
|
|
330
|
+
if (indicator) {
|
|
331
|
+
const label = document.createElement('span');
|
|
332
|
+
label.textContent = block.type === 'tool_use' ? `Using ${block.name}...` : 'Responding...';
|
|
333
|
+
indicator.appendChild(label);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
this.scrollToBottom();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
renderBlockContent(block) {
|
|
340
|
+
if (block.type === 'text' && block.text) {
|
|
341
|
+
const text = block.text;
|
|
342
|
+
if (text.includes('<') && (text.includes('</') || text.includes('/>'))) {
|
|
343
|
+
return text;
|
|
344
|
+
}
|
|
345
|
+
return this.escapeHtml(text);
|
|
346
|
+
}
|
|
347
|
+
return this.escapeHtml(JSON.stringify(block));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
scrollToBottom() {
|
|
351
|
+
const scrollContainer = document.getElementById('output-scroll');
|
|
352
|
+
if (scrollContainer) {
|
|
353
|
+
requestAnimationFrame(() => {
|
|
354
|
+
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
handleStreamingError(data) {
|
|
360
|
+
console.error('Streaming error:', data);
|
|
361
|
+
this.state.isStreaming = false;
|
|
362
|
+
|
|
363
|
+
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
364
|
+
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
365
|
+
if (streamingEl) {
|
|
366
|
+
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
367
|
+
if (indicator) {
|
|
368
|
+
indicator.innerHTML = `<span style="color:var(--color-error);">Error: ${this.escapeHtml(data.error || 'Unknown error')}</span>`;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
this.enableControls();
|
|
373
|
+
this.emit('streaming:error', data);
|
|
374
|
+
}
|
|
375
|
+
|
|
311
376
|
handleStreamingComplete(data) {
|
|
312
377
|
console.log('Streaming completed:', data);
|
|
313
378
|
this.state.isStreaming = false;
|
|
314
379
|
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
380
|
+
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
381
|
+
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
382
|
+
if (streamingEl) {
|
|
383
|
+
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
384
|
+
if (indicator) indicator.remove();
|
|
385
|
+
streamingEl.classList.remove('streaming-message');
|
|
386
|
+
const prevTextEl = streamingEl.querySelector('.streaming-text-current');
|
|
387
|
+
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
388
|
+
|
|
389
|
+
const ts = document.createElement('div');
|
|
390
|
+
ts.className = 'message-timestamp';
|
|
391
|
+
ts.textContent = new Date().toLocaleString();
|
|
392
|
+
streamingEl.appendChild(ts);
|
|
393
|
+
}
|
|
323
394
|
|
|
324
395
|
this.enableControls();
|
|
325
|
-
this.emit('streaming:complete',
|
|
326
|
-
...data,
|
|
327
|
-
duration,
|
|
328
|
-
eventCount: this.state.sessionEvents.length
|
|
329
|
-
});
|
|
396
|
+
this.emit('streaming:complete', data);
|
|
330
397
|
}
|
|
331
398
|
|
|
332
399
|
/**
|
|
@@ -339,32 +406,55 @@ class AgentGUIClient {
|
|
|
339
406
|
}
|
|
340
407
|
}
|
|
341
408
|
|
|
342
|
-
/**
|
|
343
|
-
* Handle message created
|
|
344
|
-
*/
|
|
345
409
|
handleMessageCreated(data) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (scrollContainer) {
|
|
361
|
-
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
410
|
+
if (data.conversationId !== this.state.currentConversation?.id || !data.message) {
|
|
411
|
+
this.emit('message:created', data);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (data.message.role === 'assistant' && this.state.isStreaming) {
|
|
416
|
+
this.emit('message:created', data);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const outputEl = document.querySelector('.conversation-messages');
|
|
421
|
+
if (!outputEl) {
|
|
422
|
+
this.emit('message:created', data);
|
|
423
|
+
return;
|
|
364
424
|
}
|
|
425
|
+
|
|
426
|
+
const messageHtml = `
|
|
427
|
+
<div class="message message-${data.message.role}" data-msg-id="${data.message.id}">
|
|
428
|
+
<div class="message-role">${data.message.role.charAt(0).toUpperCase() + data.message.role.slice(1)}</div>
|
|
429
|
+
${this.renderMessageContent(data.message.content)}
|
|
430
|
+
<div class="message-timestamp">${new Date(data.message.created_at).toLocaleString()}</div>
|
|
431
|
+
</div>
|
|
432
|
+
`;
|
|
433
|
+
outputEl.insertAdjacentHTML('beforeend', messageHtml);
|
|
434
|
+
this.scrollToBottom();
|
|
365
435
|
this.emit('message:created', data);
|
|
366
436
|
}
|
|
367
437
|
|
|
438
|
+
handleQueueStatus(data) {
|
|
439
|
+
if (data.conversationId !== this.state.currentConversation?.id) return;
|
|
440
|
+
|
|
441
|
+
const outputEl = document.querySelector('.conversation-messages');
|
|
442
|
+
if (!outputEl) return;
|
|
443
|
+
|
|
444
|
+
let queueEl = outputEl.querySelector('.queue-indicator');
|
|
445
|
+
if (data.queueLength > 0) {
|
|
446
|
+
if (!queueEl) {
|
|
447
|
+
queueEl = document.createElement('div');
|
|
448
|
+
queueEl.className = 'queue-indicator';
|
|
449
|
+
queueEl.style.cssText = 'padding:0.5rem 1rem;margin:0.5rem 0;border-radius:0.375rem;background:var(--color-warning);color:#000;font-size:0.875rem;text-align:center;';
|
|
450
|
+
outputEl.appendChild(queueEl);
|
|
451
|
+
}
|
|
452
|
+
queueEl.textContent = `${data.queueLength} message${data.queueLength > 1 ? 's' : ''} queued`;
|
|
453
|
+
} else if (queueEl) {
|
|
454
|
+
queueEl.remove();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
368
458
|
/**
|
|
369
459
|
* Parse markdown code blocks from text
|
|
370
460
|
* Returns array of parts with type ('text' or 'code') and content/language/code
|
|
@@ -476,15 +566,7 @@ class AgentGUIClient {
|
|
|
476
566
|
}
|
|
477
567
|
}
|
|
478
568
|
|
|
479
|
-
/**
|
|
480
|
-
* Start execution
|
|
481
|
-
*/
|
|
482
569
|
async startExecution() {
|
|
483
|
-
if (this.state.isStreaming) {
|
|
484
|
-
this.showError('Streaming already in progress');
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
570
|
const prompt = this.ui.messageInput?.value || '';
|
|
489
571
|
const agentId = this.ui.agentSelector?.value || 'claude-code';
|
|
490
572
|
|
|
@@ -493,23 +575,31 @@ class AgentGUIClient {
|
|
|
493
575
|
return;
|
|
494
576
|
}
|
|
495
577
|
|
|
496
|
-
|
|
497
|
-
this.
|
|
578
|
+
if (this.ui.messageInput) {
|
|
579
|
+
this.ui.messageInput.value = '';
|
|
580
|
+
this.ui.messageInput.style.height = 'auto';
|
|
581
|
+
}
|
|
498
582
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
583
|
+
try {
|
|
584
|
+
if (this.state.currentConversation?.id) {
|
|
585
|
+
await this.streamToConversation(this.state.currentConversation.id, prompt, agentId);
|
|
586
|
+
} else {
|
|
587
|
+
this.disableControls();
|
|
588
|
+
const response = await fetch(window.__BASE_URL + '/api/conversations', {
|
|
589
|
+
method: 'POST',
|
|
590
|
+
headers: { 'Content-Type': 'application/json' },
|
|
591
|
+
body: JSON.stringify({ agentId, title: prompt.substring(0, 50) })
|
|
592
|
+
});
|
|
593
|
+
const { conversation } = await response.json();
|
|
594
|
+
this.state.currentConversation = conversation;
|
|
507
595
|
|
|
508
|
-
|
|
509
|
-
|
|
596
|
+
if (window.conversationManager) {
|
|
597
|
+
window.conversationManager.loadConversations();
|
|
598
|
+
window.conversationManager.select(conversation.id);
|
|
599
|
+
}
|
|
510
600
|
|
|
511
|
-
|
|
512
|
-
|
|
601
|
+
await this.streamToConversation(conversation.id, prompt, agentId);
|
|
602
|
+
}
|
|
513
603
|
} catch (error) {
|
|
514
604
|
console.error('Execution error:', error);
|
|
515
605
|
this.showError('Failed to start execution: ' + error.message);
|
|
@@ -517,33 +607,32 @@ class AgentGUIClient {
|
|
|
517
607
|
}
|
|
518
608
|
}
|
|
519
609
|
|
|
520
|
-
/**
|
|
521
|
-
* Stream execution to conversation
|
|
522
|
-
*/
|
|
523
610
|
async streamToConversation(conversationId, prompt, agentId) {
|
|
524
611
|
try {
|
|
612
|
+
if (this.wsManager.isConnected) {
|
|
613
|
+
this.wsManager.sendMessage({ type: 'subscribe', conversationId });
|
|
614
|
+
}
|
|
615
|
+
|
|
525
616
|
const response = await fetch(`${window.__BASE_URL}/api/conversations/${conversationId}/stream`, {
|
|
526
617
|
method: 'POST',
|
|
527
618
|
headers: { 'Content-Type': 'application/json' },
|
|
528
|
-
body: JSON.stringify({
|
|
529
|
-
content: prompt,
|
|
530
|
-
agentId,
|
|
531
|
-
skipPermissions: false
|
|
532
|
-
})
|
|
619
|
+
body: JSON.stringify({ content: prompt, agentId, skipPermissions: false })
|
|
533
620
|
});
|
|
534
621
|
|
|
535
|
-
if (!response.ok) {
|
|
536
|
-
throw new Error(`HTTP ${response.status}`);
|
|
537
|
-
}
|
|
622
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
538
623
|
|
|
539
|
-
const
|
|
624
|
+
const result = await response.json();
|
|
540
625
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
626
|
+
if (result.queued) {
|
|
627
|
+
console.log('Message queued, position:', result.queuePosition);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (result.session && this.wsManager.isConnected) {
|
|
632
|
+
this.wsManager.subscribeToSession(result.session.id);
|
|
544
633
|
}
|
|
545
634
|
|
|
546
|
-
this.emit('execution:started',
|
|
635
|
+
this.emit('execution:started', result);
|
|
547
636
|
} catch (error) {
|
|
548
637
|
console.error('Stream execution error:', error);
|
|
549
638
|
this.showError('Failed to stream execution: ' + error.message);
|
|
@@ -675,37 +764,33 @@ class AgentGUIClient {
|
|
|
675
764
|
}
|
|
676
765
|
}
|
|
677
766
|
|
|
678
|
-
/**
|
|
679
|
-
* Load and display conversation messages
|
|
680
|
-
*/
|
|
681
767
|
async loadConversationMessages(conversationId) {
|
|
682
768
|
try {
|
|
683
|
-
this.state.currentConversation = { id: conversationId };
|
|
684
|
-
|
|
685
|
-
// Fetch conversation details
|
|
686
769
|
const convResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}`);
|
|
687
770
|
const { conversation } = await convResponse.json();
|
|
771
|
+
this.state.currentConversation = conversation;
|
|
688
772
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
if (!messagesResponse.ok) {
|
|
692
|
-
throw new Error(`Failed to fetch messages: ${messagesResponse.status}`);
|
|
773
|
+
if (this.wsManager.isConnected) {
|
|
774
|
+
this.wsManager.sendMessage({ type: 'subscribe', conversationId });
|
|
693
775
|
}
|
|
776
|
+
|
|
777
|
+
const messagesResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}/messages`);
|
|
778
|
+
if (!messagesResponse.ok) throw new Error(`Failed to fetch messages: ${messagesResponse.status}`);
|
|
694
779
|
const messagesData = await messagesResponse.json();
|
|
695
780
|
|
|
696
|
-
// Clear output and display conversation header
|
|
697
781
|
const outputEl = document.getElementById('output');
|
|
698
782
|
if (outputEl) {
|
|
699
|
-
const wdInfo = conversation.workingDirectory ? `
|
|
783
|
+
const wdInfo = conversation.workingDirectory ? ` - ${this.escapeHtml(conversation.workingDirectory)}` : '';
|
|
700
784
|
outputEl.innerHTML = `
|
|
701
785
|
<div class="conversation-header">
|
|
702
786
|
<h2>${this.escapeHtml(conversation.title || 'Conversation')}</h2>
|
|
703
|
-
<p class="text-secondary">${conversation.agentType || 'unknown'}
|
|
787
|
+
<p class="text-secondary">${conversation.agentType || 'unknown'} - ${new Date(conversation.created_at).toLocaleDateString()}${wdInfo}</p>
|
|
704
788
|
</div>
|
|
705
789
|
<div class="conversation-messages">
|
|
706
790
|
${this.renderMessages(messagesData.messages || [])}
|
|
707
791
|
</div>
|
|
708
792
|
`;
|
|
793
|
+
this.scrollToBottom();
|
|
709
794
|
}
|
|
710
795
|
} catch (error) {
|
|
711
796
|
console.error('Failed to load conversation messages:', error);
|
package/static/js/features.js
CHANGED
|
@@ -10,36 +10,39 @@
|
|
|
10
10
|
let dragCounter = 0;
|
|
11
11
|
|
|
12
12
|
function init() {
|
|
13
|
-
|
|
13
|
+
setupSidebarToggle();
|
|
14
14
|
setupDragAndDrop();
|
|
15
15
|
setupViewToggle();
|
|
16
16
|
setupConversationListener();
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const overlay = document.querySelector('[data-sidebar-overlay]');
|
|
19
|
+
function setupSidebarToggle() {
|
|
20
|
+
var toggleBtn = document.querySelector('[data-sidebar-toggle]');
|
|
21
|
+
var sidebar = document.querySelector('[data-sidebar]');
|
|
22
|
+
var overlay = document.querySelector('[data-sidebar-overlay]');
|
|
24
23
|
|
|
25
|
-
if (!
|
|
24
|
+
if (!sidebar) return;
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
var savedState = localStorage.getItem('sidebar-collapsed');
|
|
27
|
+
if (savedState === 'true' && window.innerWidth > 768) {
|
|
28
|
+
sidebar.classList.add('collapsed');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isMobile() { return window.innerWidth <= 768; }
|
|
32
|
+
|
|
33
|
+
function toggleSidebar() {
|
|
34
|
+
if (isMobile()) {
|
|
35
|
+
var isOpen = sidebar.classList.contains('mobile-visible');
|
|
36
|
+
if (isOpen) { closeSidebar(); } else { openSidebar(); }
|
|
32
37
|
} else {
|
|
33
|
-
|
|
38
|
+
sidebar.classList.toggle('collapsed');
|
|
39
|
+
localStorage.setItem('sidebar-collapsed', sidebar.classList.contains('collapsed'));
|
|
34
40
|
}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
if (overlay) {
|
|
38
|
-
overlay.addEventListener('click', closeSidebar);
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
function openSidebar() {
|
|
42
44
|
sidebar.classList.add('mobile-visible');
|
|
45
|
+
sidebar.classList.remove('collapsed');
|
|
43
46
|
if (overlay) overlay.classList.add('visible');
|
|
44
47
|
}
|
|
45
48
|
|
|
@@ -48,17 +51,32 @@
|
|
|
48
51
|
if (overlay) overlay.classList.remove('visible');
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
if (toggleBtn) {
|
|
55
|
+
toggleBtn.addEventListener('click', function(e) {
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
toggleSidebar();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (overlay) {
|
|
62
|
+
overlay.addEventListener('click', closeSidebar);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
document.addEventListener('keydown', function(e) {
|
|
66
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'b') {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
toggleSidebar();
|
|
55
69
|
}
|
|
56
70
|
});
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
window.addEventListener('conversation-selected', function() {
|
|
73
|
+
if (isMobile()) closeSidebar();
|
|
74
|
+
});
|
|
75
|
+
|
|
59
76
|
window.addEventListener('resize', function() {
|
|
60
|
-
if (
|
|
61
|
-
|
|
77
|
+
if (!isMobile()) {
|
|
78
|
+
sidebar.classList.remove('mobile-visible');
|
|
79
|
+
if (overlay) overlay.classList.remove('visible');
|
|
62
80
|
}
|
|
63
81
|
});
|
|
64
82
|
}
|
|
@@ -175,7 +193,7 @@
|
|
|
175
193
|
currentView = view;
|
|
176
194
|
var bar = document.getElementById('viewToggleBar');
|
|
177
195
|
var chatArea = document.getElementById('output-scroll');
|
|
178
|
-
var execPanel = document.querySelector('.
|
|
196
|
+
var execPanel = document.querySelector('.input-section');
|
|
179
197
|
var fileBrowser = document.getElementById('fileBrowserContainer');
|
|
180
198
|
var iframe = document.getElementById('fileBrowserIframe');
|
|
181
199
|
|
|
@@ -50,7 +50,7 @@ class WebSocketManager {
|
|
|
50
50
|
getWebSocketURL() {
|
|
51
51
|
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
52
52
|
const baseURL = window.__BASE_URL || '/gm';
|
|
53
|
-
return `${protocol}//${window.location.host}${baseURL}/
|
|
53
|
+
return `${protocol}//${window.location.host}${baseURL}/sync`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|