Skip to content

Commit 2f3885a

Browse files
committed
add switchable theme styles
1 parent 0c263a1 commit 2f3885a

29 files changed

Lines changed: 398 additions & 526 deletions

packages/desktop/electron/main.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ app.whenReady().then(async () => {
8686
currentWorkspaceCwd = cwd;
8787
});
8888

89+
// Theme sync: update window background and title bar overlay
90+
const themeColors = {
91+
dark: { bg: '#1e1e1e', overlay: '#1a1a1a', symbol: '#858585' },
92+
light: { bg: '#ffffff', overlay: '#f5f5f5', symbol: '#666666' },
93+
paper: { bg: '#f5f0e8', overlay: '#ede8de', symbol: '#7a7872' },
94+
};
95+
96+
ipcMain.on('theme:change', (_e, theme: string) => {
97+
if (!mainWindow) return;
98+
const c = themeColors[theme as keyof typeof themeColors];
99+
if (!c) return;
100+
101+
mainWindow.setBackgroundColor(c.bg);
102+
103+
if (process.platform === 'win32') {
104+
mainWindow.setTitleBarOverlay({
105+
color: c.overlay,
106+
symbolColor: c.symbol,
107+
});
108+
}
109+
});
110+
89111
registerFsHandlers();
90112
registerGitHandlers();
91113

packages/desktop/electron/preload.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ const api = {
3131
// Workspace cwd sync (renderer -> main)
3232
setWorkspaceCwd: (cwd: string): void => ipcRenderer.send('workspace:setCwd', cwd),
3333

34+
// Theme sync (renderer -> main)
35+
setTheme: (theme: string): void => ipcRenderer.send('theme:change', theme),
36+
3437
// Git (explicit cwd)
3538
gitStatus: (cwd: string): Promise<unknown> => ipcRenderer.invoke('git:status', cwd),
3639
gitBranches: (cwd: string): Promise<string[]> => ipcRenderer.invoke('git:branches', cwd),

packages/desktop/src/App.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ErrorBoundary from './shared/ErrorBoundary';
77

88
export default function App() {
99
const mode = useGlobalStore((s) => s.ui.mode);
10+
const theme = useGlobalStore((s) => s.ui.theme);
1011
const setMode = useGlobalStore((s) => s.setMode);
1112
const rootPath = useGlobalStore((s) => s.workspace.rootPath);
1213

@@ -17,6 +18,12 @@ export default function App() {
1718
}
1819
}, [rootPath]);
1920

