-
Notifications
You must be signed in to change notification settings - Fork 448
allow Vue nodes to be resized from all 4 corners #6187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🎨 Storybook Build Status✅ Build completed successfully! ⏰ Completed at: 10/23/2025, 07:17:37 PM UTC 🔗 Links🎉 Your Storybook is ready for review! |
🎭 Playwright Test Results❌ Some tests failed ⏰ Completed at: 10/23/2025, 07:33:04 PM UTC 📈 Summary
📊 Test Reports by Browser
🎉 Click on the links above to view detailed test results for each browser configuration. |
Bundle Size ReportSummary
Category Glance Per-category breakdownApp Entry Points — 3.29 MB (baseline 3.29 MB) • 🔴 +224 B_Main entry bundles and manifests_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | ---------------------------------------- | ------- | ------- | ----------------------- | ---------------------- | ----------------------- | | **assets/index-1zY9b7lk.js** _(new)_ | — | 2.67 MB | 🔴 +2.67 MB | 🔴 +555 kB | 🔴 +421 kB | | ~~assets/index-ldaT9BrQ.js~~ _(removed)_ | 2.67 MB | — | 🟢 -2.67 MB | 🟢 -555 kB | 🟢 -420 kB | | ~~assets/index-eM-m3V6Q.js~~ _(removed)_ | 614 kB | — | 🟢 -614 kB | 🟢 -114 kB | 🟢 -90.1 kB | | **assets/index-vlnEHE3q.js** _(new)_ | — | 614 kB | 🔴 +614 kB | 🔴 +114 kB | 🔴 +90.1 kB |Status: 2 added / 2 removed Graph Workspace — 711 kB (baseline 707 kB) • 🔴 +4.02 kB_Graph editor runtime, canvas, workflow orchestration_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | -------------------------------------------- | ------ | ------ | ---------------------- | ---------------------- | ---------------------- | | **assets/GraphView-Pk_-B0FP.js** _(new)_ | — | 711 kB | 🔴 +711 kB | 🔴 +139 kB | 🔴 +108 kB | | ~~assets/GraphView-CkYngSnH.js~~ _(removed)_ | 707 kB | — | 🟢 -707 kB | 🟢 -138 kB | 🟢 -107 kB |Status: 1 added / 1 removed Views & Navigation — 8.15 kB (baseline 8.15 kB) • ⚪ 0 B_Top-level views, pages, and routed surfaces_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | ------------------------------------------------- | ------- | ------- | ----------------------- | ----------------------- | ----------------------- | | **assets/UserSelectView-BBgssrWE.js** _(new)_ | — | 8.15 kB | 🔴 +8.15 kB | 🔴 +2.47 kB | 🔴 +2.16 kB | | ~~assets/UserSelectView-Brnhhy6g.js~~ _(removed)_ | 8.15 kB | — | 🟢 -8.15 kB | 🟢 -2.47 kB | 🟢 -2.16 kB |Status: 1 added / 1 removed Panels & Settings — 294 kB (baseline 294 kB) • ⚪ 0 B_Configuration panels, inspectors, and settings screens_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | ---------------------------------------------------- | ------- | ------- | ----------------------- | ----------------------- | ----------------------- | | **assets/CreditsPanel-B67V1c8j.js** _(new)_ | — | 22.1 kB | 🔴 +22.1 kB | 🔴 +5.28 kB | 🔴 +4.61 kB | | ~~assets/CreditsPanel-Dt4Ut9Br.js~~ _(removed)_ | 22.1 kB | — | 🟢 -22.1 kB | 🟢 -5.28 kB | 🟢 -4.61 kB | | ~~assets/KeybindingPanel-BnwCDuP_.js~~ _(removed)_ | 15.2 kB | — | 🟢 -15.2 kB | 🟢 -3.76 kB | 🟢 -3.3 kB | | **assets/KeybindingPanel-DC1AGOIV.js** _(new)_ | — | 15.2 kB | 🔴 +15.2 kB | 🔴 +3.76 kB | 🔴 +3.31 kB | | ~~assets/ExtensionPanel-BkdJi3nA.js~~ _(removed)_ | 12.1 kB | — | 🟢 -12.1 kB | 🟢 -2.83 kB | 🟢 -2.47 kB | | **assets/ExtensionPanel-Ce-sCiVZ.js** _(new)_ | — | 12.1 kB | 🔴 +12.1 kB | 🔴 +2.83 kB | 🔴 +2.47 kB | | **assets/AboutPanel-BYZLbTc6.js** _(new)_ | — | 10.3 kB | 🔴 +10.3 kB | 🔴 +2.67 kB | 🔴 +2.35 kB | | ~~assets/AboutPanel-CJ8ZgROM.js~~ _(removed)_ | 10.3 kB | — | 🟢 -10.3 kB | 🟢 -2.67 kB | 🟢 -2.35 kB | | **assets/ServerConfigPanel-BXgvnOsr.js** _(new)_ | — | 8.2 kB | 🔴 +8.2 kB | 🔴 +2.17 kB | 🔴 +1.9 kB | | ~~assets/ServerConfigPanel-CgkhuqTZ.js~~ _(removed)_ | 8.2 kB | — | 🟢 -8.2 kB | 🟢 -2.16 kB | 🟢 -1.9 kB | | ~~assets/UserPanel-BmVbqcFg.js~~ _(removed)_ | 7.91 kB | — | 🟢 -7.91 kB | 🟢 -2.06 kB | 🟢 -1.79 kB | | **assets/UserPanel-ByH3gPJH.js** _(new)_ | — | 7.91 kB | 🔴 +7.91 kB | 🔴 +2.06 kB | 🔴 +1.79 kB | | assets/settings-B-df0dZe.js | 20.7 kB | 20.7 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-CI6OKvJn.js | 22.9 kB | 22.9 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-CXGVj_nD.js | 24.5 kB | 24.5 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-DfQ6dSJj.js | 31.6 kB | 31.6 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-DJ2QgDzm.js | 25.2 kB | 25.2 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-DRNLPMG6.js | 23.7 kB | 23.7 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-DVVycxDc.js | 19.9 kB | 19.9 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-G6Dybj1b.js | 24.1 kB | 24.1 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/settings-M6_GZccG.js | 26 kB | 26 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B |Status: 6 added / 6 removed UI Components — 12.3 kB (baseline 12.3 kB) • ⚪ 0 B_Reusable component library chunks_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | ----------------------------------------------------------------- | ------- | ------- | ----------------------- | ----------------------- | ----------------------- | | ~~assets/ComfyQueueButton-B4nTvK1b.js~~ _(removed)_ | 11.1 kB | — | 🟢 -11.1 kB | 🟢 -2.76 kB | 🟢 -2.43 kB | | **assets/ComfyQueueButton-CJC171BR.js** _(new)_ | — | 11.1 kB | 🔴 +11.1 kB | 🔴 +2.76 kB | 🔴 +2.43 kB | | assets/UserAvatar.vue_vue_type_script_setup_true_lang-C9bSkTC5.js | 1.12 kB | 1.12 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B |Status: 1 added / 1 removed Data & Services — 10 kB (baseline 10 kB) • ⚪ 0 B_Stores, services, APIs, and repositories_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | ---------------------------------------------------- | ------- | ------- | ----------------------- | ----------------------- | ----------------------- | | **assets/keybindingService-C1H3Q_Q2.js** _(new)_ | — | 7.21 kB | 🔴 +7.21 kB | 🔴 +1.75 kB | 🔴 +1.5 kB | | ~~assets/keybindingService-DZvr_isu.js~~ _(removed)_ | 7.21 kB | — | 🟢 -7.21 kB | 🟢 -1.75 kB | 🟢 -1.51 kB | | assets/serverConfigStore-BE22gWlb.js | 2.79 kB | 2.79 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B |Status: 1 added / 1 removed Utilities & Hooks — 1.07 kB (baseline 1.07 kB) • ⚪ 0 B_Helpers, composables, and utility bundles_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | --------------------------- | ------- | ------- | ------------------ | ------------------ | ------------------ | | assets/mathUtil-CTARWQ-l.js | 1.07 kB | 1.07 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B |Vendor & Third-Party — 5.36 MB (baseline 5.36 MB) • ⚪ 0 B_External libraries and shared vendor chunks_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | --------------------------------------- | ------- | ------- | ------------------ | ------------------ | ------------------ | | assets/vendor-other-DUFQbqPR.js | 3.22 MB | 3.22 MB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/vendor-primevue-PESgPnbc.js | 517 B | 517 B | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/vendor-tiptap-DrFxugC4.js | 232 kB | 232 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/vendor-visualization-BEfdbjRw.js | 1.82 MB | 1.82 MB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/vendor-vue-DJFUVoPA.js | 92.4 kB | 92.4 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B |Other — 2.58 MB (baseline 2.58 MB) • ⚪ 0 B_Bundles that do not match a named category_ | File | Before | After | Δ Raw | Δ Gzip | Δ Brotli | | --------------------------- | ------- | ------- | ------------------ | ------------------ | ------------------ | | assets/commands-B2KZRBmX.js | 15.1 kB | 15.1 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-Bw-ckyga.js | 13.9 kB | 13.9 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-C_NmM85I.js | 13.8 kB | 13.8 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-CuozCW4W.js | 14 kB | 14 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-DGfVUJCR.js | 16.2 kB | 16.2 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-dOJNDogK.js | 14.5 kB | 14.5 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-DwiE551e.js | 14.7 kB | 14.7 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-Fw7mvqSy.js | 13.1 kB | 13.1 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/commands-FXnO1W4Q.js | 13.2 kB | 13.2 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-Bgu6_Hvd.js | 59.5 kB | 59.5 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-Bv0L0qvp.js | 93 kB | 93 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-C3Doz3n_.js | 67.6 kB | 67.6 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-C7eBl607.js | 70.7 kB | 70.7 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-CHiV9ds2.js | 76.4 kB | 76.4 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-CIc79Nts.js | 68.5 kB | 68.5 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-DK5LmuBm.js | 58.8 kB | 58.8 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-J1nit7cj.js | 66.3 kB | 66.3 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/main-W97XgvAQ.js | 80.4 kB | 80.4 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-BePSqkA4.js | 195 kB | 195 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-BfT7dJcF.js | 204 kB | 204 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-BiAtoiXc.js | 194 kB | 194 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-CDfbduPY.js | 219 kB | 219 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-CDurg_KW.js | 197 kB | 197 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-CE-vG3RG.js | 182 kB | 182 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-DAwVV156.js | 200 kB | 200 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-DexhCMEi.js | 233 kB | 233 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | | assets/nodeDefs-kTrYLFPK.js | 184 kB | 184 kB | ⚪ 0 B | ⚪ 0 B | ⚪ 0 B | |
faf4c2d to
cfe0676
Compare
| v-for="handle in cornerResizeHandles" | ||
| :key="handle.id" | ||
| :class="cn(baseResizeHandleClasses, handle.classes)" | ||
| @pointerdown.stop="handleResizePointerDown(handle.direction)($event)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[quality] medium Priority
Issue: Resize handles missing accessibility attributes
Context: Resize handles lack aria-label or role attributes for screen reader users
Suggestion: Add aria-label describing the resize direction (e.g., "Resize from bottom-right corner")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added role="button" and localized aria-label in b54e919
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hate that they still haven't added a role for handles like this :-(
| currentPointer: Point, | ||
| scale: number | ||
| ): Point { | ||
| const safeScale = scale === 0 ? 1 : scale |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[quality] low Priority
Issue: Redundant division by zero protection
Context: toCanvasDelta already protects against scale=0 with safeScale variable
Suggestion: This protection is good, but consider extracting as a utility function since scale division occurs in multiple places
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipping - only used once, extraction wouldn't add value
| delta: Point, | ||
| handle: ResizeHandleDirection | ||
| ): Size { | ||
| const horizontalFactor = handle.horizontal === 'right' ? 1 : -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[architecture] medium Priority
Issue: Complex resize math could benefit from more descriptive variable names
Context: Variables like horizontalFactor and verticalFactor are clear but could be more semantic
Suggestion: Consider renaming to directionMultiplierX/Y or leftRightFactor/topBottomFactor for better readability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to horizontalMultiplier/verticalMultiplier in fc4a43d
|
|
||
| expect(delta).toEqual({ x: 50, y: 30 }) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[quality] medium Priority
Issue: Test coverage missing some edge cases
Context: Tests cover basic resize scenarios but miss zero scale, negative deltas, and extreme minSize values
Suggestion: Add test cases for edge conditions like zero/negative scale, very large deltas, and boundary minSize values
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 6 edge case tests covering zero scale, negative/extreme deltas, and boundary conditions in 27222a0
| }) | ||
| const baseResizeHandleClasses = | ||
| 'absolute h-3 w-3 opacity-0 pointer-events-auto focus-visible:outline focus-visible:outline-2 focus-visible:outline-white/40' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[performance] low Priority
Issue: Resize handles always rendered but invisible by default
Context: All 4 handles are rendered in DOM with opacity-0, which creates unnecessary DOM nodes
Suggestion: Consider using v-show or CSS-only hover reveal to avoid DOM overhead when not needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed (handles remain invisible with cursor-only indication)
| const baseResizeHandleClasses = | ||
| 'absolute h-3 w-3 opacity-0 pointer-events-auto focus-visible:outline focus-visible:outline-2 focus-visible:outline-white/40' | ||
| const POSITION_EPSILON = 0.01 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[quality] low Priority
Issue: Magic number POSITION_EPSILON without explanation
Context: Value 0.01 is used to check position changes but lacks documentation about why this threshold was chosen
Suggestion: Add comment explaining the epsilon value rationale or extract to a named constant with documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skipping - unclear rationale for the threshold
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comprehensive PR Review
This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another.
Review Summary
PR: allow Vue nodes to be resized from all 4 corners (#6187)
Impact: 345 additions, 75 deletions across 5 files
Issue Distribution
- Critical: 0
- High: 0
- Medium: 3
- Low: 3
Category Breakdown
- Architecture: 1 issues
- Security: 0 issues
- Performance: 1 issues
- Code Quality: 4 issues
Key Findings
Architecture & Design
The PR introduces a well-structured four-corner resize system that follows good separation of concerns. The math utilities in resizeMath.ts are pure functions with clear responsibilities, and the composable pattern in useNodeResize.ts integrates well with Vue's reactivity system. The resize handles use a declarative configuration approach which is maintainable and extensible.
Security Considerations
No security issues identified. The pointer event handling follows secure patterns with proper event capture and cleanup.
Performance Impact
The implementation is generally efficient with appropriate use of pointer capture and coordinate transformations. Minor concerns include always-rendered DOM elements for resize handles (though with opacity: 0) and potential memory leaks if resize operations are interrupted during component unmounting.
Integration Points
The resize system integrates well with existing ComfyUI patterns:
- Uses established layout stores and transform states
- Follows Vue 3 Composition API conventions
- Maintains compatibility with existing node snapping and shift-key behaviors
- Respects pinned node states to prevent accidental resizing
Positive Observations
- Excellent Test Coverage: The resizeMath.test.ts provides comprehensive unit tests covering the core mathematical operations
- Clean Pure Functions: The resize math utilities are well-separated and testable
- Proper TypeScript Usage: Strong typing throughout with clear interfaces
- Accessibility Consideration: Includes focus-visible styles for keyboard navigation
- Integration with Existing Systems: Properly integrates with snap-to-grid, shift-key sync, and pointer capture
- Error Handling: Includes safety checks for pinned nodes and scale division by zero
References
- Repository Architecture Guide: https://github.com/Comfy-Org/comfy-claude-prompt-library/blob/master/project-summaries-for-agents/ComfyUI_frontend/REPOSITORY_GUIDE.md
- Vue 3 Composition API: https://vuejs.org/guide/reusability/composables.html
- ComfyUI Frontend Guidelines: https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/CLAUDE.md
Next Steps
- Address accessibility improvements for resize handles (medium priority)
- Consider test coverage enhancements for edge cases
- Review variable naming for better code readability
- Evaluate DOM optimization opportunities for handle rendering
- Add documentation for magic number constants
This is a comprehensive automated review. The implementation demonstrates solid engineering practices and should provide a great user experience for node resizing from all four corners.
src/renderer/extensions/vueNodes/interactions/resize/resizeMath.ts
Outdated
Show resolved
Hide resolved
…ddresses review feedback Co-authored-by: DrJKL <[email protected]>
…ses review feedback Co-authored-by: DrJKL <[email protected]>
…sses review feedback Co-authored-by: DrJKL <[email protected]>
…eview feedback Co-authored-by: DrJKL <[email protected]>
…resses review feedback Co-authored-by: DrJKL <[email protected]>
…ses review feedback
Handles should remain opacity-0 with only the cursor indicating resize capability. Removed group-hover visibility classes.
🔧 Auto-fixes AppliedThis PR has been automatically updated to fix linting and formatting issues.
Changes made:
|
|
Oops, didn't realize the tests were failing, got bamboozled by lint and format green checkmark. |
Trigger CI to regenerate Playwright snapshots affected by the four-corner Vue node resize feature merged in #6187. This empty commit will trigger the automatic snapshot update workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6238-chore-update-Playwright-snapshots-2956d73d36508159b1f7e6e0e4d16d9f) by [Unito](https://www.unito.io)
Add missing English translation keys that were added in recent releases (PR #6256, #6112, #6187) but not backported to rh-test: - Media asset management (delete, selection, job ID toast) - Asset browser aria labels - Sidebar labels (console, menu, assets, imported, generated) - File management strings (no files found messages) - Resize handle tooltips - Basic UI strings (edit/delete image, chart, file, etc.) These will be translated in the next commit.
Summary
Enables Vue nodes to resize from all four corners and consolidated the interaction pipeline.
Changes
LGraphNode, wired them through the refactoreduseNodeResizecomposable, and centralized the math/preset helpers underinteractions/resize/with cleaner pure functions and lint-compliant markup.Review Focus
Corner-to-corner resizing accuracy (position + size), pinned-node guard preventing resize start, and snap-to-grid behavior at varied zoom levels.
┆Issue is synchronized with this Notion page by Unito