Skip to content

Commit fdd9909

Browse files
committed
refactor(theme): 替换主题色从 accent 到 primary
- 将全局主题中所有 accent/active 样式替换为 primary 样式 - 替换主题 token 名称及测试中的相关字段与断言 - 调整多个组件及视图中的颜色应用,使用 primary 色调 - 更新 Chalk 主题工程实现,使用 primary 和 secondary 代替 accent 和 accentAlpha - 替换边框颜色及渐变色为 secondary - 使用 useEffect 初始化主题,确保灵活响应主题变更 - 简化部分 UI 组件边框样式与文本加粗设置,统一区域主色调用
1 parent 7c2b97a commit fdd9909

21 files changed

Lines changed: 141 additions & 152 deletions

src/tests/theme.test.ts

Lines changed: 50 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,8 @@ chalk.level = 1;
2020

2121
/** All token keys that every ThemeTokens must define. */
2222
const REQUIRED_TOKEN_KEYS: Array<keyof ThemeTokens> = [
23-
"accent",
24-
"accentAlpha",
25-
"active",
23+
"primary",
24+
"secondary",
2625
"success",
2726
"error",
2827
"warning",
@@ -53,23 +52,22 @@ test("DEFAULT_THEME has all required token keys", () => {
5352
}
5453
});
5554

