Skip to content

Fix connector endpoint drag UX (no-crash translate + ghost line preview)#265

Merged
hackerwins merged 4 commits into
mainfrom
fix/slides-connector-drag-ux
May 18, 2026
Merged

Fix connector endpoint drag UX (no-crash translate + ghost line preview)#265
hackerwins merged 4 commits into
mainfrom
fix/slides-connector-drag-ux

Conversation

@hackerwins

@hackerwins hackerwins commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Fix runtime crash on connector drag. Selection-translate paths (body drag, keyboard nudge, align, distribute, rotate) all funneled every selected id through store.updateElementFrame, which rejects connectors because their frame is derived from start/end endpoints. Routes connectors through a new commitTranslate(store, slideId, el, dx, dy) helper that updates free endpoints via updateConnectorEndpoint and leaves attached endpoints anchored to their host.
  • Ghost line preview during endpoint drag. Previously the canvas re-rendered with the dragged endpoint at the cursor, so the real line disappeared from its original position and the user lost their reference point. Now the real line and its handles stay anchored at the pre-drag state; a translucent copy of the connector is layered on top through the renderer's existing forceRender(slide, doc, ghost) slot (same path the shape-insert hover preview uses). On release, the store commit + repaint teleports the line to where the ghost was.
  • Endpoint handle hover cursor → crosshair. Matches the connector-insert tool: in both cases the user is targeting a point that will become an endpoint.

Secondary cleanup that fell out of the work:

  • applyDrag deleted (unused in production; its tests now exercise the new translateElement directly, with explicit connector + attached-endpoint coverage).
  • applyFrameUpdates no longer takes a redundant slideId parameter.
  • New endpointDragging flag prevents the canvas pointerleave handler from clobbering snap-site dots when the cursor crosses overlay DOM mid-drag.

Test plan

  • pnpm verify:fast green (792 tests across packages; slides package at 1031).
  • New unit tests in packages/slides/test/view/editor/:
    • drag.test.ts: translateElement translates a shape's frame, preserves rotation/size, doesn't mutate input, translates a connector's free endpoints + cached frame, leaves attached endpoints anchored to their host.
    • editor.test.ts: drag a connector body without throwing + endpoints translate; multi-select drag (shape + connector) translates both; half-attached connector body drag translates only the free end; keyboard arrow nudge on a connector translates endpoints; mid-drag the real endpoint handle stays anchored at the pre-drag position.
  • Browser smoke (pnpm dev): select a connector, drag start/end endpoint — confirm the real line stays in place and a faded ghost line follows the cursor; release commits the move. Drag the connector body — confirm both endpoints translate (free) / stay anchored (attached). Keyboard nudge + align on a selection that includes a connector — no crash.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@hackerwins has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 40 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ba4ed83-734c-468b-b475-cfbfbf88ad04

📥 Commits

Reviewing files that changed from the base of the PR and between 4e9c310 and 2382c39.

📒 Files selected for processing (12)
  • docs/design/slides/slides-connectors.md
  • docs/tasks/archive/2026/05/20260519-slides-drag-connector-crash-lessons.md
  • docs/tasks/archive/2026/05/20260519-slides-drag-connector-crash-todo.md
  • docs/tasks/archive/README.md
  • packages/slides/src/view/canvas/slide-renderer.ts
  • packages/slides/src/view/editor/editor.ts
  • packages/slides/src/view/editor/interactions/drag.ts
  • packages/slides/src/view/editor/interactions/keyboard.ts
  • packages/slides/src/view/editor/overlay.ts
  • packages/slides/test/view/canvas/slide-renderer.test.ts
  • packages/slides/test/view/editor/editor.test.ts
  • packages/slides/test/view/editor/interactions/drag.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/slides-connector-drag-ux

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 221.2s

Lane Status Duration
sheets:build ✅ pass 14.4s
docs:build ✅ pass 12.9s
slides:build ✅ pass 14.3s
verify:fast ✅ pass 131.2s
frontend:build ✅ pass 18.9s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 5.0s
cli:build ✅ pass 2.2s
verify:entropy ✅ pass 22.0s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

