Skip to content

refactor(ui): draw the last two overflow menus from the shared menu parts - #206

Merged
kipavy merged 1 commit into
devfrom
refactor/snippets-panel-menus
Sep 1, 2026
Merged

refactor(ui): draw the last two overflow menus from the shared menu parts#206
kipavy merged 1 commit into
devfrom
refactor/snippets-panel-menus

Conversation

@kipavy

@kipavy kipavy commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #203 and #205, and the end of the sweep. These two were the menus
that did not merely position themselves by hand — they painted their own rows
too, on the wrong tokens: a --t-bg-modal surface with a --t-border divider
and --t-bg-elevated hover, where every other menu uses surface-float with
--t-bg-card-hover, and py-1.5 rows with 12px icons against the shared 16px.

terminal/SnippetsPanel renders MenuItemList inside a PickerSurface. That
turns the two-step "move to folder / back" flow into a real submenu and deletes
the movingToFolder state along with the entire second menu — the file loses 118
lines and gains 43.

mobile/MobileTerminalTopBar renders DropdownMenuItem rows, and picks up an
outside-tap dismiss it never had: until now only tapping an item closed it.

The one non-obvious bit

Hosting MenuItemList inside PickerSurface needed the submenu to be legible to
the surface that opened it. A submenu portals to the body, so it is not a DOM
descendant of that surface, and dismiss-on-outside-mousedown would unmount the row
before its click fired — the exact bug ContextMenu's backdrop comment already
warns about. So the portal is marked data-menu-portal, PickerSurface skips
mousedowns inside one, and the portal moves to z-10000 because a submenu that
flips left overlaps its parent. In the narrow right-hand panel that flip is the
normal case, not the edge case.

ContextMenu and PanelActionsMenu keep their own backdrop-based surfaces for
that same reason — swapping them for PickerSurface would reintroduce the bug.

Verification

tsc --noEmit clean; 205 tests across terminal, shared, mobile and
connections pass.

Driven in the headless build against a real snippet and a real folder:

  • the row menu opens as a surface-float card with 16px icons, a divider and a
    red Delete;
  • hovering "Move to folder" opens the submenu, which flips left over the parent
    and draws above it;
  • clicking Ops in that submenu moves the snippet and closes both menus — the
    dismissal hazard above, confirmed handled rather than assumed;
  • Duplicate and Delete apply; an outside mousedown dismisses.

MobileTerminalTopBar is not live-verified: the mobile shell only mounts when
the backend reports android, so it is unreachable from the desktop build. It is
typechecked and covered by the suite; a run on the emulator would need
iterating-on-voltius-android-emulator.

…arts

The snippet row's menu and the mobile terminal bar's panels menu were the two
that did not merely position themselves by hand — they painted their own rows
too, on the wrong tokens: a `--t-bg-modal` surface with a `--t-border` divider
and `--t-bg-elevated` hover, where every other menu in the app uses
`surface-float` with `--t-bg-card-hover`. Their rows were `py-1.5` with 12px
icons against the shared 16px.

Both now open a PickerSurface. The snippet row renders MenuItemList, which turns
its two-step "move to folder / back" flow into a submenu and deletes the
`movingToFolder` state along with the second menu. The mobile bar renders
DropdownMenuItem, and gains an outside-tap dismiss it never had — until now only
tapping an item closed it.

Hosting MenuItemList inside PickerSurface needed the submenu to be legible to the
surface that opened it: a submenu portals to the body, so a surface that dismisses
on outside-mousedown would unmount the row before its click fired. The portal is
marked `data-menu-portal` and PickerSurface skips those, and the portal moves to
`z-10000` because a submenu that flips left overlaps its parent — which is the
common case in the narrow right-hand panel.

ContextMenu and PanelActionsMenu keep their own backdrop-based surfaces: that
backdrop is what lets their submenus receive a click at all, and swapping it for
PickerSurface would reintroduce the bug the comment there already warns about.
@kipavy
kipavy merged commit ed700b8 into dev Sep 1, 2026
4 checks passed
@kipavy
kipavy deleted the refactor/snippets-panel-menus branch September 1, 2026 22:35
kipavy added a commit that referenced this pull request Sep 1, 2026
#206 replaced the terminal bar's hand-written menu buttons with DropdownMenuItem
and dropped `data-mobile-panel={key}` with them. Nothing in the repo reads that
attribute, but nothing reads `data-mobile-terminal-menu` or
`data-mobile-session-chip` either — the whole family arrived together in 1817d08
as the selector surface for driving the Android build over CDP, and this is the
one member that went missing.

DropdownMenuItem takes a `dataAttrs` map so a shared row can carry the hook the
hand-written button carried. It matters more here than elsewhere: the mobile
shell only mounts when the backend reports `android`, so this menu cannot be
reached from the desktop build and those selectors are how it gets exercised at
all.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant