feat(duckdb): create database file from connection form

This commit is contained in:
t8y2 2026-05-19 16:34:08 +08:00
parent b60063162f
commit b76efa56d6
5 changed files with 54 additions and 1 deletions

View File

@ -19,7 +19,18 @@ import { applyParsedConnectionUrl, parseConnectionUrl } from "@/lib/connectionUr
import { connectionUrlPlaceholder as getUrlPlaceholder } from "@/lib/connectionPresentation";
import { mongodbAuthFailureHint, mongoUrlParam, setMongoUrlParam } from "@/lib/mongoConnectionOptions";
import { showAgentDriverInstallHint, type AgentDriverInstallState } from "@/lib/agentDriverInstallHint";
import { ArrowLeft, ChevronRight, Copy, ExternalLink, FolderOpen, Grid3X3, Link2, List, Search } from "lucide-vue-next";
import {
ArrowLeft,
ChevronRight,
Copy,
ExternalLink,
FilePlus2,
FolderOpen,
Grid3X3,
Link2,
List,
Search,
} from "lucide-vue-next";
type DbOption = { value: string; label: string };
type DbCategory = { key: string; title: string; options: DbOption[] };
@ -770,6 +781,24 @@ async function browseDbFilePath() {
}
}
function ensureDuckDbFileExtension(path: string): string {
return /\.(duckdb|db)$/i.test(path) ? path : `${path}.duckdb`;
}
async function createDuckDbFilePath() {
if (!isTauriRuntime()) return;
const { save } = await import("@tauri-apps/plugin-dialog");
const selected = await save({
title: t("connection.createDuckDbFile"),
defaultPath: "database.duckdb",
filters: [{ name: "DuckDB", extensions: ["duckdb", "db"] }],
});
if (!selected) return;
const path = ensureDuckDbFileExtension(selected);
form.value.host = path;
}
async function browseJdbcDriverPaths() {
if (!isTauriRuntime()) return;
const { open } = await import("@tauri-apps/plugin-dialog");
@ -1138,6 +1167,19 @@ function openExternalUrl(url: string) {
</TooltipTrigger>
<TooltipContent>{{ t("connection.sshKeyPathBrowse") }}</TooltipContent>
</Tooltip>
<Tooltip v-if="isDesktop && form.db_type === 'duckdb'">
<TooltipTrigger as-child>
<Button
variant="outline"
size="icon"
class="h-9 w-9 shrink-0"
@click="createDuckDbFilePath"
>
<FilePlus2 class="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>{{ t("connection.createDuckDbFile") }}</TooltipContent>
</Tooltip>
</div>
<p v-if="supportsMemoryDatabasePath" class="text-xs text-muted-foreground">
{{ t("connection.memoryDatabasePathHint") }}

View File

@ -97,6 +97,7 @@ export default {
type: "Type",
host: "Host",
filePath: "File Path",
createDuckDbFile: "Create DuckDB file",
memoryDatabasePathHint: "Use :memory: to create an in-memory SQLite or DuckDB database.",
user: "User",
password: "Password",

View File

@ -97,6 +97,7 @@ export default {
type: "Tipo",
host: "Host",
filePath: "Ruta del archivo",
createDuckDbFile: "Crear archivo DuckDB",
user: "Usuario",
password: "Contraseña",
database: "Base de datos",

View File

@ -96,6 +96,7 @@ export default {
type: "类型",
host: "主机",
filePath: "文件路径",
createDuckDbFile: "新建 DuckDB 文件",
memoryDatabasePathHint: "输入 :memory: 可创建 SQLite 或 DuckDB 内存数据库。",
user: "用户名",
password: "密码",

View File

@ -28,3 +28,11 @@ test("connection dialog initializes edit form on first mount", () => {
test("connection dialog maps legacy Dameng configs to the DM profile", () => {
assert.match(connectionDialogSource, /if \(config\.db_type === "dameng"\) return "dm";/);
});
test("connection dialog offers DuckDB file creation from the new connection form", () => {
assert.match(connectionDialogSource, /async function createDuckDbFilePath\(\)/);
assert.match(connectionDialogSource, /const \{ save \} = await import\("@tauri-apps\/plugin-dialog"\);/);
assert.match(connectionDialogSource, /form\.value\.host = path;/);
assert.match(connectionDialogSource, /form\.db_type === 'duckdb'/);
assert.match(connectionDialogSource, /@click="createDuckDbFilePath"/);
});