hackerwins and others added 4 commits May 19, 2026 02:34
The selection-translate paths (body drag, keyboard nudge, align,
distribute, rotate) all funnelled every selected id through
`store.updateElementFrame`, which rejects connectors — their frame
is derived from `start`/`end` endpoints. So a connector in the
selection always threw on commit.

Route translations through new `commitTranslate(store, slideId, el,
dx, dy)` in `interactions/drag.ts`: connectors update their free
endpoints (attached endpoints stay anchored to their host shape),
non-connectors get the same `updateElementFrame` call as before. The
pure `translateElement` twin powers `paintLive` so the drag preview
and the commit produce identical state — fixes a latent bug where
the dragged connector's cached frame moved during the preview but
the rendered line (which reads endpoint positions) stayed put until
commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Previously the live drag re-rendered the canvas with the connector's
endpoint moved to the cursor, so the real line disappeared from its
original position and the user lost their reference point. Keep the
real line and its handles anchored at the pre-drag state; layer a
translucent ghost copy of the connector through the renderer's
existing `forceRender(slide, doc, ghost)` slot (same path the
shape-insert hover preview uses) so the user sees where the endpoint
will land without losing the original as a comparison anchor.

Also gate `onInsertHoverLeave` with a new `endpointDragging` flag.
The canvas `pointerleave` listener fires every time the cursor
crosses overlay DOM (handle, snap-site dots, ghost) — without the
gate it nulls `connectorCursor` and re-paints the overlay, which
flickers the snap-site dots and (when the previous handle-anchor
implementation was in place) wiped the ghost on every move.

And swap the endpoint handle cursor from `pointer` to `crosshair` to
match the connector-insert tool: in both cases the user is targeting
a point that will become an endpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- Pin the "attachment wins" semantic with a body-drag test on a
  connector whose both endpoints attach to shapes outside the
  selection — without this, a refactor switching attached endpoints
  to "also translate by delta" would silently regress.
- Add a renderer-level test that `forceRender(slide, doc, ghost)`
  with a connector ghost strokes the line with `GHOST_ALPHA` and
  brackets the paint with `save`/`restore`. Closes the gap the
  editor-side test comment hand-waved as "covered by renderer
  tests."
- Update the `forceRender` JSDoc to name both uses (hover-preview
  insert AND endpoint-drag preview) and the fact that a half-
  attached ghost connector resolves through the slide lookup.
- Document the `paintLive` `el.type`-preservation invariant so a
  future caller can't quietly substitute a connector for a shape.
- Note the `commitTranslate(0,0)` no-op suppression in the lessons
  file — silent behavior shift from the prior `updateElementFrame`
  path that emitted a redundant op.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
All todo items checked off; lessons captured; review feedback
addressed in the same branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins
hackerwins force-pushed the fix/slides-connector-drag-ux branch from 3ea202a to 2382c39 Compare May 18, 2026 17:39
@hackerwins
hackerwins merged commit c23d8d9 into main May 18, 2026
4 checks passed
@hackerwins
hackerwins deleted the fix/slides-connector-drag-ux branch May 18, 2026 23:11
hackerwins added a commit that referenced this pull request May 18, 2026
Rebase folded in main's depth-aware `hitTestSlide` (added with the
group / ungroup work in #263 and reused by the connector endpoint
drag fix in #265). The integration commit reroutes that function
through the new precise `hitTestElement`, which two existing tests
now need:

- `hit-test-elements.test.ts` — pass a `ctx` option to every
  `hitTestSlide` call, and give the shape factory a `fill` so the
  rect has a visible body for `isPointInPath` to land on. (The
  previous fixture relied on bbox-only hit-test which the precise
  test rejects for empty shapes.)
- `editor.test.ts` body-drag-of-half-attached-connector — the click
  used to land in empty bbox space; move it onto the line's midpoint
  with the same drag delta so the connector still gets selected.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@hackerwins hackerwins mentioned this pull request May 24, 2026
2 tasks
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