From 5c35755219f2f25620008dff1a25c02cc38fe95a Mon Sep 17 00:00:00 2001 From: zipg Date: Sun, 5 Jul 2026 00:34:47 +0800 Subject: [PATCH] =?UTF-8?q?Feature=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=A4=9A?= =?UTF-8?q?=E5=A5=97=E5=BA=94=E7=94=A8=E9=85=8D=E8=89=B2=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=20(#2559)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: staff --- apps/desktop/src/App.vue | 4 +- .../components/diff/SchemaDiffDeployStep.vue | 4 +- .../editor/EditorSettingsDialog.vue | 159 ++- .../src/components/editor/QueryEditor.vue | 10 +- apps/desktop/src/components/grid/DataGrid.vue | 9 +- .../src/components/layout/AppSidebar.vue | 4 +- .../src/components/layout/AppTabBar.vue | 2 +- .../src/components/layout/AppToolbar.vue | 13 +- .../src/components/layout/EditorToolbar.vue | 2 +- .../components/nacos/NacosAdminConsole.vue | 8 +- .../src/components/objects/DdlViewDialog.vue | 4 +- .../src/components/sidebar/ConnectionTree.vue | 2 +- .../src/composables/useCellDetailEditor.ts | 9 +- apps/desktop/src/composables/useTheme.ts | 33 +- apps/desktop/src/i18n/locales/en.ts | 15 + apps/desktop/src/i18n/locales/es.ts | 15 + apps/desktop/src/i18n/locales/it.ts | 15 + apps/desktop/src/i18n/locales/ja.ts | 15 + apps/desktop/src/i18n/locales/pt-BR.ts | 15 + apps/desktop/src/i18n/locales/zh-CN.ts | 15 + apps/desktop/src/i18n/locales/zh-TW.ts | 15 + .../src/lib/__tests__/editorThemes.spec.ts | 45 + apps/desktop/src/lib/app/appTheme.ts | 50 +- apps/desktop/src/lib/editor/editorThemes.ts | 414 ++++++- .../stores/__tests__/settingsStore.spec.ts | 6 + apps/desktop/src/stores/settingsStore.ts | 31 +- apps/desktop/src/styles/globals.css | 1069 +++++++++++++++++ 27 files changed, 1884 insertions(+), 99 deletions(-) create mode 100644 apps/desktop/src/lib/__tests__/editorThemes.spec.ts diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 3961293c2..f3dc10aaf 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -122,7 +122,7 @@ const connectionDialogPrefill = ref(null); const connectionDialogInitialTab = ref(undefined); const settingsPageTabOpen = ref(false); const settingsPageActive = ref(false); -const settingsInitialTab = ref("editor"); +const settingsInitialTab = ref("appearance"); const settingsInitialSection = ref(undefined); const showQueryEditorDdlDialog = ref(false); const driverStoreTabOpen = ref(false); @@ -265,7 +265,7 @@ const appVersion = ref(""); const isClassicLayout = computed(() => settingsStore.editorSettings.appLayout === "classic"); const updateNotificationsEnabled = computed(() => settingsStore.editorSettings.updateNotificationsEnabled); -function openSettings(initialTab = "editor", initialSection?: string) { +function openSettings(initialTab = "appearance", initialSection?: string) { settingsInitialTab.value = initialTab; settingsInitialSection.value = initialSection; if (!settingsPageActive.value) { diff --git a/apps/desktop/src/components/diff/SchemaDiffDeployStep.vue b/apps/desktop/src/components/diff/SchemaDiffDeployStep.vue index 7af0d54bc..26d3841a9 100644 --- a/apps/desktop/src/components/diff/SchemaDiffDeployStep.vue +++ b/apps/desktop/src/components/diff/SchemaDiffDeployStep.vue @@ -15,7 +15,7 @@ import { ArrowLeft, Copy, Download, Play, Loader2, PlusCircle, XCircle, ArrowRig const { t } = useI18n(); const { toast } = useToast(); const settingsStore = useSettingsStore(); -const { isDark } = useTheme(); +const { isDark, themePalette } = useTheme(); const props = defineProps<{ deploySql: string; @@ -120,7 +120,7 @@ async function initEditor() { const fontSize = settingsStore.editorSettings.fontSize; const fontFamily = settingsStore.editorSettings.fontFamily; - const themeExt = await loadEditorTheme(editorTheme, appAppearance); + const themeExt = await loadEditorTheme(editorTheme, appAppearance, undefined, themePalette.value); const fontExt = editorFontTheme(EditorView, fontSize, fontFamily, { fixedHeight: true, scrollable: true }); const dialect = createDbxCodeMirrorSqlDialect(langSql, "postgres"); diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index 0b319e015..e953e07fc 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -81,7 +81,7 @@ import { DEFAULT_SQL_SNIPPETS } from "@/lib/sql/sqlCompletion"; import AiProviderLogo from "@/components/icons/AiProviderLogo.vue"; import AppLogo from "@/components/icons/AppLogo.vue"; import SqlFormatterSettingsPanel from "./SqlFormatterSettingsPanel.vue"; -import type { AppThemeAppearance } from "@/lib/app/appTheme"; +import { APP_THEME_PALETTES, type AppThemeAppearance, type AppThemeMode, type AppThemePalette } from "@/lib/app/appTheme"; import { useConnectionStore } from "@/stores/connectionStore"; import { useSavedSqlStore } from "@/stores/savedSqlStore"; import { currentLocale, setLocale, type Locale } from "@/i18n"; @@ -95,7 +95,23 @@ const { toast } = useToast(); const settingsStore = useSettingsStore(); const connectionStore = useConnectionStore(); const savedSqlStore = useSavedSqlStore(); -const { isDark, themeMode, setThemeMode } = useTheme(); +const { isDark, themeMode, themePalette, setThemeMode, setThemePalette } = useTheme(); + +const appThemePaletteOptions = computed( + (): Array<{ value: AppThemePalette; label: string; previewColor: string }> => + APP_THEME_PALETTES.map((palette) => ({ + value: palette.value, + label: t(palette.labelKey), + previewColor: palette.previewColor, + })), +); +const selectedThemePaletteOption = computed(() => appThemePaletteOptions.value.find((option) => option.value === themePalette.value) ?? appThemePaletteOptions.value[0]); +const selectedLocaleOption = computed(() => LOCALE_OPTIONS.find((locale) => locale.value === currentLocale()) ?? LOCALE_OPTIONS[0]); +const appThemeModeOptions = computed(() => [ + { value: "light" as AppThemeMode, label: t("toolbar.themeLight"), icon: Sun }, + { value: "dark" as AppThemeMode, label: t("toolbar.themeDark"), icon: Moon }, + { value: "system" as AppThemeMode, label: t("toolbar.themeSystem"), icon: SunMoon }, +]); let cachedSystemFonts: string[] | null = null; let pendingSystemFonts: Promise | null = null; @@ -274,7 +290,9 @@ const systemFontsLoaded = ref(false); const uiScaleOptions = [0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2]; const fontSearchTriggerClass = "h-8 w-full max-w-none justify-between gap-1.5 rounded-[6px] border border-input bg-transparent py-2 pl-2.5 pr-2 text-sm font-normal shadow-none hover:bg-transparent focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-expanded:bg-transparent dark:bg-input/30 dark:hover:bg-input/50"; +const appearanceFontSearchTriggerClass = `${fontSearchTriggerClass} gap-0 pl-2 pr-1.5`; const fontSearchTriggerIconClass = "size-4 text-muted-foreground"; +const appearanceFontSearchTriggerIconClass = "size-2.5 text-muted-foreground"; const disconnectTabHandlingModeDescriptionKey = computed(() => { switch (editDisconnectTabHandlingMode.value) { case "close-tabs": @@ -1123,14 +1141,14 @@ function setSidebarActivation(value: "single" | "double") { editSidebarActivation.value = value; } -const activeSettingsTab = ref("editor"); +const activeSettingsTab = ref("appearance"); const isWeb = !isTauriRuntime(); const displayedAppVersion = computed(() => (props.appVersion ? `v${props.appVersion}` : "")); type SettingsCategory = "editor" | "formatter" | "appearance" | "navigation" | "data" | "shortcuts" | "snippets" | "sync" | "ai" | "mcp" | "security" | "about"; const settingsCategoryNav = computed<{ value: SettingsCategory; label: string }[]>(() => [ + { value: "appearance", label: t("settings.appearanceTab") }, { value: "editor", label: t("settings.editorTab") }, { value: "formatter", label: t("settings.sqlFormatterTab") }, - { value: "appearance", label: t("settings.appearanceTab") }, { value: "navigation", label: t("settings.navigationTab") }, { value: "data", label: t("settings.dataTab") }, { value: "shortcuts", label: t("settings.shortcutsTab") }, @@ -1148,7 +1166,7 @@ function hasSettingsApplyFooter(value: SettingsCategory): boolean { } function settingsCategoryButton(value: SettingsCategory): string { - return ["w-full rounded-md px-3 py-2 text-left text-sm transition-colors", value === activeSettingsTab.value ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"].join(" "); + return ["w-auto shrink-0 whitespace-nowrap rounded-md px-3 py-2 text-left text-sm transition-colors lg:w-full", value === activeSettingsTab.value ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:bg-muted hover:text-foreground"].join(" "); } function openExternalUrl(url: string) { @@ -1477,7 +1495,7 @@ watch( () => settingsVisible.value, async (open) => { if (open) { - activeSettingsTab.value = props.initialTab || "editor"; + activeSettingsTab.value = props.initialTab || "appearance"; passwordMessage.value = ""; oldPassword.value = ""; newPassword.value = ""; @@ -1962,12 +1980,14 @@ const previewSettings = computed<{ fontSize: number; theme: EditorTheme; appAppearance: AppThemeAppearance; + appPalette: AppThemePalette; customColors?: CustomThemeColors; }>(() => ({ fontFamily: editFontFamily.value, fontSize: editFontSize.value, theme: editTheme.value, appAppearance: isDark.value ? "dark" : "light", + appPalette: themePalette.value, customColors: getPreviewCustomThemeColors(), })); @@ -2017,7 +2037,7 @@ watch( async ([ss]) => { if (!previewView.value || !fontThemeComp || !themeComp || !editorViewModule) return; - const themeExt = await loadEditorTheme(ss.theme, ss.appAppearance, ss.customColors); + const themeExt = await loadEditorTheme(ss.theme, ss.appAppearance, ss.customColors, ss.appPalette); previewView.value.dispatch({ effects: [themeComp.reconfigure(themeExt), fontThemeComp.reconfigure(editorFontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily))], }); @@ -2065,7 +2085,7 @@ watch(previewRef, async (el) => { previewSqlDiagnostics = previewDiagnosticsForSql(currentPreviewSql()); const ss = previewSettings.value; - const themeExt = await loadEditorTheme(ss.theme, ss.appAppearance, ss.customColors); + const themeExt = await loadEditorTheme(ss.theme, ss.appAppearance, ss.customColors, ss.appPalette); const diagnosticTheme = EditorView.baseTheme({ ".cm-settings-preview-sql-error": { textDecoration: "underline wavy var(--destructive)", @@ -2125,8 +2145,8 @@ onUnmounted(cleanupPreviewEditor); -
-