forge / frontend /tooltip.jsx
juiceb0xc0de's picture
Forge: initial deploy — API + static UI over prebuilt forge.db (seeded at build)
00cabba
Raw
History Blame Contribute Delete
3.81 kB
// Evidence tooltip — appears on hover over a node or edge, anchored to cursor.
window.EvidenceTip = function EvidenceTip({ payload }) {
if (!payload) return null;
const { node, edge, status, reasons, pos } = payload;
const W = 320;
let left = pos.x + 16;
let top = pos.y + 14;
if (typeof window !== "undefined") {
if (left + W > window.innerWidth - 12) left = pos.x - W - 16;
if (top + 220 > window.innerHeight - 12) top = pos.y - 220;
if (top < 12) top = 12;
}
return (
<div className="evidence-tip" style={{ left, top, width: W }}>
{node && (
<>
<div className="tip-head">
<span className="tip-name">{node.name}</span>
<span className={`tip-status ${status}`}>{status}</span>
</div>
<div style={{ fontSize: 11.5, color: "var(--bone-3)", lineHeight: 1.5, marginBottom: 8 }}>
{node.description}
</div>
{reasons && reasons.length > 0 && (
<>
{reasons.slice(0, 2).map((r, i) => {
const ev = (r.evidence && r.evidence[0]) || null;
return (
<div className="tip-reason" key={i}>
<div className="tip-reason-label" style={{
color: r.kind === "blocked" ? "var(--rust)"
: r.kind === "needed-by" ? "var(--amber)"
: "var(--amber)"
}}>
{r.label}
</div>
{r.fix && <div className="tip-reason-fix">{r.fix}</div>}
{ev && ev.quote && <div className="quote">“{ev.quote}”</div>}
{ev && (
<div className="tip-foot">
<a className="src-link" href={ev.url} target="_blank" rel="noopener">{prettyUrl(ev.url)}</a>
<span className={`tier-badge t${ev.tier || r.tier || 1}`}>T{ev.tier || r.tier || 1}</span>
</div>
)}
</div>
);
})}
</>
)}
{(!reasons || reasons.length === 0) && (
<div className="tip-foot">
<span style={{ color: "var(--bone-4)" }}>type · {node.type}</span>
<span className={`tier-badge t${node.tier}`}>T{node.tier}</span>
</div>
)}
</>
)}
{edge && (
<>
<div className="tip-head">
<span className="tip-name" style={{ fontSize: 13 }}>
{displayName(edge.from)} <span style={{ color: "var(--bone-4)", margin: "0 6px" }}></span> {displayName(edge.to)}
</span>
<span className={`tip-status ${edge.relation === "BREAKS" ? "blocked" : "conditional"}`}>{edge.relation}</span>
</div>
{edge.fix && <div className="tip-reason-fix" style={{ marginBottom: 8 }}>{edge.fix}</div>}
{edge.evidence && edge.evidence[0] && (
<>
<div className="quote">“{edge.evidence[0].quote}”</div>
<div className="tip-foot">
<a className="src-link" href={edge.evidence[0].url} target="_blank" rel="noopener">{prettyUrl(edge.evidence[0].url)}</a>
<span className={`tier-badge t${edge.tier}`}>T{edge.tier}</span>
</div>
</>
)}
</>
)}
</div>
);
};
function displayName(c) {
const n = (window.FORGE_NODES || []).find(x => x.canonical === c);
return n ? n.name : c;
}
function prettyUrl(url) {
if (!url) return "";
if (url.startsWith("mempalace://")) return "mempalace · rick";
try {
const u = new URL(url);
return u.hostname.replace(/^www\./, "") + u.pathname;
} catch { return url; }
}