// TextCanvas — narrative editor surface with markup chips, headings, prose. // Renders chips per markupStyle: "pill" | "underline" | "bracket" | "color". const CHIP_TYPE_LABEL = { block: "block", property: "property", association: "assoc", requirement: "req", }; function Chip({ kind, id, label, markupStyle, onHover, onClick, focused }) { const cls = `chip chip-${kind} chip-style-${markupStyle}${focused ? " chip-focus" : ""}`; const handlers = { onMouseEnter: () => onHover && onHover(id), onMouseLeave: () => onHover && onHover(null), onClick: () => onClick && onClick(id, kind), }; if (markupStyle === "bracket") { return ( [ {CHIP_TYPE_LABEL[kind]}: {label} ] ); } if (markupStyle === "underline") { return ( {kindGlyph(kind)} {label} ); } // pill (default) and color both render as filled chips, color uses kind color, pill is muted. return ( {kindGlyph(kind)} {label} ); } function kindGlyph(kind) { switch (kind) { case "block": return "▢"; case "property": return "·"; case "association": return "→"; case "requirement": return "§"; default: return "·"; } } function TextCanvas({ data, density, markupStyle, focusBlockId, setFocusBlockId, gutter }) { const padY = density === "compact" ? 10 : 18; return (
{node.children.map((c, j) => {
if (c.t === "text") return {c.v};
if (c.t === "chip") return (