Skip to content

chess: drag pieces to move, mouse and touch - #398

Merged
aaylward merged 2 commits into
mainfrom
claude/chess-drag
Oct 1, 2026
Merged

aaylward merged 2 commits into
mainfrom
claude/chess-drag

Conversation

@aaylward

@aaylward aaylward commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

You can now drag a piece to move it, as well as tapping the piece and then its square. Mouse and touch both go through Pointer Events.

  • A press becomes a drag only after it moves 6px, so a tap that wobbles a little still selects the piece.
  • During a drag, the piece follows the pointer and the squares it can reach are marked.
  • Dropping on a reachable square plays the move, or opens the promotion picker. Dropping anywhere else plays nothing and leaves the piece selected, so a tap can finish the move.
  • The drop square is read from where the pointer is, not from the event's target, because a touch keeps every event on the square it started on.
  • The click a browser fires after a drag is ignored, so it can't drop the selection.
  • Only the viewer's movable pieces get touch-action: none, so the rest of the board still scrolls the page.
  • Tapping, the keyboard and screen-reader labels work as before.

Tests:

  • Drag to a square plays the move.
  • The dragged piece and the reachable squares show mid-drag.
  • Dragging to the promotion square asks for the piece.
  • A drop on a square the piece can't reach, or off the board, plays nothing and keeps the piece selected.
  • Nothing can be dragged off turn or while disconnected.
  • A small wobble is still a tap.
  • data-grab is set only on movable pieces.

Three mutations were run and all three caught: removing the click suppression, landing on the event's target square instead of the square under the pointer, and dropping the slop. Vitest, typecheck and lint are clean.

In Chromium at 390×844, a mouse drag and a touch drag (sent through the DevTools protocol) both played g6f7, and the touch drag didn't scroll the page.

Whether to replace the board with react-chessboard is tracked in #397.


Generated by Claude Code

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
muchq c8b9b48 Commit Preview URL

Branch Preview URL
Oct 01 2026, 02:03 AM

@cursor

cursor Bot commented Oct 1, 2026

Copy link
Copy Markdown

Blocking

  • No setPointerCapture once the press crosses the slop. onPointerMove / onPointerUp live on the board, so a release outside the board element never lands: press and the ghost stick, and the advertised “drop anywhere else → keep selected” path does not run. Castle’s hand drag takes capture only after the slop for the same reason (setPointerCapture on press would eat the tap). Capture on boardRef when moving becomes true; optional ?. for jsdom like Castle.

Nits

  • The ghost is a child of .board, which is overflow: hidden, so it clips at the rim — and once capture lets the pointer leave, it disappears entirely while still dragging. Worth overflow: visible during a drag, or parking the ghost outside the clipped box.
  • The off-board / “null under pointer” test fires pointerUp on the origin square (still inside the board). That passes without capture. Pinning a release whose target is outside the board would lock the Blocking fix in.

Fine

Pointer Events for mouse and touch; 6px slop; land via elementFromPoint (not the touch’s retargeted square); click after a drag suppressed; touch-action: none only on movable pieces; tap / keyboard / SR path unchanged. Tests cover play, mid-drag chrome, promotion, miss, off-turn / offline, and data-grab. CI green. No hub wire change — merge when the capture fix is in. #397 stays the longer board question.


Review of #398

aaylward commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in c8b9b48: the blocking finding and both nits.

  • Pointer capture: once a press passes the slop, the board calls setPointerCapture (with ?. for jsdom), so a release off the board still ends the drag. Capture is not taken on the press itself, so a tap still gets its click.
  • Clipping: the dragged piece is now portalled to document.body and drawn position: fixed at the pointer, sized from the board's width. The board's overflow: hidden no longer clips it.
  • Tests: new tests pin the capture (not on the press, only past the slop, with the right pointerId), a release delivered to the board from off-board coordinates, and the ghost living outside the board.
  • Browser check: in Chromium, a mouse released at (200, 820), below the board, cleared the ghost and kept g6 selected. The next drag played g6f7.

Generated by Claude Code

@aaylward
aaylward merged commit 540068c into main Oct 1, 2026
2 checks passed
@aaylward
aaylward deleted the claude/chess-drag branch October 1, 2026 02:06
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.

2 participants