From ad80e903e3004c3f97c3a749fb3848175ebbd185 Mon Sep 17 00:00:00 2001 From: arookieofc <2128194521@qq.com> Date: Sun, 1 Nov 2026 11:38:26 +0800 Subject: [PATCH] feat(settings): add "Show tool window badges" (#637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Counts on the tool window stripe icons (changed files on Commit, problems, agent tasks needing input, running processes) can be turned off in Settings › Appearance & Behavior › Appearance › Tool Windows. On by default; the setting is stored in appearance settings and survives restarts. Co-authored-by: a-rookie-of-C-language <186315048+a-rookie-of-C-language@users.noreply.github.com> --- crates/ide-core/src/settings.rs | 17 ++++++++ src/bindings.ts | 1 + .../settings/__tests__/behavior.test.ts | 6 +++ src/features/settings/model/behaviorSchema.ts | 3 ++ src/features/settings/pages/behavior.tsx | 5 +++ src/platform/locales/text/zh.json | 2 + src/workbench/toolwindows/ToolWindows.tsx | 4 +- src/workbench/toolwindows/badges.test.tsx | 43 +++++++++++++++++++ 8 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/workbench/toolwindows/badges.test.tsx diff --git a/crates/ide-core/src/settings.rs b/crates/ide-core/src/settings.rs index 5900f7e80..f9390f0da 100644 --- a/crates/ide-core/src/settings.rs +++ b/crates/ide-core/src/settings.rs @@ -292,6 +292,7 @@ pub struct AppearanceSettings { pub editor_antialiasing: String, pub show_tool_window_bars: bool, pub show_tool_window_names: bool, + pub show_tool_window_badges: bool, pub widescreen_tool_windows: bool, /// Status bar widgets shown (`true`) or hidden (`false`) by the user /// (status bar context menu); widgets not listed use their default. @@ -338,6 +339,7 @@ impl Default for AppearanceSettings { editor_antialiasing: "greyscale".into(), show_tool_window_bars: true, show_tool_window_names: false, + show_tool_window_badges: true, widescreen_tool_windows: false, status_bar_widgets: BTreeMap::new(), main_toolbar: None, @@ -719,6 +721,21 @@ impl Settings { mod tests { use super::*; + #[test] + fn tool_window_badges_default_on_and_round_trip() { + let old: Settings = serde_json::from_str(r#"{"appearance":{"showToolWindowNames":true}}"#).unwrap(); + assert!(old.appearance.show_tool_window_badges); + assert!(Settings::default().appearance.show_tool_window_badges); + for enabled in [false, true] { + let mut settings = Settings::default(); + settings.appearance.show_tool_window_badges = enabled; + let json = serde_json::to_value(&settings).unwrap(); + assert_eq!(json["appearance"]["showToolWindowBadges"], enabled); + let restored: Settings = serde_json::from_value(json).unwrap(); + assert_eq!(restored.appearance.show_tool_window_badges, enabled); + } + } + #[test] fn main_menu_style_defaults_and_round_trips() { let old: Settings = serde_json::from_str(r#"{"appearance":{"compactMode":true}}"#).unwrap(); diff --git a/src/bindings.ts b/src/bindings.ts index a70080cf7..20ee0eba2 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -1434,6 +1434,7 @@ export type AppearanceSettings = { editorAntialiasing?: string, showToolWindowBars?: boolean, showToolWindowNames?: boolean, + showToolWindowBadges?: boolean, widescreenToolWindows?: boolean, /** * Status bar widgets shown (`true`) or hidden (`false`) by the user diff --git a/src/features/settings/__tests__/behavior.test.ts b/src/features/settings/__tests__/behavior.test.ts index de00873e7..3f2cff405 100644 --- a/src/features/settings/__tests__/behavior.test.ts +++ b/src/features/settings/__tests__/behavior.test.ts @@ -21,6 +21,12 @@ describe("appearance & behavior settings", () => { expect(validateAppearanceExtras({ theme: "system" }).syncWithOs).toBe(true); }); + it("defaults Show tool window badges to on and honours the stored value", () => { + expect(validateAppearanceExtras({}).showToolWindowBadges).toBe(true); + expect(validateAppearanceExtras({ showToolWindowBadges: false }).showToolWindowBadges).toBe(false); + expect(validateAppearanceExtras({ showToolWindowBadges: 1 }).showToolWindowBadges).toBe(true); + }); + it("formats dates with an override pattern and pretty dates", () => { const d = new Date(2026, 0, 5, 14, 7); expect(formatPattern(d, "dd.MM.yyyy")).toBe("05.01.2026"); diff --git a/src/features/settings/model/behaviorSchema.ts b/src/features/settings/model/behaviorSchema.ts index 6ee7b326f..37fadb745 100644 --- a/src/features/settings/model/behaviorSchema.ts +++ b/src/features/settings/model/behaviorSchema.ts @@ -33,6 +33,7 @@ export interface AppearanceExtras { editorAntialiasing: Antialiasing; showToolWindowBars: boolean; showToolWindowNames: boolean; + showToolWindowBadges: boolean; widescreenToolWindows: boolean; } @@ -84,6 +85,7 @@ export const DEFAULT_APPEARANCE_EXTRAS: AppearanceExtras = { editorAntialiasing: "greyscale", showToolWindowBars: true, showToolWindowNames: false, + showToolWindowBadges: true, widescreenToolWindows: false, }; @@ -134,6 +136,7 @@ export function validateAppearanceExtras(raw: unknown): AppearanceExtras { editorAntialiasing: oneOf(a.editorAntialiasing, AA, d.editorAntialiasing), showToolWindowBars: b("showToolWindowBars"), showToolWindowNames: b("showToolWindowNames"), + showToolWindowBadges: b("showToolWindowBadges"), widescreenToolWindows: b("widescreenToolWindows"), }; } diff --git a/src/features/settings/pages/behavior.tsx b/src/features/settings/pages/behavior.tsx index ed4f12ae9..f2a1732a0 100644 --- a/src/features/settings/pages/behavior.tsx +++ b/src/features/settings/pages/behavior.tsx @@ -284,6 +284,11 @@ function ToolWindowsGrid() { <> + } right={ diff --git a/src/platform/locales/text/zh.json b/src/platform/locales/text/zh.json index daff493a2..f66b845de 100644 --- a/src/platform/locales/text/zh.json +++ b/src/platform/locales/text/zh.json @@ -1985,6 +1985,8 @@ "Show the parameter info popup in": "在以下时间后显示形参信息弹出窗口", "Show the selected coverage suite in editors and the project view": "在编辑器和项目视图中显示所选覆盖率套件", "Show tool window bars": "显示工具窗口栏", + "Show tool window badges": "显示工具窗口角标", + "Counts on the stripe icons, e.g. changed files or agent tasks needing input": "侧栏图标上的数字,如变更文件数、等待输入的智能体任务数", "Show tool window names": "显示工具窗口名称", "Show value tooltip on code selection": "选中代码时显示值提示", "Show value tooltip on mouse hover": "鼠标悬停时显示值工具提示", diff --git a/src/workbench/toolwindows/ToolWindows.tsx b/src/workbench/toolwindows/ToolWindows.tsx index 3d874c903..d957e2a7d 100644 --- a/src/workbench/toolwindows/ToolWindows.tsx +++ b/src/workbench/toolwindows/ToolWindows.tsx @@ -359,8 +359,10 @@ function startStripeDrag(e: PointerEvent, id: string) { } function StripeBadge({ useBadge }: { useBadge: () => number | string | null }) { + // Settings › Appearance › Tool Windows › "Show tool window badges". + const showBadges = useProject((s) => s.settings?.appearance?.showToolWindowBadges) !== false; const value = useBadge(); - if (value === null || value === 0 || value === "") return null; + if (!showBadges || value === null || value === 0 || value === "") return null; return {typeof value === "number" && value > 99 ? "99+" : value}; } diff --git a/src/workbench/toolwindows/badges.test.tsx b/src/workbench/toolwindows/badges.test.tsx new file mode 100644 index 000000000..1a6741e63 --- /dev/null +++ b/src/workbench/toolwindows/badges.test.tsx @@ -0,0 +1,43 @@ +import { act, render, screen } from "@testing-library/react"; +import { afterEach, beforeEach, expect, it } from "vitest"; +import { create } from "zustand"; +import { useProject } from "@/platform/project"; +import { ToolWindowStripe } from "./ToolWindows"; +import { resetToolWindowLayout, useToolWindowLayout } from "./store"; + +beforeEach(() => { + resetToolWindowLayout(); + useProject.setState({ settings: null }); +}); + +afterEach(() => useProject.setState({ settings: null })); + +it("hides stripe counts immediately and restores the current count when enabled", () => { + const useCount = create(() => ({ count: 3 })); + render( + null, + useBadge: () => useCount((s) => s.count), + }, + ]} + />, + ); + const button = screen.getByRole("button", { name: "Git" }); + expect(button).toHaveTextContent("3"); + act(() => useProject.setState({ settings: { appearance: { showToolWindowBadges: false } } })); + expect(button).not.toHaveTextContent("3"); + act(() => useCount.setState({ count: 7 })); + expect(button).not.toHaveTextContent("7"); + act(() => useProject.setState({ settings: { appearance: { showToolWindowBadges: true } } })); + expect(button).toHaveTextContent("7"); + act(() => useCount.setState({ count: 0 })); + expect(button).not.toHaveTextContent("0"); +});