Skip to content

Commit 49283d9

Browse files
committed
refactor(terminal): address review feedback on profile picker
1 parent d7e3f70 commit 49283d9

4 files changed

Lines changed: 55 additions & 15 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
397397
terminalZshOhMy,
398398
terminalZshP10k,
399399
terminalZdotdir,
400-
terminalProfile,
400+
terminalProfile: terminalProfile ?? "", // "" clears a saved profile; undefined is dropped by JSON.stringify
401401
terminalOutputPreviewSize: terminalOutputPreviewSize ?? "medium",
402402
mcpEnabled,
403403
maxOpenTabsContext: Math.min(Math.max(0, maxOpenTabsContext ?? 20), 500),

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

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HTMLAttributes, useState, useCallback } from "react"
1+
import { HTMLAttributes, useState, useCallback, useEffect, useId } from "react"
22
import { useAppTranslation } from "@/i18n/TranslationContext"
33
import { vscode } from "@/utils/vscode"
44
import { VSCodeCheckbox, VSCodeLink, VSCodeButton } from "@vscode/webview-ui-toolkit/react"
@@ -66,6 +66,11 @@ export const TerminalSettings = ({
6666

6767
const [inheritEnv, setInheritEnv] = useState<boolean>(true)
6868
const [profileNames, setProfileNames] = useState<string[]>([])
69+
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))
6974

