Spaces:
Running on Zero
Running on Zero
| import React, { useState } from "react"; | |
| import { ShieldAlert } from "lucide-react"; | |
| import { C, FD, FB, FM, fmt } from "../theme.js"; | |
| // Small presentational atoms lifted verbatim-in-spirit from the validated mock. | |
| // Do NOT restyle these; they carry the Tactical Grey look. | |
| export function Chip({ dot, text }) { | |
| return ( | |
| <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 10.5, color: C.text2, fontFamily: FM, border: `1px solid ${C.borderSoft}`, borderRadius: 999, padding: "4px 10px" }}> | |
| <span style={{ width: 7, height: 7, borderRadius: 999, background: dot, boxShadow: `0 0 6px ${dot}` }} /> | |
| {text} | |
| </div> | |
| ); | |
| } | |
| export function Stat({ label, v, grad }) { | |
| return ( | |
| <div style={{ textAlign: "right" }}> | |
| <div className={grad ? "hb" : ""} style={{ fontFamily: FM, fontSize: 14, fontWeight: 700, color: grad ? undefined : C.text }}>{v}</div> | |
| <div style={{ fontSize: 9, color: C.muted, letterSpacing: 0.5, textTransform: "uppercase" }}>{label}</div> | |
| </div> | |
| ); | |
| } | |
| export function Hero({ v, label, grad }) { | |
| return ( | |
| <div style={{ flex: 1, background: `linear-gradient(135deg,${C.card},${C.panel})`, border: `1px solid ${C.borderSoft}`, borderRadius: 10, padding: "13px 15px" }}> | |
| <div className={grad ? "hb" : ""} style={{ fontFamily: FD, fontWeight: 700, fontSize: 24, color: grad ? undefined : C.text }}>{v}</div> | |
| <div style={{ fontSize: 10, color: C.muted, letterSpacing: 0.6, textTransform: "uppercase", marginTop: 2 }}>{label}</div> | |
| </div> | |
| ); | |
| } | |
| export function PanelTitle({ icon: Icon, text, mt }) { | |
| return ( | |
| <div style={{ display: "flex", alignItems: "center", gap: 7, marginTop: mt ? 20 : 0, color: C.muted }}> | |
| <Icon size={13} /> | |
| <span style={{ fontFamily: FM, fontSize: 10.5, letterSpacing: 0.6 }}>{text}</span> | |
| </div> | |
| ); | |
| } | |
| export function SecHead({ icon: Icon, c, text }) { | |
| return ( | |
| <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 26 }}> | |
| <Icon size={16} color={c} /> | |
| <span style={{ fontFamily: FM, fontSize: 11, letterSpacing: 0.9, color: c, fontWeight: 600 }}>{text}</span> | |
| <div style={{ flex: 1, height: 1, background: `linear-gradient(90deg,${c}44,transparent)` }} /> | |
| </div> | |
| ); | |
| } | |
| export function Bar({ label, v, max, c, note }) { | |
| return ( | |
| <div style={{ marginTop: 9 }}> | |
| <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11.5 }}> | |
| <span style={{ color: C.text2 }}>{label}</span> | |
| <span style={{ fontFamily: FM, color: c, fontWeight: 600 }}>{fmt(v)}</span> | |
| </div> | |
| <div style={{ height: 6, background: C.black, borderRadius: 3, marginTop: 4, overflow: "hidden" }}> | |
| <div style={{ width: `${Math.max(2, (100 * v) / Math.max(1, max))}%`, height: "100%", background: `linear-gradient(90deg,${c},${c}aa)` }} /> | |
| </div> | |
| {note && <div style={{ fontSize: 9.5, color: C.muted, marginTop: 3, fontStyle: "italic" }}>{note}</div>} | |
| </div> | |
| ); | |
| } | |
| // A line-style legend row used inside the turn graph (proven vs hypothesis). | |
| export function EdgeLegend({ c, text, dashed }) { | |
| return ( | |
| <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: C.text2 }}> | |
| <span style={{ width: 16, height: 0, borderTop: `2px ${dashed ? "dotted" : "solid"} ${c}`, display: "inline-block" }} /> | |
| {text} | |
| </div> | |
| ); | |
| } | |
| export function ChainNode({ icon: Icon, c, title, sub }) { | |
| return ( | |
| <div style={{ display: "flex", gap: 10, alignItems: "center", padding: "9px 11px", background: `linear-gradient(135deg,${C.card},${C.panel})`, border: `1px solid ${c}`, borderRadius: 8, marginBottom: 4, boxShadow: "0 1px 3px rgba(0,0,0,.35)" }}> | |
| <div style={{ width: 28, height: 28, borderRadius: 7, background: C.elevated, display: "flex", alignItems: "center", justifyContent: "center", border: `1px solid ${c}` }}> | |
| <Icon size={15} color={c} /> | |
| </div> | |
| <div style={{ minWidth: 0 }}> | |
| <div style={{ fontSize: 13, fontWeight: 600, color: c }}>{title}</div> | |
| <div style={{ fontFamily: FM, fontSize: 10.5, color: C.muted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", maxWidth: 520 }}>{sub}</div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // --------------------------------------------------------------------------- | |
| // BINARIES — the real tools run via Bash (npx remotion -> remotion, railway, …), | |
| // a SEPARATE entity dimension from tool calls. Logo served LOCALLY from | |
| // /binary-logos/<name>.svg (pulled by the background enricher); colored-monogram | |
| // fallback when the file isn't there yet. No network on render (NN #2). | |
| // --------------------------------------------------------------------------- | |
| const _MONO = [C.orange, C.cyan, C.amber, C.blue, "#a78bfa", "#f472b6", "#34d399", "#fb923c", "#60a5fa"]; | |
| function monoColor(name) { | |
| let h = 0; | |
| for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0; | |
| return _MONO[h % _MONO.length]; | |
| } | |
| export function BinaryLogo({ b, size = 18 }) { | |
| const [err, setErr] = useState(false); | |
| const name = (b.binary || b.name || "?"); | |
| const src = b.logo || `/binary-logos/${encodeURIComponent(name)}.svg`; | |
| const col = monoColor(name); | |
| if (err) { | |
| return ( | |
| <span style={{ width: size, height: size, borderRadius: 5, background: col + "22", border: `1px solid ${col}66`, color: col, display: "inline-flex", alignItems: "center", justifyContent: "center", fontFamily: FM, fontSize: Math.round(size * 0.5), fontWeight: 700, flexShrink: 0, lineHeight: 1 }}> | |
| {name[0] ? name[0].toUpperCase() : "?"} | |
| </span> | |
| ); | |
| } | |
| return ( | |
| <img src={src} alt="" width={size} height={size} onError={() => setErr(true)} | |
| style={{ borderRadius: 4, objectFit: "contain", flexShrink: 0, background: "transparent" }} /> | |
| ); | |
| } | |
| // Compact chip (logo + product) for session cards / inline lists. | |
| export function BinaryBadge({ b, onClick }) { | |
| const name = b.binary || b.name; | |
| const label = b.product || name; | |
| return ( | |
| <span onClick={onClick} className={onClick ? "lift" : ""} | |
| title={`${label}${b.blurb ? " — " + b.blurb : ""}${b.via && b.via !== "direct" ? " · via " + b.via : ""}${b.security ? " ⚠ " + b.security : ""}`} | |
| style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: FM, fontSize: 9, color: C.text2, border: `1px solid ${C.borderSoft}`, borderRadius: 5, padding: "2px 6px 2px 4px", cursor: onClick ? "pointer" : "default" }}> | |
| <BinaryLogo b={b} size={12} />{label} | |
| {b.security && <ShieldAlert size={9} color={C.amber} />} | |
| </span> | |
| ); | |
| } | |
| // A traceable row for the left rails: logo + product, blurb, count, security note, | |
| // and the turns it fired in. `onOpen(turnIndex)` jumps to the first turn. | |
| export function BinaryRow({ b, onOpen, color = C.orange }) { | |
| const name = b.binary || b.name; | |
| const label = b.product || name; | |
| const turns = b.turns || []; | |
| return ( | |
| <div className="row lift" | |
| onClick={() => turns.length && onOpen && onOpen(turns[0])} | |
| title={`${b.identified ? label : name + " (not yet identified)"}${b.blurb ? " — " + b.blurb : ""}` + | |
| `${b.via && b.via !== "direct" ? " · via " + b.via : ""}` + | |
| `${turns.length ? " · turn(s) " + turns.join(", ") : ""}` + | |
| `${b.security ? " ⚠ " + b.security : ""}`} | |
| style={{ display: "flex", alignItems: "center", gap: 7, padding: "5px 8px", borderRadius: 6, cursor: turns.length ? "pointer" : "default", borderLeft: `2px solid ${color}` }}> | |
| <BinaryLogo b={b} size={16} /> | |
| <span style={{ minWidth: 0, flex: 1 }}> | |
| <span style={{ fontFamily: FM, fontSize: 11.5, color: C.text, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "block" }}> | |
| {label}{!b.identified && <span style={{ color: C.muted }}> · {name}</span>} | |
| </span> | |
| {b.blurb && ( | |
| <span style={{ fontFamily: FB, fontSize: 9.5, color: C.muted, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "block" }}>{b.blurb}</span> | |
| )} | |
| </span> | |
| {b.security && <ShieldAlert size={11} color={C.amber} title={b.security} style={{ flexShrink: 0 }} />} | |
| <span style={{ fontFamily: FM, fontSize: 9, color: C.muted, flexShrink: 0 }}>×{b.count ?? b.total}</span> | |
| </div> | |
| ); | |
| } | |
| // "Generated" provenance badge — every piece of narrator prose must be labelled | |
| // generated (NON-NEGOTIABLE #7). Deterministic numbers are NOT badged. | |
| export function GeneratedTag({ cites }) { | |
| return ( | |
| <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontFamily: FM, fontSize: 9, letterSpacing: 0.5, color: C.muted, border: `1px solid ${C.borderSoft}`, borderRadius: 5, padding: "1px 7px" }}> | |
| GENERATED{cites ? ` · reads ${cites}` : ""} | |
| </span> | |
| ); | |
| } | |