Skip to content

feat(app): open session Extensions lists on hover - #58021

Merged
kitlangton merged 1 commit into
betafrom
extensions-hover-menu
Oct 22, 2026
Merged

kitlangton merged 1 commit into
betafrom
extensions-hover-menu

Conversation

@kitlangton

@kitlangton kitlangton commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

What it does

The session details popover's Extensions lists (MCP, Plugins, Skills, LSP) now open on hover, the way native submenus do, instead of needing a click. One panel beside the card moves to the row under the pointer and resizes to fit each list. A coloured dot shows each item's status, and hovering a red dot shows the error.

How it works

  • The first open happens on pointer-enter. Moving to another row switches lists at once.
  • A safe triangle runs from the pointer to the flyout's near edge, so a diagonal move into the open list doesn't switch rows on the way. A pointer that comes to rest over another row switches after 100 ms. The flyout's position is measured when a list opens and again on scroll or resize, so pointer moves don't read layout.
  • The flyout is a single panel. It moves with translateY and resizes with a clip-path, on a 0.25 s spring with no bounce. Its shadow is a drop-shadow so it follows the clip. Lists swap instantly while the box moves, so two lists never show at once. Only the first open and the last close fade. With reduced motion, the flyout just fades.
  • Keyboard: ArrowRight or Enter opens a list and focuses its first control. Up and down move between controls, and ArrowLeft or Escape returns to the row. Escape closes only the flyout. The rows are disclosure buttons (aria-expanded, aria-controls) for labelled groups.
  • The dot is the status. A failed MCP server keeps its switch, and its dot opens the error details. Status words stay in each switch's description for screen readers. The "Configured …" headings are gone, and their four keys are removed from every locale.
  • The popover no longer scrolls as a whole; only an inner box does, so the flyout isn't clipped. A pointer open focuses the popover itself, so the first row shows no focus ring. data-prevent-autofocus keeps Escape away from the session view, which used to send subagents to their parent.

Risk

This changes only the desktop popover; the narrow-screen drawers are unchanged. Hover timing and the safe triangle are the parts most likely to feel off, so watch the clip for the diagonal move.

Proof

Both clips use fictional servers and plugins. Each was recorded headless at 8× and 16× time dilation, then retimed to 1× and 0.25×. The pointer is drawn into the page.

1×: hovering down the rows, back to MCP, then diagonally into its list, ending on a failed server's dot.

extensions-hover-1x.mp4

0.25×:

extensions-hover-0.25x.mp4
  • bun run test:e2e:built -- session-summary mcp-workspace-toggle mobile-extension-drawers: 31 passed. This includes a new case for hover, the diagonal move and the keyboard.
  • bun typecheck in gui-extensions and bun run lint:changed: clean.
  • I pushed with --no-verify. The pre-push bun run check fails only on activity-chips.fixture.tsx, the beta typecheck break that fix(app): restore beta typecheck in activity chip fixture #57969 fixes.

One flyout beside the Extensions card glides between MCP, Plugins, Skills
and LSP and clips to each list. Lists swap at once while the box glides,
so two lists never show together. Rows switch at once; a safe triangle,
measured when a list opens and on scroll or resize, keeps a diagonal move
into the open list from switching rows. ArrowRight and Enter open into the
list, ArrowLeft and Escape close back to the row. The rows are disclosures
(aria-expanded, aria-controls) of labelled groups.

The dot is the status: a failed dot shows its error on hover and opens
the details, status words stay for assistive technology, and the
"Configured …" headings are gone.

@opencode-agent opencode-agent 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.

I couldn't find a bug in the hover flyout, and it works as described. On this branch, the three e2e specs gave 30/30 in a second full run. The first run had one failure that didn't come back: "submenus follow ltr", where the popover didn't reappear on reopen. That test then passed 6/6 when run alone, so it looks like an occasional failure under parallel load, but I didn't run it on the base to compare. typecheck and bun test in packages/gui-extensions are clean.

Public API: unchanged. This only touches the app UI and its gui-extensions i18n strings.

One concern, not blocking: in the MCP list, failed and connecting servers are now shown only by a dot colour (see inline comment).

Before

After

aria-disabled={pending()}
aria-busy={busy()}
description={server().status.status === "pending" ? label() : undefined}
description={label()}

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.

With the status words moved to sr-only, sighted users can only tell "failed" and "connecting" apart from "connected" by the dot colour. A failed server's switch is also shown as on (see the screenshot: playwright looks the same as figma apart from a red vs green dot), and its error is reached through a 6px dot. Could the failed state (and perhaps "Connecting…") keep a small visible cue, so the status isn't conveyed by colour alone?

@kitlangton
kitlangton merged commit a3883d6 into beta Oct 22, 2026
12 of 20 checks passed
@kitlangton
kitlangton deleted the extensions-hover-menu branch October 22, 2026 05:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant