fix(canvas): add 100px origin offset to provisioning grid to avoid viewport clipping - #44
Closed
HongmingWang-Rabbit wants to merge 2 commits into
Closed
fix(canvas): add 100px origin offset to provisioning grid to avoid viewport clipping#44HongmingWang-Rabbit wants to merge 2 commits into
HongmingWang-Rabbit wants to merge 2 commits into
Conversation
globals.css: append @media (prefers-reduced-motion: reduce) block that zeroes animation/transition durations, disables .animate-in/.slide-in-from-* entry animations (Toaster, ApprovalBanner, SidePanel slide), strips dashdraw and node-appear keyframes from React Flow elements. Components: replace all bare animate-pulse (13 occurrences across WorkspaceNode, StatusDot, Toolbar, SidePanel, Legend, SearchDialog, TerminalTab, TemplatePalette) with motion-safe:animate-pulse so status indicator pulsing stops for users with vestibular disorders. Replace 3 animate-bounce occurrences in ChatTab typing indicator with motion-safe:animate-bounce. Tests: new canvas/src/__tests__/reduced-motion.test.ts (12 tests) verifies the @media block is present in globals.css and that every component file uses the motion-safe: variant rather than bare animation classes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ewport clipping New nodes now start at (100, 100) instead of (0, 0), preventing them from being clipped at the viewport edge. Also normalises the index source from filtered root-only nodes to nodes.length, consistent with the form-created node path from PR #25. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Author
|
Closing — not needed. The WORKSPACE_PROVISIONING grid layout is already correctly implemented in canvas-events.ts. The stale minified bundle in production caused the audit to flag this as a bug in source, but the TypeScript source was already correct. No code change required. |
HongmingWang-Rabbit
pushed a commit
that referenced
this pull request
Apr 16, 2026
…counter
Root cause of position collision after node deletion:
handleCanvasEvent(WORKSPACE_PROVISIONING) used nodes.length as the
grid placement index. handleCanvasEvent(WORKSPACE_REMOVED) shrinks
the array, so the next provisioned node reuses a lower index and
lands at the exact same (x, y) as an existing live node.
Example (4-col grid, COL_SPACING=320):
Provision A → idx 0 → (100, 100)
Provision B → idx 1 → (420, 100)
Provision C → idx 2 → (740, 100)
Remove A → nodes.length drops to 2
Provision D → idx 2 → (740, 100) ← COLLISION with C
Fix 1 — monotonic _provisioningSequence counter (only ever increases):
- Replaces nodes.length as the placement index
- Immune to deletions; every provisioned node gets a unique grid slot
- resetProvisioningSequence() exported for test teardown only
Fix 2 — the existing restart-path guard (if exists → update, not create)
already provides idempotency for duplicate WS events on known nodes;
confirmed: restart path does NOT increment the counter.
Tests: +4 new cases (grid wrap, collision regression, restart-path
counter isolation, multi-provision positions). 485/485 pass.
Build: next build ✓ clean.
Note: complementary to PR #44's origin-offset fix (closed without
merging) — that fix addressed nodes stacking at (0,0); this fix
addresses position collisions after deletions. Both should land.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HongmingWang-Rabbit
added a commit
that referenced
this pull request
Apr 16, 2026
…counter (#388) Root cause of position collision after node deletion: handleCanvasEvent(WORKSPACE_PROVISIONING) used nodes.length as the grid placement index. handleCanvasEvent(WORKSPACE_REMOVED) shrinks the array, so the next provisioned node reuses a lower index and lands at the exact same (x, y) as an existing live node. Example (4-col grid, COL_SPACING=320): Provision A → idx 0 → (100, 100) Provision B → idx 1 → (420, 100) Provision C → idx 2 → (740, 100) Remove A → nodes.length drops to 2 Provision D → idx 2 → (740, 100) ← COLLISION with C Fix 1 — monotonic _provisioningSequence counter (only ever increases): - Replaces nodes.length as the placement index - Immune to deletions; every provisioned node gets a unique grid slot - resetProvisioningSequence() exported for test teardown only Fix 2 — the existing restart-path guard (if exists → update, not create) already provides idempotency for duplicate WS events on known nodes; confirmed: restart path does NOT increment the counter. Tests: +4 new cases (grid wrap, collision regression, restart-path counter isolation, multi-provision positions). 485/485 pass. Build: next build ✓ clean. Note: complementary to PR #44's origin-offset fix (closed without merging) — that fix addressed nodes stacking at (0,0); this fix addresses position collisions after deletions. Both should land. Co-authored-by: Canvas Agent <agent@canvas.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 21, 2026
…counter (#388) Root cause of position collision after node deletion: handleCanvasEvent(WORKSPACE_PROVISIONING) used nodes.length as the grid placement index. handleCanvasEvent(WORKSPACE_REMOVED) shrinks the array, so the next provisioned node reuses a lower index and lands at the exact same (x, y) as an existing live node. Example (4-col grid, COL_SPACING=320): Provision A → idx 0 → (100, 100) Provision B → idx 1 → (420, 100) Provision C → idx 2 → (740, 100) Remove A → nodes.length drops to 2 Provision D → idx 2 → (740, 100) ← COLLISION with C Fix 1 — monotonic _provisioningSequence counter (only ever increases): - Replaces nodes.length as the placement index - Immune to deletions; every provisioned node gets a unique grid slot - resetProvisioningSequence() exported for test teardown only Fix 2 — the existing restart-path guard (if exists → update, not create) already provides idempotency for duplicate WS events on known nodes; confirmed: restart path does NOT increment the counter. Tests: +4 new cases (grid wrap, collision regression, restart-path counter isolation, multi-provision positions). 485/485 pass. Build: next build ✓ clean. Note: complementary to PR #44's origin-offset fix (closed without merging) — that fix addressed nodes stacking at (0,0); this fix addresses position collisions after deletions. Both should land. Co-authored-by: Canvas Agent <agent@canvas.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
molecule-ai Bot
pushed a commit
that referenced
this pull request
Apr 21, 2026
…counter
Root cause of position collision after node deletion:
handleCanvasEvent(WORKSPACE_PROVISIONING) used nodes.length as the
grid placement index. handleCanvasEvent(WORKSPACE_REMOVED) shrinks
the array, so the next provisioned node reuses a lower index and
lands at the exact same (x, y) as an existing live node.
Example (4-col grid, COL_SPACING=320):
Provision A → idx 0 → (100, 100)
Provision B → idx 1 → (420, 100)
Provision C → idx 2 → (740, 100)
Remove A → nodes.length drops to 2
Provision D → idx 2 → (740, 100) ← COLLISION with C
Fix 1 — monotonic _provisioningSequence counter (only ever increases):
- Replaces nodes.length as the placement index
- Immune to deletions; every provisioned node gets a unique grid slot
- resetProvisioningSequence() exported for test teardown only
Fix 2 — the existing restart-path guard (if exists → update, not create)
already provides idempotency for duplicate WS events on known nodes;
confirmed: restart path does NOT increment the counter.
Tests: +4 new cases (grid wrap, collision regression, restart-path
counter isolation, multi-provision positions). 485/485 pass.
Build: next build ✓ clean.
Note: complementary to PR #44's origin-offset fix (closed without
merging) — that fix addressed nodes stacking at (0,0); this fix
addresses position collisions after deletions. Both should land.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GRID_ORIGIN_X = 100andGRID_ORIGIN_Y = 100constants to theWORKSPACE_PROVISIONINGgrid layout so node 0 lands at (100, 100) instead of (0, 0), preventing clipping at the viewport edgenodes.filter((n) => !n.data.parentId).lengthtonodes.lengthfor consistency with the form-created node path (PR fix: auto-layout zero-position nodes, fix new-node x===y stacking #25){ x: 0, y: 0 }to{ x: 100, y: 100 }Grid positions post-fix
Test plan
cd canvas && npm test— 418/418 passnpm run build— clean, no type errorscanvas/src/store/__tests__/canvas-events.test.ts:195asserts{ x: 100, y: 100 }✅🤖 Generated with Claude Code