// Board-level SVG connectors span canvases and tiles. Clip their strokes to // the visible space outside window interiors; their endpoint highlights stay // in the overlay. Only window movement, never focus alone, lifts the strokes. export type ConnectorBox = { x: number; y: number; width: number; height: number }; export function connectorOcclusion(root: Element, origin: DOMRect): { holes: ConnectorBox[]; lifted: boolean } { const lifted = root.matches('.carrying') || !!root.querySelector('.svelte-flow__node.dragging, .canvas.carrying'); if (lifted) return { holes: [], lifted }; const holes: ConnectorBox[] = []; for (const slot of root.querySelectorAll('.window-slot')) { if (!slot.offsetParent || slot.closest('[inert]')) continue; const rect = slot.getBoundingClientRect(); let { left, right, top, bottom } = rect; // A window panned outside a nested canvas must not hide a line over the // neighbouring tiles. Only the part visible through every canvas counts. for (let canvas = slot.closest('.canvas-tile'); canvas; canvas = canvas.parentElement?.closest('.canvas-tile') ?? null) { const view = canvas.getBoundingClientRect(); left = Math.max(left, view.left); right = Math.min(right, view.right); top = Math.max(top, view.top); bottom = Math.min(bottom, view.bottom); } if (right > left && bottom > top) holes.push({ x: left - origin.left, y: top - origin.top, width: right - left, height: bottom - top }); } return { holes, lifted }; }