From 5d1a6e63cd4eb7d0a60a728f73649acb7463d728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=88=E7=84=89=E4=B9=8B=E6=97=B6?= <74604296+ZhongYanZhiShi@users.noreply.github.com> Date: Wed, 22 Jul 2026 22:20:42 +0800 Subject: [PATCH] feat(grid): add table font shortcuts --- apps/desktop/src/App.vue | 15 ++++- .../editor/EditorSettingsDialog.vue | 56 ++++--------------- apps/desktop/src/components/grid/DataGrid.vue | 9 +-- .../grid/DataGridBulkEditDialog.vue | 2 +- .../grid/DataGridCellDetailDialog.vue | 4 +- .../grid/DataGridCellDetailPanel.vue | 4 +- .../components/grid/DataGridDetailDialogs.vue | 8 +-- .../grid/DataGridFontFamilyControl.vue | 55 ++++++++++++++++++ .../grid/DataGridMongoJsonPreview.vue | 4 +- .../src/components/layout/ContentArea.vue | 3 + .../__tests__/useDataGridCellDetail.spec.ts | 10 +++- .../src/composables/useDataGridCellDetail.ts | 2 +- apps/desktop/src/i18n/locales/en.ts | 1 + apps/desktop/src/i18n/locales/es.ts | 1 + apps/desktop/src/i18n/locales/it.ts | 1 + apps/desktop/src/i18n/locales/ja.ts | 1 + apps/desktop/src/i18n/locales/pt-BR.ts | 1 + apps/desktop/src/i18n/locales/zh-CN.ts | 1 + apps/desktop/src/i18n/locales/zh-TW.ts | 1 + apps/desktop/src/lib/app/appFonts.ts | 28 +++++++++- apps/desktop/src/lib/app/fontFamilyOptions.ts | 29 ++++++++++ .../stores/__tests__/settingsStore.spec.ts | 7 +++ apps/desktop/src/stores/settingsStore.ts | 10 ---- apps/desktop/src/styles/globals.css | 4 ++ .../app-tests/dataGridFontSettings.test.ts | 18 ++++++ 25 files changed, 198 insertions(+), 77 deletions(-) create mode 100644 apps/desktop/src/components/grid/DataGridFontFamilyControl.vue create mode 100644 apps/desktop/src/lib/app/fontFamilyOptions.ts diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index bcac82df6..c31ee5c92 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -85,7 +85,7 @@ import { countAvailableAgentDriverUpdates, type AgentDriverUpdateBadgeState } fr import type { DriverStoreFocus } from "@/lib/connection/agentDriverInstallHint"; import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage"; import { apiUrl, webPath } from "@/lib/common/webPath"; -import { APP_FONT_SANS_CSS_VAR, DEFAULT_UI_FONT_FAMILY } from "@/lib/app/appFonts"; +import { APP_FONT_SANS_CSS_VAR, DATA_GRID_FONT_FAMILY_CSS_VAR, DEFAULT_DATA_GRID_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY } from "@/lib/app/appFonts"; import { rankSavedSqlHistory } from "@/lib/savedSql/savedSqlHistory"; import { countActiveUpdateBlockingTasks } from "@/lib/app/appUpdateTaskGuard"; import { initSavedSqlEditorPositions } from "@/lib/app/savedSqlEditorPosition"; @@ -503,6 +503,11 @@ function applyUiFontFamily(fontFamily: string) { document.body.style.fontFamily = `var(${APP_FONT_SANS_CSS_VAR}, ${DEFAULT_UI_FONT_FAMILY})`; } +function applyDataGridFontFamily(fontFamily: string) { + if (typeof document === "undefined") return; + document.documentElement.style.setProperty(DATA_GRID_FONT_FAMILY_CSS_VAR, fontFamily || DEFAULT_DATA_GRID_FONT_FAMILY); +} + const appUiFontFamilyStyle = computed>(() => { const fontFamily = settingsStore.editorSettings.uiFontFamily || DEFAULT_UI_FONT_FAMILY; return { @@ -564,6 +569,14 @@ watch( { immediate: true }, ); +watch( + () => settingsStore.editorSettings.tableFontFamily, + (fontFamily) => { + applyDataGridFontFamily(fontFamily); + }, + { immediate: true }, +); + watch( [() => settingsStore.isEditorSettingsLoaded, () => settingsStore.editorSettings.toolbarItems.exclusiveRightSidebarPanels], ([loaded, exclusive]) => { diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index b94925915..e1ec1b8b1 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -21,7 +21,6 @@ import { useSettingsStore, AI_PROVIDER_PRESETS, EDITOR_THEMES, - FONT_FAMILIES, DEFAULT_EDITOR_SETTINGS, DEFAULT_DESKTOP_SETTINGS, DEFAULT_SIDEBAR_TABLE_PAGE_SIZE, @@ -64,7 +63,6 @@ import { forgetWebdavSyncSecretsPassphrase, forgetWebdavSavedPassword, getAppSupportInfo, - listSystemFonts, saveWebdavSyncSecretsPreference, saveWebdavSavedPassword, saveSnippetSavedToken, @@ -119,7 +117,8 @@ import { currentLocale, setLocale, type Locale } from "@/i18n"; import { LOCALE_OPTIONS } from "@/lib/app/localeOptions"; import { DEFAULT_WEB_DAV_AUTO_UPLOAD_INTERVAL_MINUTES, DEFAULT_WEB_DAV_REMOTE_PATH, normalizedWebDavAutoUploadInterval, writeWebDavAutoUploadFields } from "@/lib/webdav/webdavAutoUploadConfig"; import { apiUrl } from "@/lib/common/webPath"; -import { DEFAULT_UI_FONT_FAMILY, SYSTEM_UI_FONT_FAMILY } from "@/lib/app/appFonts"; +import { DEFAULT_DATA_GRID_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY, normalizeCustomFontFamilyInput, readableFontFamily, SYSTEM_UI_FONT_FAMILY } from "@/lib/app/appFonts"; +import { buildFontFamilyOptions, displayFontFamily, isPresetFontFamily, loadSystemFontNames } from "@/lib/app/fontFamilyOptions"; import { buildAppSupportInfoRows, formatAppSupportInfoForClipboard, type AppSupportInfoLabels } from "@/lib/app/supportInfo"; import { DateTimePatterns, normalizeSupportedDateTimePattern } from "@/lib/dataGrid/columnFormatter"; import { MAX_RESULT_PAGE_SIZE, MIN_RESULT_PAGE_SIZE } from "@/lib/dataGrid/paginationPageSize"; @@ -151,9 +150,6 @@ const appThemeModeOptions = computed(() => [ { value: "system" as AppThemeMode, label: t("toolbar.themeSystem"), icon: SunMoon }, ]); -let cachedSystemFonts: string[] | null = null; -let pendingSystemFonts: Promise | null = null; - const props = defineProps<{ open?: boolean; variant?: "dialog" | "page"; @@ -629,44 +625,20 @@ function confirmDeleteSnippet(snippet: SqlSnippet) { } } -const presetFontLabels = new Map(FONT_FAMILIES.map((font) => [font.value, font.label])); -const presetFontValues = new Set(FONT_FAMILIES.map((font) => font.value)); const uiFontPreviewValues = new Set([DEFAULT_UI_FONT_FAMILY, SYSTEM_UI_FONT_FAMILY]); -function cssFontFamilyForName(name: string): string { - return `'${name.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}', monospace`; -} - -function readableFontFamily(value: string): string { - const first = value.split(",")[0]?.trim() ?? value; - return first.replace(/^['"]|['"]$/g, "").replace(/\\'/g, "'"); -} - -function normalizeCustomFontFamilyInput(value: string): string { - const trimmed = value.trim(); - if (!trimmed) return ""; - if (trimmed.includes(",") || trimmed.includes("'") || trimmed.includes('"')) return trimmed; - return cssFontFamilyForName(trimmed); -} - const systemFontOptions = computed(() => { - const options = new Set(FONT_FAMILIES.map((font) => font.value)); - for (const font of systemFonts.value) options.add(cssFontFamilyForName(font)); - if (editFontFamily.value) options.add(editFontFamily.value); - if (editTableFontFamily.value) options.add(editTableFontFamily.value); - return [...options]; + return buildFontFamilyOptions(systemFonts.value, [editFontFamily.value]); }); +const tableFontOptions = computed(() => buildFontFamilyOptions(systemFonts.value, [editTableFontFamily.value], [DEFAULT_DATA_GRID_FONT_FAMILY])); + const uiFontOptions = computed(() => { const options = new Set([SYSTEM_UI_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY, ...systemFontOptions.value]); if (editUiFontFamily.value) options.add(editUiFontFamily.value); return [...options]; }); -function displayFontFamily(value: string): string { - return presetFontLabels.get(value) ?? readableFontFamily(value); -} - function displayUiFontFamily(value: string): string { if (value === SYSTEM_UI_FONT_FAMILY) return t("settings.uiFontSystemDefault"); if (value === DEFAULT_UI_FONT_FAMILY) return t("settings.uiFontAppDefault"); @@ -674,22 +646,14 @@ function displayUiFontFamily(value: string): string { } function fontOptionStyle(value: string, selectedValue = editFontFamily.value) { - return presetFontValues.has(value) || uiFontPreviewValues.has(value) || value === selectedValue ? { fontFamily: value } : undefined; + return isPresetFontFamily(value) || uiFontPreviewValues.has(value) || value === selectedValue ? { fontFamily: value } : undefined; } async function loadSystemFontOptions() { if (systemFontsLoaded.value || systemFontsLoading.value) return; systemFontsLoading.value = true; try { - if (cachedSystemFonts) { - systemFonts.value = cachedSystemFonts; - } else { - pendingSystemFonts ??= listSystemFonts().finally(() => { - pendingSystemFonts = null; - }); - cachedSystemFonts = await pendingSystemFonts; - systemFonts.value = cachedSystemFonts; - } + systemFonts.value = await loadSystemFontNames(); systemFontsLoaded.value = true; } catch { systemFonts.value = []; @@ -3571,7 +3535,7 @@ onUnmounted(cleanupPreviewEditor); settingsStore.editorSettings.theme; const editorAppAppearance = () => (isDark.value ? "dark" : "light") as import("@/lib/app/appTheme").AppThemeAppearance; const editorAppPalette = () => themePalette.value; const editorFontSize = () => settingsStore.editorSettings.fontSize; -const editorFontFamily = () => settingsStore.editorSettings.fontFamily; +const detailEditorFontFamily = () => tableFontFamily.value; const SIDE_DETAIL_EDITOR_MIN_HEIGHT = 160; const SIDE_DETAIL_EDITOR_MAX_HEIGHT = 360; const SIDE_DETAIL_EDITOR_LINE_HEIGHT = 20; @@ -3874,7 +3874,7 @@ watch(valueEditorContainer, async (el) => { appAppearance: editorAppAppearance, appPalette: editorAppPalette, fontSize: editorFontSize, - fontFamily: editorFontFamily, + fontFamily: detailEditorFontFamily, }); await valueDetailEditor.create(el, detailEditValue.value, activeCellDetail.value?.type); } else if (!el && valueDetailEditor) { @@ -4751,7 +4751,7 @@ function canvasCellContentOverflows(item: RowItem, actualColIdx: number, visible const displayText = formatCellCached(item.data[actualColIdx], actualColIdx); const editText = cellEditorTextForValue(item.data[actualColIdx], actualColIdx); if (editText.includes("\n") || editText.includes("\r") || editText.length > displayText.length) return true; - const textWidth = measureCellTextWidth(displayText, `400 13px ${settingsStore.editorSettings.fontFamily}`); + const textWidth = measureCellTextWidth(displayText, `400 ${tableFontSize.value}px ${tableFontFamily.value}`); return textWidth > Math.max(0, cellWidth - 24); } @@ -7761,7 +7761,7 @@ const gridContextMenuItems = computed(() => {
{ appAppearance: () => (isDark.value ? "dark" : "light"), appPalette: () => themePalette.value, fontSize: () => settingsStore.editorSettings.fontSize, - fontFamily: () => settingsStore.editorSettings.fontFamily, + fontFamily: () => settingsStore.editorSettings.tableFontFamily, }); await jsonPreviewEditor.create(element, props.detail?.formattedJson ?? "", "json"); } else if (!element && jsonPreviewEditor) { @@ -181,7 +181,7 @@ watch(
-
{{ detail.rawValuePreview }}
+
{{ detail.rawValuePreview }}
{{ t("grid.largeValuePreviewHint", { count: detail.rawValuePreview.length }) }}
diff --git a/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue b/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue index dba473921..04ab39ba9 100644 --- a/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue +++ b/apps/desktop/src/components/grid/DataGridCellDetailPanel.vue @@ -143,7 +143,7 @@ defineExpose({ openSearch }); />