diff --git a/src/core/config/__tests__/maxTasksHomeScreen.test.ts b/src/core/config/__tests__/maxTasksHomeScreen.test.ts new file mode 100644 index 0000000000..362e31a6a2 --- /dev/null +++ b/src/core/config/__tests__/maxTasksHomeScreen.test.ts @@ -0,0 +1,99 @@ +import { describe, it, expect, beforeEach, vi } from "vitest" +import * as vscode from "vscode" +import { ContextProxy } from "../ContextProxy" + +describe("maxTasksHomeScreen setting", () => { + let mockContext: vscode.ExtensionContext + let contextProxy: ContextProxy + + beforeEach(async () => { + // Create mock VSCode context + const mockGlobalState = new Map() + const mockSecrets = new Map() + + mockContext = { + globalState: { + get: vi.fn((key: string) => mockGlobalState.get(key)), + update: vi.fn(async (key: string, value: any) => { + mockGlobalState.set(key, value) + }), + keys: vi.fn(() => Array.from(mockGlobalState.keys())), + setKeysForSync: vi.fn(), + }, + secrets: { + get: vi.fn(async (key: string) => mockSecrets.get(key)), + store: vi.fn(async (key: string, value: string) => { + mockSecrets.set(key, value) + }), + delete: vi.fn(async (key: string) => { + mockSecrets.delete(key) + }), + onDidChange: vi.fn(), + }, + extensionUri: {} as vscode.Uri, + extensionPath: "/test/path", + globalStorageUri: {} as vscode.Uri, + logUri: {} as vscode.Uri, + extension: {} as vscode.Extension, + extensionMode: 3, // vscode.ExtensionMode.Test + } as unknown as vscode.ExtensionContext + + contextProxy = new ContextProxy(mockContext) + await contextProxy.initialize() + }) + + it("should save maxTasksHomeScreen value", async () => { + // Set the value + await contextProxy.setValue("maxTasksHomeScreen", 10) + + // Verify it was saved + expect(mockContext.globalState.update).toHaveBeenCalledWith("maxTasksHomeScreen", 10) + }) + + it("should retrieve maxTasksHomeScreen value", async () => { + // Set the value + await contextProxy.setValue("maxTasksHomeScreen", 15) + + // Get the value + const value = contextProxy.getValue("maxTasksHomeScreen") + + // Verify it matches + expect(value).toBe(15) + }) + + it("should persist maxTasksHomeScreen across initialization", async () => { + // Set the value + await contextProxy.setValue("maxTasksHomeScreen", 8) + + // Create a new instance (simulating restart) + const newContextProxy = new ContextProxy(mockContext) + await newContextProxy.initialize() + + // Get the value from the new instance + const value = newContextProxy.getValue("maxTasksHomeScreen") + + // Verify it was persisted + expect(value).toBe(8) + }) + + it("should handle default value of 4", async () => { + // Don't set any value, should use default + const values = contextProxy.getValues() + + // maxTasksHomeScreen should be undefined or 4 (depending on implementation) + expect(values.maxTasksHomeScreen === undefined || values.maxTasksHomeScreen === 4).toBe(true) + }) + + it("should validate min/max bounds", async () => { + // The schema should enforce min=0, max=20 + // Try setting valid values + await contextProxy.setValue("maxTasksHomeScreen", 0) + expect(contextProxy.getValue("maxTasksHomeScreen")).toBe(0) + + await contextProxy.setValue("maxTasksHomeScreen", 20) + expect(contextProxy.getValue("maxTasksHomeScreen")).toBe(20) + + await contextProxy.setValue("maxTasksHomeScreen", 10) + expect(contextProxy.getValue("maxTasksHomeScreen")).toBe(10) + }) +}) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 828c7da99c..82ea1070e2 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1916,6 +1916,7 @@ export class ClineProvider includeDiagnosticMessages, maxDiagnosticMessages, includeTaskHistoryInEnhance, + maxTasksHomeScreen, includeCurrentTime, includeCurrentCost, taskSyncEnabled, @@ -2080,6 +2081,7 @@ export class ClineProvider includeDiagnosticMessages: includeDiagnosticMessages ?? true, maxDiagnosticMessages: maxDiagnosticMessages ?? 50, includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true, + maxTasksHomeScreen: maxTasksHomeScreen ?? 4, includeCurrentTime: includeCurrentTime ?? true, includeCurrentCost: includeCurrentCost ?? true, taskSyncEnabled, @@ -2295,6 +2297,7 @@ export class ClineProvider includeDiagnosticMessages: stateValues.includeDiagnosticMessages ?? true, maxDiagnosticMessages: stateValues.maxDiagnosticMessages ?? 50, includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? true, + maxTasksHomeScreen: stateValues.maxTasksHomeScreen ?? 4, includeCurrentTime: stateValues.includeCurrentTime ?? true, includeCurrentCost: stateValues.includeCurrentCost ?? true, taskSyncEnabled, diff --git a/src/core/webview/__tests__/maxTasksHomeScreen.integration.spec.ts b/src/core/webview/__tests__/maxTasksHomeScreen.integration.spec.ts new file mode 100644 index 0000000000..bebc65d60b --- /dev/null +++ b/src/core/webview/__tests__/maxTasksHomeScreen.integration.spec.ts @@ -0,0 +1,28 @@ +import { describe, it, expect } from "vitest" +import type { ExtensionState } from "../../../shared/ExtensionMessage" + +/** + * Integration test for maxTasksHomeScreen setting + * This test verifies that the setting is properly typed in ExtensionState + */ +describe("maxTasksHomeScreen integration", () => { + it("should be a valid ExtensionState property", () => { + // Type-level test: This will fail to compile if maxTasksHomeScreen is not in ExtensionState + const state: Partial = { + maxTasksHomeScreen: 10, + } + + expect(state.maxTasksHomeScreen).toBe(10) + }) + + it("should accept valid range values", () => { + const validValues = [0, 4, 10, 15, 20] + + validValues.forEach((value) => { + const state: Partial = { + maxTasksHomeScreen: value, + } + expect(state.maxTasksHomeScreen).toBe(value) + }) + }) +}) diff --git a/webview-ui/src/components/settings/UISettings.tsx b/webview-ui/src/components/settings/UISettings.tsx index de7d029196..f3df82568e 100644 --- a/webview-ui/src/components/settings/UISettings.tsx +++ b/webview-ui/src/components/settings/UISettings.tsx @@ -1,9 +1,11 @@ import { HTMLAttributes } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" -import { VSCodeCheckbox, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" import { Glasses } from "lucide-react" import { telemetryClient } from "@/utils/TelemetryClient" +import { Slider } from "@/components/ui" + import { SetCachedStateField } from "./types" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" @@ -32,16 +34,13 @@ export const UISettings = ({ }) } - const handleMaxTasksHomeScreenChange = (value: string) => { - const numValue = parseInt(value, 10) - if (!isNaN(numValue) && numValue >= 0 && numValue <= 20) { - setCachedStateField("maxTasksHomeScreen", numValue) + const handleMaxTasksHomeScreenChange = (value: number) => { + setCachedStateField("maxTasksHomeScreen", value) - // Track telemetry event - telemetryClient.capture("ui_settings_max_tasks_home_screen_changed", { - value: numValue, - }) - } + // Track telemetry event + telemetryClient.capture("ui_settings_max_tasks_home_screen_changed", { + value: value, + }) } return ( @@ -70,18 +69,17 @@ export const UISettings = ({ {/* Maximum Tasks in Home Screen Setting */}
- - handleMaxTasksHomeScreenChange(e.target.value)} - data-testid="max-tasks-home-screen-input" - className="w-32" - /> -
- {t("settings:ui.maxTasksHomeScreen.description")} + +
+ handleMaxTasksHomeScreenChange(value)} + data-testid="max-tasks-home-screen-slider" + /> + {maxTasksHomeScreen}
diff --git a/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx b/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx index cb8d5b04f1..a13b1b02ce 100644 --- a/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx @@ -15,10 +15,10 @@ describe("UISettings", () => { expect(checkbox).toBeTruthy() }) - it("renders the max tasks home screen input", () => { + it("renders the max tasks home screen slider", () => { const { getByTestId } = render() - const input = getByTestId("max-tasks-home-screen-input") - expect(input).toBeTruthy() + const slider = getByTestId("max-tasks-home-screen-slider") + expect(slider).toBeTruthy() }) it("displays the correct initial state for collapse thinking", () => { @@ -29,8 +29,9 @@ describe("UISettings", () => { it("displays the correct initial value for max tasks", () => { const { getByTestId } = render() - const input = getByTestId("max-tasks-home-screen-input") as HTMLInputElement - expect(input.value).toBe("10") + const slider = getByTestId("max-tasks-home-screen-slider") + const thumb = slider.querySelector('[role="slider"]') as HTMLElement + expect(thumb.getAttribute("aria-valuenow")).toBe("10") }) it("calls setCachedStateField when checkbox is toggled", async () => { @@ -54,12 +55,15 @@ describe("UISettings", () => { expect(checkbox.checked).toBe(true) }) - it("updates input value when maxTasksHomeScreen prop changes", () => { + it("updates slider value when maxTasksHomeScreen prop changes", () => { const { getByTestId, rerender } = render() - const input = getByTestId("max-tasks-home-screen-input") as HTMLInputElement - expect(input.value).toBe("4") + let slider = getByTestId("max-tasks-home-screen-slider") + let thumb = slider.querySelector('[role="slider"]') as HTMLElement + expect(thumb.getAttribute("aria-valuenow")).toBe("4") rerender() - expect(input.value).toBe("10") + slider = getByTestId("max-tasks-home-screen-slider") + thumb = slider.querySelector('[role="slider"]') as HTMLElement + expect(thumb.getAttribute("aria-valuenow")).toBe("10") }) }) diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 8e0188f68a..3e447f4cda 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -44,8 +44,7 @@ "description": "When enabled, thinking blocks will be collapsed by default until you interact with them" }, "maxTasksHomeScreen": { - "label": "Maximum number of tasks in home screen", - "description": "Number of recent tasks to show on the home screen (0-20). Set to 0 to hide the history preview entirely." + "label": "Maximum number of tasks in home screen" } }, "prompts": {