Roo-Code/webview-ui/src/components/settings/TerminalSettings.tsx
Roo Code 5b6a5a69b0 feat: add built-in terminal output filtering to reduce LLM token usage
Introduces a TerminalOutputFilter module with command-aware output
filtering. Built-in filters for test runners, git, package managers,
and build tools strip noise while preserving actionable information.

New setting terminalOutputFilterEnabled (default: true) with toggle
in Terminal Settings UI.

Closes #11459
2026-02-14 13:07:41 +00:00

429 lines
15 KiB
TypeScript

import { HTMLAttributes, useState, useCallback } from "react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { vscode } from "@/utils/vscode"
import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { Trans } from "react-i18next"
import { buildDocLink } from "@src/utils/docLinks"
import { useEvent, useMount } from "react-use"
import { type ExtensionMessage, type TerminalOutputPreviewSize } from "@roo-code/types"
import { cn } from "@/lib/utils"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui"
import { SetCachedStateField } from "./types"
import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"
import { SearchableSetting } from "./SearchableSetting"
type TerminalSettingsProps = HTMLAttributes<HTMLDivElement> & {
terminalOutputPreviewSize?: TerminalOutputPreviewSize
terminalOutputFilterEnabled?: boolean
terminalShellIntegrationTimeout?: number
terminalShellIntegrationDisabled?: boolean
terminalCommandDelay?: number
terminalPowershellCounter?: boolean
terminalZshClearEolMark?: boolean
terminalZshOhMy?: boolean
terminalZshP10k?: boolean
terminalZdotdir?: boolean
setCachedStateField: SetCachedStateField<
| "terminalOutputPreviewSize"
| "terminalOutputFilterEnabled"
| "terminalShellIntegrationTimeout"
| "terminalShellIntegrationDisabled"
| "terminalCommandDelay"
| "terminalPowershellCounter"
| "terminalZshClearEolMark"
| "terminalZshOhMy"
| "terminalZshP10k"
| "terminalZdotdir"
>
}
export const TerminalSettings = ({
terminalOutputPreviewSize,
terminalOutputFilterEnabled,
terminalShellIntegrationTimeout,
terminalShellIntegrationDisabled,
terminalCommandDelay,
terminalPowershellCounter,
terminalZshClearEolMark,
terminalZshOhMy,
terminalZshP10k,
terminalZdotdir,
setCachedStateField,
className,
...props
}: TerminalSettingsProps) => {
const { t } = useAppTranslation()
const [inheritEnv, setInheritEnv] = useState<boolean>(true)
useMount(() => vscode.postMessage({ type: "getVSCodeSetting", setting: "terminal.integrated.inheritEnv" }))
const onMessage = useCallback((event: MessageEvent) => {
const message: ExtensionMessage = event.data
switch (message.type) {
case "vsCodeSetting":
switch (message.setting) {
case "terminal.integrated.inheritEnv":
setInheritEnv(message.value ?? true)
break
default:
break
}
break
default:
break
}
}, [])
useEvent("message", onMessage)
return (
<div className={cn("flex flex-col", className)} {...props}>
<SectionHeader>{t("settings:sections.terminal")}</SectionHeader>
<Section>
{/* Basic Settings */}
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2 font-bold">
<span className="codicon codicon-settings-gear" />
<div>{t("settings:terminal.basic.label")}</div>
</div>
</div>
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
<SearchableSetting
settingId="terminal-output-filter-enabled"
section="terminal"
label={t("settings:terminal.outputFilterEnabled.label")}>
<VSCodeCheckbox
checked={terminalOutputFilterEnabled !== false}
onChange={(e: any) =>
setCachedStateField("terminalOutputFilterEnabled", e.target.checked)
}>
<span className="font-medium">{t("settings:terminal.outputFilterEnabled.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
{t("settings:terminal.outputFilterEnabled.description")}
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-output-preview-size"
section="terminal"
label={t("settings:terminal.outputPreviewSize.label")}>
<label className="block font-medium mb-1">
{t("settings:terminal.outputPreviewSize.label")}
</label>
<Select
value={terminalOutputPreviewSize || "medium"}
onValueChange={(value) =>
setCachedStateField("terminalOutputPreviewSize", value as TerminalOutputPreviewSize)
}>
<SelectTrigger className="w-full" data-testid="terminal-output-preview-size-dropdown">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
<SelectItem value="small">
{t("settings:terminal.outputPreviewSize.options.small")}
</SelectItem>
<SelectItem value="medium">
{t("settings:terminal.outputPreviewSize.options.medium")}
</SelectItem>
<SelectItem value="large">
{t("settings:terminal.outputPreviewSize.options.large")}
</SelectItem>
</SelectContent>
</Select>
<div className="text-vscode-descriptionForeground text-sm mt-1">
{t("settings:terminal.outputPreviewSize.description")}
</div>
</SearchableSetting>
</div>
</div>
{/* Advanced Settings */}
<div className="flex flex-col gap-3">
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2 font-bold">
<span className="codicon codicon-tools" />
<div>{t("settings:terminal.advanced.label")}</div>
</div>
<div className="text-vscode-descriptionForeground">
{t("settings:terminal.advanced.description")}
</div>
</div>
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
<SearchableSetting
settingId="terminal-shell-integration-disabled"
section="terminal"
label={t("settings:terminal.shellIntegrationDisabled.label")}>
<VSCodeCheckbox
checked={terminalShellIntegrationDisabled ?? true}
onChange={(e: any) =>
setCachedStateField("terminalShellIntegrationDisabled", e.target.checked)
}>
<span className="font-medium">
{t("settings:terminal.shellIntegrationDisabled.label")}
</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.shellIntegrationDisabled.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#use-inline-terminal-recommended",
"settings_terminal_shell_integration_disabled",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
{!terminalShellIntegrationDisabled && (
<>
<SearchableSetting
settingId="terminal-inherit-env"
section="terminal"
label={t("settings:terminal.inheritEnv.label")}>
<VSCodeCheckbox
checked={inheritEnv}
onChange={(e: any) => {
setInheritEnv(e.target.checked)
vscode.postMessage({
type: "updateVSCodeSetting",
setting: "terminal.integrated.inheritEnv",
value: e.target.checked,
})
}}
data-testid="terminal-inherit-env-checkbox">
<span className="font-medium">{t("settings:terminal.inheritEnv.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.inheritEnv.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#inherit-environment-variables",
"settings_terminal_inherit_env",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-shell-integration-timeout"
section="terminal"
label={t("settings:terminal.shellIntegrationTimeout.label")}>
<label className="block font-medium mb-1">
{t("settings:terminal.shellIntegrationTimeout.label")}
</label>
<div className="flex items-center gap-2">
<Slider
min={1000}
max={60000}
step={1000}
value={[terminalShellIntegrationTimeout ?? 5000]}
onValueChange={([value]) =>
setCachedStateField(
"terminalShellIntegrationTimeout",
Math.min(60000, Math.max(1000, value)),
)
}
/>
<span className="w-10">
{(terminalShellIntegrationTimeout ?? 5000) / 1000}s
</span>
</div>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.shellIntegrationTimeout.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#terminal-shell-integration-timeout",
"settings_terminal_shell_integration_timeout",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-command-delay"
section="terminal"
label={t("settings:terminal.commandDelay.label")}>
<label className="block font-medium mb-1">
{t("settings:terminal.commandDelay.label")}
</label>
<div className="flex items-center gap-2">
<Slider
min={0}
max={1000}
step={10}
value={[terminalCommandDelay ?? 0]}
onValueChange={([value]) =>
setCachedStateField(
"terminalCommandDelay",
Math.min(1000, Math.max(0, value)),
)
}
/>
<span className="w-10">{terminalCommandDelay ?? 50}ms</span>
</div>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.commandDelay.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#terminal-command-delay",
"settings_terminal_command_delay",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-powershell-counter"
section="terminal"
label={t("settings:terminal.powershellCounter.label")}>
<VSCodeCheckbox
checked={terminalPowershellCounter ?? false}
onChange={(e: any) =>
setCachedStateField("terminalPowershellCounter", e.target.checked)
}
data-testid="terminal-powershell-counter-checkbox">
<span className="font-medium">
{t("settings:terminal.powershellCounter.label")}
</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.powershellCounter.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#enable-powershell-counter-workaround",
"settings_terminal_powershell_counter",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-zsh-clear-eol-mark"
section="terminal"
label={t("settings:terminal.zshClearEolMark.label")}>
<VSCodeCheckbox
checked={terminalZshClearEolMark ?? true}
onChange={(e: any) =>
setCachedStateField("terminalZshClearEolMark", e.target.checked)
}
data-testid="terminal-zsh-clear-eol-mark-checkbox">
<span className="font-medium">
{t("settings:terminal.zshClearEolMark.label")}
</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.zshClearEolMark.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#clear-zsh-eol-mark",
"settings_terminal_zsh_clear_eol_mark",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-zsh-oh-my"
section="terminal"
label={t("settings:terminal.zshOhMy.label")}>
<VSCodeCheckbox
checked={terminalZshOhMy ?? false}
onChange={(e: any) => setCachedStateField("terminalZshOhMy", e.target.checked)}
data-testid="terminal-zsh-oh-my-checkbox">
<span className="font-medium">{t("settings:terminal.zshOhMy.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.zshOhMy.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#enable-oh-my-zsh-integration",
"settings_terminal_zsh_oh_my",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-zsh-p10k"
section="terminal"
label={t("settings:terminal.zshP10k.label")}>
<VSCodeCheckbox
checked={terminalZshP10k ?? false}
onChange={(e: any) => setCachedStateField("terminalZshP10k", e.target.checked)}
data-testid="terminal-zsh-p10k-checkbox">
<span className="font-medium">{t("settings:terminal.zshP10k.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.zshP10k.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#enable-powerlevel10k-integration",
"settings_terminal_zsh_p10k",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
<SearchableSetting
settingId="terminal-zdotdir"
section="terminal"
label={t("settings:terminal.zdotdir.label")}>
<VSCodeCheckbox
checked={terminalZdotdir ?? false}
onChange={(e: any) => setCachedStateField("terminalZdotdir", e.target.checked)}
data-testid="terminal-zdotdir-checkbox">
<span className="font-medium">{t("settings:terminal.zdotdir.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
<Trans i18nKey="settings:terminal.zdotdir.description">
<VSCodeLink
href={buildDocLink(
"features/shell-integration#enable-zdotdir-handling",
"settings_terminal_zdotdir",
)}
style={{ display: "inline" }}>
{" "}
</VSCodeLink>
</Trans>
</div>
</SearchableSetting>
</>
)}
</div>
</div>
</Section>
</div>
)
}