/* ============================================================
gp-app.jsx — shell: brand, pipeline nav, routing, tweaks
============================================================ */
const { useState: aS, useEffect: aE, useMemo: aM } = React;
const STAGES = [
{ id: "radar", label: "Radar", num: "01", sub: "market scan" },
{ id: "selection", label: "Selection", num: "02", sub: "asset picks" },
{ id: "plan", label: "Plan", num: "03", sub: "sweep audit" },
{ id: "execution", label: "Execution", num: "04", sub: "plan vs. fills" },
];
function Clock() {
const [t, setT] = aS("");
aE(() => {
const tick = () => {
const d = new Date();
const p = (n) => String(n).padStart(2, "0");
setT(`${p(d.getUTCHours())}:${p(d.getUTCMinutes())}:${p(d.getUTCSeconds())} UTC`);
};
tick(); const id = setInterval(tick, 1000); return () => clearInterval(id);
}, []);
return {t} ;
}
function PipelineNav({ stage, setStage, summary }) {
return (
{STAGES.map((s, i) => {
const active = stage === s.id;
return (
setStage(s.id)} style={{
display: "flex", alignItems: "center", gap: 11, padding: "11px 20px",
background: active ? "var(--bg-2)" : "transparent", border: "none", cursor: "pointer",
borderBottom: "2px solid " + (active ? "var(--cyan)" : "transparent"),
transition: "background 120ms",
}}>
{s.num}
{s.id === "plan" && summary.flagged > 0 && (
{summary.flagged}
)}
{s.id === "plan" && summary.flagged === 0 && (
)}
{i < STAGES.length - 1 && (
→
)}
);
})}
);
}
function Header() {
return (
OVERMONT V2 · COMMAND CENTER
GAME PLAN AUDIT · MODULE 08
● PAPER
D5 GATE · v0.4.2
);
}
// ---- Tweak panel (host protocol, like the AXIOM terminal) ----
function TweakPanel({ open, onClose, state, set }) {
if (!open) return null;
const Group = ({ label, children }) => (
{label}
{children}
);
const Seg = ({ value, options }) => (
{options.map((o) => (
set(value, o.v)} style={{
padding: "6px 11px", cursor: "pointer", borderRadius: 2,
background: state[value] === o.v ? "var(--cyan)" : "var(--bg-2)",
color: state[value] === o.v ? "var(--bg-0)" : "var(--fg-2)",
border: "1px solid " + (state[value] === o.v ? "var(--cyan)" : "var(--line)"),
fontFamily: "var(--mono)", fontSize: 10, letterSpacing: "0.06em",
}}>{o.l}
))}
);
return (
TWEAKS
✕
how the VERIFIED / NEEDS-REVIEW verdict reads at the top of each audit.
);
}
// ---- no-data state: artifact present but empty / not today's briefing ----
function NoBriefing() {
return (
NO BRIEFING TO AUDIT
/api/strategy-rationale returned no decisions. Either the D5 gate has
not produced today's plan yet, or the artifact is empty. Seed one with{" "}
python scripts/seed_sample_briefing.py and reload.
);
}
function App() {
const decisions = window.GP.DATA.decisions;
const summary = aM(() => window.GP.verify.summary(decisions), [decisions]);
const [stage, setStage] = aS("plan");
const [jumpId, setJumpId] = aS(null);
const [tweakOpen, setTweakOpen] = aS(false);
const TWEAKS = /*EDITMODE-BEGIN*/{
"flagStyle": "banner",
"accent": "cyan",
"density": "regular",
"scanlines": "on"
}/*EDITMODE-END*/;
const [state, setState] = aS(TWEAKS);
const set = (k, v) => setState((s) => ({ ...s, [k]: v }));
// jump from radar/selection/execution into a specific plan decision
const jumpToDecision = (id) => { setJumpId(id); setStage("plan"); };
aE(() => {
window.parent.postMessage({ type: "__edit_mode_set_keys", edits: state }, "*");
document.body.setAttribute("data-density", state.density);
document.body.setAttribute("data-scanlines", state.scanlines);
document.body.setAttribute("data-accent", state.accent);
}, [state]);
aE(() => {
const handler = (e) => {
if (e.data?.type === "__activate_edit_mode") setTweakOpen(true);
if (e.data?.type === "__deactivate_edit_mode") setTweakOpen(false);
};
window.addEventListener("message", handler);
window.parent.postMessage({ type: "__edit_mode_available" }, "*");
return () => window.removeEventListener("message", handler);
}, []);
return (
{decisions.length === 0 ?
: (
<>
{stage === "radar" &&
}
{stage === "selection" &&
}
{stage === "plan" &&
}
{stage === "execution" &&
}
>
)}
setTweakOpen(false)} state={state} set={set} />
);
}
// ---- token entry (§5): React surface for the localStorage Bearer contract ----
// The backend auth contract is unchanged — the password field IS the server's
// `CENTAUR_API_TOKEN`, stored browser-only in localStorage["centaur.token"]. An
// optional base-URL override targets a non-origin server (localStorage
// ["centaur.baseUrl"]); blank = same origin. Submitting re-runs window.GP.load().
function TokenEntry({ status, onRetry }) {
const [base, setBase] = aS(localStorage.getItem("centaur.baseUrl") || "");
const [token, setToken] = aS(localStorage.getItem("centaur.token") || "");
const [err, setErr] = aS(status ? `auth rejected · ${status}` : null);
const connect = () => {
if (!token.trim()) { setErr("a bearer token is required"); return; }
const b = base.trim().replace(/\/$/, "");
if (b) localStorage.setItem("centaur.baseUrl", b);
else localStorage.removeItem("centaur.baseUrl");
localStorage.setItem("centaur.token", token.trim());
setErr(null);
onRetry();
};
const field = {
width: "100%", boxSizing: "border-box", padding: "9px 11px", marginTop: 6,
background: "var(--bg-0)", border: "1px solid var(--line)", borderRadius: 3,
color: "var(--fg)", fontFamily: "var(--mono)", fontSize: 12, letterSpacing: "0.04em",
};
const lbl = { fontFamily: "var(--mono)", fontSize: 9, letterSpacing: "0.16em", textTransform: "uppercase", color: "var(--fg-3)" };
return (
);
}
// ---- boot lifecycle (§4.3): loading → token | error | ready ----
function BootScreen({ children }) {
return ;
}
function Boot() {
const [phase, setPhase] = aS("loading"); // loading | token | error | ready
const [err, setErr] = aS(null);
const run = () => {
setPhase("loading");
window.GP.load()
.then(() => setPhase("ready"))
.catch((e) => {
setErr(e);
setPhase(e && (e.status === 401 || e.status === 403) ? "token" : "error");
});
};
aE(() => { run(); }, []);
if (phase === "ready") return ;
if (phase === "token") return ;
if (phase === "error") {
return (
failed to load briefing
{err && err.message ? err.message : "unknown error"}
);
}
return resolving audit… ;
}
ReactDOM.createRoot(document.getElementById("root")).render( );