56-
test("DEFAULT_THEME accent matches expected brand color", () => {
57-
assert.equal(DEFAULT_THEME.accent, "#229ac3");
58-
assert.equal(DEFAULT_THEME.accentAlpha, "#229ac3e6");
55+
test("DEFAULT_THEME primary matches expected brand color", () => {
56+
assert.equal(DEFAULT_THEME.primary, "#229ac3");
57+
assert.equal(DEFAULT_THEME.secondary, "#229ac3e6");
5958
});
6059

6160
test("DEFAULT_THEME semantic colors match expected values", () => {
6261
assert.equal(DEFAULT_THEME.success, "#52c41a");
63-
assert.equal(DEFAULT_THEME.error, "#ff4d4f");
64-
assert.equal(DEFAULT_THEME.warning, "#faad14");
65-
assert.equal(DEFAULT_THEME.info, "#1677ff");
66-
assert.equal(DEFAULT_THEME.active, "#89B4FA");
67-
assert.equal(DEFAULT_THEME.thinking, "#CCCFD3");
62+
assert.equal(DEFAULT_THEME.error, "#f5222d");
63+
assert.equal(DEFAULT_THEME.warning, "#fa8c16");
64+
assert.equal(DEFAULT_THEME.info, "#2f54eb");
65+
assert.equal(DEFAULT_THEME.thinking, "#ff4400");
6866
});
6967

7068
test("DEFAULT_THEME base colors match expected values", () => {
71-
assert.equal(DEFAULT_THEME.text, "#6C7086");
72-
assert.equal(DEFAULT_THEME.textDim, "#6C7086");
69+
assert.equal(DEFAULT_THEME.text, "#3D4149");
70+
assert.equal(DEFAULT_THEME.textDim, "#646A71");
7371
assert.equal(DEFAULT_THEME.code, "#787f8a");
7472
});
7573

@@ -91,50 +89,49 @@ test("PRESETS map contains default", () => {
9189

9290
test("resolveTheme returns DEFAULT_THEME when settings is undefined", () => {
9391
const result = resolveTheme(undefined);
94-
assert.equal(result.accent, DEFAULT_THEME.accent);
92+
assert.equal(result.primary, DEFAULT_THEME.primary);
9593
assert.equal(result.success, DEFAULT_THEME.success);
9694
});
9795

9896
test("resolveTheme returns DEFAULT_THEME for explicit 'default' preset", () => {
9997
const result = resolveTheme({ preset: "default" });
100-
assert.equal(result.accent, DEFAULT_THEME.accent);
98+
assert.equal(result.primary, DEFAULT_THEME.primary);
10199
});
102100

103101
test("resolveTheme returns DEFAULT_THEME when preset is not 'custom'", () => {
104102
const result = resolveTheme({ preset: "default" });
105103
assert.equal(result.text, DEFAULT_THEME.text);
106-
assert.equal(result.accent, DEFAULT_THEME.accent);
104+
assert.equal(result.primary, DEFAULT_THEME.primary);
107105
});
108106

109107
test("resolveTheme applies overrides when preset is 'custom'", () => {
110108
const result = resolveTheme({
111109
preset: "custom",
112-
overrides: { accent: "#ff0000" },
110+
overrides: { primary: "#ff0000" },
113111
});
114-
assert.equal(result.accent, "#ff0000");
112+
assert.equal(result.primary, "#ff0000");
115113
assert.equal(result.success, DEFAULT_THEME.success);
116114
});
117115

118116
test("resolveTheme applies multiple overrides with custom preset", () => {
119117
const result = resolveTheme({
120118
preset: "custom",
121119
overrides: {
122-
accent: "#ff6600",
120+
primary: "#ff6600",
123121
success: "greenBright",
124122
warning: "yellowBright",
125123
},
126124
});
127-
assert.equal(result.accent, "#ff6600");
125+
assert.equal(result.primary, "#ff6600");
128126
assert.equal(result.success, "greenBright");
129127
assert.equal(result.warning, "yellowBright");
130128
assert.equal(result.error, DEFAULT_THEME.error);
131129
});
132130

133131
test("resolveTheme full custom tokens with custom preset", () => {
134132
const customTokens: ThemeTokens = {
135-
accent: "#aaaaaa",
136-
accentAlpha: "#aaaaaacc",
137-
active: "blue",
133+
primary: "#aaaaaa",
134+
secondary: "#aaaaaacc",
138135
success: "blue",
139136
error: "blue",
140137
warning: "blue",
@@ -150,51 +147,51 @@ test("resolveTheme full custom tokens with custom preset", () => {
150147
gradients: ["#aaaaaa", "#bbbbbb"],
151148
};
152149
const result = resolveTheme({ preset: "custom", tokens: customTokens });
153-
assert.equal(result.accent, "#aaaaaa");
150+
assert.equal(result.primary, "#aaaaaa");
154151
assert.equal(result.code, "blue");
155152
assert.deepEqual(result.gradients, ["#aaaaaa", "#bbbbbb"]);
156153
});
157154

158155
test("resolveTheme handles override with undefined fields gracefully", () => {
159156
const result = resolveTheme({
160157
preset: "custom",
161-
overrides: { accent: undefined, success: undefined } as Partial<ThemeTokens>,
158+
overrides: { primary: undefined, success: undefined } as Partial<ThemeTokens>,
162159
});
163-
assert.equal(result.accent, DEFAULT_THEME.accent);
160+
assert.equal(result.primary, DEFAULT_THEME.primary);
164161
assert.equal(result.success, DEFAULT_THEME.success);
165162
});
166163

167164
test("resolveTheme ignores overrides when preset is not custom", () => {
168165
const result = resolveTheme({
169166
preset: "default",
170-
overrides: { accent: "#ff0000" },
167+
overrides: { primary: "#ff0000" },
171168
});
172-
assert.equal(result.accent, DEFAULT_THEME.accent);
169+
assert.equal(result.primary, DEFAULT_THEME.primary);
173170
});
174171

175172
test("resolveTheme ignores tokens when preset is not custom", () => {
176173
const result = resolveTheme({
177-
tokens: { accent: "#ff0000" } as ThemeTokens,
174+
tokens: { primary: "#ff0000" } as ThemeTokens,
178175
});
179-
assert.equal(result.accent, DEFAULT_THEME.accent);
176+
assert.equal(result.primary, DEFAULT_THEME.primary);
180177
});
181178

182179
test("resolveTheme returns DEFAULT_THEME for custom preset without token/overrides", () => {
183180
const result = resolveTheme({ preset: "custom" });
184-
assert.equal(result.accent, DEFAULT_THEME.accent);
181+
assert.equal(result.primary, DEFAULT_THEME.primary);
185182
});
186183

187184
// ---------------------------------------------------------------------------
188185
// createThemedChalk — markdown 方法直接复用顶层 token
189186
// ---------------------------------------------------------------------------
190187

191-
test("createThemedChalk heading1 produces styled output via accent", () => {
188+
test("createThemedChalk heading1 produces styled output via primary", () => {
192189
const tc = createThemedChalk(DEFAULT_THEME);
193190
assert.notEqual(tc.heading1("Hello"), "Hello");
194191
});
195192

196-
test("createThemedChalk heading1 changes when accent changes", () => {
197-
const custom: ThemeTokens = { ...DEFAULT_THEME, accent: "#ff0000" };
193+
test("createThemedChalk heading1 changes when primary changes", () => {
194+
const custom: ThemeTokens = { ...DEFAULT_THEME, primary: "#ff0000" };
198195
assert.notEqual(createThemedChalk(DEFAULT_THEME).heading1("test"), createThemedChalk(custom).heading1("test"));
199196
});
200197

@@ -220,16 +217,16 @@ test("createThemedChalk bold / italic / dim produce styled output", () => {
220217
assert.notEqual(tc.dim("dim"), "dim");
221218
});
222219

223-
test("createThemedChalk produces different output for different accent values", () => {
224-
const custom1: ThemeTokens = { ...DEFAULT_THEME, accent: "#ff0000" };
225-
const custom2: ThemeTokens = { ...DEFAULT_THEME, accent: "#00ff00" };
226-
assert.notEqual(createThemedChalk(custom1).accent("test"), createThemedChalk(custom2).accent("test"));
220+
test("createThemedChalk produces different output for different primary values", () => {
221+
const custom1: ThemeTokens = { ...DEFAULT_THEME, primary: "#ff0000" };
222+
const custom2: ThemeTokens = { ...DEFAULT_THEME, primary: "#00ff00" };
223+
assert.notEqual(createThemedChalk(custom1).primary("test"), createThemedChalk(custom2).primary("test"));
227224
});
228225

229226
test("createThemedChalk handles hex colors correctly", () => {
230227
const hexTheme: ThemeTokens = {
231228
...DEFAULT_THEME,
232-
accent: "#ff6600",
229+
primary: "#ff6600",
233230
warning: "#ffcc00",
234231
code: "#00ccff",
235232
};
@@ -244,16 +241,16 @@ test("createThemedChalk handles hex colors correctly", () => {
244241

245242
test("getCurrentThemedChalk returns DEFAULT_THEME chalk by default", () => {
246243
setCurrentTheme(DEFAULT_THEME);
247-
assert.notEqual(getCurrentThemedChalk().accent("test"), "test");
244+
assert.notEqual(getCurrentThemedChalk().primary("test"), "test");
248245
});
249246

250247
test("setCurrentTheme changes getCurrentThemedChalk output", () => {
251248
setCurrentTheme(DEFAULT_THEME);
252-
const first = getCurrentThemedChalk().accent("test");
249+
const first = getCurrentThemedChalk().primary("test");
253250

254-
const custom: ThemeTokens = { ...DEFAULT_THEME, accent: "#ff0000" };
251+
const custom: ThemeTokens = { ...DEFAULT_THEME, primary: "#ff0000" };
255252
setCurrentTheme(custom);
256-
const second = getCurrentThemedChalk().accent("test");
253+
const second = getCurrentThemedChalk().primary("test");
257254

258255
assert.notEqual(first, second);
259256

@@ -262,11 +259,11 @@ test("setCurrentTheme changes getCurrentThemedChalk output", () => {
262259

263260
test("setCurrentTheme changes getCurrentThemeTokens output", () => {
264261
setCurrentTheme(DEFAULT_THEME);
265-
assert.equal(getCurrentThemeTokens().accent, DEFAULT_THEME.accent);
262+
assert.equal(getCurrentThemeTokens().primary, DEFAULT_THEME.primary);
266263

267-
const custom: ThemeTokens = { ...DEFAULT_THEME, accent: "#ff0000" };
264+
const custom: ThemeTokens = { ...DEFAULT_THEME, primary: "#ff0000" };
268265
setCurrentTheme(custom);
269-
assert.equal(getCurrentThemeTokens().accent, "#ff0000");
266+
assert.equal(getCurrentThemeTokens().primary, "#ff0000");
270267

271268
setCurrentTheme(DEFAULT_THEME);
272269
});
@@ -278,37 +275,37 @@ test("setCurrentTheme changes getCurrentThemeTokens output", () => {
278275
test("resolveSettingsSources includes theme field in resolved settings", () => {
279276
const result = resolveSettingsSources(null, null, DEFAULTS, {});
280277
assert.ok("theme" in result);
281-
assert.equal(result.theme.accent, DEFAULT_THEME.accent);
278+
assert.equal(result.theme.primary, DEFAULT_THEME.primary);
282279
});
283280

284281
test("resolveSettingsSources resolves custom theme from user settings", () => {
285282
const result = resolveSettingsSources(
286-
{ theme: { preset: "custom", overrides: { accent: "#abcdef" } } },
283+
{ theme: { preset: "custom", overrides: { primary: "#abcdef" } } },
287284
null,
288285
DEFAULTS,
289286
{}
290287
);
291-
assert.equal(result.theme.accent, "#abcdef");
288+
assert.equal(result.theme.primary, "#abcdef");
292289
});
293290

294291
test("resolveSettingsSources resolves custom theme from project settings", () => {
295292
const result = resolveSettingsSources(
296293
null,
297-
{ theme: { preset: "custom", overrides: { accent: "#123456" } } },
294+
{ theme: { preset: "custom", overrides: { primary: "#123456" } } },
298295
DEFAULTS,
299296
{}
300297
);
301-
assert.equal(result.theme.accent, "#123456");
298+
assert.equal(result.theme.primary, "#123456");
302299
});
303300

304301
test("resolveSettingsSources uses default theme when preset is not custom", () => {
305302
const result = resolveSettingsSources(
306-
{ theme: { preset: "default", overrides: { accent: "#abcdef" } } },
303+
{ theme: { preset: "default", overrides: { primary: "#abcdef" } } },
307304
null,
308305
DEFAULTS,
309306
{}
310307
);
311-
assert.equal(result.theme.accent, DEFAULT_THEME.accent);
308+
assert.equal(result.theme.primary, DEFAULT_THEME.primary);
312309
});
313310

314311
// ---------------------------------------------------------------------------

src/ui/components/DropdownMenu/index.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ const DropdownMenu = React.memo(function DropdownMenu({
7272
renderItem,
7373
}: DropdownMenuProps): React.ReactElement | null {
7474
const theme = useTheme();
75-
const effectiveTitleColor = titleColor ?? theme.accent;
76-
const effectiveActiveColor = activeColor ?? theme.active;
75+
const effectiveTitleColor = titleColor ?? theme.primary;
76+
const effectiveActiveColor = activeColor ?? theme.primary;
7777
// Calculate visible window
7878
const visibleStart = calculateVisibleStart(activeIndex, items?.length, maxVisible);
7979
const visibleItems = items?.slice(visibleStart, visibleStart + maxVisible);
@@ -117,18 +117,20 @@ const DropdownMenu = React.memo(function DropdownMenu({
117117
}
118118

119119
return (
120-
<Box flexDirection="column" marginBottom={1} borderStyle={"round"} borderDimColor width={width}>
120+
<Box
121+
flexDirection="column"
122+
marginBottom={1}
123+
borderStyle={"round"}
124+
borderBottom={true}
125+
borderTop={true}
126+
borderLeft={false}
127+
borderRight={false}
128+
borderColor={theme.border}
129+
width={width}
130+
>
121131
{/* Title */}
122132
{title ? (
123-
<Box
124-
borderStyle={"single"}
125-
borderDimColor
126-
borderBottom={true}
127-
borderRight={false}
128-
borderTop={false}
129-
borderLeft={false}
130-
paddingX={1}
131-
>
133+
<Box paddingX={1} marginBottom={1}>
132134
<Text color={effectiveTitleColor} bold>
133135
{title}
134136
</Text>
@@ -159,7 +161,7 @@ const DropdownMenu = React.memo(function DropdownMenu({
159161
<Box width={labelColumnWidth} flexShrink={0}>
160162
<Text color={isActive ? effectiveActiveColor : undefined} wrap="truncate-end">
161163
{isActive ? "> " : " "}
162-
{item.selected !== undefined ? (item.selected ? "●" : "○") : null} <Text bold>{item.label}</Text>
164+
{item.selected !== undefined ? (item.selected ? "●" : "○") : null} <Text>{item.label}</Text>
163165
{item.statusIndicator ? (
164166
<Text color={item.statusIndicator.color}> {item.statusIndicator.symbol}</Text>
165167
) : null}
@@ -180,15 +182,7 @@ const DropdownMenu = React.memo(function DropdownMenu({
180182

181183
{/* Help text */}
182184
{helpText ? (
183-
<Box
184-
borderStyle={"single"}
185-
borderDimColor
186-
borderBottom={false}
187-
borderRight={false}
188-
borderTop={true}
189-
borderLeft={false}
190-
paddingX={1}
191-
>
185+
<Box paddingX={1} marginLeft={2} marginTop={1}>
192186
<Text dimColor>{helpText}</Text>
193187
</Box>
194188
) : null}

src/ui/components/FileMentionMenu/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ const FileMentionMenu: React.FC<Props> = ({ open, width, token, items, onClose,
9898
maxVisible={8}
9999
renderItem={(item, isActive) => (
100100
<Box flexDirection="row" paddingX={1} gap={1}>
101-
<Text color={isActive ? theme.active : undefined}>{isActive ? "> " : " "}</Text>
101+
<Text color={isActive ? theme.primary : undefined}>{isActive ? "> " : " "}</Text>
102102
<Box flexGrow={1}>
103-
<Text color={isActive ? theme.active : undefined} wrap="truncate-end" bold={isActive}>
103+
<Text color={isActive ? theme.primary : undefined} wrap="truncate-end" bold={isActive}>
104104
{item.label}
105105
</Text>
106106
</Box>

src/ui/components/MessageView/index.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ export function MessageView({ message, collapsed, width = 80 }: MessageViewProps
2525
return (
2626
<Box marginLeft={1} marginBottom={1} flexDirection="row" marginY={0} flexGrow={1} gap={1}>
2727
<Box>
28-
<Text color={theme.accent}>{`>`}</Text>
28+
<Text color={theme.primary}>{`>`}</Text>
2929
</Box>
3030
<Box flexGrow={1}>
31-
<Text color={theme.accent}>{text}</Text>
31+
<Text color={theme.primary}>{text}</Text>
3232
{Array.isArray(message.contentParams) && message.contentParams.length > 0 ? (
3333
<Text color={theme.info}>{` 📎 ${message.contentParams.length} image attachment(s)`}</Text>
3434
) : null}
@@ -66,7 +66,7 @@ export function MessageView({ message, collapsed, width = 80 }: MessageViewProps
6666
return (
6767
<Box marginLeft={1} marginBottom={1} width={containerWidth} gap={1} marginY={0} flexDirection="row">
6868
<Box alignSelf="stretch">
69-
<Text color={theme.accent}></Text>
69+
<Text color={theme.primary}></Text>
7070
</Box>
7171
<Box flexGrow={1} width={contentWidth} flexDirection="column">
7272
{content
@@ -114,10 +114,10 @@ export function MessageView({ message, collapsed, width = 80 }: MessageViewProps
114114
return (
115115
<Box marginY={0} marginLeft={1} marginBottom={1} flexGrow={1} flexDirection="row" gap={1}>
116116
<Box>
117-
<Text color={theme.accent}>{`>`}</Text>
117+
<Text color={theme.primary}>{`>`}</Text>
118118
</Box>
119119
<Box flexGrow={1} flexDirection="column">
120-
<Text color={theme.accent}>{message.content}</Text>
120+
<Text color={theme.primary}>{message.content}</Text>
121121
</Box>
122122
</Box>
123123
);

0 commit comments

Comments
 (0)