diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index bb59394568..8621bc1649 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -369,6 +369,7 @@ const appSidebarRef = ref | null>(null); const appTabBarRef = ref | null>(null); const contentAreaRef = ref | null>(null); const lastFocusedAuxiliarySurface = ref(null); +const lastFocusedSidebarSurface = ref(false); const selectedSql = ref(""); const cursorPos = ref(0); @@ -1434,6 +1435,7 @@ function setRightSidebarPanelOpen(panelId: RightSidebarPanelId, open: boolean) { lastOpenedRightSidebarPanel = panelId; if (panelId === "ai" || panelId === "history" || panelId === "sqlLibrary") { lastFocusedAuxiliarySurface.value = panelId; + lastFocusedSidebarSurface.value = false; } } else if (lastOpenedRightSidebarPanel === panelId) { lastOpenedRightSidebarPanel = RIGHT_SIDEBAR_PANEL_IDS.find((candidate) => rightSidebarPanelRefs[candidate].value); @@ -3225,6 +3227,10 @@ function focusSearchInAuxiliarySurface(target: Element | null): boolean { if (showHistory.value && target?.closest("[data-history-panel], [data-history-search]")) return focusSearchInput("[data-history-search]"); if (showSqlLibraryPanel.value && target?.closest("[data-sql-library-panel], [data-sql-library-search]")) return focusSearchInput("[data-sql-library-search]"); + const targetIsDocument = !target || target === document.body || target === document.documentElement; + if (!targetIsDocument) return false; + if (lastFocusedSidebarSurface.value) return false; + if (lastFocusedAuxiliarySurface.value === "ai" && showAiPanel.value) { if (aiAssistantRef.value) return aiAssistantRef.value.focusSearch(); invokeWhenAiReady((handle) => handle.focusSearch()); @@ -3237,6 +3243,11 @@ function focusSearchInAuxiliarySurface(target: Element | null): boolean { function rememberAuxiliarySearchSurface(surface: Exclude) { lastFocusedAuxiliarySurface.value = surface; + lastFocusedSidebarSurface.value = false; +} + +function rememberSidebarSearchSurface() { + lastFocusedSidebarSurface.value = true; } function setPluginWorkbenchTabRef(tabId: string, element: unknown) { @@ -3315,7 +3326,9 @@ async function handleKeydown(e: KeyboardEvent) { // Otherwise Ctrl+F from empty space in the sidebar incorrectly opens the // search belonging to the active table/query tab. const target = e.target instanceof Element ? e.target : null; - const focused = target?.closest("[data-app-sidebar]") ? appSidebarRef.value?.focusSearch(target) : focusSearchInAuxiliarySurface(target) || contentAreaRef.value?.focusSearch(target) || appSidebarRef.value?.focusSearch(target); + const targetIsDocument = !target || target === document.body || target === document.documentElement; + const sidebarFocused = target?.closest("[data-app-sidebar]") || (targetIsDocument && lastFocusedSidebarSurface.value); + const focused = sidebarFocused ? appSidebarRef.value?.focusSearch(target) : focusSearchInAuxiliarySurface(target) || contentAreaRef.value?.focusSearch(target) || appSidebarRef.value?.focusSearch(target); if (focused) { e.preventDefault(); e.stopPropagation(); @@ -3771,6 +3784,7 @@ onUnmounted(() => { @collapse="setSidebarOpen(false)" @open-settings="(initialTab) => openSettings(initialTab ?? 'appearance')" @add-to-ai="addToAi" + @mousedown="rememberSidebarSearchSurface" />
diff --git a/apps/desktop/src/__tests__/searchShortcutRouting.spec.ts b/apps/desktop/src/__tests__/searchShortcutRouting.spec.ts index 9eb7eea834..cd1a480393 100644 --- a/apps/desktop/src/__tests__/searchShortcutRouting.spec.ts +++ b/apps/desktop/src/__tests__/searchShortcutRouting.spec.ts @@ -38,6 +38,8 @@ describe("search shortcut focus routing", () => { it("routes auxiliary page and panel searches before the query surface", () => { expect(appSource).toMatch(/function focusSearchInAuxiliarySurface\(target: Element \| null\)[\s\S]*data-connection-db-search[\s\S]*data-settings-global-search[\s\S]*data-driver-store-agent-search[\s\S]*data-plugin-marketplace-search[\s\S]*data-history-panel[\s\S]*data-sql-library-panel/); expect(appSource).toMatch(/lastFocusedAuxiliarySurface[\s\S]*rememberAuxiliarySearchSurface[\s\S]*data-sql-library-panel/); + expect(appSource).toMatch(/const targetIsDocument = !target \|\| target === document\.body \|\| target === document\.documentElement;[\s\S]*if \(!targetIsDocument\) return false;/); + expect(appSource).toMatch(/lastFocusedSidebarSurface[\s\S]*rememberSidebarSearchSurface[\s\S]*data-app-sidebar/); expect(appSource).toMatch(/function handleAuxiliarySearchKeydownCapture\(e: KeyboardEvent\)[\s\S]*focusSearchInAuxiliarySurface\(target\)[\s\S]*addEventListener\("keydown", handleAuxiliarySearchKeydownCapture, true\)/); expect(appSource).toContain("aiAssistantRef.value.focusSearch()"); expect(aiAssistantSource).toMatch(/function focusSearch\(\): boolean[\s\S]*setConversationListOpen\(true\)/);