Excalidraw scene now mirrors the v5 routing model:
- Clamps export as 12×12 grey rounded squares (BackgroundColor=#888888,
StrokeColor=#555555, Roundness type 3). Distinct from the red IO
marker diamonds so wall outlets vs. routing anchors stay readable.
Frame_id propagates into the element's FrameID per the existing
pattern.
- Cable arrows include clamp positions as mid-vertices in the
`points` array. Pre-grouped + sort.Slice-sorted by ord; each
mid-vertex is added as an (x-fromAnchor.x, y-fromAnchor.y) offset.
startBinding / endBinding still point at the from / to endpoint
excalidraw_ids; mid-vertices are unbound (Excalidraw doesn't have
per-vertex binding).
- IDAssignment grows a Clamps map; PersistExcalidrawIDs accepts it
and updates clamps.excalidraw_id on first export so re-exports
reuse the same element ids (collab cursors / undo history survive).
- Bundle-stripe overlay is **viewer-only** — Excalidraw can't
represent gradient strokes losslessly, so we export individual
cable arrows and let the in-app viewer derive the bundle viz.
Tests:
- TestBuildScene_ClampsRenderAsRectangles — 2 clamps → 2 rectangle
elements + 2 ids in IDAssignment.Clamps.
- TestBuildScene_ArrowPointsIncludeClamps — cable with 1 clamp →
arrow.Points has 3 entries; middle vertex equals the clamp's
position relative to fromAnchor.
This closes the v5 slice plan (§11.10). Six slices, one branch,
one redeploy below.