Added preset theme

This commit is contained in:
2025-10-19 23:57:03 +08:00
parent 03780b5bc7
commit 9a15df01ee
33 changed files with 2362 additions and 2307 deletions

View File

@@ -1191,9 +1191,20 @@ export class Theme {
} }
/** /**
* ThemeColorConfig 主题颜色配置 * ThemeColorConfig 主题颜色配置(与前端 ThemeColors 接口保持一致)
*/ */
export class ThemeColorConfig { export class ThemeColorConfig {
/**
* 主题基本信息
* 主题名称
*/
"name": string;
/**
* 是否为深色主题
*/
"dark": boolean;
/** /**
* 基础色调 * 基础色调
* 主背景色 * 主背景色
@@ -1201,7 +1212,7 @@ export class ThemeColorConfig {
"background": string; "background": string;
/** /**
* 次要背景色 * 次要背景色(用于代码块交替背景)
*/ */
"backgroundSecondary": string; "backgroundSecondary": string;
@@ -1211,6 +1222,17 @@ export class ThemeColorConfig {
"surface": string; "surface": string;
/** /**
* 下拉菜单背景
*/
"dropdownBackground": string;
/**
* 下拉菜单边框
*/
"dropdownBorder": string;
/**
* 文本颜色
* 主文本色 * 主文本色
*/ */
"foreground": string; "foreground": string;
@@ -1221,12 +1243,12 @@ export class ThemeColorConfig {
"foregroundSecondary": string; "foregroundSecondary": string;
/** /**
* 语法高亮
* 注释色 * 注释色
*/ */
"comment": string; "comment": string;
/** /**
* 语法高亮色 - 核心
* 关键字 * 关键字
*/ */
"keyword": string; "keyword": string;
@@ -1261,6 +1283,42 @@ export class ThemeColorConfig {
*/ */
"type": string; "type": string;
/**
* 语法高亮色 - 扩展
* 常量
*/
"constant": string;
/**
* 存储类型(如 static, const
*/
"storage": string;
/**
* 参数
*/
"parameter": string;
/**
* 类名
*/
"class": string;
/**
* 标题Markdown等
*/
"heading": string;
/**
* 无效内容/错误
*/
"invalid": string;
/**
* 正则表达式
*/
"regexp": string;
/** /**
* 界面元素 * 界面元素
* 光标 * 光标
@@ -1288,12 +1346,12 @@ export class ThemeColorConfig {
"lineNumber": string; "lineNumber": string;
/** /**
* 活动行号 * 活动行号颜色
*/ */
"activeLineNumber": string; "activeLineNumber": string;
/** /**
* 边框分割线 * 边框分割线
* 边框色 * 边框色
*/ */
"borderColor": string; "borderColor": string;
@@ -1304,7 +1362,7 @@ export class ThemeColorConfig {
"borderLight": string; "borderLight": string;
/** /**
* 搜索匹配 * 搜索匹配
* 搜索匹配 * 搜索匹配
*/ */
"searchMatch": string; "searchMatch": string;
@@ -1316,6 +1374,12 @@ export class ThemeColorConfig {
/** Creates a new ThemeColorConfig instance. */ /** Creates a new ThemeColorConfig instance. */
constructor($$source: Partial<ThemeColorConfig> = {}) { constructor($$source: Partial<ThemeColorConfig> = {}) {
if (!("name" in $$source)) {
this["name"] = "";
}
if (!("dark" in $$source)) {
this["dark"] = false;
}
if (!("background" in $$source)) { if (!("background" in $$source)) {
this["background"] = ""; this["background"] = "";
} }
@@ -1325,6 +1389,12 @@ export class ThemeColorConfig {
if (!("surface" in $$source)) { if (!("surface" in $$source)) {
this["surface"] = ""; this["surface"] = "";
} }
if (!("dropdownBackground" in $$source)) {
this["dropdownBackground"] = "";
}
if (!("dropdownBorder" in $$source)) {
this["dropdownBorder"] = "";
}
if (!("foreground" in $$source)) { if (!("foreground" in $$source)) {
this["foreground"] = ""; this["foreground"] = "";
} }
@@ -1355,6 +1425,27 @@ export class ThemeColorConfig {
if (!("type" in $$source)) { if (!("type" in $$source)) {
this["type"] = ""; this["type"] = "";
} }
if (!("constant" in $$source)) {
this["constant"] = "";
}
if (!("storage" in $$source)) {
this["storage"] = "";
}
if (!("parameter" in $$source)) {
this["parameter"] = "";
}
if (!("class" in $$source)) {
this["class"] = "";
}
if (!("heading" in $$source)) {
this["heading"] = "";
}
if (!("invalid" in $$source)) {
this["invalid"] = "";
}
if (!("regexp" in $$source)) {
this["regexp"] = "";
}
if (!("cursor" in $$source)) { if (!("cursor" in $$source)) {
this["cursor"] = ""; this["cursor"] = "";
} }

View File

@@ -42,12 +42,12 @@ export function GetAllThemes(): Promise<(models$0.Theme | null)[]> & { cancel():
} }
/** /**
* GetDefaultThemes 获取默认主题 * GetThemeByID 根据ID获取主题
*/ */
export function GetDefaultThemes(): Promise<{ [_: string]: models$0.Theme | null }> & { cancel(): void } { export function GetThemeByID(id: number): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3801788118) as any; let $resultPromise = $Call.ByID(3053137052, id) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType3($result); return $$createType1($result);
}) as any; }) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise); $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise; return $typingPromise;
@@ -66,10 +66,22 @@ export function GetThemeByType(themeType: models$0.ThemeType): Promise<models$0.
} }
/** /**
* ResetThemeColors 重置主题颜色为默认值 * GetThemesByType 根据类型获取所有主题
*/ */
export function ResetThemeColors(themeType: models$0.ThemeType): Promise<void> & { cancel(): void } { export function GetThemesByType(themeType: models$0.ThemeType): Promise<(models$0.Theme | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(342461245, themeType) as any; 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 重置主题为预设配置
*/
export function ResetTheme(id: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1806334457, id) as any;
return $resultPromise; return $resultPromise;
} }
@@ -90,10 +102,10 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
} }
/** /**
* UpdateThemeColors 更新主题颜色 * UpdateTheme 更新主题
*/ */
export function UpdateThemeColors(themeType: models$0.ThemeType, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } { export function UpdateTheme(id: number, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(2750902529, themeType, colors) as any; let $resultPromise = $Call.ByID(70189749, id, colors) as any;
return $resultPromise; return $resultPromise;
} }
@@ -101,4 +113,3 @@ export function UpdateThemeColors(themeType: models$0.ThemeType, colors: models$
const $$createType0 = models$0.Theme.createFrom; const $$createType0 = models$0.Theme.createFrom;
const $$createType1 = $Create.Nullable($$createType0); const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1); const $$createType2 = $Create.Array($$createType1);
const $$createType3 = $Create.Map($Create.Any, $$createType1);

View File

@@ -36,9 +36,9 @@
"@codemirror/lint": "^6.9.0", "@codemirror/lint": "^6.9.0",
"@codemirror/search": "^6.5.11", "@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2", "@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.5", "@codemirror/view": "^6.38.6",
"@cospaia/prettier-plugin-clojure": "^0.0.2", "@cospaia/prettier-plugin-clojure": "^0.0.2",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.2",
"@lezer/lr": "^1.4.2", "@lezer/lr": "^1.4.2",
"@prettier/plugin-xml": "^3.4.2", "@prettier/plugin-xml": "^3.4.2",
"@replit/codemirror-lang-svelte": "^6.0.0", "@replit/codemirror-lang-svelte": "^6.0.0",
@@ -62,23 +62,23 @@
"vue": "^3.5.22", "vue": "^3.5.22",
"vue-i18n": "^11.1.12", "vue-i18n": "^11.1.12",
"vue-pick-colors": "^1.8.0", "vue-pick-colors": "^1.8.0",
"vue-router": "^4.5.1" "vue-router": "^4.6.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.37.0", "@eslint/js": "^9.38.0",
"@lezer/generator": "^1.8.0", "@lezer/generator": "^1.8.0",
"@types/node": "^24.7.1", "@types/node": "^24.8.1",
"@types/remarkable": "^2.0.8", "@types/remarkable": "^2.0.8",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.1",
"@wailsio/runtime": "latest", "@wailsio/runtime": "latest",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"eslint": "^9.37.0", "eslint": "^9.38.0",
"eslint-plugin-vue": "^10.5.0", "eslint-plugin-vue": "^10.5.1",
"globals": "^16.4.0", "globals": "^16.4.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.46.0", "typescript-eslint": "^8.46.1",
"unplugin-vue-components": "^29.1.0", "unplugin-vue-components": "^29.1.0",
"vite": "^7.1.9", "vite": "^7.1.10",
"vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-node-polyfills": "^0.24.0",
"vue-eslint-parser": "^10.2.0", "vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.1.1" "vue-tsc": "^3.1.1"
@@ -554,9 +554,9 @@
} }
}, },
"node_modules/@codemirror/view": { "node_modules/@codemirror/view": {
"version": "6.38.5", "version": "6.38.6",
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.5.tgz", "resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.6.tgz",
"integrity": "sha512-SFVsNAgsAoou+BjRewMqN+m9jaztB9wCWN9RSRgePqUbq8UVlvJfku5zB2KVhLPgH/h0RLk38tvd4tGeAhygnw==", "integrity": "sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@codemirror/state": "^6.5.0", "@codemirror/state": "^6.5.0",
@@ -1046,13 +1046,13 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.21.0", "version": "0.21.1",
"resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.0.tgz", "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/object-schema": "^2.1.6", "@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1", "debug": "^4.3.1",
"minimatch": "^3.1.2" "minimatch": "^3.1.2"
}, },
@@ -1061,9 +1061,9 @@
} }
}, },
"node_modules/@eslint/config-helpers": { "node_modules/@eslint/config-helpers": {
"version": "0.4.0", "version": "0.4.1",
"resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.1.tgz",
"integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -1124,9 +1124,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.37.0", "version": "9.38.0",
"resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.37.0.tgz", "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.38.0.tgz",
"integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -1137,9 +1137,9 @@
} }
}, },
"node_modules/@eslint/object-schema": { "node_modules/@eslint/object-schema": {
"version": "2.1.6", "version": "2.1.7",
"resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.6.tgz", "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.7.tgz",
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@@ -1320,9 +1320,9 @@
} }
}, },
"node_modules/@lezer/common": { "node_modules/@lezer/common": {
"version": "1.2.3", "version": "1.3.0",
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.2.3.tgz", "resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.3.0.tgz",
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", "integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@lezer/cpp": { "node_modules/@lezer/cpp": {
@@ -1373,12 +1373,12 @@
} }
}, },
"node_modules/@lezer/highlight": { "node_modules/@lezer/highlight": {
"version": "1.2.1", "version": "1.2.2",
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.1.tgz", "resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.2.tgz",
"integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", "integrity": "sha512-z8TQwaBXXQIvG6i2g3e9cgMwUUXu9Ib7jo2qRRggdhwKpM56Dw3PM3wmexn+EGaaOZ7az0K7sjc3/gcGW7sz7A==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@lezer/common": "^1.0.0" "@lezer/common": "^1.3.0"
} }
}, },
"node_modules/@lezer/html": { "node_modules/@lezer/html": {
@@ -2327,9 +2327,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.7.1", "version": "24.8.1",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.7.1.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.8.1.tgz",
"integrity": "sha512-CmyhGZanP88uuC5GpWU9q+fI61j2SkhO3UGMUdfYRE6Bcy0ccyzn1Rqj9YAB/ZY4kOXmNf0ocah5GtphmLMP6Q==", "integrity": "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2344,17 +2344,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.1.tgz",
"integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", "integrity": "sha512-rUsLh8PXmBjdiPY+Emjz9NX2yHvhS11v0SR6xNJkm5GM1MO9ea/1GoDKlHHZGrOJclL/cZ2i/vRUYVtjRhrHVQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.10.0", "@eslint-community/regexpp": "^4.10.0",
"@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/scope-manager": "8.46.1",
"@typescript-eslint/type-utils": "8.46.0", "@typescript-eslint/type-utils": "8.46.1",
"@typescript-eslint/utils": "8.46.0", "@typescript-eslint/utils": "8.46.1",
"@typescript-eslint/visitor-keys": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.1",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^7.0.0", "ignore": "^7.0.0",
"natural-compare": "^1.4.0", "natural-compare": "^1.4.0",
@@ -2368,7 +2368,7 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/parser": "^8.46.0", "@typescript-eslint/parser": "^8.46.1",
"eslint": "^8.57.0 || ^9.0.0", "eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0" "typescript": ">=4.8.4 <6.0.0"
} }
@@ -2384,16 +2384,16 @@
} }
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.46.1.tgz",
"integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", "integrity": "sha512-6JSSaBZmsKvEkbRUkf7Zj7dru/8ZCrJxAqArcLaVMee5907JdtEbKGsZ7zNiIm/UAkpGUkaSMZEXShnN2D1HZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/scope-manager": "8.46.1",
"@typescript-eslint/types": "8.46.0", "@typescript-eslint/types": "8.46.1",
"@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.1",
"@typescript-eslint/visitor-keys": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@@ -2409,14 +2409,14 @@
} }
}, },
"node_modules/@typescript-eslint/project-service": { "node_modules/@typescript-eslint/project-service": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.46.1.tgz",
"integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", "integrity": "sha512-FOIaFVMHzRskXr5J4Jp8lFVV0gz5ngv3RHmn+E4HYxSJ3DgDzU7fVI1/M7Ijh1zf6S7HIoaIOtln1H5y8V+9Zg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.46.0", "@typescript-eslint/tsconfig-utils": "^8.46.1",
"@typescript-eslint/types": "^8.46.0", "@typescript-eslint/types": "^8.46.1",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@@ -2431,14 +2431,14 @@
} }
}, },
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.1.tgz",
"integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", "integrity": "sha512-weL9Gg3/5F0pVQKiF8eOXFZp8emqWzZsOJuWRUNtHT+UNV2xSJegmpCNQHy37aEQIbToTq7RHKhWvOsmbM680A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.46.0", "@typescript-eslint/types": "8.46.1",
"@typescript-eslint/visitor-keys": "8.46.0" "@typescript-eslint/visitor-keys": "8.46.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2449,9 +2449,9 @@
} }
}, },
"node_modules/@typescript-eslint/tsconfig-utils": { "node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.1.tgz",
"integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", "integrity": "sha512-X88+J/CwFvlJB+mK09VFqx5FE4H5cXD+H/Bdza2aEWkSb8hnWIQorNcscRl4IEo1Cz9VI/+/r/jnGWkbWPx54g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -2466,15 +2466,15 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.46.1.tgz",
"integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", "integrity": "sha512-+BlmiHIiqufBxkVnOtFwjah/vrkF4MtKKvpXrKSPLCkCtAp8H01/VV43sfqA98Od7nJpDcFnkwgyfQbOG0AMvw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.46.0", "@typescript-eslint/types": "8.46.1",
"@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.1",
"@typescript-eslint/utils": "8.46.0", "@typescript-eslint/utils": "8.46.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"ts-api-utils": "^2.1.0" "ts-api-utils": "^2.1.0"
}, },
@@ -2491,9 +2491,9 @@
} }
}, },
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.46.1.tgz",
"integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", "integrity": "sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -2505,16 +2505,16 @@
} }
}, },
"node_modules/@typescript-eslint/typescript-estree": { "node_modules/@typescript-eslint/typescript-estree": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.1.tgz",
"integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", "integrity": "sha512-uIifjT4s8cQKFQ8ZBXXyoUODtRoAd7F7+G8MKmtzj17+1UbdzFl52AzRyZRyKqPHhgzvXunnSckVu36flGy8cg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/project-service": "8.46.0", "@typescript-eslint/project-service": "8.46.1",
"@typescript-eslint/tsconfig-utils": "8.46.0", "@typescript-eslint/tsconfig-utils": "8.46.1",
"@typescript-eslint/types": "8.46.0", "@typescript-eslint/types": "8.46.1",
"@typescript-eslint/visitor-keys": "8.46.0", "@typescript-eslint/visitor-keys": "8.46.1",
"debug": "^4.3.4", "debug": "^4.3.4",
"fast-glob": "^3.3.2", "fast-glob": "^3.3.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
@@ -2560,16 +2560,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.46.1.tgz",
"integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", "integrity": "sha512-vkYUy6LdZS7q1v/Gxb2Zs7zziuXN0wxqsetJdeZdRe/f5dwJFglmuvZBfTUivCtjH725C1jWCDfpadadD95EDQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.7.0", "@eslint-community/eslint-utils": "^4.7.0",
"@typescript-eslint/scope-manager": "8.46.0", "@typescript-eslint/scope-manager": "8.46.1",
"@typescript-eslint/types": "8.46.0", "@typescript-eslint/types": "8.46.1",
"@typescript-eslint/typescript-estree": "8.46.0" "@typescript-eslint/typescript-estree": "8.46.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2584,13 +2584,13 @@
} }
}, },
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.1.tgz",
"integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", "integrity": "sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/types": "8.46.0", "@typescript-eslint/types": "8.46.1",
"eslint-visitor-keys": "^4.2.1" "eslint-visitor-keys": "^4.2.1"
}, },
"engines": { "engines": {
@@ -3997,25 +3997,24 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.37.0", "version": "9.38.0",
"resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.37.0.tgz", "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.38.0.tgz",
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.0", "@eslint/config-array": "^0.21.1",
"@eslint/config-helpers": "^0.4.0", "@eslint/config-helpers": "^0.4.1",
"@eslint/core": "^0.16.0", "@eslint/core": "^0.16.0",
"@eslint/eslintrc": "^3.3.1", "@eslint/eslintrc": "^3.3.1",
"@eslint/js": "9.37.0", "@eslint/js": "9.38.0",
"@eslint/plugin-kit": "^0.4.0", "@eslint/plugin-kit": "^0.4.0",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2", "@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6", "@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15",
"ajv": "^6.12.4", "ajv": "^6.12.4",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
@@ -4058,9 +4057,9 @@
} }
}, },
"node_modules/eslint-plugin-vue": { "node_modules/eslint-plugin-vue": {
"version": "10.5.0", "version": "10.5.1",
"resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.5.0.tgz", "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-10.5.1.tgz",
"integrity": "sha512-7BZHsG3kC2vei8F2W8hnfDi9RK+cv5eKPMvzBdrl8Vuc0hR5odGQRli8VVzUkrmUHkxFEm4Iio1r5HOKslO0Aw==", "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -6652,16 +6651,16 @@
} }
}, },
"node_modules/typescript-eslint": { "node_modules/typescript-eslint": {
"version": "8.46.0", "version": "8.46.1",
"resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.46.0.tgz", "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.46.1.tgz",
"integrity": "sha512-6+ZrB6y2bT2DX3K+Qd9vn7OFOJR+xSLDj+Aw/N3zBwUt27uTw2sw2TE2+UcY1RiyBZkaGbTkVg9SSdPNUG6aUw==", "integrity": "sha512-VHgijW803JafdSsDO8I761r3SHrgk4T00IdyQ+/UsthtgPRsBWQLqoSxOolxTpxRKi1kGXK0bSz4CoAc9ObqJA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "8.46.0", "@typescript-eslint/eslint-plugin": "8.46.1",
"@typescript-eslint/parser": "8.46.0", "@typescript-eslint/parser": "8.46.1",
"@typescript-eslint/typescript-estree": "8.46.0", "@typescript-eslint/typescript-estree": "8.46.1",
"@typescript-eslint/utils": "8.46.0" "@typescript-eslint/utils": "8.46.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -7029,9 +7028,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "7.1.9", "version": "7.1.10",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.9.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.10.tgz",
"integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==", "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -7250,9 +7249,9 @@
} }
}, },
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.5.1", "version": "4.6.3",
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.5.1.tgz", "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.3.tgz",
"integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@vue/devtools-api": "^6.6.4" "@vue/devtools-api": "^6.6.4"
@@ -7261,7 +7260,7 @@
"url": "https://github.com/sponsors/posva" "url": "https://github.com/sponsors/posva"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.2.0" "vue": "^3.5.0"
} }
}, },
"node_modules/vue-router/node_modules/@vue/devtools-api": { "node_modules/vue-router/node_modules/@vue/devtools-api": {

View File

@@ -41,9 +41,9 @@
"@codemirror/lint": "^6.9.0", "@codemirror/lint": "^6.9.0",
"@codemirror/search": "^6.5.11", "@codemirror/search": "^6.5.11",
"@codemirror/state": "^6.5.2", "@codemirror/state": "^6.5.2",
"@codemirror/view": "^6.38.5", "@codemirror/view": "^6.38.6",
"@cospaia/prettier-plugin-clojure": "^0.0.2", "@cospaia/prettier-plugin-clojure": "^0.0.2",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.2",
"@lezer/lr": "^1.4.2", "@lezer/lr": "^1.4.2",
"@prettier/plugin-xml": "^3.4.2", "@prettier/plugin-xml": "^3.4.2",
"@replit/codemirror-lang-svelte": "^6.0.0", "@replit/codemirror-lang-svelte": "^6.0.0",
@@ -67,23 +67,23 @@
"vue": "^3.5.22", "vue": "^3.5.22",
"vue-i18n": "^11.1.12", "vue-i18n": "^11.1.12",
"vue-pick-colors": "^1.8.0", "vue-pick-colors": "^1.8.0",
"vue-router": "^4.5.1" "vue-router": "^4.6.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.37.0", "@eslint/js": "^9.38.0",
"@lezer/generator": "^1.8.0", "@lezer/generator": "^1.8.0",
"@types/node": "^24.7.1", "@types/node": "^24.8.1",
"@types/remarkable": "^2.0.8", "@types/remarkable": "^2.0.8",
"@vitejs/plugin-vue": "^6.0.1", "@vitejs/plugin-vue": "^6.0.1",
"@wailsio/runtime": "latest", "@wailsio/runtime": "latest",
"cross-env": "^10.1.0", "cross-env": "^10.1.0",
"eslint": "^9.37.0", "eslint": "^9.38.0",
"eslint-plugin-vue": "^10.5.0", "eslint-plugin-vue": "^10.5.1",
"globals": "^16.4.0", "globals": "^16.4.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.46.0", "typescript-eslint": "^8.46.1",
"unplugin-vue-components": "^29.1.0", "unplugin-vue-components": "^29.1.0",
"vite": "^7.1.9", "vite": "^7.1.10",
"vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-node-polyfills": "^0.24.0",
"vue-eslint-parser": "^10.2.0", "vue-eslint-parser": "^10.2.0",
"vue-tsc": "^3.1.1" "vue-tsc": "^3.1.1"

View File

@@ -166,12 +166,17 @@ export default {
interface: 'Interface Elements', interface: 'Interface Elements',
border: 'Borders & Dividers', border: 'Borders & Dividers',
search: 'Search & Matching', search: 'Search & Matching',
// Base Colors
background: 'Main Background', background: 'Main Background',
backgroundSecondary: 'Secondary Background', backgroundSecondary: 'Secondary Background',
surface: 'Panel Background', surface: 'Panel Background',
dropdownBackground: 'Dropdown Background',
dropdownBorder: 'Dropdown Border',
// Text Colors
foreground: 'Primary Text', foreground: 'Primary Text',
foregroundSecondary: 'Secondary Text', foregroundSecondary: 'Secondary Text',
comment: 'Comments', comment: 'Comments',
// Syntax Highlighting - Core
keyword: 'Keywords', keyword: 'Keywords',
string: 'Strings', string: 'Strings',
function: 'Functions', function: 'Functions',
@@ -179,14 +184,25 @@ export default {
operator: 'Operators', operator: 'Operators',
variable: 'Variables', variable: 'Variables',
type: 'Types', type: 'Types',
// Syntax Highlighting - Extended
constant: 'Constants',
storage: 'Storage Type',
parameter: 'Parameters',
class: 'Class Names',
heading: 'Headings',
invalid: 'Invalid/Error',
regexp: 'Regular Expressions',
// Interface Elements
cursor: 'Cursor', cursor: 'Cursor',
selection: 'Selection Background', selection: 'Selection Background',
selectionBlur: 'Unfocused Selection', selectionBlur: 'Unfocused Selection',
activeLine: 'Active Line Highlight', activeLine: 'Active Line Highlight',
lineNumber: 'Line Numbers', lineNumber: 'Line Numbers',
activeLineNumber: 'Active Line Number', activeLineNumber: 'Active Line Number',
// Borders & Dividers
borderColor: 'Border Color', borderColor: 'Border Color',
borderLight: 'Light Border', borderLight: 'Light Border',
// Search & Matching
searchMatch: 'Search Match', searchMatch: 'Search Match',
matchingBracket: 'Matching Bracket' matchingBracket: 'Matching Bracket'
}, },
@@ -199,6 +215,7 @@ export default {
enableTabIndent: 'Enable Tab Indent', enableTabIndent: 'Enable Tab Indent',
language: 'Interface Language', language: 'Interface Language',
systemTheme: 'System Theme', systemTheme: 'System Theme',
presetTheme: 'Preset Theme',
saveOptions: 'Save Options', saveOptions: 'Save Options',
autoSaveDelay: 'Auto Save Delay (ms)', autoSaveDelay: 'Auto Save Delay (ms)',
updateSettings: 'Update Settings', updateSettings: 'Update Settings',

View File

@@ -166,6 +166,7 @@ export default {
enableTabIndent: '启用 Tab 缩进', enableTabIndent: '启用 Tab 缩进',
language: '界面语言', language: '界面语言',
systemTheme: '系统主题', systemTheme: '系统主题',
presetTheme: '预设主题',
saveOptions: '保存选项', saveOptions: '保存选项',
autoSaveDelay: '自动保存延迟(毫秒)', autoSaveDelay: '自动保存延迟(毫秒)',
updateSettings: '更新设置', updateSettings: '更新设置',
@@ -206,12 +207,17 @@ export default {
interface: '界面元素', interface: '界面元素',
border: '边框分割线', border: '边框分割线',
search: '搜索匹配', search: '搜索匹配',
// 基础色调
background: '主背景色', background: '主背景色',
backgroundSecondary: '次要背景色', backgroundSecondary: '次要背景色',
surface: '面板背景', surface: '面板背景',
dropdownBackground: '下拉菜单背景',
dropdownBorder: '下拉菜单边框',
// 文本颜色
foreground: '主文本色', foreground: '主文本色',
foregroundSecondary: '次要文本色', foregroundSecondary: '次要文本色',
comment: '注释色', comment: '注释色',
// 语法高亮 - 核心
keyword: '关键字', keyword: '关键字',
string: '字符串', string: '字符串',
function: '函数名', function: '函数名',
@@ -219,14 +225,25 @@ export default {
operator: '操作符', operator: '操作符',
variable: '变量', variable: '变量',
type: '类型', type: '类型',
// 语法高亮 - 扩展
constant: '常量',
storage: '存储类型',
parameter: '参数',
class: '类名',
heading: '标题',
invalid: '无效内容',
regexp: '正则表达式',
// 界面元素
cursor: '光标', cursor: '光标',
selection: '选中背景', selection: '选中背景',
selectionBlur: '失焦选中背景', selectionBlur: '失焦选中背景',
activeLine: '当前行高亮', activeLine: '当前行高亮',
lineNumber: '行号', lineNumber: '行号',
activeLineNumber: '活动行号', activeLineNumber: '活动行号',
// 边框和分割线
borderColor: '边框色', borderColor: '边框色',
borderLight: '浅色边框', borderLight: '浅色边框',
// 搜索和匹配
searchMatch: '搜索匹配', searchMatch: '搜索匹配',
matchingBracket: '匹配括号' matchingBracket: '匹配括号'
}, },

View File

@@ -1,33 +1,59 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { computed, reactive } from 'vue'; import { computed, reactive, ref } from 'vue';
import { SystemThemeType, ThemeType, 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 { defaultDarkColors } from '@/views/editor/theme/dark'; import type { ThemeColors } from '@/views/editor/theme/types';
import { defaultLightColors } from '@/views/editor/theme/light'; import { getThemeConfig } from '@/views/editor/theme/registry';
/** /**
* 主题管理 Store * 主题管理 Store
* 职责:管理主题状态颜色配置 * 职责:管理主题状态颜色配置和预设主题列表
*/ */
export const useThemeStore = defineStore('theme', () => { export const useThemeStore = defineStore('theme', () => {
const configStore = useConfigStore(); const configStore = useConfigStore();
// 响应式状态 // 所有主题列表(从数据库加载)
const themeColors = reactive({ const allThemes = ref<Theme[]>([]);
darkTheme: { ...defaultDarkColors },
lightTheme: { ...defaultLightColors } // 当前选中的主题 ID
const currentThemeIds = reactive({
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 getDefaultColors = (themeType: ThemeType) => const darkThemes = computed(() =>
themeType === ThemeType.ThemeTypeDark ? defaultDarkColors : defaultLightColors; allThemes.value.filter(t => t.type === ThemeType.ThemeTypeDark)
);
const lightThemes = computed(() =>
allThemes.value.filter(t => t.type === ThemeType.ThemeTypeLight)
);
// 计算属性:获取当前激活的主题对象
const activeTheme = computed(() => {
const isDark = currentTheme.value === SystemThemeType.SystemThemeDark ||
(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) => {
@@ -39,30 +65,77 @@ export const useThemeStore = defineStore('theme', () => {
document.documentElement.setAttribute('data-theme', themeMap[theme]); document.documentElement.setAttribute('data-theme', themeMap[theme]);
}; };
// 初始化主题颜色 // 从数据库加载所有主题
const loadAllThemes = async () => {
try {
const themes = await ThemeService.GetAllThemes();
allThemes.value = (themes || []).filter((t): t is Theme => t !== null);
return allThemes.value;
} catch (error) {
console.error('Failed to load themes from database:', error);
allThemes.value = [];
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 { try {
const themes = await ThemeService.GetDefaultThemes(); // 加载所有主题
await loadAllThemes();
// 如果没有获取到主题数据,使用默认值 // 查找默认主题
if (!themes) { const defaultDark = allThemes.value.find(
Object.assign(themeColors.darkTheme, defaultDarkColors); t => t.type === ThemeType.ThemeTypeDark && t.isDefault
Object.assign(themeColors.lightTheme, defaultLightColors); );
return; const defaultLight = allThemes.value.find(
t => t.type === ThemeType.ThemeTypeLight && t.isDefault
);
// 设置默认主题
if (defaultDark) {
currentThemeIds.dark = defaultDark.id;
currentColors.dark = loadThemeColors(defaultDark);
} }
// 更新主题颜色 if (defaultLight) {
if (themes[ThemeType.ThemeTypeDark]) { currentThemeIds.light = defaultLight.id;
Object.assign(themeColors.darkTheme, themes[ThemeType.ThemeTypeDark].colors); currentColors.light = loadThemeColors(defaultLight);
} }
if (themes[ThemeType.ThemeTypeLight]) {
Object.assign(themeColors.lightTheme, themes[ThemeType.ThemeTypeLight].colors); // 如果没有找到默认主题,使用第一个可用主题
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) { } catch (error) {
console.warn('Failed to load themes from database, using defaults:', error); console.error('Failed to initialize theme colors:', error);
// 如果数据库加载失败,使用默认主题 // 使用预设主题作为后备
Object.assign(themeColors.darkTheme, defaultDarkColors); currentColors.dark = getThemeConfig('default-dark');
Object.assign(themeColors.lightTheme, defaultLightColors); currentColors.light = getThemeConfig('default-light');
} }
}; };
@@ -73,77 +146,109 @@ export const useThemeStore = defineStore('theme', () => {
await initializeThemeColors(); await initializeThemeColors();
}; };
// 设置主题 // 设置系统主题模式(深色/浅色/自动)
const setTheme = async (theme: SystemThemeType) => { const setTheme = async (theme: SystemThemeType) => {
await configStore.setSystemTheme(theme); await configStore.setSystemTheme(theme);
applyThemeToDOM(theme); applyThemeToDOM(theme);
refreshEditorTheme(); refreshEditorTheme();
}; };
// 更新主题颜色 - 合并逻辑,减少重复代码 // 切换到指定的预设主题通过主题ID
const updateThemeColors = (darkColors?: any, lightColors?: any): boolean => { const switchToTheme = async (themeId: number) => {
let hasChanges = false; try {
const theme = allThemes.value.find(t => t.id === themeId);
if (!theme) {
console.error('Theme not found:', themeId);
return false;
}
const updateColors = (target: any, source: any) => { // 加载主题颜色
if (!source) return false; const colors = loadThemeColors(theme);
let changed = false; // 根据主题类型更新对应的颜色配置
Object.entries(source).forEach(([key, value]) => { if (theme.type === ThemeType.ThemeTypeDark) {
if (value !== undefined && target[key] !== value) { currentThemeIds.dark = themeId;
target[key] = value; currentColors.dark = colors;
changed = true; } else {
} currentThemeIds.light = themeId;
}); currentColors.light = colors;
return changed; }
};
hasChanges = updateColors(themeColors.darkTheme, darkColors) || hasChanges; // 刷新编辑器主题
hasChanges = updateColors(themeColors.lightTheme, lightColors) || hasChanges; refreshEditorTheme();
return true;
return hasChanges; } catch (error) {
console.error('Failed to switch theme:', error);
return false;
}
}; };
// 保存主题颜色到数据库 // 更新当前主题颜色配置
const saveThemeColors = async () => { const updateCurrentColors = (colors: Partial<ThemeColors>, isDark: boolean) => {
try { const target = isDark ? currentColors.dark : currentColors.light;
const darkColors = ThemeColorConfig.createFrom(themeColors.darkTheme); if (!target) return;
const lightColors = ThemeColorConfig.createFrom(themeColors.lightTheme);
await Promise.all([ Object.assign(target, colors);
ThemeService.UpdateThemeColors(ThemeType.ThemeTypeDark, darkColors), };
ThemeService.UpdateThemeColors(ThemeType.ThemeTypeLight, lightColors)
]); // 保存当前主题颜色到数据库
const saveCurrentTheme = async (isDark: boolean) => {
try {
const themeId = isDark ? currentThemeIds.dark : currentThemeIds.light;
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) { } catch (error) {
console.error('Failed to save theme colors:', error); console.error('Failed to save theme:', error);
throw error; throw error;
} }
}; };
// 重置主题颜色 // 重置当前主题为预设配置
const resetThemeColors = async (themeType: 'darkTheme' | 'lightTheme') => { const resetCurrentTheme = async (isDark: boolean) => {
try { try {
const dbThemeType = themeType === 'darkTheme' ? ThemeType.ThemeTypeDark : ThemeType.ThemeTypeLight; const themeId = isDark ? currentThemeIds.dark : currentThemeIds.light;
// 1. 调用后端重置服务 if (!themeId) {
await ThemeService.ResetThemeColors(dbThemeType); throw new Error('No theme selected');
}
// 2. 更新内存中的颜色状态 // 调用后端重置服务
const defaultColors = getDefaultColors(dbThemeType); await ThemeService.ResetTheme(themeId);
Object.assign(themeColors[themeType], defaultColors);
// 3. 刷新编辑器主题 // 重新加载主题
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(); refreshEditorTheme();
return true; return true;
} catch (error) { } catch (error) {
console.error('Failed to reset theme colors:', error); console.error('Failed to reset theme:', error);
return false; return false;
} }
}; };
// 刷新编辑器主题 // 刷新编辑器主题
const refreshEditorTheme = () => { const refreshEditorTheme = () => {
// 使用当前主题重新应用DOM主题
applyThemeToDOM(currentTheme.value); applyThemeToDOM(currentTheme.value);
const editorStore = useEditorStore(); const editorStore = useEditorStore();
@@ -151,14 +256,24 @@ export const useThemeStore = defineStore('theme', () => {
}; };
return { return {
// 状态
allThemes,
darkThemes,
lightThemes,
currentTheme, currentTheme,
themeColors, currentThemeIds,
currentColors,
activeTheme,
// 方法
setTheme, setTheme,
switchToTheme,
initializeTheme, initializeTheme,
loadAllThemes,
updateCurrentColors,
saveCurrentTheme,
resetCurrentTheme,
refreshEditorTheme,
applyThemeToDOM, applyThemeToDOM,
updateThemeColors,
saveThemeColors,
resetThemeColors,
refreshEditorTheme
}; };
}); });

View File

@@ -1,8 +1,7 @@
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 { SystemThemeType } from '@/../bindings/voidraft/internal/models/models';
import { createDarkTheme } from '@/views/editor/theme/dark'; import { createThemeByColors } from '@/views/editor/theme/registry';
import { createLightTheme } from '@/views/editor/theme/light';
import { useThemeStore } from '@/stores/themeStore'; import { useThemeStore } from '@/stores/themeStore';
// 主题区间 - 用于动态切换主题 // 主题区间 - 用于动态切换主题
@@ -11,23 +10,25 @@ export const themeCompartment = new Compartment();
/** /**
* 根据主题类型获取主题扩展 * 根据主题类型获取主题扩展
*/ */
const getThemeExtension = (themeType: SystemThemeType): Extension => { const getThemeExtension = (themeType: SystemThemeType): Extension | null => {
const themeStore = useThemeStore(); const themeStore = useThemeStore();
// 处理 auto 主题类型 // 处理 auto 主题类型
let actualTheme: SystemThemeType = themeType; let isDark = themeType === SystemThemeType.SystemThemeDark;
if (themeType === SystemThemeType.SystemThemeAuto) { if (themeType === SystemThemeType.SystemThemeAuto) {
actualTheme = window.matchMedia('(prefers-color-scheme: dark)').matches isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
? SystemThemeType.SystemThemeDark
: SystemThemeType.SystemThemeLight;
} }
// 根据主题类型创建主题 // 根据主题类型获取对应的颜色配置
if (actualTheme === SystemThemeType.SystemThemeLight) { const colors = isDark ? themeStore.currentColors.dark : themeStore.currentColors.light;
return createLightTheme(themeStore.themeColors.lightTheme);
} else { if (!colors) {
return createDarkTheme(themeStore.themeColors.darkTheme); console.warn('Theme colors not loaded yet');
return null;
} }
// 使用颜色配置创建主题
return createThemeByColors(colors);
}; };
/** /**
@@ -35,6 +36,12 @@ const getThemeExtension = (themeType: SystemThemeType): Extension => {
*/ */
export const createThemeExtension = (themeType: SystemThemeType = SystemThemeType.SystemThemeDark): Extension => { export const createThemeExtension = (themeType: SystemThemeType = SystemThemeType.SystemThemeDark): Extension => {
const extension = getThemeExtension(themeType); const extension = getThemeExtension(themeType);
// 如果主题未加载,返回空扩展
if (!extension) {
return themeCompartment.of([]);
}
return themeCompartment.of(extension); return themeCompartment.of(extension);
}; };
@@ -48,6 +55,13 @@ export const updateEditorTheme = (view: EditorView, themeType: SystemThemeType):
try { try {
const extension = getThemeExtension(themeType); const extension = getThemeExtension(themeType);
// 如果主题未加载,不更新
if (!extension) {
console.warn('Cannot update theme: theme not loaded');
return;
}
view.dispatch({ view.dispatch({
effects: themeCompartment.reconfigure(extension) effects: themeCompartment.reconfigure(extension)
}); });

View File

@@ -1,48 +1,17 @@
import {EditorView} from '@codemirror/view'; import {EditorView} from '@codemirror/view';
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'; import {HighlightStyle, syntaxHighlighting} from '@codemirror/language';
import {tags} from '@lezer/highlight'; import {tags} from '@lezer/highlight';
import {Extension} from '@codemirror/state';
import type {ThemeColors} from './types';
// 默认深色主题颜色 /**
export const defaultDarkColors = { *
// 基础色调 * @param colors
background: '#252B37', // 主背景色 * @returns CodeMirror Extension数组
backgroundSecondary: '#213644', // 次要背景色 */
surface: '#474747', // 面板背景 export function createBaseTheme(colors: ThemeColors): Extension {
// 编辑器主题样式
// 文本颜色 const theme = EditorView.theme({
foreground: '#9BB586', // 主文本色
foregroundSecondary: '#9c9c9c', // 次要文本色
comment: '#6272a4', // 注释色
// 语法高亮色
keyword: '#ff79c6', // 关键字
string: '#f1fa8c', // 字符串
function: '#50fa7b', // 函数名
number: '#bd93f9', // 数字
operator: '#ff79c6', // 操作符
variable: '#8fbcbb', // 变量
type: '#8be9fd', // 类型
// 界面元素
cursor: '#ffffff', // 光标
selection: '#0865a9', // 选中背景
selectionBlur: '#225377', // 失焦选中背景
activeLine: '#ffffff0a', // 当前行高亮
lineNumber: '#ffffff26', // 行号
activeLineNumber: '#ffffff99', // 活动行号
// 边框和分割线
borderColor: '#1e222a', // 边框色
borderLight: '#ffffff19', // 浅色边框
// 搜索和匹配
searchMatch: '#8fbcbb', // 搜索匹配
matchingBracket: '#ffffff19', // 匹配括号
};
// 创建深色主题
export function createDarkTheme(colors = defaultDarkColors) {
const darkTheme = EditorView.theme({
'&': { '&': {
color: colors.foreground, color: colors.foreground,
backgroundColor: colors.background, backgroundColor: colors.background,
@@ -79,6 +48,9 @@ export function createDarkTheme(colors = defaultDarkColors) {
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': { '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
backgroundColor: colors.selection, backgroundColor: colors.selection,
}, },
'.cm-content ::selection': {
backgroundColor: colors.selection,
},
'.cm-activeLine.code-empty-block-selected': { '.cm-activeLine.code-empty-block-selected': {
backgroundColor: colors.selection, backgroundColor: colors.selection,
}, },
@@ -90,9 +62,10 @@ export function createDarkTheme(colors = defaultDarkColors) {
// 行号区域 // 行号区域
'.cm-gutters': { '.cm-gutters': {
backgroundColor: 'rgba(0,0,0, 0.1)', backgroundColor: colors.dark ? 'rgba(0,0,0, 0.1)' : 'rgba(0,0,0, 0.04)',
color: colors.lineNumber, color: colors.lineNumber,
border: 'none', border: 'none',
borderRight: colors.dark ? 'none' : `1px solid ${colors.borderLight}`,
padding: '0 2px 0 4px', padding: '0 2px 0 4px',
userSelect: 'none', userSelect: 'none',
}, },
@@ -115,9 +88,20 @@ export function createDarkTheme(colors = defaultDarkColors) {
'.cm-foldPlaceholder': { '.cm-foldPlaceholder': {
backgroundColor: 'transparent', backgroundColor: 'transparent',
border: 'none', border: 'none',
color: '#ddd', color: colors.comment,
}, },
// 面板
'.cm-panels': {
backgroundColor: colors.dropdownBackground,
color: colors.foreground
},
'.cm-panels.cm-panels-top': {
borderBottom: '2px solid black'
},
'.cm-panels.cm-panels-bottom': {
borderTop: '2px solid black'
},
// 搜索匹配 // 搜索匹配
'.cm-searchMatch': { '.cm-searchMatch': {
@@ -125,11 +109,11 @@ export function createDarkTheme(colors = defaultDarkColors) {
outline: `1px solid ${colors.searchMatch}`, outline: `1px solid ${colors.searchMatch}`,
}, },
'.cm-searchMatch.cm-searchMatch-selected': { '.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: colors.foreground, backgroundColor: colors.searchMatch,
color: colors.background, color: colors.background,
}, },
'.cm-selectionMatch': { '.cm-selectionMatch': {
backgroundColor: '#50606D', backgroundColor: colors.dark ? '#50606D' : '#e6f3ff',
}, },
// 括号匹配 // 括号匹配
@@ -141,7 +125,7 @@ export function createDarkTheme(colors = defaultDarkColors) {
color: 'inherit', color: 'inherit',
}, },
'&.cm-focused .cm-nonmatchingBracket': { '&.cm-focused .cm-nonmatchingBracket': {
outline: '0.5px solid #bc8f8f', outline: colors.dark ? '0.5px solid #bc8f8f' : '0.5px solid #d73a49',
}, },
// 编辑器焦点 // 编辑器焦点
@@ -151,8 +135,10 @@ export function createDarkTheme(colors = defaultDarkColors) {
// 工具提示 // 工具提示
'.cm-tooltip': { '.cm-tooltip': {
border: 'none', border: colors.dark ? 'none' : `1px solid ${colors.dropdownBorder}`,
backgroundColor: colors.surface, backgroundColor: colors.surface,
color: colors.foreground,
boxShadow: colors.dark ? 'none' : '0 2px 8px rgba(0,0,0,0.1)',
}, },
'.cm-tooltip .cm-tooltip-arrow:before': { '.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent', borderTopColor: 'transparent',
@@ -169,7 +155,7 @@ export function createDarkTheme(colors = defaultDarkColors) {
}, },
}, },
// 代码块层 // 代码块层(自定义)
'.code-blocks-layer': { '.code-blocks-layer': {
width: '100%', width: '100%',
}, },
@@ -188,34 +174,36 @@ export function createDarkTheme(colors = defaultDarkColors) {
background: colors.backgroundSecondary, background: colors.backgroundSecondary,
borderTop: `1px solid ${colors.borderColor}`, borderTop: `1px solid ${colors.borderColor}`,
}, },
'.code-blocks-math-result': {
paddingLeft: "12px",
position: "relative",
},
".code-blocks-math-result .inner": {
background: "#0e1217",
color: "#a0e7c7",
padding: '0px 4px',
borderRadius: '2px',
boxShadow: '0 0 3px rgba(0,0,0, 0.3)',
cursor: 'pointer',
whiteSpace: "nowrap",
},
'.code-blocks-math-result-copied': {
position: "absolute",
top: "0px",
left: "0px",
marginLeft: "calc(100% + 10px)",
width: "60px",
transition: "opacity 500ms",
transitionDelay: "1000ms",
color: "rgba(220,240,230, 1.0)",
},
'.code-blocks-math-result-copied.fade-out': {
opacity: 0,
},
// 代码块开始标记 // 数学计算结果(自定义)
'.code-blocks-math-result': {
paddingLeft: "12px",
position: "relative",
},
".code-blocks-math-result .inner": {
background: colors.dark ? '#0e1217' : '#48b57e',
color: colors.dark ? '#a0e7c7' : '#fff',
padding: '0px 4px',
borderRadius: '2px',
boxShadow: colors.dark ? '0 0 3px rgba(0,0,0, 0.3)' : '0 0 3px rgba(0,0,0, 0.1)',
cursor: 'pointer',
whiteSpace: "nowrap",
},
'.code-blocks-math-result-copied': {
position: "absolute",
top: "0px",
left: "0px",
marginLeft: "calc(100% + 10px)",
width: "60px",
transition: "opacity 500ms",
transitionDelay: "1000ms",
color: colors.dark ? 'rgba(220,240,230, 1.0)' : 'rgba(0,0,0, 0.8)',
},
'.code-blocks-math-result-copied.fade-out': {
opacity: 0,
},
// 代码块开始标记(自定义)
'.code-block-start': { '.code-block-start': {
height: '12px', height: '12px',
position: 'relative', position: 'relative',
@@ -223,47 +211,79 @@ export function createDarkTheme(colors = defaultDarkColors) {
'.code-block-start.first': { '.code-block-start.first': {
height: '0px', height: '0px',
}, },
}, {dark: true}); }, {dark: colors.dark});
// 语法高亮样式 // 语法高亮样式
const darkHighlightStyle = HighlightStyle.define([ const highlightStyle = HighlightStyle.define([
// 关键字
{tag: tags.keyword, color: colors.keyword}, {tag: tags.keyword, color: colors.keyword},
{tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName], color: colors.variable},
{tag: [tags.variableName], color: colors.variable}, // 操作符
{tag: [tags.function(tags.variableName)], color: colors.function},
{tag: [tags.labelName], color: colors.operator},
{tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: colors.keyword},
{tag: [tags.definition(tags.name), tags.separator], color: colors.function},
{tag: [tags.brace], color: colors.variable},
{tag: [tags.annotation], color: '#d30102'},
{tag: [tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace], color: colors.number},
{tag: [tags.typeName, tags.className], color: colors.type},
{tag: [tags.operator, tags.operatorKeyword], color: colors.operator}, {tag: [tags.operator, tags.operatorKeyword], color: colors.operator},
{tag: [tags.tagName], color: colors.number},
{tag: [tags.squareBracket], color: '#bf616a'}, // 名称、变量
{tag: [tags.angleBracket], color: '#d08770'}, {tag: [tags.name, tags.deleted, tags.character, tags.macroName], color: colors.variable},
{tag: [tags.attributeName], color: colors.variable}, {tag: [tags.variableName], color: colors.variable},
{tag: [tags.regexp], color: colors.string}, {tag: [tags.labelName], color: colors.operator},
{tag: [tags.atom, tags.bool, tags.special(tags.variableName)], color: colors.variable},
// 函数
{tag: [tags.function(tags.variableName)], color: colors.function},
{tag: [tags.propertyName], color: colors.function},
// 类型、类
{tag: [tags.typeName], color: colors.type},
{tag: [tags.className], color: colors.class},
// 常量
{tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: colors.constant},
// 字符串
{tag: [tags.processingInstruction, tags.string, tags.inserted], color: colors.string},
{tag: [tags.special(tags.string)], color: colors.string},
{tag: [tags.quote], color: colors.comment}, {tag: [tags.quote], color: colors.comment},
{tag: [tags.string], color: colors.string},
{tag: tags.link, color: colors.variable, textDecoration: 'underline'}, // 数字
{tag: [tags.url, tags.escape, tags.special(tags.string)], color: colors.string}, {tag: [tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace], color: colors.number},
{tag: [tags.meta], color: colors.comment},
{tag: [tags.comment], color: colors.comment, fontStyle: 'italic'}, // 正则表达式
{tag: [tags.url, tags.escape, tags.regexp, tags.link], color: colors.regexp},
// 注释
{tag: [tags.meta, tags.comment], color: colors.comment, fontStyle: 'italic'},
// 分隔符、括号
{tag: [tags.definition(tags.name), tags.separator], color: colors.variable},
{tag: [tags.brace], color: colors.variable},
{tag: [tags.squareBracket], color: colors.dark ? '#bf616a' : colors.keyword},
{tag: [tags.angleBracket], color: colors.dark ? '#d08770' : colors.operator},
{tag: [tags.attributeName], color: colors.variable},
// 标签
{tag: [tags.tagName], color: colors.number},
// 注解
{tag: [tags.annotation], color: colors.invalid},
// 特殊样式
{tag: tags.strong, fontWeight: 'bold'}, {tag: tags.strong, fontWeight: 'bold'},
{tag: tags.emphasis, fontStyle: 'italic'}, {tag: tags.emphasis, fontStyle: 'italic'},
{tag: tags.strikethrough, textDecoration: 'line-through'}, {tag: tags.strikethrough, textDecoration: 'line-through'},
{tag: tags.heading, fontWeight: 'bold', color: colors.keyword}, {tag: tags.link, color: colors.variable, textDecoration: 'underline'},
// 标题
{tag: tags.heading, fontWeight: 'bold', color: colors.heading},
{tag: [tags.heading1, tags.heading2], fontSize: '1.4em'}, {tag: [tags.heading1, tags.heading2], fontSize: '1.4em'},
{tag: [tags.heading3, tags.heading4], fontSize: '1.2em'}, {tag: [tags.heading3, tags.heading4], fontSize: '1.2em'},
{tag: [tags.heading5, tags.heading6], fontSize: '1.1em'}, {tag: [tags.heading5, tags.heading6], fontSize: '1.1em'},
// 无效内容
{tag: tags.invalid, color: colors.invalid},
]); ]);
return [ return [
darkTheme, theme,
syntaxHighlighting(darkHighlightStyle), syntaxHighlighting(highlightStyle),
]; ];
} }
// 默认深色主题
export const dark = createDarkTheme(defaultDarkColors);

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'aura', name: 'aura',
dark: true, dark: true,
// 基础色调
background: '#21202e', background: '#21202e',
foreground: '#edecee', backgroundSecondary: '#21202e',
selection: '#3d375e7f', surface: '#21202e',
cursor: '#a277ff',
dropdownBackground: '#21202e', dropdownBackground: '#21202e',
dropdownBorder: '#3b334b', dropdownBorder: '#3b334b',
activeLine: '#4d4b6622',
lineNumber: '#a394f033', // 文本颜色
activeLineNumber: '#cdccce', foreground: '#edecee',
matchingBracket: '#a394f033', foregroundSecondary: '#edecee',
keyword: '#a277ff',
storage: '#a277ff',
variable: '#edecee',
parameter: '#edecee',
function: '#ffca85',
string: '#61ffca',
constant: '#61ffca',
type: '#82e2ff',
class: '#82e2ff',
number: '#61ffca',
comment: '#6d6d6d', comment: '#6d6d6d',
// 语法高亮色 - 核心
keyword: '#a277ff',
string: '#61ffca',
function: '#ffca85',
number: '#61ffca',
operator: '#a277ff',
variable: '#edecee',
type: '#82e2ff',
// 语法高亮色 - 扩展
constant: '#61ffca',
storage: '#a277ff',
parameter: '#edecee',
class: '#82e2ff',
heading: '#a277ff', heading: '#a277ff',
invalid: '#ff6767', invalid: '#ff6767',
regexp: '#61ffca', regexp: '#61ffca',
// 界面元素
cursor: '#a277ff',
selection: '#3d375e7f',
selectionBlur: '#3d375e7f',
activeLine: '#4d4b6622',
lineNumber: '#a394f033',
activeLineNumber: '#cdccce',
// 边框和分割线
borderColor: '#3b334b',
borderLight: '#edecee19',
// 搜索和匹配
searchMatch: '#61ffca',
matchingBracket: '#a394f033',
} }
export const auraTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Aura 主题
'&': { export const aura: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const auraHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const aura: Extension = [
auraTheme,
syntaxHighlighting(auraHighlightStyle),
]

View File

@@ -0,0 +1,63 @@
import {createBaseTheme} from '../base';
import type {ThemeColors} from '../types';
// 默认深色主题颜色
export const defaultDarkColors: ThemeColors = {
// 主题信息
name: 'default-dark',
dark: true,
// 基础色调
background: '#252B37', // 主背景色
backgroundSecondary: '#213644', // 次要背景色
surface: '#474747', // 面板背景
dropdownBackground: '#252B37', // 下拉菜单背景
dropdownBorder: '#ffffff19', // 下拉菜单边框
// 文本颜色
foreground: '#9BB586', // 主文本色
foregroundSecondary: '#9c9c9c', // 次要文本色
comment: '#6272a4', // 注释色
// 语法高亮色 - 核心
keyword: '#ff79c6', // 关键字
string: '#f1fa8c', // 字符串
function: '#50fa7b', // 函数名
number: '#bd93f9', // 数字
operator: '#ff79c6', // 操作符
variable: '#8fbcbb', // 变量
type: '#8be9fd', // 类型
// 语法高亮色 - 扩展
constant: '#bd93f9', // 常量
storage: '#ff79c6', // 存储类型
parameter: '#8fbcbb', // 参数
class: '#8be9fd', // 类名
heading: '#ff79c6', // 标题
invalid: '#d30102', // 无效内容
regexp: '#f1fa8c', // 正则表达式
// 界面元素
cursor: '#ffffff', // 光标
selection: '#0865a9', // 选中背景
selectionBlur: '#225377', // 失焦选中背景
activeLine: '#ffffff0a', // 当前行高亮
lineNumber: '#ffffff26', // 行号
activeLineNumber: '#ffffff99', // 活动行号
// 边框和分割线
borderColor: '#1e222a', // 边框色
borderLight: '#ffffff19', // 浅色边框
// 搜索和匹配
searchMatch: '#8fbcbb', // 搜索匹配
matchingBracket: '#ffffff19', // 匹配括号
};
// 创建深色主题
export function createDarkTheme(colors: ThemeColors = defaultDarkColors) {
return createBaseTheme({...colors, dark: true});
}
// 默认深色主题
export const defaultDark = createDarkTheme(defaultDarkColors);

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'dracula', name: 'dracula',
dark: true, dark: true,
// 基础色调
background: '#282A36', background: '#282A36',
foreground: '#F8F8F2', backgroundSecondary: '#282A36',
selection: '#44475A', surface: '#282A36',
cursor: '#F8F8F2',
dropdownBackground: '#282A36', dropdownBackground: '#282A36',
dropdownBorder: '#191A21', dropdownBorder: '#191A21',
activeLine: '#53576c22',
lineNumber: '#6272A4', // 文本颜色
activeLineNumber: '#F8F8F2', foreground: '#F8F8F2',
matchingBracket: '#44475A', foregroundSecondary: '#F8F8F2',
keyword: '#FF79C6',
storage: '#FF79C6',
variable: '#F8F8F2',
parameter: '#F8F8F2',
function: '#50FA7B',
string: '#F1FA8C',
constant: '#BD93F9',
type: '#8BE9FD',
class: '#8BE9FD',
number: '#BD93F9',
comment: '#6272A4', comment: '#6272A4',
// 语法高亮色 - 核心
keyword: '#FF79C6',
string: '#F1FA8C',
function: '#50FA7B',
number: '#BD93F9',
operator: '#FF79C6',
variable: '#F8F8F2',
type: '#8BE9FD',
// 语法高亮色 - 扩展
constant: '#BD93F9',
storage: '#FF79C6',
parameter: '#F8F8F2',
class: '#8BE9FD',
heading: '#BD93F9', heading: '#BD93F9',
invalid: '#FF5555', invalid: '#FF5555',
regexp: '#F1FA8C', regexp: '#F1FA8C',
// 界面元素
cursor: '#F8F8F2',
selection: '#44475A',
selectionBlur: '#44475A',
activeLine: '#53576c22',
lineNumber: '#6272A4',
activeLineNumber: '#F8F8F2',
// 边框和分割线
borderColor: '#191A21',
borderLight: '#F8F8F219',
// 搜索和匹配
searchMatch: '#50FA7B',
matchingBracket: '#44475A',
} }
export const draculaTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Dracula 主题
'&': { export const dracula: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const draculaHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const dracula: Extension = [
draculaTheme,
syntaxHighlighting(draculaHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView, lineNumbers} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'githubDark', name: 'github-dark',
dark: true, dark: true,
// 基础色调
background: '#24292e', background: '#24292e',
foreground: '#d1d5da', backgroundSecondary: '#24292e',
selection: '#3392FF44', surface: '#24292e',
cursor: '#c8e1ff',
dropdownBackground: '#24292e', dropdownBackground: '#24292e',
dropdownBorder: '#1b1f23', dropdownBorder: '#1b1f23',
activeLine: '#4d566022',
lineNumber: '#444d56', // 文本颜色
activeLineNumber: '#e1e4e8', foreground: '#d1d5da',
matchingBracket: '#17E5E650', foregroundSecondary: '#d1d5da',
keyword: '#f97583',
storage: '#f97583',
variable: '#ffab70',
parameter: '#e1e4e8',
function: '#79b8ff',
string: '#9ecbff',
constant: '#79b8ff',
type: '#79b8ff',
class: '#b392f0',
number: '#79b8ff',
comment: '#6a737d', comment: '#6a737d',
// 语法高亮色 - 核心
keyword: '#f97583',
string: '#9ecbff',
function: '#79b8ff',
number: '#79b8ff',
operator: '#f97583',
variable: '#ffab70',
type: '#79b8ff',
// 语法高亮色 - 扩展
constant: '#79b8ff',
storage: '#f97583',
parameter: '#e1e4e8',
class: '#b392f0',
heading: '#79b8ff', heading: '#79b8ff',
invalid: '#f97583', invalid: '#f97583',
regexp: '#9ecbff', regexp: '#9ecbff',
// 界面元素
cursor: '#c8e1ff',
selection: '#3392FF44',
selectionBlur: '#3392FF44',
activeLine: '#4d566022',
lineNumber: '#444d56',
activeLineNumber: '#e1e4e8',
// 边框和分割线
borderColor: '#1b1f23',
borderLight: '#d1d5da19',
// 搜索和匹配
searchMatch: '#79b8ff',
matchingBracket: '#17E5E650',
} }
export const githubDarkTheme = EditorView.theme({ // 使用通用主题工厂函数创建 GitHub Dark 主题
'&': { export const githubDark: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const githubDarkHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const githubDark: Extension = [
githubDarkTheme,
syntaxHighlighting(githubDarkHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'materialDark', name: 'material-dark',
dark: true, dark: true,
// 基础色调
background: '#263238', background: '#263238',
foreground: '#EEFFFF', backgroundSecondary: '#263238',
selection: '#80CBC420', surface: '#263238',
cursor: '#FFCC00',
dropdownBackground: '#263238', dropdownBackground: '#263238',
dropdownBorder: '#FFFFFF10', dropdownBorder: '#FFFFFF10',
activeLine: '#4c616c22',
lineNumber: '#37474F', // 文本颜色
activeLineNumber: '#607a86', foreground: '#EEFFFF',
matchingBracket: '#263238', foregroundSecondary: '#EEFFFF',
keyword: '#C792EA',
storage: '#C792EA',
variable: '#EEFFFF',
parameter: '#EEFFFF',
function: '#82AAFF',
string: '#C3E88D',
constant: '#F78C6C',
type: '#B2CCD6',
class: '#FFCB6B',
number: '#F78C6C',
comment: '#546E7A', comment: '#546E7A',
// 语法高亮色 - 核心
keyword: '#C792EA',
string: '#C3E88D',
function: '#82AAFF',
number: '#F78C6C',
operator: '#C792EA',
variable: '#EEFFFF',
type: '#B2CCD6',
// 语法高亮色 - 扩展
constant: '#F78C6C',
storage: '#C792EA',
parameter: '#EEFFFF',
class: '#FFCB6B',
heading: '#C3E88D', heading: '#C3E88D',
invalid: '#FF5370', invalid: '#FF5370',
regexp: '#89DDFF', regexp: '#89DDFF',
// 界面元素
cursor: '#FFCC00',
selection: '#80CBC420',
selectionBlur: '#80CBC420',
activeLine: '#4c616c22',
lineNumber: '#37474F',
activeLineNumber: '#607a86',
// 边框和分割线
borderColor: '#FFFFFF10',
borderLight: '#EEFFFF19',
// 搜索和匹配
searchMatch: '#82AAFF',
matchingBracket: '#263238',
} }
export const materialDarkTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Material Dark 主题
'&': { export const materialDark: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const materialDarkHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const materialDark: Extension = [
materialDarkTheme,
syntaxHighlighting(materialDarkHighlightStyle),
]

View File

@@ -1,7 +1,6 @@
import {EditorView} from "@codemirror/view"
import {Extension} from "@codemirror/state" import {Extension} from "@codemirror/state"
import {HighlightStyle, syntaxHighlighting} from "@codemirror/language" import {createBaseTheme} from '../base'
import {tags as t} from "@lezer/highlight" import type {ThemeColors} from '../types'
// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors // Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors
@@ -22,133 +21,56 @@ const chalky = "#e5c07b",
selection = "#3E4451", selection = "#3E4451",
cursor = "#528bff" cursor = "#528bff"
/// The colors used in the theme, as CSS color strings. export const config: ThemeColors = {
export const color = { name: 'one-dark',
chalky, dark: true,
coral,
cyan, // 基础色调
invalid, background: background,
ivory, backgroundSecondary: highlightBackground,
stone, surface: tooltipBackground,
malibu, dropdownBackground: darkBackground,
sage, dropdownBorder: stone,
whiskey,
violet, // 文本颜色
darkBackground, foreground: ivory,
highlightBackground, foregroundSecondary: stone,
background, comment: stone,
tooltipBackground,
selection, // 语法高亮色 - 核心
cursor keyword: violet,
string: sage,
function: malibu,
number: chalky,
operator: cyan,
variable: coral,
type: chalky,
// 语法高亮色 - 扩展
constant: whiskey,
storage: violet,
parameter: coral,
class: chalky,
heading: coral,
invalid: invalid,
regexp: cyan,
// 界面元素
cursor: cursor,
selection: selection,
selectionBlur: selection,
activeLine: '#6699ff0b',
lineNumber: stone,
activeLineNumber: ivory,
// 边框和分割线
borderColor: darkBackground,
borderLight: ivory + '19',
// 搜索和匹配
searchMatch: malibu,
matchingBracket: '#bad0f847',
} }
/// The editor theme styles for One Dark. // 使用通用主题工厂函数创建 One Dark 主题
export const oneDarkTheme = EditorView.theme({ export const oneDark: Extension = createBaseTheme(config)
"&": {
color: ivory,
backgroundColor: background
},
".cm-content": {
caretColor: cursor
},
".cm-cursor, .cm-dropCursor": {borderLeftColor: cursor},
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": {backgroundColor: selection},
".cm-panels": {backgroundColor: darkBackground, color: ivory},
".cm-panels.cm-panels-top": {borderBottom: "2px solid black"},
".cm-panels.cm-panels-bottom": {borderTop: "2px solid black"},
".cm-searchMatch": {
backgroundColor: "#72a1ff59",
outline: "1px solid #457dff"
},
".cm-searchMatch.cm-searchMatch-selected": {
backgroundColor: "#6199ff2f"
},
".cm-activeLine": {backgroundColor: "#6699ff0b"},
".cm-selectionMatch": {backgroundColor: "#aafe661a"},
"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
backgroundColor: "#bad0f847"
},
".cm-gutters": {
backgroundColor: background,
color: stone,
border: "none"
},
".cm-activeLineGutter": {
backgroundColor: highlightBackground
},
".cm-foldPlaceholder": {
backgroundColor: "transparent",
border: "none",
color: "#ddd"
},
".cm-tooltip": {
border: "none",
backgroundColor: tooltipBackground
},
".cm-tooltip .cm-tooltip-arrow:before": {
borderTopColor: "transparent",
borderBottomColor: "transparent"
},
".cm-tooltip .cm-tooltip-arrow:after": {
borderTopColor: tooltipBackground,
borderBottomColor: tooltipBackground
},
".cm-tooltip-autocomplete": {
"& > ul > li[aria-selected]": {
backgroundColor: highlightBackground,
color: ivory
}
}
}, {dark: true})
/// The highlighting style for code in the One Dark theme.
export const oneDarkHighlightStyle = HighlightStyle.define([
{tag: t.keyword,
color: violet},
{tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
color: coral},
{tag: [t.function(t.variableName), t.labelName],
color: malibu},
{tag: [t.color, t.constant(t.name), t.standard(t.name)],
color: whiskey},
{tag: [t.definition(t.name), t.separator],
color: ivory},
{tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
color: chalky},
{tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)],
color: cyan},
{tag: [t.meta, t.comment],
color: stone},
{tag: t.strong,
fontWeight: "bold"},
{tag: t.emphasis,
fontStyle: "italic"},
{tag: t.strikethrough,
textDecoration: "line-through"},
{tag: t.link,
color: stone,
textDecoration: "underline"},
{tag: t.heading,
fontWeight: "bold",
color: coral},
{tag: [t.atom, t.bool, t.special(t.variableName)],
color: whiskey },
{tag: [t.processingInstruction, t.string, t.inserted],
color: sage},
{tag: t.invalid,
color: invalid},
])
/// Extension to enable the One Dark theme (both the editor theme and
/// the highlight style).
export const oneDark: Extension = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'solarizedDark', name: 'solarized-dark',
dark: true, dark: true,
// 基础色调
background: '#002B36', background: '#002B36',
foreground: '#93A1A1', backgroundSecondary: '#002B36',
selection: '#274642', surface: '#002B36',
cursor: '#D30102',
dropdownBackground: '#002B36', dropdownBackground: '#002B36',
dropdownBorder: '#2AA19899', dropdownBorder: '#2AA19899',
activeLine: '#005b7022',
lineNumber: '#93A1A1', // 文本颜色
activeLineNumber: '#949494', foreground: '#93A1A1',
matchingBracket: '#073642', foregroundSecondary: '#93A1A1',
keyword: '#859900',
storage: '#93A1A1',
variable: '#268BD2',
parameter: '#268BD2',
function: '#268BD2',
string: '#2AA198',
constant: '#CB4B16',
type: '#CB4B16',
class: '#CB4B16',
number: '#D33682',
comment: '#586E75', comment: '#586E75',
// 语法高亮色 - 核心
keyword: '#859900',
string: '#2AA198',
function: '#268BD2',
number: '#D33682',
operator: '#859900',
variable: '#268BD2',
type: '#CB4B16',
// 语法高亮色 - 扩展
constant: '#CB4B16',
storage: '#93A1A1',
parameter: '#268BD2',
class: '#CB4B16',
heading: '#268BD2', heading: '#268BD2',
invalid: '#DC322F', invalid: '#DC322F',
regexp: '#DC322F', regexp: '#DC322F',
// 界面元素
cursor: '#D30102',
selection: '#274642',
selectionBlur: '#274642',
activeLine: '#005b7022',
lineNumber: '#93A1A1',
activeLineNumber: '#949494',
// 边框和分割线
borderColor: '#073642',
borderLight: '#93A1A119',
// 搜索和匹配
searchMatch: '#2AA198',
matchingBracket: '#073642',
} }
export const solarizedDarkTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Solarized Dark 主题
'&': { export const solarizedDark: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const solarizedDarkHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const solarizedDark: Extension = [
solarizedDarkTheme,
syntaxHighlighting(solarizedDarkHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'tokyoNightStorm', name: 'tokyo-night-storm',
dark: true, dark: true,
// 基础色调
background: '#24283b', background: '#24283b',
foreground: '#7982a9', backgroundSecondary: '#24283b',
selection: '#6f7bb630', surface: '#24283b',
cursor: '#c0caf5',
dropdownBackground: '#24283b', dropdownBackground: '#24283b',
dropdownBorder: '#7982a9', dropdownBorder: '#7982a9',
activeLine: '#4d547722',
lineNumber: '#3b4261', // 文本颜色
activeLineNumber: '#737aa2', foreground: '#7982a9',
matchingBracket: '#1f2335', foregroundSecondary: '#7982a9',
keyword: '#bb9af7',
storage: '#bb9af7',
variable: '#c0caf5',
parameter: '#c0caf5',
function: '#7aa2f7',
string: '#9ece6a',
constant: '#bb9af7',
type: '#2ac3de',
class: '#c0caf5',
number: '#ff9e64',
comment: '#565f89', comment: '#565f89',
// 语法高亮色 - 核心
keyword: '#bb9af7',
string: '#9ece6a',
function: '#7aa2f7',
number: '#ff9e64',
operator: '#bb9af7',
variable: '#c0caf5',
type: '#2ac3de',
// 语法高亮色 - 扩展
constant: '#bb9af7',
storage: '#bb9af7',
parameter: '#c0caf5',
class: '#c0caf5',
heading: '#89ddff', heading: '#89ddff',
invalid: '#ff5370', invalid: '#ff5370',
regexp: '#b4f9f8', regexp: '#b4f9f8',
// 界面元素
cursor: '#c0caf5',
selection: '#6f7bb630',
selectionBlur: '#6f7bb630',
activeLine: '#4d547722',
lineNumber: '#3b4261',
activeLineNumber: '#737aa2',
// 边框和分割线
borderColor: '#1f2335',
borderLight: '#7982a919',
// 搜索和匹配
searchMatch: '#7aa2f7',
matchingBracket: '#1f2335',
} }
export const tokyoNightStormTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Tokyo Night Storm 主题
'&': { export const tokyoNightStorm: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const tokyoNightStormHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const tokyoNightStorm: Extension = [
tokyoNightStormTheme,
syntaxHighlighting(tokyoNightStormHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'tokyoNight', name: 'tokyo-night',
dark: true, dark: true,
// 基础色调
background: '#1a1b26', background: '#1a1b26',
foreground: '#787c99', backgroundSecondary: '#1a1b26',
selection: '#515c7e40', surface: '#1a1b26',
cursor: '#c0caf5',
dropdownBackground: '#1a1b26', dropdownBackground: '#1a1b26',
dropdownBorder: '#787c99', dropdownBorder: '#787c99',
activeLine: '#43455c22',
lineNumber: '#363b54', // 文本颜色
activeLineNumber: '#737aa2', foreground: '#787c99',
matchingBracket: '#16161e', foregroundSecondary: '#787c99',
keyword: '#bb9af7',
storage: '#bb9af7',
variable: '#c0caf5',
parameter: '#c0caf5',
function: '#7aa2f7',
string: '#9ece6a',
constant: '#bb9af7',
type: '#0db9d7',
class: '#c0caf5',
number: '#ff9e64',
comment: '#444b6a', comment: '#444b6a',
// 语法高亮色 - 核心
keyword: '#bb9af7',
string: '#9ece6a',
function: '#7aa2f7',
number: '#ff9e64',
operator: '#bb9af7',
variable: '#c0caf5',
type: '#0db9d7',
// 语法高亮色 - 扩展
constant: '#bb9af7',
storage: '#bb9af7',
parameter: '#c0caf5',
class: '#c0caf5',
heading: '#89ddff', heading: '#89ddff',
invalid: '#ff5370', invalid: '#ff5370',
regexp: '#b4f9f8', regexp: '#b4f9f8',
// 界面元素
cursor: '#c0caf5',
selection: '#515c7e40',
selectionBlur: '#515c7e40',
activeLine: '#43455c22',
lineNumber: '#363b54',
activeLineNumber: '#737aa2',
// 边框和分割线
borderColor: '#16161e',
borderLight: '#787c9919',
// 搜索和匹配
searchMatch: '#7aa2f7',
matchingBracket: '#16161e',
} }
export const tokyoNightTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Tokyo Night 主题
'&': { export const tokyoNight: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const tokyoNightHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const tokyoNight: Extension = [
tokyoNightTheme,
syntaxHighlighting(tokyoNightHighlightStyle),
]

View File

@@ -1,273 +0,0 @@
import {EditorView} from '@codemirror/view';
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language';
import {tags} from '@lezer/highlight';
// 默认浅色主题颜色
export const defaultLightColors = {
// 基础色调
background: '#ffffff', // 主背景色
backgroundSecondary: '#f1faf1', // 次要背景色
surface: '#f5f5f5', // 面板背景
// 文本颜色
foreground: '#444d56', // 主文本色
foregroundSecondary: '#6a737d', // 次要文本色
comment: '#6a737d', // 注释色
// 语法高亮色
keyword: '#d73a49', // 关键字
string: '#032f62', // 字符串
function: '#005cc5', // 函数名
number: '#005cc5', // 数字
operator: '#d73a49', // 操作符
variable: '#24292e', // 变量
type: '#6f42c1', // 类型
// 界面元素
cursor: '#000000', // 光标
selection: '#77baff', // 选中背景
selectionBlur: '#b2c2ca', // 失焦选中背景
activeLine: '#0000000a', // 当前行高亮
lineNumber: '#00000040', // 行号
activeLineNumber: '#000000aa', // 活动行号
// 边框和分割线
borderColor: '#dfdfdf', // 边框色
borderLight: '#0000000c', // 浅色边框
// 搜索和匹配
searchMatch: '#005cc5', // 搜索匹配
matchingBracket: '#00000019', // 匹配括号
};
// 创建浅色主题
export function createLightTheme(colors = defaultLightColors) {
const lightTheme = EditorView.theme({
'&': {
color: colors.foreground,
backgroundColor: colors.background,
},
// 确保编辑器容器背景一致
'.cm-editor': {
backgroundColor: colors.background,
},
// 确保滚动区域背景一致
'.cm-scroller': {
backgroundColor: colors.background,
},
// 编辑器内容
'.cm-content': {
caretColor: colors.cursor,
paddingTop: '4px',
},
// 光标
'.cm-cursor, .cm-dropCursor': {
borderLeftColor: colors.cursor,
borderLeftWidth: '2px',
paddingTop: '4px',
marginTop: '-2px',
},
// 选择
'.cm-selectionBackground': {
backgroundColor: colors.selectionBlur,
},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
backgroundColor: colors.selection,
},
'.cm-activeLine.code-empty-block-selected': {
backgroundColor: colors.selection,
},
// 当前行高亮
'.cm-activeLine': {
backgroundColor: colors.activeLine
},
// 行号区域
'.cm-gutters': {
backgroundColor: 'rgba(0,0,0, 0.04)',
color: colors.lineNumber,
border: 'none',
borderRight: `1px solid ${colors.borderLight}`,
padding: '0 2px 0 4px',
userSelect: 'none',
},
'.cm-activeLineGutter': {
backgroundColor: 'transparent',
color: colors.activeLineNumber,
},
// 折叠功能
'.cm-foldGutter': {
marginLeft: '0px',
},
'.cm-foldGutter .cm-gutterElement': {
opacity: 0,
transition: 'opacity 400ms',
},
'.cm-gutters:hover .cm-gutterElement': {
opacity: 1,
},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: colors.comment,
},
// 搜索匹配
'.cm-searchMatch': {
backgroundColor: 'transparent',
outline: `1px solid ${colors.searchMatch}`,
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: colors.searchMatch,
color: colors.background,
},
'.cm-selectionMatch': {
backgroundColor: '#e6f3ff',
},
// 括号匹配
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
outline: `0.5px solid ${colors.searchMatch}`,
},
'&.cm-focused .cm-matchingBracket': {
backgroundColor: colors.matchingBracket,
color: 'inherit',
},
'&.cm-focused .cm-nonmatchingBracket': {
outline: '0.5px solid #d73a49',
},
// 编辑器焦点
'&.cm-editor.cm-focused': {
outline: 'none',
},
// 工具提示
'.cm-tooltip': {
border: 'none',
backgroundColor: colors.surface,
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: colors.surface,
borderBottomColor: colors.surface,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
backgroundColor: colors.activeLine,
color: colors.foreground,
},
},
// 代码块层
'.code-blocks-layer': {
width: '100%',
},
'.code-blocks-layer .block-even, .code-blocks-layer .block-odd': {
width: '100%',
boxSizing: 'content-box',
},
'.code-blocks-layer .block-even': {
background: colors.background,
borderTop: `1px solid ${colors.borderColor}`,
},
'.code-blocks-layer .block-even:first-child': {
borderTop: 'none',
},
'.code-blocks-layer .block-odd': {
background: colors.backgroundSecondary,
borderTop: `1px solid ${colors.borderColor}`,
},
'.code-blocks-math-result': {
paddingLeft: "12px",
position: "relative",
},
'.code-blocks-math-result .inner': {
background: '#48b57e',
color: '#fff',
padding: '0px 4px',
borderRadius: '2px',
boxShadow: '0 0 3px rgba(0,0,0, 0.1)',
cursor: 'pointer',
whiteSpace: "nowrap",
},
'.code-blocks-math-result-copied': {
position: "absolute",
top: "0px",
left: "0px",
marginLeft: "calc(100% + 10px)",
width: "60px",
transition: "opacity 500ms",
transitionDelay: "1000ms",
color: "rgba(0,0,0, 0.8)",
},
'.code-blocks-math-result-copied.fade-out': {
opacity: 0,
},
// 代码块开始标记
'.code-block-start': {
height: '12px',
},
'.code-block-start.first': {
height: '0px',
},
}, {dark: false});
// 语法高亮样式
const lightHighlightStyle = HighlightStyle.define([
{tag: tags.keyword, color: colors.keyword},
{tag: [tags.name, tags.deleted, tags.character, tags.propertyName, tags.macroName], color: colors.variable},
{tag: [tags.variableName], color: colors.variable},
{tag: [tags.function(tags.variableName)], color: colors.function},
{tag: [tags.labelName], color: colors.operator},
{tag: [tags.color, tags.constant(tags.name), tags.standard(tags.name)], color: colors.keyword},
{tag: [tags.definition(tags.name), tags.separator], color: colors.function},
{tag: [tags.brace], color: colors.variable},
{tag: [tags.annotation], color: '#d73a49'},
{
tag: [tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace],
color: colors.number
},
{tag: [tags.typeName, tags.className], color: colors.type},
{tag: [tags.operator, tags.operatorKeyword], color: colors.operator},
{tag: [tags.tagName], color: colors.type},
{tag: [tags.squareBracket], color: colors.keyword},
{tag: [tags.angleBracket], color: colors.operator},
{tag: [tags.attributeName], color: colors.variable},
{tag: [tags.regexp], color: colors.string},
{tag: [tags.quote], color: colors.comment},
{tag: [tags.string], color: colors.string},
{tag: tags.link, color: colors.function, textDecoration: 'underline'},
{tag: [tags.url, tags.escape, tags.special(tags.string)], color: colors.string},
{tag: [tags.meta], color: colors.comment},
{tag: [tags.comment], color: colors.comment, fontStyle: 'italic'},
{tag: tags.strong, fontWeight: 'bold'},
{tag: tags.emphasis, fontStyle: 'italic'},
{tag: tags.strikethrough, textDecoration: 'line-through'},
{tag: tags.heading, fontWeight: 'bold', color: colors.keyword},
{tag: [tags.heading1, tags.heading2], fontSize: '1.4em'},
{tag: [tags.heading3, tags.heading4], fontSize: '1.2em'},
{tag: [tags.heading5, tags.heading6], fontSize: '1.1em'},
]);
return [
lightTheme,
syntaxHighlighting(lightHighlightStyle),
];
}
// 默认浅色主题
export const light = createLightTheme(defaultLightColors);

View File

@@ -0,0 +1,63 @@
import {createBaseTheme} from '../base';
import type {ThemeColors} from '../types';
// 默认浅色主题颜色
export const defaultLightColors: ThemeColors = {
// 主题信息
name: 'default-light',
dark: false,
// 基础色调
background: '#ffffff', // 主背景色
backgroundSecondary: '#f1faf1', // 次要背景色
surface: '#f5f5f5', // 面板背景
dropdownBackground: '#ffffff', // 下拉菜单背景
dropdownBorder: '#e1e4e8', // 下拉菜单边框
// 文本颜色
foreground: '#444d56', // 主文本色
foregroundSecondary: '#6a737d', // 次要文本色
comment: '#6a737d', // 注释色
// 语法高亮色 - 核心
keyword: '#d73a49', // 关键字
string: '#032f62', // 字符串
function: '#005cc5', // 函数名
number: '#005cc5', // 数字
operator: '#d73a49', // 操作符
variable: '#24292e', // 变量
type: '#6f42c1', // 类型
// 语法高亮色 - 扩展
constant: '#005cc5', // 常量
storage: '#d73a49', // 存储类型
parameter: '#24292e', // 参数
class: '#6f42c1', // 类名
heading: '#d73a49', // 标题
invalid: '#cb2431', // 无效内容
regexp: '#032f62', // 正则表达式
// 界面元素
cursor: '#000000', // 光标
selection: '#77baff', // 选中背景
selectionBlur: '#b2c2ca', // 失焦选中背景
activeLine: '#0000000a', // 当前行高亮
lineNumber: '#00000040', // 行号
activeLineNumber: '#000000aa', // 活动行号
// 边框和分割线
borderColor: '#dfdfdf', // 边框色
borderLight: '#0000000c', // 浅色边框
// 搜索和匹配
searchMatch: '#005cc5', // 搜索匹配
matchingBracket: '#00000019', // 匹配括号
};
// 创建浅色主题
export function createLightTheme(colors: ThemeColors = defaultLightColors) {
return createBaseTheme({...colors, dark: false});
}
// 默认浅色主题
export const defaultLight = createLightTheme(defaultLightColors);

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'githubLight', name: 'github-light',
dark: false, dark: false,
// 基础色调
background: '#fff', background: '#fff',
foreground: '#444d56', backgroundSecondary: '#fff',
selection: '#0366d625', surface: '#fff',
cursor: '#044289',
dropdownBackground: '#fff', dropdownBackground: '#fff',
dropdownBorder: '#e1e4e8', dropdownBorder: '#e1e4e8',
activeLine: '#c6c6c622',
lineNumber: '#1b1f234d', // 文本颜色
activeLineNumber: '#24292e', foreground: '#444d56',
matchingBracket: '#34d05840', foregroundSecondary: '#444d56',
keyword: '#d73a49',
storage: '#d73a49',
variable: '#e36209',
parameter: '#24292e',
function: '#005cc5',
string: '#032f62',
constant: '#005cc5',
type: '#005cc5',
class: '#6f42c1',
number: '#005cc5',
comment: '#6a737d', comment: '#6a737d',
// 语法高亮色 - 核心
keyword: '#d73a49',
string: '#032f62',
function: '#005cc5',
number: '#005cc5',
operator: '#d73a49',
variable: '#e36209',
type: '#005cc5',
// 语法高亮色 - 扩展
constant: '#005cc5',
storage: '#d73a49',
parameter: '#24292e',
class: '#6f42c1',
heading: '#005cc5', heading: '#005cc5',
invalid: '#cb2431', invalid: '#cb2431',
regexp: '#032f62', regexp: '#032f62',
// 界面元素
cursor: '#044289',
selection: '#0366d625',
selectionBlur: '#0366d625',
activeLine: '#c6c6c622',
lineNumber: '#1b1f234d',
activeLineNumber: '#24292e',
// 边框和分割线
borderColor: '#e1e4e8',
borderLight: '#444d5619',
// 搜索和匹配
searchMatch: '#005cc5',
matchingBracket: '#34d05840',
} }
export const githubLightTheme = EditorView.theme({ // 使用通用主题工厂函数创建 GitHub Light 主题
'&': { export const githubLight: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const githubLightHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const githubLight: Extension = [
githubLightTheme,
syntaxHighlighting(githubLightHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'materialLight', name: 'material-light',
dark: false, dark: false,
// 基础色调
background: '#FAFAFA', background: '#FAFAFA',
foreground: '#90A4AE', backgroundSecondary: '#FAFAFA',
selection: '#80CBC440', surface: '#FAFAFA',
cursor: '#272727',
dropdownBackground: '#FAFAFA', dropdownBackground: '#FAFAFA',
dropdownBorder: '#00000010', dropdownBorder: '#00000010',
activeLine: '#c2c2c222',
lineNumber: '#CFD8DC', // 文本颜色
activeLineNumber: '#7E939E', foreground: '#90A4AE',
matchingBracket: '#FAFAFA', foregroundSecondary: '#90A4AE',
keyword: '#7C4DFF',
storage: '#7C4DFF',
variable: '#90A4AE',
parameter: '#90A4AE',
function: '#6182B8',
string: '#91B859',
constant: '#F76D47',
type: '#8796B0',
class: '#FFB62C',
number: '#F76D47',
comment: '#90A4AE', comment: '#90A4AE',
// 语法高亮色 - 核心
keyword: '#7C4DFF',
string: '#91B859',
function: '#6182B8',
number: '#F76D47',
operator: '#7C4DFF',
variable: '#90A4AE',
type: '#8796B0',
// 语法高亮色 - 扩展
constant: '#F76D47',
storage: '#7C4DFF',
parameter: '#90A4AE',
class: '#FFB62C',
heading: '#91B859', heading: '#91B859',
invalid: '#E53935', invalid: '#E53935',
regexp: '#39ADB5', regexp: '#39ADB5',
// 界面元素
cursor: '#272727',
selection: '#80CBC440',
selectionBlur: '#80CBC440',
activeLine: '#c2c2c222',
lineNumber: '#CFD8DC',
activeLineNumber: '#7E939E',
// 边框和分割线
borderColor: '#00000010',
borderLight: '#90A4AE19',
// 搜索和匹配
searchMatch: '#6182B8',
matchingBracket: '#FAFAFA',
} }
export const materialLightTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Material Light 主题
'&': { export const materialLight: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const materialLightHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const materialLight: Extension = [
materialLightTheme,
syntaxHighlighting(materialLightHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'solarizedLight', name: 'solarized-light',
dark: false, dark: false,
// 基础色调
background: '#FDF6E3', background: '#FDF6E3',
foreground: '#586E75', backgroundSecondary: '#FDF6E3',
selection: '#EEE8D5', surface: '#FDF6E3',
cursor: '#657B83',
dropdownBackground: '#FDF6E3', dropdownBackground: '#FDF6E3',
dropdownBorder: '#D3AF86', dropdownBorder: '#D3AF86',
activeLine: '#d5bd5c22',
lineNumber: '#586E75', // 文本颜色
activeLineNumber: '#567983', foreground: '#586E75',
matchingBracket: '#EEE8D5', foregroundSecondary: '#586E75',
keyword: '#859900',
storage: '#586E75',
variable: '#268BD2',
parameter: '#268BD2',
function: '#268BD2',
string: '#2AA198',
constant: '#CB4B16',
type: '#CB4B16',
class: '#CB4B16',
number: '#D33682',
comment: '#93A1A1', comment: '#93A1A1',
// 语法高亮色 - 核心
keyword: '#859900',
string: '#2AA198',
function: '#268BD2',
number: '#D33682',
operator: '#859900',
variable: '#268BD2',
type: '#CB4B16',
// 语法高亮色 - 扩展
constant: '#CB4B16',
storage: '#586E75',
parameter: '#268BD2',
class: '#CB4B16',
heading: '#268BD2', heading: '#268BD2',
invalid: '#DC322F', invalid: '#DC322F',
regexp: '#DC322F', regexp: '#DC322F',
// 界面元素
cursor: '#657B83',
selection: '#EEE8D5',
selectionBlur: '#EEE8D5',
activeLine: '#d5bd5c22',
lineNumber: '#586E75',
activeLineNumber: '#567983',
// 边框和分割线
borderColor: '#EEE8D5',
borderLight: '#586E7519',
// 搜索和匹配
searchMatch: '#268BD2',
matchingBracket: '#EEE8D5',
} }
export const solarizedLightTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Solarized Light 主题
'&': { export const solarizedLight: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const solarizedLightHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const solarizedLight: Extension = [
solarizedLightTheme,
syntaxHighlighting(solarizedLightHighlightStyle),
]

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state' import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language' import {createBaseTheme} from '../base'
import {tags as t} from '@lezer/highlight' import type {ThemeColors} from '../types'
export const config = { export const config: ThemeColors = {
name: 'tokyoNightDay', name: 'tokyo-night-day',
dark: false, dark: false,
// 基础色调
background: '#e1e2e7', background: '#e1e2e7',
foreground: '#6a6f8e', backgroundSecondary: '#e1e2e7',
selection: '#8591b840', surface: '#e1e2e7',
cursor: '#3760bf',
dropdownBackground: '#e1e2e7', dropdownBackground: '#e1e2e7',
dropdownBorder: '#6a6f8e', dropdownBorder: '#6a6f8e',
activeLine: '#a7aaba22',
lineNumber: '#b3b6cd', // 文本颜色
activeLineNumber: '#68709a', foreground: '#6a6f8e',
matchingBracket: '#e9e9ec', foregroundSecondary: '#6a6f8e',
keyword: '#9854f1',
storage: '#9854f1',
variable: '#3760bf',
parameter: '#3760bf',
function: '#2e7de9',
string: '#587539',
constant: '#9854f1',
type: '#07879d',
class: '#3760bf',
number: '#b15c00',
comment: '#9da3c2', comment: '#9da3c2',
// 语法高亮色 - 核心
keyword: '#9854f1',
string: '#587539',
function: '#2e7de9',
number: '#b15c00',
operator: '#9854f1',
variable: '#3760bf',
type: '#07879d',
// 语法高亮色 - 扩展
constant: '#9854f1',
storage: '#9854f1',
parameter: '#3760bf',
class: '#3760bf',
heading: '#006a83', heading: '#006a83',
invalid: '#ff3e64', invalid: '#ff3e64',
regexp: '#2e5857', regexp: '#2e5857',
// 界面元素
cursor: '#3760bf',
selection: '#8591b840',
selectionBlur: '#8591b840',
activeLine: '#a7aaba22',
lineNumber: '#b3b6cd',
activeLineNumber: '#68709a',
// 边框和分割线
borderColor: '#e9e9ec',
borderLight: '#6a6f8e19',
// 搜索和匹配
searchMatch: '#2e7de9',
matchingBracket: '#e9e9ec',
} }
export const tokyoNightDayTheme = EditorView.theme({ // 使用通用主题工厂函数创建 Tokyo Night Day 主题
'&': { export const tokyoNightDay: Extension = createBaseTheme(config)
color: config.foreground,
backgroundColor: config.background,
},
'.cm-content': {caretColor: config.cursor},
'.cm-cursor, .cm-dropCursor': {borderLeftColor: config.cursor},
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection': {backgroundColor: config.selection},
'.cm-panels': {backgroundColor: config.dropdownBackground, color: config.foreground},
'.cm-panels.cm-panels-top': {borderBottom: '2px solid black'},
'.cm-panels.cm-panels-bottom': {borderTop: '2px solid black'},
'.cm-searchMatch': {
backgroundColor: config.dropdownBackground,
outline: `1px solid ${config.dropdownBorder}`
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: config.selection
},
'.cm-activeLine': {backgroundColor: config.activeLine},
'.cm-selectionMatch': {backgroundColor: config.selection},
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: config.matchingBracket,
outline: 'none'
},
'.cm-gutters': {
backgroundColor: config.background,
color: config.foreground,
border: 'none'
},
'.cm-activeLineGutter': {backgroundColor: config.background},
'.cm-lineNumbers .cm-gutterElement': {color: config.lineNumber},
'.cm-lineNumbers .cm-activeLineGutter': {color: config.activeLineNumber},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: config.foreground
},
'.cm-tooltip': {
border: `1px solid ${config.dropdownBorder}`,
backgroundColor: config.dropdownBackground,
color: config.foreground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: config.foreground,
borderBottomColor: config.foreground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
background: config.selection,
color: config.foreground,
}
}
}, {dark: config.dark})
export const tokyoNightDayHighlightStyle = HighlightStyle.define([
{tag: t.keyword, color: config.keyword},
{tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable},
{tag: [t.propertyName], color: config.function},
{tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string},
{tag: [t.function(t.variableName), t.labelName], color: config.function},
{tag: [t.color, t.constant(t.name), t.standard(t.name)], color: config.constant},
{tag: [t.definition(t.name), t.separator], color: config.variable},
{tag: [t.className], color: config.class},
{tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number},
{tag: [t.typeName], color: config.type, fontStyle: config.type},
{tag: [t.operator, t.operatorKeyword], color: config.keyword},
{tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp},
{tag: [t.meta, t.comment], color: config.comment},
{tag: t.strong, fontWeight: 'bold'},
{tag: t.emphasis, fontStyle: 'italic'},
{tag: t.link, textDecoration: 'underline'},
{tag: t.heading, fontWeight: 'bold', color: config.heading},
{tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable},
{tag: t.invalid, color: config.invalid},
{tag: t.strikethrough, textDecoration: 'line-through'},
])
export const tokyoNightDay: Extension = [
tokyoNightDayTheme,
syntaxHighlighting(tokyoNightDayHighlightStyle),
]

View File

@@ -0,0 +1,59 @@
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,50 +1,52 @@
export interface ThemeColors { export interface ThemeColors {
// 主题基本信息 // 主题基本信息
name?: string; // 主题名称 name: string; // 主题名称
dark?: boolean; // 是否为深色主题标识 dark: boolean; // 是否为深色主题标识
// 基础色调 // 基础色调
background: string; // 主背景色 background: string; // 主背景色
backgroundSecondary?: string; // 次要背景色 backgroundSecondary: string; // 次要背景色(用于代码块交替背景)
surface?: string; // 面板背景 surface: string; // 面板背景
dropdownBackground?: string; // 下拉菜单背景 dropdownBackground: string; // 下拉菜单背景
dropdownBorder?: string; // 下拉菜单边框 dropdownBorder: string; // 下拉菜单边框
// 文本颜色 // 文本颜色
foreground: string; // 主文本色 foreground: string; // 主文本色
foregroundSecondary?: string; // 次要文本色 foregroundSecondary: string; // 次要文本色
comment: string; // 注释色 comment: string; // 注释色
// 语法高亮色 // 语法高亮色 - 核心
keyword: string; // 关键字 keyword: string; // 关键字
string: string; // 字符串 string: string; // 字符串
function: string; // 函数名 function: string; // 函数名
number: string; // 数字 number: string; // 数字
operator?: string; // 操作符 operator: string; // 操作符
variable: string; // 变量 variable: string; // 变量
type: string; // 类型 type: string; // 类型
// 语法高亮色 - 扩展
constant: string; // 常量 constant: string; // 常量
storage?: string; // 存储类型 storage: string; // 存储类型(如 static, const
parameter?: string; // 参数 parameter: string; // 参数
class?: string; // 类名 class: string; // 类名
heading?: string; // 标题 heading: string; // 标题Markdown等
invalid?: string; // 无效内容 invalid: string; // 无效内容/错误
regexp?: string; // 正则表达式 regexp: string; // 正则表达式
// 界面元素 // 界面元素
cursor: string; // 光标 cursor: string; // 光标
selection: string; // 选中背景 selection: string; // 选中背景
selectionBlur?: string; // 失焦选中背景 selectionBlur: string; // 失焦选中背景
activeLine: string; // 当前行高亮 activeLine: string; // 当前行高亮
lineNumber: string; // 行号 lineNumber: string; // 行号
activeLineNumber: string; // 活动行号颜色 activeLineNumber: string; // 活动行号颜色
// 边框和分割线 // 边框和分割线
borderColor?: string; // 边框色 borderColor: string; // 边框色
borderLight?: string; // 浅色边框 borderLight: string; // 浅色边框
// 搜索和匹配 // 搜索和匹配
searchMatch?: string; // 搜索匹配 searchMatch: string; // 搜索匹配
matchingBracket?: string; // 匹配括号 matchingBracket: string; // 匹配括号
} }

View File

@@ -6,11 +6,10 @@ import { computed, watch, onMounted, ref } from 'vue';
import SettingSection from '../components/SettingSection.vue'; import SettingSection from '../components/SettingSection.vue';
import SettingItem from '../components/SettingItem.vue'; import SettingItem from '../components/SettingItem.vue';
import { SystemThemeType, LanguageType } from '@/../bindings/voidraft/internal/models/models'; import { SystemThemeType, LanguageType } from '@/../bindings/voidraft/internal/models/models';
import { defaultDarkColors } from '@/views/editor/theme/dark';
import { defaultLightColors } from '@/views/editor/theme/light';
import { createDebounce } from '@/common/utils/debounce'; import { createDebounce } from '@/common/utils/debounce';
import { createTimerManager } from '@/common/utils/timerUtils'; import { createTimerManager } from '@/common/utils/timerUtils';
import PickColors from 'vue-pick-colors'; import PickColors from 'vue-pick-colors';
import type { ThemeColors } from '@/views/editor/theme/types';
const { t } = useI18n(); const { t } = useI18n();
const configStore = useConfigStore(); const configStore = useConfigStore();
@@ -24,14 +23,12 @@ const { debouncedFn: debouncedUpdateColor } = createDebounce(
const { debouncedFn: debouncedResetTheme } = createDebounce( const { debouncedFn: debouncedResetTheme } = createDebounce(
async () => { async () => {
const themeType = activeThemeType.value; const isDark = isDarkMode.value;
const success = await themeStore.resetThemeColors(themeType); const success = await themeStore.resetCurrentTheme(isDark);
if (success) { if (success) {
tempColors.value = { // 重新加载临时颜色
darkTheme: { ...themeStore.themeColors.darkTheme }, syncTempColors();
lightTheme: { ...themeStore.themeColors.lightTheme }
};
hasUnsavedChanges.value = false; hasUnsavedChanges.value = false;
} }
}, },
@@ -41,11 +38,8 @@ const { debouncedFn: debouncedResetTheme } = createDebounce(
// 创建定时器管理器 // 创建定时器管理器
const resetTimer = createTimerManager(); const resetTimer = createTimerManager();
// 添加临时颜色状态 // 临时颜色状态(用于编辑)
const tempColors = ref({ const tempColors = ref<ThemeColors | null>(null);
darkTheme: { ...defaultDarkColors },
lightTheme: { ...defaultLightColors }
});
// 标记是否有未保存的更改 // 标记是否有未保存的更改
const hasUnsavedChanges = ref(false); const hasUnsavedChanges = ref(false);
@@ -55,81 +49,72 @@ const resetButtonState = ref({
confirming: false confirming: false
}); });
// 当前激活的主题类型 // 当前激活的主题类型(深色/浅色)
const isDarkMode = computed(() => const isDarkMode = computed(() =>
themeStore.currentTheme === SystemThemeType.SystemThemeDark || themeStore.currentTheme === SystemThemeType.SystemThemeDark ||
(themeStore.currentTheme === SystemThemeType.SystemThemeAuto && (themeStore.currentTheme === SystemThemeType.SystemThemeAuto &&
window.matchMedia('(prefers-color-scheme: dark)').matches) window.matchMedia('(prefers-color-scheme: dark)').matches)
); );
const activeThemeType = computed(() => isDarkMode.value ? 'darkTheme' : 'lightTheme'); // 当前可用的预设主题列表
const availableThemes = computed(() =>
isDarkMode.value ? themeStore.darkThemes : themeStore.lightThemes
);
// 当前主题的颜色配置 // 当前选中的主题ID
const currentColors = computed(() => { const currentThemeId = computed({
const themeType = activeThemeType.value; get: () => isDarkMode.value ? themeStore.currentThemeIds.dark : themeStore.currentThemeIds.light,
return tempColors.value[themeType] || set: async (themeId: number) => {
(themeType === 'darkTheme' ? defaultDarkColors : defaultLightColors); await themeStore.switchToTheme(themeId);
syncTempColors();
hasUnsavedChanges.value = false;
}
}); });
// 获取当前主题模式 // 当前主题的颜色配置
const currentColors = computed(() => tempColors.value || ({} as ThemeColors));
// 获取当前主题模式(用于颜色选择器)
const currentThemeMode = computed(() => isDarkMode.value ? 'dark' : 'light'); const currentThemeMode = computed(() => isDarkMode.value ? 'dark' : 'light');
// 监听主题颜色变更, // 同步临时颜色从 store
const syncTempColors = () => {
const colors = isDarkMode.value ? themeStore.currentColors.dark : themeStore.currentColors.light;
if (colors) {
tempColors.value = { ...colors };
}
};
// 监听主题切换,同步临时颜色
watch( watch(
() => themeStore.themeColors, [() => themeStore.currentColors.dark, () => themeStore.currentColors.light, isDarkMode],
(newValue) => { () => {
if (!hasUnsavedChanges.value) { if (!hasUnsavedChanges.value) {
tempColors.value.darkTheme = { ...newValue.darkTheme }; syncTempColors();
tempColors.value.lightTheme = { ...newValue.lightTheme };
} }
}, },
{ deep: true, immediate: true } { deep: true }
); );
onMounted(() => { onMounted(() => {
tempColors.value = { syncTempColors();
darkTheme: { ...themeStore.themeColors.darkTheme },
lightTheme: { ...themeStore.themeColors.lightTheme }
};
}); });
// 颜色配置 // 从 ThemeColors 接口自动提取所有颜色字段
const colorConfig = [ const colorKeys = computed(() => {
{ if (!tempColors.value) return [];
key: 'basic',
colors: ['background', 'backgroundSecondary', 'surface']
},
{
key: 'text',
colors: ['foreground', 'foregroundSecondary', 'comment']
},
{
key: 'syntax',
colors: ['keyword', 'string', 'function', 'number', 'operator', 'variable', 'type']
},
{
key: 'interface',
colors: ['cursor', 'selection', 'selectionBlur', 'activeLine', 'lineNumber', 'activeLineNumber']
},
{
key: 'border',
colors: ['borderColor', 'borderLight']
},
{
key: 'search',
colors: ['searchMatch', 'matchingBracket']
}
];
// 颜色配置分组 // 获取所有字段,排除 name 和 dark这两个是元数据
const colorGroups = computed(() => return Object.keys(tempColors.value).filter(key =>
colorConfig.map(group => ({ key !== 'name' && key !== 'dark'
key: group.key, );
title: t(`settings.themeColors.${group.key}`), });
colors: group.colors.map(colorKey => ({
key: colorKey, // 颜色配置列表
label: t(`settings.themeColors.${colorKey}`) const colorList = computed(() =>
})) colorKeys.value.map(colorKey => ({
key: colorKey,
label: t(`settings.themeColors.${colorKey}`)
})) }))
); );
@@ -152,15 +137,12 @@ const handleResetClick = () => {
// 更新本地颜色配置 // 更新本地颜色配置
const updateLocalColor = (colorKey: string, value: string) => { const updateLocalColor = (colorKey: string, value: string) => {
const themeType = activeThemeType.value; if (!tempColors.value) return;
// 更新临时颜色 // 更新临时颜色
tempColors.value = { tempColors.value = {
...tempColors.value, ...tempColors.value,
[themeType]: { [colorKey]: value
...tempColors.value[themeType],
[colorKey]: value
}
}; };
hasUnsavedChanges.value = true; hasUnsavedChanges.value = true;
@@ -169,17 +151,15 @@ const updateLocalColor = (colorKey: string, value: string) => {
// 应用颜色更改到系统 // 应用颜色更改到系统
const applyChanges = async () => { const applyChanges = async () => {
try { try {
// 获取当前主题的自定义颜色 if (!tempColors.value) return;
const customTheme = {
darkTheme: tempColors.value.darkTheme,
lightTheme: tempColors.value.lightTheme
};
// 更新themeStore中的颜色 const isDark = isDarkMode.value;
themeStore.updateThemeColors(customTheme.darkTheme, customTheme.lightTheme);
// 保存到配置 // 更新 store 中的颜色
await themeStore.saveThemeColors(); themeStore.updateCurrentColors(tempColors.value, isDark);
// 保存到数据库
await themeStore.saveCurrentTheme(isDark);
// 刷新编辑器主题 // 刷新编辑器主题
themeStore.refreshEditorTheme(); themeStore.refreshEditorTheme();
@@ -193,11 +173,8 @@ const applyChanges = async () => {
// 取消颜色更改 // 取消颜色更改
const cancelChanges = () => { const cancelChanges = () => {
// 恢复到themeStore中的颜色 // 恢复到 store 中的颜色
tempColors.value = { syncTempColors();
darkTheme: { ...themeStore.themeColors.darkTheme },
lightTheme: { ...themeStore.themeColors.lightTheme }
};
// 清除未保存标记 // 清除未保存标记
hasUnsavedChanges.value = false; hasUnsavedChanges.value = false;
@@ -266,6 +243,15 @@ const handlePickerClose = () => {
</option> </option>
</select> </select>
</SettingItem> </SettingItem>
<!-- 预设主题选择 -->
<SettingItem :title="t('settings.presetTheme')">
<select class="select-input" v-model="currentThemeId" :disabled="hasUnsavedChanges">
<option v-for="theme in availableThemes" :key="theme.id" :value="theme.id">
{{ theme.name }}
</option>
</select>
</SettingItem>
</SettingSection> </SettingSection>
<!-- 自定义主题颜色配置 --> <!-- 自定义主题颜色配置 -->
@@ -290,45 +276,41 @@ const handlePickerClose = () => {
</div> </div>
</template> </template>
<div class="color-groups"> <!-- 颜色列表 -->
<div v-for="group in colorGroups" :key="group.key" class="color-group"> <div class="color-list">
<h4 class="group-title">{{ group.title }}</h4> <SettingItem
<div class="color-items"> v-for="color in colorList"
<SettingItem :key="color.key"
v-for="color in group.colors" :title="color.label"
:key="color.key" class="color-setting-item"
:title="color.label" >
class="color-setting-item" <div class="color-input-wrapper">
> <div class="color-picker-wrapper">
<div class="color-input-wrapper"> <PickColors
<div class="color-picker-wrapper"> v-model:value="currentColors[color.key]"
<PickColors v-model:show-picker="showPickerMap[color.key]"
v-model:value="currentColors[color.key]" :size="28"
v-model:show-picker="showPickerMap[color.key]" show-alpha
:size="28" :theme="currentThemeMode"
show-alpha :colors="[]"
:theme="currentThemeMode" format="hex"
:colors="[]" :format-options="['rgb', 'hex', 'hsl', 'hsv']"
format="hex" placement="bottom"
:format-options="['rgb', 'hex', 'hsl', 'hsv']" position="absolute"
placement="bottom" :z-index="1000"
position="absolute" @change="(val) => handleColorChange(color.key, val)"
:z-index="1000" @close-picker="handlePickerClose"
@change="(val) => handleColorChange(color.key, val)" />
@close-picker="handlePickerClose" </div>
/> <input
</div> type="text"
<input :value="currentColors[color.key] || ''"
type="text" @input="debouncedUpdateColor(color.key, ($event.target as HTMLInputElement).value)"
:value="currentColors[color.key] || ''" class="color-text-input"
@input="debouncedUpdateColor(color.key, ($event.target as HTMLInputElement).value)" :placeholder="t('settings.colorValue')"
class="color-text-input" />
:placeholder="t('settings.colorValue')"
/>
</div>
</SettingItem>
</div> </div>
</div> </SettingItem>
</div> </div>
</SettingSection> </SettingSection>
</div> </div>
@@ -430,27 +412,11 @@ const handlePickerClose = () => {
} }
} }
.color-groups { .color-list {
display: flex; display: grid;
flex-direction: column; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 24px; gap: 12px;
} margin-top: 12px;
.color-group {
.group-title {
font-size: 14px;
font-weight: 600;
color: var(--settings-text);
margin: 0 0 12px 0;
padding-bottom: 6px;
border-bottom: 1px solid var(--settings-input-border);
}
.color-items {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 8px;
}
} }
.color-setting-item { .color-setting-item {

6
go.mod
View File

@@ -10,11 +10,11 @@ require (
github.com/knadh/koanf/providers/structs v1.0.0 github.com/knadh/koanf/providers/structs v1.0.0
github.com/knadh/koanf/v2 v2.3.0 github.com/knadh/koanf/v2 v2.3.0
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/wailsapp/wails/v3 v3.0.0-alpha.34 github.com/wailsapp/wails/v3 v3.0.0-alpha.36
golang.org/x/net v0.46.0 golang.org/x/net v0.46.0
golang.org/x/sys v0.37.0 golang.org/x/sys v0.37.0
golang.org/x/text v0.30.0 golang.org/x/text v0.30.0
modernc.org/sqlite v1.39.0 modernc.org/sqlite v1.39.1
) )
require ( require (
@@ -72,7 +72,7 @@ require (
github.com/sergi/go-diff v1.4.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect
github.com/skeema/knownhosts v1.3.2 // indirect github.com/skeema/knownhosts v1.3.2 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect github.com/ulikunitz/xz v0.5.15 // indirect
github.com/wailsapp/go-webview2 v1.0.21 // indirect github.com/wailsapp/go-webview2 v1.0.22 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect github.com/wailsapp/mimetype v1.4.1 // indirect
github.com/xanzy/go-gitlab v0.115.0 // indirect github.com/xanzy/go-gitlab v0.115.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect

12
go.sum
View File

@@ -162,12 +162,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA= github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58=
github.com/wailsapp/go-webview2 v1.0.21/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc= github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs= github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o= github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v3 v3.0.0-alpha.34 h1:t6NwHOLJzXuESb3YSXarSd1C/U1h2CpXF+BLr0ekj2g= github.com/wailsapp/wails/v3 v3.0.0-alpha.36 h1:GQ8vSrFgafITwMd/p4k+WBjG9K/anma9Pk2eJ/5CLsI=
github.com/wailsapp/wails/v3 v3.0.0-alpha.34/go.mod h1:UZpnhYuju4saspCJrIHAvC0H5XjtKnqd26FRxJLrQ0M= github.com/wailsapp/wails/v3 v3.0.0-alpha.36/go.mod h1:7i8tSuA74q97zZ5qEJlcVZdnO+IR7LT2KU8UpzYMPsw=
github.com/xanzy/go-gitlab v0.115.0 h1:6DmtItNcVe+At/liXSgfE/DZNZrGfalQmBRmOcJjOn8= github.com/xanzy/go-gitlab v0.115.0 h1:6DmtItNcVe+At/liXSgfE/DZNZrGfalQmBRmOcJjOn8=
github.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M= github.com/xanzy/go-gitlab v0.115.0/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@@ -256,8 +256,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY= modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4=
modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E= modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

View File

@@ -14,17 +14,25 @@ const (
ThemeTypeLight ThemeType = "light" ThemeTypeLight ThemeType = "light"
) )
// ThemeColorConfig 主题颜色配置 // ThemeColorConfig 主题颜色配置(与前端 ThemeColors 接口保持一致)
type ThemeColorConfig struct { type ThemeColorConfig struct {
// 主题基本信息
Name string `json:"name"` // 主题名称
Dark bool `json:"dark"` // 是否为深色主题
// 基础色调 // 基础色调
Background string `json:"background"` // 主背景色 Background string `json:"background"` // 主背景色
BackgroundSecondary string `json:"backgroundSecondary"` // 次要背景色 BackgroundSecondary string `json:"backgroundSecondary"` // 次要背景色(用于代码块交替背景)
Surface string `json:"surface"` // 面板背景 Surface string `json:"surface"` // 面板背景
DropdownBackground string `json:"dropdownBackground"` // 下拉菜单背景
DropdownBorder string `json:"dropdownBorder"` // 下拉菜单边框
// 文本颜色
Foreground string `json:"foreground"` // 主文本色 Foreground string `json:"foreground"` // 主文本色
ForegroundSecondary string `json:"foregroundSecondary"` // 次要文本色 ForegroundSecondary string `json:"foregroundSecondary"` // 次要文本色
Comment string `json:"comment"` // 注释色
// 语法高亮 // 语法高亮色 - 核心
Comment string `json:"comment"` // 注释色
Keyword string `json:"keyword"` // 关键字 Keyword string `json:"keyword"` // 关键字
String string `json:"string"` // 字符串 String string `json:"string"` // 字符串
Function string `json:"function"` // 函数名 Function string `json:"function"` // 函数名
@@ -33,34 +41,32 @@ type ThemeColorConfig struct {
Variable string `json:"variable"` // 变量 Variable string `json:"variable"` // 变量
Type string `json:"type"` // 类型 Type string `json:"type"` // 类型
// 语法高亮色 - 扩展
Constant string `json:"constant"` // 常量
Storage string `json:"storage"` // 存储类型(如 static, const
Parameter string `json:"parameter"` // 参数
Class string `json:"class"` // 类名
Heading string `json:"heading"` // 标题Markdown等
Invalid string `json:"invalid"` // 无效内容/错误
Regexp string `json:"regexp"` // 正则表达式
// 界面元素 // 界面元素
Cursor string `json:"cursor"` // 光标 Cursor string `json:"cursor"` // 光标
Selection string `json:"selection"` // 选中背景 Selection string `json:"selection"` // 选中背景
SelectionBlur string `json:"selectionBlur"` // 失焦选中背景 SelectionBlur string `json:"selectionBlur"` // 失焦选中背景
ActiveLine string `json:"activeLine"` // 当前行高亮 ActiveLine string `json:"activeLine"` // 当前行高亮
LineNumber string `json:"lineNumber"` // 行号 LineNumber string `json:"lineNumber"` // 行号
ActiveLineNumber string `json:"activeLineNumber"` // 活动行号 ActiveLineNumber string `json:"activeLineNumber"` // 活动行号颜色
// 边框分割线 // 边框分割线
BorderColor string `json:"borderColor"` // 边框色 BorderColor string `json:"borderColor"` // 边框色
BorderLight string `json:"borderLight"` // 浅色边框 BorderLight string `json:"borderLight"` // 浅色边框
// 搜索匹配 // 搜索匹配
SearchMatch string `json:"searchMatch"` // 搜索匹配 SearchMatch string `json:"searchMatch"` // 搜索匹配
MatchingBracket string `json:"matchingBracket"` // 匹配括号 MatchingBracket string `json:"matchingBracket"` // 匹配括号
} }
// Theme 主题数据库模型
type Theme struct {
ID int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Type ThemeType `db:"type" json:"type"`
Colors ThemeColorConfig `db:"colors" json:"colors"`
IsDefault bool `db:"is_default" json:"isDefault"`
CreatedAt string `db:"created_at" json:"createdAt"`
UpdatedAt string `db:"updated_at" json:"updatedAt"`
}
// Value 实现 driver.Valuer 接口,用于将 ThemeColorConfig 存储到数据库 // Value 实现 driver.Valuer 接口,用于将 ThemeColorConfig 存储到数据库
func (tc ThemeColorConfig) Value() (driver.Value, error) { func (tc ThemeColorConfig) Value() (driver.Value, error) {
return json.Marshal(tc) return json.Marshal(tc)
@@ -85,18 +91,37 @@ func (tc *ThemeColorConfig) Scan(value interface{}) error {
return json.Unmarshal(bytes, tc) return json.Unmarshal(bytes, tc)
} }
// NewDefaultDarkTheme 创建默认深色主题配置 // Theme 主题数据库模型
type Theme struct {
ID int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
Type ThemeType `db:"type" json:"type"`
Colors ThemeColorConfig `db:"colors" json:"colors"`
IsDefault bool `db:"is_default" json:"isDefault"`
CreatedAt string `db:"created_at" json:"createdAt"`
UpdatedAt string `db:"updated_at" json:"updatedAt"`
}
// NewDefaultDarkTheme 创建默认深色主题配置(与前端 defaultDarkColors 完全一致)
func NewDefaultDarkTheme() *ThemeColorConfig { func NewDefaultDarkTheme() *ThemeColorConfig {
return &ThemeColorConfig{ return &ThemeColorConfig{
// 主题信息
Name: "default-dark",
Dark: true,
// 基础色调 // 基础色调
Background: "#252B37", Background: "#252B37",
BackgroundSecondary: "#213644", BackgroundSecondary: "#213644",
Surface: "#474747", Surface: "#474747",
DropdownBackground: "#252B37",
DropdownBorder: "#ffffff19",
// 文本颜色
Foreground: "#9BB586", Foreground: "#9BB586",
ForegroundSecondary: "#9c9c9c", ForegroundSecondary: "#9c9c9c",
Comment: "#6272a4",
// 语法高亮 // 语法高亮色 - 核心
Comment: "#6272a4",
Keyword: "#ff79c6", Keyword: "#ff79c6",
String: "#f1fa8c", String: "#f1fa8c",
Function: "#50fa7b", Function: "#50fa7b",
@@ -105,6 +130,15 @@ func NewDefaultDarkTheme() *ThemeColorConfig {
Variable: "#8fbcbb", Variable: "#8fbcbb",
Type: "#8be9fd", Type: "#8be9fd",
// 语法高亮色 - 扩展
Constant: "#bd93f9",
Storage: "#ff79c6",
Parameter: "#8fbcbb",
Class: "#8be9fd",
Heading: "#ff79c6",
Invalid: "#d30102",
Regexp: "#f1fa8c",
// 界面元素 // 界面元素
Cursor: "#ffffff", Cursor: "#ffffff",
Selection: "#0865a9", Selection: "#0865a9",
@@ -113,28 +147,36 @@ func NewDefaultDarkTheme() *ThemeColorConfig {
LineNumber: "#ffffff26", LineNumber: "#ffffff26",
ActiveLineNumber: "#ffffff99", ActiveLineNumber: "#ffffff99",
// 边框分割线 // 边框分割线
BorderColor: "#1e222a", BorderColor: "#1e222a",
BorderLight: "#ffffff1a", BorderLight: "#ffffff19",
// 搜索匹配 // 搜索匹配
SearchMatch: "#8fbcbb", SearchMatch: "#8fbcbb",
MatchingBracket: "#ffffff1a", MatchingBracket: "#ffffff19",
} }
} }
// NewDefaultLightTheme 创建默认浅色主题配置 // NewDefaultLightTheme 创建默认浅色主题配置(与前端 defaultLightColors 完全一致)
func NewDefaultLightTheme() *ThemeColorConfig { func NewDefaultLightTheme() *ThemeColorConfig {
return &ThemeColorConfig{ return &ThemeColorConfig{
// 主题信息
Name: "default-light",
Dark: false,
// 基础色调 // 基础色调
Background: "#ffffff", Background: "#ffffff",
BackgroundSecondary: "#f1faf1", BackgroundSecondary: "#f1faf1",
Surface: "#f5f5f5", Surface: "#f5f5f5",
DropdownBackground: "#ffffff",
DropdownBorder: "#e1e4e8",
// 文本颜色
Foreground: "#444d56", Foreground: "#444d56",
ForegroundSecondary: "#6a737d", ForegroundSecondary: "#6a737d",
Comment: "#6a737d",
// 语法高亮 // 语法高亮色 - 核心
Comment: "#6a737d",
Keyword: "#d73a49", Keyword: "#d73a49",
String: "#032f62", String: "#032f62",
Function: "#005cc5", Function: "#005cc5",
@@ -143,6 +185,15 @@ func NewDefaultLightTheme() *ThemeColorConfig {
Variable: "#24292e", Variable: "#24292e",
Type: "#6f42c1", Type: "#6f42c1",
// 语法高亮色 - 扩展
Constant: "#005cc5",
Storage: "#d73a49",
Parameter: "#24292e",
Class: "#6f42c1",
Heading: "#d73a49",
Invalid: "#cb2431",
Regexp: "#032f62",
// 界面元素 // 界面元素
Cursor: "#000000", Cursor: "#000000",
Selection: "#77baff", Selection: "#77baff",
@@ -151,12 +202,578 @@ func NewDefaultLightTheme() *ThemeColorConfig {
LineNumber: "#00000040", LineNumber: "#00000040",
ActiveLineNumber: "#000000aa", ActiveLineNumber: "#000000aa",
// 边框分割线 // 边框分割线
BorderColor: "#dfdfdf", BorderColor: "#dfdfdf",
BorderLight: "#0000000c", BorderLight: "#0000000c",
// 搜索匹配 // 搜索匹配
SearchMatch: "#005cc5", SearchMatch: "#005cc5",
MatchingBracket: "#00000019", MatchingBracket: "#00000019",
} }
} }
// NewDraculaTheme 创建 Dracula 深色主题配置
func NewDraculaTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "dracula",
Dark: true,
Background: "#282A36",
BackgroundSecondary: "#282A36",
Surface: "#282A36",
DropdownBackground: "#282A36",
DropdownBorder: "#191A21",
Foreground: "#F8F8F2",
ForegroundSecondary: "#F8F8F2",
Comment: "#6272A4",
Keyword: "#FF79C6",
String: "#F1FA8C",
Function: "#50FA7B",
Number: "#BD93F9",
Operator: "#FF79C6",
Variable: "#F8F8F2",
Type: "#8BE9FD",
Constant: "#BD93F9",
Storage: "#FF79C6",
Parameter: "#F8F8F2",
Class: "#8BE9FD",
Heading: "#BD93F9",
Invalid: "#FF5555",
Regexp: "#F1FA8C",
Cursor: "#F8F8F2",
Selection: "#44475A",
SelectionBlur: "#44475A",
ActiveLine: "#53576c22",
LineNumber: "#6272A4",
ActiveLineNumber: "#F8F8F2",
BorderColor: "#191A21",
BorderLight: "#F8F8F219",
SearchMatch: "#50FA7B",
MatchingBracket: "#44475A",
}
}
// NewAuraTheme 创建 Aura 深色主题配置
func NewAuraTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "aura",
Dark: true,
Background: "#21202e",
BackgroundSecondary: "#21202e",
Surface: "#21202e",
DropdownBackground: "#21202e",
DropdownBorder: "#3b334b",
Foreground: "#edecee",
ForegroundSecondary: "#edecee",
Comment: "#6d6d6d",
Keyword: "#a277ff",
String: "#61ffca",
Function: "#ffca85",
Number: "#61ffca",
Operator: "#a277ff",
Variable: "#edecee",
Type: "#82e2ff",
Constant: "#61ffca",
Storage: "#a277ff",
Parameter: "#edecee",
Class: "#82e2ff",
Heading: "#a277ff",
Invalid: "#ff6767",
Regexp: "#61ffca",
Cursor: "#a277ff",
Selection: "#3d375e7f",
SelectionBlur: "#3d375e7f",
ActiveLine: "#4d4b6622",
LineNumber: "#a394f033",
ActiveLineNumber: "#cdccce",
BorderColor: "#3b334b",
BorderLight: "#edecee19",
SearchMatch: "#61ffca",
MatchingBracket: "#a394f033",
}
}
// NewGitHubDarkTheme 创建 GitHub Dark 主题配置
func NewGitHubDarkTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "github-dark",
Dark: true,
Background: "#24292e",
BackgroundSecondary: "#24292e",
Surface: "#24292e",
DropdownBackground: "#24292e",
DropdownBorder: "#1b1f23",
Foreground: "#d1d5da",
ForegroundSecondary: "#d1d5da",
Comment: "#6a737d",
Keyword: "#f97583",
String: "#9ecbff",
Function: "#79b8ff",
Number: "#79b8ff",
Operator: "#f97583",
Variable: "#ffab70",
Type: "#79b8ff",
Constant: "#79b8ff",
Storage: "#f97583",
Parameter: "#e1e4e8",
Class: "#b392f0",
Heading: "#79b8ff",
Invalid: "#f97583",
Regexp: "#9ecbff",
Cursor: "#c8e1ff",
Selection: "#3392FF44",
SelectionBlur: "#3392FF44",
ActiveLine: "#4d566022",
LineNumber: "#444d56",
ActiveLineNumber: "#e1e4e8",
BorderColor: "#1b1f23",
BorderLight: "#d1d5da19",
SearchMatch: "#79b8ff",
MatchingBracket: "#17E5E650",
}
}
// NewMaterialDarkTheme 创建 Material Dark 主题配置
func NewMaterialDarkTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "material-dark",
Dark: true,
Background: "#263238",
BackgroundSecondary: "#263238",
Surface: "#263238",
DropdownBackground: "#263238",
DropdownBorder: "#FFFFFF10",
Foreground: "#EEFFFF",
ForegroundSecondary: "#EEFFFF",
Comment: "#546E7A",
Keyword: "#C792EA",
String: "#C3E88D",
Function: "#82AAFF",
Number: "#F78C6C",
Operator: "#C792EA",
Variable: "#EEFFFF",
Type: "#B2CCD6",
Constant: "#F78C6C",
Storage: "#C792EA",
Parameter: "#EEFFFF",
Class: "#FFCB6B",
Heading: "#C3E88D",
Invalid: "#FF5370",
Regexp: "#89DDFF",
Cursor: "#FFCC00",
Selection: "#80CBC420",
SelectionBlur: "#80CBC420",
ActiveLine: "#4c616c22",
LineNumber: "#37474F",
ActiveLineNumber: "#607a86",
BorderColor: "#FFFFFF10",
BorderLight: "#EEFFFF19",
SearchMatch: "#82AAFF",
MatchingBracket: "#263238",
}
}
// NewOneDarkTheme 创建 One Dark 主题配置
func NewOneDarkTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "one-dark",
Dark: true,
Background: "#282c34",
BackgroundSecondary: "#2c313a",
Surface: "#353a42",
DropdownBackground: "#21252b",
DropdownBorder: "#7d8799",
Foreground: "#abb2bf",
ForegroundSecondary: "#7d8799",
Comment: "#7d8799",
Keyword: "#c678dd",
String: "#98c379",
Function: "#61afef",
Number: "#e5c07b",
Operator: "#56b6c2",
Variable: "#e06c75",
Type: "#e5c07b",
Constant: "#d19a66",
Storage: "#c678dd",
Parameter: "#e06c75",
Class: "#e5c07b",
Heading: "#e06c75",
Invalid: "#ffffff",
Regexp: "#56b6c2",
Cursor: "#528bff",
Selection: "#3E4451",
SelectionBlur: "#3E4451",
ActiveLine: "#6699ff0b",
LineNumber: "#7d8799",
ActiveLineNumber: "#abb2bf",
BorderColor: "#21252b",
BorderLight: "#abb2bf19",
SearchMatch: "#61afef",
MatchingBracket: "#bad0f847",
}
}
// NewSolarizedDarkTheme 创建 Solarized Dark 主题配置
func NewSolarizedDarkTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "solarized-dark",
Dark: true,
Background: "#002B36",
BackgroundSecondary: "#002B36",
Surface: "#002B36",
DropdownBackground: "#002B36",
DropdownBorder: "#2AA19899",
Foreground: "#93A1A1",
ForegroundSecondary: "#93A1A1",
Comment: "#586E75",
Keyword: "#859900",
String: "#2AA198",
Function: "#268BD2",
Number: "#D33682",
Operator: "#859900",
Variable: "#268BD2",
Type: "#CB4B16",
Constant: "#CB4B16",
Storage: "#93A1A1",
Parameter: "#268BD2",
Class: "#CB4B16",
Heading: "#268BD2",
Invalid: "#DC322F",
Regexp: "#DC322F",
Cursor: "#D30102",
Selection: "#274642",
SelectionBlur: "#274642",
ActiveLine: "#005b7022",
LineNumber: "#93A1A1",
ActiveLineNumber: "#949494",
BorderColor: "#073642",
BorderLight: "#93A1A119",
SearchMatch: "#2AA198",
MatchingBracket: "#073642",
}
}
// NewTokyoNightTheme 创建 Tokyo Night 主题配置
func NewTokyoNightTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "tokyo-night",
Dark: true,
Background: "#1a1b26",
BackgroundSecondary: "#1a1b26",
Surface: "#1a1b26",
DropdownBackground: "#1a1b26",
DropdownBorder: "#787c99",
Foreground: "#787c99",
ForegroundSecondary: "#787c99",
Comment: "#444b6a",
Keyword: "#bb9af7",
String: "#9ece6a",
Function: "#7aa2f7",
Number: "#ff9e64",
Operator: "#bb9af7",
Variable: "#c0caf5",
Type: "#0db9d7",
Constant: "#bb9af7",
Storage: "#bb9af7",
Parameter: "#c0caf5",
Class: "#c0caf5",
Heading: "#89ddff",
Invalid: "#ff5370",
Regexp: "#b4f9f8",
Cursor: "#c0caf5",
Selection: "#515c7e40",
SelectionBlur: "#515c7e40",
ActiveLine: "#43455c22",
LineNumber: "#363b54",
ActiveLineNumber: "#737aa2",
BorderColor: "#16161e",
BorderLight: "#787c9919",
SearchMatch: "#7aa2f7",
MatchingBracket: "#16161e",
}
}
// NewTokyoNightStormTheme 创建 Tokyo Night Storm 主题配置
func NewTokyoNightStormTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "tokyo-night-storm",
Dark: true,
Background: "#24283b",
BackgroundSecondary: "#24283b",
Surface: "#24283b",
DropdownBackground: "#24283b",
DropdownBorder: "#7982a9",
Foreground: "#7982a9",
ForegroundSecondary: "#7982a9",
Comment: "#565f89",
Keyword: "#bb9af7",
String: "#9ece6a",
Function: "#7aa2f7",
Number: "#ff9e64",
Operator: "#bb9af7",
Variable: "#c0caf5",
Type: "#2ac3de",
Constant: "#bb9af7",
Storage: "#bb9af7",
Parameter: "#c0caf5",
Class: "#c0caf5",
Heading: "#89ddff",
Invalid: "#ff5370",
Regexp: "#b4f9f8",
Cursor: "#c0caf5",
Selection: "#6f7bb630",
SelectionBlur: "#6f7bb630",
ActiveLine: "#4d547722",
LineNumber: "#3b4261",
ActiveLineNumber: "#737aa2",
BorderColor: "#1f2335",
BorderLight: "#7982a919",
SearchMatch: "#7aa2f7",
MatchingBracket: "#1f2335",
}
}
// 浅色主题预设配置
// NewGitHubLightTheme 创建 GitHub Light 主题配置
func NewGitHubLightTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "github-light",
Dark: false,
Background: "#fff",
BackgroundSecondary: "#fff",
Surface: "#fff",
DropdownBackground: "#fff",
DropdownBorder: "#e1e4e8",
Foreground: "#444d56",
ForegroundSecondary: "#444d56",
Comment: "#6a737d",
Keyword: "#d73a49",
String: "#032f62",
Function: "#005cc5",
Number: "#005cc5",
Operator: "#d73a49",
Variable: "#e36209",
Type: "#005cc5",
Constant: "#005cc5",
Storage: "#d73a49",
Parameter: "#24292e",
Class: "#6f42c1",
Heading: "#005cc5",
Invalid: "#cb2431",
Regexp: "#032f62",
Cursor: "#044289",
Selection: "#0366d625",
SelectionBlur: "#0366d625",
ActiveLine: "#c6c6c622",
LineNumber: "#1b1f234d",
ActiveLineNumber: "#24292e",
BorderColor: "#e1e4e8",
BorderLight: "#444d5619",
SearchMatch: "#005cc5",
MatchingBracket: "#34d05840",
}
}
// NewMaterialLightTheme 创建 Material Light 主题配置
func NewMaterialLightTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "material-light",
Dark: false,
Background: "#FAFAFA",
BackgroundSecondary: "#FAFAFA",
Surface: "#FAFAFA",
DropdownBackground: "#FAFAFA",
DropdownBorder: "#00000010",
Foreground: "#90A4AE",
ForegroundSecondary: "#90A4AE",
Comment: "#90A4AE",
Keyword: "#7C4DFF",
String: "#91B859",
Function: "#6182B8",
Number: "#F76D47",
Operator: "#7C4DFF",
Variable: "#90A4AE",
Type: "#8796B0",
Constant: "#F76D47",
Storage: "#7C4DFF",
Parameter: "#90A4AE",
Class: "#FFB62C",
Heading: "#91B859",
Invalid: "#E53935",
Regexp: "#39ADB5",
Cursor: "#272727",
Selection: "#80CBC440",
SelectionBlur: "#80CBC440",
ActiveLine: "#c2c2c222",
LineNumber: "#CFD8DC",
ActiveLineNumber: "#7E939E",
BorderColor: "#00000010",
BorderLight: "#90A4AE19",
SearchMatch: "#6182B8",
MatchingBracket: "#FAFAFA",
}
}
// NewSolarizedLightTheme 创建 Solarized Light 主题配置
func NewSolarizedLightTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "solarized-light",
Dark: false,
Background: "#FDF6E3",
BackgroundSecondary: "#FDF6E3",
Surface: "#FDF6E3",
DropdownBackground: "#FDF6E3",
DropdownBorder: "#D3AF86",
Foreground: "#586E75",
ForegroundSecondary: "#586E75",
Comment: "#93A1A1",
Keyword: "#859900",
String: "#2AA198",
Function: "#268BD2",
Number: "#D33682",
Operator: "#859900",
Variable: "#268BD2",
Type: "#CB4B16",
Constant: "#CB4B16",
Storage: "#586E75",
Parameter: "#268BD2",
Class: "#CB4B16",
Heading: "#268BD2",
Invalid: "#DC322F",
Regexp: "#DC322F",
Cursor: "#657B83",
Selection: "#EEE8D5",
SelectionBlur: "#EEE8D5",
ActiveLine: "#d5bd5c22",
LineNumber: "#586E75",
ActiveLineNumber: "#567983",
BorderColor: "#EEE8D5",
BorderLight: "#586E7519",
SearchMatch: "#268BD2",
MatchingBracket: "#EEE8D5",
}
}
// NewTokyoNightDayTheme 创建 Tokyo Night Day 主题配置
func NewTokyoNightDayTheme() *ThemeColorConfig {
return &ThemeColorConfig{
Name: "tokyo-night-day",
Dark: false,
Background: "#e1e2e7",
BackgroundSecondary: "#e1e2e7",
Surface: "#e1e2e7",
DropdownBackground: "#e1e2e7",
DropdownBorder: "#6a6f8e",
Foreground: "#6a6f8e",
ForegroundSecondary: "#6a6f8e",
Comment: "#9da3c2",
Keyword: "#9854f1",
String: "#587539",
Function: "#2e7de9",
Number: "#b15c00",
Operator: "#9854f1",
Variable: "#3760bf",
Type: "#07879d",
Constant: "#9854f1",
Storage: "#9854f1",
Parameter: "#3760bf",
Class: "#3760bf",
Heading: "#006a83",
Invalid: "#ff3e64",
Regexp: "#2e5857",
Cursor: "#3760bf",
Selection: "#8591b840",
SelectionBlur: "#8591b840",
ActiveLine: "#a7aaba22",
LineNumber: "#b3b6cd",
ActiveLineNumber: "#68709a",
BorderColor: "#e9e9ec",
BorderLight: "#6a6f8e19",
SearchMatch: "#2e7de9",
MatchingBracket: "#e9e9ec",
}
}

View File

@@ -67,13 +67,12 @@ CREATE TABLE IF NOT EXISTS key_bindings (
sqlCreateThemesTable = ` sqlCreateThemesTable = `
CREATE TABLE IF NOT EXISTS themes ( CREATE TABLE IF NOT EXISTS themes (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL UNIQUE,
type TEXT NOT NULL, type TEXT NOT NULL,
colors TEXT NOT NULL, colors TEXT NOT NULL,
is_default INTEGER NOT NULL DEFAULT 0, is_default INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
updated_at TEXT NOT NULL, updated_at TEXT NOT NULL
UNIQUE(type, is_default)
)` )`
) )
@@ -222,6 +221,8 @@ func (ds *DatabaseService) createIndexes() error {
// Themes indexes // Themes indexes
`CREATE INDEX IF NOT EXISTS idx_themes_type ON themes(type)`, `CREATE INDEX IF NOT EXISTS idx_themes_type ON themes(type)`,
`CREATE INDEX IF NOT EXISTS idx_themes_is_default ON themes(is_default)`, `CREATE INDEX IF NOT EXISTS idx_themes_is_default ON themes(is_default)`,
// 条件唯一索引:确保每种类型只能有一个默认主题
`CREATE UNIQUE INDEX IF NOT EXISTS idx_themes_type_default ON themes(type) WHERE is_default = 1`,
} }
for _, index := range indexes { for _, index := range indexes {

View File

@@ -3,6 +3,7 @@ package services
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"time" "time"
"voidraft/internal/models" "voidraft/internal/models"
@@ -43,96 +44,141 @@ func (ts *ThemeService) getDB() *sql.DB {
return ts.databaseService.db return ts.databaseService.db
} }
// initializeDefaultThemes 初始化默认主题 // initializeDefaultThemes 初始化所有预设主题
func (ts *ThemeService) initializeDefaultThemes() error { func (ts *ThemeService) initializeDefaultThemes() error {
db := ts.getDB() db := ts.getDB()
if db == nil { if db == nil {
return fmt.Errorf("database not available") return fmt.Errorf("database not available")
} }
// 检查是否已存在默认主题 // 获取所有已存在的主题名称
var count int existingThemes := make(map[string]bool)
err := db.QueryRow("SELECT COUNT(*) FROM themes WHERE is_default = 1").Scan(&count) rows, err := db.Query("SELECT name FROM themes")
if err != nil { if err != nil {
return fmt.Errorf("failed to check existing themes: %w", err) return fmt.Errorf("failed to query existing themes: %w", err)
}
if count > 0 {
return nil // 默认主题已存在
}
// 创建默认深色主题
now := time.Now().Format("2006-01-02 15:04:05")
darkTheme := &models.Theme{
Name: "Default Dark",
Type: models.ThemeTypeDark,
Colors: *models.NewDefaultDarkTheme(),
IsDefault: true,
CreatedAt: now,
UpdatedAt: now,
}
// 创建默认浅色主题
lightTheme := &models.Theme{
Name: "Default Light",
Type: models.ThemeTypeLight,
Colors: *models.NewDefaultLightTheme(),
IsDefault: true,
CreatedAt: now,
UpdatedAt: now,
}
// 插入默认主题
if _, err := ts.CreateTheme(darkTheme); err != nil {
return fmt.Errorf("failed to create default dark theme: %w", err)
}
if _, err := ts.CreateTheme(lightTheme); err != nil {
return fmt.Errorf("failed to create default light theme: %w", err)
}
return nil
}
// GetDefaultThemes 获取默认主题
func (ts *ThemeService) GetDefaultThemes() (map[string]*models.Theme, error) {
query := `
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
WHERE is_default = 1
ORDER BY type
`
db := ts.getDB()
rows, err := db.Query(query)
if err != nil {
return nil, fmt.Errorf("failed to query default themes: %w", err)
} }
defer rows.Close() defer rows.Close()
themes := make(map[string]*models.Theme)
for rows.Next() { for rows.Next() {
theme := &models.Theme{} var name string
err := rows.Scan( if err := rows.Scan(&name); err != nil {
&theme.ID, return fmt.Errorf("failed to scan theme name: %w", err)
&theme.Name, }
&theme.Type, existingThemes[name] = true
&theme.Colors, }
&theme.IsDefault,
&theme.CreatedAt, // 定义所有预设主题配置
&theme.UpdatedAt, now := time.Now().Format("2006-01-02 15:04:05")
presetThemes := []struct {
config *models.ThemeColorConfig
themeType models.ThemeType
isDefault bool
}{
// 默认主题
{models.NewDefaultDarkTheme(), models.ThemeTypeDark, true},
{models.NewDefaultLightTheme(), models.ThemeTypeLight, true},
// 深色主题预设
{models.NewDraculaTheme(), models.ThemeTypeDark, false},
{models.NewAuraTheme(), models.ThemeTypeDark, false},
{models.NewGitHubDarkTheme(), models.ThemeTypeDark, false},
{models.NewMaterialDarkTheme(), models.ThemeTypeDark, false},
{models.NewOneDarkTheme(), models.ThemeTypeDark, false},
{models.NewSolarizedDarkTheme(), models.ThemeTypeDark, false},
{models.NewTokyoNightTheme(), models.ThemeTypeDark, false},
{models.NewTokyoNightStormTheme(), models.ThemeTypeDark, false},
// 浅色主题预设
{models.NewGitHubLightTheme(), models.ThemeTypeLight, false},
{models.NewMaterialLightTheme(), models.ThemeTypeLight, false},
{models.NewSolarizedLightTheme(), models.ThemeTypeLight, false},
{models.NewTokyoNightDayTheme(), models.ThemeTypeLight, false},
}
// 筛选出需要创建的主题
var themesToCreate []*models.Theme
for _, preset := range presetThemes {
if !existingThemes[preset.config.Name] {
themesToCreate = append(themesToCreate, &models.Theme{
Name: preset.config.Name,
Type: preset.themeType,
Colors: *preset.config,
IsDefault: preset.isDefault,
CreatedAt: now,
UpdatedAt: now,
})
}
}
if len(themesToCreate) == 0 {
return nil
}
// 批量插入主题
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("failed to begin transaction: %w", err)
}
defer tx.Rollback()
stmt, err := tx.Prepare(`
INSERT INTO themes (name, type, colors, is_default, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
`)
if err != nil {
return fmt.Errorf("failed to prepare statement: %w", err)
}
defer stmt.Close()
for _, theme := range themesToCreate {
_, err := stmt.Exec(
theme.Name,
theme.Type,
theme.Colors,
theme.IsDefault,
theme.CreatedAt,
theme.UpdatedAt,
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to scan theme: %w", err) return fmt.Errorf("failed to insert theme %s: %w", theme.Name, err)
} }
themes[string(theme.Type)] = theme
} }
if err := rows.Err(); err != nil { if err := tx.Commit(); err != nil {
return nil, fmt.Errorf("failed to iterate themes: %w", err) return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
// GetThemeByID 根据ID获取主题
func (ts *ThemeService) GetThemeByID(id int) (*models.Theme, error) {
query := `
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
WHERE id = ?
LIMIT 1
`
theme := &models.Theme{}
db := ts.getDB()
err := db.QueryRow(query, id).Scan(
&theme.ID,
&theme.Name,
&theme.Type,
&theme.Colors,
&theme.IsDefault,
&theme.CreatedAt,
&theme.UpdatedAt,
)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, fmt.Errorf("theme not found with id: %d", id)
}
return nil, fmt.Errorf("failed to get theme by id: %w", err)
} }
return themes, nil return theme, nil
} }
// GetThemeByType 根据类型获取默认主题 // GetThemeByType 根据类型获取默认主题
@@ -166,37 +212,123 @@ func (ts *ThemeService) GetThemeByType(themeType models.ThemeType) (*models.Them
return theme, nil return theme, nil
} }
// UpdateThemeColors 更新主题颜色 // GetThemesByType 根据类型获取所有主题
func (ts *ThemeService) UpdateThemeColors(themeType models.ThemeType, colors models.ThemeColorConfig) error { func (ts *ThemeService) GetThemesByType(themeType models.ThemeType) ([]*models.Theme, error) {
query := ` query := `
UPDATE themes SELECT id, name, type, colors, is_default, created_at, updated_at
SET colors = ?, updated_at = ? FROM themes
WHERE type = ? AND is_default = 1 WHERE type = ?
ORDER BY is_default DESC, name ASC
` `
db := ts.getDB() db := ts.getDB()
_, err := db.Exec(query, colors, time.Now().Format("2006-01-02 15:04:05"), themeType) rows, err := db.Query(query, themeType)
if err != nil { if err != nil {
return fmt.Errorf("failed to update theme colors: %w", err) 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 更新主题
func (ts *ThemeService) UpdateTheme(id int, colors models.ThemeColorConfig) error {
query := `
UPDATE themes
SET colors = ?, updated_at = ?
WHERE id = ?
`
db := ts.getDB()
result, err := db.Exec(query, colors, time.Now().Format("2006-01-02 15:04:05"), id)
if err != nil {
return fmt.Errorf("failed to update theme: %w", err)
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return fmt.Errorf("failed to get rows affected: %w", err)
}
if rowsAffected == 0 {
return fmt.Errorf("theme not found with id: %d", id)
} }
return nil return nil
} }
// ResetThemeColors 重置主题颜色为默认值 // ResetTheme 重置主题为预设配置
func (ts *ThemeService) ResetThemeColors(themeType models.ThemeType) error { func (ts *ThemeService) ResetTheme(id int) error {
var defaultColors models.ThemeColorConfig // 先获取主题信息
theme, err := ts.GetThemeByID(id)
switch themeType { if err != nil {
case models.ThemeTypeDark: return err
defaultColors = *models.NewDefaultDarkTheme()
case models.ThemeTypeLight:
defaultColors = *models.NewDefaultLightTheme()
default:
return fmt.Errorf("unknown theme type: %s", themeType)
} }
return ts.UpdateThemeColors(themeType, defaultColors) // 根据主题名称获取预设配置
var presetConfig *models.ThemeColorConfig
switch theme.Name {
// 默认主题
case "default-dark":
presetConfig = models.NewDefaultDarkTheme()
case "default-light":
presetConfig = models.NewDefaultLightTheme()
// 深色主题预设
case "dracula":
presetConfig = models.NewDraculaTheme()
case "aura":
presetConfig = models.NewAuraTheme()
case "github-dark":
presetConfig = models.NewGitHubDarkTheme()
case "material-dark":
presetConfig = models.NewMaterialDarkTheme()
case "one-dark":
presetConfig = models.NewOneDarkTheme()
case "solarized-dark":
presetConfig = models.NewSolarizedDarkTheme()
case "tokyo-night":
presetConfig = models.NewTokyoNightTheme()
case "tokyo-night-storm":
presetConfig = models.NewTokyoNightStormTheme()
// 浅色主题预设
case "github-light":
presetConfig = models.NewGitHubLightTheme()
case "material-light":
presetConfig = models.NewMaterialLightTheme()
case "solarized-light":
presetConfig = models.NewSolarizedLightTheme()
case "tokyo-night-day":
presetConfig = models.NewTokyoNightDayTheme()
default:
return fmt.Errorf("no preset configuration found for theme: %s", theme.Name)
}
return ts.UpdateTheme(id, *presetConfig)
} }
// CreateTheme 创建新主题 // CreateTheme 创建新主题
@@ -235,7 +367,7 @@ func (ts *ThemeService) GetAllThemes() ([]*models.Theme, error) {
query := ` 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
ORDER BY is_default DESC, created_at ASC ORDER BY is_default DESC, type DESC, name ASC
` `
db := ts.getDB() db := ts.getDB()

View File

@@ -1 +1 @@
VERSION=1.5.0 VERSION=1.5.1