From a36d2f12f75040358002c34ed6d30e98e688bb2d Mon Sep 17 00:00:00 2001 From: luoianun Date: Tue, 23 Jun 2026 21:17:55 +0800 Subject: [PATCH] feat: add selectable database charset options --- .../src/components/sidebar/TreeItem.vue | 78 +++++++++++- .../ui/searchable-select/SearchableSelect.vue | 5 +- apps/desktop/src/i18n/locales/en.ts | 8 ++ apps/desktop/src/i18n/locales/it.ts | 8 ++ apps/desktop/src/i18n/locales/ja.ts | 8 ++ apps/desktop/src/i18n/locales/pt-BR.ts | 8 ++ apps/desktop/src/i18n/locales/zh-CN.ts | 8 ++ apps/desktop/src/i18n/locales/zh-TW.ts | 8 ++ .../createDatabaseCharsetOptions.spec.ts | 73 +++++++++++ .../src/lib/createDatabaseCharsetOptions.ts | 116 ++++++++++++++++++ 10 files changed, 317 insertions(+), 3 deletions(-) create mode 100644 apps/desktop/src/lib/__tests__/createDatabaseCharsetOptions.spec.ts create mode 100644 apps/desktop/src/lib/createDatabaseCharsetOptions.ts diff --git a/apps/desktop/src/components/sidebar/TreeItem.vue b/apps/desktop/src/components/sidebar/TreeItem.vue index 3c487a4c25..1da30867d6 100644 --- a/apps/desktop/src/components/sidebar/TreeItem.vue +++ b/apps/desktop/src/components/sidebar/TreeItem.vue @@ -122,8 +122,10 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from " import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; +import { SearchableSelect } from "@/components/ui/searchable-select"; import LightTooltip from "@/components/ui/LightTooltip.vue"; import { flattenTree } from "@/composables/useFlatTree"; +import { createDatabaseCollationOptionsForCharset, fallbackCreateDatabaseCharsetMetadata, nextCreateDatabaseCollation, normalizeCreateDatabaseCharset, parseCreateDatabaseCharsetMetadata } from "@/lib/createDatabaseCharsetOptions"; const { t } = useI18n(); const labelRef = ref(); @@ -1357,6 +1359,10 @@ const showCreateDatabaseDialog = ref(false); const createDatabaseName = ref(""); const createDatabaseCharset = ref("utf8mb4"); const createDatabaseCollation = ref("utf8mb4_unicode_ci"); +const fallbackCreateDatabaseCharset = fallbackCreateDatabaseCharsetMetadata(); +const createDatabaseCharsetOptions = ref(fallbackCreateDatabaseCharset.charsets); +const createDatabaseCollationsByCharset = ref>(fallbackCreateDatabaseCharset.collationsByCharset); +const createDatabaseCharsetLoading = ref(false); const showDropDatabaseConfirm = ref(false); const dropDatabaseLoading = ref(false); const showDropMongoCollectionConfirm = ref(false); @@ -1983,7 +1989,40 @@ function openCreateDatabaseDialog() { createDatabaseName.value = ""; createDatabaseCharset.value = "utf8mb4"; createDatabaseCollation.value = "utf8mb4_unicode_ci"; + createDatabaseCharsetOptions.value = fallbackCreateDatabaseCharset.charsets; + createDatabaseCollationsByCharset.value = fallbackCreateDatabaseCharset.collationsByCharset; showCreateDatabaseDialog.value = true; + void loadCreateDatabaseCharsetMetadata(); +} + +function updateCreateDatabaseCharset(value: string) { + const previousCharset = createDatabaseCharset.value; + createDatabaseCharset.value = value; + createDatabaseCollation.value = nextCreateDatabaseCollation(value, previousCharset, createDatabaseCollation.value, createDatabaseCollationsByCharset.value); +} + +async function loadCreateDatabaseCharsetMetadata() { + const node = props.node; + if (!node.connectionId || createDatabaseCharsetLoading.value) return; + createDatabaseCharsetLoading.value = true; + try { + await connectionStore.ensureConnected(node.connectionId); + const [charsetResult, collationResult] = await Promise.all([api.executeQuery(node.connectionId, "", "SHOW CHARACTER SET", undefined, undefined, { maxRows: 1000 }), api.executeQuery(node.connectionId, "", "SHOW COLLATION", undefined, undefined, { maxRows: 10000 })]); + if (!showCreateDatabaseDialog.value) return; + const metadata = parseCreateDatabaseCharsetMetadata(charsetResult, collationResult); + createDatabaseCharsetOptions.value = metadata.charsets; + createDatabaseCollationsByCharset.value = metadata.collationsByCharset; + if (!createDatabaseCharsetOptions.value.includes(createDatabaseCharset.value) && createDatabaseCharsetOptions.value.length) { + updateCreateDatabaseCharset(createDatabaseCharsetOptions.value[0]); + } else { + createDatabaseCollation.value = nextCreateDatabaseCollation(createDatabaseCharset.value, createDatabaseCharset.value, createDatabaseCollation.value, createDatabaseCollationsByCharset.value); + } + } catch { + createDatabaseCharsetOptions.value = fallbackCreateDatabaseCharset.charsets; + createDatabaseCollationsByCharset.value = fallbackCreateDatabaseCharset.collationsByCharset; + } finally { + createDatabaseCharsetLoading.value = false; + } } function ensureDuckDbFileExtension(path: string): string { @@ -3904,11 +3943,46 @@ function treeItemMenuItems(): ContextMenuItem[] {
- + + +
- + + +
diff --git a/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue b/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue index 3c779aa002..2db820fe17 100644 --- a/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue +++ b/apps/desktop/src/components/ui/searchable-select/SearchableSelect.vue @@ -3,6 +3,7 @@ import { computed, nextTick, ref, watch } from "vue"; import type { HTMLAttributes } from "vue"; import { Check, ChevronDown, Search } from "@lucide/vue"; import { Button } from "@/components/ui/button"; +import type { ButtonVariants } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { filterDatabaseOptions } from "@/lib/databaseOptionSearch"; @@ -18,6 +19,7 @@ const props = withDefaults( loadingText: string; loading?: boolean; allowCustom?: boolean; + triggerVariant?: ButtonVariants["variant"]; triggerClass?: HTMLAttributes["class"]; contentClass?: HTMLAttributes["class"]; itemClass?: HTMLAttributes["class"]; @@ -27,6 +29,7 @@ const props = withDefaults( { loading: false, allowCustom: false, + triggerVariant: "ghost", displayName: (option: string) => option, normalizeCustom: (value: string) => value, }, @@ -146,7 +149,7 @@ function handleKeydown(event: KeyboardEvent) {