diff --git a/app/settings.css b/app/settings.css
index 5ec9659212..3cd3b08bb4 100644
--- a/app/settings.css
+++ b/app/settings.css
@@ -634,20 +634,36 @@
.config-switch {
position: relative;
flex-shrink: 0;
- width: 40px;
- height: 22px;
+ width: 32px;
+ height: 18px;
padding: 0;
- border: 0;
- border-radius: 11px;
- background: var(--border);
+ border: 1px solid var(--border);
+ border-radius: 9px;
+ background: var(--bg-selected);
cursor: pointer;
- transition: background 0.18s;
+ transition: background 0.15s, border-color 0.15s;
}
.config-switch[aria-checked="true"] {
+ border-color: var(--accent);
background: var(--accent);
}
+.config-switch:not(:disabled):hover {
+ border-color: var(--text-muted);
+}
+
+.config-switch[aria-checked="true"]:not(:disabled):hover {
+ border-color: var(--accent-hover);
+ background: var(--accent-hover);
+}
+
+.config-switch::before {
+ content: "";
+ position: absolute;
+ inset: -5px -3px;
+}
+
.config-switch:disabled {
cursor: default;
opacity: 0.55;
@@ -659,18 +675,18 @@
.config-switch-knob {
position: absolute;
- top: 3px;
- left: 3px;
- width: 16px;
- height: 16px;
+ top: 2px;
+ left: 2px;
+ width: 12px;
+ height: 12px;
border-radius: 50%;
- background: var(--bg);
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.22);
- transition: left 0.18s cubic-bezier(0.4, 0, 0.2, 1);
+ background: var(--text-muted);
+ transition: transform 0.15s, background 0.15s;
}
.config-switch[aria-checked="true"] .config-switch-knob {
- left: 21px;
+ transform: translateX(14px);
+ background: var(--bg);
}
.config-switch:focus-visible {
@@ -1031,6 +1047,7 @@
font-size: 10px;
}
+.settings-toggle-option,
.settings-shell-option {
display: flex;
align-items: center;
diff --git a/components/ChatWindow.tsx b/components/ChatWindow.tsx
index b05fb81991..5ab8e4e248 100644
--- a/components/ChatWindow.tsx
+++ b/components/ChatWindow.tsx
@@ -4,9 +4,9 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, typ
import type { AgentMessage, AssistantContentBlock, AssistantMessage, BashExecutionMessage, BlockingExtensionUiRequest, ExtensionUiRequest, SessionInfo, SessionTreeNode, ToolResultMessage, UserMessage } from "@/lib/types";
import { normalizeCustomPanelLines } from "@/lib/ansi";
import { asBracketedPaste, toTerminalKeyData } from "@/lib/terminal-input";
-import { countToolCallBlocks, getAssistantErrorMessage, getDisplayableAssistantBlocks, isMessageGroupAnchor, splitFinalAssistantBlocks } from "@/lib/message-display";
+import { countToolCallBlocks, getAssistantErrorMessage, getDisplayableAssistantBlocks, isMessageGroupAnchor, splitFinalAssistantBlocks, splitThinkingBlocks } from "@/lib/message-display";
import { extractTurnWrittenFiles, type WrittenFile } from "@/lib/turn-written-files";
-import { MessageView } from "./MessageView";
+import { MessageView, ThinkingBlock } from "./MessageView";
import { ChatInput, type ChatInputHandle } from "./ChatInput";
import { ChatMinimap, useMessageRefs } from "./ChatMinimap";
import { ExtensionStatusBar } from "./ExtensionStatusBar";
@@ -172,23 +172,6 @@ function getUserInputText(message: AgentMessage): string | null {
return text.length > 0 ? text : null;
}
-function countToolCalls(messages: AgentMessage[], indices: number[]): number {
- let count = 0;
- for (const idx of indices) {
- const msg = messages[idx];
- if (msg?.role !== "assistant") continue;
- count += countToolCallBlocks(getDisplayableAssistantBlocks(msg as AssistantMessage));
- }
- return count;
-}
-
-function hasDisplayableProcessMessage(message: AgentMessage): boolean {
- if (message.role === "assistant") {
- return getDisplayableAssistantBlocks(message as AssistantMessage).length > 0;
- }
- return message.role === "custom";
-}
-
function withAssistantBlocks(
message: AssistantMessage,
content: AssistantContentBlock[],
@@ -846,47 +829,83 @@ export function ChatWindow({ session, searchTarget, onSearchTargetHandled, sessi
rendered.push(renderMessage(userIdx));
- const processIndices: number[] = [];
- for (let processIdx = userIdx + 1; processIdx < finalAssistantIdx; processIdx++) {
- processIndices.push(processIdx);
- }
- const visibleProcessIndices = processIndices.filter((processIdx) => hasDisplayableProcessMessage(messages[processIdx]));
const finalAssistant = messages[finalAssistantIdx] as AssistantMessage;
const finalSplit = splitFinalAssistantBlocks(finalAssistant);
- const finalProcessMessage = finalSplit.processBlocks.length > 0
- ? withAssistantBlocks(finalAssistant, finalSplit.processBlocks, { omitUsage: true })
- : null;
const finalAnswerMessage = finalSplit.answerBlocks.length > 0 || getAssistantErrorMessage(finalAssistant)
? withAssistantBlocks(finalAssistant, finalSplit.answerBlocks)
: null;
- const processCount = visibleProcessIndices.length + (finalProcessMessage ? 1 : 0);
- if (processCount > 0) {
- const processRefIdx = visibleProcessIndices
- .map((processIdx) => visibleRefIndexByMessage.get(processIdx))
- .find((value): value is number => typeof value === "number")
- ?? (finalAnswerMessage ? undefined : visibleRefIndexByMessage.get(finalAssistantIdx));
- const processGroup = (
-