Make sidebar shortcuts leave focus mode (#2717)

* fix(app): exit focus mode for sidebar shortcuts

Focus mode kept sidebar actions hidden after their handlers ran. Exit only after a sidebar action is handled so the requested action still takes effect.

* fix(app): order focus exit before sidebar action

Leave focus mode before dispatching the sidebar command so its handler runs against the visible panel state.

* fix(app): preserve focus mode outside workspaces

Only exit active workspace focus mode after a sidebar shortcut is handled. Keep persisted focus state unchanged when the shortcut has no foreground workspace target.
This commit is contained in:
Mohamed Boudra
2026-08-01 11:16:48 +02:00
committed by GitHub
parent 70ed70d36e
commit 6177d85e99
4 changed files with 78 additions and 7 deletions
+56 -6
View File
@@ -1,23 +1,73 @@
import { expect, test } from "./fixtures";
import { expect, test, type Page } from "./fixtures";
const modifier = process.platform === "darwin" ? "Meta" : "Control";
async function pressFocusModeShortcut(page: import("@playwright/test").Page) {
async function pressFocusModeShortcut(page: Page) {
await page.keyboard.press(`${modifier}+Shift+F`);
}
async function pressSettingsShortcut(page: import("@playwright/test").Page) {
async function pressSettingsShortcut(page: Page) {
await page.keyboard.press(`${modifier}+Comma`);
}
async function blurActiveElement(page: import("@playwright/test").Page) {
async function pressRightSidebarShortcut(page: Page) {
await page.keyboard.press(`${modifier}+E`);
}
async function returnToWorkspace(page: Page, workspaceUrl: string) {
await page.goto(workspaceUrl);
}
async function blurActiveElement(page: Page) {
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
}
function exitFocusModeButton(page: Page) {
return page.getByRole("button", { name: "Exit focus mode" });
}
async function enterFocusMode(page: Page) {
await blurActiveElement(page);
await pressFocusModeShortcut(page);
await expect(exitFocusModeButton(page)).toBeVisible();
}
test("sidebar shortcuts exit focus mode and execute their action", async ({
page,
withWorkspace,
}) => {
const workspace = await withWorkspace({ prefix: "focus-mode-sidebar-shortcut-" });
await workspace.navigateTo();
await enterFocusMode(page);
await pressRightSidebarShortcut(page);
await expect(exitFocusModeButton(page)).toHaveCount(0);
await expect(page.getByText("Changes", { exact: true })).toBeVisible();
});
test("sidebar shortcuts outside a workspace preserve its focus mode", async ({
page,
withWorkspace,
}) => {
const workspace = await withWorkspace({ prefix: "focus-mode-sidebar-boundary-" });
await workspace.navigateTo();
await enterFocusMode(page);
const workspaceUrl = page.url();
await pressSettingsShortcut(page);
await expect(page.getByRole("navigation", { name: "Settings" })).toBeVisible();
await pressRightSidebarShortcut(page);
await returnToWorkspace(page, workspaceUrl);
await expect(exitFocusModeButton(page)).toBeVisible();
});
test("focus mode only applies to the active workspace screen", async ({ page, withWorkspace }) => {
const workspace = await withWorkspace({ prefix: "focus-mode-boundary-" });
await workspace.navigateTo();
const exitFocusMode = page.getByRole("button", { name: "Exit focus mode" });
const exitFocusMode = exitFocusModeButton(page);
const settingsButton = page.getByRole("button", { name: "Settings", exact: true });
const settingsSidebar = page.getByRole("navigation", { name: "Settings" });
@@ -41,7 +91,7 @@ test("focus mode only applies to the active workspace screen", async ({ page, wi
await expect(exitFocusMode).toHaveCount(0);
await pressFocusModeShortcut(page);
await page.goto(workspaceUrl);
await returnToWorkspace(page, workspaceUrl);
await expect(exitFocusMode).toBeVisible();
await exitFocusMode.click();
+3
View File
@@ -428,6 +428,7 @@ function AppContainer({ children, chromeEnabled: chromeEnabledOverride }: AppCon
const openDesktopAgentList = usePanelStore((state) => state.openDesktopAgentList);
const closeDesktopAgentList = usePanelStore((state) => state.closeDesktopAgentList);
const closeDesktopFileExplorer = usePanelStore((state) => state.closeDesktopFileExplorer);
const exitFocusMode = usePanelStore((state) => state.exitFocusMode);
const isFocusModeEnabled = usePanelStore((state) => state.desktop.focusModeEnabled);
const isDesktopAgentListOpen = usePanelStore((state) => state.desktop.agentListOpen);
const isDesktopFileExplorerOpen = usePanelStore((state) => state.desktop.fileExplorerOpen);
@@ -472,8 +473,10 @@ function AppContainer({ children, chromeEnabled: chromeEnabledOverride }: AppCon
useKeyboardShortcuts({
enabled: keyboardShortcutsEnabled,
isMobile: isCompactLayout,
isWorkspaceFocusModeEnabled,
toggleAgentList,
toggleBothSidebars: toggleDesktopSidebars,
exitFocusMode,
cycleTheme,
});
@@ -40,14 +40,18 @@ import {
export function useKeyboardShortcuts({
enabled,
isMobile,
isWorkspaceFocusModeEnabled,
toggleAgentList,
toggleBothSidebars,
exitFocusMode,
cycleTheme,
}: {
enabled: boolean;
isMobile: boolean;
isWorkspaceFocusModeEnabled: boolean;
toggleAgentList: () => void;
toggleBothSidebars?: () => void;
exitFocusMode: () => void;
cycleTheme?: () => void;
}) {
const pathname = usePathname();
@@ -207,11 +211,15 @@ export function useKeyboardShortcuts({
shortcutsDialogOpen: store.shortcutsDialogOpen,
},
);
return performShortcutAction(
const handled = performShortcutAction(
shortcutAction,
input.domEvent,
input.browserFocusRestoreElement,
);
if (handled && isWorkspaceFocusModeEnabled && input.action.startsWith("sidebar.")) {
exitFocusMode();
}
return handled;
};
const resolveAndPerformShortcut = (input: {
@@ -371,10 +379,12 @@ export function useKeyboardShortcuts({
bindings,
cycleTheme,
enabled,
exitFocusMode,
activeWorkspaceSelection,
isDesktopApp,
isMac,
isMobile,
isWorkspaceFocusModeEnabled,
openProjectPickerAction,
pathname,
publishBrowserShortcutPolicy,
@@ -90,6 +90,7 @@ export interface PanelState {
// Actions
toggleFocusMode: () => void;
exitFocusMode: () => void;
showMobileAgent: () => void;
showMobileAgentList: () => void;
toggleMobileAgentList: () => void;
@@ -157,6 +158,13 @@ export const usePanelStore = create<PanelState>()(
desktop: { ...state.desktop, focusModeEnabled: !state.desktop.focusModeEnabled },
})),
exitFocusMode: () =>
set((state) =>
state.desktop.focusModeEnabled
? { desktop: { ...state.desktop, focusModeEnabled: false } }
: state,
),
showMobileAgent: () => set((state) => setMobilePanelTargetPatch(state, "agent")),
showMobileAgentList: () => set((state) => setMobilePanelTargetPatch(state, "agent-list")),