agentgui 1.0.30 → 1.0.32
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/database.js +47 -23
- package/package.json +1 -1
- package/static/app.js +293 -80
- package/static/index.html +0 -7
package/database.js
CHANGED
|
@@ -245,16 +245,34 @@ export const queries = {
|
|
|
245
245
|
},
|
|
246
246
|
|
|
247
247
|
getMessage(id) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
248
|
+
const stmt = db.prepare('SELECT * FROM messages WHERE id = ?');
|
|
249
|
+
const msg = stmt.get(id);
|
|
250
|
+
if (msg && typeof msg.content === 'string') {
|
|
251
|
+
try {
|
|
252
|
+
msg.content = JSON.parse(msg.content);
|
|
253
|
+
} catch (_) {
|
|
254
|
+
// If it's not JSON, leave it as string
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return msg;
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
getConversationMessages(conversationId) {
|
|
261
|
+
const stmt = db.prepare(
|
|
262
|
+
'SELECT * FROM messages WHERE conversationId = ? ORDER BY created_at ASC'
|
|
263
|
+
);
|
|
264
|
+
const messages = stmt.all(conversationId);
|
|
265
|
+
return messages.map(msg => {
|
|
266
|
+
if (typeof msg.content === 'string') {
|
|
267
|
+
try {
|
|
268
|
+
msg.content = JSON.parse(msg.content);
|
|
269
|
+
} catch (_) {
|
|
270
|
+
// If it's not JSON, leave it as string
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return msg;
|
|
274
|
+
});
|
|
275
|
+
},
|
|
258
276
|
|
|
259
277
|
createSession(conversationId) {
|
|
260
278
|
const id = generateId('sess');
|
|
@@ -499,19 +517,25 @@ export const queries = {
|
|
|
499
517
|
if (content && !content.startsWith('[{"tool_use_id"')) {
|
|
500
518
|
messages.push({ id: obj.uuid || generateId('msg'), role: 'user', content, created_at: new Date(obj.timestamp).getTime() });
|
|
501
519
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
520
|
+
} else if (obj.type === 'assistant' && obj.message?.content) {
|
|
521
|
+
let text = '';
|
|
522
|
+
const content = obj.message.content;
|
|
523
|
+
if (Array.isArray(content)) {
|
|
524
|
+
// CRITICAL FIX: Join text blocks with newlines to preserve separation
|
|
525
|
+
const textBlocks = [];
|
|
526
|
+
for (const c of content) {
|
|
527
|
+
if (c.type === 'text' && c.text) {
|
|
528
|
+
textBlocks.push(c.text);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
// Join with double newline to preserve logical separation
|
|
532
|
+
text = textBlocks.join('\n\n');
|
|
533
|
+
} else if (typeof content === 'string') {
|
|
534
|
+
text = content;
|
|
535
|
+
}
|
|
536
|
+
if (text) {
|
|
537
|
+
messages.push({ id: obj.uuid || generateId('msg'), role: 'assistant', content: text, created_at: new Date(obj.timestamp).getTime() });
|
|
538
|
+
}
|
|
515
539
|
}
|
|
516
540
|
} catch (_) {}
|
|
517
541
|
}
|
package/package.json
CHANGED
package/static/app.js
CHANGED
|
@@ -96,10 +96,15 @@ class GMGUIApp {
|
|
|
96
96
|
console.error('[CRITICAL] GMGUIApp.init() failed:', err);
|
|
97
97
|
console.error('[CRITICAL] Stack:', err.stack);
|
|
98
98
|
throw err;
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Helper for authenticated API calls - ensures credentials sent for proxy auth
|
|
103
|
+
async apiFetch(url, options = {}) {
|
|
104
|
+
return fetch(url, { credentials: 'include', ...options });
|
|
105
|
+
}
|
|
101
106
|
|
|
102
|
-
|
|
107
|
+
async init() {
|
|
103
108
|
console.log('[DEBUG] Init: Starting initialization');
|
|
104
109
|
console.log('[DEBUG] Init: BASE_URL =', BASE_URL);
|
|
105
110
|
console.log('[DEBUG] Init: Window width:', window.innerWidth);
|
|
@@ -155,7 +160,7 @@ class GMGUIApp {
|
|
|
155
160
|
async verifyConsistency() {
|
|
156
161
|
// Silent consistency check - only log if mismatch found
|
|
157
162
|
try {
|
|
158
|
-
const res = await
|
|
163
|
+
const res = await this.apiFetch(BASE_URL + '/api/conversations');
|
|
159
164
|
if (!res.ok) return;
|
|
160
165
|
|
|
161
166
|
const data = await res.json();
|
|
@@ -176,7 +181,7 @@ class GMGUIApp {
|
|
|
176
181
|
|
|
177
182
|
async autoImportClaudeCode() {
|
|
178
183
|
try {
|
|
179
|
-
await
|
|
184
|
+
await this.apiFetch(BASE_URL + '/api/import/claude-code');
|
|
180
185
|
} catch (e) {
|
|
181
186
|
console.error('autoImportClaudeCode:', e);
|
|
182
187
|
}
|
|
@@ -356,7 +361,7 @@ class GMGUIApp {
|
|
|
356
361
|
|
|
357
362
|
async fetchHome() {
|
|
358
363
|
try {
|
|
359
|
-
const res = await
|
|
364
|
+
const res = await this.apiFetch(BASE_URL + '/api/home');
|
|
360
365
|
if (res.ok) {
|
|
361
366
|
const data = await res.json();
|
|
362
367
|
localStorage.setItem('gmgui-home', data.home);
|
|
@@ -391,35 +396,129 @@ class GMGUIApp {
|
|
|
391
396
|
return p.startsWith('~') ? p.replace('~', home) : p;
|
|
392
397
|
}
|
|
393
398
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
399
|
+
setupEventListeners() {
|
|
400
|
+
window.addEventListener('focus', () => {
|
|
401
|
+
this.autoImportClaudeCode().then(() => {
|
|
402
|
+
this.fetchConversations().then(() => this.renderChatHistory());
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// THEME CHANGE LISTENER: Update HTML blocks when theme changes
|
|
407
|
+
// Listen for theme changes on document element
|
|
408
|
+
const themeObserver = new MutationObserver(() => {
|
|
409
|
+
console.log('[THEME] Theme changed, updating HTML blocks');
|
|
410
|
+
this.updateHtmlBlockThemes();
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
themeObserver.observe(document.documentElement, {
|
|
414
|
+
attributes: true,
|
|
415
|
+
attributeFilter: ['data-theme']
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Also listen for system theme changes
|
|
419
|
+
if (window.matchMedia) {
|
|
420
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
421
|
+
console.log('[THEME] System theme changed, updating HTML blocks');
|
|
422
|
+
this.updateHtmlBlockThemes();
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const input = document.getElementById('messageInput');
|
|
427
|
+
if (input) {
|
|
428
|
+
input.addEventListener('keydown', (e) => {
|
|
429
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
430
|
+
e.preventDefault();
|
|
431
|
+
this.sendMessage();
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
input.addEventListener('input', () => this.updateSendButtonState());
|
|
435
|
+
}
|
|
436
|
+
document.getElementById('autoScroll')?.addEventListener('change', (e) => {
|
|
437
|
+
this.settings.autoScroll = e.target.checked;
|
|
438
|
+
this.saveSettings();
|
|
439
|
+
});
|
|
440
|
+
document.getElementById('connectTimeout')?.addEventListener('change', (e) => {
|
|
441
|
+
this.settings.connectTimeout = parseInt(e.target.value) * 1000;
|
|
442
|
+
this.saveSettings();
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
updateHtmlBlockThemes() {
|
|
447
|
+
// Update theme attribute and CSS for all existing HTML blocks
|
|
448
|
+
const currentTheme = document.documentElement.getAttribute('data-theme') ||
|
|
449
|
+
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
450
|
+
|
|
451
|
+
// CRITICAL: Remove old theme styles and inject new ones for all HTML blocks
|
|
452
|
+
document.querySelectorAll('.html-content').forEach(content => {
|
|
453
|
+
// Remove any existing theme style tags
|
|
454
|
+
const oldStyles = content.querySelectorAll('style');
|
|
455
|
+
oldStyles.forEach(style => {
|
|
456
|
+
if (style.textContent.includes('.html-content')) {
|
|
457
|
+
style.remove();
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Inject new theme-aware CSS
|
|
462
|
+
const themeCSS = currentTheme === 'dark'
|
|
463
|
+
? `<style>
|
|
464
|
+
.html-content {
|
|
465
|
+
color: #f8fafc;
|
|
466
|
+
background: transparent;
|
|
467
|
+
}
|
|
468
|
+
.html-content p { color: #cbd5e1; }
|
|
469
|
+
.html-content h1, .html-content h2, .html-content h3,
|
|
470
|
+
.html-content h4, .html-content h5, .html-content h6 {
|
|
471
|
+
color: #f8fafc;
|
|
472
|
+
}
|
|
473
|
+
.html-content a { color: #6366f1; }
|
|
474
|
+
.html-content code { color: #c7d2fe; background: rgba(0,0,0,0.3); }
|
|
475
|
+
.html-content pre { background: rgba(0,0,0,0.5); color: #e0e7ff; }
|
|
476
|
+
.html-content table { border-color: #334155; }
|
|
477
|
+
.html-content th { background: #1a202c; color: #f8fafc; }
|
|
478
|
+
.html-content td { border-color: #334155; }
|
|
479
|
+
.html-content blockquote { border-color: #334155; color: #cbd5e1; }
|
|
480
|
+
.html-content ul, .html-content ol { color: #cbd5e1; }
|
|
481
|
+
.html-content li { color: #cbd5e1; }
|
|
482
|
+
</style>`
|
|
483
|
+
: `<style>
|
|
484
|
+
.html-content {
|
|
485
|
+
color: #1d2129;
|
|
486
|
+
background: transparent;
|
|
487
|
+
}
|
|
488
|
+
.html-content p { color: #475569; }
|
|
489
|
+
.html-content h1, .html-content h2, .html-content h3,
|
|
490
|
+
.html-content h4, .html-content h5, .html-content h6 {
|
|
491
|
+
color: #1d2129;
|
|
492
|
+
}
|
|
493
|
+
.html-content a { color: #4f46e5; }
|
|
494
|
+
.html-content code { color: #6366f1; background: rgba(99,102,241,0.1); }
|
|
495
|
+
.html-content pre { background: #f3f4f6; color: #1d2129; }
|
|
496
|
+
.html-content table { border-color: #e5e7eb; }
|
|
497
|
+
.html-content th { background: #f9fafb; color: #1d2129; }
|
|
498
|
+
.html-content td { border-color: #e5e7eb; }
|
|
499
|
+
.html-content blockquote { border-color: #e5e7eb; color: #475569; }
|
|
500
|
+
.html-content ul, .html-content ol { color: #475569; }
|
|
501
|
+
.html-content li { color: #475569; }
|
|
502
|
+
</style>`;
|
|
503
|
+
|
|
504
|
+
// Create a temporary wrapper to parse and insert the style
|
|
505
|
+
const tempDiv = document.createElement('div');
|
|
506
|
+
tempDiv.innerHTML = themeCSS;
|
|
507
|
+
const styleEl = tempDiv.querySelector('style');
|
|
508
|
+
if (styleEl) {
|
|
509
|
+
content.insertBefore(styleEl.cloneNode(true), content.firstChild);
|
|
406
510
|
}
|
|
511
|
+
|
|
512
|
+
// Update data-theme attribute
|
|
513
|
+
content.setAttribute('data-theme', currentTheme);
|
|
407
514
|
});
|
|
408
|
-
|
|
515
|
+
|
|
516
|
+
console.log(`[THEME] Updated ${document.querySelectorAll('.html-content').length} HTML blocks to ${currentTheme} mode`);
|
|
409
517
|
}
|
|
410
|
-
document.getElementById('autoScroll')?.addEventListener('change', (e) => {
|
|
411
|
-
this.settings.autoScroll = e.target.checked;
|
|
412
|
-
this.saveSettings();
|
|
413
|
-
});
|
|
414
|
-
document.getElementById('connectTimeout')?.addEventListener('change', (e) => {
|
|
415
|
-
this.settings.connectTimeout = parseInt(e.target.value) * 1000;
|
|
416
|
-
this.saveSettings();
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
518
|
|
|
420
519
|
async fetchAgents() {
|
|
421
520
|
try {
|
|
422
|
-
const res = await
|
|
521
|
+
const res = await this.apiFetch(BASE_URL + '/api/agents');
|
|
423
522
|
const data = await res.json();
|
|
424
523
|
if (data.agents) {
|
|
425
524
|
data.agents.forEach(a => this.agents.set(a.id, a));
|
|
@@ -432,7 +531,7 @@ class GMGUIApp {
|
|
|
432
531
|
async fetchConversations() {
|
|
433
532
|
try {
|
|
434
533
|
console.log('[DEBUG] fetchConversations: Starting fetch from', BASE_URL + '/api/conversations');
|
|
435
|
-
const res = await
|
|
534
|
+
const res = await this.apiFetch(BASE_URL + '/api/conversations');
|
|
436
535
|
console.log('[DEBUG] fetchConversations: Response status:', res.status);
|
|
437
536
|
|
|
438
537
|
if (!res.ok) {
|
|
@@ -470,7 +569,7 @@ class GMGUIApp {
|
|
|
470
569
|
|
|
471
570
|
async fetchMessages(conversationId) {
|
|
472
571
|
try {
|
|
473
|
-
const res = await
|
|
572
|
+
const res = await this.apiFetch(`${BASE_URL}/api/conversations/${conversationId}/messages`);
|
|
474
573
|
const data = await res.json();
|
|
475
574
|
return data.messages || [];
|
|
476
575
|
} catch (e) {
|
|
@@ -589,7 +688,7 @@ class GMGUIApp {
|
|
|
589
688
|
|
|
590
689
|
async deleteConversation(id) {
|
|
591
690
|
try {
|
|
592
|
-
const res = await
|
|
691
|
+
const res = await this.apiFetch(`${BASE_URL}/api/conversations/${id}`, { method: 'DELETE' });
|
|
593
692
|
if (!res.ok) {
|
|
594
693
|
console.error('deleteConversation failed:', res.status);
|
|
595
694
|
return;
|
|
@@ -745,25 +844,129 @@ class GMGUIApp {
|
|
|
745
844
|
return elements.length > 0 ? elements : null;
|
|
746
845
|
}
|
|
747
846
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
847
|
+
renderTextOrHtml(text) {
|
|
848
|
+
if (this.looksLikeHtml(text)) {
|
|
849
|
+
return this.createSandboxedHtml(text);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// CRITICAL FIX: Don't bundle all text into one bubble
|
|
853
|
+
// Try splitting by paragraph breaks first (double newlines)
|
|
854
|
+
let parts = text.split('\n\n').filter(p => p.trim());
|
|
855
|
+
|
|
856
|
+
// If no paragraphs found, try splitting by single newlines
|
|
857
|
+
// (handles imported messages that may not have proper paragraph breaks)
|
|
858
|
+
if (parts.length === 1) {
|
|
859
|
+
const singleNewlines = text.split('\n').filter(p => p.trim());
|
|
860
|
+
// Only use single newlines if we get reasonable chunks (3+ non-empty lines)
|
|
861
|
+
if (singleNewlines.length >= 3) {
|
|
862
|
+
parts = singleNewlines;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// If still just one part and it's very long (>500 chars), split by sentences
|
|
867
|
+
if (parts.length === 1 && text.length > 500) {
|
|
868
|
+
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
|
|
869
|
+
if (sentences.length > 1) {
|
|
870
|
+
parts = sentences.map(s => s.trim()).filter(s => s);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (parts.length === 1) {
|
|
875
|
+
// Single item - just one bubble
|
|
876
|
+
const bubble = document.createElement('div');
|
|
877
|
+
bubble.className = 'message-bubble';
|
|
878
|
+
bubble.textContent = text;
|
|
879
|
+
return bubble;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// Multiple parts - create separate bubbles for each
|
|
883
|
+
const container = document.createElement('div');
|
|
884
|
+
container.className = 'message-bubbles-container';
|
|
885
|
+
|
|
886
|
+
for (const part of parts) {
|
|
887
|
+
const bubble = document.createElement('div');
|
|
888
|
+
bubble.className = 'message-bubble';
|
|
889
|
+
bubble.textContent = part;
|
|
890
|
+
container.appendChild(bubble);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
return container;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
createSandboxedHtml(rawHtml) {
|
|
897
|
+
const wrap = document.createElement('div');
|
|
898
|
+
wrap.className = 'html-block rendered-html';
|
|
899
|
+
const content = document.createElement('div');
|
|
900
|
+
content.className = 'html-content';
|
|
901
|
+
|
|
902
|
+
// Get current theme to apply to HTML content
|
|
903
|
+
const currentTheme = document.documentElement.getAttribute('data-theme') ||
|
|
904
|
+
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
905
|
+
|
|
906
|
+
// CRITICAL: Inject theme-aware CSS to ensure text colors work in dark/light mode
|
|
907
|
+
const themeCSS = currentTheme === 'dark'
|
|
908
|
+
? `<style>
|
|
909
|
+
.html-content {
|
|
910
|
+
color: #f8fafc;
|
|
911
|
+
background: transparent;
|
|
912
|
+
}
|
|
913
|
+
.html-content p { color: #cbd5e1; }
|
|
914
|
+
.html-content h1, .html-content h2, .html-content h3,
|
|
915
|
+
.html-content h4, .html-content h5, .html-content h6 {
|
|
916
|
+
color: #f8fafc;
|
|
917
|
+
}
|
|
918
|
+
.html-content a { color: #6366f1; }
|
|
919
|
+
.html-content code { color: #c7d2fe; background: rgba(0,0,0,0.3); }
|
|
920
|
+
.html-content pre { background: rgba(0,0,0,0.5); color: #e0e7ff; }
|
|
921
|
+
.html-content table { border-color: #334155; }
|
|
922
|
+
.html-content th { background: #1a202c; color: #f8fafc; }
|
|
923
|
+
.html-content td { border-color: #334155; }
|
|
924
|
+
.html-content blockquote { border-color: #334155; color: #cbd5e1; }
|
|
925
|
+
.html-content ul, .html-content ol { color: #cbd5e1; }
|
|
926
|
+
.html-content li { color: #cbd5e1; }
|
|
927
|
+
</style>`
|
|
928
|
+
: `<style>
|
|
929
|
+
.html-content {
|
|
930
|
+
color: #1d2129;
|
|
931
|
+
background: transparent;
|
|
932
|
+
}
|
|
933
|
+
.html-content p { color: #475569; }
|
|
934
|
+
.html-content h1, .html-content h2, .html-content h3,
|
|
935
|
+
.html-content h4, .html-content h5, .html-content h6 {
|
|
936
|
+
color: #1d2129;
|
|
937
|
+
}
|
|
938
|
+
.html-content a { color: #4f46e5; }
|
|
939
|
+
.html-content code { color: #6366f1; background: rgba(99,102,241,0.1); }
|
|
940
|
+
.html-content pre { background: #f3f4f6; color: #1d2129; }
|
|
941
|
+
.html-content table { border-color: #e5e7eb; }
|
|
942
|
+
.html-content th { background: #f9fafb; color: #1d2129; }
|
|
943
|
+
.html-content td { border-color: #e5e7eb; }
|
|
944
|
+
.html-content blockquote { border-color: #e5e7eb; color: #475569; }
|
|
945
|
+
.html-content ul, .html-content ol { color: #475569; }
|
|
946
|
+
.html-content li { color: #475569; }
|
|
947
|
+
</style>`;
|
|
948
|
+
|
|
949
|
+
// CRITICAL: Ensure RippleUI styles are available for agent HTML
|
|
950
|
+
// Agent responses use RippleUI/Tailwind classes, so wrap in a context that has those styles
|
|
951
|
+
let enhancedHtml = themeCSS + rawHtml;
|
|
952
|
+
|
|
953
|
+
// If HTML doesn't already have the RippleUI wrapper classes, add them
|
|
954
|
+
if (!rawHtml.includes('space-y-4') && !rawHtml.includes('card') && !rawHtml.includes('alert')) {
|
|
955
|
+
// Wrap in RippleUI container if agent didn't already wrap it
|
|
956
|
+
enhancedHtml = themeCSS + `<div class="space-y-4 p-6 max-w-4xl">${rawHtml}</div>`;
|
|
957
|
+
console.log('[HTML] Wrapped agent HTML in RippleUI container with theme CSS');
|
|
958
|
+
} else {
|
|
959
|
+
console.log('[HTML] Agent HTML already has RippleUI classes, applying theme CSS');
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
content.innerHTML = this.sanitizeHtml(enhancedHtml);
|
|
963
|
+
wrap.appendChild(content);
|
|
964
|
+
|
|
965
|
+
// Apply theme attribute to content so nested elements inherit
|
|
966
|
+
content.setAttribute('data-theme', currentTheme);
|
|
967
|
+
|
|
968
|
+
return wrap;
|
|
969
|
+
}
|
|
767
970
|
|
|
768
971
|
addMessageToDisplay(msg) {
|
|
769
972
|
const div = document.getElementById('chatMessages');
|
|
@@ -810,30 +1013,40 @@ class GMGUIApp {
|
|
|
810
1013
|
});
|
|
811
1014
|
}
|
|
812
1015
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1016
|
+
// CRITICAL: Agent responses are now HTML from system prompt
|
|
1017
|
+
// Check if we have text first (which should be HTML)
|
|
1018
|
+
if (msg.content.text && !hasHtmlContent) {
|
|
1019
|
+
// ALWAYS check if text itself contains HTML first
|
|
1020
|
+
if (this.looksLikeHtml(msg.content.text)) {
|
|
1021
|
+
console.log('[HTML] ✅ Agent response is HTML - rendering directly (NOT segmenting)');
|
|
1022
|
+
el.appendChild(this.createSandboxedHtml(msg.content.text));
|
|
1023
|
+
} else {
|
|
1024
|
+
// Only if NOT HTML, then try segmenting
|
|
1025
|
+
console.log('[HTML] Text is not HTML, attempting segmentation');
|
|
1026
|
+
if (msg.content.segments && Array.isArray(msg.content.segments)) {
|
|
1027
|
+
console.log('[HTML] Rendering', msg.content.segments.length, 'segments');
|
|
1028
|
+
msg.content.segments.forEach(segment => {
|
|
1029
|
+
el.appendChild(this.renderSegment(segment));
|
|
1030
|
+
});
|
|
1031
|
+
} else {
|
|
1032
|
+
const parsed = this.parseAndRenderContent(msg.content.text);
|
|
1033
|
+
if (parsed) {
|
|
1034
|
+
parsed.forEach(elem => el.appendChild(elem));
|
|
1035
|
+
} else {
|
|
1036
|
+
const bubble = document.createElement('div');
|
|
1037
|
+
bubble.className = 'message-bubble';
|
|
1038
|
+
bubble.textContent = msg.content.text;
|
|
1039
|
+
el.appendChild(bubble);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
} else if (msg.content.segments && Array.isArray(msg.content.segments) && !hasHtmlContent) {
|
|
1044
|
+
// Fallback: only use segments if we have them and no text
|
|
1045
|
+
console.log('[HTML] No text content, rendering segments');
|
|
1046
|
+
msg.content.segments.forEach(segment => {
|
|
1047
|
+
el.appendChild(this.renderSegment(segment));
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
837
1050
|
|
|
838
1051
|
// Display metadata if available
|
|
839
1052
|
if (msg.content.metadata) {
|
|
@@ -1041,7 +1254,7 @@ class GMGUIApp {
|
|
|
1041
1254
|
? folderPath.split('/').pop() || folderPath
|
|
1042
1255
|
: `Chat ${this.conversations.size + 1}`;
|
|
1043
1256
|
try {
|
|
1044
|
-
const res = await
|
|
1257
|
+
const res = await this.apiFetch(BASE_URL + '/api/conversations', {
|
|
1045
1258
|
method: 'POST',
|
|
1046
1259
|
headers: { 'Content-Type': 'application/json' },
|
|
1047
1260
|
body: JSON.stringify({ agentId: this.selectedAgent || 'claude-code', title }),
|
|
@@ -1082,7 +1295,7 @@ class GMGUIApp {
|
|
|
1082
1295
|
|
|
1083
1296
|
try {
|
|
1084
1297
|
const folderPath = conv?.folderPath || localStorage.getItem('gmgui-home') || '/config';
|
|
1085
|
-
const res = await
|
|
1298
|
+
const res = await this.apiFetch(`${BASE_URL}/api/conversations/${this.currentConversation}/messages`, {
|
|
1086
1299
|
method: 'POST',
|
|
1087
1300
|
headers: { 'Content-Type': 'application/json' },
|
|
1088
1301
|
body: JSON.stringify({
|
|
@@ -1128,7 +1341,7 @@ class GMGUIApp {
|
|
|
1128
1341
|
|
|
1129
1342
|
this.pollingInterval = setInterval(async () => {
|
|
1130
1343
|
try {
|
|
1131
|
-
const res = await
|
|
1344
|
+
const res = await this.apiFetch(`${BASE_URL}/api/conversations/${conversationId}/messages`);
|
|
1132
1345
|
const data = await res.json();
|
|
1133
1346
|
const messages = data.messages || [];
|
|
1134
1347
|
|
|
@@ -1295,7 +1508,7 @@ class GMGUIApp {
|
|
|
1295
1508
|
if (!list) return;
|
|
1296
1509
|
list.innerHTML = '<div style="padding: 1rem; color: var(--text-tertiary);">Loading...</div>';
|
|
1297
1510
|
try {
|
|
1298
|
-
const res = await
|
|
1511
|
+
const res = await this.apiFetch(BASE_URL + '/api/folders', {
|
|
1299
1512
|
method: 'POST',
|
|
1300
1513
|
headers: { 'Content-Type': 'application/json' },
|
|
1301
1514
|
body: JSON.stringify({ path: folderPath }),
|
|
@@ -1377,7 +1590,7 @@ function createChatInFolder() {
|
|
|
1377
1590
|
async function importClaudeCodeConversations() {
|
|
1378
1591
|
closeNewChatModal();
|
|
1379
1592
|
try {
|
|
1380
|
-
const res = await
|
|
1593
|
+
const res = await this.apiFetch(BASE_URL + '/api/import/claude-code');
|
|
1381
1594
|
const data = await res.json();
|
|
1382
1595
|
|
|
1383
1596
|
if (!data.imported) {
|
package/static/index.html
CHANGED
|
@@ -130,13 +130,6 @@
|
|
|
130
130
|
<div class="chat-option-desc">Contextualize chat to a specific folder</div>
|
|
131
131
|
</div>
|
|
132
132
|
</button>
|
|
133
|
-
<button class="chat-option-btn" onclick="importClaudeCodeConversations()">
|
|
134
|
-
<span class="chat-option-icon">📥</span>
|
|
135
|
-
<div class="chat-option-content">
|
|
136
|
-
<div class="chat-option-title">Import Claude Code conversations</div>
|
|
137
|
-
<div class="chat-option-desc">Load existing conversations from Claude Code</div>
|
|
138
|
-
</div>
|
|
139
|
-
</button>
|
|
140
133
|
</div>
|
|
141
134
|
</div>
|
|
142
135
|
</div>
|