Skip to content

The connector handles were unreachable, because hover was the wrong question - #46

Merged
boubou666 merged 1 commit into
mainfrom
connect-handles-reach
Aug 9, 2026
Merged

boubou666 merged 1 commit into
mainfrom
connect-handles-reach

Conversation

@boubou666

@boubou666 boubou666 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Reported from the app, and true: the handles a connector is dragged from could not be pressed.

They are drawn outside an object's border — they have to be, or they stand on the resize handles — so a pointer travelling out to one leaves the object before it arrives. The offer was .obj:hover, so it went away on the way to itself. Walking a pointer out across the edge two pixels at a time, the handles are gone from the first pixel past the border and never come back.

Reach, not hover

The input layer tracks the pointer and marks the topmost object whose box, grown by thirty screen pixels, contains it. That is the whole affordance — a region bigger than the element, which is not something :hover can express — and it is why the class is applied from JavaScript rather than by a selector.

Near rather than selected is still what keeps these clear of the resize handles: those appear for a single selected object at the eight compass points, so the two sets are never under the pointer at the same moment.

Why eight tests covered a feature nobody could use

Every one of them moved the pointer in a single hop, from the middle of the card to the handle — a path no hand takes, and one that never passes through the dead zone. CDP will happily teleport a pointer; a person cannot.

The new test walks it: out from the middle, across the border, to the handle, two pixels at a time. It fails against the version this replaces, which I checked before keeping it.

And one the suite caught immediately

e.target is not always a node. An event dispatched at the window has the window as its target, and Node.contains throws rather than answering false — on a listener that runs on every pointer move, which is every pointer move throwing. Four tests in interact.test.js said so; the nodeType check is what tells a node from a window.

Checks

1060 tests, all passing, 97.2% line coverage.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PTizw7U35rC2MPquwr4Fv5

Summary by CodeRabbit

  • New Features

    • Connector handles now appear when the pointer is within 30 pixels of an object’s edge.
    • The nearest topmost object is prioritized when multiple objects are nearby.
    • Connector handles remain separate from object resize handles.
  • Bug Fixes

    • Connector handles remain visible and interactive as the pointer moves from an object toward an external handle.
    • Handles are hidden during editing and gestures as expected.

…uestion

Reported from the app, and true: the handles could not be pressed. They are
drawn *outside* the border — they have to be, or they stand on the resize
handles — so a pointer moving out to one leaves the object before it gets there.
The offer was `.obj:hover`, so it went away on the way to itself. Walked out
across the edge two pixels at a time, the handles are gone from the first pixel
past the border and never come back.

So the offer is by **reach**, not by hover: the input layer tracks the pointer
and marks the topmost object whose box, grown by thirty screen pixels, contains
it. That is the whole affordance — a region bigger than the element, which is
not a thing `:hover` can express.

**Every test in that block moved the pointer in one hop**, which is a path no
hand takes, and that is why eight tests covered a feature nobody could use. The
new one walks: out from the middle of the card, across the border, to the
handle, two pixels at a time — and it fails against the version this replaces,
which I checked before keeping it.

One more thing found on the way, by the suite rather than by me: `e.target` is
not always a node. An event dispatched at the window has the window as its
target and `Node.contains` throws rather than answering false — on a listener
that runs on every pointer move, which is every pointer move throwing. Four
tests in `interact.test.js` said so immediately; the `nodeType` check is what
tells a node from a window.

1060 tests, 97.2% lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PTizw7U35rC2MPquwr4Fv5
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b3a8f169-789f-4297-ba25-2b29850e2958

📥 Commits

Reviewing files that changed from the base of the PR and between c5665df and 0bb27de.

📒 Files selected for processing (4)
  • README.md
  • src/platform/input.js
  • src/styles/canvas.css
  • test/browser/connectors.test.js

📝 Walkthrough

Walkthrough

Connector handles now appear when the pointer is within 30 screen pixels of an object edge. The input layer selects the topmost nearby object and applies its .near state. CSS and browser tests use the new behavior.

Changes

Connector handle proximity

Layer / File(s) Summary
Track nearby connector objects
src/platform/input.js
The input controller defines CONNECT_REACH, tracks nearby placed objects, updates state during pointer movement, suppresses offers during active interactions, and clears state on destruction.
Render and validate nearby handles
src/styles/canvas.css, test/browser/connectors.test.js, README.md
Connector handles use the .near class. The browser test verifies incremental pointer movement keeps the handles visible and interactive. The README documents the 30-pixel reach.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Pointer
  participant InputController
  participant PlacedObjects
  Pointer->>InputController: Move pointer
  InputController->>PlacedObjects: Check scale-adjusted reach
  PlacedObjects-->>InputController: Return topmost nearby object
  InputController->>PlacedObjects: Set near state
Loading

Possibly related PRs

🚥 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 summarizes the main change from hover-based visibility to pointer-reach tracking for connector handles.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch connect-handles-reach

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

@boubou666
boubou666 merged commit 861d443 into main Aug 9, 2026
4 of 5 checks passed
@boubou666
boubou666 deleted the connect-handles-reach branch August 9, 2026 22:28
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