feat: add SQL library history query entry points

This commit is contained in:
SuLea-IT authored and GitHub committed 2026-06-11 10:53:54 +08:00
1 parent 634e26e8e7
commit 806d7c758b
16 files changed
+345 -8

No files matched your search

@@ -439,6 +439,7 @@ function openFile(file: SavedSqlFile) {
if (suppressNextRowClick.value) return;
queryStore.openSavedSql(file);
connectionStore.activeConnectionId = file.connectionId;
void savedSqlStore.recordFileUsage(file.id);
}
const contextTarget = ref<SavedSqlFolder | SavedSqlFile | "panel" | null>(null);
@@ -5,15 +5,26 @@ import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import { connectionDriverLabel, connectionIconType, connectionRedactedNameLabel, connectionRedactedOptionSubtitle } from "@/lib/connectionPresentation";
import type { ConnectionConfig } from "@/types/database";
export interface WelcomeSavedSqlHistoryItem {
id: string;
name: string;
connectionName: string;
database?: string;
folderName?: string;
openCount?: number;
}
defineProps<{
connectionStats: { total: number; connected: number; types: number };
recentConnections: ConnectionConfig[];
savedSqlHistoryItems: WelcomeSavedSqlHistoryItem[];
appVersion: string;
hasConnections: boolean;
}>();
const emit = defineEmits<{
"open-connection-query": [connectionId: string];
"open-saved-sql": [fileId: string];
"new-connection": [];
"new-query": [];
"show-history": [];
@@ -83,6 +94,30 @@ const { t } = useI18n();
</div>
</div>
<div class="rounded-lg border">
<div class="flex items-center justify-between border-b px-4 py-3">
<div class="flex items-center gap-2 text-sm font-medium"><History class="h-4 w-4" /> {{ t("welcome.sqlHistory") }}</div>
</div>
<div class="divide-y">
<button v-for="item in savedSqlHistoryItems" :key="item.id" class="flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-muted/40" @click="emit('open-saved-sql', item.id)">
<History class="h-4 w-4 text-muted-foreground" />
<div class="min-w-0 flex-1">
<div class="truncate text-sm font-medium">{{ item.name }}</div>
<div class="truncate text-xs text-muted-foreground">
<span>{{ item.connectionName }}</span>
<span v-if="item.database"> · {{ item.database }}</span>
<span v-if="item.folderName"> · {{ item.folderName }}</span>
<span v-if="item.openCount"> · {{ t("welcome.sqlHistoryOpenCount", { count: item.openCount }) }}</span>
</div>
</div>
<FilePlus2 class="h-4 w-4 text-muted-foreground" />
</button>
<div v-if="savedSqlHistoryItems.length === 0" class="px-4 py-8 text-sm text-muted-foreground">
{{ t("welcome.sqlHistoryEmpty") }}
</div>
</div>
</div>
<!-- MCP Integration Hint -->
<div class="rounded-lg border bg-muted/10 px-5 py-4">
<div class="flex items-start gap-3">
@@ -53,6 +53,7 @@ import CustomContextMenu, { type ContextMenuItem } from "@/components/ui/CustomC
import { useConnectionStore } from "@/stores/connectionStore";
import { useQueryStore } from "@/stores/queryStore";
import { useSettingsStore } from "@/stores/settingsStore";
import { useSavedSqlStore } from "@/stores/savedSqlStore";
import { useToast } from "@/composables/useToast";
import { useDatabaseOptions } from "@/composables/useDatabaseOptions";
import type { ColumnInfo, DatabaseType, TreeNode, TreeNodeType } from "@/types/database";
@@ -103,6 +104,7 @@ import { isTauriRuntime } from "@/lib/tauriRuntime";
import { copyToClipboard } from "@/lib/clipboard";
import { hasEnabledTransportLayers } from "@/lib/connectionTransport";
import { formatShortcut } from "@/lib/shortcutRegistry";
import { rankSavedSqlHistory, type SavedSqlHistoryScope } from "@/lib/savedSqlHistory";
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import ConnectionErrorIndicator from "@/components/connection/ConnectionErrorIndicator.vue";
import VisibleDatabasesDialog from "@/components/sidebar/VisibleDatabasesDialog.vue";
@@ -126,6 +128,7 @@ function isLabelTruncated(): boolean {
const connectionStore = useConnectionStore();
const queryStore = useQueryStore();
const settingsStore = useSettingsStore();
const savedSqlStore = useSavedSqlStore();
const { toast } = useToast();
const { highlight } = useSqlHighlighter();
@@ -2529,6 +2532,60 @@ function copyStructureAsSubmenu(): ContextMenuItem {
};
}
function savedSqlHistoryScopeForNode(node: TreeNode): SavedSqlHistoryScope | null {
if (!node.connectionId) return null;
if (node.type === "connection") {
return { connectionId: node.connectionId };
}
if ((node.type === "database" || node.type === "schema") && hasTreeNodeDatabaseContext(node)) {
return {
connectionId: node.connectionId,
database: node.database,
schema: node.type === "schema" ? node.schema : undefined,
};
}
if ((node.type === "table" || node.type === "view") && hasTreeNodeDatabaseContext(node)) {
return {
connectionId: node.connectionId,
database: node.database,
schema: node.schema,
tableName: node.label,
};
}
return null;
}
function openSavedSqlHistoryFile(fileId: string) {
const file = savedSqlStore.getFile(fileId);
if (!file) return;
queryStore.openSavedSql(file);
connectionStore.activeConnectionId = file.connectionId;
void savedSqlStore.recordFileUsage(file.id);
}
function savedSqlHistorySubmenu(): ContextMenuItem | null {
const scope = savedSqlHistoryScopeForNode(props.node);
if (!scope) return null;
const files = rankSavedSqlHistory(savedSqlStore.allFiles, { ...scope, limit: 10 });
return {
label: t("contextMenu.sqlHistory"),
icon: ScrollText,
children:
files.length > 0
? files.map((file) => ({
label: file.name,
action: () => openSavedSqlHistoryFile(file.id),
icon: FileCode,
}))
: [
{
label: t("contextMenu.noSqlHistory"),
disabled: true,
},
],
};
}
function treeItemMenuItems(): ContextMenuItem[] {
const node = props.node;
const items: ContextMenuItem[] = [];
@@ -2554,6 +2611,8 @@ function treeItemMenuItems(): ContextMenuItem[] {
items.push({ label: t("contextMenu.closeConnection"), action: disconnectConnection, icon: Unplug });
}
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
const sqlHistoryMenu = savedSqlHistorySubmenu();
if (sqlHistoryMenu) items.push(sqlHistoryMenu);
if (supportsDatabaseUserAdmin(currentDatabaseType())) {
items.push({ label: t("contextMenu.userAdmin"), action: openUserAdmin, icon: UsersRound });
}
@@ -2641,6 +2700,8 @@ function treeItemMenuItems(): ContextMenuItem[] {
items.push({ label: t("contextMenu.openObjectBrowser"), action: openObjectBrowser, icon: TableProperties });
}
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
const sqlHistoryMenu = savedSqlHistorySubmenu();
if (sqlHistoryMenu) items.push(sqlHistoryMenu);
if (node.type === "database") {
if (!isNodeDefaultDatabase.value) {
items.push({ label: t("contextMenu.setDefaultDatabase"), action: setNodeAsDefaultDatabase, icon: Database });
@@ -2758,6 +2819,8 @@ function treeItemMenuItems(): ContextMenuItem[] {
});
}
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
const sqlHistoryMenu = savedSqlHistorySubmenu();
if (sqlHistoryMenu) items.push(sqlHistoryMenu);
if (canOpenDiagram.value) {
items.push({ label: t("diagram.open"), action: openDiagram, icon: Network });
}