diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index e37c74ff6c..2c8ab72930 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -494,6 +494,15 @@ const isAiPanelMaximized = ref(false); const isHistoryPanelMaximized = ref(false); const isZenMode = ref(false); const showSqlLibraryPanel = ref(safeLocalStorageGet("dbx-sql-library-open") === "true"); +const sqlLibraryLocateRequest = ref<{ id: number; fileId: string } | null>(null); +let sqlLibraryLocateRequestId = 0; +watch( + showSqlLibraryPanel, + (open) => { + if (!open) sqlLibraryLocateRequest.value = null; + }, + { flush: "sync" }, +); const sqlFileLocateRequest = ref<{ id: number; path: string } | null>(null); let sqlFileLocateRequestId = 0; const showSqlFilePanel = ref(safeLocalStorageGet("dbx-sql-file-panel-open") === "true"); @@ -3740,6 +3749,17 @@ function finishSqlFileLocate(requestId: number) { if (sqlFileLocateRequest.value?.id === requestId) sqlFileLocateRequest.value = null; } +function locateSqlLibraryFile(tab: QueryTab) { + if (!tab.savedSqlId || isDetachedWindowContext) return; + isZenMode.value = false; + openRightSidebarPanel("sqlLibrary"); + sqlLibraryLocateRequest.value = { id: ++sqlLibraryLocateRequestId, fileId: tab.savedSqlId }; +} + +function finishSqlLibraryLocate(requestId: number) { + if (sqlLibraryLocateRequest.value?.id === requestId) sqlLibraryLocateRequest.value = null; +} + async function locateTabInSidebar(tab: QueryTab) { setSidebarOpen(true); await nextTick(); @@ -5586,6 +5606,7 @@ onUnmounted(() => { :content-suppressed="activeTab?.mode === 'plugin-workbench'" @locate-tab="locateTabInSidebar" @locate-sql-file="locateSqlFile" + @locate-sql-library-file="locateSqlLibraryFile" @close-tab=" (tabId: string) => { if (tabId === queryStore.activeTabId) void closeActiveSurface(); @@ -5819,7 +5840,7 @@ onUnmounted(() => {
- +
diff --git a/apps/desktop/src/components/layout/EditorGroup.vue b/apps/desktop/src/components/layout/EditorGroup.vue index 83f2ef7ebf..e7e0030983 100644 --- a/apps/desktop/src/components/layout/EditorGroup.vue +++ b/apps/desktop/src/components/layout/EditorGroup.vue @@ -192,6 +192,7 @@ const groupExecutableSql = computed(() => { @activate-tab="$emit('activate-tab', $event)" @locate-tab="$emit('locate-tab', $event)" @locate-sql-file="$emit('locate-sql-file', $event)" + @locate-sql-library-file="$emit('locate-sql-library-file', $event)" @toggle-zen-mode="$emit('toggle-zen-mode')" @start-resize="$emit('start-resize', $event)" @toggle-collapse="$emit('toggle-collapse')" diff --git a/apps/desktop/src/components/layout/EditorGroupTabBar.vue b/apps/desktop/src/components/layout/EditorGroupTabBar.vue index 3466839d1c..0338fb24c9 100644 --- a/apps/desktop/src/components/layout/EditorGroupTabBar.vue +++ b/apps/desktop/src/components/layout/EditorGroupTabBar.vue @@ -145,6 +145,7 @@ const emit = defineEmits<{ "activate-tab": [tabId: string]; "locate-tab": [tab: QueryTab]; "locate-sql-file": [tab: QueryTab]; + "locate-sql-library-file": [tab: QueryTab]; "toggle-zen-mode": []; "start-resize": [event: PointerEvent]; "toggle-collapse": []; @@ -1705,6 +1706,12 @@ function getTabMenuItems(tab: QueryTab): ContextMenuItem[] { visible: !!queryTabExternalFilePath(tab), action: () => emit("locate-sql-file", tab), }, + { + label: t("sqlFileTree.locateActiveFile"), + icon: Crosshair, + visible: !!tab.savedSqlId, + action: () => emit("locate-sql-library-file", tab), + }, { label: "", separator: true }, createPinTabMenuItem({ label: tab.pinned ? t("contextMenu.unpinTab") : t("contextMenu.pinTab"), diff --git a/apps/desktop/src/components/layout/SqlLibraryPanel.vue b/apps/desktop/src/components/layout/SqlLibraryPanel.vue index ca2008d087..172a148bd6 100644 --- a/apps/desktop/src/components/layout/SqlLibraryPanel.vue +++ b/apps/desktop/src/components/layout/SqlLibraryPanel.vue @@ -78,8 +78,13 @@ const queryStore = useQueryStore(); const settingsStore = useSettingsStore(); const { databaseOptions, loadingDatabaseOptions, loadDatabaseOptions, catalogOptions, loadingCatalogOptions, loadCatalogOptions, catalogDatabaseOptions, loadingCatalogDatabaseOptions, loadCatalogDatabaseOptions } = useDatabaseOptions(); +const props = defineProps<{ + locateRequest?: { id: number; fileId: string } | null; +}>(); + const emit = defineEmits<{ close: []; + "locate-handled": [id: number]; }>(); const UNFILED_DROP_TARGET_ID = "__sql-library-unfiled__"; @@ -541,8 +546,8 @@ const activeSavedSqlId = computed(() => queryStore.tabs.find((tab) => tab.id === const libraryTree = ref(null); const currentSavedSqlFile = computed(() => (activeSavedSqlId.value ? savedSqlStore.getFile(activeSavedSqlId.value) : undefined)); -async function locateCurrentFile() { - const file = currentSavedSqlFile.value; +async function locateFile(fileId: string) { + const file = savedSqlStore.getFile(fileId); if (!file) return; if (searchInputPending.value || !treeRows.value.some((row) => row.type === "file" && row.file.id === file.id)) { searchInput.value?.clear(); @@ -565,6 +570,25 @@ async function locateCurrentFile() { row?.scrollIntoView({ block: "center", inline: "nearest" }); } +async function locateCurrentFile() { + const file = currentSavedSqlFile.value; + if (!file) return; + await locateFile(file.id); +} + +watch( + [() => savedSqlStore.loadState, () => props.locateRequest], + async ([loadState, request]) => { + if (!request || loadState === "loading") return; + try { + await locateFile(request.fileId); + } finally { + emit("locate-handled", request.id); + } + }, + { immediate: true, flush: "post" }, +); + const hasCurrentSavedSqlExecutionTarget = computed(() => { const activeTab = queryStore.tabs.find((tab) => tab.id === queryStore.activeTabId); const target = savedSqlExecutionTargetFromTab(activeTab); @@ -1611,6 +1635,12 @@ function showDropAfter(targetId: string) { function showDropInside(targetId: string) { return dragState.active && dragState.targetId === targetId && dragState.dropPosition === "inside"; } + +defineExpose({ + locateFile, + locateCurrentFile, + searchInput, +});