diff --git a/themes/acrylic-auto.json b/themes/acrylic-auto.json
new file mode 100644
index 0000000..e7eefdc
--- /dev/null
+++ b/themes/acrylic-auto.json
@@ -0,0 +1,31 @@
+{
+ "name": "Acrylic-自动",
+ "id": "acrylic-auto",
+ "mode": "light",
+ "mica": {
+ "effect": "acrylic",
+ "theme": "auto",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#0052D9",
+ "warningColor": "#E37318",
+ "errorColor": "#D32029",
+ "successColor": "#248232"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(255, 255, 255, 0.85)",
+ "--ss-text-main": "#1a1a1a",
+ "--ss-text-secondary": "#5e5e5e",
+ "--ss-border-color": "rgba(0, 0, 0, 0.08)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(255, 255, 255, 0.9)",
+ "--ss-item-hover": "rgba(0, 0, 0, 0.04)",
+ "--ss-sidebar-text": "#1a1a1a",
+ "--ss-sidebar-active-bg": "rgba(0, 0, 0, 0.06)",
+ "--ss-sidebar-active-text": "#1a1a1a"
+ }
+ }
+}
diff --git a/themes/acrylic-dark.json b/themes/acrylic-dark.json
new file mode 100644
index 0000000..4e86118
--- /dev/null
+++ b/themes/acrylic-dark.json
@@ -0,0 +1,31 @@
+{
+ "name": "Acrylic-深色",
+ "id": "acrylic-dark",
+ "mode": "dark",
+ "mica": {
+ "effect": "acrylic",
+ "theme": "dark",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#4C8BF5",
+ "warningColor": "#E37318",
+ "errorColor": "#D54941",
+ "successColor": "#2BA471"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(30, 30, 30, 0.85)",
+ "--ss-text-main": "#e0e0e0",
+ "--ss-text-secondary": "#a0a0a0",
+ "--ss-border-color": "rgba(255, 255, 255, 0.08)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(30, 30, 30, 0.9)",
+ "--ss-item-hover": "rgba(255, 255, 255, 0.04)",
+ "--ss-sidebar-text": "#e0e0e0",
+ "--ss-sidebar-active-bg": "rgba(255, 255, 255, 0.06)",
+ "--ss-sidebar-active-text": "#e0e0e0"
+ }
+ }
+}
diff --git a/themes/acrylic-light.json b/themes/acrylic-light.json
new file mode 100644
index 0000000..cd6ddd7
--- /dev/null
+++ b/themes/acrylic-light.json
@@ -0,0 +1,31 @@
+{
+ "name": "Acrylic-浅色",
+ "id": "acrylic-light",
+ "mode": "light",
+ "mica": {
+ "effect": "acrylic",
+ "theme": "light",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#0052D9",
+ "warningColor": "#E37318",
+ "errorColor": "#D32029",
+ "successColor": "#248232"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(255, 255, 255, 0.85)",
+ "--ss-text-main": "#1a1a1a",
+ "--ss-text-secondary": "#5e5e5e",
+ "--ss-border-color": "rgba(0, 0, 0, 0.08)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(255, 255, 255, 0.9)",
+ "--ss-item-hover": "rgba(0, 0, 0, 0.04)",
+ "--ss-sidebar-text": "#1a1a1a",
+ "--ss-sidebar-active-bg": "rgba(0, 0, 0, 0.06)",
+ "--ss-sidebar-active-text": "#1a1a1a"
+ }
+ }
+}
diff --git a/themes/custom.json b/themes/custom.json
new file mode 100644
index 0000000..f9374db
--- /dev/null
+++ b/themes/custom.json
@@ -0,0 +1,31 @@
+{
+ "name": "自定义",
+ "id": "custom",
+ "mode": "light",
+ "mica": {
+ "effect": "mica",
+ "theme": "auto",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#0052D9",
+ "warningColor": "#E37318",
+ "errorColor": "#D32029",
+ "successColor": "#248232"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(255, 255, 255, 0.7)",
+ "--ss-text-main": "#1a1a1a",
+ "--ss-text-secondary": "#5e5e5e",
+ "--ss-border-color": "rgba(0, 0, 0, 0.1)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(255, 255, 255, 0.85)",
+ "--ss-item-hover": "rgba(0, 0, 0, 0.05)",
+ "--ss-sidebar-text": "#1a1a1a",
+ "--ss-sidebar-active-bg": "rgba(0, 0, 0, 0.08)",
+ "--ss-sidebar-active-text": "#1a1a1a"
+ }
+ }
+}
diff --git a/themes/mica-auto.json b/themes/mica-auto.json
new file mode 100644
index 0000000..c462a38
--- /dev/null
+++ b/themes/mica-auto.json
@@ -0,0 +1,31 @@
+{
+ "name": "Mica-自动",
+ "id": "mica-auto",
+ "mode": "light",
+ "mica": {
+ "effect": "mica",
+ "theme": "auto",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#0052D9",
+ "warningColor": "#E37318",
+ "errorColor": "#D32029",
+ "successColor": "#248232"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(255, 255, 255, 0.7)",
+ "--ss-text-main": "#1a1a1a",
+ "--ss-text-secondary": "#5e5e5e",
+ "--ss-border-color": "rgba(0, 0, 0, 0.1)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(255, 255, 255, 0.85)",
+ "--ss-item-hover": "rgba(0, 0, 0, 0.05)",
+ "--ss-sidebar-text": "#1a1a1a",
+ "--ss-sidebar-active-bg": "rgba(0, 0, 0, 0.08)",
+ "--ss-sidebar-active-text": "#1a1a1a"
+ }
+ }
+}
diff --git a/themes/mica-dark.json b/themes/mica-dark.json
new file mode 100644
index 0000000..217bdec
--- /dev/null
+++ b/themes/mica-dark.json
@@ -0,0 +1,31 @@
+{
+ "name": "Mica-深色",
+ "id": "mica-dark",
+ "mode": "dark",
+ "mica": {
+ "effect": "mica",
+ "theme": "dark",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#4C8BF5",
+ "warningColor": "#E37318",
+ "errorColor": "#D54941",
+ "successColor": "#2BA471"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(30, 30, 30, 0.7)",
+ "--ss-text-main": "#e0e0e0",
+ "--ss-text-secondary": "#a0a0a0",
+ "--ss-border-color": "rgba(255, 255, 255, 0.1)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(30, 30, 30, 0.85)",
+ "--ss-item-hover": "rgba(255, 255, 255, 0.05)",
+ "--ss-sidebar-text": "#e0e0e0",
+ "--ss-sidebar-active-bg": "rgba(255, 255, 255, 0.08)",
+ "--ss-sidebar-active-text": "#e0e0e0"
+ }
+ }
+}
diff --git a/themes/mica-light.json b/themes/mica-light.json
new file mode 100644
index 0000000..80f274d
--- /dev/null
+++ b/themes/mica-light.json
@@ -0,0 +1,31 @@
+{
+ "name": "Mica-浅色",
+ "id": "mica-light",
+ "mode": "light",
+ "mica": {
+ "effect": "mica",
+ "theme": "light",
+ "radius": "medium"
+ },
+ "config": {
+ "tdesign": {
+ "brandColor": "#0052D9",
+ "warningColor": "#E37318",
+ "errorColor": "#D32029",
+ "successColor": "#248232"
+ },
+ "custom": {
+ "--ss-bg-color": "transparent",
+ "--ss-card-bg": "rgba(255, 255, 255, 0.7)",
+ "--ss-text-main": "#1a1a1a",
+ "--ss-text-secondary": "#5e5e5e",
+ "--ss-border-color": "rgba(0, 0, 0, 0.1)",
+ "--ss-header-bg": "transparent",
+ "--ss-sidebar-bg": "rgba(255, 255, 255, 0.85)",
+ "--ss-item-hover": "rgba(0, 0, 0, 0.05)",
+ "--ss-sidebar-text": "#1a1a1a",
+ "--ss-sidebar-active-bg": "rgba(0, 0, 0, 0.08)",
+ "--ss-sidebar-active-text": "#1a1a1a"
+ }
+ }
+}
From 3c21a05c3767dad6b32f6291f8e51927ec7f5e00 Mon Sep 17 00:00:00 2001
From: Linkon <116425752+Linkon-lcw@users.noreply.github.com>
Date: Tue, 20 Jan 2026 02:28:24 +0800
Subject: [PATCH 13/18] =?UTF-8?q?fix(window):=20=E4=BF=AE=E5=A4=8D=20Mica/?=
=?UTF-8?q?Acrylic=20=E8=83=8C=E6=99=AF=E4=B8=8D=E9=80=8F=E6=98=8E?=
=?UTF-8?q?=E5=8F=8A=E5=A4=B1=E7=84=A6=E5=A4=B1=E6=95=88=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/services/WindowManager.ts | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/main/services/WindowManager.ts b/src/main/services/WindowManager.ts
index 3cc2852..8e68065 100644
--- a/src/main/services/WindowManager.ts
+++ b/src/main/services/WindowManager.ts
@@ -98,6 +98,8 @@ export class WindowManager extends Service {
show: false,
autoHideMenuBar: true,
frame: false,
+ transparent: true,
+ backgroundColor: '#00000000',
icon: this.opts.icon,
title: input.title,
webPreferences: {
@@ -161,6 +163,18 @@ export class WindowManager extends Service {
win.webContents.send('window:maximized-changed', false)
})
+ win.on('blur', () => {
+ if (input.key === 'global-sidebar' || input.key === 'main') {
+ this.applyMicaEffect(win)
+ }
+ })
+
+ win.on('focus', () => {
+ if (input.key === 'global-sidebar' || input.key === 'main') {
+ this.applyMicaEffect(win)
+ }
+ })
+
win.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url)
return { action: 'deny' }
@@ -226,7 +240,10 @@ export class WindowManager extends Service {
}
}
- public setMicaEffect(win: BrowserWindow, effect: 'mica' | 'tabbed' | 'acrylic' | 'blur' | 'transparent' | 'none' = 'mica') {
+ public setMicaEffect(
+ win: BrowserWindow,
+ effect: 'mica' | 'tabbed' | 'acrylic' | 'blur' | 'transparent' | 'none' = 'mica'
+ ) {
if (!micaElectron) return
const micaWin = win as MicaWindow
From 0c4877497bd5be148e5375cd849c8e7c2a98f177 Mon Sep 17 00:00:00 2001
From: Linkon <116425752+Linkon-lcw@users.noreply.github.com>
Date: Tue, 20 Jan 2026 02:46:21 +0800
Subject: [PATCH 14/18] =?UTF-8?q?style(theme):=20=E8=B0=83=E6=95=B4?=
=?UTF-8?q?=E4=B8=BB=E9=A2=98=E6=A0=B7=E5=BC=8F=E5=92=8C=E9=80=8F=E6=98=8E?=
=?UTF-8?q?=E5=BA=A6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修改 custom.json 中的圆角大小
- 在 main.css 中添加 TDesign 样式覆盖
- 调整 acrylic 主题的卡片和侧边栏透明度
---
src/renderer/src/assets/main.css | 9 +++++++++
themes/acrylic-dark.json | 4 ++--
themes/acrylic-light.json | 4 ++--
themes/custom.json | 2 +-
4 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css
index b11421c..4ac71f1 100644
--- a/src/renderer/src/assets/main.css
+++ b/src/renderer/src/assets/main.css
@@ -1,6 +1,15 @@
@import 'tdesign-react/dist/reset.css';
@import 'tdesign-react/dist/tdesign.css';
+/* 没用过tdesign,有ai或者人类会用顺便帮我改一下 */
+.t-layout {
+ background-color: var(--ss-bg-color) !important;
+}
+.t-default-menu{
+ background-color: transparent !important;
+}
+/* 没用过tdesign,有ai或者人类会用顺便帮我改一下 */
+
html,
body,
#root {
diff --git a/themes/acrylic-dark.json b/themes/acrylic-dark.json
index 4e86118..2fbd765 100644
--- a/themes/acrylic-dark.json
+++ b/themes/acrylic-dark.json
@@ -16,12 +16,12 @@
},
"custom": {
"--ss-bg-color": "transparent",
- "--ss-card-bg": "rgba(30, 30, 30, 0.85)",
+ "--ss-card-bg": "rgba(30, 30, 30, 0.3)",
"--ss-text-main": "#e0e0e0",
"--ss-text-secondary": "#a0a0a0",
"--ss-border-color": "rgba(255, 255, 255, 0.08)",
"--ss-header-bg": "transparent",
- "--ss-sidebar-bg": "rgba(30, 30, 30, 0.9)",
+ "--ss-sidebar-bg": "rgba(30, 30, 30, 0.5)",
"--ss-item-hover": "rgba(255, 255, 255, 0.04)",
"--ss-sidebar-text": "#e0e0e0",
"--ss-sidebar-active-bg": "rgba(255, 255, 255, 0.06)",
diff --git a/themes/acrylic-light.json b/themes/acrylic-light.json
index cd6ddd7..12cb599 100644
--- a/themes/acrylic-light.json
+++ b/themes/acrylic-light.json
@@ -16,12 +16,12 @@
},
"custom": {
"--ss-bg-color": "transparent",
- "--ss-card-bg": "rgba(255, 255, 255, 0.85)",
+ "--ss-card-bg": "rgba(255, 255, 255, 0.3)",
"--ss-text-main": "#1a1a1a",
"--ss-text-secondary": "#5e5e5e",
"--ss-border-color": "rgba(0, 0, 0, 0.08)",
"--ss-header-bg": "transparent",
- "--ss-sidebar-bg": "rgba(255, 255, 255, 0.9)",
+ "--ss-sidebar-bg": "rgba(255, 255, 255, 0.5)",
"--ss-item-hover": "rgba(0, 0, 0, 0.04)",
"--ss-sidebar-text": "#1a1a1a",
"--ss-sidebar-active-bg": "rgba(0, 0, 0, 0.06)",
diff --git a/themes/custom.json b/themes/custom.json
index f9374db..7e1efa1 100644
--- a/themes/custom.json
+++ b/themes/custom.json
@@ -5,7 +5,7 @@
"mica": {
"effect": "mica",
"theme": "auto",
- "radius": "medium"
+ "radius": "large"
},
"config": {
"tdesign": {
From 2403cfb588e0ef6c366b765e093bbdf1a7b8b97e Mon Sep 17 00:00:00 2001
From: Linkon <116425752+Linkon-lcw@users.noreply.github.com>
Date: Tue, 20 Jan 2026 03:00:11 +0800
Subject: [PATCH 15/18] feat: add ThemeEditor component with real-time preview
and settings integration
---
src/renderer/src/App.tsx | 17 +-
src/renderer/src/components/Settings.tsx | 32 ++-
src/renderer/src/components/ThemeEditor.tsx | 232 ++++++++++++++++++++
3 files changed, 266 insertions(+), 15 deletions(-)
create mode 100644 src/renderer/src/components/ThemeEditor.tsx
diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx
index ba27e7d..e6a860e 100644
--- a/src/renderer/src/App.tsx
+++ b/src/renderer/src/App.tsx
@@ -5,7 +5,9 @@ import { Sidebar } from './components/Sidebar'
import { ContentArea } from './components/ContentArea'
import { Wizard } from './components/Wizard'
import { ThemeProvider } from './contexts/ThemeContext'
+import { ThemeEditorProvider } from './contexts/ThemeEditorContext'
import { GlobalSidebar } from './components/GlobalSidebar'
+import { ThemeEditor } from './components/ThemeEditor'
function MainContent(): React.JSX.Element {
const navigate = useNavigate()
@@ -143,12 +145,15 @@ function MainContent(): React.JSX.Element {
function App(): React.JSX.Element {
return (
-
-
- } />
- } />
-
-
+
+
+
+ } />
+ } />
+
+
+
+
)
}
diff --git a/src/renderer/src/components/Settings.tsx b/src/renderer/src/components/Settings.tsx
index a1ddd90..075bcbe 100644
--- a/src/renderer/src/components/Settings.tsx
+++ b/src/renderer/src/components/Settings.tsx
@@ -13,6 +13,7 @@ import {
MessagePlugin
} from 'tdesign-react'
import { useTheme } from '../contexts/ThemeContext'
+import { useThemeEditor } from '../contexts/ThemeEditorContext'
type permissionLevel = 'admin' | 'points' | 'view'
type appSettings = {
@@ -23,6 +24,7 @@ type appSettings = {
export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission }) => {
const { themes, currentTheme, setTheme } = useTheme()
+ const { startEditing } = useThemeEditor()
const [activeTab, setActiveTab] = useState('appearance')
const [settings, setSettings] = useState
({
is_wizard_completed: false,
@@ -261,15 +263,27 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
-
+
+
+
+
+
diff --git a/src/renderer/src/components/ThemeEditor.tsx b/src/renderer/src/components/ThemeEditor.tsx
new file mode 100644
index 0000000..d794a5a
--- /dev/null
+++ b/src/renderer/src/components/ThemeEditor.tsx
@@ -0,0 +1,232 @@
+import React, { useEffect } from 'react'
+import {
+ Drawer,
+ Form,
+ Input,
+ Select,
+ ColorPicker,
+ Button,
+ Space,
+ Divider,
+ Row,
+ Col
+} from 'tdesign-react'
+import { useThemeEditor } from '../contexts/ThemeEditorContext'
+import { useTheme } from '../contexts/ThemeContext'
+import { generateColorMap } from '../utils/color'
+import { themeConfig } from '../../../preload/types'
+
+const variableGroups = {
+ background: {
+ title: '背景颜色',
+ items: [
+ { key: '--ss-bg-color', label: '全局背景' },
+ { key: '--ss-card-bg', label: '卡片背景' },
+ { key: '--ss-header-bg', label: '顶部栏背景' },
+ { key: '--ss-sidebar-bg', label: '侧边栏背景' }
+ ]
+ },
+ text: {
+ title: '文字颜色',
+ items: [
+ { key: '--ss-text-main', label: '主要文字' },
+ { key: '--ss-text-secondary', label: '次要文字' },
+ { key: '--ss-sidebar-active-text', label: '侧边栏选中文字' }
+ ]
+ },
+ border: {
+ title: '边框与分割线',
+ items: [{ key: '--ss-border-color', label: '通用边框' }]
+ },
+ interaction: {
+ title: '交互状态',
+ items: [
+ { key: '--ss-item-hover', label: '列表悬浮' },
+ { key: '--ss-sidebar-active-bg', label: '侧边栏选中背景' }
+ ]
+ }
+}
+
+export const ThemeEditor: React.FC = () => {
+ const {
+ isEditing,
+ editingTheme,
+ updateEditingTheme,
+ updateConfig,
+ saveEditingTheme,
+ cancelEditing
+ } = useThemeEditor()
+
+ const { currentTheme } = useTheme()
+
+ // 实时预览逻辑
+ useEffect(() => {
+ if (!isEditing || !editingTheme) return
+
+ const applyPreview = (theme: themeConfig) => {
+ const { tdesign, custom } = theme.config
+ const root = document.documentElement
+
+ // 1. 设置 TDesign 模式
+ root.setAttribute('theme-mode', theme.mode)
+
+ // 2. 设置 TDesign 品牌色
+ if (tdesign.brandColor) {
+ const colorMap = generateColorMap(tdesign.brandColor, theme.mode)
+ Object.entries(colorMap).forEach(([key, value]) => {
+ root.style.setProperty(key, value)
+ })
+ }
+
+ // 3. 应用自定义变量
+ Object.entries(custom).forEach(([key, value]) => {
+ root.style.setProperty(key, value)
+ })
+ }
+
+ applyPreview(editingTheme)
+ }, [editingTheme, isEditing])
+
+ // 关闭时恢复原有主题
+ useEffect(() => {
+ if (!isEditing && currentTheme) {
+ const { tdesign, custom } = currentTheme.config
+ const root = document.documentElement
+
+ root.setAttribute('theme-mode', currentTheme.mode)
+
+ if (tdesign.brandColor) {
+ const colorMap = generateColorMap(tdesign.brandColor, currentTheme.mode)
+ Object.entries(colorMap).forEach(([key, value]) => {
+ root.style.setProperty(key, value)
+ })
+ }
+
+ Object.entries(custom).forEach(([key, value]) => {
+ root.style.setProperty(key, value)
+ })
+ }
+ }, [isEditing, currentTheme])
+
+ if (!editingTheme) return null
+
+ return (
+
+
+
+
+ }
+ destroyOnClose
+ >
+
+
+ )
+}
From b5874980fcfc34cdc9b339ef8e73b7ebbceb76c0 Mon Sep 17 00:00:00 2001
From: Linkon <116425752+Linkon-lcw@users.noreply.github.com>
Date: Tue, 20 Jan 2026 03:32:38 +0800
Subject: [PATCH 16/18] =?UTF-8?q?feat(=E4=B8=BB=E9=A2=98):=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E4=B8=BB=E9=A2=98=E7=BC=96=E8=BE=91=E5=99=A8=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD=E5=B9=B6=E6=9B=B4=E6=96=B0=E7=9B=B8=E5=85=B3UI?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增主题编辑器组件和上下文管理
- 实现主题的保存、删除和自定义功能
- 更新主题相关API接口
- 调整主题样式透明度
- 替换应用logo为高清版本
- 更新README和package.json描述信息
---
README.md | 19 +++-
package.json | 7 +-
scripts/ci/parse-commit.mjs | 14 ++-
scripts/clean-db.mjs | 6 +-
src/main/services/SettingsService.ts | 3 +-
src/main/services/ThemeService.ts | 67 +++++++++++---
src/preload/index.ts | 2 +
src/preload/types.ts | 2 +
src/renderer/src/assets/logoHD-dark.svg | 7 ++
src/renderer/src/assets/logoHD.svg | 7 ++
src/renderer/src/components/ContentArea.tsx | 2 +
src/renderer/src/components/GlobalSidebar.tsx | 1 -
src/renderer/src/components/Sidebar.tsx | 2 +-
src/renderer/src/components/ThemeEditor.tsx | 17 ++--
.../src/contexts/ThemeEditorContext.tsx | 89 +++++++++++++++++++
themes/acrylic-auto.json | 4 +-
themes/mica-auto.json | 4 +-
themes/mica-dark.json | 4 +-
themes/mica-light.json | 4 +-
19 files changed, 221 insertions(+), 40 deletions(-)
create mode 100644 src/renderer/src/assets/logoHD-dark.svg
create mode 100644 src/renderer/src/assets/logoHD.svg
create mode 100644 src/renderer/src/contexts/ThemeEditorContext.tsx
diff --git a/README.md b/README.md
index 0bac02c..5a7ec28 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,23 @@
# SecScore
-一款本地离线(未来加入远程模式)的“教育积分管理”桌面软件(Electron + React + TypeScript),用于管理学生名单、记录加/扣分、查看排行榜与结算历史,并提供基础的权限保护与数据备份。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+SecScore 是一款教育积分管理软件,基于 Electron + React + TypeScript 开发,用于管理学生名单、记录加/扣分、查看排行榜与结算历史,并提供权限保护与数据备份。
+
+
+SecScore 是一款教育积分管理软件,基于 Electron + React + TypeScript 开发,用于管理学生名单、记录加/扣分、查看排行榜与结算历史,并提供权限保护与数据备份。
## 主要功能
diff --git a/package.json b/package.json
index c987989..2b7cf6d 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"name": "secscore",
"version": "1.0.0",
- "description": "An Electron application with React and TypeScript",
+ "description": "SecScore – Your Education Points Management Expert",
"main": "./out/main/index.js",
"author": "example.com",
- "homepage": "https://electron-vite.org",
+ "homepage": "https://example.org",
"scripts": {
"format": "prettier --write .",
"lint": "eslint --cache .",
@@ -35,7 +35,8 @@
"uuid": "^13.0.0",
"winston": "^3.19.0",
"winston-daily-rotate-file": "^5.0.0",
- "xlsx": "^0.18.5"
+ "xlsx": "^0.18.5",
+ "pnpm": "^8.1.2"
},
"devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
diff --git a/scripts/ci/parse-commit.mjs b/scripts/ci/parse-commit.mjs
index e734af5..cfacb09 100644
--- a/scripts/ci/parse-commit.mjs
+++ b/scripts/ci/parse-commit.mjs
@@ -13,19 +13,27 @@ const rawMessage =
''
const message = String(rawMessage || '').trim()
+// 是否以 release: 开头
+const isReleasePrefix = /^release:/i.test(message)
+
const semverRe = /\bv?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/
const versionMatch = message.match(semverRe)
const version = versionMatch ? versionMatch[1] : ''
const buildToken =
message.match(/\b(build:(?:win|mac|linux|unpack|all))\b/i)?.[1]?.toLowerCase() ?? ''
-const buildScript =
+let buildScript =
buildToken === 'build:all'
? 'build:all'
: buildToken && buildToken.startsWith('build:')
? buildToken
: ''
+// 如果是 release: 开头且没有显式指定构建脚本,默认为 build:all
+if (isReleasePrefix && !buildScript) {
+ buildScript = 'build:all'
+}
+
const supportedBuildScripts = new Set(['build:win', 'build:mac', 'build:linux', 'build:unpack'])
const hasBuildScript = buildScript
? buildScript === 'build:all'
@@ -38,7 +46,7 @@ const fail = (text) => {
process.exit(1)
}
-const hasAnySignal = Boolean(version || buildScript)
+const hasAnySignal = Boolean(isReleasePrefix || version || buildScript)
const shouldSkip = !hasAnySignal
if (!shouldSkip) {
@@ -46,7 +54,7 @@ if (!shouldSkip) {
fail(
[
'未在提交信息中检测到版本号。',
- '示例:release: v1.2.3 build:win',
+ '示例:release: v1.2.3 build:win 或 release: 1.2.3',
'支持格式:v1.2.3 或 1.2.3(可带 -beta.1 等后缀)'
].join('\n')
)
diff --git a/scripts/clean-db.mjs b/scripts/clean-db.mjs
index 8ce8e19..53f062a 100644
--- a/scripts/clean-db.mjs
+++ b/scripts/clean-db.mjs
@@ -12,6 +12,10 @@ for (const name of targets) {
console.log(`[clean-db] removed ${filePath}`)
}
} catch (e) {
- console.error(`[clean-db] failed to remove ${filePath}:`, e?.message || e)
+ if (e.code === 'EPERM' || e.code === 'EACCES') {
+ console.warn(`[clean-db] skipped ${filePath}: file in use or permission denied`)
+ } else {
+ console.error(`[clean-db] failed to remove ${filePath}:`, e?.message || e)
+ }
}
}
diff --git a/src/main/services/SettingsService.ts b/src/main/services/SettingsService.ts
index 7bfcd61..0ee83ff 100644
--- a/src/main/services/SettingsService.ts
+++ b/src/main/services/SettingsService.ts
@@ -91,7 +91,8 @@ export class SettingsService extends Service {
kind: 'string',
defaultValue: 'mica',
writePermission: 'admin',
- validate: (v) => ['mica', 'tabbed', 'acrylic', 'blur', 'transparent', 'none'].includes(v as string)
+ validate: (v) =>
+ ['mica', 'tabbed', 'acrylic', 'blur', 'transparent', 'none'].includes(v as string)
},
window_radius: {
kind: 'string',
diff --git a/src/main/services/ThemeService.ts b/src/main/services/ThemeService.ts
index b5791c6..0493893 100644
--- a/src/main/services/ThemeService.ts
+++ b/src/main/services/ThemeService.ts
@@ -88,19 +88,62 @@ export class ThemeService extends Service {
return { success: true }
})
- this.mainCtx.handle('theme:set-custom', async (event, config: {
- effect: 'mica' | 'tabbed' | 'acrylic' | 'blur' | 'transparent' | 'none'
- theme: 'auto' | 'dark' | 'light'
- radius: 'small' | 'medium' | 'large'
- }) => {
+ this.mainCtx.handle('theme:save', async (event, theme: themeConfig) => {
if (!this.mainCtx.permissions.requirePermission(event, 'admin'))
return { success: false, message: 'Permission denied' }
- this.currentThemeId = 'custom'
- this.applyMicaConfig(config)
- this.notifyThemeUpdate()
- return { success: true }
+ try {
+ const filePath = path.join(this.themeDir, `${theme.id}.json`)
+ fs.writeFileSync(filePath, JSON.stringify(theme, null, 2), 'utf-8')
+ this.notifyThemeUpdate()
+ return { success: true }
+ } catch (e) {
+ return { success: false, message: String(e) }
+ }
})
+
+ this.mainCtx.handle('theme:delete', async (event, themeId: string) => {
+ if (!this.mainCtx.permissions.requirePermission(event, 'admin'))
+ return { success: false, message: 'Permission denied' }
+
+ if (themeId.startsWith('default-')) {
+ return { success: false, message: 'Cannot delete default themes' }
+ }
+
+ try {
+ const filePath = path.join(this.themeDir, `${themeId}.json`)
+ if (fs.existsSync(filePath)) {
+ fs.unlinkSync(filePath)
+ }
+ if (this.currentThemeId === themeId) {
+ this.currentThemeId = 'light' // Fallback
+ }
+ this.notifyThemeUpdate()
+ return { success: true }
+ } catch (e) {
+ return { success: false, message: String(e) }
+ }
+ })
+
+ this.mainCtx.handle(
+ 'theme:set-custom',
+ async (
+ event,
+ config: {
+ effect: 'mica' | 'tabbed' | 'acrylic' | 'blur' | 'transparent' | 'none'
+ theme: 'auto' | 'dark' | 'light'
+ radius: 'small' | 'medium' | 'large'
+ }
+ ) => {
+ if (!this.mainCtx.permissions.requirePermission(event, 'admin'))
+ return { success: false, message: 'Permission denied' }
+
+ this.currentThemeId = 'custom'
+ this.applyMicaConfig(config)
+ this.notifyThemeUpdate()
+ return { success: true }
+ }
+ )
}
private applyMicaEffect(themeId: string) {
@@ -116,9 +159,9 @@ export class ThemeService extends Service {
radius: 'small' | 'medium' | 'large'
}) {
const radiusMap: Record = {
- 'small': 'small',
- 'medium': 'rounded',
- 'large': 'rounded'
+ small: 'small',
+ medium: 'rounded',
+ large: 'rounded'
}
const windows = BrowserWindow.getAllWindows()
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 8102a3f..d169f51 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -7,6 +7,8 @@ const api = {
getThemes: () => ipcRenderer.invoke('theme:list'),
getCurrentTheme: () => ipcRenderer.invoke('theme:current'),
setTheme: (themeId: string) => ipcRenderer.invoke('theme:set', themeId),
+ saveTheme: (theme: themeConfig) => ipcRenderer.invoke('theme:save', theme),
+ deleteTheme: (themeId: string) => ipcRenderer.invoke('theme:delete', themeId),
setCustomTheme: (config: {
effect: 'mica' | 'tabbed' | 'acrylic' | 'blur' | 'transparent' | 'none'
theme: 'auto' | 'dark' | 'light'
diff --git a/src/preload/types.ts b/src/preload/types.ts
index 6c9cdaf..a456e31 100644
--- a/src/preload/types.ts
+++ b/src/preload/types.ts
@@ -43,6 +43,8 @@ export interface electronApi {
getThemes: () => Promise>
getCurrentTheme: () => Promise>
setTheme: (themeId: string) => Promise>
+ saveTheme: (theme: themeConfig) => Promise>
+ deleteTheme: (themeId: string) => Promise>
setCustomTheme: (config: {
effect: 'mica' | 'tabbed' | 'acrylic' | 'blur' | 'transparent' | 'none'
theme: 'auto' | 'dark' | 'light'
diff --git a/src/renderer/src/assets/logoHD-dark.svg b/src/renderer/src/assets/logoHD-dark.svg
new file mode 100644
index 0000000..b190979
--- /dev/null
+++ b/src/renderer/src/assets/logoHD-dark.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/renderer/src/assets/logoHD.svg b/src/renderer/src/assets/logoHD.svg
new file mode 100644
index 0000000..51ff5ea
--- /dev/null
+++ b/src/renderer/src/assets/logoHD.svg
@@ -0,0 +1,7 @@
+
diff --git a/src/renderer/src/components/ContentArea.tsx b/src/renderer/src/components/ContentArea.tsx
index 9108a5e..a049d52 100644
--- a/src/renderer/src/components/ContentArea.tsx
+++ b/src/renderer/src/components/ContentArea.tsx
@@ -2,6 +2,7 @@ import React, { Suspense, lazy } from 'react'
import { Layout, Space, Button, Tag, Loading } from 'tdesign-react'
import { Routes, Route, Navigate } from 'react-router-dom'
import { WindowControls } from './WindowControls'
+import { ThemeEditor } from './ThemeEditor'
const Home = lazy(() => import('./Home').then((m) => ({ default: m.Home })))
const StudentManager = lazy(() =>
@@ -125,6 +126,7 @@ export function ContentArea({
+
)
}
diff --git a/src/renderer/src/components/GlobalSidebar.tsx b/src/renderer/src/components/GlobalSidebar.tsx
index adccddc..c8fa6d1 100644
--- a/src/renderer/src/components/GlobalSidebar.tsx
+++ b/src/renderer/src/components/GlobalSidebar.tsx
@@ -55,7 +55,6 @@ export const GlobalSidebar: React.FC = () => {
// 1. 先隐藏三角
setShowToggle(false)
-
// 2. 稍后扩大窗口
setTimeout(() => {
if ((window as any).api) {
diff --git a/src/renderer/src/components/Sidebar.tsx b/src/renderer/src/components/Sidebar.tsx
index 9f30b8f..1c80c65 100644
--- a/src/renderer/src/components/Sidebar.tsx
+++ b/src/renderer/src/components/Sidebar.tsx
@@ -7,7 +7,7 @@ import {
ViewListIcon,
HomeIcon
} from 'tdesign-icons-react'
-import appLogo from '../assets/logo.svg'
+import appLogo from '../assets/logoHD.svg'
const { Aside } = Layout
diff --git a/src/renderer/src/components/ThemeEditor.tsx b/src/renderer/src/components/ThemeEditor.tsx
index d794a5a..8786d18 100644
--- a/src/renderer/src/components/ThemeEditor.tsx
+++ b/src/renderer/src/components/ThemeEditor.tsx
@@ -135,16 +135,16 @@ export const ThemeEditor: React.FC = () => {
基本信息
-
+
updateEditingTheme({ name: v })}
placeholder="请输入主题名称"
/>
-
+
-
+
+
-
+
updateEditingTheme({ id: v })}
placeholder="请输入主题 ID"
/>
-
+
@@ -170,14 +170,14 @@ export const ThemeEditor: React.FC = () => {
{/* TDesign 品牌色 */}
{/* 业务自定义变量 */}
@@ -216,7 +216,6 @@ export const ThemeEditor: React.FC = () => {
onChange={(v) => updateConfig('custom', item.key, v)}
enableAlpha
format="HEX"
- size="small"
/>