Skip to content

fix(desktop): prevent context menu and popover flash-close during streaming generation#3608

Merged
esengine merged 1 commit into
esengine:main-v2from
ttmouse:pr/menu-scroll-fix
Jun 9, 2026
Merged

fix(desktop): prevent context menu and popover flash-close during streaming generation#3608
esengine merged 1 commit into
esengine:main-v2from
ttmouse:pr/menu-scroll-fix

Conversation

@ttmouse

@ttmouse ttmouse commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Problem

During AI streaming generation, right-click context menus and popover dropdowns (branch selector, recent files menu, etc.) flash open and immediately close, making them unreadable and unclickable.

Root Cause

Both AnchoredPopover and ContextMenu listen for scroll events on window to close themselves. During streaming, the transcript area auto-scrolls on every incoming token (Transcript.tsx:218-226), which fires window scroll events that instantly close any open menu.

Fix

  1. AnchoredPopover.tsx — Remove the closeOnScroll listener. The popover still closes on Escape key, window resize, and backdrop click. Also fixed useLayoutEffect missing dependency array [open, align, offset, placement] to reduce unnecessary layout recalculations during streaming re-renders.

  2. ContextMenu.tsx — Remove the closeOnScroll listener. The menu still closes on Escape key, window resize, and click-outside (via pointerdown).

Impact

  • All right-click menus (TabBar, ProjectTree, HistoryPanel, WorkspacePanel tree rows)
  • All AnchoredPopover instances (branch selector, recent files menu)
  • / slash command menu and @ file menu are not affected (they never had global scroll listeners)

Closes #TBD

…eaming generation

Root cause: AnchoredPopover and ContextMenu both listen for scroll events
on window to close themselves. During AI streaming generation, the transcript
area auto-scrolls on every incoming token (Transcript.tsx:218-226), which
triggers window scroll events and instantly closes any open menu/popover
before the user can interact with it.

Fix: remove the close-on-scroll listener from both components. Menus still
close on Escape key, window resize, click-outside (ContextMenu via pointerdown,
AnchoredPopover via backdrop), which are the standard and predictable ways
to dismiss a menu.

Also fixes AnchoredPopover's useLayoutEffect missing dependency array
[open, align, offset, placement] to reduce unnecessary position recalculations.
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development desktop Wails desktop app (desktop/**) and removed v2 Go rewrite (1.x) — main-v2 branch, active development labels Jun 8, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0d8cb764d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const next = calculatePosition(anchor, menu, align, offset, placement);
setPosition((current) => (samePosition(current, next) ? current : next));
});
}, [open, align, offset, placement]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recompute popover position when content changes

Because this layout effect now only reruns for open/placement props, an already-open popover is not measured again when its children change size. For example, ModelSwitcher opens with the empty state and then asynchronously calls setModels(...); with the default upward placement, the initial top is based on the short empty menu, so once the model rows arrive the fixed-position menu grows downward into the trigger/status area (or off-screen) instead of remaining anchored above it. Consider keeping the measurement on content updates or using a ResizeObserver so dynamic popovers are repositioned after their dimensions change.

Useful? React with 👍 / 👎.

Comment on lines 82 to 83
window.addEventListener("resize", close);
window.addEventListener("scroll", closeOnScroll, true);
window.addEventListener("keydown", closeOnEscape);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Close context menus when their scrollable list moves

With the scroll listener removed, a menu opened in a scrollable surface such as the history list (styles.css lines 6138-6140) stays fixed on screen when the user scrolls that list with a wheel/trackpad, while the row underneath moves away. The menu actions still target the session captured when it was opened (HistoryPanel.tsx lines 52-53 and 438), so after scrolling it is easy to invoke an action on a different item than the one now visually under the menu. Consider closing or repositioning on user-initiated non-menu scrolls while excluding the streaming autoscroll case.

Useful? React with 👍 / 👎.

@esengine esengine left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff: the fix is correct, focused, and CI is green. Approving for merge.

@esengine esengine merged commit b30215a into esengine:main-v2 Jun 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop Wails desktop app (desktop/**)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants