feat(ai): add connection tree context

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-08-20 15:15:19 +08:00
1 parent e4577abeed
commit 0e0edcd206
15 files changed
+132 -4

No files matched your search

@@ -1063,6 +1063,8 @@ async function loadDatabases(connection = props.connection): Promise<string[]> {
async function changeConnection(connectionId: string) {
const conn = connectionStore.getConfig(connectionId);
if (!conn) return;
if (props.connection?.id === connectionId) return;
clearContextReferences();
connectionStore.activeConnectionId = connectionId;
const tab = props.tab;
const tabId = tab ? tab.id : queryStore.createTab(connectionId, resolveDefaultDatabase(conn, []));
@@ -1087,6 +1089,8 @@ function changeNamespace(value: string) {
const connection = props.connection;
if (!tab || !connection) return;
const namespace = decodeSelectableDatabaseValue(connection.db_type, value);
if (resolveAiNamespaceSelection(tab, connection).value === namespace) return;
clearContextReferences();
if (resolveAiNamespaceSelection(tab, connection).kind === "schema") {
queryStore.updateSchema(tab.id, namespace || undefined);
} else {
@@ -2942,7 +2946,23 @@ function setPrompt(text: string) {
nextTick(() => promptTextareaRef.value?.focus());
}
defineExpose({ triggerAction, setPrompt });
function addTableMention(target: { schema?: string; table: string }) {
const table = target.table.trim();
if (!table) return;
addSelectedMention({ kind: "table", schema: target.schema, name: table, tableType: "TABLE" });
nextTick(() => promptTextareaRef.value?.focus());
}
function clearContextReferences() {
selectedMentions.value = [];
selectedSqlFileMentions.value = [];
mentionCache.value = {};
mentionCandidates.value = [];
mentionOpen.value = false;
mentionError.value = "";
}
defineExpose({ triggerAction, setPrompt, addTableMention, clearContextReferences });
const messageRenderer = computed(() => {
const appearance = aiCodeAppearance.value;
@@ -12,6 +12,7 @@ import ConnectionTree from "@/components/sidebar/ConnectionTree.vue";
import { applyConnectionMultiSelection, emptyConnectionMultiSelection, isExitConnectionMultiSelectionShortcut } from "@/lib/sidebar/sidebarConnectionMultiSelect";
import { useConnectionStore } from "@/stores/connectionStore";
import { useToast } from "@/composables/useToast";
import type { TreeNode } from "@/types/database";
defineProps<{
sidebarWidth: number;
@@ -24,6 +25,7 @@ const emit = defineEmits<{
startResize: [event: MouseEvent];
collapse: [];
"open-settings": [initialTab: string];
"add-to-ai": [node: TreeNode];
}>();
type ImportSource = "dbx" | "navicat" | "dbeaver" | "datagrip";
@@ -264,7 +266,7 @@ defineExpose({ focusSearch });
</template>
</div>
<div class="flex-1 min-h-0">
<ConnectionTree ref="connectionTreeRef" @open-settings="(initialTab) => emit('open-settings', initialTab)" />
<ConnectionTree ref="connectionTreeRef" @open-settings="(initialTab) => emit('open-settings', initialTab)" @add-to-ai="(node) => emit('add-to-ai', node)" />
</div>
</div>
<div class="panel-resize-handle panel-resize-handle--right" @mousedown="emit('startResize', $event)" />
@@ -97,6 +97,7 @@ const sidebarContextMenuRef = ref<{ close: () => void } | null>(null);
const sidebarContextMenuItems = ref<ContextMenuItem[]>([]);
const emit = defineEmits<{
"open-settings": [initialTab: string];
"add-to-ai": [node: TreeNode];
}>();
const sidebarContextMenuTarget = ref<SidebarActionTarget | null>(null);
@@ -2159,6 +2160,7 @@ defineExpose({ focusSearch, createNewGroup, collapseAllTreeNodes });
@open-visible-schemas="openSidebarVisibleSchemas"
@open-visible-nacos-namespaces="openSidebarVisibleNacosNamespaces"
@open-table-name-filters="openSidebarTableNameFilters"
@add-to-ai="(node) => emit('add-to-ai', node)"
@request-group-rename="startRenamingCreatedGroup"
@request-saved-sql-rename="startRenamingSavedSqlNode"
@open-danger-dialog="openSidebarDangerDialog"
@@ -57,6 +57,7 @@ import {
X,
Settings2,
GitBranch,
Sparkles,
} from "@lucide/vue";
import type { ContextMenuItem } from "@/components/ui/CustomContextMenu.vue";
import { CONNECTION_ATTEMPT_CANCELLED_MESSAGE, useConnectionStore } from "@/stores/connectionStore";
@@ -92,6 +93,7 @@ import {
supportsDatabaseSearch,
supportsConnectionDatabaseBrowser,
supportsConnectionQueryActions,
supportsAiAssistantContext,
supportsFieldLineage,
supportsObjectBrowserTreeNode,
supportsSchemaDiagram,
@@ -374,6 +376,7 @@ const emit = defineEmits<{
"open-visible-schemas": [node: TreeNode];
"open-visible-nacos-namespaces": [node: TreeNode];
"open-table-name-filters": [node: TreeNode];
"add-to-ai": [node: TreeNode];
"open-danger-dialog": [request: SidebarDangerDialogRequest];
"open-dialog-controller": [controller: Record<string, any> | null];
"open-install-extension": [node: TreeNode];
@@ -4581,8 +4584,12 @@ function buildConnectionSidebarMenu(context: SidebarMenuFactoryContext): boolean
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
items.push({ label: "", separator: true });
const supportsQueryActions = supportsConnectionQueryActions(currentDatabaseType());
const supportsAiContext = supportsAiAssistantContext(currentDatabaseType());
if (supportsQueryActions) {
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
if (supportsAiContext) {
items.push({ label: t("contextMenu.addToAi"), action: () => emit("add-to-ai", node), icon: Sparkles });
}
}
const connectionWorkspace = node.connectionId ? driverProfileDatabaseWorkspace(connectionStore.getConfig(node.connectionId)?.driver_profile) : undefined;
if (connectionWorkspace?.entryScopes.includes("connection")) {
@@ -4777,6 +4784,9 @@ function buildDatabaseSidebarMenu(context: SidebarMenuFactoryContext): boolean {
}
if (supportsConnectionQueryActions(currentDatabaseType())) {
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
if (node.type === "database" && supportsAiAssistantContext(currentDatabaseType())) {
items.push({ label: t("contextMenu.addToAi"), action: () => emit("add-to-ai", node), icon: Sparkles });
}
const sqlHistoryMenu = savedSqlHistorySubmenu();
if (sqlHistoryMenu) items.push(sqlHistoryMenu);
}
@@ -5072,6 +5082,9 @@ function buildObjectSidebarMenu(context: SidebarMenuFactoryContext): boolean {
items.push({ label: t("contextMenu.viewData"), action: openDataImmediately, icon: TableProperties });
items.push({ label: t("contextMenu.openInNewDataTab"), action: openDataInNewTabImmediately, icon: CopyPlus });
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
if (supportsAiAssistantContext(currentDatabaseType())) {
items.push({ label: t("contextMenu.addToAi"), action: () => emit("add-to-ai", node), icon: Sparkles });
}
items.push({ label: "", separator: true });
items.push(exportDataSubmenu(false));
items.push({ label: t("contextMenu.refreshChildren"), action: refresh, icon: RefreshCw, shortcut: shortcutRefresh });
@@ -5080,6 +5093,9 @@ function buildObjectSidebarMenu(context: SidebarMenuFactoryContext): boolean {
const destructiveActions: ContextMenuItem[] = [];
items.push(copyNameMenuItem());
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
if (node.type === "table" && supportsAiAssistantContext(currentDatabaseType())) {
items.push({ label: t("contextMenu.addToAi"), action: () => emit("add-to-ai", node), icon: Sparkles });
}
items.push({ label: "", separator: true });
items.push({ label: t("contextMenu.viewData"), action: openDataImmediately, icon: TableProperties });
items.push({