Spaces:
Sleeping
Sleeping
| // 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; } | |
| } | |