diff --git a/Cargo.lock b/Cargo.lock index a62ce89ba..cb2733ac9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1844,6 +1844,7 @@ name = "dbx" version = "0.5.29" dependencies = [ "anyhow", + "base64 0.22.1", "calamine", "chrono", "csv", diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 97768bddd..4a9b89130 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -279,7 +279,10 @@ function isGlobalUiZoomTarget(target: EventTarget | null): target is Element { watch( () => queryStore.activeTabId, - (id) => { + (id, previousId) => { + if (previousId && previousId !== id && typeof window !== "undefined") { + window.dispatchEvent(new CustomEvent("dbx:before-tab-switch", { detail: { tabId: id, fromTabId: previousId } })); + } if (id) newQueryContextSource.value = "tab"; selectedSql.value = ""; activeOutputView.value = "result"; @@ -862,11 +865,6 @@ async function reconnectRestoredTabs() { await connectionStore.ensureConnected(activeConnectionId); } catch {} } - - const tab = activeTab.value; - if (tab?.mode === "data" && tab.tableMeta && tab.sql) { - queryStore.executeTabSql(tab.id, tab.sql).catch(() => {}); - } } function handleContextMenu(e: MouseEvent) { @@ -1058,7 +1056,7 @@ onUnmounted(() => { @set-default-database="setActiveDatabaseAsDefault" @clear-default-database="clearActiveDefaultDatabase" /> - + @@ -3415,6 +3419,7 @@ function onCanvasScroll(event: Event) { const gutter = scrollbarGutterWidth(scroller); if (gridScrollbarGutter.value !== gutter) gridScrollbarGutter.value = gutter; if (headerRef.value && headerRef.value.scrollLeft !== scrollLeft) headerRef.value.scrollLeft = scrollLeft; + recordScrollPosition({ top: scrollTop, left: scrollLeft }); markGridScrolling(); scheduleCanvasDraw(); } @@ -4606,6 +4611,8 @@ function updateTransposeViewport() { function onTransposeScroll() { updateTransposeViewport(); + const el = transposeScrollElement(); + recordScrollPosition(el ? { top: el.scrollTop, left: el.scrollLeft } : undefined); markGridScrolling(); } @@ -6312,7 +6319,7 @@ const gridContextMenuItems = computed(() => { autocorrect="off" spellcheck="false" class="cell-edit-input absolute inset-0 bg-background border-2 border-primary px-1.5 py-0 text-xs leading-[26px] outline-none z-10" - @blur="commitEdit" + @blur="commitEditFromBlur" @click.stop @keydown.stop="onEditKeydown" @paste.stop @@ -6948,7 +6955,7 @@ const gridContextMenuItems = computed(() => { autocorrect="off" spellcheck="false" class="cell-edit-input absolute inset-0 bg-background border-2 border-primary px-2.5 py-0 text-xs leading-[22px] outline-none z-10" - @blur="commitEdit" + @blur="commitEditFromBlur" @click.stop @keydown.stop="onEditKeydown" @paste.stop @@ -7123,7 +7130,7 @@ const gridContextMenuItems = computed(() => { autocorrect="off" spellcheck="false" class="cell-edit-input absolute inset-0 bg-background border-2 border-primary px-2.5 py-0 text-xs leading-[22px] outline-none z-10" - @blur="commitEdit" + @blur="commitEditFromBlur" @click.stop @keydown.stop="onEditKeydown" @paste.stop diff --git a/apps/desktop/src/components/layout/AppTabBar.vue b/apps/desktop/src/components/layout/AppTabBar.vue index b8f4e261d..f42cac9df 100644 --- a/apps/desktop/src/components/layout/AppTabBar.vue +++ b/apps/desktop/src/components/layout/AppTabBar.vue @@ -229,17 +229,19 @@ function tabMenuIcon(tab: QueryTab) { return Code2; } -function activateTab(tabId: string) { - tabScrollBehavior.value = "auto"; - queryStore.activeTabId = tabId; - emit("close-driver-store"); -} - function handleTabClick(tab: QueryTab) { if (tabDrag.state.wasDragged) return; activateTab(tab.id); } +function handleTabMouseDown(event: MouseEvent, tabId: string) { + if (event.button === 0) { + dispatchBeforeTabSwitch(tabId); + event.preventDefault(); + } + tabDrag.startDrag(event, tabId); +} + function tabDropStyle(tabId: string) { if (!tabDrag.state.active) return {}; if (tabDrag.state.draggedId === tabId) return { opacity: 0.4 }; @@ -266,6 +268,20 @@ const tabOverflowControlClass = computed(() => ? "h-full w-8 border-r border-border/80 dark:border-border/45 bg-background/80 text-foreground/75 hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-40" : "h-7 w-7 rounded-md border border-border/60 bg-background text-foreground/70 hover:border-border hover:text-foreground", ); + +function dispatchBeforeTabSwitch(tabId: string) { + if (tabId === queryStore.activeTabId) return; + window.dispatchEvent( + new CustomEvent("dbx:before-tab-switch", { detail: { tabId, fromTabId: queryStore.activeTabId } }), + ); +} + +function activateTab(tabId: string) { + dispatchBeforeTabSwitch(tabId); + tabScrollBehavior.value = "auto"; + queryStore.activeTabId = tabId; + emit("close-driver-store"); +}