Skip to content

Commit 96f5e09

Browse files
Merge branch 'main' into fix/issue-457-edit-unsuccessfull
2 parents 0340931 + 25545e9 commit 96f5e09

23 files changed

Lines changed: 248 additions & 288 deletions

apps/vscode-e2e/fixtures/task-hello-world.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
"fixtures": [
33
{
44
"match": {
5-
"userMessage": "Hello world, what is your name? Respond with 'My name is ...'"
5+
"userMessage": "TASK_HELLO_WORLD_SMOKE"
66
},
77
"response": {
88
"toolCalls": [
99
{
1010
"name": "attempt_completion",
11-
"arguments": "{\"result\":\"My name is Zoo! I'm your AI coding assistant, here to help you with development tasks.\"}",
11+
"arguments": "{\"result\":\"My name is Zoo.\"}",
1212
"id": "call_task_hello_world_001"
1313
}
1414
]

apps/vscode-e2e/src/fixtures/subtasks.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,13 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
177177
},
178178
})
179179

180+
// Issue #561: parent prompt embeds SAME_CHILD_MARKER verbatim, so parent-resume turns
181+
// also match a bare substring check. Exclude the parent marker to let them fall through.
180182
mock.addFixture({
181183
match: {
182-
userMessage: new RegExp(SUBTASK_XPROFILE_SAME_CHILD_MARKER),
184+
predicate: (req) =>
185+
requestContains(req, [SUBTASK_XPROFILE_SAME_CHILD_MARKER]) &&
186+
!requestContains(req, [SUBTASK_XPROFILE_PARENT_MARKER]),
183187
},
184188
response: {
185189
toolCalls: [
@@ -212,6 +216,8 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
212216
},
213217
})
214218

219+
// Safe as bare regex: DIFFERENT_CHILD_MARKER is NOT embedded in SUBTASK_XPROFILE_PARENT_PROMPT,
220+
// so parent-resume turns never contain it. If that ever changes, add an exclusion predicate.
215221
mock.addFixture({
216222
match: {
217223
userMessage: new RegExp(SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER),

apps/vscode-e2e/src/suite/task.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ suite("Roo Code Task", function () {
2121

2222
const taskId = await api.startNewTask({
2323
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
24-
text: "Hello world, what is your name? Respond with 'My name is ...'",
24+
text: "TASK_HELLO_WORLD_SMOKE: what is your name?",
2525
})
2626

2727
await waitUntilCompleted({ api, taskId })

webview-ui/src/components/settings/TerminalSettings.tsx

Lines changed: 76 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { HTMLAttributes, useState, useCallback, useEffect, useId } from "react"
1+
import { HTMLAttributes, useState, useCallback, useEffect } from "react"
22
import { useAppTranslation } from "@/i18n/TranslationContext"
33
import { vscode } from "@/utils/vscode"
4-
import { VSCodeCheckbox, VSCodeLink, VSCodeButton } from "@vscode/webview-ui-toolkit/react"
4+
import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
55
import { Trans } from "react-i18next"
66
import { buildDocLink } from "@src/utils/docLinks"
77
import { useEvent, useMount } from "react-use"
8+
import { Terminal } from "lucide-react"
89

910
import { type ExtensionMessage, type TerminalOutputPreviewSize } from "@roo-code/types"
1011

1112
import { cn } from "@/lib/utils"
12-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui"
13+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider, Button } from "@/components/ui"
1314

1415
import { SetCachedStateField } from "./types"
1516
import { SectionHeader } from "./SectionHeader"
@@ -44,7 +45,7 @@ type TerminalSettingsProps = HTMLAttributes<HTMLDivElement> & {
4445

4546
// Sentinel value that maps to `undefined` (use VS Code's default shell).
4647
// The Select component cannot accept empty-string item values.
47-
const DEFAULT_PROFILE_VALUE = "__default__"
48+
export const DEFAULT_PROFILE_VALUE = "__zoo_code_follow_vscode_sentinel__"
4849

4950
export const TerminalSettings = ({
5051
terminalOutputPreviewSize,
@@ -67,10 +68,6 @@ export const TerminalSettings = ({
6768
const [inheritEnv, setInheritEnv] = useState<boolean>(true)
6869
const [profileNames, setProfileNames] = useState<string[]>([])
6970
const [isProfilesLoaded, setIsProfilesLoaded] = useState(false)
70-
const profileModeId = useId()
71-
const defaultProfileId = `${profileModeId}-default`
72-
const overrideProfileId = `${profileModeId}-override`
73-
const isProfileOverrideSelected = !!terminalProfile && (!isProfilesLoaded || profileNames.includes(terminalProfile))
7471
const isVSCodeTerminalEnabled = terminalShellIntegrationDisabled === false
7572

7673
useMount(() => {
@@ -166,111 +163,7 @@ export const TerminalSettings = ({
166163
</div>
167164
</div>
168165
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
169-
{/* Profile override — only applies when VS Code integrated terminal is active
170-
(shell integration enabled). Hidden in Execa/inline mode since getProfileShell()
171-
is not wired there. */}
172-
{isVSCodeTerminalEnabled && (
173-
<SearchableSetting
174-
settingId="terminal-profile"
175-
section="terminal"
176-
label={t("settings:terminal.profile.label")}>
177-
<label className="block font-medium mb-1">{t("settings:terminal.profile.label")}</label>
178-
179-
{/* Level 1: Default (recommended) */}
180-
<div className="flex items-center gap-2 mb-2">
181-
<input
182-
type="radio"
183-
id={defaultProfileId}
184-
name={profileModeId}
185-
checked={!isProfileOverrideSelected}
186-
onChange={() => setCachedStateField("terminalProfile", undefined)}
187-
data-testid="terminal-profile-default-radio"
188-
/>
189-
<label htmlFor={defaultProfileId} className="cursor-pointer">
190-
{t("settings:terminal.profile.default")}
191-
</label>
192-
<VSCodeButton
193-
appearance="secondary"
194-
onClick={() => {
195-
onTerminalProfilePickerOpened?.()
196-
vscode.postMessage({ type: "openTerminalProfilePicker" })
197-
}}
198-
data-testid="terminal-profile-configure-button">
199-
{t("settings:terminal.profile.configureButton")}
200-
</VSCodeButton>
201-
</div>
202-
203-
{/* Level 2: Override */}
204-
<div className="flex items-center gap-2 mb-2">
205-
<input
206-
type="radio"
207-
id={overrideProfileId}
208-
name={profileModeId}
209-
checked={isProfileOverrideSelected}
210-
disabled={profileNames.length === 0}
211-
onChange={() => {
212-
if (!terminalProfile && profileNames.length > 0) {
213-
setCachedStateField("terminalProfile", profileNames[0])
214-
}
215-
}}
216-
data-testid="terminal-profile-override-radio"
217-
/>
218-
<label
219-
htmlFor={overrideProfileId}
220-
className={
221-
profileNames.length === 0
222-
? "cursor-not-allowed text-vscode-disabledForeground"
223-
: "cursor-pointer"
224-
}>
225-
{t("settings:terminal.profile.overrideLabel")}
226-
</label>
227-
{profileNames.length === 0 && (
228-
<span
229-
className="text-vscode-descriptionForeground text-xs"
230-
data-testid="terminal-profile-no-profiles-hint">
231-
{t("settings:terminal.profile.noProfiles")}
232-
</span>
233-
)}
234-
</div>
235-
236-
{isProfileOverrideSelected && profileNames.length > 0 && (
237-
<Select
238-
value={terminalProfile || DEFAULT_PROFILE_VALUE}
239-
data-testid="terminal-profile-dropdown"
240-
onValueChange={(value) =>
241-
setCachedStateField(
242-
"terminalProfile",
243-
value === DEFAULT_PROFILE_VALUE ? undefined : value,
244-
)
245-
}>
246-
<SelectTrigger className="w-full ml-6">
247-
<SelectValue placeholder={t("settings:common.select")} />
248-
</SelectTrigger>
249-
<SelectContent>
250-
{profileNames.map((name) => (
251-
<SelectItem key={name} value={name}>
252-
{name}
253-
</SelectItem>
254-
))}
255-
</SelectContent>
256-
</Select>
257-
)}
258-
259-
<div className="text-vscode-descriptionForeground text-sm mt-1">
260-
<Trans i18nKey="settings:terminal.profile.description">
261-
<VSCodeLink
262-
href={buildDocLink(
263-
"features/shell-integration",
264-
"settings_terminal_profile",
265-
)}
266-
style={{ display: "inline" }}>
267-
{" "}
268-
</VSCodeLink>
269-
</Trans>
270-
</div>
271-
</SearchableSetting>
272-
)}
273-
166+
{/* "Use Inline Terminal" checkbox — ALWAYS at the top */}
274167
<SearchableSetting
275168
settingId="terminal-shell-integration-disabled"
276169
section="terminal"
@@ -300,6 +193,76 @@ export const TerminalSettings = ({
300193

301194
{isVSCodeTerminalEnabled && (
302195
<>
196+
{/* Profile override — unified dropdown, now below checkbox */}
197+
<SearchableSetting
198+
settingId="terminal-profile"
199+
section="terminal"
200+
label={t("settings:terminal.profile.label")}>
201+
<label className="block font-medium mb-1">
202+
{t("settings:terminal.profile.label")}
203+
</label>
204+
205+
<Select
206+
value={terminalProfile ?? DEFAULT_PROFILE_VALUE}
207+
onValueChange={(value) =>
208+
setCachedStateField(
209+
"terminalProfile",
210+
value === DEFAULT_PROFILE_VALUE ? undefined : value,
211+
)
212+
}>
213+
<SelectTrigger className="w-full" data-testid="terminal-profile-dropdown">
214+
<SelectValue placeholder={t("settings:common.select")} />
215+
</SelectTrigger>
216+
<SelectContent>
217+
<SelectItem value={DEFAULT_PROFILE_VALUE}>
218+
{t("settings:terminal.profile.followVscode")}
219+
</SelectItem>
220+
{profileNames.map((name) => (
221+
<SelectItem key={name} value={name}>
222+
{name}
223+
</SelectItem>
224+
))}
225+
</SelectContent>
226+
</Select>
227+
228+
{!terminalProfile && (
229+
<div className="mt-2 flex flex-col">
230+
<Button
231+
variant="secondary"
232+
className="py-1"
233+
onClick={() => {
234+
onTerminalProfilePickerOpened?.()
235+
vscode.postMessage({ type: "openTerminalProfilePicker" })
236+
}}
237+
data-testid="terminal-profile-configure-button">
238+
<Terminal />
239+
{t("settings:terminal.profile.configureButton")}
240+
</Button>
241+
</div>
242+
)}
243+
244+
{isProfilesLoaded && profileNames.length === 0 && (
245+
<div
246+
className="text-vscode-descriptionForeground text-xs mt-1"
247+
data-testid="terminal-profile-no-profiles-hint">
248+
{t("settings:terminal.profile.noProfiles")}
249+
</div>
250+
)}
251+
252+
<div className="text-vscode-descriptionForeground text-sm mt-1">
253+
<Trans i18nKey="settings:terminal.profile.description">
254+
<VSCodeLink
255+
href={buildDocLink(
256+
"features/shell-integration",
257+
"settings_terminal_profile",
258+
)}
259+
style={{ display: "inline" }}>
260+
{" "}
261+
</VSCodeLink>
262+
</Trans>
263+
</div>
264+
</SearchableSetting>
265+
303266
<SearchableSetting
304267
settingId="terminal-inherit-env"
305268
section="terminal"

0 commit comments

Comments
 (0)