7075
useMount(() => {
7176
vscode.postMessage({ type: "getVSCodeSetting", setting: "terminal.integrated.inheritEnv" })
@@ -85,6 +90,7 @@ export const TerminalSettings = ({
8590
break
8691
case "terminalProfiles":
8792
setProfileNames(message.profiles ?? [])
93+
setIsProfilesLoaded(true)
8894
break
8995
default:
9096
break
@@ -93,6 +99,12 @@ export const TerminalSettings = ({
9399

94100
useEvent("message", onMessage)
95101

102+
useEffect(() => {
103+
if (isProfilesLoaded && terminalProfile && !profileNames.includes(terminalProfile)) {
104+
setCachedStateField("terminalProfile", undefined)
105+
}
106+
}, [isProfilesLoaded, profileNames, setCachedStateField, terminalProfile])
107+
96108
return (
97109
<div className={cn("flex flex-col", className)} {...props}>
98110
<SectionHeader>{t("settings:sections.terminal")}</SectionHeader>
@@ -167,13 +179,13 @@ export const TerminalSettings = ({
167179
<div className="flex items-center gap-2 mb-2">
168180
<input
169181
type="radio"
170-
id="terminal-profile-default"
171-
name="terminal-profile-mode"
172-
checked={!terminalProfile}
182+
id={defaultProfileId}
183+
name={profileModeId}
184+
checked={!isProfileOverrideSelected}
173185
onChange={() => setCachedStateField("terminalProfile", undefined)}
174186
data-testid="terminal-profile-default-radio"
175187
/>
176-
<label htmlFor="terminal-profile-default" className="cursor-pointer">
188+
<label htmlFor={defaultProfileId} className="cursor-pointer">
177189
{t("settings:terminal.profile.default")}
178190
</label>
179191
<VSCodeButton
@@ -191,9 +203,9 @@ export const TerminalSettings = ({
191203
<div className="flex items-center gap-2 mb-2">
192204
<input
193205
type="radio"
194-
id="terminal-profile-override"
195-
name="terminal-profile-mode"
196-
checked={!!terminalProfile}
206+
id={overrideProfileId}
207+
name={profileModeId}
208+
checked={isProfileOverrideSelected}
197209
disabled={profileNames.length === 0}
198210
onChange={() => {
199211
if (!terminalProfile && profileNames.length > 0) {
@@ -203,7 +215,7 @@ export const TerminalSettings = ({
203215
data-testid="terminal-profile-override-radio"
204216
/>
205217
<label
206-
htmlFor="terminal-profile-override"
218+
htmlFor={overrideProfileId}
207219
className={
208220
profileNames.length === 0
209221
? "cursor-not-allowed text-vscode-disabledForeground"
@@ -220,7 +232,7 @@ export const TerminalSettings = ({
220232
)}
221233
</div>
222234

223-
{!!terminalProfile && profileNames.length > 0 && (
235+
{isProfileOverrideSelected && profileNames.length > 0 && (
224236
<Select
225237
value={terminalProfile || DEFAULT_PROFILE_VALUE}
226238
data-testid="terminal-profile-dropdown"

webview-ui/src/components/settings/__tests__/TerminalSettings.profile.spec.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,36 @@ describe("TerminalSettings VS Code terminal profile (#277)", () => {
117117
expect(screen.getByTestId("terminal-profile-dropdown")).toBeInTheDocument()
118118
})
119119

120+
it("falls back to the default radio and clears an unavailable saved profile after profiles load", () => {
121+
const { setCachedStateField } = setup("Git Bash")
122+
act(() => {
123+
window.dispatchEvent(
124+
new MessageEvent("message", {
125+
data: { type: "terminalProfiles", profiles: ["Command Prompt"] },
126+
}),
127+
)
128+
})
129+
130+
expect(screen.getByTestId("terminal-profile-default-radio")).toBeChecked()
131+
expect(screen.getByTestId("terminal-profile-override-radio")).not.toBeChecked()
132+
expect(screen.queryByTestId("terminal-profile-dropdown")).not.toBeInTheDocument()
133+
expect(setCachedStateField).toHaveBeenCalledWith("terminalProfile", undefined)
134+
})
135+
136+
it("uses instance-local radio groups", () => {
137+
render(
138+
<>
139+
<TerminalSettings terminalShellIntegrationDisabled={false} setCachedStateField={vi.fn()} />
140+
<TerminalSettings terminalShellIntegrationDisabled={false} setCachedStateField={vi.fn()} />
141+
</>,
142+
)
143+
144+
const defaultRadios = screen.getAllByTestId("terminal-profile-default-radio")
145+
expect(defaultRadios[0]).toBeChecked()
146+
expect(defaultRadios[1]).toBeChecked()
147+
expect(defaultRadios[0]).not.toHaveAttribute("name", defaultRadios[1].getAttribute("name"))
148+
})
149+
120150
it("populates the dropdown from received profile names and selecting one sets the profile", () => {
121151
const { setCachedStateField } = setup("Git Bash")
122152

@@ -128,8 +158,8 @@ describe("TerminalSettings VS Code terminal profile (#277)", () => {
128158
)
129159
})
130160

131-
fireEvent.click(screen.getByTestId("option-Git Bash"))
132-
expect(setCachedStateField).toHaveBeenCalledWith("terminalProfile", "Git Bash")
161+
fireEvent.click(screen.getByTestId("option-zsh"))
162+
expect(setCachedStateField).toHaveBeenCalledWith("terminalProfile", "zsh")
133163
})
134164

135165
it("clicking default radio sets terminalProfile to undefined", () => {

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ export interface ExtensionStateContextType extends ExtensionState {
8585
terminalZdotdir?: boolean
8686
setTerminalZdotdir: (value: boolean) => void
8787
terminalProfile?: string
88-
setTerminalProfile: (value: string | undefined) => void
8988
setTtsEnabled: (value: boolean) => void
9089
setTtsSpeed: (value: number) => void
9190
setEnableCheckpoints: (value: boolean) => void
@@ -536,7 +535,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
536535
setTerminalShellIntegrationDisabled: (value) =>
537536
setState((prevState) => ({ ...prevState, terminalShellIntegrationDisabled: value })),
538537
setTerminalZdotdir: (value) => setState((prevState) => ({ ...prevState, terminalZdotdir: value })),
539-
setTerminalProfile: (value) => setState((prevState) => ({ ...prevState, terminalProfile: value })),
540538
setMcpEnabled: (value) => setState((prevState) => ({ ...prevState, mcpEnabled: value })),
541539
setTaskSyncEnabled: (value) => setState((prevState) => ({ ...prevState, taskSyncEnabled: value }) as any),
542540
setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })),

0 commit comments

Comments
 (0)