feat(ui): hybrid sidebar (sectioned + active pill + agent dots) + agent profile viewer in Settings
Sidebar: Spaces / Agents / Navigate sections, accent pill on active item, status dots on agents. Settings Agents rows expand to show the agent's persona (soul) + capabilities/scopes via GET /api/agents/:id/profile. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,11 @@ async function renderTokens(c) {
|
||||
paint();
|
||||
}
|
||||
|
||||
function fileBlock(parent, label, content) {
|
||||
parent.appendChild(el('div', { class: 'agent-file-label' }, label));
|
||||
parent.appendChild(el('div', { class: 'agent-file-content' }, content));
|
||||
}
|
||||
|
||||
async function renderAgents(c) {
|
||||
c.replaceChildren(el('div', { class: 'muted' }, 'Loading…'));
|
||||
let agents = [];
|
||||
@@ -59,10 +64,35 @@ async function renderAgents(c) {
|
||||
if (!agents.length) { c.appendChild(el('div', { class: 'muted' }, 'No agents.')); return; }
|
||||
for (const a of agents) {
|
||||
const caps = Object.entries(a.capabilities || {}).filter(([, v]) => v).map(([k]) => k).join(', ') || '—';
|
||||
const scope = a.scopes?.space_id ? 'space-scoped' : '';
|
||||
c.appendChild(el('div', { class: 'settings-row' },
|
||||
el('span', { class: 'settings-label' }, a.name),
|
||||
el('span', { class: 'settings-value muted' }, `${a.slug} · ${a.kind} · caps: ${caps}${scope ? ' · ' + scope : ''}`)));
|
||||
const row = el('div', { class: 'agent-row' });
|
||||
const body = el('div', { class: 'agent-body hidden' });
|
||||
let loaded = false;
|
||||
const hd = el('div', {
|
||||
class: 'agent-row-hd',
|
||||
onclick: async () => {
|
||||
const open = row.classList.toggle('open');
|
||||
body.classList.toggle('hidden', !open);
|
||||
if (open && !loaded) {
|
||||
loaded = true;
|
||||
body.replaceChildren(el('div', { class: 'muted' }, 'Loading…'));
|
||||
try {
|
||||
const p = await api.get('/api/agents/' + a.id + '/profile');
|
||||
body.replaceChildren();
|
||||
if (p.persona) fileBlock(body, 'Soul · persona', p.persona);
|
||||
else body.appendChild(el('div', { class: 'muted' }, 'No persona defined (config-only agent).'));
|
||||
fileBlock(body, 'Capabilities', JSON.stringify(p.capabilities || {}, null, 2));
|
||||
if (p.scopes && Object.keys(p.scopes).length) fileBlock(body, 'Scopes', JSON.stringify(p.scopes, null, 2));
|
||||
body.appendChild(el('div', { class: 'muted', style: { fontSize: '11px', marginTop: '8px' } },
|
||||
'Void 2 agents are persona-in-code + DB config — no separate memory files (unlike Void 1).'));
|
||||
} catch (e) { body.replaceChildren(el('div', { class: 'err' }, 'Error: ' + e.message)); }
|
||||
}
|
||||
}
|
||||
},
|
||||
el('span', { class: 'agent-nm' }, a.name),
|
||||
el('span', { class: 'settings-value muted' }, `${a.slug} · ${a.kind} · ${caps}`),
|
||||
el('span', { class: 'agent-row-chev' }, '›'));
|
||||
row.append(hd, body);
|
||||
c.appendChild(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user