// overlays.jsx — Correx overlays: command palette, approval gate, and inspectors.
const { useState, useEffect, useRef, useMemo } = React;
/* ── generic overlay shell ── */
function Overlay({ tag, title, esc = "esc", center, width, onClose, children }) {
return (
{ if (e.target === e.currentTarget) onClose(); }}>
e.stopPropagation()}>
{tag}
{title}
{esc} to close
{children}
);
}
/* ── command palette ── */
function CommandPalette({ commands, onRun, onClose }) {
const [q, setQ] = useState("");
const [i, setI] = useState(0);
const inp = useRef(null);
useEffect(() => { inp.current && inp.current.focus(); }, []);
const score = (c) => {
const s = (c.label + " " + c.hint).toLowerCase(), t = q.toLowerCase().trim();
if (!t) return 1;
if (s.includes(t)) return 2;
let qi = 0; for (const ch of s) if (ch === t[qi]) qi++;
return qi === t.length ? 1 : 0;
};
const list = useMemo(() => commands.map((c) => [c, score(c)]).filter(([, s]) => s > 0)
.sort((a, b) => b[1] - a[1]).map(([c]) => c), [q]);
useEffect(() => { setI(0); }, [q]);
const key = (e) => {
if (e.key === "ArrowDown") { setI((x) => Math.min(x + 1, list.length - 1)); e.preventDefault(); }
else if (e.key === "ArrowUp") { setI((x) => Math.max(x - 1, 0)); e.preventDefault(); }
else if (e.key === "Enter") { list[i] && onRun(list[i].id); e.preventDefault(); }
else if (e.key === "Escape") { onClose(); }
e.stopPropagation();
};
return (
{ if (e.target === e.currentTarget) onClose(); }}>
e.stopPropagation()}>
⌘
setQ(e.target.value)} onKeyDown={key} />
{list.length === 0 &&
no matches
}
{list.map((c, n) => (
setI(n)} onClick={() => onRun(c.id)}>
{c.key}
{c.label}
{c.hint}
))}
);
}
/* ── approval gate (modal interrupt) ── */
function ApprovalModal({ data, onApprove, onReject, onSteer, onClose }) {
const [steer, setSteer] = useState("");
const sref = useRef(null);
return (
e.stopPropagation()}>
e.stopPropagation()}>
{data.tier}
Approval required · {data.tierLabel}
{data.intent}
execution paused
$ {data.command}
{data.detail}
- tool
- {data.tool} {data.tier}
- reversible
- {data.reversible ? "yes" : "no"}
- policy
- {data.policy}
semantic risk scan
{data.risks.map((r, n) =>
{r}
)}
↳
setSteer(e.target.value)}
onKeyDown={(e) => { e.stopPropagation(); if (e.key === "Enter" && steer.trim()) onSteer(steer.trim()); if (e.key === "Escape") sref.current.blur(); }} />
);
}
/* ── event stream inspector ── */
function EventInspector({ events, onClose }) {
const cats = ["All", "Lifecycle", "Context", "Inference", "Tool", "Domain", "Approval"];
const [cat, setCat] = useState("All");
const [sel, setSel] = useState(events[events.length - 1]);
const list = events.filter((e) => cat === "All" || e.cat === cat);
const payload = {
id: sel.id, type: sel.type, category: sel.cat, ts: "2026-05-29T" + sel.t,
session_id: window.CORREX.SESSION.id, correlation_id: "cor-7f2a", causation_id: sel.id === "e49" ? "e48" : null,
payload: { detail: sel.text },
};
return (
{cats.map((c) => setCat(c)}>{c})}
{list.map((e) => (
setSel(e)}>
{e.t}
{e.cat}
{e.type} {e.text}
))}
{JSON.stringify(payload, null, 2)}
);
}
/* ── context pack inspector ── */
function ContextPackInspector({ pack, onClose }) {
const pct = Math.round((pack.used / pack.budget) * 100);
return (
{pack.used.toLocaleString()} / {pack.budget.toLocaleString()} tok
{pct}%
{pack.layers.map((l) => (
{l.id}
{l.name}
{l.mode}
{l.items.join(" · ")}
{l.tok}t
))}
excluded by synthesis
{pack.dropped.map((d, n) => − {d}
)}
);
}
/* ── stage / transition graph ── */
function StageGraph({ stages, transitions, onClose }) {
const glyph = { done: "✔", active: "▸", pending: "·" };
return (
{stages.map((s, n) => (
{glyph[s.status]}
{s.id}
role:{s.role}
{s.status === "active" && running · {s.enter}}
{s.note}
{n < stages.length - 1 &&
│
}
))}
transition rules
no hardcoded graph — rules eval per artifact
{transitions.map((t, n) => (
when {t.when}
→ goto {t.goto}
))}
);
}
/* ── model registry / provider switch ── */
function ModelRegistry({ models, active, onSwitch, onClose }) {
return (
models are stateless engines — switching re-synthesizes the Context Pack for the new window.
{models.map((m) => (
m.id !== active && onSwitch(m.id)}>
{m.id}
{m.provider}
{m.id === active && active}
{m.status}
{m.caps.join(" · ")} — ctx {m.ctx.toLocaleString()} · t={m.temp} · {m.gpu}
sessions {m.parallel}{m.id !== active &&
switch →
}
))}
);
}
/* ── tool palette ── */
function ToolPalette({ tools, onToggle, onClose }) {
return (
config-driven · capability-scoped. tier sets the approval gate; disabled tools are invisible to the agent.
{tools.map((t) => (
onToggle(t.id)}>
{t.tier}
{t.id} — {t.desc}
{t.mode}
{t.enabled ? "enabled" : "disabled"}
))}
);
}
Object.assign(window, { Overlay, CommandPalette, ApprovalModal, EventInspector, ContextPackInspector, StageGraph, ModelRegistry, ToolPalette });