diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index fce48cfb5d..b704c0aee3 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -167,6 +167,7 @@ export const globalSettingsSchema = z.object({ ttsSpeed: z.number().optional(), soundEnabled: z.boolean().optional(), soundVolume: z.number().optional(), + taskHeaderHighlightEnabled: z.boolean().optional(), maxOpenTabsContext: z.number().optional(), maxWorkspaceFiles: z.number().optional(), @@ -368,6 +369,7 @@ export const EVALS_SETTINGS: RooCodeSettings = { ttsSpeed: 1, soundEnabled: false, soundVolume: 0.5, + taskHeaderHighlightEnabled: false, terminalShellIntegrationTimeout: 30000, terminalCommandDelay: 0, diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index 49a63c3ae4..38d0fc0e23 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -303,6 +303,7 @@ export type ExtensionState = Pick< | "ttsSpeed" | "soundEnabled" | "soundVolume" + | "taskHeaderHighlightEnabled" | "terminalOutputPreviewSize" | "terminalShellIntegrationTimeout" | "terminalShellIntegrationDisabled" diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index fc15a8dd5c..b5f949e318 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2064,6 +2064,7 @@ export class ClineProvider historyPreviewCollapsed, reasoningBlockCollapsed, enterBehavior, + taskHeaderHighlightEnabled, cloudUserInfo, cloudIsAuthenticated, sharingEnabled, @@ -2208,6 +2209,7 @@ export class ClineProvider historyPreviewCollapsed: historyPreviewCollapsed ?? false, reasoningBlockCollapsed: reasoningBlockCollapsed ?? true, enterBehavior: enterBehavior ?? "send", + taskHeaderHighlightEnabled: taskHeaderHighlightEnabled ?? false, cloudUserInfo, cloudIsAuthenticated: cloudIsAuthenticated ?? false, cloudAuthSkipModel: this.context.globalState.get("roo-auth-skip-model") ?? false, @@ -2446,6 +2448,7 @@ export class ClineProvider historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false, reasoningBlockCollapsed: stateValues.reasoningBlockCollapsed ?? true, enterBehavior: stateValues.enterBehavior ?? "send", + taskHeaderHighlightEnabled: stateValues.taskHeaderHighlightEnabled ?? false, cloudUserInfo, cloudIsAuthenticated, sharingEnabled, diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index d5424b7422..ff516425c4 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -68,7 +68,8 @@ const TaskHeader = ({ todos, }: TaskHeaderProps) => { const { t } = useTranslation() - const { apiConfiguration, currentTaskItem, clineMessages, isBrowserSessionActive } = useExtensionState() + const { apiConfiguration, currentTaskItem, clineMessages, isBrowserSessionActive, taskHeaderHighlightEnabled } = + useExtensionState() const { id: modelId, info: model } = useSelectedModel(apiConfiguration) const [isTaskExpanded, setIsTaskExpanded] = useState(false) const [showLongRunningTaskMessage, setShowLongRunningTaskMessage] = useState(false) @@ -76,19 +77,34 @@ const TaskHeader = ({ autoOpenOnAuth: false, }) + // Determine if this is a subtask (has a parent) + const isSubtask = !!parentTaskId + + // Find the last message that isn't a resume action (shared by isTaskComplete and highlightClass) + const lastRelevantMessage = useMemo(() => { + const msgs = clineMessages || [] + const idx = findLastIndex(msgs, (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task")) + return idx !== -1 ? msgs[idx] : undefined + }, [clineMessages]) + // Check if the task is complete by looking at the last relevant message (skipping resume messages) - const isTaskComplete = - clineMessages && clineMessages.length > 0 - ? (() => { - const lastRelevantIndex = findLastIndex( - clineMessages, - (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task"), - ) - return lastRelevantIndex !== -1 - ? clineMessages[lastRelevantIndex]?.ask === "completion_result" - : false - })() - : false + const isTaskComplete = lastRelevantMessage?.ask === "completion_result" + + // Compute highlight CSS class: green for task complete, yellow for user attention needed + const highlightClass = useMemo(() => { + if (!taskHeaderHighlightEnabled || isSubtask) return undefined + if (!lastRelevantMessage || lastRelevantMessage.partial) return undefined + + if (lastRelevantMessage.ask === "completion_result") { + return "task-header-highlight-green" + } + + if (lastRelevantMessage.ask) { + return "task-header-highlight-yellow" + } + + return undefined + }, [taskHeaderHighlightEnabled, isSubtask, lastRelevantMessage]) useEffect(() => { const timer = setTimeout(() => { @@ -141,9 +157,6 @@ const TaskHeader = ({ const hasTodos = todos && Array.isArray(todos) && todos.length > 0 - // Determine if this is a subtask (has a parent) - const isSubtask = !!parentTaskId - const handleBackToParent = () => { if (parentTaskId) { vscode.postMessage({ type: "showTaskWithId", text: parentTaskId }) @@ -174,12 +187,14 @@ const TaskHeader = ({ )}
{ // Don't expand if clicking on todos section diff --git a/webview-ui/src/components/chat/__tests__/TaskHeader.spec.tsx b/webview-ui/src/components/chat/__tests__/TaskHeader.spec.tsx index c4ebe06973..7414d7a9c5 100644 --- a/webview-ui/src/components/chat/__tests__/TaskHeader.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/TaskHeader.spec.tsx @@ -40,6 +40,7 @@ let mockExtensionState: { apiConfiguration: ProviderSettings currentTaskItem: { id: string } | null clineMessages: any[] + taskHeaderHighlightEnabled?: boolean } = { apiConfiguration: { apiProvider: "anthropic", @@ -48,6 +49,7 @@ let mockExtensionState: { } as ProviderSettings, currentTaskItem: { id: "test-task-id" }, clineMessages: [], + taskHeaderHighlightEnabled: false, } // Mock the ExtensionStateContext @@ -215,6 +217,7 @@ describe("TaskHeader", () => { } as ProviderSettings, currentTaskItem: { id: "test-task-id" }, clineMessages: [], + taskHeaderHighlightEnabled: false, } }) @@ -423,6 +426,175 @@ describe("TaskHeader", () => { }) }) + describe("Task header highlight", () => { + const completionMessages = [ + { + type: "ask", + ask: "completion_result", + ts: Date.now(), + text: "Task completed!", + }, + ] + + beforeEach(() => { + mockExtensionState = { + apiConfiguration: { + apiProvider: "anthropic", + apiKey: "test-api-key", + apiModelId: "claude-3-opus-20240229", + } as ProviderSettings, + currentTaskItem: { id: "test-task-id" }, + clineMessages: [], + taskHeaderHighlightEnabled: false, + } + }) + + it("should apply green highlight class when task is complete and highlight is enabled", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: completionMessages, + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(true) + expect(container.classList.contains("task-header-highlight-yellow")).toBe(false) + }) + + it("should apply yellow highlight class when task needs user attention and highlight is enabled", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: [ + { + type: "ask", + ask: "tool", + ts: Date.now(), + text: "Need permission to use tool", + }, + ], + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-yellow")).toBe(true) + expect(container.classList.contains("task-header-highlight-green")).toBe(false) + }) + + it("should not apply highlight when highlight is disabled", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: completionMessages, + taskHeaderHighlightEnabled: false, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(false) + expect(container.classList.contains("task-header-highlight-yellow")).toBe(false) + }) + + it("should not apply highlight when task is a subtask", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: completionMessages, + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader({ parentTaskId: "parent-task-123" }) + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(false) + expect(container.classList.contains("task-header-highlight-yellow")).toBe(false) + }) + + it("should not apply highlight when last message is partial", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: [ + { + type: "ask", + ask: "completion_result", + ts: Date.now(), + text: "Task completed!", + partial: true, + }, + ], + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(false) + expect(container.classList.contains("task-header-highlight-yellow")).toBe(false) + }) + + it("should not apply highlight when no clineMessages exist", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: [], + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(false) + expect(container.classList.contains("task-header-highlight-yellow")).toBe(false) + }) + + it("should not apply highlight when last relevant message has no ask type", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: [{ type: "say", say: "text", ts: Date.now(), text: "Working..." }], + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(false) + expect(container.classList.contains("task-header-highlight-yellow")).toBe(false) + }) + + it("should apply green class when completion_result is followed by resume messages", () => { + mockExtensionState = { + ...mockExtensionState, + clineMessages: [ + { + type: "ask", + ask: "completion_result", + ts: Date.now() - 2000, + text: "Task completed!", + }, + { + type: "ask", + ask: "resume_completed_task", + ts: Date.now() - 1000, + text: "Resume completed task?", + }, + { + type: "ask", + ask: "resume_task", + ts: Date.now(), + text: "Resume task?", + }, + ], + taskHeaderHighlightEnabled: true, + } + + renderTaskHeader() + + const container = screen.getByTestId("task-header-container") + expect(container.classList.contains("task-header-highlight-green")).toBe(true) + }) + }) + describe("Context window percentage calculation", () => { // The percentage should be calculated as: // contextTokens / (contextWindow - reservedForOutput) * 100 diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 1876302b47..86f4ded338 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -124,6 +124,11 @@ type SettingsViewProps = { targetSection?: string } +const withCachedStateDefaults = (state: ExtensionStateContextType): ExtensionStateContextType => ({ + ...state, + taskHeaderHighlightEnabled: state.taskHeaderHighlightEnabled ?? false, +}) + const SettingsView = forwardRef(({ onDone, targetSection }, ref) => { const { t } = useAppTranslation() @@ -147,7 +152,7 @@ const SettingsView = forwardRef(({ onDone, t const prevApiConfigName = useRef(currentApiConfigName) const confirmDialogHandler = useRef<() => void>() - const [cachedState, setCachedState] = useState(() => extensionState) + const [cachedState, setCachedState] = useState(() => withCachedStateDefaults(extensionState)) const { alwaysAllowReadOnly, @@ -212,6 +217,7 @@ const SettingsView = forwardRef(({ onDone, t includeCurrentTime, includeCurrentCost, maxGitStatusFiles, + taskHeaderHighlightEnabled, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -223,7 +229,7 @@ const SettingsView = forwardRef(({ onDone, t return } - setCachedState((prevCachedState) => ({ ...prevCachedState, ...extensionState })) + setCachedState((prevCachedState) => withCachedStateDefaults({ ...prevCachedState, ...extensionState })) prevApiConfigName.current = currentApiConfigName setChangeDetected(false) }, [currentApiConfigName, extensionState]) @@ -231,7 +237,7 @@ const SettingsView = forwardRef(({ onDone, t // Bust the cache when settings are imported. useEffect(() => { if (settingsImportedAt) { - setCachedState((prevCachedState) => ({ ...prevCachedState, ...extensionState })) + setCachedState((prevCachedState) => withCachedStateDefaults({ ...prevCachedState, ...extensionState })) setChangeDetected(false) } }, [settingsImportedAt, extensionState]) @@ -415,6 +421,7 @@ const SettingsView = forwardRef(({ onDone, t includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true, reasoningBlockCollapsed: reasoningBlockCollapsed ?? true, enterBehavior: enterBehavior ?? "send", + taskHeaderHighlightEnabled, includeCurrentTime: includeCurrentTime ?? true, includeCurrentCost: includeCurrentCost ?? true, maxGitStatusFiles: maxGitStatusFiles ?? 0, @@ -455,7 +462,7 @@ const SettingsView = forwardRef(({ onDone, t (confirm: boolean) => { if (confirm) { // Discard changes: Reset state and flag - setCachedState(extensionState) // Revert to original state + setCachedState(withCachedStateDefaults(extensionState)) // Revert to original state setChangeDetected(false) // Reset change flag confirmDialogHandler.current?.() // Execute the pending action (e.g., tab switch) } @@ -906,6 +913,7 @@ const SettingsView = forwardRef(({ onDone, t {/* UI Section */} {renderTab === "ui" && ( { reasoningBlockCollapsed: boolean enterBehavior: "send" | "newline" - setCachedStateField: SetCachedStateField + taskHeaderHighlightEnabled: boolean + setCachedStateField: SetCachedStateField<"reasoningBlockCollapsed" | "enterBehavior" | "taskHeaderHighlightEnabled"> } export const UISettings = ({ reasoningBlockCollapsed, enterBehavior, + taskHeaderHighlightEnabled, setCachedStateField, ...props }: UISettingsProps) => { @@ -48,12 +49,36 @@ export const UISettings = ({ }) } + const handleTaskHeaderHighlightChange = (enabled: boolean) => { + setCachedStateField("taskHeaderHighlightEnabled", enabled) + } + return (
{t("settings:sections.ui")}
+ {/* Task Header Highlight Setting */} + +
+ + handleTaskHeaderHighlightChange((event.target as HTMLInputElement).checked) + } + data-testid="task-header-highlight-checkbox"> + {t("settings:ui.taskCompleteColor.label")} + +
+ {t("settings:ui.taskCompleteColor.description")} +
+
+
+ {/* Collapse Thinking Messages Setting */} handleReasoningBlockCollapsedChange(e.target.checked)} + onChange={(event) => + handleReasoningBlockCollapsedChange((event.target as HTMLInputElement).checked) + } data-testid="collapse-thinking-checkbox"> {t("settings:ui.collapseThinking.label")} @@ -80,7 +107,9 @@ export const UISettings = ({
handleEnterBehaviorChange(e.target.checked)} + onChange={(event) => + handleEnterBehaviorChange((event.target as HTMLInputElement).checked) + } data-testid="enter-behavior-checkbox"> {t("settings:ui.requireCtrlEnterToSend.label", { primaryMod })} diff --git a/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx b/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx index 2a21a410b3..ea8dc84ba2 100644 --- a/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/UISettings.spec.tsx @@ -2,10 +2,38 @@ import { render, fireEvent, waitFor } from "@testing-library/react" import { describe, it, expect, vi } from "vitest" import { UISettings } from "../UISettings" +// Mock useAppTranslation +vi.mock("@/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => key, + }), +})) + +// Mock telemetry +vi.mock("@/utils/TelemetryClient", () => ({ + telemetryClient: { capture: vi.fn() }, +})) + +// Mock SearchableSetting to render children directly +vi.mock("../SearchableSetting", () => ({ + SearchableSetting: ({ children }: { children: React.ReactNode }) =>
{children}
, +})) + +// Mock SectionHeader to render children +vi.mock("../SectionHeader", () => ({ + SectionHeader: ({ children }: { children: React.ReactNode }) =>
{children}
, +})) + +// Mock Section to render children +vi.mock("../Section", () => ({ + Section: ({ children }: { children: React.ReactNode }) =>
{children}
, +})) + describe("UISettings", () => { const defaultProps = { reasoningBlockCollapsed: false, enterBehavior: "send" as const, + taskHeaderHighlightEnabled: false, setCachedStateField: vi.fn(), } @@ -41,4 +69,55 @@ describe("UISettings", () => { rerender() expect(checkbox.checked).toBe(true) }) + + describe("Task header highlight", () => { + it("renders checkbox unchecked when taskHeaderHighlightEnabled is false", () => { + const { getByTestId } = render() + const checkbox = getByTestId("task-header-highlight-checkbox") as HTMLInputElement + expect(checkbox).toBeTruthy() + expect(checkbox.checked).toBe(false) + }) + + it("renders checkbox checked when taskHeaderHighlightEnabled is true", () => { + const { getByTestId } = render() + const checkbox = getByTestId("task-header-highlight-checkbox") as HTMLInputElement + expect(checkbox.checked).toBe(true) + }) + + it("calls setCachedStateField with true when toggling on", async () => { + const setCachedStateField = vi.fn() + const { getByTestId } = render( + , + ) + + const checkbox = getByTestId("task-header-highlight-checkbox") + fireEvent.click(checkbox) + + await waitFor(() => { + expect(setCachedStateField).toHaveBeenCalledWith("taskHeaderHighlightEnabled", true) + }) + }) + + it("calls setCachedStateField with false when toggling off", async () => { + const setCachedStateField = vi.fn() + const { getByTestId } = render( + , + ) + + const checkbox = getByTestId("task-header-highlight-checkbox") + fireEvent.click(checkbox) + + await waitFor(() => { + expect(setCachedStateField).toHaveBeenCalledWith("taskHeaderHighlightEnabled", false) + }) + }) + }) }) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 2378873f01..9e24172eda 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -139,6 +139,7 @@ export interface ExtensionStateContextType extends ExtensionState { setReasoningBlockCollapsed: (value: boolean) => void enterBehavior?: "send" | "newline" setEnterBehavior: (value: "send" | "newline") => void + taskHeaderHighlightEnabled?: boolean autoCondenseContext: boolean setAutoCondenseContext: (value: boolean) => void autoCondenseContextPercent: number @@ -238,6 +239,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode historyPreviewCollapsed: false, // Initialize the new state (default to expanded) reasoningBlockCollapsed: true, // Default to collapsed enterBehavior: "send", // Default: Enter sends, Shift+Enter creates newline + taskHeaderHighlightEnabled: false, cloudUserInfo: null, cloudIsAuthenticated: false, cloudOrganizations: [], diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index cfcc0727c5..1148e254d2 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Ressaltat de la capçalera de tasca", + "description": "Ressalta la capçalera de la tasca amb un indicador de color quan les tasques es completen o necessiten la teva atenció, ajudant-te a detectar ràpidament quan cal actuar" + }, "collapseThinking": { "label": "Replega els missatges de pensament per defecte", "description": "Quan estigui activat, els blocs de pensament es replegaran per defecte fins que interactuïs amb ells" diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index f3e753e011..315841b942 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Aufgaben-Header-Hervorhebung", + "description": "Hebt den Aufgaben-Header mit einem Farbindikator hervor, wenn Aufgaben abgeschlossen werden oder deine Aufmerksamkeit erfordern, damit du schnell erkennst, wann eine Aktion nötig ist" + }, "collapseThinking": { "label": "Gedankenblöcke standardmäßig ausblenden", "description": "Wenn aktiviert, werden Gedankenblöcke standardmäßig ausgeblendet, bis du mit ihnen interagierst" diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 61dfaf42af..51bfaa462e 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -157,6 +157,10 @@ "footer": "Create your own skills with the Skill Writer mode, available in the Modes Marketplace." }, "ui": { + "taskCompleteColor": { + "label": "Task Header Highlight", + "description": "Highlights the task header with a color indicator when tasks complete or need your attention, helping you quickly spot when action is required" + }, "collapseThinking": { "label": "Collapse Thinking messages by default", "description": "When enabled, thinking blocks will be collapsed by default until you interact with them" diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index aafa9568e0..8543064cf4 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Resaltado del encabezado de tarea", + "description": "Resalta el encabezado de la tarea con un indicador de color cuando las tareas se completan o necesitan tu atención, ayudándote a detectar rápidamente cuándo se requiere acción" + }, "collapseThinking": { "label": "Colapsar mensajes de pensamiento por defecto", "description": "Cuando está activado, los bloques de pensamiento se colapsarán por defecto hasta que interactúes con ellos" diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index ba51bf7ca8..fa7f4d4b92 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Surlignage de l'en-tête de tâche", + "description": "Surligne l'en-tête de la tâche avec un indicateur de couleur lorsque les tâches sont terminées ou nécessitent ton attention, t'aidant à repérer rapidement quand une action est requise" + }, "collapseThinking": { "label": "Réduire les messages de réflexion par défaut", "description": "Si activé, les blocs de réflexion seront réduits par défaut jusqu'à ce que vous interagissiez avec eux" diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index e9a3c3cbd2..91e1c313e9 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "टास्क हेडर हाइलाइट", + "description": "जब टास्क पूरे होते हैं या तुम्हारे ध्यान की ज़रूरत होती है, तो टास्क हेडर को रंग संकेतक से हाइलाइट करता है, ताकि तुम जल्दी से पहचान सको कि कब कार्रवाई ज़रूरी है" + }, "collapseThinking": { "label": "सोच संदेशों को डिफ़ॉल्ट रूप से संक्षिप्त करें", "description": "सक्षम होने पर, सोच ब्लॉक आपके द्वारा उनके साथ इंटरैक्ट करने तक डिफ़ॉल्ट रूप से संक्षिप्त रहेंगे" diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index cca52db56c..62a7aa35a2 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Sorotan Header Tugas", + "description": "Menyoroti header tugas dengan indikator warna saat tugas selesai atau membutuhkan perhatianmu, membantumu dengan cepat mengetahui kapan tindakan diperlukan" + }, "collapseThinking": { "label": "Ciutkan pesan Berpikir secara default", "description": "Jika diaktifkan, blok berpikir akan diciutkan secara default sampai Anda berinteraksi dengannya" diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 5478568ebe..dd0a14c4ed 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Evidenziazione intestazione attività", + "description": "Evidenzia l'intestazione dell'attività con un indicatore di colore quando le attività vengono completate o richiedono la tua attenzione, aiutandoti a individuare rapidamente quando è necessaria un'azione" + }, "collapseThinking": { "label": "Comprimi i messaggi di pensiero per impostazione predefinita", "description": "Se abilitato, i blocchi di pensiero verranno compressi per impostazione predefinita finché non interagisci con essi" diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index c33fdd85e2..ded25fb0a5 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "タスクヘッダーのハイライト", + "description": "タスクが完了したり注意が必要なときに、タスクヘッダーをカラーインジケーターでハイライトし、アクションが必要なタイミングをすばやく把握できるようにします" + }, "collapseThinking": { "label": "デフォルトで思考メッセージを折りたたむ", "description": "有効にすると、操作するまで思考ブロックがデフォルトで折りたたまれます" diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 146c6044ba..bacec0b0e5 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "작업 헤더 하이라이트", + "description": "작업이 완료되거나 주의가 필요할 때 작업 헤더를 색상 표시로 강조하여 조치가 필요한 시점을 빠르게 파악할 수 있도록 합니다" + }, "collapseThinking": { "label": "기본적으로 생각 메시지 접기", "description": "활성화하면 상호 작용할 때까지 생각 블록이 기본적으로 접힙니다" diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 942b2d6bd8..f0c6c6ca24 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Taakkop markering", + "description": "Markeert de taakkop met een kleurindicator wanneer taken voltooid zijn of je aandacht nodig hebben, zodat je snel kunt zien wanneer actie vereist is" + }, "collapseThinking": { "label": "Denkberichten standaard samenvouwen", "description": "Indien ingeschakeld, worden denkblokken standaard samengevouwen totdat je ermee interageert" diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 7a3632ab48..f60eea2561 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Podświetlenie nagłówka zadania", + "description": "Podświetla nagłówek zadania kolorowym wskaźnikiem, gdy zadania zostaną ukończone lub wymagają twojej uwagi, pomagając szybko zauważyć, kiedy wymagane jest działanie" + }, "collapseThinking": { "label": "Domyślnie zwijaj komunikaty o myśleniu", "description": "Gdy włączone, bloki myślenia będą domyślnie zwinięte, dopóki nie wejdziesz z nimi w interakcję" diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index b82cfa8ee8..dd3f8a302d 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Destaque do cabeçalho da tarefa", + "description": "Destaca o cabeçalho da tarefa com um indicador de cor quando as tarefas são concluídas ou precisam da sua atenção, ajudando você a identificar rapidamente quando uma ação é necessária" + }, "collapseThinking": { "label": "Recolher mensagens de pensamento por padrão", "description": "Quando ativado, os blocos de pensamento serão recolhidos por padrão até que você interaja com eles" diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 93cbaa8370..888c3cbc32 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Подсветка заголовка задачи", + "description": "Подсвечивает заголовок задачи цветовым индикатором при завершении задач или когда требуется твоё внимание, помогая быстро заметить, когда необходимо действие" + }, "collapseThinking": { "label": "Сворачивать сообщения о размышлениях по умолчанию", "description": "Если включено, блоки с размышлениями будут свернуты по умолчанию, пока вы не начнете с ними взаимодействовать" diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 714d3f98b8..ea5f5910a0 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Görev Başlığı Vurgulama", + "description": "Görevler tamamlandığında veya dikkatine ihtiyaç duyulduğunda görev başlığını renk göstergesiyle vurgular, eylem gerektiğinde hızlıca fark etmene yardımcı olur" + }, "collapseThinking": { "label": "Düşünme mesajlarını varsayılan olarak daralt", "description": "Etkinleştirildiğinde, düşünme blokları siz onlarla etkileşime girene kadar varsayılan olarak daraltılır" diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 15465c60f3..33c08e3e96 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "Đánh dấu tiêu đề tác vụ", + "description": "Đánh dấu tiêu đề tác vụ bằng chỉ báo màu khi tác vụ hoàn thành hoặc cần sự chú ý của bạn, giúp bạn nhanh chóng nhận biết khi nào cần hành động" + }, "collapseThinking": { "label": "Thu gọn tin nhắn Suy nghĩ theo mặc định", "description": "Khi được bật, các khối suy nghĩ sẽ được thu gọn theo mặc định cho đến khi bạn tương tác với chúng" diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 771bf7ca7e..8f23cb18c0 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -1005,6 +1005,10 @@ } }, "ui": { + "taskCompleteColor": { + "label": "任务标题高亮", + "description": "当任务完成或需要你关注时,通过颜色指示器高亮任务标题,帮助你快速发现需要操作的时机" + }, "collapseThinking": { "label": "默认折叠「思考」消息", "description": "启用后,「思考」块将默认折叠,直到您与其交互" diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 8c19327e1f..2144ca63f5 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -104,6 +104,10 @@ "footer": "使用斜線命令快速存取經常使用的提示詞和工作流程。" }, "ui": { + "taskCompleteColor": { + "label": "工作標題醒目提示", + "description": "當工作完成或需要你注意時,以顏色指示器醒目提示工作標題,幫助你快速發現需要採取行動的時機" + }, "collapseThinking": { "label": "預設折疊「思考」訊息", "description": "啟用後,「思考」塊將預設折疊,直到您與其互動" diff --git a/webview-ui/src/index.css b/webview-ui/src/index.css index 2395b69f57..837c4d6b64 100644 --- a/webview-ui/src/index.css +++ b/webview-ui/src/index.css @@ -586,3 +586,30 @@ input[cmdk-input]:focus { padding: 8px; margin: -8px; } + +/* Task header highlight overrides - force all children to use contrasting text. + Uses hardcoded background colors instead of theme-dependent --vscode-charts-* + variables to guarantee WCAG AA 4.5:1 contrast with text colors. */ +.task-header-highlight-green { + --vscode-foreground: white; + background-color: #15803d !important; /* green-700, WCAG AA ~6.5:1 with white */ +} +.task-header-highlight-green, +.task-header-highlight-green * { + color: white !important; +} +.task-header-highlight-green svg { + stroke: white !important; +} + +.task-header-highlight-yellow { + --vscode-foreground: black; + background-color: #ca8a04 !important; /* yellow-600, WCAG AA ~6.2:1 with black */ +} +.task-header-highlight-yellow, +.task-header-highlight-yellow * { + color: black !important; +} +.task-header-highlight-yellow svg { + stroke: black !important; +}