From 1071717537758b43058d82c2c1d3983c7e1cb47e Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Wed, 28 Jan 2026 14:18:22 -0700 Subject: [PATCH] feat(ui): add terminal session indicator in TaskHeader - Add activeTerminalSessions count to ExtensionState type - Add ProcessManager query in ClineProvider.getState() to count running sessions - Add TerminalSquare icon indicator in TaskHeader showing session count - Show indicator only when there are active sessions (green icon + count) - Add i18n translations for terminal session tooltip - Update ExtensionStateContext test with new property --- packages/types/src/vscode-extension-host.ts | 1 + src/core/webview/ClineProvider.ts | 12 ++++++++ webview-ui/src/components/chat/TaskHeader.tsx | 28 ++++++++++++++++++- .../__tests__/ExtensionStateContext.spec.tsx | 1 + webview-ui/src/i18n/locales/en/chat.json | 5 ++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index 7ae89e8777..d1435500da 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -385,6 +385,7 @@ export type ExtensionState = Pick< organizationSettingsVersion?: number isBrowserSessionActive: boolean // Actual browser session state + activeTerminalSessions: number // Count of active terminal sessions for current task autoCondenseContext: boolean autoCondenseContextPercent: number diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index b101bee7d2..00a29930c3 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -62,6 +62,7 @@ import { EMBEDDING_MODEL_PROFILES } from "../../shared/embeddingModels" import { ProfileValidator } from "../../shared/ProfileValidator" import { Terminal } from "../../integrations/terminal/Terminal" +import { ProcessManager } from "../../integrations/terminal/ProcessManager" import { downloadTask, getTaskFileName } from "../../integrations/misc/export-markdown" import { resolveDefaultSaveUri, saveLastExportPath } from "../../utils/export" import { getTheme } from "../../integrations/theme/getTheme" @@ -2074,6 +2075,7 @@ export class ClineProvider openRouterImageGenerationSelectedModel, featureRoomoteControlEnabled, isBrowserSessionActive, + activeTerminalSessions, } = await this.getState() let cloudOrganizations: CloudOrganizationMembership[] = [] @@ -2123,6 +2125,7 @@ export class ClineProvider alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false, alwaysAllowSubtasks: alwaysAllowSubtasks ?? false, isBrowserSessionActive, + activeTerminalSessions, allowedMaxRequests, allowedMaxCost, autoCondenseContext: autoCondenseContext ?? true, @@ -2357,6 +2360,14 @@ export class ClineProvider // Get actual browser session state const isBrowserSessionActive = this.getCurrentTask()?.browserSession?.isSessionActive() ?? false + // Get active terminal sessions count for current task + const currentTaskId = this.getCurrentTask()?.taskId + const activeTerminalSessions = currentTaskId + ? ProcessManager.getInstance() + .listSessions(currentTaskId) + .filter((s) => s.running).length + : 0 + // Return the same structure as before. return { apiConfiguration: providerSettings, @@ -2375,6 +2386,7 @@ export class ClineProvider alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false, alwaysAllowFollowupQuestions: stateValues.alwaysAllowFollowupQuestions ?? false, isBrowserSessionActive, + activeTerminalSessions, followupAutoApproveTimeoutMs: stateValues.followupAutoApproveTimeoutMs ?? 60000, diagnosticsEnabled: stateValues.diagnosticsEnabled ?? true, allowedMaxRequests: stateValues.allowedMaxRequests, diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index d5424b7422..09e7e070c3 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -11,6 +11,7 @@ import { FoldVertical, Globe, ArrowLeft, + TerminalSquare, } from "lucide-react" import prettyBytes from "pretty-bytes" @@ -68,7 +69,8 @@ const TaskHeader = ({ todos, }: TaskHeaderProps) => { const { t } = useTranslation() - const { apiConfiguration, currentTaskItem, clineMessages, isBrowserSessionActive } = useExtensionState() + const { apiConfiguration, currentTaskItem, clineMessages, isBrowserSessionActive, activeTerminalSessions } = + useExtensionState() const { id: modelId, info: model } = useSelectedModel(apiConfiguration) const [isTaskExpanded, setIsTaskExpanded] = useState(false) const [showLongRunningTaskMessage, setShowLongRunningTaskMessage] = useState(false) @@ -369,6 +371,30 @@ const TaskHeader = ({ )} )} + {(activeTerminalSessions ?? 0) > 0 && ( +
e.stopPropagation()}> + +
+ +
+
+ + {activeTerminalSessions} + +
+ )} )} {/* Expanded state: Show task text and images */} diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx index 0ee69a4ad6..9c5d11b30d 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.spec.tsx @@ -219,6 +219,7 @@ describe("mergeExtensionState", () => { taskSyncEnabled: false, featureRoomoteControlEnabled: false, isBrowserSessionActive: false, + activeTerminalSessions: 0, checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS, // Add the checkpoint timeout property } diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 336605e5ce..a69c068afe 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -407,6 +407,11 @@ "close": "Close browser" } }, + "terminal": { + "sessionsRunning_one": "{{count}} terminal session running", + "sessionsRunning_other": "{{count}} terminal sessions running", + "sessionsRunning": "{{count}} terminal session(s) running" + }, "codeblock": { "tooltips": { "expand": "Expand code block",