Initial open-source release

Claude-Session: https://claude.ai/code/session_01B65qLEKnkeYTGdbwhLpjCu
Цей коміт міститься в:
l0ng-ai committed 2026-10-22 18:55:10 +08:00
коміт 9a2025b76e
2034 files changed
+243381

No files matched your search

+155
Переглянути файл
@@ -0,0 +1,155 @@
/** Menus of the Database tool window, laid out like IDEA's (New ▸, Copy/Paste ▸, SQL Scripts ▸ …). */
import type { DbDriver } from "@/platform/ipc";
import type { MenuItem } from "@/ui";
import { driverIcon, fileSourceName } from "./model";
import { nodeTarget, objectOf, tableOf, type DbNode } from "./nodes";
import { SCRIPT_LABELS, canDrop, canRename, type ScriptKind } from "./objects";
import { connectedTargets, openDialog, openSourceDialog, sourceById, useDatabase } from "./store";
const act = (action: string, node: DbNode | null, extra: Partial<{ label: string; disabled: boolean; icon: string }> = {}): MenuItem => ({
kind: "action",
action,
arg: node ?? undefined,
...extra,
});
const sep: MenuItem = { kind: "separator" };
async function sqliteFromFile() {
const { open } = await import("@tauri-apps/plugin-dialog");
const file = await open({ multiple: false, directory: false, title: "SQLite Database File" });
if (typeof file === "string") openSourceDialog(null, { driver: "sqlite", file, name: fileSourceName(file) });
}
/** New ▸ Data Source ▸ — the drivers the backend speaks. */
function dataSourceItems(): MenuItem[] {
const preset = (label: string, driver: DbDriver): MenuItem => ({
label,
icon: driverIcon(driver),
run: () => openSourceDialog(null, { driver }),
});
return [
{
label: "Data Source",
icon: "database",
submenu: [preset("MariaDB", "mysql"), preset("MySQL", "mysql"), preset("PostgreSQL", "postgres"), preset("SQLite", "sqlite")],
},
{ label: "Data Source from File…", icon: "folder", run: () => void sqliteFromFile() },
{ label: "Data Source from URL…", icon: "external-link", run: () => openDialog({ kind: "sourceUrl" }) },
];
}
/** The toolbar's "+" menu (IDEA: New). */
export function newMenu(): MenuItem[] {
return [{ kind: "action", action: "db.openConsole", label: "Query Console" }, sep, ...dataSourceItems()];
}
function sqlScripts(n: DbNode, table: boolean): MenuItem {
const scripts: MenuItem[] = table
? (Object.keys(SCRIPT_LABELS) as ScriptKind[]).map((kind) => ({
kind: "action",
action: "db.sqlScript",
arg: { node: n, kind },
label: SCRIPT_LABELS[kind],
}))
: [act("db.runSqlScript", n)];
return { label: "SQL Scripts", submenu: [act("db.ddlToClipboard", n), act("db.ddlToConsole", n), sep, ...scripts] };
}
/** Context menu of a tree node (`null`: the empty area). */
export function nodeMenu(n: DbNode | null): MenuItem[] {
if (!n) return [{ label: "New", icon: "plus", submenu: newMenu() }];
const id = nodeTarget(n) ?? "";
const driver = sourceById(id)?.driver ?? "sqlite";
const o = objectOf(n);
const t = tableOf(n);
const newConsole = act("db.openConsole", n, { label: "Query Console", icon: "db-console" });
const refresh = act("db.refresh", n);
const quickDoc = act("db.quickDoc", n);
const rename = act("db.rename", n, { disabled: !(n.kind === "source" || (o && canRename(driver, o))) });
const drop = act("db.drop", n, { disabled: !o || !canDrop(driver, o) });
const copyRef = act("db.copyReference", n);
const navigation = (extra: MenuItem[] = []): MenuItem => ({ label: "Navigation", submenu: [...extra, act("db.jumpToConsole", n)] });
switch (n.kind) {
case "source": {
const connected = connectedTargets(n.source.id ?? "").length > 0;
return [
act("db.properties", n, { label: "Properties" }),
sep,
{ label: "New", icon: "plus", submenu: [newConsole, sep, ...dataSourceItems()] },
{ label: "Copy/Paste", submenu: [copyRef, act("db.duplicateSource", n)] },
refresh,
act("db.disconnect", n, { disabled: !connected }),
sep,
rename,
sep,
quickDoc,
navigation(),
sep,
sqlScripts(n, false),
sep,
act("db.drop", n, { label: "Remove Data Source…" }),
];
}
case "database":
return [
{ label: "New", icon: "plus", submenu: [newConsole] },
{ label: "Copy/Paste", submenu: [copyRef] },
refresh,
act("db.disconnect", n, { disabled: !useDatabase.getState().connected.has(id) || n.current }),
sep,
navigation(),
sqlScripts(n, false),
];
case "schema":
case "folder":
return [
{ label: "New", icon: "plus", submenu: [newConsole, sep, act("db.newTable", n)] },
...(n.kind === "schema" ? [{ label: "Copy/Paste", submenu: [copyRef] }] : []),
refresh,
sep,
...(n.kind === "schema" ? [quickDoc] : []),
navigation(),
sep,
sqlScripts(n, false),
...(n.kind === "schema" ? [sep, drop] : []),
];
case "table":
case "column": {
const view = !!t?.view;
const column = n.kind === "column";
return [
{ label: "New", icon: "plus", submenu: [newConsole, sep, act("db.newColumn", n, { disabled: view }), act("db.newTable", n)] },
act("db.modifyTable", n, { label: column ? "Modify Column…" : "Modify Table…", disabled: view }),
{ label: "Copy/Paste", submenu: [copyRef] },
act("db.editData", n),
refresh,
sep,
rename,
sep,
quickDoc,
navigation([act("db.goToDdl", n)]),
sep,
sqlScripts(n, true),
...(n.kind === "table" && !view ? [{ label: "Object Actions", submenu: [act("db.truncate", n)] }] : []),
sep,
drop,
];
}
case "index":
return [{ label: "Copy/Paste", submenu: [copyRef] }, refresh, sep, rename, sep, navigation([act("db.goToDdl", n)]), sep, drop];
case "foreignKey":
return [refresh, sep, { label: "Navigation", submenu: [act("db.goToReferencedTable", n), act("db.goToDdl", n)] }];
case "group":
case "key":
return [
...(n.kind === "group" && n.group === "columns" ? [{ label: "New", icon: "plus", submenu: [act("db.newColumn", n)] }] : []),
refresh,
sep,
{ label: "Navigation", submenu: [act("db.goToDdl", n)] },
];
default:
return [];
}
}