// Primitives — reusable building blocks for the Swivy console const { useState, useEffect, useRef, useCallback, useMemo } = React; // --- Status pill const Status = ({ kind, children }) => { const labels = { active: "Active", pending: "Pending", revoked: "Revoked", verified: "Verified", failed: "Failed", action: "Requires action", neutral: children, }; return ( {children || labels[kind]} ); }; // --- Truncated id / address with copy const MonoId = ({ children, full }) => { const [copied, setCopied] = useState(false); const onCopy = (e) => { e.stopPropagation(); navigator.clipboard?.writeText(full || children); setCopied(true); setTimeout(() => setCopied(false), 1200); }; return ( {children} {copied ? : } ); }; // --- Chain badge const Chain = ({ chain }) => { const c = CHAINS.find(x => x.key === chain) || { key: chain, label: chain, glyph: chain[0] }; return ( {c.glyph} {c.label} ); }; // --- Button const Btn = (props) => { const { variant = "secondary", size, children, icon, iconRight, ...rest } = props; return ( ); }; // --- Card const Card = ({ title, sub, actions, children, bodyClass = "" }) => (
{(title || actions) && (
{title &&
{title}
} {sub &&
{sub}
}
{actions}
)}
{children}
); // --- Field const Field = ({ label, hint, error, required, children }) => (
{label && ( )} {children} {hint && !error &&
{hint}
} {error &&
{error}
}
); // --- Segmented const Segmented = ({ value, onChange, options }) => (
{options.map(o => ( ))}
); // --- Toggle const Toggle = ({ on, onChange }) => (
); }; // --- Very small syntax highlighter (regex-based, enough for snippets) const Highlight = ({ code }) => { // Tokenize line-by-line; avoid regex lookbehind for safety. const lines = code.split("\n"); const KEYS = /\b(const|let|var|import|from|export|default|return|async|await|function|new|if|else|true|false|null|undefined|void|class|this|as|in)\b/g; const FNS = /\b([A-Za-z_$][\w$]*)(?=\()/g; const NUMS = /\b(\d+\.?\d*)\b/g; const STRS = /(["'`])((?:\\.|(?!\1).)*)\1/g; const COMMS = /(\/\/[^\n]*)/g; const renderLine = (line, ln) => { // comments first — return raw tokens to avoid replacing inside strings const parts = []; let i = 0; while (i < line.length) { // string STRS.lastIndex = i; const sm = STRS.exec(line); // comment COMMS.lastIndex = i; const cm = COMMS.exec(line); const nextStr = sm && sm.index >= i ? sm.index : Infinity; const nextCom = cm && cm.index >= i ? cm.index : Infinity; const next = Math.min(nextStr, nextCom); if (next === Infinity) { parts.push({ t: "x", v: line.slice(i) }); break; } if (next > i) parts.push({ t: "x", v: line.slice(i, next) }); if (next === nextCom) { parts.push({ t: "c", v: cm[0] }); i = next + cm[0].length; } else { parts.push({ t: "s", v: sm[0] }); i = next + sm[0].length; } } const tokenize = (text) => { const out = []; let rest = text; let idx = 0; // split by non-identifier chars keeping them const re = /([A-Za-z_$][\w$]*|\d+\.?\d*|[^\w\s]+|\s+)/g; let m; while ((m = re.exec(text))) { const tok = m[0]; if (/^\s+$/.test(tok)) out.push({ t: "x", v: tok }); else if (/^(const|let|var|import|from|export|default|return|async|await|function|new|if|else|true|false|null|undefined|void|class|this|as|in)$/.test(tok)) out.push({ t: "k", v: tok }); else if (/^\d/.test(tok)) out.push({ t: "n", v: tok }); else if (/^[A-Za-z_$][\w$]*$/.test(tok) && text.slice(re.lastIndex, re.lastIndex + 1) === "(") out.push({ t: "f", v: tok }); else if (/^[{}()\[\];,.:=+\-*/<>!?&|]+$/.test(tok)) out.push({ t: "p", v: tok }); else out.push({ t: "x", v: tok }); } return out; }; const nodes = []; parts.forEach((p, i) => { if (p.t === "s") nodes.push({p.v}); else if (p.t === "c") nodes.push({p.v}); else tokenize(p.v).forEach((tk, j) => { const cls = tk.t === "x" ? "" : `tok-${tk.t}`; nodes.push({tk.v}); }); }); return (
{ln + 1} {nodes.length ? nodes :  }
); }; return <>{lines.map((l, i) => renderLine(l, i))}; }; // --- Empty state const Empty = ({ title, desc, actions, illus = "diagram" }) => (
{title}
{desc}
{actions &&
{actions}
}
); const EmptyIllus = ({ kind }) => { if (kind === "glyph") { return (
); } if (kind === "stripes") { return (
); } // diagram (default): small schematic return ( identity wallet chain ); }; // --- KV row const KV = ({ k, children }) => (
{k}
{children}
); // --- Banner const Banner = ({ kind = "info", title, children, action }) => { const colors = { info: { bg: "var(--info-soft)", fg: "var(--info)", border: "color-mix(in oklch, var(--info) 30%, var(--line))" }, warn: { bg: "var(--warn-soft)", fg: "var(--warn)", border: "color-mix(in oklch, var(--warn) 30%, var(--line))" }, danger: { bg: "var(--danger-soft)", fg: "var(--danger)", border: "color-mix(in oklch, var(--danger) 30%, var(--line))" }, ok: { bg: "var(--ok-soft)", fg: "var(--ok)", border: "color-mix(in oklch, var(--ok) 30%, var(--line))" }, }[kind]; const Icon = kind === "danger" || kind === "warn" ? IWarn : kind === "ok" ? ICheck : IInfo; return (
{title &&
{title}
}
{children}
{action}
); }; // --- Progress bar const Progress = ({ value }) => (
); Object.assign(window, { Status, MonoId, Chain, Btn, Card, Field, Segmented, Toggle, CodeBlock, Highlight, Empty, KV, Banner, Progress, });