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");
+});