Skip to content

feat(helper-lines): add smart alignment and paint snap opt-out - #860

Merged
hm21 merged 4 commits into
hm21:stablefrom
saif-ellafi:smart-alignment
Sep 7, 2026
Merged

feat(helper-lines): add smart alignment and paint snap opt-out#860
hm21 merged 4 commits into
hm21:stablefrom
saif-ellafi:smart-alignment

Conversation

@saif-ellafi

@saif-ellafi saif-ellafi commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Description

Hello, this is a bit of an experimental, AI-Assisted branch that improves snapping with helperLines.
The motivation is to soften the magnetic force field without having to disable edge snapping (as previous PR did) to improve the user experience.

However, Smart Snapping is quite opinionated, and I put it on a way to be optional, but feel free to decide what you think out of it. Tested manually.

This adds opt-in smart alignment so same-kind layers snap matching edges (left↔left, etc.), mixed types stay center↔center, and only nearby / shared-axis neighbors participate. Canvas midlines snap centers only when smart is on. Custom guides stay wildcards.

Snapping is measured from the pointer’s intended position, so both axes can catch and still be left (diagonal L drags don’t get stuck).

Also adds enablePaintLayerSnapping (default true). When false, paint is ignored as a snap target and does not snap while dragging. This is because painting layers are free form weird shaped things, I found it annoying to snap to a drawing layer, I think it goes well together.

Defaults keep current behavior: enableSmartAlignment: false, enablePaintLayerSnapping: true.

Related Issue: Closes #

Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 🛠️ Bug fix (non-breaking change which fixes an issue)
  • ❌ Breaking change (fix or feature that would cause existing functionality to change)
  • 🧹 Code refactor
  • ✅ Build configuration change
  • 📝 Documentation
  • 🗑️ Chore

saif-ellafi and others added 4 commits September 7, 2026 11:21
Dense canvases can snap same-kind edges from the pointer's intended
position, with paint layers optionally excluded from snapping.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… edges

Expose the neighbour reach and shared-axis minimum as
`smartAlignmentNeighborLimit` / `smartAlignmentSharedAxisMin` instead of
hardcoding 3 and 2, snap a shared axis to the real edge closest to the
cluster instead of the cluster average, and measure neighbour distances
once instead of inside the sort comparator.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hm21

hm21 commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Thanks a lot for this PR — really nice work. The problem you describe is real: on a dense canvas the old magnet field gets sticky, and disabling edge snapping entirely was too blunt a fix. Measuring the snap from the pointer's intended position instead of the glued position is the right call, and it's what makes diagonal drags catch both axes without trapping the layer.

I reviewed it carefully and confirmed the default path (enableSmartAlignment: false, enablePaintLayerSnapping: true) is behaviour-preserving — addTarget, _anchorForTarget, _heldAlignKey and _canvasSnapAnchor all collapse to the previous logic when the flag is off. The one delta, releasing held targets at max(snapThreshold, releaseThreshold), is a no-op at normal zoom and fixes a case when zoomed far out where a target could be released and re-trapped in the same frame. Good catch.

I pushed three small follow-ups on top of your branch rather than sending you back and forth:

  • Tunable reach_smartAlignmentNeighborLimit (3) and _smartAlignmentSharedMin (2) are now HelperLineConfigs.smartAlignmentNeighborLimit / smartAlignmentSharedAxisMin. The feature is opinionated by design, so apps should be able to dial it in rather than take our numbers.
  • Shared axes snap to a real edge — a cluster previously snapped to the average of its positions, which can sit up to snapThreshold off every actual layer edge, so the guide line didn't quite overlay anything. It now picks the real edge closest to the cluster mean.
  • Neighbour distances measured once instead of recomputing _snapLayerCenter inside the sort comparator on every drag frame.

Plus the CHANGELOG entries and tests for the new fields. Merging now — thanks again!

@hm21
hm21 merged commit a4f61fc into hm21:stable Sep 7, 2026
1 check passed
hm21 added a commit that referenced this pull request Sep 7, 2026
13.4.0 is already published, so the smart alignment and paint snap
opt-out from #860 belong in a new unreleased section.
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