/* global React */ /* ANIMA KERNEL — primitive component set */ const { useState, useMemo, useEffect, useRef } = React; /* ----------------------------------------------------------- HashBadge — hash-as-identity Props: value, kind (MERKLE|FEATURE|SIG|KEY|MODEL|RULESET|TX), ver ("verified"|"pending"|"failed"|"unsigned"), short (4), onClick ----------------------------------------------------------- */ function HashBadge({ value = "", kind, ver = "verified", short = 4, onClick }) { const display = useMemo(() => { if (!value) return ""; const v = value.replace(/^0x/, ""); if (v.length <= short * 2 + 1) return "0x" + v; return `0x${v.slice(0, short)}…${v.slice(-short)}`; }, [value, short]); return ( ); } /* ----------------------------------------------------------- AttestationStatePill — crypto state machine ----------------------------------------------------------- */ const STATE_META = { unsigned: { label: "UNSIGNED", shape: "square-hollow" }, signed: { label: "SIGNED", shape: "diamond" }, witnessed: { label: "WITNESSED", shape: "circle-ring" }, threshold: { label: "THRESHOLD", shape: "hex" }, finalized: { label: "FINALIZED", shape: "check-square" }, failed: { label: "FAILED", shape: "cross" }, expired: { label: "EXPIRED", shape: "clock" }, }; function StateShape({ kind }) { const c = "currentColor"; switch (kind) { case "square-hollow": return ; case "diamond": return ; case "circle-ring": return ; case "hex": return ; case "check-square": return ; case "cross": return ; case "clock": return ; default: return null; } } function AttestationStatePill({ state = "signed", label }) { const meta = STATE_META[state] || STATE_META.signed; return ( {label || meta.label} ); } /* ----------------------------------------------------------- ReasonCodeChip ----------------------------------------------------------- */ function ReasonCodeChip({ code, label, weight = "low" }) { return ( {code} {label} ); } /* ----------------------------------------------------------- LatencyBudgetMeter — against 200ms card-auth window ----------------------------------------------------------- */ function LatencyBudgetMeter({ value, budget = 200, ticks = [50, 100, 150], height, showLabel = true }) { const pct = Math.min(100, (value / budget) * 100); let color = "var(--ak-latency-ok)"; if (pct > 90) color = "var(--ak-latency-over)"; else if (pct > 60) color = "var(--ak-latency-hot)"; else if (pct > 25) color = "var(--ak-latency-warn)"; return (
{ticks.map(t => (
))}
{showLabel && ( {value.toFixed(2)}/{budget}ms )}
); } /* ----------------------------------------------------------- MerkleProofTree — readable inclusion proof Shows the siblings along the path from leaf → root. Props: leaf, siblings [{side:'L'|'R', hash}], root ----------------------------------------------------------- */ function MerkleProofTree({ leaf, siblings = [], root, highlighted = true }) { // render as a compact ladder: leaf at bottom, each level folds one sibling in const levels = siblings.length; return (
inclusion proof · depth {levels}
ROOT
{siblings.slice().reverse().map((s, i) => { const level = levels - i; return (
h{level}
{s.side === "L" ? : ↓ path}
{s.side === "R" ? : ↓ path}
); })}
leaf
); } /* ----------------------------------------------------------- TimelineAxis — dual-scale (sub-ms for auth window, day-scale for settlement) Props: mode ("submillisecond" | "day"), startMs, endMs, events [{t, label, state}], height ----------------------------------------------------------- */ function TimelineAxis({ mode = "submillisecond", startMs = 0, endMs = 200, events = [], height = 80, title }) { const ref = useRef(null); const [w, setW] = useState(0); useEffect(() => { if (!ref.current) return; const ro = new ResizeObserver(entries => setW(entries[0].contentRect.width)); ro.observe(ref.current); return () => ro.disconnect(); }, []); const span = endMs - startMs; const tickCount = mode === "submillisecond" ? 10 : 8; const ticks = Array.from({ length: tickCount + 1 }, (_, i) => startMs + (span * i) / tickCount); const fmt = t => { if (mode === "submillisecond") { if (span <= 10) return t.toFixed(2) + "ms"; return t.toFixed(0) + "ms"; } // day scale — ms since epoch const d = new Date(t); if (span > 86400000) return d.toISOString().slice(5, 10); return d.toISOString().slice(11, 16); }; return (
{title && (
{title} · {mode === "submillisecond" ? "sub-ms scale" : "day scale"}
)}
{/* SLA band for submillisecond mode — the 200ms card-auth window */} {mode === "submillisecond" && endMs >= 200 && (
200ms SLA
)} {/* ticks */} {ticks.map((t, i) => (
{fmt(t)}
))} {/* events */} {events.map((e, i) => { const left = ((e.t - startMs) / span) * 100; const color = e.state === "failed" ? "var(--ak-state-failed-fg)" : e.state === "finalized" ? "var(--ak-state-finalized-fg)" : e.state === "witnessed" ? "var(--ak-state-witnessed-fg)" : "var(--ak-state-signed-fg)"; return (
{e.label} {mode === "submillisecond" ? e.t.toFixed(2) + "ms" : ""}
); })}
); } /* ----------------------------------------------------------- ThresholdGateIndicator — M-of-N signers ----------------------------------------------------------- */ function ThresholdGateIndicator({ met = 2, needed = 3, total = 4, label }) { const pct = (met / needed) * 100; const metState = met >= needed ? "finalized" : met > 0 ? "threshold" : "unsigned"; return (
{Array.from({ length: total }).map((_, i) => ( ))}
{met}/{needed} {label || "M-of-N"} = needed ? "THRESHOLD MET" : "PARTIAL"} />
); } /* ----------------------------------------------------------- PartyAttestationRow — one party's attestation lifecycle ----------------------------------------------------------- */ function PartyAttestationRow({ party, role, state, signer, t, sig, isLast }) { return (
{party}
{role}
{signer}
{state !== "unsigned" && (
)}
{t}
); } /* ----------------------------------------------------------- Small utility — Spark sparkline ----------------------------------------------------------- */ function Spark({ values = [], w = 120, h = 26, color = "var(--ak-accent)" }) { if (!values.length) return null; const max = Math.max(...values), min = Math.min(...values); const span = (max - min) || 1; const step = w / (values.length - 1 || 1); const d = values.map((v, i) => `${i ? "L" : "M"}${(i * step).toFixed(1)},${(h - ((v - min) / span) * h).toFixed(1)}`).join(" "); return ( ); } /* ----------------------------------------------------------- Export to window for other Babel files ----------------------------------------------------------- */ Object.assign(window, { HashBadge, AttestationStatePill, ReasonCodeChip, LatencyBudgetMeter, MerkleProofTree, TimelineAxis, ThresholdGateIndicator, PartyAttestationRow, Spark, });