🎨 Optimize preset theme code

This commit is contained in:
2025-10-20 21:58:37 +08:00
parent 9a15df01ee
commit aa8139884b
26 changed files with 245 additions and 465 deletions

View File

@@ -110,6 +110,11 @@ export class AppearanceConfig {
*/ */
"systemTheme": SystemThemeType; "systemTheme": SystemThemeType;
/**
* 当前选择的预设主题名称
*/
"currentTheme": string;
/** Creates a new AppearanceConfig instance. */ /** Creates a new AppearanceConfig instance. */
constructor($$source: Partial<AppearanceConfig> = {}) { constructor($$source: Partial<AppearanceConfig> = {}) {
if (!("language" in $$source)) { if (!("language" in $$source)) {
@@ -118,6 +123,9 @@ export class AppearanceConfig {
if (!("systemTheme" in $$source)) { if (!("systemTheme" in $$source)) {
this["systemTheme"] = ("" as SystemThemeType); this["systemTheme"] = ("" as SystemThemeType);
} }
if (!("currentTheme" in $$source)) {
this["currentTheme"] = "";
}
Object.assign(this, $$source); Object.assign(this, $$source);
} }

View File

@@ -17,18 +17,6 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as models$0 from "../models/models.js"; import * as models$0 from "../models/models.js";
/**
* CreateTheme 创建新主题
*/
export function CreateTheme(theme: models$0.Theme | null): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3274757686, theme) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/** /**
* GetAllThemes 获取所有主题 * GetAllThemes 获取所有主题
*/ */
@@ -42,10 +30,11 @@ export function GetAllThemes(): Promise<(models$0.Theme | null)[]> & { cancel():
} }
/** /**
* GetThemeByID 根据ID获取主题 * GetThemeByID 根据ID或名称获取主题
* 如果 id > 0按ID查询如果 id = 0按名称查询
*/ */
export function GetThemeByID(id: number): Promise<models$0.Theme | null> & { cancel(): void } { export function GetThemeByIdOrName(id: number, ...name: string[]): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3053137052, id) as any; let $resultPromise = $Call.ByID(127385338, id, name) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result); return $$createType1($result);
}) as any; }) as any;
@@ -53,35 +42,11 @@ export function GetThemeByID(id: number): Promise<models$0.Theme | null> & { can
return $typingPromise; return $typingPromise;
} }
/**
* GetThemeByType 根据类型获取默认主题
*/
export function GetThemeByType(themeType: models$0.ThemeType): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(1680465265, themeType) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* GetThemesByType 根据类型获取所有主题
*/
export function GetThemesByType(themeType: models$0.ThemeType): Promise<(models$0.Theme | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(1478417492, themeType) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/** /**
* ResetTheme 重置主题为预设配置 * ResetTheme 重置主题为预设配置
*/ */
export function ResetTheme(id: number): Promise<void> & { cancel(): void } { export function ResetTheme(id: number, ...name: string[]): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1806334457, id) as any; let $resultPromise = $Call.ByID(1806334457, id, name) as any;
return $resultPromise; return $resultPromise;
} }

View File

