feat(app): make the New tab launcher keyboard-first

Cmd/Ctrl+T now opens the focused pane's launcher, while explicit target shortcuts bypass the menu. Dispatcher ownership is scoped to the app tree so retained workspaces cannot intercept commands.
This commit is contained in:
Mohamed Boudra
2026-08-17 21:15:16 +02:00
parent 8655722296
commit 60bafad3ee
38 changed files with 1047 additions and 168 deletions
@@ -27,6 +27,7 @@ import { currentPageId, isSubPageOpen } from "./menu-navigation";
import { AnchoredSurface, MenuOverlay } from "./menu-overlay";
import { getMenuSheetBottomPadding } from "./menu-sheet-layout";
import type { Alignment, Placement } from "./menu-anchor";
import type { KeyboardFocusScope } from "@/keyboard/actions";
const ThemedChevronLeft = withUnistyles(ChevronLeft);
const mutedIconMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
@@ -105,6 +106,8 @@ export interface MenuSurfaceProps {
horizontalPadding?: number;
scrollable?: boolean;
testID?: string;
/** Limits ordinary app shortcuts to the time this menu owns keyboard focus. */
keyboardFocusScope?: KeyboardFocusScope;
}
/**
@@ -188,6 +191,7 @@ function MenuPopoverSurface({
horizontalPadding = 16,
scrollable = false,
testID,
keyboardFocusScope,
}: MenuSurfaceProps): ReactElement | null {
const menu = useMenuContext("MenuSurface");
const { value: surfaceValue, getAnchor } = useSubAnchors();
@@ -208,7 +212,7 @@ function MenuPopoverSurface({
return (
<MenuSurfaceContext.Provider value={surfaceValue}>
<MenuOverlay visible={menu.open} onClose={handleClose}>
<MenuOverlay visible={menu.open} onClose={handleClose} restoreFocusRef={menu.triggerRef}>
<>
<AnchoredSurface
open={menu.open}
@@ -226,6 +230,7 @@ function MenuPopoverSurface({
horizontalPadding={horizontalPadding}
scrollable={scrollable}
testID={testID}
keyboardFocusScope={keyboardFocusScope}
>
<MenuPage depth={0}>{children}</MenuPage>
</AnchoredSurface>
@@ -304,6 +309,7 @@ function MenuSheetSurface({
pages = [],
sheetTitle,
testID,
keyboardFocusScope,
}: MenuSurfaceProps): ReactElement | null {
const menu = useMenuContext("MenuSurface");
const { value: surfaceValue } = useSubAnchors();
@@ -318,6 +324,10 @@ function MenuSheetSurface({
}),
[safeAreaInsets.bottom],
);
const sheetDataSet = useMemo(
() => (keyboardFocusScope ? { keyboardScope: keyboardFocusScope } : undefined),
[keyboardFocusScope],
);
const handleClose = useCallback(() => menu.setOpen(false), [menu]);
const { sheetRef, handleSheetChange, handleSheetDismiss } = useIsolatedBottomSheetVisibility({
@@ -369,6 +379,7 @@ function MenuSheetSurface({
keyboardBlurBehavior="restore"
>
<BottomSheetScrollView
dataSet={sheetDataSet}
contentContainerStyle={sheetScrollContentStyle}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}