From 5cd770503686622d0d18bbc810834c4486cfeca3 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Thu, 13 Aug 2026 20:39:26 -0400 Subject: [PATCH] fix(tui): correct tab context menu behavior (#42453) --- packages/tui/src/component/session-tabs.tsx | 133 ++++++++++---------- 1 file changed, 63 insertions(+), 70 deletions(-) diff --git a/packages/tui/src/component/session-tabs.tsx b/packages/tui/src/component/session-tabs.tsx index 70ac09d2bdb..68b54652109 100644 --- a/packages/tui/src/component/session-tabs.tsx +++ b/packages/tui/src/component/session-tabs.tsx @@ -11,7 +11,7 @@ import { onCleanup, untrack, } from "solid-js" -import { useTerminalDimensions } from "@opentui/solid" +import { Portal, useTerminalDimensions } from "@opentui/solid" import { useConfig } from "../config" import { useSessionTabs } from "../context/session-tabs" import { useData } from "../context/data" @@ -37,8 +37,6 @@ import { projectName } from "../util/project" import { marqueeCycleWidth, marqueeOverflows, marqueeText } from "../util/marquee" import { useDialog } from "../ui/dialog" import { DialogSessionRename } from "./dialog-session-rename" -import { Keymap } from "../context/keymap" -import { moveSelection } from "../ui/select-controller" // A long title fades out over its last cells instead of cutting hard. const FADE_WIDTH = 4 @@ -189,7 +187,6 @@ function TabContextMenu(props: { state: TabContextMenuState; tabs: SessionTabsCo const dimensions = useTerminalDimensions() const theme = useTheme("elevated") const dialog = useDialog() - const keymap = Keymap.use() const actions = createMemo(() => { const sessionID = props.state.sessionID return [ @@ -205,73 +202,69 @@ function TabContextMenu(props: { state: TabContextMenuState; tabs: SessionTabsCo : []), ] }) - const [selected, setSelected] = createSignal(0) + const [selected, setSelected] = createSignal() const top = () => Math.max(0, Math.min(props.state.y + 1, dimensions().height - actions().length)) const left = () => Math.max(0, Math.min(props.state.x, dimensions().width - CONTEXT_MENU_WIDTH)) const run = (index: number) => { + const action = actions()[index] props.onClose() - actions()[index]?.run() + action?.run() } - createEffect(() => { - const popMode = keymap.mode.push("modal") - onCleanup(popMode) - }) - Keymap.createLayer(() => ({ - mode: "modal", - commands: [ - { bind: "escape", title: "Close tab menu", group: "Tabs", run: props.onClose }, - { - bind: "up", - title: "Previous tab menu item", - group: "Tabs", - run: () => setSelected(moveSelection(selected(), { count: actions().length, delta: -1, policy: "wrap" })), - }, - { - bind: "down", - title: "Next tab menu item", - group: "Tabs", - run: () => setSelected(moveSelection(selected(), { count: actions().length, delta: 1, policy: "wrap" })), - }, - { bind: "return", title: "Select tab menu item", group: "Tabs", run: () => run(selected()) }, - ], - })) return ( - { - event.preventDefault() - event.stopPropagation() - }} - > - - {(action, index) => ( - setSelected(index())} - onMouseUp={(event) => { - event.preventDefault() - event.stopPropagation() - run(index()) - }} - > - - {action.title} - - - )} - - + + { + props.onClose() + event.preventDefault() + event.stopPropagation() + }} + > + { + if (event.button === RIGHT_MOUSE_BUTTON) props.onClose() + event.preventDefault() + event.stopPropagation() + }} + > + + {(action, index) => ( + setSelected(index())} + onMouseOut={() => setSelected(undefined)} + onMouseUp={(event) => { + event.preventDefault() + event.stopPropagation() + if (event.button === RIGHT_MOUSE_BUTTON) return + run(index()) + }} + > + + {action.title} + + + )} + + + + ) } @@ -520,8 +513,8 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat setDragging(undefined) if (!rail) return setContextMenu({ - x: event.x - rail.screenX, - y: event.y - rail.screenY, + x: event.x, + y: event.y, sessionID: tab.sessionID, title: tab.title, }) @@ -712,7 +705,7 @@ function VerticalSessionTabs(props: { controller?: SessionTabsController; animat onMouseDown={(event: MouseEvent) => { if (event.button !== RIGHT_MOUSE_BUTTON) return if (!rail) return - setContextMenu({ x: event.x - rail.screenX, y: event.y - rail.screenY }) + setContextMenu({ x: event.x, y: event.y }) event.preventDefault() event.stopPropagation() }} @@ -1037,8 +1030,8 @@ function HorizontalSessionTabs(props: { controller?: SessionTabsController; anim if (event.button === RIGHT_MOUSE_BUTTON) { setDragging(undefined) setContextMenu({ - x: event.x - (strip?.screenX ?? 0), - y: event.y - (strip?.screenY ?? 0), + x: event.x, + y: event.y, sessionID: tab === NEW_SESSION_TAB ? undefined : tab.sessionID, title: tab === NEW_SESSION_TAB ? undefined : tab.title, }) @@ -1134,7 +1127,7 @@ function HorizontalSessionTabs(props: { controller?: SessionTabsController; anim onMouseOut={() => setAddHovered(false)} onMouseDown={(event) => { if (event.button !== RIGHT_MOUSE_BUTTON) return - setContextMenu({ x: event.x - (strip?.screenX ?? 0), y: event.y - (strip?.screenY ?? 0) }) + setContextMenu({ x: event.x, y: event.y }) event.preventDefault() event.stopPropagation() }}