Connector nodes: bindable arrows between shapes
Problem
FigJam connectors do not survive OpenPencil today. CONNECTOR is in the node-type enum (packages/scene-graph/src/types.ts, kiwi type 19), but import and paste filter it as a non-visual type (NON_VISUAL_TYPES in packages/core/src/clipboard.ts), no binding fields (connectorStart / connectorEnd / magnets) exist anywhere in the model, and nothing renders or edits them. Pasting a FigJam flow keeps the boxes and silently drops every arrow.
Beyond imports, diagram and wireframe users want the Figma/Canva interaction: drag from one shape to another, get an arrow that stays attached when either shape moves.
Proposed direction
Follow Figma's data model so .fig round-trip stays natural:
- Scene graph: connector data on the node:
connectorStart/connectorEndas{ endpointNodeId, magnet: AUTO | TOP | BOTTOM | LEFT | RIGHT }or{ position }for a free end, plusconnectorLineType(STRAIGHT first, ELBOWED later) and end caps reusing the arrow-cap rendering from #615. - Derived geometry: connector shape recomputed from the current bounds of bound nodes (resolve the anchor, clip at the shape edge with a small gap, route, place the head), invalidated when a bound node moves or resizes; a deleted target unbinds that end to a fixed position.
- Rendering: draw the routed path through the existing stroke pipeline plus the arrow-cap heads.
- Import/paste: stop filtering CONNECTOR, decode endpoint bindings, and render statically even before any interaction exists.
- Editing: a connector tool (drag from a shape edge to another shape), magnet highlights on hover, endpoint re-drag to re-bind. MCP and design-JSX surfaces (
create_connector,<Connector>) so agents can build flow diagrams programmatically.
Phasing
- Schema + import + static rendering, so FigJam connectors survive and draw.
- Live re-routing on bound-node changes, plus MCP tools.
- Connector tool UX (magnets, previews), elbow routing, text labels.
I am happy to build this as a series of focused PRs if the direction works for you, and would appreciate guidance on where the derive pass should live and any constraints on scene-graph additions.
Source: open-pencil/open-pencil