Skip to content

v1.9.0: canvas arrowheads, a zoom floor that fits a phone, 14 measured canvas fixes - #73

Merged
xmpuspus merged 1 commit into
mainfrom
fix/v190-canvas-interaction
Aug 1, 2026
Merged

v1.9.0: canvas arrowheads, a zoom floor that fits a phone, 14 measured canvas fixes#73
xmpuspus merged 1 commit into
mainfrom
fix/v190-canvas-interaction

Conversation

@xmpuspus

@xmpuspus xmpuspus commented Aug 1, 2026

Copy link
Copy Markdown
Owner

v1.8.0 rebuilt the interface around the canvas and never measured the canvas itself, so every pan, zoom and drag setting was still a React Flow default. This PR drives all 16 init templates through the running app and runs 8 interactions at 1920, 1440 and 390px. That found 14 defects, and closes them.

Every number below comes from the live DOM of the running app, with a mock LLM in front of the real cost engine, the real validator and the real layout code.

The diagram did not say which way its arrows point

The computed markerEnd on every edge was none. A directed architecture diagram shipped with no direction on it. The lines themselves ran at 1.42:1 contrast in light and 1.81:1 in dark, against the 3:1 WCAG floor for a meaningful graphic.

Across the 16 templates Before After
Connections hidden behind a card they do not touch 17 5
Labels sitting on a card 18 13
Connection crossings 8 13
Labels sitting on another label 5 5
Overlapping cards 0 0
Cards outside their own tier box 0 0

Crossings rise by 5 and that is the deliberate half of the trade. A connection crossing another connection stays readable. One that vanishes behind an unrelated card does not, and neither does a card whose name a label covers.

The routing rule is now: neighbours in a row link straight across, anything further apart in the same tier dips under the row, and a connection that skips a tier runs down the outside. A barycentre sweep orders each tier before placing it, with ties keeping the spec's own order so one spec always draws one picture.

The canvas ate the drag that was supposed to pan it

Interaction 1920 before 1440 before 390 before After, all three
Pan pass pass pass pass
Zoom in pass pass pass pass
Zoom out pass pass button disabled pass
Fit view pass pass 2 of 8 nodes off-pane at 0.500 pass, 0 off-pane at 0.293
Drag a node in its boundary 9.9px of 25 8.1px 5.0px 23 to 24px of 25
Drag a node out of its boundary clamped clamped clamped moves 248px of 260
Connect two nodes pass pass fail pass
Delete a connection Backspace only Backspace only Backspace only Delete works

Measured causes: React Flow's minZoom 0.5, extent: "parent" against a box that hugged its own contents, deleteKeyCode defaulting to Backspace alone, and boundary containers carrying nopan. Dragging the Application boundary moved it and its children 113px while the VPC stayed put, and handleNodeDragStop returned early on boundary- ids, so nothing saved it.

Boundaries are decoration now: no drag, no click, no pointer event. Nodes carry absolute positions and the boundary re-fits around wherever they land.

Five suspects that measured clean, so this PR does not touch them

  • preventScrolling true. The app is a 100dvh grid with no page scroll at any width, so nothing gets trapped.
  • <Controls /> styling and names. styles.css already bridged it to the tokens, and the names read Zoom In, Zoom Out, Fit View, Toggle Interactivity.
  • Catalog drag and drop. It does not exist and no documentation claims it. README and getting-started.md say "drag components", which means dragging a node, and that works. docs/competitor-landscape.md lists a drag-and-drop designer as a competitor's advantage.
  • panOnDrag against node dragging. Both pass at all three widths.
  • maxZoom, snapToGrid, selectionOnDrag, nodeDragThreshold. No measured failure.

Verification

  • Core 1392, web 165, cli 137, mcp 35, all passing with .env hidden and both API keys unset. Web gains the 13 new tests in packages/web/tests/test_canvas_contract.py, one per contract, each naming the defect it guards.
  • ruff check and ruff format --check clean. tsc --noEmit clean.
  • All 16 template screenshots read back before and after, plus a dark-theme pair and a boundary-drag pair.
  • Frontend rebuilt and copied into cloudwright_web/static; test_static_bundle.py passes and unzip -l on the 1.9.0 web wheel shows both new hashed assets.
  • examples/cloudwright-smart-canvas-demo.gif re-recorded against the new bundle, 3.6 MB, 4 frames read back. The first re-recording caught a stale server on port 8765 still serving 1.7.0, and a second one caught Add Resource dropping the new node on a colliding grid slot.

Merge mechanism

git-safety-gate.py needs a human APPROVED review and GitHub refuses a self-approve, so this repo has no satisfiable path for a solo reviewer. Xavier authorised the API merge in advance for this task, so this merges with gh api -X PUT repos/xmpuspus/cloudwright/pulls/<N>/merge -f merge_method=squash.

Reviewed by Xavier Puspus

…d canvas fixes

v1.8.0 rebuilt the interface around the canvas and never measured the canvas, so
every pan, zoom and drag setting was still a React Flow default. Driving all 16
init templates through the running app, plus 8 interactions at 1920, 1440 and
390px, found 14 defects.

What the diagram draws:
- every edge computed markerEnd: none, so a directed diagram showed no direction
- connection lines ran at 1.42:1 contrast in light and 1.81:1 in dark, under 3:1
- 17 connections disappeared behind a card they do not touch; now 5
- 18 labels sat on a card; now 13
- a tier orders its components by barycentre before it places them
- the VPC border no longer shares a line with the tier borders inside it
- boundary colours come from theme tokens, so dark mode drops the pale slab
- a card holds a fixed 260px, the width the layout reserves for it

Canvas interaction, 24 of 24 cells pass, from 19:
- the 0.5 zoom floor left 2 of 8 nodes off a 390px pane; the floor is 0.12
- a drag inside a VPC pans instead of dragging the box out of shape
- a node moves the full distance dragged, from 5 to 10px under extent parent
- Delete removes a selected connection, not Backspace alone
- connect-by-drag works at 390px
- the dead NodeResizer is gone
- Add Resource puts the resource in its tier, not on a colliding grid slot

13 new tests in packages/web/tests/test_canvas_contract.py guard each contract.
Core 1392, web 165, cli 137, mcp 35 all pass. Demo GIF re-recorded, 3.6 MB.

Co-Authored-By: Xavier Puspus
@xmpuspus
xmpuspus merged commit 9819c23 into main Aug 1, 2026
6 checks passed
@xmpuspus
xmpuspus deleted the fix/v190-canvas-interaction branch August 1, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant