added brain and session viewer

This commit is contained in:
2026-03-13 16:45:56 +01:00
parent 6a04d861bd
commit 706005d7f5
3 changed files with 438 additions and 0 deletions
+212
View File
@@ -160,6 +160,43 @@
</div>
</div>
<!-- Session + Brain Viewer -->
<div class="grid" style="grid-template-columns: 1fr 1fr;">
<div class="card">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
<h2>Sessions</h2>
<button class="btn secondary" onclick="loadSessions()" style="padding:4px 10px;font-size:11px;">Laden</button>
</div>
<div id="sessions-list" style="max-height:300px;overflow-y:auto;font-size:12px;"></div>
<div id="session-detail" style="display:none;margin-top:8px;background:#080810;border:1px solid #1E1E2E;border-radius:4px;padding:8px;max-height:300px;overflow-y:auto;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;">
<span style="font-size:11px;color:#0096FF;font-weight:bold;" id="session-detail-title"></span>
<button class="btn secondary" onclick="closeSessionDetail()" style="padding:2px 8px;font-size:10px;">Schliessen</button>
</div>
<div id="session-detail-content" style="font-size:11px;line-height:1.5;"></div>
</div>
</div>
<div class="card">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
<h2>Brain / Memory</h2>
<button class="btn secondary" onclick="loadBrain()" style="padding:4px 10px;font-size:11px;">Laden</button>
</div>
<div id="brain-list" style="max-height:200px;overflow-y:auto;font-size:12px;"></div>
<div id="brain-content" style="display:none;margin-top:8px;background:#080810;border:1px solid #1E1E2E;border-radius:4px;padding:8px;max-height:300px;overflow-y:auto;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;">
<span style="font-size:11px;color:#0096FF;font-weight:bold;" id="brain-content-title"></span>
<button class="btn secondary" onclick="closeBrainContent()" style="padding:2px 8px;font-size:10px;">Schliessen</button>
</div>
<pre id="brain-content-text" style="font-size:11px;line-height:1.5;white-space:pre-wrap;color:#E0E0F0;margin:0;"></pre>
</div>
<div id="brain-empty" style="display:none;text-align:center;padding:20px;color:#555570;">
<div style="font-size:24px;margin-bottom:8px;">🧠</div>
<div style="font-size:12px;">Gehirn ist leer</div>
<div style="font-size:10px;margin-top:4px;">ARIA speichert Erinnerungen wenn sie etwas Wichtiges lernt</div>
</div>
</div>
</div>
<!-- Logs mit Tabs -->
<div class="card" style="margin-top:12px; padding: 8px 0 0 0;">
<div style="padding: 0 12px;">
@@ -420,6 +457,16 @@
showDockerLogs(msg);
return;
}
// Session + Brain Viewer
if (msg.type === 'sessions_list') { renderSessions(msg); return; }
if (msg.type === 'session_detail') { renderSessionDetail(msg); return; }
if (msg.type === 'session_deleted') {
if (msg.ok) loadSessions(); // Liste neu laden
else alert('Loeschen fehlgeschlagen: ' + (msg.error || '?'));
return;
}
if (msg.type === 'brain_list') { renderBrainList(msg); return; }
if (msg.type === 'brain_content') { renderBrainContent(msg); return; }
if (msg.type === 'response') { return; }
};
}
@@ -794,6 +841,171 @@
}
}
// ── Session Viewer ────────────────────────────────────────
function loadSessions() {
document.getElementById('sessions-list').innerHTML = '<div style="color:#8888AA;padding:8px;">Lade...</div>';
send({ action: 'list_sessions' });
}
function renderSessions(data) {
const container = document.getElementById('sessions-list');
if (data.error) {
container.innerHTML = `<div style="color:#FF6B6B;padding:8px;">Fehler: ${escapeHtml(data.error)}</div>`;
return;
}
if (!data.sessions || data.sessions.length === 0) {
container.innerHTML = data.raw
? `<pre style="color:#555570;font-size:10px;white-space:pre-wrap;padding:8px;">${escapeHtml(data.raw)}</pre>`
: '<div style="color:#555570;padding:8px;text-align:center;">Keine Sessions gefunden</div>';
return;
}
let html = '<table style="width:100%;border-collapse:collapse;">';
html += '<tr style="color:#8888AA;font-size:10px;text-align:left;border-bottom:1px solid #1E1E2E;">'
+ '<th style="padding:4px 6px;">Session</th>'
+ '<th style="padding:4px 6px;">Zeilen</th>'
+ '<th style="padding:4px 6px;">Groesse</th>'
+ '<th style="padding:4px 6px;">Zuletzt</th>'
+ '<th style="padding:4px 6px;"></th></tr>';
for (const s of data.sessions) {
const date = s.modified ? new Date(s.modified * 1000).toLocaleString('de-DE') : '?';
const key = escapeHtml(s.sessionKey || s.path.split('/').pop());
html += `<tr style="border-bottom:1px solid #0D0D1A;cursor:pointer;" onmouseover="this.style.background='#1E1E2E'" onmouseout="this.style.background=''">`
+ `<td style="padding:4px 6px;color:#E0E0F0;" onclick="viewSession('${escapeHtml(s.path)}')">${key}</td>`
+ `<td style="padding:4px 6px;color:#8888AA;">${s.lines}</td>`
+ `<td style="padding:4px 6px;color:#8888AA;">${escapeHtml(s.size)}</td>`
+ `<td style="padding:4px 6px;color:#8888AA;font-size:10px;">${date}</td>`
+ `<td style="padding:4px 6px;"><button class="btn secondary" onclick="event.stopPropagation();deleteSession('${escapeHtml(s.path)}')" style="padding:2px 6px;font-size:10px;color:#FF6B6B;">X</button></td>`
+ '</tr>';
}
html += '</table>';
container.innerHTML = html;
}
function viewSession(path) {
const detail = document.getElementById('session-detail');
const title = document.getElementById('session-detail-title');
const content = document.getElementById('session-detail-content');
detail.style.display = 'block';
title.textContent = path.split('/').pop();
content.innerHTML = '<div style="color:#8888AA;">Lade...</div>';
send({ action: 'read_session', sessionPath: path });
}
function renderSessionDetail(data) {
const content = document.getElementById('session-detail-content');
if (data.error) {
content.innerHTML = `<div style="color:#FF6B6B;">${escapeHtml(data.error)}</div>`;
return;
}
if (data.raw) {
content.innerHTML = `<pre style="color:#555570;font-size:10px;white-space:pre-wrap;">${escapeHtml(data.raw)}</pre>`;
return;
}
if (!data.messages || data.messages.length === 0) {
content.innerHTML = '<div style="color:#555570;">Keine Nachrichten</div>';
return;
}
let html = '';
for (const msg of data.messages) {
const role = msg.role || msg.type || '?';
const text = extractMessageText(msg);
const roleColor = role === 'user' ? '#0096FF' : role === 'assistant' ? '#34C759' : '#8888AA';
html += `<div style="margin-bottom:4px;padding:4px 6px;border-left:2px solid ${roleColor};background:#0D0D1A;border-radius:0 4px 4px 0;">`
+ `<span style="color:${roleColor};font-size:10px;font-weight:bold;">${escapeHtml(role)}</span> `
+ `<span style="color:#E0E0F0;">${escapeHtml(text.slice(0, 500))}${text.length > 500 ? '...' : ''}</span>`
+ '</div>';
}
content.innerHTML = html;
}
function extractMessageText(msg) {
if (typeof msg.content === 'string') return msg.content;
if (Array.isArray(msg.content)) {
return msg.content.filter(c => c.type === 'text').map(c => c.text || '').join('');
}
if (msg.text) return msg.text;
if (msg.message) return typeof msg.message === 'string' ? msg.message : JSON.stringify(msg.message);
return JSON.stringify(msg).slice(0, 200);
}
function closeSessionDetail() {
document.getElementById('session-detail').style.display = 'none';
}
function deleteSession(path) {
const name = path.split('/').pop();
if (!confirm(`Session "${name}" wirklich loeschen?`)) return;
send({ action: 'delete_session', sessionPath: path });
}
// ── Brain Viewer ────────────────────────────────────────
function loadBrain() {
document.getElementById('brain-list').innerHTML = '<div style="color:#8888AA;padding:8px;">Lade...</div>';
document.getElementById('brain-empty').style.display = 'none';
send({ action: 'list_brain' });
}
function renderBrainList(data) {
const container = document.getElementById('brain-list');
const emptyEl = document.getElementById('brain-empty');
if (data.error) {
container.innerHTML = `<div style="color:#FF6B6B;padding:8px;">Fehler: ${escapeHtml(data.error)}</div>`;
emptyEl.style.display = 'none';
return;
}
if (!data.files || data.files.length === 0) {
container.innerHTML = '';
emptyEl.style.display = 'block';
return;
}
emptyEl.style.display = 'none';
const TYPE_COLORS = { user: '#0096FF', feedback: '#FFD60A', project: '#34C759', reference: '#FF9500' };
let html = '';
for (const f of data.files) {
if (f.name === '.gitkeep') continue;
const color = TYPE_COLORS[f.memType] || '#8888AA';
const date = f.modified ? new Date(f.modified * 1000).toLocaleString('de-DE') : '?';
html += `<div style="padding:6px 8px;border-bottom:1px solid #0D0D1A;cursor:pointer;display:flex;align-items:center;gap:8px;" onmouseover="this.style.background='#1E1E2E'" onmouseout="this.style.background=''" onclick="viewBrainFile('${escapeHtml(f.name)}')">`
+ `<span style="background:${color};width:8px;height:8px;border-radius:50%;flex-shrink:0;" title="${escapeHtml(f.memType || 'unbekannt')}"></span>`
+ `<div style="flex:1;min-width:0;">`
+ `<div style="color:#E0E0F0;font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(f.name)}</div>`
+ (f.description ? `<div style="color:#8888AA;font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(f.description)}</div>` : '')
+ `</div>`
+ `<div style="color:#555570;font-size:10px;white-space:nowrap;">${escapeHtml(f.size)}</div>`
+ '</div>';
}
container.innerHTML = html || '<div style="color:#555570;padding:8px;text-align:center;">Nur .gitkeep gefunden</div>';
}
function viewBrainFile(name) {
const panel = document.getElementById('brain-content');
const title = document.getElementById('brain-content-title');
const text = document.getElementById('brain-content-text');
panel.style.display = 'block';
title.textContent = name;
text.textContent = 'Lade...';
send({ action: 'read_brain_file', filename: name });
}
function renderBrainContent(data) {
const text = document.getElementById('brain-content-text');
if (data.error) {
text.textContent = `Fehler: ${data.error}`;
text.style.color = '#FF6B6B';
return;
}
text.style.color = '#E0E0F0';
text.textContent = data.content || '(leer)';
}
function closeBrainContent() {
document.getElementById('brain-content').style.display = 'none';
}
connectWS();
</script>
</body>