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 {
/**
* 主题基本信息
* 主题名称
*/
"name": string;
/**
* 是否为深色主题
*/
"dark": boolean;
/**
* 基础色调
* 主背景色
@@ -1201,7 +1212,7 @@ export class ThemeColorConfig {
"background": string;
/**
* 次要背景色
* 次要背景色(用于代码块交替背景)
*/
"backgroundSecondary": string;
@@ -1211,6 +1222,17 @@ export class ThemeColorConfig {
"surface": string;
/**
* 下拉菜单背景
*/
"dropdownBackground": string;
/**
* 下拉菜单边框
*/
"dropdownBorder": string;
/**
* 文本颜色
* 主文本色
*/
"foreground": string;
@@ -1221,12 +1243,12 @@ export class ThemeColorConfig {
"foregroundSecondary": string;
/**
* 语法高亮
* 注释色
*/
"comment": string;
/**
* 语法高亮色 - 核心
* 关键字
*/
"keyword": string;
@@ -1261,6 +1283,42 @@ export class ThemeColorConfig {
*/
"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;
/**
* 活动行号
* 活动行号颜色
*/
"activeLineNumber": string;
/**
* 边框分割线
* 边框分割线
* 边框色
*/
"borderColor": string;
@@ -1304,7 +1362,7 @@ export class ThemeColorConfig {
"borderLight": string;
/**
* 搜索匹配
* 搜索匹配
* 搜索匹配
*/
"searchMatch": string;
@@ -1316,6 +1374,12 @@ export class ThemeColorConfig {
/** Creates a new ThemeColorConfig instance. */
constructor($$source: Partial<ThemeColorConfig> = {}) {
if (!("name" in $$source)) {
this["name"] = "";
}
if (!("dark" in $$source)) {
this["dark"] = false;
}
if (!("background" in $$source)) {
this["background"] = "";
}
@@ -1325,6 +1389,12 @@ export class ThemeColorConfig {
if (!("surface" in $$source)) {
this["surface"] = "";
}
if (!("dropdownBackground" in $$source)) {
this["dropdownBackground"] = "";
}
if (!("dropdownBorder" in $$source)) {
this["dropdownBorder"] = "";
}
if (!("foreground" in $$source)) {
this["foreground"] = "";
}
@@ -1355,6 +1425,27 @@ export class ThemeColorConfig {
if (!("type" in $$source)) {
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)) {
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 } {
let $resultPromise = $Call.ByID(3801788118) as any;
export function GetThemeByID(id: number): Promise<models$0.Theme | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(3053137052, id) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType3($result);
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
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 } {
let $resultPromise = $Call.ByID(342461245, themeType) as any;
export function GetThemesByType(themeType: models$0.ThemeType): Promise<(models$0.Theme | null)[]> & { cancel(): void } {
let $resultPromise = $Call.ByID(1478417492, themeType) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* ResetTheme 重置主题为预设配置
*/
export function ResetTheme(id: number): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1806334457, id) as any;
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 } {
let $resultPromise = $Call.ByID(2750902529, themeType, colors) as any;
export function UpdateTheme(id: number, colors: models$0.ThemeColorConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(70189749, id, colors) as any;
return $resultPromise;
}
@@ -101,4 +113,3 @@ export function UpdateThemeColors(themeType: models$0.ThemeType, colors: models$
const $$createType0 = models$0.Theme.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $Create.Array($$createType1);
const $$createType3 = $Create.Map($Create.Any, $$createType1);

View File

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

View File

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

View File

@@ -166,12 +166,17 @@ export default {
interface: 'Interface Elements',
border: 'Borders & Dividers',
search: 'Search & Matching',
// Base Colors
background: 'Main Background',
backgroundSecondary: 'Secondary Background',
surface: 'Panel Background',
dropdownBackground: 'Dropdown Background',
dropdownBorder: 'Dropdown Border',
// Text Colors
foreground: 'Primary Text',
foregroundSecondary: 'Secondary Text',
comment: 'Comments',
// Syntax Highlighting - Core
keyword: 'Keywords',
string: 'Strings',
function: 'Functions',
@@ -179,14 +184,25 @@ export default {
operator: 'Operators',
variable: 'Variables',
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',
selection: 'Selection Background',
selectionBlur: 'Unfocused Selection',
activeLine: 'Active Line Highlight',
lineNumber: 'Line Numbers',
activeLineNumber: 'Active Line Number',
// Borders & Dividers
borderColor: 'Border Color',
borderLight: 'Light Border',
// Search & Matching
searchMatch: 'Search Match',
matchingBracket: 'Matching Bracket'
},
@@ -199,6 +215,7 @@ export default {
enableTabIndent: 'Enable Tab Indent',
language: 'Interface Language',
systemTheme: 'System Theme',
presetTheme: 'Preset Theme',
saveOptions: 'Save Options',
autoSaveDelay: 'Auto Save Delay (ms)',
updateSettings: 'Update Settings',

View File

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

View File

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

View File

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

View File

@@ -1,48 +1,17 @@
import {EditorView} from '@codemirror/view';
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language';
import {tags} from '@lezer/highlight';
import {Extension} from '@codemirror/state';
import type {ThemeColors} from './types';
// 默认深色主题颜色
export const defaultDarkColors = {
// 基础色调
background: '#252B37', // 主背景色
backgroundSecondary: '#213644', // 次要背景色
surface: '#474747', // 面板背景
// 文本颜色
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({
/**
*
* @param colors
* @returns CodeMirror Extension数组
*/
export function createBaseTheme(colors: ThemeColors): Extension {
// 编辑器主题样式
const theme = EditorView.theme({
'&': {
color: colors.foreground,
backgroundColor: colors.background,
@@ -79,6 +48,9 @@ export function createDarkTheme(colors = defaultDarkColors) {
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
backgroundColor: colors.selection,
},
'.cm-content ::selection': {
backgroundColor: colors.selection,
},
'.cm-activeLine.code-empty-block-selected': {
backgroundColor: colors.selection,
},
@@ -90,9 +62,10 @@ export function createDarkTheme(colors = defaultDarkColors) {
// 行号区域
'.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,
border: 'none',
borderRight: colors.dark ? 'none' : `1px solid ${colors.borderLight}`,
padding: '0 2px 0 4px',
userSelect: 'none',
},
@@ -115,9 +88,20 @@ export function createDarkTheme(colors = defaultDarkColors) {
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
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': {
@@ -125,11 +109,11 @@ export function createDarkTheme(colors = defaultDarkColors) {
outline: `1px solid ${colors.searchMatch}`,
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: colors.foreground,
backgroundColor: colors.searchMatch,
color: colors.background,
},
'.cm-selectionMatch': {
backgroundColor: '#50606D',
backgroundColor: colors.dark ? '#50606D' : '#e6f3ff',
},
// 括号匹配
@@ -141,7 +125,7 @@ export function createDarkTheme(colors = defaultDarkColors) {
color: 'inherit',
},
'&.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': {
border: 'none',
border: colors.dark ? 'none' : `1px solid ${colors.dropdownBorder}`,
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': {
borderTopColor: 'transparent',
@@ -169,7 +155,7 @@ export function createDarkTheme(colors = defaultDarkColors) {
},
},
// 代码块层
// 代码块层(自定义)
'.code-blocks-layer': {
width: '100%',
},
@@ -188,34 +174,36 @@ export function createDarkTheme(colors = defaultDarkColors) {
background: colors.backgroundSecondary,
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': {
height: '12px',
position: 'relative',
@@ -223,47 +211,79 @@ export function createDarkTheme(colors = defaultDarkColors) {
'.code-block-start.first': {
height: '0px',
},
}, {dark: true});
}, {dark: colors.dark});
// 语法高亮样式
const darkHighlightStyle = HighlightStyle.define([
const highlightStyle = 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: '#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.tagName], color: colors.number},
{tag: [tags.squareBracket], color: '#bf616a'},
{tag: [tags.angleBracket], color: '#d08770'},
{tag: [tags.attributeName], color: colors.variable},
{tag: [tags.regexp], color: colors.string},
// 名称、变量
{tag: [tags.name, tags.deleted, tags.character, tags.macroName], color: colors.variable},
{tag: [tags.variableName], color: colors.variable},
{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.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.meta], color: colors.comment},
{tag: [tags.comment], color: colors.comment, fontStyle: 'italic'},
// 数字
{tag: [tags.number, tags.changed, tags.annotation, tags.modifier, tags.self, tags.namespace], color: colors.number},
// 正则表达式
{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.emphasis, fontStyle: 'italic'},
{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.heading3, tags.heading4], fontSize: '1.2em'},
{tag: [tags.heading5, tags.heading6], fontSize: '1.1em'},
// 无效内容
{tag: tags.invalid, color: colors.invalid},
]);
return [
darkTheme,
syntaxHighlighting(darkHighlightStyle),
theme,
syntaxHighlighting(highlightStyle),
];
}
// 默认深色主题
export const dark = createDarkTheme(defaultDarkColors);

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
export const config: ThemeColors = {
name: 'aura',
dark: true,
// 基础色调
background: '#21202e',
foreground: '#edecee',
selection: '#3d375e7f',
cursor: '#a277ff',
backgroundSecondary: '#21202e',
surface: '#21202e',
dropdownBackground: '#21202e',
dropdownBorder: '#3b334b',
activeLine: '#4d4b6622',
lineNumber: '#a394f033',
activeLineNumber: '#cdccce',
matchingBracket: '#a394f033',
keyword: '#a277ff',
storage: '#a277ff',
variable: '#edecee',
parameter: '#edecee',
function: '#ffca85',
string: '#61ffca',
constant: '#61ffca',
type: '#82e2ff',
class: '#82e2ff',
number: '#61ffca',
// 文本颜色
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',
}
export const auraTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Aura 主题
export const aura: Extension = createBaseTheme(config)

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 {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
export const config: ThemeColors = {
name: 'dracula',
dark: true,
// 基础色调
background: '#282A36',
foreground: '#F8F8F2',
selection: '#44475A',
cursor: '#F8F8F2',
backgroundSecondary: '#282A36',
surface: '#282A36',
dropdownBackground: '#282A36',
dropdownBorder: '#191A21',
activeLine: '#53576c22',
lineNumber: '#6272A4',
activeLineNumber: '#F8F8F2',
matchingBracket: '#44475A',
keyword: '#FF79C6',
storage: '#FF79C6',
variable: '#F8F8F2',
parameter: '#F8F8F2',
function: '#50FA7B',
string: '#F1FA8C',
constant: '#BD93F9',
type: '#8BE9FD',
class: '#8BE9FD',
number: '#BD93F9',
// 文本颜色
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',
}
export const draculaTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Dracula 主题
export const dracula: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView, lineNumbers} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'githubDark',
export const config: ThemeColors = {
name: 'github-dark',
dark: true,
// 基础色调
background: '#24292e',
foreground: '#d1d5da',
selection: '#3392FF44',
cursor: '#c8e1ff',
backgroundSecondary: '#24292e',
surface: '#24292e',
dropdownBackground: '#24292e',
dropdownBorder: '#1b1f23',
activeLine: '#4d566022',
lineNumber: '#444d56',
activeLineNumber: '#e1e4e8',
matchingBracket: '#17E5E650',
keyword: '#f97583',
storage: '#f97583',
variable: '#ffab70',
parameter: '#e1e4e8',
function: '#79b8ff',
string: '#9ecbff',
constant: '#79b8ff',
type: '#79b8ff',
class: '#b392f0',
number: '#79b8ff',
// 文本颜色
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',
}
export const githubDarkTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 GitHub Dark 主题
export const githubDark: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'materialDark',
export const config: ThemeColors = {
name: 'material-dark',
dark: true,
// 基础色调
background: '#263238',
foreground: '#EEFFFF',
selection: '#80CBC420',
cursor: '#FFCC00',
backgroundSecondary: '#263238',
surface: '#263238',
dropdownBackground: '#263238',
dropdownBorder: '#FFFFFF10',
activeLine: '#4c616c22',
lineNumber: '#37474F',
activeLineNumber: '#607a86',
matchingBracket: '#263238',
keyword: '#C792EA',
storage: '#C792EA',
variable: '#EEFFFF',
parameter: '#EEFFFF',
function: '#82AAFF',
string: '#C3E88D',
constant: '#F78C6C',
type: '#B2CCD6',
class: '#FFCB6B',
number: '#F78C6C',
// 文本颜色
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',
}
export const materialDarkTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Material Dark 主题
export const materialDark: Extension = createBaseTheme(config)

View File

@@ -1,7 +1,6 @@
import {EditorView} from "@codemirror/view"
import {Extension} from "@codemirror/state"
import {HighlightStyle, syntaxHighlighting} from "@codemirror/language"
import {tags as t} from "@lezer/highlight"
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors
@@ -22,133 +21,56 @@ const chalky = "#e5c07b",
selection = "#3E4451",
cursor = "#528bff"
/// The colors used in the theme, as CSS color strings.
export const color = {
chalky,
coral,
cyan,
invalid,
ivory,
stone,
malibu,
sage,
whiskey,
violet,
darkBackground,
highlightBackground,
background,
tooltipBackground,
selection,
cursor
export const config: ThemeColors = {
name: 'one-dark',
dark: true,
// 基础色调
background: background,
backgroundSecondary: highlightBackground,
surface: tooltipBackground,
dropdownBackground: darkBackground,
dropdownBorder: stone,
// 文本颜色
foreground: ivory,
foregroundSecondary: stone,
comment: stone,
// 语法高亮色 - 核心
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.
export const oneDarkTheme = EditorView.theme({
"&": {
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)]
// 使用通用主题工厂函数创建 One Dark 主题
export const oneDark: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'solarizedDark',
export const config: ThemeColors = {
name: 'solarized-dark',
dark: true,
// 基础色调
background: '#002B36',
foreground: '#93A1A1',
selection: '#274642',
cursor: '#D30102',
backgroundSecondary: '#002B36',
surface: '#002B36',
dropdownBackground: '#002B36',
dropdownBorder: '#2AA19899',
activeLine: '#005b7022',
lineNumber: '#93A1A1',
activeLineNumber: '#949494',
matchingBracket: '#073642',
keyword: '#859900',
storage: '#93A1A1',
variable: '#268BD2',
parameter: '#268BD2',
function: '#268BD2',
string: '#2AA198',
constant: '#CB4B16',
type: '#CB4B16',
class: '#CB4B16',
number: '#D33682',
// 文本颜色
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',
}
export const solarizedDarkTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Solarized Dark 主题
export const solarizedDark: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'tokyoNightStorm',
export const config: ThemeColors = {
name: 'tokyo-night-storm',
dark: true,
// 基础色调
background: '#24283b',
foreground: '#7982a9',
selection: '#6f7bb630',
cursor: '#c0caf5',
backgroundSecondary: '#24283b',
surface: '#24283b',
dropdownBackground: '#24283b',
dropdownBorder: '#7982a9',
activeLine: '#4d547722',
lineNumber: '#3b4261',
activeLineNumber: '#737aa2',
matchingBracket: '#1f2335',
keyword: '#bb9af7',
storage: '#bb9af7',
variable: '#c0caf5',
parameter: '#c0caf5',
function: '#7aa2f7',
string: '#9ece6a',
constant: '#bb9af7',
type: '#2ac3de',
class: '#c0caf5',
number: '#ff9e64',
// 文本颜色
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',
}
export const tokyoNightStormTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Tokyo Night Storm 主题
export const tokyoNightStorm: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'tokyoNight',
export const config: ThemeColors = {
name: 'tokyo-night',
dark: true,
// 基础色调
background: '#1a1b26',
foreground: '#787c99',
selection: '#515c7e40',
cursor: '#c0caf5',
backgroundSecondary: '#1a1b26',
surface: '#1a1b26',
dropdownBackground: '#1a1b26',
dropdownBorder: '#787c99',
activeLine: '#43455c22',
lineNumber: '#363b54',
activeLineNumber: '#737aa2',
matchingBracket: '#16161e',
keyword: '#bb9af7',
storage: '#bb9af7',
variable: '#c0caf5',
parameter: '#c0caf5',
function: '#7aa2f7',
string: '#9ece6a',
constant: '#bb9af7',
type: '#0db9d7',
class: '#c0caf5',
number: '#ff9e64',
// 文本颜色
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',
}
export const tokyoNightTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Tokyo Night 主题
export const tokyoNight: Extension = createBaseTheme(config)

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 {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'githubLight',
export const config: ThemeColors = {
name: 'github-light',
dark: false,
// 基础色调
background: '#fff',
foreground: '#444d56',
selection: '#0366d625',
cursor: '#044289',
backgroundSecondary: '#fff',
surface: '#fff',
dropdownBackground: '#fff',
dropdownBorder: '#e1e4e8',
activeLine: '#c6c6c622',
lineNumber: '#1b1f234d',
activeLineNumber: '#24292e',
matchingBracket: '#34d05840',
keyword: '#d73a49',
storage: '#d73a49',
variable: '#e36209',
parameter: '#24292e',
function: '#005cc5',
string: '#032f62',
constant: '#005cc5',
type: '#005cc5',
class: '#6f42c1',
number: '#005cc5',
// 文本颜色
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',
}
export const githubLightTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 GitHub Light 主题
export const githubLight: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'materialLight',
export const config: ThemeColors = {
name: 'material-light',
dark: false,
// 基础色调
background: '#FAFAFA',
foreground: '#90A4AE',
selection: '#80CBC440',
cursor: '#272727',
backgroundSecondary: '#FAFAFA',
surface: '#FAFAFA',
dropdownBackground: '#FAFAFA',
dropdownBorder: '#00000010',
activeLine: '#c2c2c222',
lineNumber: '#CFD8DC',
activeLineNumber: '#7E939E',
matchingBracket: '#FAFAFA',
keyword: '#7C4DFF',
storage: '#7C4DFF',
variable: '#90A4AE',
parameter: '#90A4AE',
function: '#6182B8',
string: '#91B859',
constant: '#F76D47',
type: '#8796B0',
class: '#FFB62C',
number: '#F76D47',
// 文本颜色
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',
}
export const materialLightTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Material Light 主题
export const materialLight: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'solarizedLight',
export const config: ThemeColors = {
name: 'solarized-light',
dark: false,
// 基础色调
background: '#FDF6E3',
foreground: '#586E75',
selection: '#EEE8D5',
cursor: '#657B83',
backgroundSecondary: '#FDF6E3',
surface: '#FDF6E3',
dropdownBackground: '#FDF6E3',
dropdownBorder: '#D3AF86',
activeLine: '#d5bd5c22',
lineNumber: '#586E75',
activeLineNumber: '#567983',
matchingBracket: '#EEE8D5',
keyword: '#859900',
storage: '#586E75',
variable: '#268BD2',
parameter: '#268BD2',
function: '#268BD2',
string: '#2AA198',
constant: '#CB4B16',
type: '#CB4B16',
class: '#CB4B16',
number: '#D33682',
// 文本颜色
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',
}
export const solarizedLightTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Solarized Light 主题
export const solarizedLight: Extension = createBaseTheme(config)

View File

@@ -1,128 +1,57 @@
import {EditorView} from '@codemirror/view'
import {Extension} from '@codemirror/state'
import {HighlightStyle, syntaxHighlighting} from '@codemirror/language'
import {tags as t} from '@lezer/highlight'
import {createBaseTheme} from '../base'
import type {ThemeColors} from '../types'
export const config = {
name: 'tokyoNightDay',
export const config: ThemeColors = {
name: 'tokyo-night-day',
dark: false,
// 基础色调
background: '#e1e2e7',
foreground: '#6a6f8e',
selection: '#8591b840',
cursor: '#3760bf',
backgroundSecondary: '#e1e2e7',
surface: '#e1e2e7',
dropdownBackground: '#e1e2e7',
dropdownBorder: '#6a6f8e',
activeLine: '#a7aaba22',
lineNumber: '#b3b6cd',
activeLineNumber: '#68709a',
matchingBracket: '#e9e9ec',
keyword: '#9854f1',
storage: '#9854f1',
variable: '#3760bf',
parameter: '#3760bf',
function: '#2e7de9',
string: '#587539',
constant: '#9854f1',
type: '#07879d',
class: '#3760bf',
number: '#b15c00',
// 文本颜色
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',
}
export const tokyoNightDayTheme = EditorView.theme({
'&': {
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),
]
// 使用通用主题工厂函数创建 Tokyo Night Day 主题
export const tokyoNightDay: Extension = createBaseTheme(config)

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

View File

@@ -6,11 +6,10 @@ import { computed, watch, onMounted, ref } from 'vue';
import SettingSection from '../components/SettingSection.vue';
import SettingItem from '../components/SettingItem.vue';
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 { createTimerManager } from '@/common/utils/timerUtils';
import PickColors from 'vue-pick-colors';
import type { ThemeColors } from '@/views/editor/theme/types';
const { t } = useI18n();
const configStore = useConfigStore();
@@ -24,14 +23,12 @@ const { debouncedFn: debouncedUpdateColor } = createDebounce(
const { debouncedFn: debouncedResetTheme } = createDebounce(
async () => {
const themeType = activeThemeType.value;
const success = await themeStore.resetThemeColors(themeType);
const isDark = isDarkMode.value;
const success = await themeStore.resetCurrentTheme(isDark);
if (success) {
tempColors.value = {
darkTheme: { ...themeStore.themeColors.darkTheme },
lightTheme: { ...themeStore.themeColors.lightTheme }
};
// 重新加载临时颜色
syncTempColors();
hasUnsavedChanges.value = false;
}
},
@@ -41,11 +38,8 @@ const { debouncedFn: debouncedResetTheme } = createDebounce(
// 创建定时器管理器
const resetTimer = createTimerManager();
// 添加临时颜色状态
const tempColors = ref({
darkTheme: { ...defaultDarkColors },
lightTheme: { ...defaultLightColors }
});
// 临时颜色状态(用于编辑)
const tempColors = ref<ThemeColors | null>(null);
// 标记是否有未保存的更改
const hasUnsavedChanges = ref(false);
@@ -55,81 +49,72 @@ const resetButtonState = ref({
confirming: false
});
// 当前激活的主题类型
// 当前激活的主题类型(深色/浅色)
const isDarkMode = computed(() =>
themeStore.currentTheme === SystemThemeType.SystemThemeDark ||
(themeStore.currentTheme === SystemThemeType.SystemThemeAuto &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
);
const activeThemeType = computed(() => isDarkMode.value ? 'darkTheme' : 'lightTheme');
// 当前可用的预设主题列表
const availableThemes = computed(() =>
isDarkMode.value ? themeStore.darkThemes : themeStore.lightThemes
);
// 当前主题的颜色配置
const currentColors = computed(() => {
const themeType = activeThemeType.value;
return tempColors.value[themeType] ||
(themeType === 'darkTheme' ? defaultDarkColors : defaultLightColors);
// 当前选中的主题ID
const currentThemeId = computed({
get: () => isDarkMode.value ? themeStore.currentThemeIds.dark : themeStore.currentThemeIds.light,
set: async (themeId: number) => {
await themeStore.switchToTheme(themeId);
syncTempColors();
hasUnsavedChanges.value = false;
}
});
// 获取当前主题模式
// 当前主题的颜色配置
const currentColors = computed(() => tempColors.value || ({} as ThemeColors));
// 获取当前主题模式(用于颜色选择器)
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(
() => themeStore.themeColors,
(newValue) => {
[() => themeStore.currentColors.dark, () => themeStore.currentColors.light, isDarkMode],
() => {
if (!hasUnsavedChanges.value) {
tempColors.value.darkTheme = { ...newValue.darkTheme };
tempColors.value.lightTheme = { ...newValue.lightTheme };
syncTempColors();
}
},
{ deep: true, immediate: true }
{ deep: true }
);
onMounted(() => {
tempColors.value = {
darkTheme: { ...themeStore.themeColors.darkTheme },
lightTheme: { ...themeStore.themeColors.lightTheme }
};
syncTempColors();
});
// 颜色配置
const colorConfig = [
{
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']
}
];
// 从 ThemeColors 接口自动提取所有颜色字段
const colorKeys = computed(() => {
if (!tempColors.value) return [];
// 获取所有字段,排除 name 和 dark这两个是元数据
return Object.keys(tempColors.value).filter(key =>
key !== 'name' && key !== 'dark'
);
});
// 颜色配置分组
const colorGroups = computed(() =>
colorConfig.map(group => ({
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 themeType = activeThemeType.value;
if (!tempColors.value) return;
// 更新临时颜色
tempColors.value = {
...tempColors.value,
[themeType]: {
...tempColors.value[themeType],
[colorKey]: value
}
[colorKey]: value
};
hasUnsavedChanges.value = true;
@@ -169,17 +151,15 @@ const updateLocalColor = (colorKey: string, value: string) => {
// 应用颜色更改到系统
const applyChanges = async () => {
try {
// 获取当前主题的自定义颜色
const customTheme = {
darkTheme: tempColors.value.darkTheme,
lightTheme: tempColors.value.lightTheme
};
if (!tempColors.value) return;
// 更新themeStore中的颜色
themeStore.updateThemeColors(customTheme.darkTheme, customTheme.lightTheme);
const isDark = isDarkMode.value;
// 保存到配置
await themeStore.saveThemeColors();
// 更新 store 中的颜色
themeStore.updateCurrentColors(tempColors.value, isDark);
// 保存到数据库
await themeStore.saveCurrentTheme(isDark);
// 刷新编辑器主题
themeStore.refreshEditorTheme();
@@ -193,11 +173,8 @@ const applyChanges = async () => {
// 取消颜色更改
const cancelChanges = () => {
// 恢复到themeStore中的颜色
tempColors.value = {
darkTheme: { ...themeStore.themeColors.darkTheme },
lightTheme: { ...themeStore.themeColors.lightTheme }
};
// 恢复到 store 中的颜色
syncTempColors();
// 清除未保存标记
hasUnsavedChanges.value = false;
@@ -266,6 +243,15 @@ const handlePickerClose = () => {
</option>
</select>
</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>
<!-- 自定义主题颜色配置 -->
@@ -290,45 +276,41 @@ const handlePickerClose = () => {
</div>
</template>
<div class="color-groups">
<div v-for="group in colorGroups" :key="group.key" class="color-group">
<h4 class="group-title">{{ group.title }}</h4>
<div class="color-items">
<SettingItem
v-for="color in group.colors"
:key="color.key"
:title="color.label"
class="color-setting-item"
>
<div class="color-input-wrapper">
<div class="color-picker-wrapper">
<PickColors
v-model:value="currentColors[color.key]"
v-model:show-picker="showPickerMap[color.key]"
:size="28"
show-alpha
:theme="currentThemeMode"
:colors="[]"
format="hex"
:format-options="['rgb', 'hex', 'hsl', 'hsv']"
placement="bottom"
position="absolute"
:z-index="1000"
@change="(val) => handleColorChange(color.key, val)"
@close-picker="handlePickerClose"
/>
</div>
<input
type="text"
:value="currentColors[color.key] || ''"
@input="debouncedUpdateColor(color.key, ($event.target as HTMLInputElement).value)"
class="color-text-input"
:placeholder="t('settings.colorValue')"
/>
</div>
</SettingItem>
<!-- 颜色列表 -->
<div class="color-list">
<SettingItem
v-for="color in colorList"
:key="color.key"
:title="color.label"
class="color-setting-item"
>
<div class="color-input-wrapper">
<div class="color-picker-wrapper">
<PickColors
v-model:value="currentColors[color.key]"
v-model:show-picker="showPickerMap[color.key]"
:size="28"
show-alpha
:theme="currentThemeMode"
:colors="[]"
format="hex"
:format-options="['rgb', 'hex', 'hsl', 'hsv']"
placement="bottom"
position="absolute"
:z-index="1000"
@change="(val) => handleColorChange(color.key, val)"
@close-picker="handlePickerClose"
/>
</div>
<input
type="text"
:value="currentColors[color.key] || ''"
@input="debouncedUpdateColor(color.key, ($event.target as HTMLInputElement).value)"
class="color-text-input"
:placeholder="t('settings.colorValue')"
/>
</div>
</div>
</SettingItem>
</div>
</SettingSection>
</div>
@@ -430,27 +412,11 @@ const handlePickerClose = () => {
}
}
.color-groups {
display: flex;
flex-direction: column;
gap: 24px;
}
.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-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 12px;
margin-top: 12px;
}
.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/v2 v2.3.0
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/sys v0.37.0
golang.org/x/text v0.30.0
modernc.org/sqlite v1.39.0
modernc.org/sqlite v1.39.1
)
require (
@@ -72,7 +72,7 @@ require (
github.com/sergi/go-diff v1.4.0 // indirect
github.com/skeema/knownhosts v1.3.2 // 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/xanzy/go-gitlab v0.115.0 // 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/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/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA=
github.com/wailsapp/go-webview2 v1.0.21/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58=
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/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.34/go.mod h1:UZpnhYuju4saspCJrIHAvC0H5XjtKnqd26FRxJLrQ0M=
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.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/go.mod h1:5XCDtM7AM6WMKmfDdOiEpyRWUqui2iS9ILfvCZ2gJ5M=
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/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4=
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/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=

View File

@@ -14,17 +14,25 @@ const (
ThemeTypeLight ThemeType = "light"
)
// ThemeColorConfig 主题颜色配置
// ThemeColorConfig 主题颜色配置(与前端 ThemeColors 接口保持一致)
type ThemeColorConfig struct {
// 主题基本信息
Name string `json:"name"` // 主题名称
Dark bool `json:"dark"` // 是否为深色主题
// 基础色调
Background string `json:"background"` // 主背景色
BackgroundSecondary string `json:"backgroundSecondary"` // 次要背景色
BackgroundSecondary string `json:"backgroundSecondary"` // 次要背景色(用于代码块交替背景)
Surface string `json:"surface"` // 面板背景
DropdownBackground string `json:"dropdownBackground"` // 下拉菜单背景
DropdownBorder string `json:"dropdownBorder"` // 下拉菜单边框
// 文本颜色
Foreground string `json:"foreground"` // 主文本色
ForegroundSecondary string `json:"foregroundSecondary"` // 次要文本色
Comment string `json:"comment"` // 注释色
// 语法高亮
Comment string `json:"comment"` // 注释色
// 语法高亮色 - 核心
Keyword string `json:"keyword"` // 关键字
String string `json:"string"` // 字符串
Function string `json:"function"` // 函数名
@@ -33,34 +41,32 @@ type ThemeColorConfig struct {
Variable string `json:"variable"` // 变量
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"` // 光标
Selection string `json:"selection"` // 选中背景
SelectionBlur string `json:"selectionBlur"` // 失焦选中背景
ActiveLine string `json:"activeLine"` // 当前行高亮
LineNumber string `json:"lineNumber"` // 行号
ActiveLineNumber string `json:"activeLineNumber"` // 活动行号
ActiveLineNumber string `json:"activeLineNumber"` // 活动行号颜色
// 边框分割线
// 边框分割线
BorderColor string `json:"borderColor"` // 边框色
BorderLight string `json:"borderLight"` // 浅色边框
// 搜索匹配
// 搜索匹配
SearchMatch string `json:"searchMatch"` // 搜索匹配
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 存储到数据库
func (tc ThemeColorConfig) Value() (driver.Value, error) {
return json.Marshal(tc)
@@ -85,18 +91,37 @@ func (tc *ThemeColorConfig) Scan(value interface{}) error {
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 {
return &ThemeColorConfig{
// 主题信息
Name: "default-dark",
Dark: true,
// 基础色调
Background: "#252B37",
BackgroundSecondary: "#213644",
Surface: "#474747",
DropdownBackground: "#252B37",
DropdownBorder: "#ffffff19",
// 文本颜色
Foreground: "#9BB586",
ForegroundSecondary: "#9c9c9c",
Comment: "#6272a4",
// 语法高亮
Comment: "#6272a4",
// 语法高亮色 - 核心
Keyword: "#ff79c6",
String: "#f1fa8c",
Function: "#50fa7b",
@@ -105,6 +130,15 @@ func NewDefaultDarkTheme() *ThemeColorConfig {
Variable: "#8fbcbb",
Type: "#8be9fd",
// 语法高亮色 - 扩展
Constant: "#bd93f9",
Storage: "#ff79c6",
Parameter: "#8fbcbb",
Class: "#8be9fd",
Heading: "#ff79c6",
Invalid: "#d30102",
Regexp: "#f1fa8c",
// 界面元素
Cursor: "#ffffff",
Selection: "#0865a9",
@@ -113,28 +147,36 @@ func NewDefaultDarkTheme() *ThemeColorConfig {
LineNumber: "#ffffff26",
ActiveLineNumber: "#ffffff99",
// 边框分割线
// 边框分割线
BorderColor: "#1e222a",
BorderLight: "#ffffff1a",
BorderLight: "#ffffff19",
// 搜索匹配
// 搜索匹配
SearchMatch: "#8fbcbb",
MatchingBracket: "#ffffff1a",
MatchingBracket: "#ffffff19",
}
}
// NewDefaultLightTheme 创建默认浅色主题配置
// NewDefaultLightTheme 创建默认浅色主题配置(与前端 defaultLightColors 完全一致)
func NewDefaultLightTheme() *ThemeColorConfig {
return &ThemeColorConfig{
// 主题信息
Name: "default-light",
Dark: false,
// 基础色调
Background: "#ffffff",
BackgroundSecondary: "#f1faf1",
Surface: "#f5f5f5",
DropdownBackground: "#ffffff",
DropdownBorder: "#e1e4e8",
// 文本颜色
Foreground: "#444d56",
ForegroundSecondary: "#6a737d",
Comment: "#6a737d",
// 语法高亮
Comment: "#6a737d",
// 语法高亮色 - 核心
Keyword: "#d73a49",
String: "#032f62",
Function: "#005cc5",
@@ -143,6 +185,15 @@ func NewDefaultLightTheme() *ThemeColorConfig {
Variable: "#24292e",
Type: "#6f42c1",
// 语法高亮色 - 扩展
Constant: "#005cc5",
Storage: "#d73a49",
Parameter: "#24292e",
Class: "#6f42c1",
Heading: "#d73a49",
Invalid: "#cb2431",
Regexp: "#032f62",
// 界面元素
Cursor: "#000000",
Selection: "#77baff",
@@ -151,12 +202,578 @@ func NewDefaultLightTheme() *ThemeColorConfig {
LineNumber: "#00000040",
ActiveLineNumber: "#000000aa",
// 边框分割线
// 边框分割线
BorderColor: "#dfdfdf",
BorderLight: "#0000000c",
// 搜索匹配
// 搜索匹配
SearchMatch: "#005cc5",
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 = `
CREATE TABLE IF NOT EXISTS themes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
name TEXT NOT NULL UNIQUE,
type TEXT NOT NULL,
colors TEXT NOT NULL,
is_default INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE(type, is_default)
updated_at TEXT NOT NULL
)`
)
@@ -222,6 +221,8 @@ func (ds *DatabaseService) createIndexes() error {
// Themes indexes
`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 UNIQUE INDEX IF NOT EXISTS idx_themes_type_default ON themes(type) WHERE is_default = 1`,
}
for _, index := range indexes {

View File

@@ -3,6 +3,7 @@ package services
import (
"context"
"database/sql"
"errors"
"fmt"
"time"
"voidraft/internal/models"
@@ -43,96 +44,141 @@ func (ts *ThemeService) getDB() *sql.DB {
return ts.databaseService.db
}
// initializeDefaultThemes 初始化默认主题
// initializeDefaultThemes 初始化所有预设主题
func (ts *ThemeService) initializeDefaultThemes() error {
db := ts.getDB()
if db == nil {
return fmt.Errorf("database not available")
}
// 检查是否已存在默认主题
var count int
err := db.QueryRow("SELECT COUNT(*) FROM themes WHERE is_default = 1").Scan(&count)
// 获取所有已存在的主题名称
existingThemes := make(map[string]bool)
rows, err := db.Query("SELECT name FROM themes")
if err != nil {
return fmt.Errorf("failed to check 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)
return fmt.Errorf("failed to query existing themes: %w", err)
}
defer rows.Close()
themes := make(map[string]*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,
var name string
if err := rows.Scan(&name); err != nil {
return fmt.Errorf("failed to scan theme name: %w", err)
}
existingThemes[name] = true
}
// 定义所有预设主题配置
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 {
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 {
return nil, fmt.Errorf("failed to iterate themes: %w", err)
if err := tx.Commit(); err != nil {
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 根据类型获取默认主题
@@ -166,37 +212,123 @@ func (ts *ThemeService) GetThemeByType(themeType models.ThemeType) (*models.Them
return theme, nil
}
// UpdateThemeColors 更新主题颜色
func (ts *ThemeService) UpdateThemeColors(themeType models.ThemeType, colors models.ThemeColorConfig) error {
// GetThemesByType 根据类型获取所有主题
func (ts *ThemeService) GetThemesByType(themeType models.ThemeType) ([]*models.Theme, error) {
query := `
UPDATE themes
SET colors = ?, updated_at = ?
WHERE type = ? AND is_default = 1
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
WHERE type = ?
ORDER BY is_default DESC, name ASC
`
db := ts.getDB()
_, err := db.Exec(query, colors, time.Now().Format("2006-01-02 15:04:05"), themeType)
rows, err := db.Query(query, themeType)
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
}
// ResetThemeColors 重置主题颜色为默认值
func (ts *ThemeService) ResetThemeColors(themeType models.ThemeType) error {
var defaultColors models.ThemeColorConfig
switch themeType {
case models.ThemeTypeDark:
defaultColors = *models.NewDefaultDarkTheme()
case models.ThemeTypeLight:
defaultColors = *models.NewDefaultLightTheme()
default:
return fmt.Errorf("unknown theme type: %s", themeType)
// ResetTheme 重置主题为预设配置
func (ts *ThemeService) ResetTheme(id int) error {
// 先获取主题信息
theme, err := ts.GetThemeByID(id)
if err != nil {
return err
}
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 创建新主题
@@ -235,7 +367,7 @@ func (ts *ThemeService) GetAllThemes() ([]*models.Theme, error) {
query := `
SELECT id, name, type, colors, is_default, created_at, updated_at
FROM themes
ORDER BY is_default DESC, created_at ASC
ORDER BY is_default DESC, type DESC, name ASC
`
db := ts.getDB()

View File

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