refactor(#1005): rename capabilities → tool-groups throughout UI

Section heading, element id, CSS classes, and JS functions all renamed
from 'capabilities'/'cap-*' to 'tool-groups'/'tg-*' to accurately
describe what the UI manages (tool-group permissions, not a capabilities
system).
This commit is contained in:
iris 2026-06-01 20:27:28 +02:00
commit a7d31046a6
3 changed files with 36 additions and 36 deletions

View file

@ -2045,55 +2045,55 @@ body.logs-shell {
font-size: 0.85em; font-size: 0.85em;
} }
/* capabilities (tool-group) table /* tool-groups (permissions) table
Agents × tool-groups matrix in the SYST3M tab. Horizontally Agents × tool-groups matrix in the SYST3M tab. Horizontally
scrollable on narrow viewports. */ scrollable on narrow viewports. */
.cap-table-wrap { .tg-table-wrap {
overflow-x: auto; overflow-x: auto;
margin-top: 0.5em; margin-top: 0.5em;
} }
.cap-table { .tg-table {
border-collapse: collapse; border-collapse: collapse;
font-size: 0.82em; font-size: 0.82em;
min-width: 100%; min-width: 100%;
} }
.cap-table th, .tg-table th,
.cap-table td { .tg-table td {
padding: 0.35em 0.6em; padding: 0.35em 0.6em;
border: 1px solid var(--border); border: 1px solid var(--border);
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
} }
.cap-table thead th { .tg-table thead th {
background: var(--bg-elev); background: var(--bg-elev);
color: var(--muted); color: var(--muted);
letter-spacing: 0.05em; letter-spacing: 0.05em;
white-space: nowrap; white-space: nowrap;
} }
.cap-agent-col { .tg-agent-col {
text-align: left !important; text-align: left !important;
min-width: 8em; min-width: 8em;
} }
.cap-agent-name { .tg-agent-name {
color: var(--fg); color: var(--fg);
font-weight: 600; font-weight: 600;
} }
.cap-default-label { .tg-default-label {
margin-left: 0.4em; margin-left: 0.4em;
font-size: 0.85em; font-size: 0.85em;
} }
.cap-cb { .tg-cb {
cursor: pointer; cursor: pointer;
width: 1em; width: 1em;
height: 1em; height: 1em;
accent-color: var(--purple); accent-color: var(--purple);
} }
.cap-save-btn { .tg-save-btn {
font-size: 0.78em; font-size: 0.78em;
padding: 0.2em 0.6em; padding: 0.2em 0.6em;
white-space: nowrap; white-space: nowrap;
} }
.cap-row:hover td { .tg-row:hover td {
background: var(--bg-elev); background: var(--bg-elev);
} }

View file

@ -172,15 +172,15 @@
<p class="meta">loading…</p> <p class="meta">loading…</p>
</div> </div>
<!-- C4P4B1L1T13S: per-agent tool-group matrix. Rows = agents, <!-- T00L GR0UPS: per-agent tool-group permission matrix. Rows = agents,
cols = tool groups fetched from GET /api/tool-groups. cols = tool groups fetched from GET /api/tool-groups.
Checking / unchecking and saving POSTs to Checking / unchecking and saving POSTs to
/api/tool-groups/{agent}; a rebuild is queued automatically. /api/tool-groups/{agent}; a rebuild is queued automatically.
Absent agents default to the role default (shown in parens). --> Absent agents default to the role default (shown in parens). -->
<h2>C4P4B1L1T13S ◆</h2> <h2>T00L GR0UPS ◆</h2>
<div class="divider">══════════════════════════════════════════════════════════════</div> <div class="divider">══════════════════════════════════════════════════════════════</div>
<p class="meta">per-agent tool-group assignments. columns are filled from the backend — adding a new group requires no UI change. agents without an explicit entry use the role default (agents: messaging, meta, inbox, execution; manager: all). saving queues a rebuild.</p> <p class="meta">per-agent tool-group permissions. columns are filled from the backend — adding a new group requires no UI change. agents without an explicit entry use the role default (agents: messaging, meta, inbox, execution; manager: all). saving queues a rebuild.</p>
<div id="capabilities-section"> <div id="tool-groups-section">
<p class="meta">loading…</p> <p class="meta">loading…</p>
</div> </div>
</section> </section>

View file

