From decd990fdebf388a8a9391e1602631b948e09da3 Mon Sep 17 00:00:00 2001 From: Neil Date: Wed, 16 Sep 2026 14:52:57 -0700 Subject: [PATCH] feat(settings): make shell choice explicit --- .../src/components/settings/TerminalPane.tsx | 88 ++++++++++++++----- 1 file changed, 66 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/components/settings/TerminalPane.tsx b/src/renderer/src/components/settings/TerminalPane.tsx index 0af8c5d97bc..b97b4ab960f 100644 --- a/src/renderer/src/components/settings/TerminalPane.tsx +++ b/src/renderer/src/components/settings/TerminalPane.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react' import type { GlobalSettings } from '../../../../shared/global-settings-types' import { Separator } from '../ui/separator' import { Input } from '../ui/input' @@ -24,7 +25,7 @@ import { TerminalInteractionSection } from './TerminalInteractionSection' import { TerminalRenderingSection } from './TerminalRenderingSection' import { TerminalSetupScriptSection } from './TerminalSetupScriptSection' import { TerminalWindowsShellSection } from './TerminalWindowsShellSection' -import { SettingsRow, SettingsSubsectionHeader } from './SettingsFormControls' +import { SettingsSegmentedControl, SettingsSubsectionHeader } from './SettingsFormControls' type TerminalPaneProps = { settings: GlobalSettings @@ -62,6 +63,22 @@ export function TerminalPane({ const showWindowsPowerShellImplementation = showWindowsHostSettings && windowsShell === 'powershell.exe' + const [shellValidationError, setShellValidationError] = useState(null) + const configuredShell = settings.terminalDefaultShell?.trim() ?? '' + const shellMode = configuredShell ? 'custom' : 'system' + const systemShell = window.api?.platform?.get?.().shell?.trim() || '/bin/zsh' + + const validateShell = async (): Promise => { + const shell = configuredShell + const isAbsolute = shell.startsWith('/') || /^[A-Za-z]:[\\/]/.test(shell) + if (!isAbsolute) { + setShellValidationError(null) + return + } + const exists = await window.api.shell.pathExists(shell) + setShellValidationError(exists ? null : `Shell not found: ${shell}`) + } + const defaultShellSection = !showWindowsHostSettings && matchesSettingsSearch(searchQuery, { @@ -71,28 +88,55 @@ export function TerminalPane({ }) ? (
- - updateSettings({ terminalDefaultShell: event.target.value.trimStart() }) - } - className="w-full max-w-64" - aria-label="Default shell executable" - /> - } + title="New terminal shell" + description="Choose what Orca opens for new local terminal panes. Existing panes are unchanged." /> +
+ { + setShellValidationError(null) + updateSettings({ terminalDefaultShell: value === 'system' ? '' : configuredShell }) + }} + options={[ + { value: 'system', label: `System shell (${systemShell})` }, + { value: 'custom', label: 'Custom shell' } + ]} + /> + {shellMode === 'custom' ? ( +
+ { + setShellValidationError(null) + updateSettings({ terminalDefaultShell: event.target.value.trimStart() }) + }} + onBlur={() => void validateShell()} + className="w-full" + aria-label="Custom shell executable" + aria-invalid={shellValidationError != null} + aria-describedby={shellValidationError ? 'default-shell-error' : undefined} + /> +

+ Enter a command on PATH or an executable path. Orca starts it as a login shell. +

+ {shellValidationError ? ( + + ) : null} +
+ ) : null} +

+ New terminals will use:{' '} + {shellMode === 'system' + ? `System shell (${systemShell})` + : configuredShell || 'the custom shell you enter'} +

+
) : null