feat: pin tabs and tree nodes (#17)
Co-authored-by: SuLe <sule@SuLedeMacBook-Air.local>
This commit is contained in:
1 parent
e3b8f57326
commit
48f56f15b1
9 files changed
+179
-28
No files matched your search
+16
-4
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { DatabaseZap, FilePlus2, Play, Loader2, X, Globe, Moon, Sun, Upload, Download, Plus, History, Server, Table2, Database, Search, ShieldCheck, Sparkles } from "lucide-vue-next";
|
||||
import { DatabaseZap, FilePlus2, Play, Loader2, X, Globe, Moon, Sun, Upload, Download, Plus, History, Server, Table2, Database, Search, ShieldCheck, Sparkles, Pin } from "lucide-vue-next";
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import "splitpanes/dist/splitpanes.css";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -591,14 +591,26 @@ async function setupFileDrop() {
|
||||
<div
|
||||
v-for="tab in queryStore.tabs"
|
||||
:key="tab.id"
|
||||
class="flex items-center gap-1 px-3 h-full text-xs cursor-pointer border-r hover:bg-accent transition-colors whitespace-nowrap"
|
||||
class="group flex min-w-0 items-center gap-1 px-3 h-full text-xs cursor-pointer border-r hover:bg-accent transition-colors whitespace-nowrap"
|
||||
:class="{ 'bg-background font-medium': tab.id === queryStore.activeTabId }"
|
||||
@click="queryStore.activeTabId = tab.id"
|
||||
>
|
||||
<span v-if="connectionColor(tab.connectionId)" class="h-4 w-1 rounded-full shrink-0" :style="{ backgroundColor: connectionColor(tab.connectionId) }" />
|
||||
<span>{{ tabDisplayTitle(tab) }}</span>
|
||||
<span class="min-w-0 truncate">{{ tabDisplayTitle(tab) }}</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<button
|
||||
class="ml-1 rounded p-0.5 text-muted-foreground hover:bg-muted-foreground/20 hover:text-foreground focus:opacity-100"
|
||||
:class="tab.pinned ? 'opacity-100 text-primary' : 'opacity-0 group-hover:opacity-100'"
|
||||
@click.stop="queryStore.togglePinnedTab(tab.id)"
|
||||
>
|
||||
<Pin class="h-3 w-3" :class="{ 'fill-current': tab.pinned }" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{{ tab.pinned ? t('contextMenu.unpin') : t('contextMenu.pin') }}</TooltipContent>
|
||||
</Tooltip>
|
||||
<button
|
||||
class="ml-1 rounded hover:bg-muted-foreground/20 p-0.5"
|
||||
class="rounded hover:bg-muted-foreground/20 p-0.5"
|
||||
@click.stop="queryStore.closeTab(tab.id)"
|
||||
>
|
||||
<X class="h-3 w-3" />
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Database, Table, Columns3, Eye, ChevronRight, ChevronDown,
|
||||
Loader2, FolderOpen, Trash2, TerminalSquare, RefreshCw,
|
||||
Copy, TableProperties, Key, Link, Zap, ListTree, Pencil, Plug, Unplug,
|
||||
Pin,
|
||||
} from "lucide-vue-next";
|
||||
import {
|
||||
ContextMenu, ContextMenuContent, ContextMenuItem,
|
||||
@@ -77,6 +78,15 @@ function getIconInfo(node: TreeNode): { icon: any; colorClass: string } | null {
|
||||
|
||||
const leafTypes: Set<TreeNodeType> = new Set(["column", "index", "fkey", "trigger", "redis-db"]);
|
||||
const groupTypes: Set<TreeNodeType> = new Set(["group-columns", "group-indexes", "group-fkeys", "group-triggers"]);
|
||||
const pinnableTypes: Set<TreeNodeType> = new Set([
|
||||
"database",
|
||||
"schema",
|
||||
"table",
|
||||
"view",
|
||||
"redis-db",
|
||||
"mongo-db",
|
||||
"mongo-collection",
|
||||
]);
|
||||
|
||||
function isGroupLabel(node: TreeNode): boolean {
|
||||
return groupTypes.has(node.type);
|
||||
@@ -220,6 +230,8 @@ function disconnectConnection() {
|
||||
}
|
||||
|
||||
const canExpand = !leafTypes.has(props.node.type);
|
||||
const canPin = computed(() => pinnableTypes.has(props.node.type));
|
||||
const isPinned = computed(() => props.node.pinned || connectionStore.isTreeNodePinned(props.node.id));
|
||||
const paddingLeft = `${props.depth * 16 + 8}px`;
|
||||
const isConnected = computed(() =>
|
||||
props.node.type === "connection" && !!props.node.connectionId && connectionStore.connectedIds.has(props.node.connectionId)
|
||||
@@ -249,6 +261,10 @@ const remainingCount = computed(() =>
|
||||
(props.node.children?.length ?? 0) - displayLimit.value
|
||||
);
|
||||
|
||||
function togglePin() {
|
||||
connectionStore.toggleTreeNodePin(props.node.id);
|
||||
}
|
||||
|
||||
async function showMore() {
|
||||
if ((props.node.children?.length ?? 0) > displayLimit.value) {
|
||||
displayLimit.value += CHILDREN_PAGE_SIZE;
|
||||
@@ -261,7 +277,7 @@ async function showMore() {
|
||||
<ContextMenuTrigger as-child>
|
||||
<div>
|
||||
<div
|
||||
class="flex items-center gap-1.5 py-1 px-2 rounded-sm cursor-pointer hover:bg-accent transition-colors"
|
||||
class="group flex min-w-0 items-center gap-1.5 py-1 px-2 rounded-sm cursor-pointer hover:bg-accent transition-colors"
|
||||
:style="{ paddingLeft }"
|
||||
@click="onClick"
|
||||
>
|
||||
@@ -274,8 +290,17 @@ async function showMore() {
|
||||
<DatabaseIcon v-if="node.type === 'connection'" :db-type="connectionIconType(node.connectionId)" class="w-3.5 h-3.5 shrink-0" />
|
||||
<component v-else :is="getIconInfo(node)?.icon || Database" class="w-3.5 h-3.5 shrink-0" :class="getIconInfo(node)?.colorClass" />
|
||||
<span v-if="node.type === 'connection' && connectionColor" class="h-3 w-1.5 rounded-full shrink-0" :style="{ backgroundColor: connectionColor }" />
|
||||
<span class="truncate">{{ isGroupLabel(node) ? t(node.label) : node.label }}</span>
|
||||
<span class="min-w-0 flex-1 truncate">{{ isGroupLabel(node) ? t(node.label) : node.label }}</span>
|
||||
<span v-if="node.type === 'connection' && node.connectionId && connectionStore.connectedIds.has(node.connectionId)" class="w-1.5 h-1.5 rounded-full bg-green-500 shrink-0" />
|
||||
<button
|
||||
v-if="canPin"
|
||||
class="rounded p-0.5 text-muted-foreground hover:bg-muted-foreground/15 hover:text-foreground focus:opacity-100"
|
||||
:class="isPinned ? 'opacity-100 text-primary' : 'opacity-0 group-hover:opacity-100'"
|
||||
:title="isPinned ? t('contextMenu.unpin') : t('contextMenu.pin')"
|
||||
@click.stop="togglePin"
|
||||
>
|
||||
<Pin class="w-3 h-3" :class="{ 'fill-current': isPinned }" />
|
||||
</button>
|
||||
</div>
|
||||
<template v-if="node.isExpanded && node.children">
|
||||
<TreeItem v-for="child in visibleChildren" :key="child.id" :node="child" :depth="depth + 1" />
|
||||
@@ -293,6 +318,12 @@ async function showMore() {
|
||||
</ContextMenuTrigger>
|
||||
|
||||
<ContextMenuContent class="w-48">
|
||||
<ContextMenuItem v-if="canPin" @click="togglePin">
|
||||
<Pin class="w-3.5 h-3.5 mr-2" :class="{ 'fill-current': isPinned }" />
|
||||
{{ isPinned ? t('contextMenu.unpin') : t('contextMenu.pin') }}
|
||||
</ContextMenuItem>
|
||||
<ContextMenuSeparator v-if="canPin" />
|
||||
|
||||
<template v-if="node.type === 'connection'">
|
||||
<ContextMenuItem v-if="!isConnected" @click="toggle">
|
||||
<Plug class="w-3.5 h-3.5 mr-2" /> {{ t('contextMenu.openConnection') }}
|
||||
|
||||
@@ -153,6 +153,8 @@ export default {
|
||||
newQuery: "New Query",
|
||||
viewData: "View Data",
|
||||
refreshChildren: "Refresh",
|
||||
pin: "Pin",
|
||||
unpin: "Unpin",
|
||||
copyName: "Copy Name",
|
||||
},
|
||||
tree: {
|
||||
|
||||
@@ -151,6 +151,8 @@ export default {
|
||||
newQuery: "新建查询",
|
||||
viewData: "查看数据",
|
||||
refreshChildren: "刷新",
|
||||
pin: "置顶",
|
||||
unpin: "取消置顶",
|
||||
copyName: "复制名称",
|
||||
},
|
||||
tree: {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export function orderPinnedFirst<T>(items: T[], isPinned: (item: T) => boolean): T[] {
|
||||
const pinned: T[] = [];
|
||||
const unpinned: T[] = [];
|
||||
|
||||
for (const item of items) {
|
||||
if (isPinned(item)) pinned.push(item);
|
||||
else unpinned.push(item);
|
||||
}
|
||||
|
||||
return [...pinned, ...unpinned];
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import type { ConnectionConfig, TreeNode } from "@/types/database";
|
||||
import { orderPinnedFirst } from "@/lib/pinnedItems";
|
||||
import * as api from "@/lib/tauri";
|
||||
|
||||
const PINNED_TREE_NODES_STORAGE_KEY = "dbx-pinned-tree-nodes";
|
||||
|
||||
export const useConnectionStore = defineStore("connection", () => {
|
||||
const connections = ref<ConnectionConfig[]>([]);
|
||||
const activeConnectionId = ref<string | null>(null);
|
||||
const treeNodes = ref<TreeNode[]>([]);
|
||||
const pinnedTreeNodeIds = ref<Set<string>>(loadPinnedTreeNodeIds());
|
||||
const connectedIds = ref<Set<string>>(new Set());
|
||||
const editingConnectionId = ref<string | null>(null);
|
||||
|
||||
@@ -42,6 +46,64 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
};
|
||||
}
|
||||
|
||||
function loadPinnedTreeNodeIds(): Set<string> {
|
||||
try {
|
||||
if (typeof localStorage === "undefined") return new Set();
|
||||
const saved = localStorage.getItem(PINNED_TREE_NODES_STORAGE_KEY);
|
||||
const ids = saved ? JSON.parse(saved) : [];
|
||||
return new Set(Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : []);
|
||||
} catch {
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
|
||||
function persistPinnedTreeNodeIds() {
|
||||
if (typeof localStorage === "undefined") return;
|
||||
localStorage.setItem(PINNED_TREE_NODES_STORAGE_KEY, JSON.stringify([...pinnedTreeNodeIds.value]));
|
||||
}
|
||||
|
||||
function isTreeNodePinned(id: string): boolean {
|
||||
return pinnedTreeNodeIds.value.has(id);
|
||||
}
|
||||
|
||||
function pinTreeNode(node: TreeNode): TreeNode {
|
||||
node.pinned = isTreeNodePinned(node.id);
|
||||
return node;
|
||||
}
|
||||
|
||||
function setChildren(parent: TreeNode, children: TreeNode[]) {
|
||||
parent.children = orderPinnedFirst(children.map(pinTreeNode), (node) => !!node.pinned);
|
||||
}
|
||||
|
||||
function findParentNode(nodes: TreeNode[], id: string, parent: TreeNode | null = null): TreeNode | null {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return parent;
|
||||
if (node.children) {
|
||||
const found = findParentNode(node.children, id, node);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function toggleTreeNodePin(id: string) {
|
||||
const next = new Set(pinnedTreeNodeIds.value);
|
||||
if (next.has(id)) next.delete(id);
|
||||
else next.add(id);
|
||||
pinnedTreeNodeIds.value = next;
|
||||
persistPinnedTreeNodeIds();
|
||||
|
||||
const node = findNode(treeNodes.value, id);
|
||||
if (node) node.pinned = next.has(id);
|
||||
|
||||
const parent = findParentNode(treeNodes.value, id);
|
||||
if (parent?.children) {
|
||||
parent.children = orderPinnedFirst(parent.children, (child) => !!child.pinned);
|
||||
} else {
|
||||
treeNodes.value = orderPinnedFirst(treeNodes.value, (child) => !!child.pinned);
|
||||
}
|
||||
}
|
||||
|
||||
function addConnection(config: ConnectionConfig) {
|
||||
connections.value.push(normalizeConnection(config));
|
||||
persistConnections();
|
||||
@@ -123,7 +185,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
await ensureConnected(connectionId);
|
||||
const databases = await api.listDatabases(connectionId);
|
||||
node.children = databases.map((db) => ({
|
||||
setChildren(node, databases.map((db) => ({
|
||||
id: `${connectionId}:${db.name}`,
|
||||
label: db.name,
|
||||
type: "database" as const,
|
||||
@@ -131,7 +193,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database: db.name,
|
||||
isExpanded: false,
|
||||
children: [],
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -146,7 +208,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
await ensureConnected(connectionId);
|
||||
const dbs = await api.redisListDatabases(connectionId);
|
||||
node.children = dbs.map((db) => ({
|
||||
setChildren(node, dbs.map((db) => ({
|
||||
id: `${connectionId}:db${db}`,
|
||||
label: `db${db}`,
|
||||
type: "redis-db" as const,
|
||||
@@ -154,7 +216,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database: String(db),
|
||||
isExpanded: false,
|
||||
children: [],
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -169,7 +231,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
await ensureConnected(connectionId);
|
||||
const dbs = await api.mongoListDatabases(connectionId);
|
||||
node.children = dbs.map((db) => ({
|
||||
setChildren(node, dbs.map((db) => ({
|
||||
id: `${connectionId}:${db}`,
|
||||
label: db,
|
||||
type: "mongo-db" as const,
|
||||
@@ -177,7 +239,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database: db,
|
||||
isExpanded: false,
|
||||
children: [],
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -192,14 +254,14 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
node.isLoading = true;
|
||||
try {
|
||||
const collections = await api.mongoListCollections(connectionId, database);
|
||||
node.children = collections.map((col) => ({
|
||||
setChildren(node, collections.map((col) => ({
|
||||
id: `${nodeId}:${col}`,
|
||||
label: col,
|
||||
type: "mongo-collection" as const,
|
||||
connectionId,
|
||||
database,
|
||||
isExpanded: false,
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -214,7 +276,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
node.isLoading = true;
|
||||
try {
|
||||
const schemas = await api.listSchemas(connectionId, database);
|
||||
node.children = schemas.map((s) => ({
|
||||
setChildren(node, schemas.map((s) => ({
|
||||
id: `${connectionId}:${database}:${s}`,
|
||||
label: s,
|
||||
type: "schema" as const,
|
||||
@@ -223,7 +285,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
schema: s,
|
||||
isExpanded: false,
|
||||
children: [],
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -241,7 +303,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
const querySchema = schema || database;
|
||||
const tables = await api.listTables(connectionId, database, querySchema);
|
||||
node.children = tables.map((t) => ({
|
||||
setChildren(node, tables.map((t) => ({
|
||||
id: `${nodeId}:${t.name}`,
|
||||
label: t.name,
|
||||
type: (t.table_type === "VIEW" ? "view" : "table") as "view" | "table",
|
||||
@@ -250,7 +312,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
schema,
|
||||
isExpanded: false,
|
||||
children: [],
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -264,12 +326,12 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
const node = findNode(treeNodes.value, parentId);
|
||||
if (!node) return;
|
||||
|
||||
node.children = [
|
||||
setChildren(node, [
|
||||
{ id: `${parentId}:__columns`, label: "tree.columns", type: "group-columns", connectionId, database, schema, tableName: table, isExpanded: false, children: [] },
|
||||
{ id: `${parentId}:__indexes`, label: "tree.indexes", type: "group-indexes", connectionId, database, schema, tableName: table, isExpanded: false, children: [] },
|
||||
{ id: `${parentId}:__fkeys`, label: "tree.foreignKeys", type: "group-fkeys", connectionId, database, schema, tableName: table, isExpanded: false, children: [] },
|
||||
{ id: `${parentId}:__triggers`, label: "tree.triggers", type: "group-triggers", connectionId, database, schema, tableName: table, isExpanded: false, children: [] },
|
||||
];
|
||||
]);
|
||||
node.isExpanded = true;
|
||||
}
|
||||
|
||||
@@ -284,7 +346,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
const querySchema = schema || database;
|
||||
const columns = await api.getColumns(connectionId, database, querySchema, table);
|
||||
node.children = columns.map((col) => ({
|
||||
setChildren(node, columns.map((col) => ({
|
||||
id: `${parentId}:${col.name}`,
|
||||
label: `${col.name} (${col.data_type})`,
|
||||
type: "column" as const,
|
||||
@@ -292,7 +354,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database,
|
||||
schema,
|
||||
meta: col,
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -310,7 +372,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
const querySchema = schema || database;
|
||||
const indexes = await api.listIndexes(connectionId, database, querySchema, table);
|
||||
node.children = indexes.map((idx) => ({
|
||||
setChildren(node, indexes.map((idx) => ({
|
||||
id: `${parentId}:${idx.name}`,
|
||||
label: `${idx.name} (${idx.columns.join(", ")})`,
|
||||
type: "index" as const,
|
||||
@@ -318,7 +380,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database,
|
||||
schema,
|
||||
meta: idx,
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -336,7 +398,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
const querySchema = schema || database;
|
||||
const fkeys = await api.listForeignKeys(connectionId, database, querySchema, table);
|
||||
node.children = fkeys.map((fk) => ({
|
||||
setChildren(node, fkeys.map((fk) => ({
|
||||
id: `${parentId}:${fk.name}`,
|
||||
label: `${fk.column} → ${fk.ref_table}.${fk.ref_column}`,
|
||||
type: "fkey" as const,
|
||||
@@ -344,7 +406,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database,
|
||||
schema,
|
||||
meta: fk,
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -362,7 +424,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
try {
|
||||
const querySchema = schema || database;
|
||||
const triggers = await api.listTriggers(connectionId, database, querySchema, table);
|
||||
node.children = triggers.map((tr) => ({
|
||||
setChildren(node, triggers.map((tr) => ({
|
||||
id: `${parentId}:${tr.name}`,
|
||||
label: `${tr.name} (${tr.timing} ${tr.event})`,
|
||||
type: "trigger" as const,
|
||||
@@ -370,7 +432,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
database,
|
||||
schema,
|
||||
meta: tr,
|
||||
}));
|
||||
})));
|
||||
node.isExpanded = true;
|
||||
} finally {
|
||||
node.isLoading = false;
|
||||
@@ -444,6 +506,8 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
treeNodes,
|
||||
connectedIds,
|
||||
getConfig,
|
||||
isTreeNodePinned,
|
||||
toggleTreeNodePin,
|
||||
addConnection,
|
||||
updateConnection,
|
||||
removeConnection,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import type { QueryTab } from "@/types/database";
|
||||
import { orderPinnedFirst } from "@/lib/pinnedItems";
|
||||
import * as api from "@/lib/tauri";
|
||||
|
||||
export const useQueryStore = defineStore("query", () => {
|
||||
@@ -50,6 +51,13 @@ export const useQueryStore = defineStore("query", () => {
|
||||
if (tab) tab.sql = sql;
|
||||
}
|
||||
|
||||
function togglePinnedTab(id: string) {
|
||||
const tab = tabs.value.find((t) => t.id === id);
|
||||
if (!tab) return;
|
||||
tab.pinned = !tab.pinned;
|
||||
tabs.value = orderPinnedFirst(tabs.value, (item) => !!item.pinned);
|
||||
}
|
||||
|
||||
function updateDatabase(id: string, database: string) {
|
||||
const tab = tabs.value.find((t) => t.id === id);
|
||||
if (!tab || tab.database === database) return;
|
||||
@@ -116,6 +124,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
createTab,
|
||||
closeTab,
|
||||
updateSql,
|
||||
togglePinnedTab,
|
||||
updateDatabase,
|
||||
updateConnection,
|
||||
setTableMeta,
|
||||
|
||||
@@ -82,6 +82,7 @@ export interface TreeNode {
|
||||
children?: TreeNode[];
|
||||
isLoading?: boolean;
|
||||
isExpanded?: boolean;
|
||||
pinned?: boolean;
|
||||
connectionId?: string;
|
||||
database?: string;
|
||||
schema?: string;
|
||||
@@ -96,6 +97,7 @@ export interface QueryTab {
|
||||
database: string;
|
||||
sql: string;
|
||||
lastExecutedSql?: string;
|
||||
pinned?: boolean;
|
||||
result?: QueryResult;
|
||||
isExecuting: boolean;
|
||||
mode: "data" | "query" | "redis" | "mongo";
|
||||
|
||||
Reference in new issue
Block a user