@ -1204,13 +1204,13 @@ window.marked = marked;
root.append(ul); root.append(ul);
} }
// ── capabilities (tool-group) table ───────────────────────────────────── // ── tool-groups (permissions) table ─────────────────────────────────────
// Fetched from GET /api/tool-groups on system tab activation and after // Fetched from GET /api/tool-groups on system tab activation and after
// each save. Groups (columns) come from the backend so the UI doesn't // each save. Groups (columns) come from the backend so the UI doesn't
// need updating when a new group is added. // need updating when a new group is added.
async function fetchAndRenderCapabilities() { async function fetchAndRenderToolGroups() {
const root = $('capabilities-section'); const root = $('tool-groups-section');
if (!root) return; if (!root) return;
root.innerHTML = ''; root.innerHTML = '';
root.append(el('p', { class: 'meta' }, 'loading…')); root.append(el('p', { class: 'meta' }, 'loading…'));
@ -1218,14 +1218,14 @@ window.marked = marked;
const resp = await fetch('/api/tool-groups'); const resp = await fetch('/api/tool-groups');
if (!resp.ok) throw new Error('HTTP ' + resp.status); if (!resp.ok) throw new Error('HTTP ' + resp.status);
const data = await resp.json(); const data = await resp.json();
renderCapabilities(root, data); renderToolGroups(root, data);
} catch (err) { } catch (err) {
root.innerHTML = ''; root.innerHTML = '';
root.append(el('p', { class: 'meta' }, 'fetch failed: ' + err)); root.append(el('p', { class: 'meta' }, 'fetch failed: ' + err));
} }
} }
function renderCapabilities(root, data) { function renderToolGroups(root, data) {
root.innerHTML = ''; root.innerHTML = '';
const { groups, assignments } = data; const { groups, assignments } = data;
if (!groups || !groups.length) { if (!groups || !groups.length) {
@ -1245,17 +1245,17 @@ window.marked = marked;
return; return;
} }
const wrap = el('div', { class: 'cap-table-wrap' }); const wrap = el('div', { class: 'tg-table-wrap' });
const table = el('table', { class: 'cap-table' }); const table = el('table', { class: 'tg-table' });
// Header row. // Header row.
const thead = el('thead'); const thead = el('thead');
const hrow = el('tr'); const hrow = el('tr');
hrow.append(el('th', { class: 'cap-agent-col' }, 'agent')); hrow.append(el('th', { class: 'tg-agent-col' }, 'agent'));
for (const g of groups) { for (const g of groups) {
hrow.append(el('th', { class: 'cap-group-col', title: g }, g)); hrow.append(el('th', { class: 'tg-group-col', title: g }, g));
} }
hrow.append(el('th', { class: 'cap-save-col' }, '')); hrow.append(el('th', { class: 'tg-save-col' }, ''));
thead.append(hrow); thead.append(hrow);
table.append(thead); table.append(thead);
@ -1264,13 +1264,13 @@ window.marked = marked;
// Explicit assignment or empty = using role default. // Explicit assignment or empty = using role default.
const assigned = assignments[name] || []; const assigned = assignments[name] || [];
const hasExplicit = Object.prototype.hasOwnProperty.call(assignments, name); const hasExplicit = Object.prototype.hasOwnProperty.call(assignments, name);
const tr = el('tr', { class: 'cap-row' }); const tr = el('tr', { class: 'tg-row' });
// Agent name cell. // Agent name cell.
const nameTd = el('td', { class: 'cap-agent-col' }); const nameTd = el('td', { class: 'tg-agent-col' });
nameTd.append(el('span', { class: 'cap-agent-name' }, name)); nameTd.append(el('span', { class: 'tg-agent-name' }, name));
if (!hasExplicit) { if (!hasExplicit) {
nameTd.append(el('span', { class: 'meta cap-default-label' }, '(default)')); nameTd.append(el('span', { class: 'meta tg-default-label' }, '(default)'));
} }
tr.append(nameTd); tr.append(nameTd);
@ -1278,10 +1278,10 @@ window.marked = marked;
const checkboxes = []; const checkboxes = [];
for (const g of groups) { for (const g of groups) {
const checked = assigned.includes(g); const checked = assigned.includes(g);
const td = el('td', { class: 'cap-group-col' }); const td = el('td', { class: 'tg-group-col' });
const cb = el('input', { const cb = el('input', {
type: 'checkbox', type: 'checkbox',
class: 'cap-cb', class: 'tg-cb',
'data-group': g, 'data-group': g,
'aria-label': g, 'aria-label': g,
}); });
@ -1292,8 +1292,8 @@ window.marked = marked;
} }
// Save button cell. // Save button cell.
const saveTd = el('td', { class: 'cap-save-col' }); const saveTd = el('td', { class: 'tg-save-col' });
const saveBtn = el('button', { type: 'button', class: 'btn cap-save-btn' }, 'save'); const saveBtn = el('button', { type: 'button', class: 'btn tg-save-btn' }, 'save');
saveBtn.addEventListener('click', async () => { saveBtn.addEventListener('click', async () => {
const selectedGroups = checkboxes const selectedGroups = checkboxes
.filter((cb) => cb.checked) .filter((cb) => cb.checked)
@ -1312,7 +1312,7 @@ window.marked = marked;
saveBtn.title = txt; saveBtn.title = txt;
} else { } else {
saveBtn.textContent = '✓'; saveBtn.textContent = '✓';
setTimeout(() => fetchAndRenderCapabilities(), 800); setTimeout(() => fetchAndRenderToolGroups(), 800);
} }
} catch (err) { } catch (err) {
saveBtn.textContent = 'err'; saveBtn.textContent = 'err';
@ -3516,7 +3516,7 @@ window.marked = marked;
if (target === 'schedules') refreshSchedules(); if (target === 'schedules') refreshSchedules();
// Capabilities table is on the system pane; fetch on each activation // Capabilities table is on the system pane; fetch on each activation
// so it stays fresh without an SSE channel. // so it stays fresh without an SSE channel.
if (target === 'system') fetchAndRenderCapabilities(); if (target === 'system') fetchAndRenderToolGroups();
} }
function syncTabFromHash() { function syncTabFromHash() {
const h = (window.location.hash || '#swarm').replace(/^#/, ''); const h = (window.location.hash || '#swarm').replace(/^#/, '');