@@ -62,7 +62,8 @@ export const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = {
export const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = { export const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = {
language: 'appearance.language', language: 'appearance.language',
systemTheme: 'appearance.systemTheme' systemTheme: 'appearance.systemTheme',
currentTheme: 'appearance.currentTheme'
} as const; } as const;
export const UPDATES_CONFIG_KEY_MAP: UpdatesConfigKeyMap = { export const UPDATES_CONFIG_KEY_MAP: UpdatesConfigKeyMap = {
@@ -128,7 +129,8 @@ export const DEFAULT_CONFIG: AppConfig = {
}, },
appearance: { appearance: {
language: LanguageType.LangZhCN, language: LanguageType.LangZhCN,
systemTheme: SystemThemeType.SystemThemeAuto systemTheme: SystemThemeType.SystemThemeAuto,
currentTheme: 'default-dark'
}, },
updates: { updates: {
version: "1.0.0", version: "1.0.0",

View File

@@ -204,6 +204,11 @@ export const useConfigStore = defineStore('config', () => {
await updateAppearanceConfig('systemTheme', systemTheme); await updateAppearanceConfig('systemTheme', systemTheme);
}; };
// 当前主题设置方法
const setCurrentTheme = async (themeName: string): Promise<void> => {
await updateAppearanceConfig('currentTheme', themeName);
};
// 初始化语言设置 // 初始化语言设置
const initializeLanguage = async (): Promise<void> => { const initializeLanguage = async (): Promise<void> => {
@@ -268,6 +273,7 @@ export const useConfigStore = defineStore('config', () => {
// 主题相关方法 // 主题相关方法
setSystemTheme, setSystemTheme,
setCurrentTheme,
// 字体大小操作 // 字体大小操作
...adjusters.fontSize, ...adjusters.fontSize,

View File

@@ -125,9 +125,7 @@ export const useEditorStore = defineStore('editor', () => {
const basicExtensions = createBasicSetup(); const basicExtensions = createBasicSetup();
// 获取主题扩展 // 获取主题扩展
const themeExtension = createThemeExtension( const themeExtension = createThemeExtension();
configStore.config.appearance.systemTheme || SystemThemeType.SystemThemeAuto
);
// Tab相关扩展 // Tab相关扩展
const tabExtensions = getTabExtensions( const tabExtensions = getTabExtensions(
@@ -504,9 +502,7 @@ export const useEditorStore = defineStore('editor', () => {
// 应用主题设置 // 应用主题设置
const applyThemeSettings = () => { const applyThemeSettings = () => {
editorCache.values().forEach(instance => { editorCache.values().forEach(instance => {
updateEditorTheme(instance.view, updateEditorTheme(instance.view);
themeStore.currentTheme || SystemThemeType.SystemThemeAuto
);
}); });
}; };

View File

@@ -1,11 +1,10 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { computed, reactive, ref } from 'vue'; import { computed, ref } from 'vue';
import {SystemThemeType, ThemeType, Theme, ThemeColorConfig} from '@/../bindings/voidraft/internal/models/models'; import {SystemThemeType, ThemeType, Theme, ThemeColorConfig} from '@/../bindings/voidraft/internal/models/models';
import { ThemeService } from '@/../bindings/voidraft/internal/services'; import { ThemeService } from '@/../bindings/voidraft/internal/services';
import { useConfigStore } from './configStore'; import { useConfigStore } from './configStore';
import { useEditorStore } from './editorStore'; import { useEditorStore } from './editorStore';
import type { ThemeColors } from '@/views/editor/theme/types'; import type { ThemeColors } from '@/views/editor/theme/types';
import { getThemeConfig } from '@/views/editor/theme/registry';
/** /**
* 主题管理 Store * 主题管理 Store
@@ -14,29 +13,24 @@ import { getThemeConfig } from '@/views/editor/theme/registry';
export const useThemeStore = defineStore('theme', () => { export const useThemeStore = defineStore('theme', () => {
const configStore = useConfigStore(); const configStore = useConfigStore();
// 所有主题列表(从数据库加载) // 所有主题列表
const allThemes = ref<Theme[]>([]); const allThemes = ref<Theme[]>([]);
// 当前选中的主题 ID // 当前主题的颜色配置
const currentThemeIds = reactive({ const currentColors = ref<ThemeColors | null>(null);
dark: 0, // 当前深色主题ID
light: 0, // 当前浅色主题ID
});
// 当前主题的颜色配置(用于编辑器渲染)
const currentColors = reactive<{
dark: ThemeColors | null;
light: ThemeColors | null;
}>({
dark: null,
light: null,
});
// 计算属性:当前系统主题模式 // 计算属性:当前系统主题模式
const currentTheme = computed(() => const currentTheme = computed(() =>
configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto configStore.config?.appearance?.systemTheme || SystemThemeType.SystemThemeAuto
); );
// 计算属性:当前是否为深色模式
const isDarkMode = computed(() =>
currentTheme.value === SystemThemeType.SystemThemeDark ||
(currentTheme.value === SystemThemeType.SystemThemeAuto &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
);
// 计算属性:根据类型获取主题列表 // 计算属性:根据类型获取主题列表
const darkThemes = computed(() => const darkThemes = computed(() =>
allThemes.value.filter(t => t.type === ThemeType.ThemeTypeDark) allThemes.value.filter(t => t.type === ThemeType.ThemeTypeDark)
@@ -46,14 +40,10 @@ export const useThemeStore = defineStore('theme', () => {
allThemes.value.filter(t => t.type === ThemeType.ThemeTypeLight) allThemes.value.filter(t => t.type === ThemeType.ThemeTypeLight)
); );
// 计算属性:获取当前激活的主题对象 // 计算属性:当前可用的主题列表
const activeTheme = computed(() => { const availableThemes = computed(() =>
const isDark = currentTheme.value === SystemThemeType.SystemThemeDark || isDarkMode.value ? darkThemes.value : lightThemes.value
(currentTheme.value === SystemThemeType.SystemThemeAuto && );
window.matchMedia('(prefers-color-scheme: dark)').matches);
return isDark ? currentColors.dark : currentColors.light;
});
// 应用主题到 DOM // 应用主题到 DOM
const applyThemeToDOM = (theme: SystemThemeType) => { const applyThemeToDOM = (theme: SystemThemeType) => {
@@ -78,65 +68,23 @@ export const useThemeStore = defineStore('theme', () => {
} }
}; };
// 根据主题对象加载颜色配置 // 初始化主题颜色
const loadThemeColors = (theme: Theme): ThemeColors => {
// 优先使用数据库中的颜色配置
const dbColors = theme.colors as unknown as ThemeColors;
// 如果数据库颜色不完整,尝试从预设主题获取
if (!dbColors || Object.keys(dbColors).length < 10) {
const presetConfig = getThemeConfig(theme.name);
if (presetConfig) {
return presetConfig;
}
}
return dbColors;
};
// 初始化主题颜色(加载默认主题)
const initializeThemeColors = async () => { const initializeThemeColors = async () => {
try {
// 加载所有主题 // 加载所有主题
await loadAllThemes(); await loadAllThemes();
// 查找默认主题 // 从配置获取当前主题名称并加载
const defaultDark = allThemes.value.find( const currentThemeName = configStore.config?.appearance?.currentTheme || 'default-dark';
t => t.type === ThemeType.ThemeTypeDark && t.isDefault
);
const defaultLight = allThemes.value.find(
t => t.type === ThemeType.ThemeTypeLight && t.isDefault
);
// 设置默认主题 const theme = allThemes.value.find(t => t.name === currentThemeName);
if (defaultDark) {
currentThemeIds.dark = defaultDark.id; if (!theme) {
currentColors.dark = loadThemeColors(defaultDark); console.error(`Theme not found: ${currentThemeName}`);
return;
} }
if (defaultLight) { // 直接设置当前主题颜色
currentThemeIds.light = defaultLight.id; currentColors.value = theme.colors as ThemeColors;
currentColors.light = loadThemeColors(defaultLight);
}
// 如果没有找到默认主题,使用第一个可用主题
if (!currentColors.dark && darkThemes.value.length > 0) {
const fallback = darkThemes.value[0];
currentThemeIds.dark = fallback.id;
currentColors.dark = loadThemeColors(fallback);
}
if (!currentColors.light && lightThemes.value.length > 0) {
const fallback = lightThemes.value[0];
currentThemeIds.light = fallback.id;
currentColors.light = loadThemeColors(fallback);
}
} catch (error) {
console.error('Failed to initialize theme colors:', error);
// 使用预设主题作为后备
currentColors.dark = getThemeConfig('default-dark');
currentColors.light = getThemeConfig('default-light');
}
}; };
// 初始化主题 // 初始化主题
@@ -153,98 +101,66 @@ export const useThemeStore = defineStore('theme', () => {
refreshEditorTheme(); refreshEditorTheme();
}; };
// 切换到指定的预设主题通过主题ID // 切换到指定的预设主题
const switchToTheme = async (themeId: number) => { const switchToTheme = async (themeName: string) => {
try { const theme = allThemes.value.find(t => t.name === themeName);
const theme = allThemes.value.find(t => t.id === themeId);
if (!theme) { if (!theme) {
console.error('Theme not found:', themeId); console.error('Theme not found:', themeName);
return false; return false;
} }
// 加载主题颜色 // 直接设置当前主题颜色
const colors = loadThemeColors(theme); currentColors.value = theme.colors as ThemeColors;
// 根据主题类型更新对应的颜色配置 // 持久化到配置
if (theme.type === ThemeType.ThemeTypeDark) { await configStore.setCurrentTheme(themeName);
currentThemeIds.dark = themeId;
currentColors.dark = colors;
} else {
currentThemeIds.light = themeId;
currentColors.light = colors;
}
// 刷新编辑器主题 // 刷新编辑器
refreshEditorTheme(); refreshEditorTheme();
return true; return true;
} catch (error) {
console.error('Failed to switch theme:', error);
return false;
}
}; };
// 更新当前主题的颜色配置 // 更新当前主题的颜色配置
const updateCurrentColors = (colors: Partial<ThemeColors>, isDark: boolean) => { const updateCurrentColors = (colors: Partial<ThemeColors>) => {
const target = isDark ? currentColors.dark : currentColors.light; if (!currentColors.value) return;
if (!target) return; Object.assign(currentColors.value, colors);
Object.assign(target, colors);
}; };
// 保存当前主题颜色到数据库 // 保存当前主题颜色到数据库
const saveCurrentTheme = async (isDark: boolean) => { const saveCurrentTheme = async () => {
try { if (!currentColors.value) {
const themeId = isDark ? currentThemeIds.dark : currentThemeIds.light;
const colors = isDark ? currentColors.dark : currentColors.light;
if (!themeId || !colors) {
throw new Error('No theme selected'); throw new Error('No theme selected');
} }
// 转换为数据库格式并保存 const theme = allThemes.value.find(t => t.name === currentColors.value!.name);
const dbColors = colors as ThemeColorConfig; // ThemeColorConfig from bindings if (!theme) {
await ThemeService.UpdateTheme(themeId, dbColors); throw new Error('Theme not found');
return true;
} catch (error) {
console.error('Failed to save theme:', error);
throw error;
} }
await ThemeService.UpdateTheme(theme.id, currentColors.value as ThemeColorConfig);
return true;
}; };
// 重置当前主题为预设配置 // 重置当前主题为预设配置
const resetCurrentTheme = async (isDark: boolean) => { const resetCurrentTheme = async () => {
try { if (!currentColors.value) {
const themeId = isDark ? currentThemeIds.dark : currentThemeIds.light;
if (!themeId) {
throw new Error('No theme selected'); throw new Error('No theme selected');
} }
// 调用后端重置服务 // 调用后端重置
await ThemeService.ResetTheme(themeId); await ThemeService.ResetTheme(0, currentColors.value.name);
// 重新加载主题 // 重新加载所有主题
await loadAllThemes(); await loadAllThemes();
const theme = allThemes.value.find(t => t.id === themeId);
if (theme) { const updatedTheme = allThemes.value.find(t => t.name === currentColors.value!.name);
const colors = loadThemeColors(theme);
if (isDark) { if (updatedTheme) {
currentColors.dark = colors; currentColors.value = updatedTheme.colors as ThemeColors;
} else {
currentColors.light = colors;
}
} }
// 刷新编辑器主题
refreshEditorTheme(); refreshEditorTheme();
return true; return true;
} catch (error) {
console.error('Failed to reset theme:', error);
return false;
}
}; };
// 刷新编辑器主题 // 刷新编辑器主题
@@ -260,10 +176,10 @@ export const useThemeStore = defineStore('theme', () => {
allThemes, allThemes,
darkThemes, darkThemes,
lightThemes, lightThemes,
availableThemes,
currentTheme, currentTheme,
currentThemeIds,
currentColors, currentColors,
activeTheme, isDarkMode,
// 方法 // 方法
setTheme, setTheme,

View File

@@ -1,7 +1,6 @@
import { Extension, Compartment } from '@codemirror/state'; import { Extension, Compartment } from '@codemirror/state';
import { EditorView } from '@codemirror/view'; import { EditorView } from '@codemirror/view';
import { SystemThemeType } from '@/../bindings/voidraft/internal/models/models'; import { createThemeByColors } from '@/views/editor/theme';
import { createThemeByColors } from '@/views/editor/theme/registry';
import { useThemeStore } from '@/stores/themeStore'; import { useThemeStore } from '@/stores/themeStore';
// 主题区间 - 用于动态切换主题 // 主题区间 - 用于动态切换主题
@@ -10,20 +9,13 @@ export const themeCompartment = new Compartment();
/** /**
* 根据主题类型获取主题扩展 * 根据主题类型获取主题扩展
*/ */
const getThemeExtension = (themeType: SystemThemeType): Extension | null => { const getThemeExtension = (): Extension | null => {
const themeStore = useThemeStore(); const themeStore = useThemeStore();
// 处理 auto 主题类型 // 直接获取当前主题颜色配置
let isDark = themeType === SystemThemeType.SystemThemeDark; const colors = themeStore.currentColors;
if (themeType === SystemThemeType.SystemThemeAuto) {
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
}
// 根据主题类型获取对应的颜色配置
const colors = isDark ? themeStore.currentColors.dark : themeStore.currentColors.light;
if (!colors) { if (!colors) {
console.warn('Theme colors not loaded yet');
return null; return null;
} }
@@ -34,8 +26,8 @@ const getThemeExtension = (themeType: SystemThemeType): Extension | null => {
/** /**
* 创建主题扩展(用于编辑器初始化) * 创建主题扩展(用于编辑器初始化)
*/ */
export const createThemeExtension = (themeType: SystemThemeType = SystemThemeType.SystemThemeDark): Extension => { export const createThemeExtension = (): Extension => {
const extension = getThemeExtension(themeType); const extension = getThemeExtension();
// 如果主题未加载,返回空扩展 // 如果主题未加载,返回空扩展
if (!extension) { if (!extension) {
@@ -48,17 +40,16 @@ export const createThemeExtension = (themeType: SystemThemeType = SystemThemeTyp
/** /**
* 更新编辑器主题 * 更新编辑器主题
*/ */
export const updateEditorTheme = (view: EditorView, themeType: SystemThemeType): void => { export const updateEditorTheme = (view: EditorView): void => {
if (!view?.dispatch) { if (!view?.dispatch) {
return; return;
} }
try { try {
const extension = getThemeExtension(themeType); const extension = getThemeExtension();
// 如果主题未加载,不更新 // 如果主题未加载,不更新
if (!extension) { if (!extension) {
console.warn('Cannot update theme: theme not loaded');
return; return;
} }

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#21202e', background: '#21202e',
backgroundSecondary: '#21202e', backgroundSecondary: '#2B2A3BFF',
surface: '#21202e', surface: '#21202e',
dropdownBackground: '#21202e', dropdownBackground: '#21202e',
dropdownBorder: '#3b334b', dropdownBorder: '#3b334b',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#282A36', background: '#282A36',
backgroundSecondary: '#282A36', backgroundSecondary: '#323543FF',
surface: '#282A36', surface: '#282A36',
dropdownBackground: '#282A36', dropdownBackground: '#282A36',
dropdownBorder: '#191A21', dropdownBorder: '#191A21',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#24292e', background: '#24292e',
backgroundSecondary: '#24292e', backgroundSecondary: '#2E343BFF',
surface: '#24292e', surface: '#24292e',
dropdownBackground: '#24292e', dropdownBackground: '#24292e',
dropdownBorder: '#1b1f23', dropdownBorder: '#1b1f23',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#263238', background: '#263238',
backgroundSecondary: '#263238', backgroundSecondary: '#2D3E46FF',
surface: '#263238', surface: '#263238',
dropdownBackground: '#263238', dropdownBackground: '#263238',
dropdownBorder: '#FFFFFF10', dropdownBorder: '#FFFFFF10',

View File

@@ -15,7 +15,7 @@ const chalky = "#e5c07b",
whiskey = "#d19a66", whiskey = "#d19a66",
violet = "#c678dd", violet = "#c678dd",
darkBackground = "#21252b", darkBackground = "#21252b",
highlightBackground = "#2c313a", highlightBackground = "#313949FF",
background = "#282c34", background = "#282c34",
tooltipBackground = "#353a42", tooltipBackground = "#353a42",
selection = "#3E4451", selection = "#3E4451",

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#002B36', background: '#002B36',
backgroundSecondary: '#002B36', backgroundSecondary: '#003643FF',
surface: '#002B36', surface: '#002B36',
dropdownBackground: '#002B36', dropdownBackground: '#002B36',
dropdownBorder: '#2AA19899', dropdownBorder: '#2AA19899',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#24283b', background: '#24283b',
backgroundSecondary: '#24283b', backgroundSecondary: '#2B3151FF',
surface: '#24283b', surface: '#24283b',
dropdownBackground: '#24283b', dropdownBackground: '#24283b',
dropdownBorder: '#7982a9', dropdownBorder: '#7982a9',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#1a1b26', background: '#1a1b26',
backgroundSecondary: '#1a1b26', backgroundSecondary: '#272839FF',
surface: '#1a1b26', surface: '#1a1b26',
dropdownBackground: '#1a1b26', dropdownBackground: '#1a1b26',
dropdownBorder: '#787c99', dropdownBorder: '#787c99',

View File

@@ -0,0 +1,12 @@
import { Extension } from '@codemirror/state';
import type { ThemeColors } from './types';
import { createBaseTheme } from './base';
/**
* 根据自定义颜色配置创建主题
*/
export function createThemeByColors(colors: ThemeColors): Extension {
return createBaseTheme(colors);
}

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#fff', background: '#fff',
backgroundSecondary: '#fff', backgroundSecondary: '#f1faf1',
surface: '#fff', surface: '#fff',
dropdownBackground: '#fff', dropdownBackground: '#fff',
dropdownBorder: '#e1e4e8', dropdownBorder: '#e1e4e8',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#FAFAFA', background: '#FAFAFA',
backgroundSecondary: '#FAFAFA', backgroundSecondary: '#f1faf1',
surface: '#FAFAFA', surface: '#FAFAFA',
dropdownBackground: '#FAFAFA', dropdownBackground: '#FAFAFA',
dropdownBorder: '#00000010', dropdownBorder: '#00000010',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#FDF6E3', background: '#FDF6E3',
backgroundSecondary: '#FDF6E3', backgroundSecondary: '#FFEEBCD4',
surface: '#FDF6E3', surface: '#FDF6E3',
dropdownBackground: '#FDF6E3', dropdownBackground: '#FDF6E3',
dropdownBorder: '#D3AF86', dropdownBorder: '#D3AF86',

View File

@@ -8,7 +8,7 @@ export const config: ThemeColors = {
// 基础色调 // 基础色调
background: '#e1e2e7', background: '#e1e2e7',
backgroundSecondary: '#e1e2e7', backgroundSecondary: '#D2D8EFFF',
surface: '#e1e2e7', surface: '#e1e2e7',
dropdownBackground: '#e1e2e7', dropdownBackground: '#e1e2e7',
dropdownBorder: '#6a6f8e', dropdownBorder: '#6a6f8e',

View File

@@ -1,59 +0,0 @@
import { Extension } from '@codemirror/state';
import type { ThemeColors } from './types';
import { createBaseTheme } from './base';
// 深色主题导入
import { config as draculaConfig } from './dark/dracula';
import { config as auraConfig } from './dark/aura';
import { config as githubDarkConfig } from './dark/github-dark';
import { config as materialDarkConfig } from './dark/material-dark';
import { config as oneDarkConfig } from './dark/one-dark';
import { config as solarizedDarkConfig } from './dark/solarized-dark';
import { config as tokyoNightConfig } from './dark/tokyo-night';
import { config as tokyoNightStormConfig } from './dark/tokyo-night-storm';
// 浅色主题导入
import { config as githubLightConfig } from './light/github-light';
import { config as materialLightConfig } from './light/material-light';
import { config as solarizedLightConfig } from './light/solarized-light';
import { config as tokyoNightDayConfig } from './light/tokyo-night-day';
/**
* 主题配置映射表
* key: 主题名称(与数据库中的 name 字段一致)
* value: 主题颜色配置
*/
const themeConfigMap: Record<string, ThemeColors> = {
// 深色主题
'dracula': draculaConfig,
'aura': auraConfig,
'github-dark': githubDarkConfig,
'material-dark': materialDarkConfig,
'one-dark': oneDarkConfig,
'solarized-dark': solarizedDarkConfig,
'tokyo-night': tokyoNightConfig,
'tokyo-night-storm': tokyoNightStormConfig,
// 浅色主题
'github-light': githubLightConfig,
'material-light': materialLightConfig,
'solarized-light': solarizedLightConfig,
'tokyo-night-day': tokyoNightDayConfig,
};
/**
* 根据主题名称获取主题配置
*/
export function getThemeConfig(themeName: string): ThemeColors | null {
return themeConfigMap[themeName] || null;
}
/**
* 根据自定义颜色配置创建主题
*/
export function createThemeByColors(colors: ThemeColors): Extension {
return createBaseTheme(colors);
}

View File

@@ -1,13 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ defineProps<{
title: string; title?: string;
}>(); }>();
</script> </script>
<template> <template>
<div class="setting-section"> <div class="setting-section">
<div class="section-header"> <div class="section-header">
<h2 class="section-title">{{ title }}</h2> <h2 class="section-title">
<slot name="title">{{ title }}</slot>
</h2>
<div class="section-title-right"> <div class="section-title-right">
<slot name="title-right"></slot> <slot name="title-right"></slot>
</div> </div>

View File

@@ -23,8 +23,7 @@ const { debouncedFn: debouncedUpdateColor } = createDebounce(
const { debouncedFn: debouncedResetTheme } = createDebounce( const { debouncedFn: debouncedResetTheme } = createDebounce(
async () => { async () => {
const isDark = isDarkMode.value; const success = await themeStore.resetCurrentTheme();
const success = await themeStore.resetCurrentTheme(isDark);
if (success) { if (success) {
// 重新加载临时颜色 // 重新加载临时颜色
@@ -49,23 +48,11 @@ const resetButtonState = ref({
confirming: false confirming: false
}); });
// 当前激活的主题类型(深色/浅色) // 当前选中的主题名称
const isDarkMode = computed(() => const currentThemeName = computed({
themeStore.currentTheme === SystemThemeType.SystemThemeDark || get: () => themeStore.currentColors?.name || '',
(themeStore.currentTheme === SystemThemeType.SystemThemeAuto && set: async (themeName: string) => {
window.matchMedia('(prefers-color-scheme: dark)').matches) await themeStore.switchToTheme(themeName);
);
// 当前可用的预设主题列表
const availableThemes = computed(() =>
isDarkMode.value ? themeStore.darkThemes : themeStore.lightThemes
);
// 当前选中的主题ID
const currentThemeId = computed({
get: () => isDarkMode.value ? themeStore.currentThemeIds.dark : themeStore.currentThemeIds.light,
set: async (themeId: number) => {
await themeStore.switchToTheme(themeId);
syncTempColors(); syncTempColors();
hasUnsavedChanges.value = false; hasUnsavedChanges.value = false;
} }
@@ -74,20 +61,19 @@ const currentThemeId = computed({
// 当前主题的颜色配置 // 当前主题的颜色配置
const currentColors = computed(() => tempColors.value || ({} as ThemeColors)); const currentColors = computed(() => tempColors.value || ({} as ThemeColors));
// 获取当前主题模式(用于颜色选择器) // 获取当前主题模式
const currentThemeMode = computed(() => isDarkMode.value ? 'dark' : 'light'); const currentThemeMode = computed(() => themeStore.isDarkMode ? 'dark' : 'light');
// 同步临时颜色从 store // 同步临时颜色从 store
const syncTempColors = () => { const syncTempColors = () => {
const colors = isDarkMode.value ? themeStore.currentColors.dark : themeStore.currentColors.light; if (themeStore.currentColors) {
if (colors) { tempColors.value = { ...themeStore.currentColors };
tempColors.value = { ...colors };
} }
}; };
// 监听主题切换,同步临时颜色 // 监听主题切换,同步临时颜色
watch( watch(
[() => themeStore.currentColors.dark, () => themeStore.currentColors.light, isDarkMode], () => themeStore.currentColors,
() => { () => {
if (!hasUnsavedChanges.value) { if (!hasUnsavedChanges.value) {
syncTempColors(); syncTempColors();
@@ -153,13 +139,11 @@ const applyChanges = async () => {
try { try {
if (!tempColors.value) return; if (!tempColors.value) return;
const isDark = isDarkMode.value;
// 更新 store 中的颜色 // 更新 store 中的颜色
themeStore.updateCurrentColors(tempColors.value, isDark); themeStore.updateCurrentColors(tempColors.value);
// 保存到数据库 // 保存到数据库
await themeStore.saveCurrentTheme(isDark); await themeStore.saveCurrentTheme();
// 刷新编辑器主题 // 刷新编辑器主题
themeStore.refreshEditorTheme(); themeStore.refreshEditorTheme();
@@ -207,6 +191,17 @@ const updateSystemTheme = async (event: Event) => {
const selectedSystemTheme = select.value as SystemThemeType; const selectedSystemTheme = select.value as SystemThemeType;
await themeStore.setTheme(selectedSystemTheme); await themeStore.setTheme(selectedSystemTheme);
const availableThemes = themeStore.availableThemes;
const currentThemeName = currentColors.value?.name;
const isCurrentThemeAvailable = availableThemes.some(t => t.name === currentThemeName);
if (!isCurrentThemeAvailable && availableThemes.length > 0) {
const firstTheme = availableThemes[0];
await themeStore.switchToTheme(firstTheme.name);
syncTempColors();
hasUnsavedChanges.value = false;
}
}; };
// 控制颜色选择器显示状态 // 控制颜色选择器显示状态
@@ -246,8 +241,8 @@ const handlePickerClose = () => {
<!-- 预设主题选择 --> <!-- 预设主题选择 -->
<SettingItem :title="t('settings.presetTheme')"> <SettingItem :title="t('settings.presetTheme')">
<select class="select-input" v-model="currentThemeId" :disabled="hasUnsavedChanges"> <select class="select-input" v-model="currentThemeName" :disabled="hasUnsavedChanges">
<option v-for="theme in availableThemes" :key="theme.id" :value="theme.id"> <option v-for="theme in themeStore.availableThemes" :key="theme.id" :value="theme.name">
{{ theme.name }} {{ theme.name }}
</option> </option>
</select> </select>
@@ -256,6 +251,12 @@ const handlePickerClose = () => {
<!-- 自定义主题颜色配置 --> <!-- 自定义主题颜色配置 -->
<SettingSection :title="t('settings.customThemeColors')"> <SettingSection :title="t('settings.customThemeColors')">
<template #title>
<div class="theme-section-title">
<span class="section-title-text">{{ t('settings.customThemeColors') }}</span>
<span v-if="currentColors.name" class="current-theme-name">{{ currentColors.name }}</span>
</div>
</template>
<template #title-right> <template #title-right>
<div class="theme-controls"> <div class="theme-controls">
<button <button
@@ -348,6 +349,27 @@ const handlePickerClose = () => {
} }
} }
// 主题部分标题
.theme-section-title {
display: flex;
align-items: center;
gap: 12px;
}
.section-title-text {
font-weight: 500;
}
.current-theme-name {
font-size: 13px;
color: var(--settings-text-secondary);
font-weight: normal;
padding: 2px 8px;
background-color: var(--settings-input-bg);
border: 1px solid var(--settings-input-border);
border-radius: 4px;
}
// 主题控制区域 // 主题控制区域
.theme-controls { .theme-controls {
display: flex; display: flex;

View File

@@ -111,6 +111,7 @@ type EditingConfig struct {
type AppearanceConfig struct { type AppearanceConfig struct {
Language LanguageType `json:"language"` // 界面语言 Language LanguageType `json:"language"` // 界面语言
SystemTheme SystemThemeType `json:"systemTheme"` // 系统界面主题 SystemTheme SystemThemeType `json:"systemTheme"` // 系统界面主题
CurrentTheme string `json:"currentTheme"` // 当前选择的预设主题名称
} }
// UpdatesConfig 更新设置配置 // UpdatesConfig 更新设置配置
@@ -181,6 +182,7 @@ func NewDefaultAppConfig() *AppConfig {
Appearance: AppearanceConfig{ Appearance: AppearanceConfig{
Language: LangEnUS, Language: LangEnUS,
SystemTheme: SystemThemeAuto, SystemTheme: SystemThemeAuto,
CurrentTheme: "default-dark", // 默认使用 default-dark 主题
}, },
Updates: UpdatesConfig{ Updates: UpdatesConfig{
Version: version.Version, Version: version.Version,

View File

@@ -219,7 +219,7 @@ func NewDraculaTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#282A36", Background: "#282A36",
BackgroundSecondary: "#282A36", BackgroundSecondary: "#323543FF",
Surface: "#282A36", Surface: "#282A36",
DropdownBackground: "#282A36", DropdownBackground: "#282A36",
DropdownBorder: "#191A21", DropdownBorder: "#191A21",
@@ -266,7 +266,7 @@ func NewAuraTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#21202e", Background: "#21202e",
BackgroundSecondary: "#21202e", BackgroundSecondary: "#2B2A3BFF",
Surface: "#21202e", Surface: "#21202e",
DropdownBackground: "#21202e", DropdownBackground: "#21202e",
DropdownBorder: "#3b334b", DropdownBorder: "#3b334b",
@@ -313,7 +313,7 @@ func NewGitHubDarkTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#24292e", Background: "#24292e",
BackgroundSecondary: "#24292e", BackgroundSecondary: "#2E343BFF",
Surface: "#24292e", Surface: "#24292e",
DropdownBackground: "#24292e", DropdownBackground: "#24292e",
DropdownBorder: "#1b1f23", DropdownBorder: "#1b1f23",
@@ -360,7 +360,7 @@ func NewMaterialDarkTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#263238", Background: "#263238",
BackgroundSecondary: "#263238", BackgroundSecondary: "#2D3E46FF",
Surface: "#263238", Surface: "#263238",
DropdownBackground: "#263238", DropdownBackground: "#263238",
DropdownBorder: "#FFFFFF10", DropdownBorder: "#FFFFFF10",
@@ -407,7 +407,7 @@ func NewOneDarkTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#282c34", Background: "#282c34",
BackgroundSecondary: "#2c313a", BackgroundSecondary: "#313949FF",
Surface: "#353a42", Surface: "#353a42",
DropdownBackground: "#21252b", DropdownBackground: "#21252b",
DropdownBorder: "#7d8799", DropdownBorder: "#7d8799",
@@ -454,7 +454,7 @@ func NewSolarizedDarkTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#002B36", Background: "#002B36",
BackgroundSecondary: "#002B36", BackgroundSecondary: "#003643FF",
Surface: "#002B36", Surface: "#002B36",
DropdownBackground: "#002B36", DropdownBackground: "#002B36",
DropdownBorder: "#2AA19899", DropdownBorder: "#2AA19899",
@@ -501,7 +501,7 @@ func NewTokyoNightTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#1a1b26", Background: "#1a1b26",
BackgroundSecondary: "#1a1b26", BackgroundSecondary: "#272839FF",
Surface: "#1a1b26", Surface: "#1a1b26",
DropdownBackground: "#1a1b26", DropdownBackground: "#1a1b26",
DropdownBorder: "#787c99", DropdownBorder: "#787c99",
@@ -548,7 +548,7 @@ func NewTokyoNightStormTheme() *ThemeColorConfig {
Dark: true, Dark: true,
Background: "#24283b", Background: "#24283b",
BackgroundSecondary: "#24283b", BackgroundSecondary: "#2B3151FF",
Surface: "#24283b", Surface: "#24283b",
DropdownBackground: "#24283b", DropdownBackground: "#24283b",
DropdownBorder: "#7982a9", DropdownBorder: "#7982a9",
@@ -597,7 +597,7 @@ func NewGitHubLightTheme() *ThemeColorConfig {
Dark: false, Dark: false,
Background: "#fff", Background: "#fff",
BackgroundSecondary: "#fff", BackgroundSecondary: "#f1faf1",
Surface: "#fff", Surface: "#fff",
DropdownBackground: "#fff", DropdownBackground: "#fff",
DropdownBorder: "#e1e4e8", DropdownBorder: "#e1e4e8",
@@ -644,7 +644,7 @@ func NewMaterialLightTheme() *ThemeColorConfig {
Dark: false, Dark: false,
Background: "#FAFAFA", Background: "#FAFAFA",
BackgroundSecondary: "#FAFAFA", BackgroundSecondary: "#f1faf1",
Surface: "#FAFAFA", Surface: "#FAFAFA",
DropdownBackground: "#FAFAFA", DropdownBackground: "#FAFAFA",
DropdownBorder: "#00000010", DropdownBorder: "#00000010",
@@ -691,7 +691,7 @@ func NewSolarizedLightTheme() *ThemeColorConfig {
Dark: false, Dark: false,
Background: "#FDF6E3", Background: "#FDF6E3",
BackgroundSecondary: "#FDF6E3", BackgroundSecondary: "#FFEEBCD4",
Surface: "#FDF6E3", Surface: "#FDF6E3",
DropdownBackground: "#FDF6E3", DropdownBackground: "#FDF6E3",
DropdownBorder: "#D3AF86", DropdownBorder: "#D3AF86",
@@ -738,7 +738,7 @@ func NewTokyoNightDayTheme() *ThemeColorConfig {
Dark: false, Dark: false,
Background: "#e1e2e7", Background: "#e1e2e7",
BackgroundSecondary: "#e1e2e7", BackgroundSecondary: "#D2D8EFFF",
Surface: "#e1e2e7", Surface: "#e1e2e7",
DropdownBackground: "#e1e2e7", DropdownBackground: "#e1e2e7",
DropdownBorder: "#6a6f8e", DropdownBorder: "#6a6f8e",

View File

@@ -150,18 +150,35 @@ func (ts *ThemeService) initializeDefaultThemes() error {
return nil return nil
} }
// GetThemeByID 根据ID获取主题 // GetThemeByID 根据ID或名称获取主题
func (ts *ThemeService) GetThemeByID(id int) (*models.Theme, error) { // 如果 id > 0按ID查询如果 id = 0按名称查询
query := ` func (ts *ThemeService) GetThemeByIdOrName(id int, name ...string) (*models.Theme, error) {
var query string
var args []interface{}
if id > 0 {
query = `
SELECT id, name, type, colors, is_default, created_at, updated_at SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes FROM themes
WHERE id = ? WHERE id = ?
LIMIT 1 LIMIT 1
` `
args = []interface{}{id}
} else if len(name) > 0 && name[0] != "" {
query = `
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
WHERE name = ?
LIMIT 1
`
args = []interface{}{name[0]}
} else {
return nil, fmt.Errorf("either id or name must be provided")
}
theme := &models.Theme{} theme := &models.Theme{}
db := ts.getDB() db := ts.getDB()
err := db.QueryRow(query, id).Scan( err := db.QueryRow(query, args...).Scan(
&theme.ID, &theme.ID,
&theme.Name, &theme.Name,
&theme.Type, &theme.Type,
@@ -173,86 +190,17 @@ func (ts *ThemeService) GetThemeByID(id int) (*models.Theme, error) {
if err != nil { if err != nil {
if errors.Is(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
if id > 0 {
return nil, fmt.Errorf("theme not found with id: %d", id) return nil, fmt.Errorf("theme not found with id: %d", id)
} }
return nil, fmt.Errorf("failed to get theme by id: %w", err) return nil, fmt.Errorf("theme not found with name: %s", name[0])
}
return nil, fmt.Errorf("failed to get theme: %w", err)
} }
return theme, nil return theme, nil
} }
// GetThemeByType 根据类型获取默认主题
func (ts *ThemeService) GetThemeByType(themeType models.ThemeType) (*models.Theme, error) {
query := `
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
WHERE type = ? AND is_default = 1
LIMIT 1
`
theme := &models.Theme{}
db := ts.getDB()
err := db.QueryRow(query, themeType).Scan(
&theme.ID,
&theme.Name,
&theme.Type,
&theme.Colors,
&theme.IsDefault,
&theme.CreatedAt,
&theme.UpdatedAt,
)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("no default theme found for type: %s", themeType)
}
return nil, fmt.Errorf("failed to get theme by type: %w", err)
}
return theme, nil
}
// GetThemesByType 根据类型获取所有主题
func (ts *ThemeService) GetThemesByType(themeType models.ThemeType) ([]*models.Theme, error) {
query := `
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
WHERE type = ?
ORDER BY is_default DESC, name ASC
`
db := ts.getDB()
rows, err := db.Query(query, themeType)
if err != nil {
return nil, fmt.Errorf("failed to query themes by type: %w", err)
}
defer rows.Close()
var themes []*models.Theme
for rows.Next() {
theme := &models.Theme{}
err := rows.Scan(
&theme.ID,
&theme.Name,
&theme.Type,
&theme.Colors,
&theme.IsDefault,
&theme.CreatedAt,
&theme.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to scan theme: %w", err)
}
themes = append(themes, theme)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("failed to iterate themes: %w", err)
}
return themes, nil
}
// UpdateTheme 更新主题 // UpdateTheme 更新主题
func (ts *ThemeService) UpdateTheme(id int, colors models.ThemeColorConfig) error { func (ts *ThemeService) UpdateTheme(id int, colors models.ThemeColorConfig) error {
query := ` query := `
@@ -280,9 +228,9 @@ func (ts *ThemeService) UpdateTheme(id int, colors models.ThemeColorConfig) erro
} }
// ResetTheme 重置主题为预设配置 // ResetTheme 重置主题为预设配置
func (ts *ThemeService) ResetTheme(id int) error { func (ts *ThemeService) ResetTheme(id int, name ...string) error {
// 先获取主题信息 // 先获取主题信息
theme, err := ts.GetThemeByID(id) theme, err := ts.GetThemeByIdOrName(id, name...)
if err != nil { if err != nil {
return err return err
} }
@@ -331,37 +279,6 @@ func (ts *ThemeService) ResetTheme(id int) error {
return ts.UpdateTheme(id, *presetConfig) return ts.UpdateTheme(id, *presetConfig)
} }
// CreateTheme 创建新主题
func (ts *ThemeService) CreateTheme(theme *models.Theme) (*models.Theme, error) {
query := `
INSERT INTO themes (name, type, colors, is_default, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`
db := ts.getDB()
result, err := db.Exec(
query,
theme.Name,
theme.Type,
theme.Colors,
theme.IsDefault,
theme.CreatedAt,
theme.UpdatedAt,
)
if err != nil {
return nil, fmt.Errorf("failed to create theme: %w", err)
}
id, err := result.LastInsertId()
if err != nil {
return nil, fmt.Errorf("failed to get theme ID: %w", err)
}
theme.ID = int(id)
return theme, nil
}
// GetAllThemes 获取所有主题 // GetAllThemes 获取所有主题
func (ts *ThemeService) GetAllThemes() ([]*models.Theme, error) { func (ts *ThemeService) GetAllThemes() ([]*models.Theme, error) {
query := ` query := `