Skip to content

fix(web): make terminal context-menu actions reliable - #8459

Closed
Adamulek123 wants to merge 4 commits into
pingdotgg:mainfrom
Adamulek123:fix/terminal-menu-actions
Closed

Adamulek123 wants to merge 4 commits into
pingdotgg:mainfrom
Adamulek123:fix/terminal-menu-actions

Conversation

@Adamulek123

@Adamulek123 Adamulek123 commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

What Changed

Unified selection-popup and right-click actions for Copy, Paste, and Add to chat. Terminal-owned menu requests now reject safely, ignore stale actions, cancel on teardown, and restore focus only for Copy and Paste.

Why

The old paths could deliver duplicate or stale actions, leave a styled menu open after terminal teardown, and report an unhandled rejection when the automatic popup failed to open.

Stack dependency

This branch is stacked on #8458. Review the focused two-file diff or the final two commits, e208071f and 6f9f05ab. GitHub must temporarily compare this fork branch against main because fork branches cannot be selected as an upstream PR base. Rebase onto main and mark ready after #8458 merges.

UI Changes

This changes terminal-menu interaction and focus behavior, but not its final visual design. The PR remains a draft until its dependency lands and integrated browser/Electron evidence can be recorded.

Validation

Checklist

Implemented with GPT-5.6 Codex in T3 Code.

Note

Fix terminal context-menu actions with accelerators and abort lifecycle

  • Adds optional accelerator to ContextMenuItem and extends LocalApi.contextMenu.show with layout, presentation, restoreFocus, and signal options in ipc.ts.
  • Propagates accelerator through Electron menu normalization and template building in ElectronMenu.ts.
  • Adds keyboard accelerator matching, compact layout, scroll-to-close, abort support, and conditional focus restoration to the web fallback host in contextMenuFallback.ts.
  • Reworks TerminalViewport in ThreadTerminalDrawer.tsx to use an AbortController-owned menu lifecycle, platform-specific accelerators (mac vs non-mac), and focus restore only for copy/paste.
  • localApi now allows a styled web menu on desktop and always dismisses the fallback on close() in localApi.ts.
  • Behavioral Change: right-click terminal menus now use a styled compact host with restoreFocus: 'on-dismiss'; stale or aborted requests skip side effects and focus restoration. Review runTerminalMenuRequest and showContextMenuFallback to confirm abort and dismissal paths.
📊 Macroscope summarized 6f9f05a. 5 files reviewed, 2 issues evaluated, 0 issues filtered, 2 comments posted

🗂️ Filtered Issues

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db6111fc-ed9c-4f82-a6de-f4c42d1eaabe

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 27, 2026
if (event.isComposing) return null;

for (const item of items) {
if (item.disabled) continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium src/contextMenuFallback.ts:207

A header: true item with an accelerator still returns its id from contextMenuAcceleratorAction, so pressing the shortcut executes an action that is non-interactive in the fallback and absent from the Electron menu. Exclude header items from accelerator matching.

Suggested change
if (item.disabled) continue;
if (item.disabled || item.header) continue;
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/contextMenuFallback.ts around line 207:

A `header: true` item with an `accelerator` still returns its `id` from `contextMenuAcceleratorAction`, so pressing the shortcut executes an action that is non-interactive in the fallback and absent from the Electron menu. Exclude header items from accelerator matching.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium

clampMenuPosition(menu, preferredLeft, preferredTop);

Nested compact submenus are moved back to the right after openSubmenu places them on the left, so near the viewport edge they overlap their parent. The requestAnimationFrame callback in openMenu reclamps using the original right-side preferredLeft; clamp the menu's current position instead.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/contextMenuFallback.ts around line 537:

Nested compact submenus are moved back to the right after `openSubmenu` places them on the left, so near the viewport edge they overlap their parent. The `requestAnimationFrame` callback in `openMenu` reclamps using the original right-side `preferredLeft`; clamp the menu's current position instead.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the changed web UI surfaces (contextMenuFallback.ts, localApi.ts, ThreadTerminalDrawer.tsx) against the shared menu primitives. The imperative fallback's default rows now correctly let the Tailwind classes own height/typography (the removed inline min-height/font-size were shadowing min-h-8/text-base), and the accelerator kbd mirrors MenuShortcut exactly, so styling ownership there looks right. Two items below: one type-scale divergence from the shared menu contract, and one dropped Electron/browser constraint comment.

Posted via Macroscope — UI Consistency

Comment on lines +392 to +394
const rowBase = usesCompactLayout
? "flex min-h-8 w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-left text-sm outline-none transition-colors sm:min-h-7 sm:text-xs"
: "flex min-h-8 w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-left text-base outline-none transition-colors sm:min-h-7 sm:text-sm";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The compact layout changes the menu row type scale (text-sm / sm:text-xs), not just the width. Every other context menu in the app — the React MenuItem primitive and this file's default layout — renders rows at text-base sm:text-sm, so the terminal menu becomes the only 12px context menu on desktop, and at sm its label ends up the same size as the text-xs accelerator kbd (which is styled to be the secondary element, matching MenuShortcut), collapsing the label/shortcut hierarchy.

Since the compact layout already widens the popup to 14rem, it does not need smaller text; consider letting compact own only min-width and keeping the shared row scale.

Suggested change
const rowBase = usesCompactLayout
? "flex min-h-8 w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-left text-sm outline-none transition-colors sm:min-h-7 sm:text-xs"
: "flex min-h-8 w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-left text-base outline-none transition-colors sm:min-h-7 sm:text-sm";
const rowBase =
"flex min-h-8 w-full cursor-default select-none items-center gap-2 rounded-sm px-2 py-1 text-left text-base outline-none transition-colors sm:min-h-7 sm:text-sm";

Posted via Macroscope — UI Consistency

Comment on lines +292 to +295
export function terminalContextMenuItems(
availability: { readonly canAddToChat: boolean; readonly canCopy: boolean },
platform = typeof navigator === "undefined" ? "" : navigator.platform,
): ContextMenuItem<TerminalContextMenuAction>[] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The JSDoc that explained the Electron/browser constraint behind this menu was dropped with the rename: it documented why Paste is always offered (the browser and Electron's default editing menu can only paste into an editable element, so a canvas terminal never gets a usable entry from them). That rationale is still the reason paste is unconditionally present here, and it now also justifies presentation: "styled" at the call sites. Consider restoring it.

Suggested change
export function terminalContextMenuItems(
availability: { readonly canAddToChat: boolean; readonly canCopy: boolean },
platform = typeof navigator === "undefined" ? "" : navigator.platform,
): ContextMenuItem<TerminalContextMenuAction>[] {
/**
* Terminal canvas menu: the selection actions (disabled until a selection
* exists) plus Paste. Paste is always offered because the browser (and
* Electron's default editing menu) can only paste into an editable element,
* so a canvas terminal never gets a usable entry from them.
*/
export function terminalContextMenuItems(
availability: { readonly canAddToChat: boolean; readonly canCopy: boolean },
platform = typeof navigator === "undefined" ? "" : navigator.platform,
): ContextMenuItem<TerminalContextMenuAction>[] {

Posted via Macroscope — UI Consistency

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant