Skip to content

fix: the drag now tracks the cursor - #28

Merged
DoodleBears merged 1 commit into
mainfrom
fix/drag-tracks-cursor
Oct 2, 2026
Merged

DoodleBears merged 1 commit into
mainfrom
fix/drag-tracks-cursor

Conversation

@DoodleBears

@DoodleBears DoodleBears commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Why

Reported as "it should go where you click, but the handle drifts further out the further out
you drag"
. That growth with distance is the diagnostic — a constant offset and a
proportional one look identical at the centre and nothing alike at the rim.

Change outline

The conversion was wrong twice, in the two ways that compound:

wrong pitch box.width / cols is not the pitch. The box is size px and includes one dot of overhang at each end, so centre-to-centre is layout().pitchX — the hand-rolled figure was ~3% out before anything else
scaled twice cx appears once to name the middle cell and again as a scale factor, so the whole thing was four times too sensitive

Measured on a 9×9 at 260px — error against the true cell:

col 0: −12 cells (clamped)   col 4: 0   col 6: +6   col 8: +12 (clamped)

That is the reported behaviour exactly: the handle reaches the edge while the cursor is
halfway out, and dragging further does nothing, because there is nothing past the clamp.

Both numbers now come from the engine's layout() — the library's own rule that field()
is the only place geometry is computed — applied to a control that has to agree with the thing
it points at. And cellCentre is now shared: the handle is drawn where the hit-test will
look, which is stronger than both being independently correct.

The handle is positioned in px, not as a percentage. A cell's centre is half a dot in
from the edge, so col / (cols - 1) as a percentage is wrong by half a dot — 16px here — and
by a different amount everywhere else.

Special things to note

  • The regression walks all 81 cells and requires the identity, plus a test that the error
    does not grow outward. A hand-picked sample of cells passes the old code: it is only
    wrong past the middle, and every sample anyone would pick by hand is in the middle.
  • Verified in a real browser by driving Input.dispatchMouseEvent along a line and
    comparing the handle's measured centre to the cursor at each step: ≤0.3px throughout.
  • 275 tests (was 269), oxlint 0, registry ok, both changelog ledgers.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Corrected drag-handle positioning and cell selection in the traversal demo so pointer input maps consistently to the displayed grid, including near its edges.

Reported as: it should go where you click, but the handle drifts further out the further
out you drag. That growth with distance is the diagnostic — a constant offset and a
proportional one look identical at the centre and nothing alike at the rim.

THE CONVERSION WAS WRONG TWICE, in the two ways that compound:

  · the pitch was `box.width / cols`, which is not the pitch. The field's box is `size` px
    and includes one dot of overhang at each end, so the centre-to-centre distance is
    `layout().pitchX`. The hand-rolled figure was about 3% out before anything else.
  · and the centre-to-cell step multiplied by the half-width a SECOND time. `cx` appears
    once to name the middle cell and again as a scale factor, so the whole thing was four
    times too sensitive.

Measured on a 9×9 at 260px, the error against the true cell: 0 at the centre, 6 cells at
col 6, and 12 cells — clamped — at the rim. That is the reported behaviour exactly: the
handle reaches the edge while the cursor is halfway out, and dragging further does nothing,
because there is nothing past the clamp.

BOTH NUMBERS NOW COME FROM THE ENGINE'S `layout()`. That is the library's own rule — `field()`
is the only place geometry is computed — applied to a control that has to agree with the
thing it is pointing at. And `cellCentre` is now SHARED: the handle is drawn where the
hit-test will look, so they cannot drift apart, which is a stronger guarantee than both being
correct.

THE HANDLE IS POSITIONED IN PX, not as a percentage of the box. A cell's centre is half a dot
in from the edge, so `col / (cols - 1)` as a percentage is wrong by half a dot — 16px here at
the rim — and wrong by a different amount everywhere else. The overlay also moved inside a
wrapper sized by the field, so `inset: 0` IS the host box.

THE REGRESSION WALKS ALL 81 CELLS and requires the identity, plus a test that the error does
not grow outward. A hand-picked sample of cells passes the old code: it is only wrong past
the middle, and every sample anyone would pick by hand is in the middle.

275 tests (was 269), oxlint 0, registry ok, both changelog ledgers.
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (2)
CHANGELOG.md — configured
CHANGELOG.zh.md — configured

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 1b090df0-4993-4056-8cc3-edabeae00864

📥 Commits

Reviewing files that changed from the base of the PR and between 11ade35 and c18ab48.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • CHANGELOG.zh.md
  • apps/site/src/components/TraversalDemo.tsx
  • apps/site/src/styles/site.css
  • apps/site/src/traversals.ts
  • apps/site/test/traversal.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The traversal demo now uses geometry from layout() to position the origin handle and map pointer coordinates to cells. The change adds cell-centre mapping and regression tests for all 81 cells, outward movement, and grid-edge clamping.

Changes

Traversal geometry

Layer / File(s) Summary
Cell geometry and mapping
apps/site/src/traversals.ts, apps/site/test/traversal.test.ts
FieldGeometry and cellCentre define cell-centre coordinates. nearestCell uses per-axis pitches. Tests check cell-centre selection, outward movement, half-dot overhang, and clamping at both grid edges.
Demo geometry integration
apps/site/src/components/TraversalDemo.tsx, apps/site/src/styles/site.css, CHANGELOG.md, CHANGELOG.zh.md
The demo derives geometry with layout(FIELD), positions the handle with cellCentre, and passes geometry to nearestCell. A stage wrapper provides the shared coordinate reference for the matrix and handle. The changelogs describe the correction and tests.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to c18ab

The drag geometry appears aligned with the rendered matrix, with no identified issue requiring a fix before merge.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to c18ab

The inspected change remains a bounded browser-demo interaction. Pointer movement updates local rendering state, existing bounds and cleanup remain intact, and no new privileged operation or material security risk was identified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — For ordinary browser pointer input, the independently affected scope is the current demo instance and its 81-cell rendering state. The traced change does not increase authority over tenants, services, stored data or credentials.

Trust Boundaries and Controls

  • observed — The demo supplies fixed field dimensions and geometry rather than accepting them from the pointer. nearestCell retains bounds clamping, and movement remains gated by rings mode and pressed buttons. These are interaction-containment controls, not authentication or authorization mechanisms.

Resilience and Maintainability Implications

  • observed — The pointer-capture lifecycle, absence of explicit cancellation handlers and last-event-wins concurrent-pointer behavior predate this change. No durable pointer transaction is introduced. Matrix still destroys its renderer on unmount, cancelling its animation frame and clearing rendered children; renderer updates retain cell-identity checks.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: correcting drag behavior so the handle tracks the cursor.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 3 files. (3 skipped: 3 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@DoodleBears
DoodleBears merged commit e200b75 into main Oct 2, 2026
2 checks passed
@DoodleBears
DoodleBears deleted the fix/drag-tracks-cursor branch October 2, 2026 05:55
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