Skip to content

Commit 27bef6a

Browse files
committed
fix(web): scope themed icons by UI context
1 parent f00ea9c commit 27bef6a

11 files changed

Lines changed: 283 additions & 60 deletions

File tree

packages/web/src/features/workspace/index.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ describe("WorkspacePage", () => {
247247
".workspace-status-bar .git-panel-status-strip__branch"
248248
);
249249
expect(branchButton).not.toBeNull();
250-
expect(branchButton?.querySelector('[data-icon-semantic="git.branch"]')).toBeTruthy();
250+
expect(branchButton?.querySelector('[data-icon-semantic="git.footer.branch"]')).toBeTruthy();
251251
fireEvent.click(branchButton as HTMLElement);
252252

253253
const gitTab = screen.getByRole("tab", { name: "Git" });

packages/web/src/features/workspace/views/mobile/mobile-dock.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function MobileDock({ activeItem, onSelectItem }: MobileDockProps) {
1818
aria-label={t("mobile.dock.open_agent")}
1919
>
2020
<span className="mobile-dock__icon" aria-hidden="true">
21-
<ThemedIcon semantic="nav.agent" size={18} />
21+
<ThemedIcon semantic="mobile.dock.agent" size={18} />
2222
</span>
2323
<span className="mobile-dock__label">{t("label.agent")}</span>
2424
</button>
@@ -30,7 +30,7 @@ export function MobileDock({ activeItem, onSelectItem }: MobileDockProps) {
3030
aria-label={t("mobile.dock.open_files")}
3131
>
3232
<span className="mobile-dock__icon" aria-hidden="true">
33-
<ThemedIcon semantic="nav.panelFiles" size={18} />
33+
<ThemedIcon semantic="mobile.dock.files" size={18} />
3434
</span>
3535
<span className="mobile-dock__label">{t("file.title")}</span>
3636
</button>
@@ -42,7 +42,7 @@ export function MobileDock({ activeItem, onSelectItem }: MobileDockProps) {
4242
aria-label={t("mobile.dock.open_terminal")}
4343
>
4444
<span className="mobile-dock__icon" aria-hidden="true">
45-
<ThemedIcon semantic="nav.panelTerminal" size={18} />
45+
<ThemedIcon semantic="mobile.dock.terminal" size={18} />
4646
</span>
4747
<span className="mobile-dock__label">{t("label.terminal")}</span>
4848
</button>

packages/web/src/features/workspace/views/shared/git-panel-status-strip.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ describe("GitPanelStatusStrip", () => {
191191
);
192192

193193
expect(
194-
container.querySelector('.git-panel-status-strip__branch [data-icon-semantic="git.branch"]')
194+
container.querySelector(
195+
'.git-panel-status-strip__branch [data-icon-semantic="git.footer.branch"]'
196+
)
195197
).toBeTruthy();
196198
});
197199
});

packages/web/src/features/workspace/views/shared/git-panel-status-strip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function BranchTrigger({
4949
aria-label={ariaLabel}
5050
disabled={disabled}
5151
>
52-
<ThemedIcon semantic="git.branch" size={11} />
52+
<ThemedIcon semantic="git.footer.branch" size={11} />
5353
<span className="git-panel-status-strip__branch-text">{branchName}</span>
5454
</button>
5555
</Tooltip>

packages/web/src/features/workspace/views/shared/git-status-bar.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ describe("GitStatusBar", () => {
5151

5252
const toolbar = screen.getByRole("button", { name: "Fetch" }).closest(".git-status-bar");
5353
expect(toolbar).not.toBeNull();
54-
expect(container.querySelector('[data-icon-semantic="git.action.diff"]')).toBeTruthy();
55-
expect(container.querySelector('[data-icon-semantic="git.action.push"]')).toBeTruthy();
56-
expect(container.querySelector('[data-icon-semantic="git.action.pull"]')).toBeTruthy();
57-
expect(container.querySelector('[data-icon-semantic="git.action.refresh"]')).toBeTruthy();
54+
expect(container.querySelector('[data-icon-semantic="git.footer.diff"]')).toBeTruthy();
55+
expect(container.querySelector('[data-icon-semantic="git.footer.push"]')).toBeTruthy();
56+
expect(container.querySelector('[data-icon-semantic="git.footer.pull"]')).toBeTruthy();
57+
expect(container.querySelector('[data-icon-semantic="git.footer.refresh"]')).toBeTruthy();
5858

5959
expect(screen.getByRole("button", { name: "Fetch" })).toHaveClass(
6060
"btn",

packages/web/src/features/workspace/views/shared/git-status-bar.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ export const GitStatusBar: FC<GitStatusBarProps> = ({
196196
<>
197197
<div className={`git-status-bar${inline ? " git-status-bar--inline" : ""}`}>
198198
<Tooltip content={t("git.statusbar.changes")}>
199-
<span className="git-status-bar__item">
200-
<ThemedIcon semantic="git.action.diff" size={13} />
199+
<span className="git-status-bar__item git-status-bar__item--changes">
200+
<ThemedIcon semantic="git.footer.diff" size={13} />
201201
<span className="git-status-bar__value">{changeCount}</span>
202202
</span>
203203
</Tooltip>
@@ -209,7 +209,7 @@ export const GitStatusBar: FC<GitStatusBarProps> = ({
209209
disabled={ahead <= 0}
210210
onClick={() => openConfirm("push", ahead)}
211211
>
212-
<ThemedIcon semantic="git.action.push" size={13} />
212+
<ThemedIcon semantic="git.footer.push" size={13} />
213213
<span className="git-status-bar__value">{ahead}</span>
214214
</button>
215215
</Tooltip>
@@ -221,7 +221,7 @@ export const GitStatusBar: FC<GitStatusBarProps> = ({
221221
disabled={behind <= 0}
222222
onClick={() => openConfirm("pull", behind)}
223223
>
224-
<ThemedIcon semantic="git.action.pull" size={13} />
224+
<ThemedIcon semantic="git.footer.pull" size={13} />
225225
<span className="git-status-bar__value">{behind}</span>
226226
</button>
227227
</Tooltip>
@@ -232,7 +232,7 @@ export const GitStatusBar: FC<GitStatusBarProps> = ({
232232
disabled={isFetching}
233233
icon={
234234
<ThemedIcon
235-
semantic="git.action.refresh"
235+
semantic="git.footer.refresh"
236236
size={13}
237237
className={isFetching ? "spin" : undefined}
238238
/>

packages/web/src/shells/mobile-shell/index.test.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,17 @@ describe("MobileShell Phase 2 workspace", () => {
608608

609609
expect(screen.getByRole("button", { name: "Switch workspace" })).toBeInTheDocument();
610610
expect(screen.queryByRole("button", { name: "Switch active agent" })).not.toBeInTheDocument();
611-
expect(screen.getByRole("button", { name: "Open Agent sheet" })).toBeInTheDocument();
612-
expect(screen.getByRole("button", { name: "Open Files sheet" })).toBeInTheDocument();
613-
expect(screen.getByRole("button", { name: "Open Terminal sheet" })).toBeInTheDocument();
611+
const agentButton = screen.getByRole("button", { name: "Open Agent sheet" });
612+
const filesButton = screen.getByRole("button", { name: "Open Files sheet" });
613+
const terminalButton = screen.getByRole("button", { name: "Open Terminal sheet" });
614+
expect(agentButton).toBeInTheDocument();
615+
expect(filesButton).toBeInTheDocument();
616+
expect(terminalButton).toBeInTheDocument();
617+
expect(agentButton.querySelector('[data-icon-semantic="mobile.dock.agent"]')).toBeTruthy();
618+
expect(filesButton.querySelector('[data-icon-semantic="mobile.dock.files"]')).toBeTruthy();
619+
expect(
620+
terminalButton.querySelector('[data-icon-semantic="mobile.dock.terminal"]')
621+
).toBeTruthy();
614622
expect(screen.queryByRole("tablist", { name: "Mobile agents" })).not.toBeInTheDocument();
615623
expect(screen.queryByText("已连接")).not.toBeInTheDocument();
616624
expect(screen.getByTestId("mobile-session-card")).toHaveTextContent("sess_2");

packages/web/src/styles/components.css

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10198,6 +10198,10 @@ textarea.input {
1019810198
white-space: nowrap;
1019910199
}
1020010200

10201+
.git-status-bar__item--changes {
10202+
color: var(--icon-warning);
10203+
}
10204+
1020110205
.git-status-bar__value {
1020210206
font-variant-numeric: tabular-nums;
1020310207
}
@@ -10228,11 +10232,11 @@ textarea.input {
1022810232
}
1022910233

1023010234
.git-status-bar__item--ahead {
10231-
color: var(--accent-blue);
10235+
color: var(--icon-success);
1023210236
}
1023310237

1023410238
.git-status-bar__item--behind {
10235-
color: var(--orange, #e5a50a);
10239+
color: var(--icon-info);
1023610240
}
1023710241

1023810242
.git-status-bar__confirm {

packages/web/src/theme/icon-theme.test.ts

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,58 @@ describe("theme icon resolver", () => {
5050
expect(new Set([diffIcon, pushIcon, pullIcon, refreshIcon]).size).toBe(4);
5151
});
5252

53+
it("gives footer git status semantics a stable visual hierarchy", () => {
54+
for (const themeId of [
55+
"mint-dark",
56+
"mint-light",
57+
"graphite-dark",
58+
"graphite-light",
59+
"nord-dark",
60+
"nord-light",
61+
"hc-dark",
62+
"hc-light",
63+
] as const) {
64+
expect(getIconPresentation(themeId, "git.footer.branch")).toEqual(
65+
expect.objectContaining({ tone: "current" })
66+
);
67+
expect(getIconPresentation(themeId, "git.footer.diff")).toEqual(
68+
expect.objectContaining({ tone: "warning" })
69+
);
70+
expect(getIconPresentation(themeId, "git.footer.push")).toEqual(
71+
expect.objectContaining({ tone: "success" })
72+
);
73+
expect(getIconPresentation(themeId, "git.footer.pull")).toEqual(
74+
expect.objectContaining({ tone: "info" })
75+
);
76+
expect(getIconPresentation(themeId, "git.footer.refresh")).toEqual(
77+
expect.objectContaining({ tone: "secondary" })
78+
);
79+
}
80+
});
81+
5382
it("applies richer common icon tones for mint themes", () => {
5483
for (const themeId of ["mint-dark", "mint-light"] as const) {
5584
expect(getIconPresentation(themeId, "nav.agent")).toEqual(
56-
expect.objectContaining({ tone: "accent" })
85+
expect.objectContaining({ tone: "current" })
5786
);
5887
expect(getIconPresentation(themeId, "agent.provider.codex")).toEqual(
5988
expect.objectContaining({ tone: "accent" })
6089
);
61-
expect(getIconPresentation(themeId, "nav.panelTerminal")).toEqual(
90+
expect(getIconPresentation(themeId, "mobile.dock.agent")).toEqual(
91+
expect.objectContaining({ tone: "accent" })
92+
);
93+
expect(getIconPresentation(themeId, "mobile.dock.files")).toEqual(
6294
expect.objectContaining({ tone: "info" })
6395
);
96+
expect(getIconPresentation(themeId, "mobile.dock.terminal")).toEqual(
97+
expect.objectContaining({ tone: "info" })
98+
);
99+
expect(getIconPresentation(themeId, "nav.panelTerminal")).toEqual(
100+
expect.objectContaining({ tone: "current" })
101+
);
102+
expect(getIconPresentation(themeId, "nav.panelFiles")).toEqual(
103+
expect.objectContaining({ tone: "current" })
104+
);
64105
expect(getIconPresentation(themeId, "terminal.action.new")).toEqual(
65106
expect.objectContaining({ tone: "info" })
66107
);
@@ -70,6 +111,15 @@ describe("theme icon resolver", () => {
70111
expect(getIconPresentation(themeId, "git.action.diff")).toEqual(
71112
expect.objectContaining({ tone: "info" })
72113
);
114+
expect(getIconPresentation(themeId, "git.action.push")).toEqual(
115+
expect.objectContaining({ tone: "info" })
116+
);
117+
expect(getIconPresentation(themeId, "git.action.pull")).toEqual(
118+
expect.objectContaining({ tone: "info" })
119+
);
120+
expect(getIconPresentation(themeId, "git.action.refresh")).toEqual(
121+
expect.objectContaining({ tone: "info" })
122+
);
73123
expect(getIconPresentation(themeId, "git.commit")).toEqual(
74124
expect.objectContaining({ tone: "info" })
75125
);
@@ -82,23 +132,44 @@ describe("theme icon resolver", () => {
82132
it("keeps graphite themes restrained while highlighting key icons", () => {
83133
for (const themeId of ["graphite-dark", "graphite-light"] as const) {
84134
expect(getIconPresentation(themeId, "nav.agent")).toEqual(
85-
expect.objectContaining({ tone: "accent" })
135+
expect.objectContaining({ tone: "current" })
86136
);
87137
expect(getIconPresentation(themeId, "agent.provider.codex")).toEqual(
88138
expect.objectContaining({ tone: "accent" })
89139
);
140+
expect(getIconPresentation(themeId, "mobile.dock.agent")).toEqual(
141+
expect.objectContaining({ tone: "accent" })
142+
);
143+
expect(getIconPresentation(themeId, "mobile.dock.files")).toEqual(
144+
expect.objectContaining({ tone: "secondary" })
145+
);
146+
expect(getIconPresentation(themeId, "mobile.dock.terminal")).toEqual(
147+
expect.objectContaining({ tone: "secondary" })
148+
);
90149
expect(getIconPresentation(themeId, "git.branch")).toEqual(
91150
expect.objectContaining({ tone: "info" })
92151
);
93152
expect(getIconPresentation(themeId, "git.action.diff")).toEqual(
94153
expect.objectContaining({ tone: "info" })
95154
);
155+
expect(getIconPresentation(themeId, "git.action.push")).toEqual(
156+
expect.objectContaining({ tone: "info" })
157+
);
158+
expect(getIconPresentation(themeId, "git.action.pull")).toEqual(
159+
expect.objectContaining({ tone: "info" })
160+
);
161+
expect(getIconPresentation(themeId, "git.action.refresh")).toEqual(
162+
expect.objectContaining({ tone: "info" })
163+
);
96164
expect(getIconPresentation(themeId, "nav.settings.appearance")).toEqual(
97165
expect.objectContaining({ tone: "accent" })
98166
);
99167
expect(getIconPresentation(themeId, "nav.panelTerminal")).toEqual(
100168
expect.objectContaining({ tone: "current" })
101169
);
170+
expect(getIconPresentation(themeId, "nav.panelFiles")).toEqual(
171+
expect.objectContaining({ tone: "current" })
172+
);
102173
expect(getIconPresentation(themeId, "terminal.action.new")).toEqual(
103174
expect.objectContaining({ tone: "secondary" })
104175
);
@@ -111,14 +182,26 @@ describe("theme icon resolver", () => {
111182
it("gives nord themes a cooler accent hierarchy", () => {
112183
for (const themeId of ["nord-dark", "nord-light"] as const) {
113184
expect(getIconPresentation(themeId, "nav.agent")).toEqual(
114-
expect.objectContaining({ tone: "accent" })
185+
expect.objectContaining({ tone: "current" })
115186
);
116187
expect(getIconPresentation(themeId, "agent.provider.codex")).toEqual(
117188
expect.objectContaining({ tone: "accent" })
118189
);
119-
expect(getIconPresentation(themeId, "nav.panelTerminal")).toEqual(
190+
expect(getIconPresentation(themeId, "mobile.dock.agent")).toEqual(
191+
expect.objectContaining({ tone: "accent" })
192+
);
193+
expect(getIconPresentation(themeId, "mobile.dock.files")).toEqual(
120194
expect.objectContaining({ tone: "info" })
121195
);
196+
expect(getIconPresentation(themeId, "mobile.dock.terminal")).toEqual(
197+
expect.objectContaining({ tone: "info" })
198+
);
199+
expect(getIconPresentation(themeId, "nav.panelTerminal")).toEqual(
200+
expect.objectContaining({ tone: "current" })
201+
);
202+
expect(getIconPresentation(themeId, "nav.panelFiles")).toEqual(
203+
expect.objectContaining({ tone: "current" })
204+
);
122205
expect(getIconPresentation(themeId, "terminal.action.new")).toEqual(
123206
expect.objectContaining({ tone: "info" })
124207
);
@@ -128,6 +211,15 @@ describe("theme icon resolver", () => {
128211
expect(getIconPresentation(themeId, "git.action.diff")).toEqual(
129212
expect.objectContaining({ tone: "accent" })
130213
);
214+
expect(getIconPresentation(themeId, "git.action.push")).toEqual(
215+
expect.objectContaining({ tone: "accent" })
216+
);
217+
expect(getIconPresentation(themeId, "git.action.pull")).toEqual(
218+
expect.objectContaining({ tone: "accent" })
219+
);
220+
expect(getIconPresentation(themeId, "git.action.refresh")).toEqual(
221+
expect.objectContaining({ tone: "accent" })
222+
);
131223
expect(getIconPresentation(themeId, "git.commit")).toEqual(
132224
expect.objectContaining({ tone: "accent" })
133225
);
@@ -137,9 +229,6 @@ describe("theme icon resolver", () => {
137229
expect(getIconPresentation(themeId, "nav.settings.providers")).toEqual(
138230
expect.objectContaining({ tone: "info" })
139231
);
140-
expect(getIconPresentation(themeId, "git.action.pull")).toEqual(
141-
expect.objectContaining({ tone: "secondary" })
142-
);
143232
}
144233
});
145234
});

packages/web/src/theme/icon-theme.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,21 @@ export const ICON_SEMANTICS = [
6161
"git.branch",
6262
"git.branch.create",
6363
"git.commit",
64+
"git.footer.branch",
65+
"git.footer.diff",
66+
"git.footer.push",
67+
"git.footer.pull",
68+
"git.footer.refresh",
6469
"worktree.action.new",
6570
"nav.settings",
6671
"nav.search",
6772
"nav.newWorkspace",
6873
"nav.panelFiles",
6974
"nav.panelTerminal",
7075
"nav.agent",
76+
"mobile.dock.agent",
77+
"mobile.dock.files",
78+
"mobile.dock.terminal",
7179
"nav.settings.general",
7280
"nav.settings.providers",
7381
"nav.settings.appearance",
@@ -169,13 +177,21 @@ export const BASE_ICON_THEME: IconThemeDefinition = {
169177
"git.branch": { glyph: GitBranch, tone: "secondary" },
170178
"git.branch.create": { glyph: Plus, tone: "secondary" },
171179
"git.commit": { glyph: ArrowUpCircle, tone: "secondary" },
180+
"git.footer.branch": { glyph: GitBranch, tone: "current" },
181+
"git.footer.diff": { glyph: GitCompare, tone: "warning" },
182+
"git.footer.push": { glyph: Upload, tone: "success" },
183+
"git.footer.pull": { glyph: Download, tone: "info" },
184+
"git.footer.refresh": { glyph: RotateCcw, tone: "secondary" },
172185
"worktree.action.new": { glyph: Plus, tone: "secondary" },
173186
"nav.settings": { glyph: Settings, tone: "secondary" },
174187
"nav.search": { glyph: Search, tone: "secondary" },
175188
"nav.newWorkspace": { glyph: Plus, tone: "secondary" },
176189
"nav.panelFiles": { glyph: PanelLeft, tone: "current" },
177190
"nav.panelTerminal": { glyph: PanelBottom, tone: "current" },
178191
"nav.agent": { glyph: Bot, tone: "current" },
192+
"mobile.dock.agent": { glyph: Bot, tone: "current" },
193+
"mobile.dock.files": { glyph: PanelLeft, tone: "current" },
194+
"mobile.dock.terminal": { glyph: PanelBottom, tone: "current" },
179195
"nav.settings.general": { glyph: Settings, tone: "secondary" },
180196
"nav.settings.providers": { glyph: Globe, tone: "secondary" },
181197
"nav.settings.appearance": { glyph: Palette, tone: "secondary" },

0 commit comments

Comments
 (0)