21+
// Sync theme to document and main process
22+
useEffect(() => {
23+
document.documentElement.dataset.theme = theme;
24+
window.electronAPI?.setTheme?.(theme);
25+
}, [theme]);
26+
2027
useEffect(() => {
2128
const off = window.electronAPI?.onFsChange?.(() => {});
2229
const handler = ((e: CustomEvent<'agent' | 'ide'>) => {
@@ -31,7 +38,7 @@ export default function App() {
3138

3239
return (
3340
<ErrorBoundary>
34-
<div className="h-screen flex flex-col bg-[#1e1e1e] text-[#cccccc] overflow-hidden">
41+
<div className="h-screen flex flex-col bg-[var(--bg-base)] text-[var(--text-primary)] overflow-hidden">
3542
<TitleBar />
3643
{/* Both layouts stay mounted; visibility toggled via display to preserve Monaco + PTY state */}
3744
<div className={`${mode === 'agent' ? 'flex' : 'hidden'} flex-1 flex-col overflow-hidden`}>

packages/desktop/src/TitleBar.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ export default function TitleBar() {
1010

1111
return (
1212
<div
13-
className="shrink-0 flex items-center px-3 bg-[#1a1a1a] select-none"
13+
className="shrink-0 flex items-center px-3 bg-[var(--bg-card)] select-none"
1414
style={
1515
{
1616
height: 'env(titlebar-area-height, 36px)',
1717
WebkitAppRegion: 'drag',
1818
} as React.CSSProperties
1919
}
2020
>
21-
<span className="text-[#858585] text-xs font-medium">Coding Code</span>
21+
<span className="text-[var(--text-tertiary)] text-xs font-medium">Coding Code</span>
2222
<div
2323
className="flex items-center gap-0.5 ml-4"
2424
style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}
@@ -27,8 +27,8 @@ export default function TitleBar() {
2727
onClick={() => setMode('agent')}
2828
className={`px-3 h-6 text-xs rounded transition-colors ${
2929
mode === 'agent'
30-
? 'bg-[#0e639c] text-white'
31-
: 'text-[#858585] hover:text-[#cccccc] hover:bg-[#2d2d2d]'
30+
? 'bg-[var(--accent-primary)] text-[var(--text-inverse)]'
31+
: 'text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)]'
3232
}`}
3333
>
3434
Agent
@@ -37,8 +37,8 @@ export default function TitleBar() {
3737
onClick={() => setMode('ide')}
3838
className={`px-3 h-6 text-xs rounded transition-colors ${
3939
mode === 'ide'
40-
? 'bg-[#0e639c] text-white'
41-
: 'text-[#858585] hover:text-[#cccccc] hover:bg-[#2d2d2d]'
40+
? 'bg-[var(--accent-primary)] text-[var(--text-inverse)]'
41+
: 'text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)]'
4242
}`}
4343
>
4444
IDE

packages/desktop/src/agent/AgentSidebar.tsx

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,12 @@ export default function AgentSidebar() {
6969

7070
if (sidebarCollapsed) {
7171
return (
72-
<div className="flex flex-col items-center w-10 shrink-0 bg-[#161616] border-r border-[#222] pt-2 gap-1">
72+
<div className="flex flex-col items-center w-10 shrink-0 bg-[var(--bg-sidebar)] border-r border-[var(--border-default)] pt-2 gap-1">
7373
<button
7474
type="button"
7575
onClick={toggleSidebar}
7676
title="展开侧边栏"
77-
className="w-7 h-7 flex items-center justify-center text-[#555] hover:text-[#ccc] hover:bg-[#252525] rounded transition-colors"
77+
className="w-7 h-7 flex items-center justify-center text-[var(--text-placeholder)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded transition-colors"
7878
>
7979
<ChevronRight size={16} strokeWidth={1.5} />
8080
</button>
@@ -83,17 +83,17 @@ export default function AgentSidebar() {
8383
}
8484

8585
return (
86-
<div className="flex flex-col shrink-0 bg-[#161616] border-r border-[#222] w-64 select-none">
86+
<div className="flex flex-col shrink-0 bg-[var(--bg-sidebar)] border-r border-[var(--border-default)] w-64 select-none">
8787
{/* 顶部栏:项目名 + 收起按钮 */}
8888
<div className="flex items-center justify-between px-2 pt-2">
89-
<span className="text-[13px] font-semibold text-[#888] truncate ml-2">
89+
<span className="text-[13px] font-semibold text-[var(--text-tertiary)] truncate ml-2">
9090
{projectName || '项目'}
9191
</span>
9292
<button
9393
type="button"
9494
onClick={toggleSidebar}
9595
title="收起侧边栏"
96-
className="w-7 h-7 flex items-center justify-center text-[#555] hover:text-[#ccc] hover:bg-[#252525] rounded transition-colors"
96+
className="w-7 h-7 flex items-center justify-center text-[var(--text-placeholder)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded transition-colors"
9797
>
9898
<ChevronLeft size={16} strokeWidth={1.5} />
9999
</button>
@@ -104,7 +104,7 @@ export default function AgentSidebar() {
104104
<button
105105
type="button"
106106
onClick={() => setCurrentThread(null)}
107-
className="w-full flex items-center gap-2.5 px-4 py-2.5 rounded-lg text-[14px] text-[#bbb] hover:text-white hover:bg-[#252525] transition-colors border border-[#2a2a2a] hover:border-[#3a3a3a]"
107+
className="w-full flex items-center gap-2.5 px-4 py-2.5 rounded-lg text-[14px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-colors border border-[var(--border-card)] hover:border-[var(--border-hover)]"
108108
>
109109
<Plus size={16} strokeWidth={1.5} />
110110
<span>新对话</span>
@@ -117,12 +117,12 @@ export default function AgentSidebar() {
117117
<NavItem icon={<Zap size={16} strokeWidth={1.5} />} label="自动化" />
118118
</nav>
119119

120-
<div className="mx-3 border-t border-[#222]" />
120+
<div className="mx-3 border-t border-[var(--border-default)]" />
121121

122122
{/* 会话列表 */}
123123
<div className="flex-1 overflow-y-auto py-3 min-h-0">
124124
<div className="px-3 pb-1.5">
125-
<span className="text-[11px] font-semibold text-[#444] uppercase tracking-wider">
125+
<span className="text-[11px] font-semibold text-[var(--text-disabled)] uppercase tracking-wider">
126126
会话
127127
</span>
128128
</div>
@@ -135,8 +135,8 @@ export default function AgentSidebar() {
135135
onMouseLeave={() => setHoveredThreadId(null)}
136136
className={`w-full text-left px-4 py-2.5 rounded-lg flex items-center gap-2 transition-colors ${
137137
currentThreadId === t.id
138-
? 'bg-[#0d2d4a] text-[#cde]'
139-
: 'text-[#888] hover:bg-[#1c1c1c] hover:text-[#bbb]'
138+
? 'bg-[var(--bg-selected)] text-[var(--text-primary)]'
139+
: 'text-[var(--text-tertiary)] hover:bg-[var(--bg-card-hover)] hover:text-[var(--text-secondary)]'
140140
}`}
141141
>
142142
<span className="flex-1 text-[14px] truncate">{t.title || '未命名对话'}</span>
@@ -147,7 +147,7 @@ export default function AgentSidebar() {
147147
e.stopPropagation();
148148
handleDelete(t.id);
149149
}}
150-
className="shrink-0 p-0.5 text-[#555] hover:text-red-400 transition-colors"
150+
className="shrink-0 p-0.5 text-[var(--text-placeholder)] hover:text-red-400 transition-colors"
151151
title="删除对话"
152152
>
153153
<svg
@@ -168,7 +168,7 @@ export default function AgentSidebar() {
168168
</svg>
169169
</button>
170170
) : (
171-
<span className="text-[12px] text-[#3a3a3a] shrink-0">
171+
<span className="text-[12px] text-[var(--text-disabled)] shrink-0">
172172
{relativeTime(t.updatedAt)}
173173
</span>
174174
)}
@@ -177,24 +177,24 @@ export default function AgentSidebar() {
177177
{threadList.length > 15 && (
178178
<button
179179
type="button"
180-
className="w-full text-left px-4 py-1.5 text-[12px] text-[#3a3a3a] hover:text-[#555] transition-colors"
180+
className="w-full text-left px-4 py-1.5 text-[12px] text-[var(--text-disabled)] hover:text-[var(--text-placeholder)] transition-colors"
181181
>
182182
+{threadList.length - 15} 条更多
183183
</button>
184184
)}
185185
{threadList.length === 0 && (
186-
<div className="px-3 py-4 text-[13px] text-[#3a3a3a]">暂无对话</div>
186+
<div className="px-3 py-4 text-[13px] text-[var(--text-disabled)]">暂无对话</div>
187187
)}
188188
</div>
189189

190-
<div className="mx-3 border-t border-[#222]" />
190+
<div className="mx-3 border-t border-[var(--border-default)]" />
191191

192192
{/* 底部 */}
193193
<div className="px-2 py-2.5">
194194
<button
195195
type="button"
196196
onClick={() => useGlobalStore.getState().setView('settings')}
197-
className="w-full flex items-center gap-2 px-3 py-2 text-[13px] text-[#555] hover:text-[#ccc] hover:bg-[#252525] rounded-lg transition-colors"
197+
className="w-full flex items-center gap-2 px-3 py-2 text-[13px] text-[var(--text-placeholder)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] rounded-lg transition-colors"
198198
>
199199
<Settings size={16} strokeWidth={1.5} />
200200
<span>设置</span>
@@ -208,11 +208,11 @@ function NavItem({ icon, label, shortcut }: { icon: React.ReactNode; label: stri
208208
return (
209209
<button
210210
type="button"
211-
className="w-full flex items-center gap-2.5 px-4 py-2 rounded-lg text-[14px] text-[#666] hover:text-[#ccc] hover:bg-[#1e1e1e] transition-colors"
211+
className="w-full flex items-center gap-2.5 px-4 py-2 rounded-lg text-[14px] text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-base)] transition-colors"
212212
>
213213
<span className="w-4 flex items-center justify-center shrink-0">{icon}</span>
214214
<span className="flex-1 text-left">{label}</span>
215-
{shortcut && <span className="text-[11px] text-[#333]">{shortcut}</span>}
215+
{shortcut && <span className="text-[11px] text-[var(--text-disabled)]">{shortcut}</span>}
216216
</button>
217217
);
218218
}

0 commit comments

Comments
 (0)