feat(settings): add "Show tool window badges" (#637)

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 <[email protected]>
Цей коміт міститься в:
arookieofcтаa-rookie-of-C-language authored and GitHub committed 2026-11-01 11:38:26 +08:00
1 джерело 9847fd2c01
коміт ad80e903e3
8 files changed
+80 -1

No files matched your search

+17
Переглянути файл
@@ -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();
+1
Переглянути файл
@@ -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
+6
Переглянути файл
@@ -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");
+3
Переглянути файл
@@ -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"),
};
}
+5
Переглянути файл
@@ -284,6 +284,11 @@ function ToolWindowsGrid() {
<>
<BoundToggle k="appearance.showToolWindowBars" label="Show tool window bars" />
<BoundToggle k="appearance.showToolWindowNames" label="Show tool window names" />
<BoundToggle
k="appearance.showToolWindowBadges"
label="Show tool window badges"
hint="Counts on the stripe icons, e.g. changed files or agent tasks needing input"
/>
</>
}
right={
+2
Переглянути файл
@@ -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": "鼠标悬停时显示值工具提示",
+3 -1
Переглянути файл
@@ -359,8 +359,10 @@ function startStripeDrag(e: PointerEvent<HTMLButtonElement>, 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 <span className={styles.badge}>{typeof value === "number" && value > 99 ? "99+" : value}</span>;
}
+43
Переглянути файл
@@ -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(
<ToolWindowStripe
side="left"
visible={useToolWindowLayout.getState().visible}
windows={[
{
id: "git",
title: "Git",
icon: "folder",
anchor: "left",
component: () => 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");
});