diff --git a/AGENTS.md b/AGENTS.md index 06af20e5c..33b898208 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1091,7 +1091,7 @@ There is **one** highlighter in the app: the Shiki instance `@pierre/diffs` alre **Diagram engine (ui 0.41.0):** every Mermaid and Graphviz diagram renders through ONE renderer slot and ONE canvas, the viewer moved in from Workspaces (owner ruling: the new engine, not an option). `MermaidBlock` and `GraphvizBlock` are thin wrappers over `packages/ui/components/DiagramBlock.tsx`, which keeps the fence side (`diagramLanguages.ts`, the pending fence under "Rendering diagram…", the error panel with the source, the per-engine Retry epoch, the Show-source toggle, the natural-height box) and renders `DiagramViewer` (`packages/ui/components/diagram/`) inline and again at full size in `DiagramPopout` (the `TablePopout` chrome; one code path). The renderer slot is `packages/ui/utils/diagram-render.ts`: `renderDiagram(kind, renderId, source, theme)` loads the engine through its runtime slot (`utils/mermaid.ts` as before, with `applyMermaidTheme` per (palette, mode) before every render; `utils/graphviz.ts`, new, the same shape, `@viz-js/viz` pinned exactly `3.30.0`), sanitizes the output into a NODE (`sanitizeDiagramSvg` = DOMPurify `parseDiagramSvg` + the in-place `scrubDiagramSvg` belt: no script, no `on*`, no `javascript:`/`data:` reference, no `` — a Mermaid click binding must never turn a pinpoint click into a navigation), and pairs it with the engine's finder (`utils/diagram-anchor.ts` for Mermaid's id grammar, `utils/diagram-anchor-graphviz.ts` keyed on `g.node > title`, never `nodeN`). A load failure is a value (`runtimeUnavailable: true`), which is the one failure Retry can change; one automatic re-attempt runs after the slot's retry delay. The canvas (`DiagramCanvas`) mounts the node with `replaceChildren` inside a CSS-transformed wrapper — wheel/drag/`+`/`-`/`0`/arrow keys; click-to-select vs drag-to-pan on a pointer-type-aware threshold (4 px mouse, 10 px finger); NO hover targeting on a plain mouse-over (owner ruling: it read as messy and fought the pan hand — the ring under the pointer appears only while the platform modifier is held, `isModKeyHeld`, and disarms on its release, on any other key, and on blur); canvas keys ignore Meta/Ctrl/Alt so browser chords pass through; inline the canvas is `touch-action: pan-y` (a finger scrolls the page past it) and only the popout is `touch-none`; the zoom strip is `data-print-hide` while rings and badges print. Every edge gets an invisible 14 px hit path in ONE layer appended last in the svg root (`widenEdgeHitAreas`: bare geometry, ancestors' transforms composed, no id/class/`data-*`, `diagramHitSource` maps it back) — measured in Chromium, the only thing painted over an edge is its OWN label box, centred on its midpoint, so a label is a target that resolves to its edge, and because the hit layer sits over the nodes the canvas resolves a click by PRIORITY over `elementsFromPoint` (node, then edge, then cluster), never by `event.target`. A click that resolves no part comments on the WHOLE diagram (kind `diagram`), so a click never does nothing. The scrub also scopes every `