fix(chat): prevent @-mention menu focus loss and stabilize render identity#4218
fix(chat): prevent @-mention menu focus loss and stabilize render identity#4218waleedlatif1 merged 5 commits intostagingfrom
Conversation
… components - Capture currentTime on click and seek lightbox video to match using useLayoutEffect - Convert lightboxStartTime from useState to useRef (no independent render needed) - Apply same fix to ActionVideo in action-media.tsx - Remove dead AnimatedBlocks component (zero imports) - Fix language-dropdown to derive currentLang during render instead of mirroring into state via effect - Replace template literals with cn() in faq.tsx and video.tsx
…ntity Radix DropdownMenu's FocusScope was restoring focus from the search input to the content root whenever registered menu items mounted or unmounted inside the content, interrupting typing after a keystroke or two. - Keep the default tree always mounted under `hidden` instead of swapping subtrees when the filter activates. - Render filtered results as plain <button role="menuitem"> so they do not participate in Radix's menu Collection. - Add activeIndex state with ArrowUp/Down/Enter keyboard nav, mouse-hover sync, and scrollIntoView so the highlighted row stays visible and users can see what Enter will select. While tracing the cascade that compounded the bug: - Hoist `select` in useWorkflowMap / useWorkspacesQuery / useFolderMap to module scope so TanStack Query caches the select result across renders. - Guard setSelectedContexts([]) with a functional updater that bails out when already empty, preventing a fresh [] literal from invalidating consumers that key on reference identity. - Wrap WorkspaceHeader in React.memo so it bails out on parent renders once its (now-stable) props are unchanged. Made-with: Cursor
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Stabilizes render identity in several places to reduce unnecessary re-renders during typing: Docs UI tweaks: video lightboxes now open at the clicked video’s current playback time ( Reviewed by Cursor Bugbot for commit 06c8066. Configure here. |
Greptile SummaryThis PR fixes the Radix Confidence Score: 5/5Safe to merge — no P0 or P1 issues found across any changed file. All findings are P2 or lower. The architectural fix is sound (always-mounted hidden tree + plain buttons avoids Collection mutations), the React Query select hoisting is a textbook optimization, the functional-updater bail-out is correct, and the lightbox seek uses useLayoutEffect at the right time. No regressions introduced. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[User types in search input] --> B{filteredItems !== null?}
B -- No/empty query --> C[div NOT hidden\nRadix DropdownMenuItems visible\nParticipate in Collection]
B -- Has query --> D[div hidden=true\nRadix items hidden, still in DOM\nDO NOT cause Collection mutations]
D --> E[Plain buttons rendered\nrole=menuitem, no Collection registration\nNo FocusScope trigger]
E --> F[DOM focus stays on search input\nactiveIndex moves visual highlight]
F --> G{Keyboard nav}
G -- ArrowUp/Down --> H[setActiveIndex, scrollIntoView]
G -- Enter/Tab --> I[handleSelect → setOpen false]
I --> J[onCloseAutoFocus\nfocuses textarea]
C --> K{Keyboard nav}
K -- ArrowDown --> L[getVisibleMenuItems 0 .focus\nDOM focus moves into Radix tree]
Reviews (2): Last reviewed commit: "fix(chat): apply same setState bail-out ..." | Re-trigger Greptile |
Matches the invariant we already established for the message effect: calling setSelectedContexts([]) against an already-empty array emits a fresh [] reference (Object.is bails out are not reference-level), which cascades through consumers that key on selectedContexts identity. clearContexts is part of the hook's public API so callers can't know whether the list is empty — make it safe for them. Made-with: Cursor
|
Thanks @greptile-apps — looked into both and want to leave my reasoning on the record. 1. 2.
Happy to revisit if/when |
|
Both points are well-reasoned — thanks for the detailed writeup. On Tip: You can customize Greptile's behavior for this repo with |
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 06c8066. Configure here.
Summary
hiddenand rendering filtered results as plain<button role="menuitem">so they don't participate in Radix's CollectionactiveIndexstate with ArrowUp/Down/Enter keyboard nav, mouse-hover sync, andscrollIntoViewso the highlighted row stays visible (combobox pattern — DOM focus stays on search input, visual highlight moves)selectarrows inuseWorkflowMap,useWorkspacesQuery, anduseFolderMapto module scope so TanStack Query caches select results across renders instead of returning a new reference every timesetSelectedContexts([])with a functional updater that bails out when already empty — prevents a fresh[]literal from invalidating consumers keyed on reference identityWorkspaceHeaderinReact.memonow that its props are reference-stable during typingcurrentTimeon click and seek the lightbox video to match usinguseLayoutEffect, so GIFs resume from where you left off instead of restartingAnimatedBlockscomponent, fixlanguage-dropdownto derivecurrentLangduring render instead of mirroring into state via effect, replace template literals withcn()Type of Change
Testing
Tested manually
Checklist