feat: add SQL library history query entry points
This commit is contained in:
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 });
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user