mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-30 00:19:35 +00:00
Revert "feat(cli): add dbx-cli runtime and agent integration (#229)"
This reverts commit58c47adef5, reversing changes made to3bcec8ef7f.
This commit is contained in:
+10
-18
@@ -15,7 +15,6 @@ import UpdateDialog from "@/components/layout/UpdateDialog.vue";
|
||||
import LoginPage from "@/components/auth/LoginPage.vue";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useAgentRuntimeStore } from "@/stores/agentRuntimeStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useSavedSqlStore } from "@/stores/savedSqlStore";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
@@ -38,7 +37,6 @@ import { isCloseTabShortcut, isExecuteSqlShortcut } from "@/lib/keyboardShortcut
|
||||
import { isPreviewTab } from "@/lib/tabPresentation";
|
||||
import { SQL_FILE_UNSUPPORTED_TYPES } from "@/lib/databaseCapabilities";
|
||||
import { classifyAiSqlExecution } from "@/lib/aiSqlExecutionPolicy";
|
||||
import { restoreStartupAgentRuntime } from "@/lib/appStartup";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
@@ -48,7 +46,6 @@ import type { HistoryEntry } from "@/lib/tauri";
|
||||
const { t } = useI18n();
|
||||
const connectionStore = useConnectionStore();
|
||||
const queryStore = useQueryStore();
|
||||
const agentRuntimeStore = useAgentRuntimeStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const savedSqlStore = useSavedSqlStore();
|
||||
const { message: toastMessage, visible: toastVisible, toast } = useToast();
|
||||
@@ -161,7 +158,6 @@ watch(
|
||||
() => queryStore.activeTabId,
|
||||
() => {
|
||||
selectedSql.value = "";
|
||||
agentRuntimeStore.setSelectedSql("");
|
||||
activeOutputView.value = "result";
|
||||
},
|
||||
);
|
||||
@@ -449,14 +445,15 @@ function onLoginSuccess() {
|
||||
}
|
||||
|
||||
function initApp() {
|
||||
restoreStartupAgentRuntime({
|
||||
initSavedSql: () => savedSqlStore.initFromStorage(),
|
||||
initConnections: () => connectionStore.initFromDisk(),
|
||||
reconnectRestoredTabs,
|
||||
scheduleSync: () => agentRuntimeStore.scheduleSync(),
|
||||
}).catch((e: any) => {
|
||||
toast(t("connection.loadFailed", { message: e?.message || String(e) }), 5000);
|
||||
});
|
||||
savedSqlStore
|
||||
.initFromStorage()
|
||||
.then(() => connectionStore.initFromDisk())
|
||||
.then(() => {
|
||||
reconnectRestoredTabs();
|
||||
})
|
||||
.catch((e: any) => {
|
||||
toast(t("connection.loadFailed", { message: e?.message || String(e) }), 5000);
|
||||
});
|
||||
settingsStore.initAiConfig();
|
||||
}
|
||||
|
||||
@@ -617,12 +614,7 @@ onUnmounted(() => {
|
||||
if (queryStore.activeTabId) queryStore.updateSql(queryStore.activeTabId, v);
|
||||
}
|
||||
"
|
||||
@editor-selection-change="
|
||||
(v: string) => {
|
||||
selectedSql = v;
|
||||
agentRuntimeStore.setSelectedSql(v);
|
||||
}
|
||||
"
|
||||
@editor-selection-change="(v: string) => (selectedSql = v)"
|
||||
@editor-cursor-change="(p: number) => (cursorPos = p)"
|
||||
@format-error="toast(t('toolbar.formatSqlFailed'))"
|
||||
@reload="
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted } from "vue";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { useAgentRuntimeStore } from "@/stores/agentRuntimeStore";
|
||||
|
||||
const agentRuntimeStore = useAgentRuntimeStore();
|
||||
let refreshTimer: number | null = null;
|
||||
|
||||
const active = computed(() => agentRuntimeStore.activeHandoff);
|
||||
const riskTone = computed(() => {
|
||||
if (!active.value) return "secondary";
|
||||
return active.value.riskLevel === "critical" || active.value.isProduction ? "destructive" : "secondary";
|
||||
});
|
||||
|
||||
async function refresh() {
|
||||
try {
|
||||
await agentRuntimeStore.loadHandoffs();
|
||||
} catch (err) {
|
||||
console.debug("[DBX] Agent handoff refresh skipped:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function rejectActive() {
|
||||
if (!active.value) return;
|
||||
await agentRuntimeStore.rejectHandoff(active.value.id);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void refresh();
|
||||
refreshTimer = window.setInterval(() => void refresh(), 5000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (refreshTimer) window.clearInterval(refreshTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog v-model:open="agentRuntimeStore.handoffDialogOpen">
|
||||
<DialogContent class="max-w-3xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>DBX Agent Handoff</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div v-if="active" class="space-y-4">
|
||||
<div class="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
<Badge :variant="riskTone">{{ active.riskLevel }}</Badge>
|
||||
<span>{{ active.connectionName }}</span>
|
||||
<span v-if="active.database">/ {{ active.database }}</span>
|
||||
<span>/ {{ active.operationClass }}</span>
|
||||
<span v-if="active.status === 'shown'">/ shown</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<h3 class="text-sm font-semibold">{{ active.title }}</h3>
|
||||
<p v-if="active.description" class="text-sm text-muted-foreground">{{ active.description }}</p>
|
||||
</div>
|
||||
|
||||
<pre class="max-h-96 overflow-auto rounded-md border bg-muted/60 p-3 text-xs leading-relaxed">{{
|
||||
active.sql
|
||||
}}</pre>
|
||||
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Review only: DBX does not execute agent handoff SQL from this dialog.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-sm text-muted-foreground">No pending agent handoffs.</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="agentRuntimeStore.handoffDialogOpen = false">Close</Button>
|
||||
<Button v-if="active" variant="destructive" @click="rejectActive">Reject</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
@@ -69,10 +69,9 @@ import {
|
||||
quoteTableIdentifier,
|
||||
} from "@/lib/tableSelectSql";
|
||||
import { isHiddenGridColumn, usesSyntheticRowIdKey } from "@/lib/tableEditing";
|
||||
import { displayCellValue, type CellValue } from "@/lib/cellValue";
|
||||
import { formatGridSqlLiteral } from "@/lib/dataGridSql";
|
||||
import { matchesRowStatusFilter, type RowStatus, type RowStatusFilter } from "@/lib/gridRowStatus";
|
||||
import { useAgentRuntimeStore } from "@/stores/agentRuntimeStore";
|
||||
import { displayCellValue, type CellValue } from "@/lib/cellValue";
|
||||
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { useDataGridExport } from "@/composables/useDataGridExport";
|
||||
@@ -82,7 +81,6 @@ import { useDataGridEditor } from "@/composables/useDataGridEditor";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
const agentRuntimeStore = useAgentRuntimeStore();
|
||||
|
||||
const props = defineProps<{
|
||||
result: QueryResult;
|
||||
@@ -1136,22 +1134,6 @@ const activeCellDetail = computed(() => {
|
||||
const detailEditValue = ref("");
|
||||
const isEditingDetail = ref(false);
|
||||
|
||||
watch(
|
||||
selectedCells,
|
||||
(data) => {
|
||||
agentRuntimeStore.setSelection(
|
||||
selectedCellCount.value > 0
|
||||
? {
|
||||
type: "grid-cells",
|
||||
range: selectedRange.value,
|
||||
data,
|
||||
}
|
||||
: { type: "none" },
|
||||
);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
function startDetailEdit() {
|
||||
const detail = activeCellDetail.value;
|
||||
if (!detail || !detail.isEditable) return;
|
||||
@@ -1673,7 +1655,6 @@ watch(
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanupFrames();
|
||||
agentRuntimeStore.setSelection({ type: "none" });
|
||||
onDdlResizeEnd();
|
||||
finishCellSelection();
|
||||
clearTimeout(_searchTimer);
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useI18n } from "vue-i18n";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import ConnectionDialog from "@/components/connection/ConnectionDialog.vue";
|
||||
import AgentHandoffDialog from "@/components/agent/AgentHandoffDialog.vue";
|
||||
import EditorSettingsDialog from "@/components/editor/EditorSettingsDialog.vue";
|
||||
import DangerConfirmDialog from "@/components/editor/DangerConfirmDialog.vue";
|
||||
const DataTransferDialog = defineAsyncComponent(() => import("@/components/transfer/DataTransferDialog.vue"));
|
||||
@@ -93,7 +92,6 @@ watch(
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AgentHandoffDialog />
|
||||
<ConnectionDialog
|
||||
:open="showConnectionDialog"
|
||||
:edit-config="editConfig"
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
export type AgentHandoffStatus = "queued" | "shown" | "approved" | "rejected" | "executed" | "failed";
|
||||
|
||||
export interface AgentHandoffItem {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
createdBy: string;
|
||||
connectionId: string;
|
||||
connectionName: string;
|
||||
database?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
sql: string;
|
||||
operationClass: string;
|
||||
riskLevel: string;
|
||||
isProduction: boolean;
|
||||
status: AgentHandoffStatus;
|
||||
resultSummary?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export function isPendingHandoff(item: AgentHandoffItem): boolean {
|
||||
return item.status === "queued" || item.status === "shown";
|
||||
}
|
||||
|
||||
export function mergeLoadedHandoffs(
|
||||
items: AgentHandoffItem[],
|
||||
ignoredIds: ReadonlySet<string> = new Set(),
|
||||
): AgentHandoffItem[] {
|
||||
return items
|
||||
.filter((item) => isPendingHandoff(item) && !ignoredIds.has(item.id))
|
||||
.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt));
|
||||
}
|
||||
|
||||
export function updateHandoffStatus(
|
||||
items: AgentHandoffItem[],
|
||||
id: string,
|
||||
status: AgentHandoffStatus,
|
||||
): AgentHandoffItem[] {
|
||||
return mergeLoadedHandoffs(items.map((item) => (item.id === id ? { ...item, status } : item)));
|
||||
}
|
||||
|
||||
export function deriveHandoffDialogState(items: AgentHandoffItem[], activeId: string | null) {
|
||||
const active = (activeId ? items.find((item) => item.id === activeId) : undefined) ?? items[0] ?? null;
|
||||
return {
|
||||
open: items.length > 0,
|
||||
active,
|
||||
};
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import type { ConnectionConfig, QueryResult, QueryTab } from "@/types/database";
|
||||
|
||||
export const DEFAULT_RESULT_SAMPLE_LIMIT = 50;
|
||||
|
||||
export interface RuntimeResultSample {
|
||||
columns: string[];
|
||||
rows: QueryResult["rows"];
|
||||
truncated: boolean;
|
||||
executionTimeMs: number;
|
||||
sampleLimit: number;
|
||||
}
|
||||
|
||||
export interface AgentRuntimeSnapshot {
|
||||
activeConnectionId?: string;
|
||||
activeConnectionName?: string;
|
||||
database?: string;
|
||||
schema?: string;
|
||||
activeTabId?: string;
|
||||
activeTabTitle?: string;
|
||||
sql?: string;
|
||||
selectedSql?: string;
|
||||
selection: unknown;
|
||||
result?: RuntimeResultSample;
|
||||
}
|
||||
|
||||
export interface BuildAgentRuntimeSnapshotOptions {
|
||||
tabs: QueryTab[];
|
||||
activeTabId: string | null;
|
||||
getConnection: (connectionId: string) => Pick<ConnectionConfig, "name"> | undefined;
|
||||
selectedSql?: string;
|
||||
selection?: unknown;
|
||||
resultSampleLimit?: number;
|
||||
}
|
||||
|
||||
export function buildAgentRuntimeSnapshot(options: BuildAgentRuntimeSnapshotOptions): AgentRuntimeSnapshot {
|
||||
const tab = options.tabs.find((item) => item.id === options.activeTabId);
|
||||
const conn = tab ? options.getConnection(tab.connectionId) : undefined;
|
||||
const selectedSql = options.selectedSql?.trim();
|
||||
const limit = options.resultSampleLimit ?? DEFAULT_RESULT_SAMPLE_LIMIT;
|
||||
const result = tab?.result
|
||||
? {
|
||||
columns: tab.result.columns,
|
||||
rows: tab.result.rows.slice(0, limit),
|
||||
truncated: tab.result.rows.length > limit || !!tab.result.truncated,
|
||||
executionTimeMs: tab.result.execution_time_ms,
|
||||
sampleLimit: limit,
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
activeConnectionId: tab?.connectionId,
|
||||
activeConnectionName: conn?.name,
|
||||
database: tab?.database,
|
||||
schema: tab?.schema,
|
||||
activeTabId: tab?.id,
|
||||
activeTabTitle: tab?.title,
|
||||
sql: tab?.sql,
|
||||
selectedSql: selectedSql || undefined,
|
||||
selection: options.selection ?? { type: "none" },
|
||||
result,
|
||||
};
|
||||
}
|
||||
@@ -72,12 +72,6 @@ export const executeScript = forward("executeScript");
|
||||
export const executeInTransaction = forward("executeInTransaction");
|
||||
export const cancelQuery = forward("cancelQuery");
|
||||
|
||||
// Agent Runtime
|
||||
export const agentRuntimeUpdateSnapshot = forward("agentRuntimeUpdateSnapshot");
|
||||
export const agentRuntimeLoadHandoffs = forward("agentRuntimeLoadHandoffs");
|
||||
export const agentRuntimeMarkHandoffShown = forward("agentRuntimeMarkHandoffShown");
|
||||
export const agentRuntimeRejectHandoff = forward("agentRuntimeRejectHandoff");
|
||||
|
||||
// AI
|
||||
export const aiComplete = forward("aiComplete");
|
||||
export const aiStream = forward("aiStream");
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export interface RestoreStartupAgentRuntimeOptions {
|
||||
initSavedSql: () => Promise<unknown>;
|
||||
initConnections: () => Promise<unknown>;
|
||||
reconnectRestoredTabs: () => Promise<unknown> | unknown;
|
||||
scheduleSync: () => void;
|
||||
}
|
||||
|
||||
export async function restoreStartupAgentRuntime(options: RestoreStartupAgentRuntimeOptions) {
|
||||
await options.initSavedSql();
|
||||
await options.initConnections();
|
||||
await options.reconnectRestoredTabs();
|
||||
options.scheduleSync();
|
||||
}
|
||||
@@ -39,8 +39,6 @@ import type {
|
||||
TableImportSummary,
|
||||
TableImportProgress,
|
||||
} from "./tauri";
|
||||
import type { AgentRuntimeSnapshot } from "@/lib/agentRuntimeSnapshot";
|
||||
import type { AgentHandoffItem } from "@/lib/agentHandoff";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
@@ -292,20 +290,6 @@ export async function cancelQuery(executionId: string): Promise<boolean> {
|
||||
return post("/api/query/cancel", { executionId });
|
||||
}
|
||||
|
||||
export async function agentRuntimeUpdateSnapshot(_snapshot: AgentRuntimeSnapshot): Promise<void> {}
|
||||
|
||||
export async function agentRuntimeLoadHandoffs(): Promise<AgentHandoffItem[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
export async function agentRuntimeMarkHandoffShown(_id: string): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function agentRuntimeRejectHandoff(_id: string): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AI
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -19,8 +19,6 @@ import type {
|
||||
SavedSqlFolder,
|
||||
SavedSqlLibrary,
|
||||
} from "@/types/database";
|
||||
import type { AgentRuntimeSnapshot } from "@/lib/agentRuntimeSnapshot";
|
||||
import type { AgentHandoffItem } from "@/lib/agentHandoff";
|
||||
import type { AiConfig } from "@/stores/settingsStore";
|
||||
|
||||
export interface AiMessage {
|
||||
@@ -82,22 +80,6 @@ export async function loadAiConfig(): Promise<AiConfig | null> {
|
||||
return invoke("load_ai_config");
|
||||
}
|
||||
|
||||
export async function agentRuntimeUpdateSnapshot(snapshot: AgentRuntimeSnapshot): Promise<void> {
|
||||
return invoke("agent_runtime_update_snapshot", { snapshot });
|
||||
}
|
||||
|
||||
export async function agentRuntimeLoadHandoffs(): Promise<AgentHandoffItem[]> {
|
||||
return invoke("agent_runtime_load_handoffs");
|
||||
}
|
||||
|
||||
export async function agentRuntimeMarkHandoffShown(id: string): Promise<boolean> {
|
||||
return invoke("agent_runtime_mark_handoff_shown", { id });
|
||||
}
|
||||
|
||||
export async function agentRuntimeRejectHandoff(id: string): Promise<boolean> {
|
||||
return invoke("agent_runtime_reject_handoff", { id });
|
||||
}
|
||||
|
||||
// --- AI Conversations ---
|
||||
|
||||
export interface AiChatMessage {
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
import * as api from "@/lib/api";
|
||||
import {
|
||||
deriveHandoffDialogState,
|
||||
mergeLoadedHandoffs,
|
||||
updateHandoffStatus,
|
||||
type AgentHandoffItem,
|
||||
} from "@/lib/agentHandoff";
|
||||
import { buildAgentRuntimeSnapshot, DEFAULT_RESULT_SAMPLE_LIMIT } from "@/lib/agentRuntimeSnapshot";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
|
||||
export const useAgentRuntimeStore = defineStore("agentRuntime", () => {
|
||||
const selection = ref<unknown>({ type: "none" });
|
||||
const selectedSql = ref("");
|
||||
const handoffs = ref<AgentHandoffItem[]>([]);
|
||||
const activeHandoffId = ref<string | null>(null);
|
||||
const handoffDialogOpen = ref(false);
|
||||
const locallyClosedHandoffIds = new Set<string>();
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const activeHandoff = computed(() => handoffs.value.find((item) => item.id === activeHandoffId.value) ?? null);
|
||||
|
||||
function setSelection(value: unknown) {
|
||||
selection.value = value;
|
||||
scheduleSync();
|
||||
}
|
||||
|
||||
function setSelectedSql(value: string) {
|
||||
selectedSql.value = value;
|
||||
scheduleSync();
|
||||
}
|
||||
|
||||
function scheduleSync() {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
void syncNow();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async function syncNow() {
|
||||
if (!globalThis.localStorage) return;
|
||||
|
||||
try {
|
||||
const connectionStore = useConnectionStore();
|
||||
const queryStore = useQueryStore();
|
||||
const snapshot = buildAgentRuntimeSnapshot({
|
||||
tabs: queryStore.tabs,
|
||||
activeTabId: queryStore.activeTabId,
|
||||
getConnection: (connectionId) => connectionStore.getConfig(connectionId),
|
||||
selectedSql: selectedSql.value,
|
||||
selection: selection.value,
|
||||
resultSampleLimit: DEFAULT_RESULT_SAMPLE_LIMIT,
|
||||
});
|
||||
await api.agentRuntimeUpdateSnapshot(snapshot);
|
||||
} catch (err) {
|
||||
console.debug("[DBX] Agent runtime snapshot sync skipped:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadHandoffs() {
|
||||
const loaded = mergeLoadedHandoffs(await api.agentRuntimeLoadHandoffs(), locallyClosedHandoffIds);
|
||||
handoffs.value = loaded;
|
||||
const state = deriveHandoffDialogState(loaded, activeHandoffId.value);
|
||||
activeHandoffId.value = state.active?.id ?? null;
|
||||
handoffDialogOpen.value = state.open;
|
||||
if (state.active?.status === "queued") {
|
||||
await markHandoffShown(state.active.id);
|
||||
}
|
||||
}
|
||||
|
||||
async function markHandoffShown(id: string) {
|
||||
handoffs.value = updateHandoffStatus(handoffs.value, id, "shown");
|
||||
await api.agentRuntimeMarkHandoffShown(id);
|
||||
}
|
||||
|
||||
async function rejectHandoff(id: string) {
|
||||
await api.agentRuntimeRejectHandoff(id);
|
||||
locallyClosedHandoffIds.add(id);
|
||||
handoffs.value = updateHandoffStatus(handoffs.value, id, "rejected");
|
||||
const state = deriveHandoffDialogState(handoffs.value, activeHandoffId.value === id ? null : activeHandoffId.value);
|
||||
activeHandoffId.value = state.active?.id ?? null;
|
||||
handoffDialogOpen.value = state.open;
|
||||
}
|
||||
|
||||
function setActiveHandoff(id: string) {
|
||||
activeHandoffId.value = id;
|
||||
const item = handoffs.value.find((handoff) => handoff.id === id);
|
||||
if (item?.status === "queued") void markHandoffShown(id);
|
||||
}
|
||||
|
||||
return {
|
||||
selection,
|
||||
selectedSql,
|
||||
handoffs,
|
||||
activeHandoff,
|
||||
handoffDialogOpen,
|
||||
setSelection,
|
||||
setSelectedSql,
|
||||
scheduleSync,
|
||||
syncNow,
|
||||
loadHandoffs,
|
||||
markHandoffShown,
|
||||
rejectHandoff,
|
||||
setActiveHandoff,
|
||||
};
|
||||
});
|
||||
@@ -10,31 +10,22 @@ import { analyzeEditableQuery, allPrimaryKeysPresent } from "@/lib/sqlAnalysis";
|
||||
import { restoreOpenTabsState, serializeOpenTabs } from "@/lib/openTabsPersistence";
|
||||
import * as api from "@/lib/api";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useAgentRuntimeStore } from "@/stores/agentRuntimeStore";
|
||||
import { isTauriRuntime } from "@/lib/tauriRuntime";
|
||||
import type { SavedSqlFile } from "@/types/database";
|
||||
|
||||
const STORAGE_KEY = "dbx-open-tabs";
|
||||
const ACTIVE_TAB_KEY = "dbx-active-tab";
|
||||
|
||||
function browserStorage(): Storage | undefined {
|
||||
return globalThis.localStorage;
|
||||
}
|
||||
|
||||
function saveTabs(tabs: QueryTab[], activeTabId: string | null) {
|
||||
const storage = browserStorage();
|
||||
if (!storage) return;
|
||||
try {
|
||||
storage.setItem(STORAGE_KEY, JSON.stringify(serializeOpenTabs(tabs)));
|
||||
storage.setItem(ACTIVE_TAB_KEY, activeTabId || "");
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(serializeOpenTabs(tabs)));
|
||||
localStorage.setItem(ACTIVE_TAB_KEY, activeTabId || "");
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function loadSavedTabs(): { tabs: QueryTab[]; activeTabId: string | null } {
|
||||
const storage = browserStorage();
|
||||
if (!storage) return { tabs: [], activeTabId: null };
|
||||
try {
|
||||
return restoreOpenTabsState(storage.getItem(STORAGE_KEY), storage.getItem(ACTIVE_TAB_KEY), {
|
||||
return restoreOpenTabsState(localStorage.getItem(STORAGE_KEY), localStorage.getItem(ACTIVE_TAB_KEY), {
|
||||
queryOnly: isTauriRuntime(),
|
||||
});
|
||||
} catch {
|
||||
@@ -81,18 +72,11 @@ export const useQueryStore = defineStore("query", () => {
|
||||
return tabs.value.find((t) => t.connectionId === connectionId && t.database === database && t.title === title);
|
||||
}
|
||||
|
||||
function scheduleAgentRuntimeSync() {
|
||||
useAgentRuntimeStore().scheduleSync();
|
||||
}
|
||||
|
||||
watch(activeTabId, () => scheduleAgentRuntimeSync());
|
||||
|
||||
function createTab(connectionId: string, database: string, title?: string, mode: QueryTab["mode"] = "query") {
|
||||
if (title) {
|
||||
const existing = findTabByTitle(connectionId, database, title);
|
||||
if (existing) {
|
||||
activeTabId.value = existing.id;
|
||||
scheduleAgentRuntimeSync();
|
||||
return existing.id;
|
||||
}
|
||||
}
|
||||
@@ -111,7 +95,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
};
|
||||
tabs.value.push(tab);
|
||||
activeTabId.value = id;
|
||||
scheduleAgentRuntimeSync();
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -162,7 +145,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
if (activeTabId.value === id) {
|
||||
activeTabId.value = tabs.value[Math.min(idx, tabs.value.length - 1)]?.id ?? null;
|
||||
}
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function closeOtherTabs(id: string) {
|
||||
@@ -171,7 +153,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const next = closeOtherTabsState(tabs.value, activeTabId.value, id);
|
||||
tabs.value = next.tabs;
|
||||
activeTabId.value = next.activeTabId;
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function closeAllTabs() {
|
||||
@@ -180,7 +161,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const next = closeAllTabsState(tabs.value, activeTabId.value);
|
||||
tabs.value = next.tabs;
|
||||
activeTabId.value = next.activeTabId;
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function updateSql(id: string, sql: string) {
|
||||
@@ -189,7 +169,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
tab.sql = sql;
|
||||
tab.resultSortedSql = undefined;
|
||||
tab.resultBaseSql = undefined;
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +183,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
const existing = tabs.value.find((tab) => tab.savedSqlId === file.id);
|
||||
if (existing) {
|
||||
activeTabId.value = existing.id;
|
||||
scheduleAgentRuntimeSync();
|
||||
return existing.id;
|
||||
}
|
||||
|
||||
@@ -224,7 +202,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
};
|
||||
tabs.value.push(tab);
|
||||
activeTabId.value = id;
|
||||
scheduleAgentRuntimeSync();
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -247,7 +224,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
tab.resultSortedSql = undefined;
|
||||
clearExplain(tab);
|
||||
tab.tableMeta = undefined;
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function updateSchema(id: string, schema: string | undefined) {
|
||||
@@ -255,7 +231,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
if (!tab || tab.schema === schema) return;
|
||||
tab.schema = schema;
|
||||
if (tab.mode === "objects") tab.objectBrowser = { ...tab.objectBrowser, schema };
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function updateConnection(id: string, connectionId: string, database = "") {
|
||||
@@ -270,7 +245,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
tab.resultSortedSql = undefined;
|
||||
clearExplain(tab);
|
||||
tab.tableMeta = undefined;
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function setTableMeta(id: string, meta: NonNullable<QueryTab["tableMeta"]>) {
|
||||
@@ -436,7 +410,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
console.info("[DBX][executeTabSql:metadata:start]", { traceId, elapsed: elapsed() });
|
||||
await analyzeQueryMetadata(current, current.resultBaseSql);
|
||||
console.info("[DBX][executeTabSql:metadata:done]", { traceId, elapsed: elapsed() });
|
||||
scheduleAgentRuntimeSync();
|
||||
} else {
|
||||
console.warn("[DBX][executeTabSql:stale-result]", {
|
||||
traceId,
|
||||
@@ -455,7 +428,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
if (current.mode !== "data") current.tableMeta = undefined;
|
||||
current.resultBaseSql = options?.resultBaseSql ?? sql;
|
||||
current.resultSortedSql = options?.resultSortedSql;
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
} finally {
|
||||
const current = tabs.value.find((t) => t.id === id);
|
||||
@@ -566,7 +538,6 @@ export const useQueryStore = defineStore("query", () => {
|
||||
if (!tab?.results || index < 0 || index >= tab.results.length) return;
|
||||
tab.activeResultIndex = index;
|
||||
tab.result = tab.results[index];
|
||||
scheduleAgentRuntimeSync();
|
||||
}
|
||||
|
||||
function trimResultCache() {
|
||||
|
||||
Reference in New Issue
Block a user