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
This commit is contained in:
Hannes Rudolph 2026-01-28 14:18:22 -07:00
parent 56da141bd9
commit 1071717537
5 changed files with 46 additions and 1 deletions

View file

@ -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

View file

@ -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,

View file

@ -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 = ({
)}
</div>
)}
{(activeTerminalSessions ?? 0) > 0 && (
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
<StandardTooltip
content={t("chat:terminal.sessionsRunning", { count: activeTerminalSessions })}>
<div
className={cn(
"relative flex items-center justify-center h-5 w-5 p-0",
"text-vscode-foreground opacity-85",
)}>
<TerminalSquare
className="w-4 h-4"
style={{
color: "#4ade80",
}}
/>
</div>
</StandardTooltip>
<span
className="text-sm font-medium"
style={{ color: "var(--vscode-testing-iconPassed)" }}>
{activeTerminalSessions}
</span>
</div>
)}
</div>
)}
{/* Expanded state: Show task text and images */}

View file

@ -219,6 +219,7 @@ describe("mergeExtensionState", () => {
taskSyncEnabled: false,
featureRoomoteControlEnabled: false,
isBrowserSessionActive: false,
activeTerminalSessions: 0,
checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS, // Add the checkpoint timeout property
}

View file

@ -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",