Просмотр исходного кода

ux: Collapse thinking blocks by default (but control all of them with a keyboard shortcut) (#8254)

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: Roo Code <[email protected]>
Co-authored-by: Matt Rubens <[email protected]>
Bruno Bergher 3 месяцев назад
Родитель
Сommit
d8dd19a6ed
48 измененных файлов с 364 добавлено и 25 удалено
  1. 1 0
      packages/types/src/global-settings.ts
  2. 3 0
      src/core/webview/ClineProvider.ts
  3. 4 0
      src/core/webview/webviewMessageHandler.ts
  4. 1 0
      src/shared/ExtensionMessage.ts
  5. 1 0
      src/shared/WebviewMessage.ts
  6. 15 8
      webview-ui/src/components/chat/ChatRow.tsx
  7. 35 17
      webview-ui/src/components/chat/ReasoningBlock.tsx
  8. 6 0
      webview-ui/src/components/common/MarkdownBlock.tsx
  9. 14 0
      webview-ui/src/components/settings/SettingsView.tsx
  10. 56 0
      webview-ui/src/components/settings/UISettings.tsx
  11. 43 0
      webview-ui/src/components/settings/__tests__/UISettings.spec.tsx
  12. 5 0
      webview-ui/src/context/ExtensionStateContext.tsx
  13. 3 0
      webview-ui/src/i18n/locales/ca/chat.json
  14. 7 0
      webview-ui/src/i18n/locales/ca/settings.json
  15. 3 0
      webview-ui/src/i18n/locales/de/chat.json
  16. 7 0
      webview-ui/src/i18n/locales/de/settings.json
  17. 3 0
      webview-ui/src/i18n/locales/en/chat.json
  18. 7 0
      webview-ui/src/i18n/locales/en/settings.json
  19. 3 0
      webview-ui/src/i18n/locales/es/chat.json
  20. 7 0
      webview-ui/src/i18n/locales/es/settings.json
  21. 3 0
      webview-ui/src/i18n/locales/fr/chat.json
  22. 7 0
      webview-ui/src/i18n/locales/fr/settings.json
  23. 3 0
      webview-ui/src/i18n/locales/hi/chat.json
  24. 7 0
      webview-ui/src/i18n/locales/hi/settings.json
  25. 3 0
      webview-ui/src/i18n/locales/id/chat.json
  26. 7 0
      webview-ui/src/i18n/locales/id/settings.json
  27. 3 0
      webview-ui/src/i18n/locales/it/chat.json
  28. 7 0
      webview-ui/src/i18n/locales/it/settings.json
  29. 3 0
      webview-ui/src/i18n/locales/ja/chat.json
  30. 7 0
      webview-ui/src/i18n/locales/ja/settings.json
  31. 3 0
      webview-ui/src/i18n/locales/ko/chat.json
  32. 7 0
      webview-ui/src/i18n/locales/ko/settings.json
  33. 3 0
      webview-ui/src/i18n/locales/nl/chat.json
  34. 7 0
      webview-ui/src/i18n/locales/nl/settings.json
  35. 3 0
      webview-ui/src/i18n/locales/pl/chat.json
  36. 7 0
      webview-ui/src/i18n/locales/pl/settings.json
  37. 3 0
      webview-ui/src/i18n/locales/pt-BR/chat.json
  38. 7 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  39. 3 0
      webview-ui/src/i18n/locales/ru/chat.json
  40. 7 0
      webview-ui/src/i18n/locales/ru/settings.json
  41. 3 0
      webview-ui/src/i18n/locales/tr/chat.json
  42. 7 0
      webview-ui/src/i18n/locales/tr/settings.json
  43. 3 0
      webview-ui/src/i18n/locales/vi/chat.json
  44. 7 0
      webview-ui/src/i18n/locales/vi/settings.json
  45. 3 0
      webview-ui/src/i18n/locales/zh-CN/chat.json
  46. 7 0
      webview-ui/src/i18n/locales/zh-CN/settings.json
  47. 3 0
      webview-ui/src/i18n/locales/zh-TW/chat.json
  48. 7 0
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 1 - 0
packages/types/src/global-settings.ts

@@ -147,6 +147,7 @@ export const globalSettingsSchema = z.object({
 	enhancementApiConfigId: z.string().optional(),
 	includeTaskHistoryInEnhance: z.boolean().optional(),
 	historyPreviewCollapsed: z.boolean().optional(),
+	reasoningBlockCollapsed: z.boolean().optional(),
 	profileThresholds: z.record(z.string(), z.number()).optional(),
 	hasOpenedModeSelector: z.boolean().optional(),
 	lastModeExportPath: z.string().optional(),

+ 3 - 0
src/core/webview/ClineProvider.ts

@@ -1792,6 +1792,7 @@ export class ClineProvider
 			maxTotalImageSize,
 			terminalCompressProgressBar,
 			historyPreviewCollapsed,
+			reasoningBlockCollapsed,
 			cloudUserInfo,
 			cloudIsAuthenticated,
 			sharingEnabled,
@@ -1925,6 +1926,7 @@ export class ClineProvider
 			terminalCompressProgressBar: terminalCompressProgressBar ?? true,
 			hasSystemPromptOverride,
 			historyPreviewCollapsed: historyPreviewCollapsed ?? false,
+			reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
 			cloudUserInfo,
 			cloudIsAuthenticated: cloudIsAuthenticated ?? false,
 			cloudOrganizations,
@@ -2139,6 +2141,7 @@ export class ClineProvider
 			maxTotalImageSize: stateValues.maxTotalImageSize ?? 20,
 			maxConcurrentFileReads: stateValues.maxConcurrentFileReads ?? 5,
 			historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false,
+			reasoningBlockCollapsed: stateValues.reasoningBlockCollapsed ?? true,
 			cloudUserInfo,
 			cloudIsAuthenticated,
 			sharingEnabled,

+ 4 - 0
src/core/webview/webviewMessageHandler.ts

@@ -1617,6 +1617,10 @@ export const webviewMessageHandler = async (
 			await updateGlobalState("historyPreviewCollapsed", message.bool ?? false)
 			// No need to call postStateToWebview here as the UI already updated optimistically
 			break
+		case "setReasoningBlockCollapsed":
+			await updateGlobalState("reasoningBlockCollapsed", message.bool ?? true)
+			// No need to call postStateToWebview here as the UI already updated optimistically
+			break
 		case "toggleApiConfigPin":
 			if (message.text) {
 				const currentPinned = getGlobalState("pinnedApiConfigs") ?? {}

+ 1 - 0
src/shared/ExtensionMessage.ts

@@ -287,6 +287,7 @@ export type ExtensionState = Pick<
 	| "maxDiagnosticMessages"
 	| "openRouterImageGenerationSelectedModel"
 	| "includeTaskHistoryInEnhance"
+	| "reasoningBlockCollapsed"
 > & {
 	version: string
 	clineMessages: ClineMessage[]

+ 1 - 0
src/shared/WebviewMessage.ts

@@ -194,6 +194,7 @@ export interface WebviewMessage {
 		| "focusPanelRequest"
 		| "profileThresholds"
 		| "setHistoryPreviewCollapsed"
+		| "setReasoningBlockCollapsed"
 		| "openExternal"
 		| "filterMarketplaceItems"
 		| "marketplaceButtonClicked"

+ 15 - 8
webview-ui/src/components/chat/ChatRow.tsx

@@ -60,6 +60,7 @@ import {
 	PocketKnife,
 	FolderTree,
 	TerminalSquare,
+	MessageCircle,
 } from "lucide-react"
 import { cn } from "@/lib/utils"
 
@@ -1118,14 +1119,20 @@ export const ChatRowContent = ({
 				case "text":
 					return (
 						<div>
-							<Markdown markdown={message.text} partial={message.partial} />
-							{message.images && message.images.length > 0 && (
-								<div style={{ marginTop: "10px" }}>
-									{message.images.map((image, index) => (
-										<ImageBlock key={index} imageData={image} />
-									))}
-								</div>
-							)}
+							<div style={headerStyle}>
+								<MessageCircle className="w-4" aria-label="Speech bubble icon" />
+								<span style={{ fontWeight: "bold" }}>{t("chat:text.rooSaid")}</span>
+							</div>
+							<div className="pl-6">
+								<Markdown markdown={message.text} partial={message.partial} />
+								{message.images && message.images.length > 0 && (
+									<div style={{ marginTop: "10px" }}>
+										{message.images.map((image, index) => (
+											<ImageBlock key={index} imageData={image} />
+										))}
+									</div>
+								)}
+							</div>
 						</div>
 					)
 				case "user_feedback":

+ 35 - 17
webview-ui/src/components/chat/ReasoningBlock.tsx

@@ -1,8 +1,10 @@
-import React, { useEffect, useRef, useState } from "react"
+import { useEffect, useRef, useState } from "react"
 import { useTranslation } from "react-i18next"
+import { useExtensionState } from "@src/context/ExtensionStateContext"
 
 import MarkdownBlock from "../common/MarkdownBlock"
-import { Lightbulb } from "lucide-react"
+import { Lightbulb, ChevronUp } from "lucide-react"
+import { cn } from "@/lib/utils"
 
 interface ReasoningBlockProps {
 	content: string
@@ -12,18 +14,20 @@ interface ReasoningBlockProps {
 	metadata?: any
 }
 
-/**
- * Render reasoning with a heading and a simple timer.
- * - Heading uses i18n key chat:reasoning.thinking
- * - Timer runs while reasoning is active (no persistence)
- */
 export const ReasoningBlock = ({ content, isStreaming, isLast }: ReasoningBlockProps) => {
 	const { t } = useTranslation()
+	const { reasoningBlockCollapsed } = useExtensionState()
+
+	const [isCollapsed, setIsCollapsed] = useState(reasoningBlockCollapsed)
 
 	const startTimeRef = useRef<number>(Date.now())
 	const [elapsed, setElapsed] = useState<number>(0)
+	const contentRef = useRef<HTMLDivElement>(null)
+
+	useEffect(() => {
+		setIsCollapsed(reasoningBlockCollapsed)
+	}, [reasoningBlockCollapsed])
 
-	// Simple timer that runs while streaming
 	useEffect(() => {
 		if (isLast && isStreaming) {
 			const tick = () => setElapsed(Date.now() - startTimeRef.current)
@@ -36,21 +40,35 @@ export const ReasoningBlock = ({ content, isStreaming, isLast }: ReasoningBlockP
 	const seconds = Math.floor(elapsed / 1000)
 	const secondsLabel = t("chat:reasoning.seconds", { count: seconds })
 
+	const handleToggle = () => {
+		setIsCollapsed(!isCollapsed)
+	}
+
 	return (
-		<div>
-			<div className="flex items-center justify-between mb-2.5 pr-2">
+		<div className="group">
+			<div
+				className="flex items-center justify-between mb-2.5 pr-2 cursor-pointer select-none"
+				onClick={handleToggle}>
 				<div className="flex items-center gap-2">
 					<Lightbulb className="w-4" />
 					<span className="font-bold text-vscode-foreground">{t("chat:reasoning.thinking")}</span>
+					{elapsed > 0 && (
+						<span className="text-sm text-vscode-descriptionForeground mt-0.5">{secondsLabel}</span>
+					)}
+				</div>
+				<div className="flex items-center gap-2">
+					<ChevronUp
+						className={cn(
+							"w-4 transition-all opacity-0 group-hover:opacity-100",
+							isCollapsed && "-rotate-180",
+						)}
+					/>
 				</div>
-				{elapsed > 0 && (
-					<span className="text-sm text-vscode-descriptionForeground tabular-nums flex items-center gap-1">
-						{secondsLabel}
-					</span>
-				)}
 			</div>
-			{(content?.trim()?.length ?? 0) > 0 && (
-				<div className="border-l border-vscode-descriptionForeground/20 ml-2 pl-4 pb-1 text-vscode-descriptionForeground">
+			{(content?.trim()?.length ?? 0) > 0 && !isCollapsed && (
+				<div
+					ref={contentRef}
+					className="border-l border-vscode-descriptionForeground/20 ml-2 pl-4 pb-1 text-vscode-descriptionForeground">
 					<MarkdownBlock markdown={content} />
 				</div>
 			)}

+ 6 - 0
webview-ui/src/components/common/MarkdownBlock.tsx

@@ -146,6 +146,12 @@ const StyledMarkdown = styled.div`
 		}
 	}
 
+	h1 {
+		font-size: 1.65em;
+		font-weight: 700;
+		margin: 1.35em 0 0.5em;
+	}
+
 	h2 {
 		font-size: 1.35em;
 		font-weight: 500;

+ 14 - 0
webview-ui/src/components/settings/SettingsView.tsx

@@ -24,6 +24,7 @@ import {
 	MessageSquare,
 	LucideIcon,
 	SquareSlash,
+	Glasses,
 } from "lucide-react"
 
 import type { ProviderSettings, ExperimentId, TelemetrySetting } from "@roo-code/types"
@@ -66,6 +67,7 @@ import { About } from "./About"
 import { Section } from "./Section"
 import PromptsSettings from "./PromptsSettings"
 import { SlashCommandsSettings } from "./SlashCommandsSettings"
+import { UISettings } from "./UISettings"
 
 export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden"
 export const settingsTabList =
@@ -88,6 +90,7 @@ const sectionNames = [
 	"contextManagement",
 	"terminal",
 	"prompts",
+	"ui",
 	"experimental",
 	"language",
 	"about",
@@ -191,6 +194,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		includeTaskHistoryInEnhance,
 		openRouterImageApiKey,
 		openRouterImageGenerationSelectedModel,
+		reasoningBlockCollapsed,
 	} = cachedState
 
 	const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
@@ -364,6 +368,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 			vscode.postMessage({ type: "updateCondensingPrompt", text: customCondensingPrompt || "" })
 			vscode.postMessage({ type: "updateSupportPrompt", values: customSupportPrompts || {} })
 			vscode.postMessage({ type: "includeTaskHistoryInEnhance", bool: includeTaskHistoryInEnhance ?? true })
+			vscode.postMessage({ type: "setReasoningBlockCollapsed", bool: reasoningBlockCollapsed ?? true })
 			vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration })
 			vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting })
 			vscode.postMessage({ type: "profileThresholds", values: profileThresholds })
@@ -458,6 +463,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 			{ id: "contextManagement", icon: Database },
 			{ id: "terminal", icon: SquareTerminal },
 			{ id: "prompts", icon: MessageSquare },
+			{ id: "ui", icon: Glasses },
 			{ id: "experimental", icon: FlaskConical },
 			{ id: "language", icon: Globe },
 			{ id: "about", icon: Info },
@@ -757,6 +763,14 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 						/>
 					)}
 
+					{/* UI Section */}
+					{activeTab === "ui" && (
+						<UISettings
+							reasoningBlockCollapsed={reasoningBlockCollapsed ?? true}
+							setCachedStateField={setCachedStateField}
+						/>
+					)}
+
 					{/* Experimental Section */}
 					{activeTab === "experimental" && (
 						<ExperimentalSettings

+ 56 - 0
webview-ui/src/components/settings/UISettings.tsx

@@ -0,0 +1,56 @@
+import { HTMLAttributes } from "react"
+import { useAppTranslation } from "@/i18n/TranslationContext"
+import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
+import { Glasses } from "lucide-react"
+import { telemetryClient } from "@/utils/TelemetryClient"
+
+import { SetCachedStateField } from "./types"
+import { SectionHeader } from "./SectionHeader"
+import { Section } from "./Section"
+import { ExtensionStateContextType } from "@/context/ExtensionStateContext"
+
+interface UISettingsProps extends HTMLAttributes<HTMLDivElement> {
+	reasoningBlockCollapsed: boolean
+	setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
+}
+
+export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...props }: UISettingsProps) => {
+	const { t } = useAppTranslation()
+
+	const handleReasoningBlockCollapsedChange = (value: boolean) => {
+		setCachedStateField("reasoningBlockCollapsed", value)
+
+		// Track telemetry event
+		telemetryClient.capture("ui_settings_collapse_thinking_changed", {
+			enabled: value,
+		})
+	}
+
+	return (
+		<div {...props}>
+			<SectionHeader>
+				<div className="flex items-center gap-2">
+					<Glasses className="w-4" />
+					<div>{t("settings:sections.ui")}</div>
+				</div>
+			</SectionHeader>
+
+			<Section>
+				<div className="space-y-6">
+					{/* Collapse Thinking Messages Setting */}
+					<div className="flex flex-col gap-1">
+						<VSCodeCheckbox
+							checked={reasoningBlockCollapsed}
+							onChange={(e: any) => handleReasoningBlockCollapsedChange(e.target.checked)}
+							data-testid="collapse-thinking-checkbox">
+							<span className="font-medium">{t("settings:ui.collapseThinking.label")}</span>
+						</VSCodeCheckbox>
+						<div className="text-vscode-descriptionForeground text-sm ml-5 mt-1">
+							{t("settings:ui.collapseThinking.description")}
+						</div>
+					</div>
+				</div>
+			</Section>
+		</div>
+	)
+}

+ 43 - 0
webview-ui/src/components/settings/__tests__/UISettings.spec.tsx

@@ -0,0 +1,43 @@
+import { render, fireEvent, waitFor } from "@testing-library/react"
+import { describe, it, expect, vi } from "vitest"
+import { UISettings } from "../UISettings"
+
+describe("UISettings", () => {
+	const defaultProps = {
+		reasoningBlockCollapsed: false,
+		setCachedStateField: vi.fn(),
+	}
+
+	it("renders the collapse thinking checkbox", () => {
+		const { getByTestId } = render(<UISettings {...defaultProps} />)
+		const checkbox = getByTestId("collapse-thinking-checkbox")
+		expect(checkbox).toBeTruthy()
+	})
+
+	it("displays the correct initial state", () => {
+		const { getByTestId } = render(<UISettings {...defaultProps} reasoningBlockCollapsed={true} />)
+		const checkbox = getByTestId("collapse-thinking-checkbox") as HTMLInputElement
+		expect(checkbox.checked).toBe(true)
+	})
+
+	it("calls setCachedStateField when checkbox is toggled", async () => {
+		const setCachedStateField = vi.fn()
+		const { getByTestId } = render(<UISettings {...defaultProps} setCachedStateField={setCachedStateField} />)
+
+		const checkbox = getByTestId("collapse-thinking-checkbox")
+		fireEvent.click(checkbox)
+
+		await waitFor(() => {
+			expect(setCachedStateField).toHaveBeenCalledWith("reasoningBlockCollapsed", true)
+		})
+	})
+
+	it("updates checkbox state when prop changes", () => {
+		const { getByTestId, rerender } = render(<UISettings {...defaultProps} reasoningBlockCollapsed={false} />)
+		const checkbox = getByTestId("collapse-thinking-checkbox") as HTMLInputElement
+		expect(checkbox.checked).toBe(false)
+
+		rerender(<UISettings {...defaultProps} reasoningBlockCollapsed={true} />)
+		expect(checkbox.checked).toBe(true)
+	})
+})

+ 5 - 0
webview-ui/src/context/ExtensionStateContext.tsx

@@ -144,6 +144,7 @@ export interface ExtensionStateContextType extends ExtensionState {
 	terminalCompressProgressBar?: boolean
 	setTerminalCompressProgressBar: (value: boolean) => void
 	setHistoryPreviewCollapsed: (value: boolean) => void
+	setReasoningBlockCollapsed: (value: boolean) => void
 	autoCondenseContext: boolean
 	setAutoCondenseContext: (value: boolean) => void
 	autoCondenseContextPercent: number
@@ -240,6 +241,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		terminalZdotdir: false, // Default ZDOTDIR handling setting
 		terminalCompressProgressBar: true, // Default to compress progress bar output
 		historyPreviewCollapsed: false, // Initialize the new state (default to expanded)
+		reasoningBlockCollapsed: true, // Default to collapsed
 		cloudUserInfo: null,
 		cloudIsAuthenticated: false,
 		cloudOrganizations: [],
@@ -416,6 +418,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 
 	const contextValue: ExtensionStateContextType = {
 		...state,
+		reasoningBlockCollapsed: state.reasoningBlockCollapsed ?? true,
 		didHydrateState,
 		showWelcome,
 		theme,
@@ -532,6 +535,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 			}),
 		setHistoryPreviewCollapsed: (value) =>
 			setState((prevState) => ({ ...prevState, historyPreviewCollapsed: value })),
+		setReasoningBlockCollapsed: (value) =>
+			setState((prevState) => ({ ...prevState, reasoningBlockCollapsed: value })),
 		setHasOpenedModeSelector: (value) => setState((prevState) => ({ ...prevState, hasOpenedModeSelector: value })),
 		setAutoCondenseContext: (value) => setState((prevState) => ({ ...prevState, autoCondenseContext: value })),
 		setAutoCondenseContextPercent: (value) =>

+ 3 - 0
webview-ui/src/i18n/locales/ca/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Resposta",
 	"arguments": "Arguments",
+	"text": {
+		"rooSaid": "En Roo ha dit"
+	},
 	"feedback": {
 		"youSaid": "Has dit"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/ca/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Comandes de barra",
 		"prompts": "Indicacions",
+		"ui": "UI",
 		"experimental": "Experimental",
 		"language": "Idioma",
 		"about": "Sobre Roo Code"
@@ -879,5 +880,11 @@
 			"output": "Sortida",
 			"cacheReads": "Lectures de memòria cau"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/de/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Antwort",
 	"arguments": "Argumente",
+	"text": {
+		"rooSaid": "Roo hat gesagt"
+	},
 	"feedback": {
 		"youSaid": "Du hast gesagt"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/de/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Slash-Befehle",
 		"prompts": "Eingabeaufforderungen",
+		"ui": "UI",
 		"experimental": "Experimentell",
 		"language": "Sprache",
 		"about": "Über Roo Code"
@@ -879,5 +880,11 @@
 			"output": "Ausgabe",
 			"cacheReads": "Cache-Lesevorgänge"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "Gedankenblöcke standardmäßig ausblenden",
+			"description": "Wenn aktiviert, werden Gedankenblöcke standardmäßig ausgeblendet, bis du mit ihnen interagierst"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/en/chat.json

@@ -239,6 +239,9 @@
 	},
 	"response": "Response",
 	"arguments": "Arguments",
+	"text": {
+		"rooSaid": "Roo said"
+	},
 	"feedback": {
 		"youSaid": "You said"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/en/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Slash Commands",
 		"prompts": "Prompts",
+		"ui": "UI",
 		"experimental": "Experimental",
 		"language": "Language",
 		"about": "About Roo Code"
@@ -37,6 +38,12 @@
 	"slashCommands": {
 		"description": "Manage your slash commands to quickly execute custom workflows and actions. <DocsLink>Learn more</DocsLink>"
 	},
+	"ui": {
+		"collapseThinking": {
+			"label": "Collapse Thinking messages by default",
+			"description": "When enabled, thinking blocks will be collapsed by default until you interact with them"
+		}
+	},
 	"prompts": {
 		"description": "Configure support prompts that are used for quick actions like enhancing prompts, explaining code, and fixing issues. These prompts help Roo provide better assistance for common development tasks."
 	},

+ 3 - 0
webview-ui/src/i18n/locales/es/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Respuesta",
 	"arguments": "Argumentos",
+	"text": {
+		"rooSaid": "Roo ha dicho"
+	},
 	"feedback": {
 		"youSaid": "Has dicho"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/es/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Comandos de Barra",
 		"prompts": "Indicaciones",
+		"ui": "UI",
 		"experimental": "Experimental",
 		"language": "Idioma",
 		"about": "Acerca de Roo Code"
@@ -879,5 +880,11 @@
 			"output": "Salida",
 			"cacheReads": "Lecturas de caché"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/fr/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Réponse",
 	"arguments": "Arguments",
+	"text": {
+		"rooSaid": "Roo a dit"
+	},
 	"feedback": {
 		"youSaid": "Tu as dit"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/fr/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Commandes Slash",
 		"prompts": "Invites",
+		"ui": "UI",
 		"experimental": "Expérimental",
 		"language": "Langue",
 		"about": "À propos de Roo Code"
@@ -879,5 +880,11 @@
 			"output": "Sortie",
 			"cacheReads": "Lectures du cache"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/hi/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "प्रतिक्रिया",
 	"arguments": "आर्ग्युमेंट्स",
+	"text": {
+		"rooSaid": "रू ने कहा"
+	},
 	"feedback": {
 		"youSaid": "आपने कहा"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/hi/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "टर्मिनल",
 		"slashCommands": "स्लैश कमांड",
 		"prompts": "प्रॉम्प्ट्स",
+		"ui": "UI",
 		"experimental": "प्रायोगिक",
 		"language": "भाषा",
 		"about": "परिचय"
@@ -880,5 +881,11 @@
 			"output": "आउटपुट",
 			"cacheReads": "कैश रीड"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "सोच संदेशों को डिफ़ॉल्ट रूप से संक्षिप्त करें",
+			"description": "सक्षम होने पर, सोच ब्लॉक आपके द्वारा उनके साथ इंटरैक्ट करने तक डिफ़ॉल्ट रूप से संक्षिप्त रहेंगे"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/id/chat.json

@@ -242,6 +242,9 @@
 	},
 	"response": "Respons",
 	"arguments": "Argumen",
+	"text": {
+		"rooSaid": "Roo berkata"
+	},
 	"feedback": {
 		"youSaid": "Anda bilang"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/id/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Perintah Slash",
 		"prompts": "Prompt",
+		"ui": "UI",
 		"experimental": "Eksperimental",
 		"language": "Bahasa",
 		"about": "Tentang Roo Code"
@@ -909,5 +910,11 @@
 			"output": "Output",
 			"cacheReads": "Pembacaan cache"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "Ciutkan pesan Berpikir secara default",
+			"description": "Jika diaktifkan, blok berpikir akan diciutkan secara default sampai Anda berinteraksi dengannya"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/it/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Risposta",
 	"arguments": "Argomenti",
+	"text": {
+		"rooSaid": "Roo ha detto"
+	},
 	"feedback": {
 		"youSaid": "Hai detto"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/it/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Comandi Slash",
 		"prompts": "Prompt",
+		"ui": "UI",
 		"experimental": "Sperimentale",
 		"language": "Lingua",
 		"about": "Informazioni su Roo Code"
@@ -880,5 +881,11 @@
 			"output": "Output",
 			"cacheReads": "Letture cache"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/ja/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "応答",
 	"arguments": "引数",
+	"text": {
+		"rooSaid": "Rooの発言"
+	},
 	"feedback": {
 		"youSaid": "あなたの発言"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/ja/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "ターミナル",
 		"slashCommands": "スラッシュコマンド",
 		"prompts": "プロンプト",
+		"ui": "UI",
 		"experimental": "実験的",
 		"language": "言語",
 		"about": "Roo Codeについて"
@@ -880,5 +881,11 @@
 			"output": "出力",
 			"cacheReads": "キャッシュ読み取り"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "デフォルトで思考メッセージを折りたたむ",
+			"description": "有効にすると、操作するまで思考ブロックがデフォルトで折りたたまれます"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/ko/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "응답",
 	"arguments": "인수",
+	"text": {
+		"rooSaid": "루가 말했다"
+	},
 	"feedback": {
 		"youSaid": "당신은 말했다"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/ko/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "터미널",
 		"slashCommands": "슬래시 명령",
 		"prompts": "프롬프트",
+		"ui": "UI",
 		"experimental": "실험적",
 		"language": "언어",
 		"about": "Roo Code 정보"
@@ -880,5 +881,11 @@
 			"output": "출력",
 			"cacheReads": "캐시 읽기"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "기본적으로 생각 메시지 접기",
+			"description": "활성화하면 상호 작용할 때까지 생각 블록이 기본적으로 접힙니다"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/nl/chat.json

@@ -215,6 +215,9 @@
 	},
 	"response": "Antwoord",
 	"arguments": "Argumenten",
+	"text": {
+		"rooSaid": "Roo zei"
+	},
 	"feedback": {
 		"youSaid": "Jij zei"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/nl/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Slash-opdrachten",
 		"prompts": "Prompts",
+		"ui": "UI",
 		"experimental": "Experimenteel",
 		"language": "Taal",
 		"about": "Over Roo Code"
@@ -880,5 +881,11 @@
 			"output": "Uitvoer",
 			"cacheReads": "Cache leest"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "Denkberichten standaard samenvouwen",
+			"description": "Indien ingeschakeld, worden denkblokken standaard samengevouwen totdat je ermee interageert"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/pl/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Odpowiedź",
 	"arguments": "Argumenty",
+	"text": {
+		"rooSaid": "Roo powiedział"
+	},
 	"feedback": {
 		"youSaid": "Powiedziałeś"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/pl/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Polecenia Slash",
 		"prompts": "Podpowiedzi",
+		"ui": "UI",
 		"experimental": "Eksperymentalne",
 		"language": "Język",
 		"about": "O Roo Code"
@@ -880,5 +881,11 @@
 			"output": "Wyjście",
 			"cacheReads": "Odczyty z pamięci podręcznej"
 		}
+	},
+	"ui": {
+		"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ę"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/pt-BR/chat.json

@@ -220,6 +220,9 @@
 	},
 	"response": "Resposta",
 	"arguments": "Argumentos",
+	"text": {
+		"rooSaid": "Roo disse"
+	},
 	"feedback": {
 		"youSaid": "Você disse"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Comandos de Barra",
 		"prompts": "Prompts",
+		"ui": "UI",
 		"experimental": "Experimental",
 		"language": "Idioma",
 		"about": "Sobre"
@@ -880,5 +881,11 @@
 			"output": "Saída",
 			"cacheReads": "Leituras de cache"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/ru/chat.json

@@ -216,6 +216,9 @@
 	},
 	"response": "Ответ",
 	"arguments": "Аргументы",
+	"text": {
+		"rooSaid": "Ру сказал"
+	},
 	"feedback": {
 		"youSaid": "Вы сказали"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/ru/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Терминал",
 		"slashCommands": "Слэш-команды",
 		"prompts": "Промпты",
+		"ui": "UI",
 		"experimental": "Экспериментальное",
 		"language": "Язык",
 		"about": "О Roo Code"
@@ -880,5 +881,11 @@
 			"output": "Выход",
 			"cacheReads": "Чтения из кэша"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "Сворачивать сообщения о размышлениях по умолчанию",
+			"description": "Если включено, блоки с размышлениями будут свернуты по умолчанию, пока вы не начнете с ними взаимодействовать"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/tr/chat.json

@@ -221,6 +221,9 @@
 	},
 	"response": "Yanıt",
 	"arguments": "Argümanlar",
+	"text": {
+		"rooSaid": "Roo dedi"
+	},
 	"feedback": {
 		"youSaid": "Dediniz ki"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/tr/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Eğik Çizgi Komutları",
 		"prompts": "Promptlar",
+		"ui": "UI",
 		"experimental": "Deneysel",
 		"language": "Dil",
 		"about": "Roo Code Hakkında"
@@ -880,5 +881,11 @@
 			"output": "Çıkış",
 			"cacheReads": "Önbellek okumaları"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/vi/chat.json

@@ -221,6 +221,9 @@
 	},
 	"response": "Phản hồi",
 	"arguments": "Tham số",
+	"text": {
+		"rooSaid": "Roo đã nói"
+	},
 	"feedback": {
 		"youSaid": "Bạn đã nói"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/vi/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "Terminal",
 		"slashCommands": "Lệnh Gạch Chéo",
 		"prompts": "Lời nhắc",
+		"ui": "UI",
 		"experimental": "Thử nghiệm",
 		"language": "Ngôn ngữ",
 		"about": "Giới thiệu"
@@ -880,5 +881,11 @@
 			"output": "Đầu ra",
 			"cacheReads": "Lượt đọc bộ nhớ đệm"
 		}
+	},
+	"ui": {
+		"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"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/zh-CN/chat.json

@@ -221,6 +221,9 @@
 	},
 	"response": "响应",
 	"arguments": "参数",
+	"text": {
+		"rooSaid": "Roo 说"
+	},
 	"feedback": {
 		"youSaid": "你说"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "终端",
 		"slashCommands": "斜杠命令",
 		"prompts": "提示词",
+		"ui": "UI",
 		"experimental": "实验性",
 		"language": "语言",
 		"about": "关于 Roo Code"
@@ -880,5 +881,11 @@
 			"output": "输出",
 			"cacheReads": "缓存读取"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "默认折叠“思考”消息",
+			"description": "启用后,“思考”块将默认折叠,直到您与其交互"
+		}
 	}
 }

+ 3 - 0
webview-ui/src/i18n/locales/zh-TW/chat.json

@@ -240,6 +240,9 @@
 	},
 	"response": "回應",
 	"arguments": "參數",
+	"text": {
+		"rooSaid": "Roo 說"
+	},
 	"feedback": {
 		"youSaid": "您說"
 	},

+ 7 - 0
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -30,6 +30,7 @@
 		"terminal": "終端機",
 		"slashCommands": "斜線命令",
 		"prompts": "提示詞",
+		"ui": "UI",
 		"experimental": "實驗性",
 		"language": "語言",
 		"about": "關於 Roo Code"
@@ -880,5 +881,11 @@
 			"output": "輸出",
 			"cacheReads": "快取讀取"
 		}
+	},
+	"ui": {
+		"collapseThinking": {
+			"label": "預設折疊“思考”訊息",
+			"description": "啟用後,“思考”塊將預設折疊,直到您與其互動"
+		}
 	}
 }