Repository navigation
fix(visual-builder): repaint the hover cursor when a peer lock changes (DRFT-921) - #661
Conversation
…s (DRFT-921) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||
hitesh-shetty-cstk
left a comment
There was a problem hiding this comment.
Automated review
What this changes: The custom hover cursor is repainted when a peer's field lock is acquired or released while the pointer sits on that field. Two changes do it: the cursor's repaint cache now keys on the disabled state as well as the field type, and the lock-store subscription repaints the cursor alongside the outline it already repainted.
Business impact: The canvas hover affordance in Visual Builder. No critical journey is blocked either way, since a wrong cursor colour misleads rather than stops an editor, and the change narrows that gap rather than widening it. The regression surface is the new repaint path firing on every lock update while hovering.
Security: Nothing beyond what the scanners cover. The innerHTML write in generateCustomCursor is unchanged and still builds from the hardcoded icon map, and the new data-disabled attribute is a stringified boolean.
Flow
flowchart TD
A[Peer lock changes] --> B[fieldLockStore notifies listeners]
B --> C{Hovered element still outlined?}
C -- no --> D[return]
C -- yes --> E[addOutline repaints outline]
E --> F{collab.enable}
F -- yes --> G[skip, collab owns the cursor]:::changed
F -- no --> H[generateCursor re-reads peer lock]:::changed
H --> I{icon and disabled both unchanged}
I -- yes --> J[return, no repaint]
I -- no --> K[repaint, store data-icon and data-disabled]:::changed
classDef changed fill:#fff3cd,stroke:#d39e00
Findings: 0 blocker, 1 should fix, 3 nit. All four are inline.
The mechanism holds up on the paths I traced. fieldDisabled is defaulted to false in the destructure, so the new cache key compares two booleans and the data-disabled attribute is always "true" or "false", with no undefined leaking in to defeat the early return. generateCursor returns early on a null cursor and re-reads getPeerLockForField after its awaits, so overlapping repaints converge on the current lock state rather than racing to a stale one. The collab guard matches the hover path: feedback mode paints its own cursor and non-feedback collab hides the cursor, so skipping both is right.
The tests are additive. No assertion was weakened, no case skipped, no snapshot regenerated. The two new listener tests fail if the subscription does not fire, rather than passing on a call left over from the initial hover, because expect.objectContaining({ fieldDisabled: ... }) does not match the resetCustomCursor and loading-cursor calls that omit the property.
Reviewer candidates: @ZuhairAhmed-cs authored 8 of the sampled commits across these files, 7 of them on mouseHover.ts. @hitesh-shetty-cstk authored 10, mostly on mouseHover.ts and its test file. @csAyushDubey is close behind with 7 on mouseHover.ts and is worth pulling in if either of the other two is unavailable.
Not covered: No local run, no build, no test execution. The lock-delta frequency the parent window pushes, which decides how much the new per-notification repaint actually costs, is outside this repository and I could not measure it. The post-message client's timeout behaviour could not be confirmed because node_modules is not installed in this checkout, which is why the first finding is written as conditional. The live two-user evidence in the description is still a video placeholder.
Automated review by Claude Code. A human review is still required.
Generated by Claude Code
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ticket: DRFT-921
Summary
User A locks a field while user B hovers it. When A releases the lock, B's outline unlocks, but B's cursor stays grey (disabled).
There are two causes:
generateCustomCursor({ fieldType, fieldDisabled }) - if data-icon === fieldType + if data-icon === fieldType and data-disabled === fieldDisabled return // nothing to repaint repaint cursor set data-icon + set data-disabledEvidence
generateCustomCursor > should repaint when only the disabled state changesfails: the cursor still hasvisual-builder__cursor-disabledafter the call withfieldDisabled: false.After: the test passes.
mouseHover — lock change while hovering(2 tests) passes: the cursor turns grey when a peer takes the lock and turns enabled when the peer releases it.drft-921-cursor-unlock.mp4
The label, outline and cursor are grey while the field is locked, and all three turn purple at the same moment when the lock is released.
Merge Danger
Door: two-way. Two small client-side changes, no data or API change. A revert removes them.
Blast Radius: the custom cursor in Visual Builder. The cursor is now repainted on every field-lock update while hovering, and once more when only the disabled state changes. Collab (feedback) mode is skipped because it uses its own cursor.
🤖 Generated with Claude Code