From e0448ac87e8a7d22351f51b83d54de8afd6c87bd Mon Sep 17 00:00:00 2001 From: kirito <65323513+kir1ito@users.noreply.github.com> Date: Tue, 18 Aug 2026 23:05:17 +0800 Subject: [PATCH] feat(data-grid): add customizable data type colors --- apps/desktop/src/App.vue | 23 ++ .../editor/EditorSettingsDialog.vue | 41 +++- apps/desktop/src/components/grid/DataGrid.vue | 10 +- .../grid/DataGridTypeColorSchemeDialog.vue | 230 ++++++++++++++++++ .../src/components/ui/ColorSpectrumPicker.vue | 168 +++++++++++++ apps/desktop/src/i18n/locales/en.ts | 25 ++ apps/desktop/src/i18n/locales/es.ts | 25 ++ apps/desktop/src/i18n/locales/it.ts | 25 ++ apps/desktop/src/i18n/locales/ja.ts | 25 ++ apps/desktop/src/i18n/locales/ko.ts | 25 ++ apps/desktop/src/i18n/locales/pt-BR.ts | 25 ++ apps/desktop/src/i18n/locales/zh-CN.ts | 25 ++ apps/desktop/src/i18n/locales/zh-TW.ts | 25 ++ .../src/lib/__tests__/common/color.spec.ts | 68 ++++++ .../dataGrid/dataGridPaintTheme.spec.ts | 25 ++ .../dataGrid/dataGridTypeColorScheme.spec.ts | 111 +++++++++ apps/desktop/src/lib/common/color.ts | 75 ++++++ .../src/lib/dataGrid/dataGridPaintTheme.ts | 18 +- .../lib/dataGrid/dataGridTypeColorScheme.ts | 120 +++++++++ .../__tests__/editorSettingsDraft.spec.ts | 19 ++ .../src/lib/settings/editorSettingsDraft.ts | 2 + .../src/lib/settings/settingsSearch.ts | 1 + .../settingsStore.typeColorScheme.spec.ts | 87 +++++++ apps/desktop/src/stores/settingsStore.ts | 17 ++ .../app-tests/dataGridFontSettings.test.ts | 9 +- 25 files changed, 1209 insertions(+), 15 deletions(-) create mode 100644 apps/desktop/src/components/grid/DataGridTypeColorSchemeDialog.vue create mode 100644 apps/desktop/src/components/ui/ColorSpectrumPicker.vue create mode 100644 apps/desktop/src/lib/__tests__/common/color.spec.ts create mode 100644 apps/desktop/src/lib/__tests__/dataGrid/dataGridTypeColorScheme.spec.ts create mode 100644 apps/desktop/src/lib/dataGrid/dataGridTypeColorScheme.ts create mode 100644 apps/desktop/src/stores/__tests__/settingsStore.typeColorScheme.spec.ts diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 6d684d8397..f8edd23a47 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -101,6 +101,7 @@ import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStor import { apiUrl, webPath } from "@/lib/common/webPath"; import { shouldBlockAppNativeSelectAll } from "@/lib/common/clipboard"; 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 { DATA_GRID_TYPE_COLOR_KEYS, dataGridTypeColorCssVar, resolveActiveDataGridTypeColors } from "@/lib/dataGrid/dataGridTypeColorScheme"; import { rankSavedSqlHistory } from "@/lib/savedSql/savedSqlHistory"; import { savedSqlErrorMessage } from "@/lib/savedSql/savedSqlErrors"; import { savedSqlDefaultTargetForWrite } from "@/lib/savedSql/savedSqlExecutionTarget"; @@ -681,6 +682,20 @@ function applyDataGridFontFamily(fontFamily: string) { document.documentElement.style.setProperty(DATA_GRID_FONT_FAMILY_CSS_VAR, fontFamily || DEFAULT_DATA_GRID_FONT_FAMILY); } +// Both grid renderers read these variables, so overriding them here recolors the +// DOM classes and the canvas paint theme at once. Clearing them hands control +// back to the light/dark blocks in globals.css. +function applyDataGridTypeColors() { + if (typeof document === "undefined") return; + const style = document.documentElement.style; + const colors = resolveActiveDataGridTypeColors(settingsStore.editorSettings.dataGridTypeColorSchemes, settingsStore.editorSettings.activeDataGridTypeColorSchemeId); + for (const key of DATA_GRID_TYPE_COLOR_KEYS) { + const varName = dataGridTypeColorCssVar(key); + if (colors) style.setProperty(varName, colors[key]); + else style.removeProperty(varName); + } +} + const appUiFontFamilyStyle = computed>(() => { const fontFamily = settingsStore.editorSettings.uiFontFamily || DEFAULT_UI_FONT_FAMILY; return { @@ -750,6 +765,14 @@ watch( { immediate: true }, ); +watch( + [() => settingsStore.editorSettings.activeDataGridTypeColorSchemeId, () => settingsStore.editorSettings.dataGridTypeColorSchemes], + () => { + applyDataGridTypeColors(); + }, + { immediate: true, deep: 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 4f2df63d5e..9bf7294b1a 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -4,7 +4,7 @@ import type { Ref } from "vue"; import type { EditorView as EditorViewType } from "@codemirror/view"; import { useI18n } from "vue-i18n"; import { translateBackendError } from "@/i18n/backend-errors"; -import { AlertTriangle, ArrowLeft, Check, CheckCircle2, CircleHelp, Cloud, Copy, Download, ExternalLink, GripVertical, Loader2, Moon, PackageSearch, Pencil, Plus, RefreshCw, RotateCcw, Search, Settings, Sun, SunMoon, Terminal, Trash2, Upload, X } from "@lucide/vue"; +import { AlertTriangle, ArrowLeft, Check, CheckCircle2, CircleHelp, Cloud, Copy, Download, ExternalLink, GripVertical, Loader2, Moon, PackageSearch, Palette, Pencil, Plus, RefreshCw, RotateCcw, Search, Settings, Sun, SunMoon, Terminal, Trash2, Upload, X } from "@lucide/vue"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; @@ -58,6 +58,8 @@ import { orderAiConfigsForDisplay } from "@/lib/ai/aiConfigOrdering"; import { isAiConnectionTestConfigCurrent } from "@/lib/ai/aiConnectionTest"; import { MAX_AGENT_TURNS_DEFAULT, MAX_AGENT_TURNS_MAX, MAX_AGENT_TURNS_MIN, maxAgentTurnsOutOfRange, normalizeMaxAgentTurns } from "@/lib/ai/maxAgentTurns"; import ThemeCustomizerDialog from "./ThemeCustomizerDialog.vue"; +import DataGridTypeColorSchemeDialog from "@/components/grid/DataGridTypeColorSchemeDialog.vue"; +import { DATA_GRID_TYPE_COLOR_SCHEME_AUTO_ID, type DataGridTypeColorScheme } from "@/lib/dataGrid/dataGridTypeColorScheme"; import TunnelProfileManager from "@/components/connection/TunnelProfileManager.vue"; import DangerConfirmDialog from "./DangerConfirmDialog.vue"; import { isTauriRuntime } from "@/lib/backend/tauriRuntime"; @@ -305,7 +307,10 @@ const editUiScale = ref(settingsStore.editorSettings.uiScale); const editTheme = ref(settingsStore.editorSettings.theme); const editCustomThemes = ref([...settingsStore.editorSettings.customThemes]); const editActiveCustomThemeId = ref(settingsStore.editorSettings.activeCustomThemeId); +const editDataGridTypeColorSchemes = ref(structuredClone(settingsStore.editorSettings.dataGridTypeColorSchemes)); +const editActiveDataGridTypeColorSchemeId = ref(settingsStore.editorSettings.activeDataGridTypeColorSchemeId); const showThemeCustomizer = ref(false); +const showDataGridTypeColorScheme = ref(false); const editExecuteMode = ref(settingsStore.editorSettings.executeMode); const editShortcuts = ref(normalizeShortcutSettings(settingsStore.editorSettings.shortcuts)); function translateWithExecuteShortcut(key: string): string { @@ -533,6 +538,8 @@ function currentEditorSettingsDraft(): EditorSettingsDraft { showColumnTypesInHeader: editShowColumnTypesInHeader.value, dataGridShowTransposeFieldMetadata: editDataGridShowTransposeFieldMetadata.value, colorizeDataGridCellTypes: editColorizeDataGridCellTypes.value, + dataGridTypeColorSchemes: editDataGridTypeColorSchemes.value, + activeDataGridTypeColorSchemeId: editActiveDataGridTypeColorSchemeId.value, showIndexIndicatorsInHeader: editShowIndexIndicatorsInHeader.value, compactColumnHeaderActions: editCompactColumnHeaderActions.value, dataGridQuickEntry: editDataGridQuickEntry.value, @@ -816,6 +823,8 @@ function syncEditorSettingsDraftFromStore() { editShowColumnTypesInHeader.value = settingsStore.editorSettings.showColumnTypesInHeader; editDataGridShowTransposeFieldMetadata.value = settingsStore.editorSettings.dataGridShowTransposeFieldMetadata; editColorizeDataGridCellTypes.value = settingsStore.editorSettings.colorizeDataGridCellTypes; + editDataGridTypeColorSchemes.value = structuredClone(settingsStore.editorSettings.dataGridTypeColorSchemes); + editActiveDataGridTypeColorSchemeId.value = settingsStore.editorSettings.activeDataGridTypeColorSchemeId; editShowIndexIndicatorsInHeader.value = settingsStore.editorSettings.showIndexIndicatorsInHeader; editCompactColumnHeaderActions.value = settingsStore.editorSettings.compactColumnHeaderActions; editDataGridQuickEntry.value = settingsStore.editorSettings.dataGridQuickEntry; @@ -1098,6 +1107,8 @@ function resetDefaultsForTab(tab: SettingsCategory) { editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader; editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata; editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes; + // Back to the built-in palette, but keep the user's saved schemes available. + editActiveDataGridTypeColorSchemeId.value = DEFAULT_EDITOR_SETTINGS.activeDataGridTypeColorSchemeId; editShowIndexIndicatorsInHeader.value = DEFAULT_EDITOR_SETTINGS.showIndexIndicatorsInHeader; editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions; editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry; @@ -1172,6 +1183,8 @@ function resetAllDefaults() { editShowColumnTypesInHeader.value = DEFAULT_EDITOR_SETTINGS.showColumnTypesInHeader; editDataGridShowTransposeFieldMetadata.value = DEFAULT_EDITOR_SETTINGS.dataGridShowTransposeFieldMetadata; editColorizeDataGridCellTypes.value = DEFAULT_EDITOR_SETTINGS.colorizeDataGridCellTypes; + // Reset the selection only; saved schemes survive a full settings reset. + editActiveDataGridTypeColorSchemeId.value = DEFAULT_EDITOR_SETTINGS.activeDataGridTypeColorSchemeId; editShowIndexIndicatorsInHeader.value = DEFAULT_EDITOR_SETTINGS.showIndexIndicatorsInHeader; editCompactColumnHeaderActions.value = DEFAULT_EDITOR_SETTINGS.compactColumnHeaderActions; editDataGridQuickEntry.value = DEFAULT_EDITOR_SETTINGS.dataGridQuickEntry; @@ -1409,6 +1422,16 @@ function handleThemeSave(updatedThemes: CustomTheme[], activeId: string) { showThemeCustomizer.value = false; } +function handleDataGridTypeColorSchemeChange(schemes: DataGridTypeColorScheme[], activeId: string) { + editDataGridTypeColorSchemes.value = structuredClone(schemes); + editActiveDataGridTypeColorSchemeId.value = activeId; +} + +const activeDataGridTypeColorSchemeName = computed(() => { + if (editActiveDataGridTypeColorSchemeId.value === DATA_GRID_TYPE_COLOR_SCHEME_AUTO_ID) return t("settings.dataGridTypeColorSchemeAuto"); + return editDataGridTypeColorSchemes.value.find((scheme) => scheme.id === editActiveDataGridTypeColorSchemeId.value)?.name ?? t("settings.dataGridTypeColorSchemeAuto"); +}); + function onDisconnectTabHandlingModeChange(v: any) { if (v === "close-tabs" || v === "keep-tabs-clear-results" || v === "keep-tabs-keep-results") { editDisconnectTabHandlingMode.value = v; @@ -4960,6 +4983,21 @@ onUnmounted(() => {
{{ t("settings.dataGridDisplay") }}
+
+
+ +

+ {{ t("settings.dataGridTypeColorSchemeDescription") }} +

+
+
+ {{ activeDataGridTypeColorSchemeName }} + +
+