Frontend refactored to ES6 modules (no bundler): js/main.js — entry point, wires all modules js/auth.js — OIDC login, token management js/ws.js — /ws, /ws/test, /ws/trace connections + HUD handler js/chat.js — messages, send, streaming js/graph.js — Cytoscape visualization + animation js/trace.js — trace panel js/dashboard.js — workspace controls rendering js/awareness.js — state panel, sensors, meters js/tests.js — test status display js/util.js — shared utilities New 2-row layout: Top: test status | connection status Middle: Workspace | Node Details | Graph Bottom: Chat | Awareness | Trace PA routing: routes ALL tool requests to expert (DB, UI, buttons, machines) Dashboard integration test: 15/15 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
89 lines
3.6 KiB
JavaScript
89 lines
3.6 KiB
JavaScript
/** Dashboard: workspace controls rendering (buttons, tables, labels, displays, machines). */
|
|
|
|
import { esc } from './util.js';
|
|
import { addTrace } from './trace.js';
|
|
import { setDashboard } from './chat.js';
|
|
|
|
let _ws = null;
|
|
|
|
export function setWs(ws) { _ws = ws; }
|
|
|
|
export function dockControls(controls) {
|
|
setDashboard(controls); // S3*: remember what's rendered
|
|
const body = document.getElementById('workspace-body');
|
|
if (!body) return;
|
|
body.innerHTML = '';
|
|
const container = document.createElement('div');
|
|
container.className = 'controls-container';
|
|
|
|
for (const ctrl of controls) {
|
|
if (ctrl.type === 'button') {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'control-btn';
|
|
btn.textContent = ctrl.label;
|
|
btn.onclick = () => {
|
|
if (_ws && _ws.readyState === 1) {
|
|
_ws.send(JSON.stringify({ type: 'action', action: ctrl.action, data: ctrl.payload || ctrl.data || {} }));
|
|
addTrace('runtime', 'action', ctrl.action);
|
|
}
|
|
};
|
|
container.appendChild(btn);
|
|
} else if (ctrl.type === 'table') {
|
|
const table = document.createElement('table');
|
|
table.className = 'control-table';
|
|
if (ctrl.columns) {
|
|
const thead = document.createElement('tr');
|
|
for (const col of ctrl.columns) {
|
|
const th = document.createElement('th');
|
|
th.textContent = col;
|
|
thead.appendChild(th);
|
|
}
|
|
table.appendChild(thead);
|
|
}
|
|
for (const row of (ctrl.data || [])) {
|
|
const tr = document.createElement('tr');
|
|
if (Array.isArray(row)) {
|
|
for (const cell of row) {
|
|
const td = document.createElement('td'); td.textContent = cell; tr.appendChild(td);
|
|
}
|
|
} else if (typeof row === 'object') {
|
|
for (const col of (ctrl.columns || Object.keys(row))) {
|
|
const td = document.createElement('td'); td.textContent = row[col] ?? ''; tr.appendChild(td);
|
|
}
|
|
}
|
|
table.appendChild(tr);
|
|
}
|
|
container.appendChild(table);
|
|
} else if (ctrl.type === 'label') {
|
|
const lbl = document.createElement('div');
|
|
lbl.className = 'control-label';
|
|
lbl.innerHTML = '<span class="cl-text">' + esc(ctrl.text || '') + '</span><span class="cl-value">' + esc(String(ctrl.value ?? '')) + '</span>';
|
|
container.appendChild(lbl);
|
|
} else if (ctrl.type === 'display') {
|
|
const disp = document.createElement('div');
|
|
const dt = ctrl.display_type || 'text';
|
|
const style = ctrl.style ? ' display-' + ctrl.style : '';
|
|
disp.className = 'control-display display-' + dt + style;
|
|
if (dt === 'progress') {
|
|
const pct = Math.min(100, Math.max(0, Number(ctrl.value) || 0));
|
|
disp.innerHTML = '<span class="cd-label">' + esc(ctrl.label) + '</span>'
|
|
+ '<div class="cd-bar"><div class="cd-fill" style="width:' + pct + '%"></div></div>'
|
|
+ '<span class="cd-pct">' + pct + '%</span>';
|
|
} else if (dt === 'status') {
|
|
disp.innerHTML = '<span class="cd-icon">' + (ctrl.style === 'success' ? '\u2713' : ctrl.style === 'error' ? '\u2717' : '\u2139') + '</span>'
|
|
+ '<span class="cd-label">' + esc(ctrl.label) + '</span>';
|
|
} else {
|
|
disp.innerHTML = '<span class="cd-label">' + esc(ctrl.label) + '</span>'
|
|
+ (ctrl.value ? '<span class="cd-value">' + esc(String(ctrl.value)) + '</span>' : '');
|
|
}
|
|
container.appendChild(disp);
|
|
}
|
|
}
|
|
body.appendChild(container);
|
|
}
|
|
|
|
export function clearDashboard() {
|
|
const body = document.getElementById('workspace-body');
|
|
if (body) body.innerHTML = '';
|
|
}
|