🎨 Optimize preset theme code
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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) => {
|
||||||
@@ -77,66 +67,24 @@ export const useThemeStore = defineStore('theme', () => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 根据主题对象加载颜色配置
|
|
||||||
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 currentThemeName = configStore.config?.appearance?.currentTheme || 'default-dark';
|
||||||
const defaultDark = allThemes.value.find(
|
|
||||||
t => t.type === ThemeType.ThemeTypeDark && t.isDefault
|
const theme = allThemes.value.find(t => t.name === currentThemeName);
|
||||||
);
|
|
||||||
const defaultLight = allThemes.value.find(
|
if (!theme) {
|
||||||
t => t.type === ThemeType.ThemeTypeLight && t.isDefault
|
console.error(`Theme not found: ${currentThemeName}`);
|
||||||
);
|
return;
|
||||||
|
|
||||||
// 设置默认主题
|
|
||||||
if (defaultDark) {
|
|
||||||
currentThemeIds.dark = defaultDark.id;
|
|
||||||
currentColors.dark = loadThemeColors(defaultDark);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultLight) {
|
|
||||||
currentThemeIds.light = defaultLight.id;
|
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 直接设置当前主题颜色
|
||||||
|
currentColors.value = theme.colors as ThemeColors;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 初始化主题
|
// 初始化主题
|
||||||
@@ -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:', themeName);
|
||||||
console.error('Theme not found:', themeId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载主题颜色
|
|
||||||
const colors = loadThemeColors(theme);
|
|
||||||
|
|
||||||
// 根据主题类型更新对应的颜色配置
|
|
||||||
if (theme.type === ThemeType.ThemeTypeDark) {
|
|
||||||
currentThemeIds.dark = themeId;
|
|
||||||
currentColors.dark = colors;
|
|
||||||
} else {
|
|
||||||
currentThemeIds.light = themeId;
|
|
||||||
currentColors.light = colors;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新编辑器主题
|
|
||||||
refreshEditorTheme();
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to switch theme:', error);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 直接设置当前主题颜色
|
||||||
|
currentColors.value = theme.colors as ThemeColors;
|
||||||
|
|
||||||
|
// 持久化到配置
|
||||||
|
await configStore.setCurrentTheme(themeName);
|
||||||
|
|
||||||
|
// 刷新编辑器
|
||||||
|
refreshEditorTheme();
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新当前主题的颜色配置
|
// 更新当前主题的颜色配置
|
||||||
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;
|
throw new Error('No theme selected');
|
||||||
const colors = isDark ? currentColors.dark : currentColors.light;
|
|
||||||
|
|
||||||
if (!themeId || !colors) {
|
|
||||||
throw new Error('No theme selected');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为数据库格式并保存
|
|
||||||
const dbColors = colors as ThemeColorConfig; // ThemeColorConfig from bindings
|
|
||||||
await ThemeService.UpdateTheme(themeId, dbColors);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to save theme:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const theme = allThemes.value.find(t => t.name === currentColors.value!.name);
|
||||||
|
if (!theme) {
|
||||||
|
throw new Error('Theme not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
throw new Error('No theme selected');
|
||||||
|
|
||||||
if (!themeId) {
|
|
||||||
throw new Error('No theme selected');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用后端重置服务
|
|
||||||
await ThemeService.ResetTheme(themeId);
|
|
||||||
|
|
||||||
// 重新加载主题
|
|
||||||
await loadAllThemes();
|
|
||||||
const theme = allThemes.value.find(t => t.id === themeId);
|
|
||||||
|
|
||||||
if (theme) {
|
|
||||||
const colors = loadThemeColors(theme);
|
|
||||||
if (isDark) {
|
|
||||||
currentColors.dark = colors;
|
|
||||||
} else {
|
|
||||||
currentColors.light = colors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新编辑器主题
|
|
||||||
refreshEditorTheme();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to reset theme:', error);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 调用后端重置
|
||||||
|
await ThemeService.ResetTheme(0, currentColors.value.name);
|
||||||
|
|
||||||
|
// 重新加载所有主题
|
||||||
|
await loadAllThemes();
|
||||||
|
|
||||||
|
const updatedTheme = allThemes.value.find(t => t.name === currentColors.value!.name);
|
||||||
|
|
||||||
|
if (updatedTheme) {
|
||||||
|
currentColors.value = updatedTheme.colors as ThemeColors;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshEditorTheme();
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 刷新编辑器主题
|
// 刷新编辑器主题
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
12
frontend/src/views/editor/theme/index.ts
Normal file
12
frontend/src/views/editor/theme/index.ts
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -109,8 +109,9 @@ type EditingConfig struct {
|
|||||||
|
|
||||||
// AppearanceConfig 外观设置配置
|
// AppearanceConfig 外观设置配置
|
||||||
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 更新设置配置
|
||||||
@@ -179,8 +180,9 @@ func NewDefaultAppConfig() *AppConfig {
|
|||||||
AutoSaveDelay: 2000,
|
AutoSaveDelay: 2000,
|
||||||
},
|
},
|
||||||
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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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) {
|
||||||
SELECT id, name, type, colors, is_default, created_at, updated_at
|
var query string
|
||||||
FROM themes
|
var args []interface{}
|
||||||
WHERE id = ?
|
|
||||||
LIMIT 1
|
if id > 0 {
|
||||||
`
|
query = `
|
||||||
|
SELECT id, name, type, colors, is_default, created_at, updated_at
|
||||||
|
FROM themes
|
||||||
|
WHERE id = ?
|
||||||
|
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) {
|
||||||
return nil, fmt.Errorf("theme not found with id: %d", id)
|
if id > 0 {
|
||||||
|
return nil, fmt.Errorf("theme not found with id: %d", id)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("theme not found with name: %s", name[0])
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to get theme by id: %w", err)
|
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 := `
|
||||||
|
|||||||
Reference in New Issue
Block a user