Improve settings organization (#2305)
This commit is contained in:
parent
0b29bfd9d3
commit
f8a2f2889e
|
|
@ -807,19 +807,18 @@
|
|||
@keyframes settings-section-flash {
|
||||
0% {
|
||||
border-color: var(--ring);
|
||||
box-shadow:
|
||||
0 0 0 2px color-mix(in srgb, var(--ring) 45%, transparent),
|
||||
0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
background: color-mix(in srgb, var(--ring) 8%, transparent);
|
||||
box-shadow: inset 3px 0 0 var(--ring);
|
||||
}
|
||||
60% {
|
||||
border-color: var(--ring);
|
||||
box-shadow:
|
||||
0 0 0 2px color-mix(in srgb, var(--ring) 30%, transparent),
|
||||
0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
background: color-mix(in srgb, var(--ring) 5%, transparent);
|
||||
box-shadow: inset 3px 0 0 color-mix(in srgb, var(--ring) 70%, transparent);
|
||||
}
|
||||
100% {
|
||||
border-color: var(--border);
|
||||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
background: transparent;
|
||||
box-shadow: inset 0 0 0 transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -252,14 +252,14 @@ export function AccountsPane({ settings, updateSettings }: AccountsPaneProps): R
|
|||
Claude
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Add and switch Claude Code accounts without moving chat sessions to account-specific
|
||||
config directories.
|
||||
Optional. Orca can use your normal Claude login; add accounts only if you want quick
|
||||
switching without moving chat sessions.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<SearchableSetting
|
||||
title="Claude Accounts"
|
||||
description="Manage which Claude account Orca materializes into the shared Claude auth files."
|
||||
description="Optional account switcher for the shared Claude auth files."
|
||||
keywords={['claude', 'account', 'rate limit', 'status bar', 'quota']}
|
||||
className="space-y-3 px-1 py-2"
|
||||
>
|
||||
|
|
@ -419,7 +419,8 @@ export function AccountsPane({ settings, updateSettings }: AccountsPaneProps): R
|
|||
Codex
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Add and switch between Codex accounts in Orca.
|
||||
Optional. Orca can use your normal Codex login; add accounts only if you want quick
|
||||
switching in Orca.
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Each account keeps its own local sign-in context in Orca. Account auth stays on this
|
||||
|
|
|
|||
|
|
@ -278,33 +278,33 @@ export function GeneralPane({ settings, updateSettings }: GeneralPaneProps): Rea
|
|||
breaks that toast action even though this pane still renders fine. */}
|
||||
<div id="general-skip-delete-worktree-confirm" className="scroll-mt-6">
|
||||
<SearchableSetting
|
||||
title="Skip Delete Worktree Confirmation"
|
||||
description="Delete worktrees from the context menu without a confirmation dialog."
|
||||
title="Ask Before Deleting Worktrees"
|
||||
description="Show a confirmation dialog before deleting a worktree."
|
||||
keywords={['delete', 'worktree', 'confirm', 'dialog', 'skip', 'prompt']}
|
||||
className="flex items-center justify-between gap-4 px-1 py-2"
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<Label>Skip Delete Worktree Confirmation</Label>
|
||||
<Label>Ask Before Deleting Worktrees</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Delete worktrees from the context menu without a confirmation dialog. Errors still
|
||||
surface as a toast with a Force Delete fallback.
|
||||
Show a confirmation before deleting a worktree from the context menu. Failed deletes
|
||||
still surface a Force Delete fallback.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
role="switch"
|
||||
aria-checked={settings.skipDeleteWorktreeConfirm}
|
||||
aria-checked={!settings.skipDeleteWorktreeConfirm}
|
||||
onClick={() =>
|
||||
updateSettings({
|
||||
skipDeleteWorktreeConfirm: !settings.skipDeleteWorktreeConfirm
|
||||
})
|
||||
}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors ${
|
||||
settings.skipDeleteWorktreeConfirm ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
!settings.skipDeleteWorktreeConfirm ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`pointer-events-none block size-3.5 rounded-full bg-background shadow-sm transition-transform ${
|
||||
settings.skipDeleteWorktreeConfirm ? 'translate-x-4' : 'translate-x-0.5'
|
||||
!settings.skipDeleteWorktreeConfirm ? 'translate-x-4' : 'translate-x-0.5'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
|
|
@ -313,32 +313,32 @@ export function GeneralPane({ settings, updateSettings }: GeneralPaneProps): Rea
|
|||
|
||||
<div id="general-skip-delete-automation-confirm" className="scroll-mt-6">
|
||||
<SearchableSetting
|
||||
title="Skip Delete Automation Confirmation"
|
||||
description="Delete automations without a confirmation dialog."
|
||||
title="Ask Before Deleting Automations"
|
||||
description="Show a confirmation dialog before deleting an automation and its run history."
|
||||
keywords={['delete', 'automation', 'confirm', 'dialog', 'skip', 'prompt']}
|
||||
className="flex items-center justify-between gap-4 px-1 py-2"
|
||||
>
|
||||
<div className="space-y-0.5">
|
||||
<Label>Skip Delete Automation Confirmation</Label>
|
||||
<Label>Ask Before Deleting Automations</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Delete automations and their run history without a confirmation dialog.
|
||||
Show a confirmation before deleting automations and their run history.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
role="switch"
|
||||
aria-checked={settings.skipDeleteAutomationConfirm}
|
||||
aria-checked={!settings.skipDeleteAutomationConfirm}
|
||||
onClick={() =>
|
||||
updateSettings({
|
||||
skipDeleteAutomationConfirm: !settings.skipDeleteAutomationConfirm
|
||||
})
|
||||
}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors ${
|
||||
settings.skipDeleteAutomationConfirm ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
!settings.skipDeleteAutomationConfirm ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`pointer-events-none block size-3.5 rounded-full bg-background shadow-sm transition-transform ${
|
||||
settings.skipDeleteAutomationConfirm ? 'translate-x-4' : 'translate-x-0.5'
|
||||
!settings.skipDeleteAutomationConfirm ? 'translate-x-4' : 'translate-x-0.5'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -116,9 +116,26 @@ type SettingsNavSection = {
|
|||
description: string
|
||||
icon: typeof SlidersHorizontal
|
||||
searchEntries: SettingsSearchEntry[]
|
||||
group: string
|
||||
badge?: string
|
||||
}
|
||||
|
||||
type SettingsNavGroup = {
|
||||
id: string
|
||||
title: string
|
||||
sections: SettingsNavSection[]
|
||||
}
|
||||
|
||||
const SETTINGS_NAV_GROUPS = [
|
||||
{ id: 'setup', title: 'Set Up' },
|
||||
{ id: 'workflows', title: 'Workflows' },
|
||||
{ id: 'interface', title: 'Interface' },
|
||||
{ id: 'capabilities', title: 'AI Capabilities' },
|
||||
{ id: 'remote', title: 'Remote Access' },
|
||||
{ id: 'safety', title: 'Safety' },
|
||||
{ id: 'experimental', title: 'Experimental' }
|
||||
] as const
|
||||
|
||||
function getSettingsSectionId(pane: SettingsNavTarget, repoId: string | null): string {
|
||||
if (pane === 'repo' && repoId) {
|
||||
return `repo-${repoId}`
|
||||
|
|
@ -416,61 +433,78 @@ function Settings(): React.JSX.Element {
|
|||
{
|
||||
id: 'general',
|
||||
title: 'General',
|
||||
description: 'Workspace, editor, and updates.',
|
||||
description: 'Workspace defaults, app setup, and maintenance.',
|
||||
icon: SlidersHorizontal,
|
||||
searchEntries: GENERAL_PANE_SEARCH_ENTRIES
|
||||
searchEntries: GENERAL_PANE_SEARCH_ENTRIES,
|
||||
group: 'setup'
|
||||
},
|
||||
{
|
||||
id: 'agents',
|
||||
title: 'Agents',
|
||||
description: 'Manage AI agents, set a default, and customize commands.',
|
||||
icon: Bot,
|
||||
searchEntries: AGENTS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: AGENTS_PANE_SEARCH_ENTRIES,
|
||||
group: 'setup'
|
||||
},
|
||||
{
|
||||
id: 'accounts',
|
||||
title: 'Agent Accounts',
|
||||
description: 'Sign in and switch between Claude, Codex, Gemini, and OpenCode Go accounts.',
|
||||
title: 'AI Provider Accounts',
|
||||
description: 'Optional account switching for Claude, Codex, Gemini, and OpenCode Go.',
|
||||
icon: UserCog,
|
||||
searchEntries: ACCOUNTS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: ACCOUNTS_PANE_SEARCH_ENTRIES,
|
||||
group: 'setup',
|
||||
badge: 'Optional'
|
||||
},
|
||||
{
|
||||
id: 'integrations',
|
||||
title: 'Integrations',
|
||||
description: 'Connect GitHub, GitLab, Linear, and source-hosting services.',
|
||||
icon: Blocks,
|
||||
searchEntries: INTEGRATIONS_PANE_SEARCH_ENTRIES,
|
||||
group: 'setup'
|
||||
},
|
||||
{
|
||||
id: 'git',
|
||||
title: 'Git',
|
||||
description: 'Branch naming, local ref behavior, and AI commit messages.',
|
||||
title: 'Git & Source Control',
|
||||
description: 'Branch naming, base refs, attribution, and AI commit messages.',
|
||||
icon: GitBranch,
|
||||
// Why: the AI commit messages pane is rendered inside the Git section,
|
||||
// so its search entries belong to Git too — that way a query like
|
||||
// "claude" or "thinking" still surfaces the section.
|
||||
searchEntries: [...GIT_PANE_SEARCH_ENTRIES, ...COMMIT_MESSAGE_AI_PANE_SEARCH_ENTRIES]
|
||||
searchEntries: [...GIT_PANE_SEARCH_ENTRIES, ...COMMIT_MESSAGE_AI_PANE_SEARCH_ENTRIES],
|
||||
group: 'workflows'
|
||||
},
|
||||
{
|
||||
id: 'tasks',
|
||||
title: 'Tasks',
|
||||
title: 'Task Sources',
|
||||
description: 'Choose which task providers appear in the Tasks page and sidebar.',
|
||||
icon: ListChecks,
|
||||
searchEntries: TASKS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: TASKS_PANE_SEARCH_ENTRIES,
|
||||
group: 'workflows'
|
||||
},
|
||||
{
|
||||
id: 'appearance',
|
||||
title: 'Appearance',
|
||||
description: 'Theme and UI scaling.',
|
||||
description: 'Theme, zoom, app font, sidebars, and status bar.',
|
||||
icon: Palette,
|
||||
searchEntries: APPEARANCE_PANE_SEARCH_ENTRIES
|
||||
searchEntries: APPEARANCE_PANE_SEARCH_ENTRIES,
|
||||
group: 'interface'
|
||||
},
|
||||
{
|
||||
id: 'input',
|
||||
title: 'Input & Editing',
|
||||
description: 'Selection and editing behavior.',
|
||||
icon: TextCursorInput,
|
||||
searchEntries: INPUT_PANE_SEARCH_ENTRIES
|
||||
searchEntries: INPUT_PANE_SEARCH_ENTRIES,
|
||||
group: 'interface'
|
||||
},
|
||||
{
|
||||
id: 'terminal',
|
||||
title: 'Terminal',
|
||||
description: 'Terminal appearance, previews, and defaults for new panes.',
|
||||
description: 'Shells, terminal appearance, quick commands, and pane behavior.',
|
||||
icon: SquareTerminal,
|
||||
searchEntries: terminalPaneSearchEntries
|
||||
searchEntries: terminalPaneSearchEntries,
|
||||
group: 'workflows'
|
||||
},
|
||||
...(showDesktopOnlySettings
|
||||
? [
|
||||
|
|
@ -479,14 +513,16 @@ function Settings(): React.JSX.Element {
|
|||
title: 'Browser',
|
||||
description: 'Home page, link routing, and session cookies.',
|
||||
icon: Globe,
|
||||
searchEntries: BROWSER_PANE_SEARCH_ENTRIES
|
||||
searchEntries: BROWSER_PANE_SEARCH_ENTRIES,
|
||||
group: 'workflows'
|
||||
},
|
||||
{
|
||||
id: 'notifications' as const,
|
||||
title: 'Notifications',
|
||||
description: 'Native desktop notifications for agent and terminal events.',
|
||||
icon: Bell,
|
||||
searchEntries: NOTIFICATIONS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: NOTIFICATIONS_PANE_SEARCH_ENTRIES,
|
||||
group: 'interface'
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
|
@ -495,26 +531,37 @@ function Settings(): React.JSX.Element {
|
|||
title: 'Orchestration',
|
||||
description: 'Coordinate multiple coding agents through Orca.',
|
||||
icon: Network,
|
||||
searchEntries: ORCHESTRATION_PANE_SEARCH_ENTRIES
|
||||
searchEntries: ORCHESTRATION_PANE_SEARCH_ENTRIES,
|
||||
group: 'capabilities'
|
||||
},
|
||||
{
|
||||
id: 'servers',
|
||||
title: 'Servers',
|
||||
title: 'Remote Orca Servers',
|
||||
description: isWebClient
|
||||
? 'Connect this browser to a saved Orca server.'
|
||||
: 'Run this client locally or through a remote Orca server.',
|
||||
: 'Switch between local desktop mode and paired remote Orca runtimes.',
|
||||
icon: Server,
|
||||
searchEntries: [runtimeEnvironmentsSearchEntry],
|
||||
group: 'remote',
|
||||
badge: 'Beta'
|
||||
},
|
||||
...(showDesktopOnlySettings
|
||||
? [
|
||||
{
|
||||
id: 'ssh' as const,
|
||||
title: 'SSH Hosts',
|
||||
description: 'Remote SSH hosts for files, terminals, and git.',
|
||||
icon: Cable,
|
||||
searchEntries: SSH_PANE_SEARCH_ENTRIES,
|
||||
group: 'remote'
|
||||
},
|
||||
{
|
||||
id: 'mobile' as const,
|
||||
title: 'Mobile',
|
||||
description: 'Control terminals and agents from your phone.',
|
||||
icon: Smartphone,
|
||||
searchEntries: MOBILE_SETTINGS_PANE_SEARCH_ENTRIES,
|
||||
group: 'remote',
|
||||
badge: 'Beta'
|
||||
},
|
||||
{
|
||||
|
|
@ -523,6 +570,7 @@ function Settings(): React.JSX.Element {
|
|||
description: 'Enable agents to control any app on your computer.',
|
||||
icon: MousePointerClick,
|
||||
searchEntries: COMPUTER_USE_PANE_SEARCH_ENTRIES,
|
||||
group: 'capabilities',
|
||||
badge: 'Beta'
|
||||
},
|
||||
{
|
||||
|
|
@ -531,6 +579,7 @@ function Settings(): React.JSX.Element {
|
|||
description: 'Local speech-to-text dictation with on-device models.',
|
||||
icon: Mic,
|
||||
searchEntries: VOICE_PANE_SEARCH_ENTRIES,
|
||||
group: 'capabilities',
|
||||
badge: 'Beta'
|
||||
}
|
||||
]
|
||||
|
|
@ -539,10 +588,11 @@ function Settings(): React.JSX.Element {
|
|||
? [
|
||||
{
|
||||
id: 'developer-permissions' as const,
|
||||
title: 'Permissions',
|
||||
title: 'macOS Permissions',
|
||||
description: 'macOS privacy access for terminal-launched developer tools.',
|
||||
icon: ShieldCheck,
|
||||
searchEntries: DEVELOPER_PERMISSIONS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: DEVELOPER_PERMISSIONS_PANE_SEARCH_ENTRIES,
|
||||
group: 'safety'
|
||||
}
|
||||
]
|
||||
: []),
|
||||
|
|
@ -551,53 +601,40 @@ function Settings(): React.JSX.Element {
|
|||
title: 'Privacy & Telemetry',
|
||||
description: 'Anonymous usage data and telemetry controls.',
|
||||
icon: Lock,
|
||||
searchEntries: PRIVACY_PANE_SEARCH_ENTRIES
|
||||
searchEntries: PRIVACY_PANE_SEARCH_ENTRIES,
|
||||
group: 'safety'
|
||||
},
|
||||
{
|
||||
id: 'shortcuts',
|
||||
title: 'Shortcuts',
|
||||
description: 'Keyboard shortcuts for common actions.',
|
||||
icon: Keyboard,
|
||||
searchEntries: SHORTCUTS_PANE_SEARCH_ENTRIES
|
||||
},
|
||||
{
|
||||
id: 'integrations',
|
||||
title: 'Integrations',
|
||||
description: 'GitHub, Linear, and other service connections.',
|
||||
icon: Blocks,
|
||||
searchEntries: INTEGRATIONS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: SHORTCUTS_PANE_SEARCH_ENTRIES,
|
||||
group: 'interface'
|
||||
},
|
||||
{
|
||||
id: 'stats',
|
||||
title: 'Stats & Usage',
|
||||
description: 'Orca stats plus Claude, Codex, and OpenCode usage analytics.',
|
||||
icon: BarChart3,
|
||||
searchEntries: STATS_PANE_SEARCH_ENTRIES
|
||||
searchEntries: STATS_PANE_SEARCH_ENTRIES,
|
||||
group: 'interface'
|
||||
},
|
||||
...(showDesktopOnlySettings
|
||||
? [
|
||||
{
|
||||
id: 'ssh' as const,
|
||||
title: 'SSH',
|
||||
description: 'Remote SSH connections.',
|
||||
icon: Cable,
|
||||
searchEntries: SSH_PANE_SEARCH_ENTRIES
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
id: 'experimental',
|
||||
title: 'Experimental',
|
||||
description: 'New features that are still taking shape. Give them a try.',
|
||||
icon: FlaskConical,
|
||||
searchEntries: EXPERIMENTAL_PANE_SEARCH_ENTRIES
|
||||
searchEntries: EXPERIMENTAL_PANE_SEARCH_ENTRIES,
|
||||
group: 'experimental'
|
||||
},
|
||||
...repos.map((repo) => ({
|
||||
id: `repo-${repo.id}`,
|
||||
title: repo.displayName,
|
||||
description: `${getRepoKindLabel(repo)} • ${repo.path}`,
|
||||
icon: SlidersHorizontal,
|
||||
searchEntries: getRepositoryPaneSearchEntries(repo)
|
||||
searchEntries: getRepositoryPaneSearchEntries(repo),
|
||||
group: 'repositories'
|
||||
}))
|
||||
],
|
||||
[
|
||||
|
|
@ -958,6 +995,10 @@ function Settings(): React.JSX.Element {
|
|||
}
|
||||
|
||||
const generalNavSections = visibleNavSections.filter((section) => !section.id.startsWith('repo-'))
|
||||
const generalNavGroups: SettingsNavGroup[] = SETTINGS_NAV_GROUPS.map((group) => ({
|
||||
...group,
|
||||
sections: generalNavSections.filter((section) => section.group === group.id)
|
||||
})).filter((group) => group.sections.length > 0)
|
||||
const repoNavSections = visibleNavSections
|
||||
.filter((section) => section.id.startsWith('repo-'))
|
||||
.map((section) => {
|
||||
|
|
@ -970,7 +1011,7 @@ function Settings(): React.JSX.Element {
|
|||
<div className="settings-view-shell flex min-h-0 flex-1 overflow-hidden bg-background">
|
||||
<SettingsSidebar
|
||||
activeSectionId={activeSectionId}
|
||||
generalSections={generalNavSections}
|
||||
generalGroups={generalNavGroups}
|
||||
repoSections={repoNavSections}
|
||||
hasRepos={repos.length > 0}
|
||||
searchQuery={settingsSearchInputQuery}
|
||||
|
|
@ -992,7 +1033,7 @@ function Settings(): React.JSX.Element {
|
|||
<SettingsSection
|
||||
id="general"
|
||||
title="General"
|
||||
description="Workspace, editor, and updates."
|
||||
description="Workspace defaults, app setup, and maintenance."
|
||||
searchEntries={GENERAL_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('general') ? (
|
||||
|
|
@ -1000,15 +1041,6 @@ function Settings(): React.JSX.Element {
|
|||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="integrations"
|
||||
title="Integrations"
|
||||
description="GitHub, Linear, and other service connections."
|
||||
searchEntries={INTEGRATIONS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('integrations') ? <IntegrationsPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="agents"
|
||||
title="Agents"
|
||||
|
|
@ -1022,8 +1054,9 @@ function Settings(): React.JSX.Element {
|
|||
|
||||
<SettingsSection
|
||||
id="accounts"
|
||||
title="Agent Accounts"
|
||||
description="Sign in and switch between Claude, Codex, Gemini, and OpenCode Go accounts."
|
||||
title="AI Provider Accounts"
|
||||
description="Optional. Orca works with your existing provider logins; add accounts only if you want Orca to help switch between them."
|
||||
badge="Optional"
|
||||
searchEntries={ACCOUNTS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('accounts') ? (
|
||||
|
|
@ -1031,10 +1064,19 @@ function Settings(): React.JSX.Element {
|
|||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="integrations"
|
||||
title="Integrations"
|
||||
description="Connect GitHub, GitLab, Linear, and source-hosting services."
|
||||
searchEntries={INTEGRATIONS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('integrations') ? <IntegrationsPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="git"
|
||||
title="Git"
|
||||
description="Branch naming, local ref behavior, and AI commit messages."
|
||||
title="Git & Source Control"
|
||||
description="Branch naming, base refs, attribution, and AI commit messages."
|
||||
searchEntries={[
|
||||
...GIT_PANE_SEARCH_ENTRIES,
|
||||
...COMMIT_MESSAGE_AI_PANE_SEARCH_ENTRIES
|
||||
|
|
@ -1060,7 +1102,7 @@ function Settings(): React.JSX.Element {
|
|||
|
||||
<SettingsSection
|
||||
id="tasks"
|
||||
title="Tasks"
|
||||
title="Task Sources"
|
||||
description="Choose which task providers appear in the Tasks page and sidebar."
|
||||
searchEntries={TASKS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
|
|
@ -1069,35 +1111,10 @@ function Settings(): React.JSX.Element {
|
|||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="appearance"
|
||||
title="Appearance"
|
||||
description="Theme and UI scaling."
|
||||
searchEntries={APPEARANCE_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('appearance') ? (
|
||||
<AppearancePane
|
||||
settings={settings}
|
||||
updateSettings={updateSettings}
|
||||
applyTheme={applyTheme}
|
||||
fontSuggestions={fontSuggestions}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="input"
|
||||
title="Input & Editing"
|
||||
description="Selection and editing behavior."
|
||||
searchEntries={INPUT_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
<InputPane settings={settings} updateSettings={updateSettings} />
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="terminal"
|
||||
title="Terminal"
|
||||
description="Terminal appearance, previews, and defaults for new panes."
|
||||
description="Shells, terminal appearance, quick commands, and pane behavior."
|
||||
searchEntries={terminalPaneSearchEntries}
|
||||
headerAction={
|
||||
<Button
|
||||
|
|
@ -1129,35 +1146,78 @@ function Settings(): React.JSX.Element {
|
|||
</SettingsSection>
|
||||
|
||||
{showDesktopOnlySettings ? (
|
||||
<>
|
||||
<SettingsSection
|
||||
id="browser"
|
||||
title="Browser"
|
||||
description="Home page, link routing, and session cookies."
|
||||
searchEntries={BROWSER_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('browser') ? (
|
||||
<BrowserPane
|
||||
settings={settings}
|
||||
updateSettings={updateSettings}
|
||||
onOpenComputerUse={openComputerUseFromBrowser}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="notifications"
|
||||
title="Notifications"
|
||||
description="Native desktop notifications for agent activity and terminal events."
|
||||
searchEntries={NOTIFICATIONS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('notifications') ? (
|
||||
<NotificationsPane settings={settings} updateSettings={updateSettings} />
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
</>
|
||||
<SettingsSection
|
||||
id="browser"
|
||||
title="Browser"
|
||||
description="Home page, link routing, and session cookies."
|
||||
searchEntries={BROWSER_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('browser') ? (
|
||||
<BrowserPane
|
||||
settings={settings}
|
||||
updateSettings={updateSettings}
|
||||
onOpenComputerUse={openComputerUseFromBrowser}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
) : null}
|
||||
|
||||
<SettingsSection
|
||||
id="appearance"
|
||||
title="Appearance"
|
||||
description="Theme, zoom, app font, sidebars, and status bar."
|
||||
searchEntries={APPEARANCE_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('appearance') ? (
|
||||
<AppearancePane
|
||||
settings={settings}
|
||||
updateSettings={updateSettings}
|
||||
applyTheme={applyTheme}
|
||||
fontSuggestions={fontSuggestions}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="input"
|
||||
title="Input & Editing"
|
||||
description="Selection and editing behavior."
|
||||
searchEntries={INPUT_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
<InputPane settings={settings} updateSettings={updateSettings} />
|
||||
</SettingsSection>
|
||||
|
||||
{showDesktopOnlySettings ? (
|
||||
<SettingsSection
|
||||
id="notifications"
|
||||
title="Notifications"
|
||||
description="Native desktop notifications for agent activity and terminal events."
|
||||
searchEntries={NOTIFICATIONS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('notifications') ? (
|
||||
<NotificationsPane settings={settings} updateSettings={updateSettings} />
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
) : null}
|
||||
|
||||
<SettingsSection
|
||||
id="shortcuts"
|
||||
title="Shortcuts"
|
||||
description="Keyboard shortcuts for common actions."
|
||||
searchEntries={SHORTCUTS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('shortcuts') ? <ShortcutsPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="stats"
|
||||
title="Stats & Usage"
|
||||
description="Orca stats plus Claude, Codex, and OpenCode usage analytics."
|
||||
searchEntries={STATS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('stats') ? <StatsPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="orchestration"
|
||||
title="Orchestration"
|
||||
|
|
@ -1167,41 +1227,8 @@ function Settings(): React.JSX.Element {
|
|||
{isSectionMounted('orchestration') ? <OrchestrationPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="servers"
|
||||
title="Servers"
|
||||
badge="Beta"
|
||||
description={
|
||||
isWebClient
|
||||
? 'Connect this browser to a saved Orca server.'
|
||||
: 'Run this desktop client locally or through a remote Orca server.'
|
||||
}
|
||||
searchEntries={[runtimeEnvironmentsSearchEntry]}
|
||||
>
|
||||
{isSectionMounted('servers') ? (
|
||||
<RuntimeEnvironmentsPane
|
||||
settings={settings}
|
||||
switchRuntimeEnvironment={switchRuntimeEnvironment}
|
||||
canGeneratePairingUrl={!isWebClient}
|
||||
allowLocalRuntime={!isWebClient}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
{showDesktopOnlySettings ? (
|
||||
<>
|
||||
<SettingsSection
|
||||
id="mobile"
|
||||
title="Mobile"
|
||||
badge="Beta"
|
||||
description="Control terminals and agents from your phone."
|
||||
searchEntries={MOBILE_SETTINGS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('mobile') ? (
|
||||
<MobileSettingsPane settings={settings} updateSettings={updateSettings} />
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="computer-use"
|
||||
title="Computer Use"
|
||||
|
|
@ -1249,10 +1276,56 @@ function Settings(): React.JSX.Element {
|
|||
</>
|
||||
) : null}
|
||||
|
||||
<SettingsSection
|
||||
id="servers"
|
||||
title="Remote Orca Servers"
|
||||
badge="Beta"
|
||||
description={
|
||||
isWebClient
|
||||
? 'Connect this browser to a saved Orca server.'
|
||||
: 'Switch between local desktop mode and paired remote Orca runtimes.'
|
||||
}
|
||||
searchEntries={[runtimeEnvironmentsSearchEntry]}
|
||||
>
|
||||
{isSectionMounted('servers') ? (
|
||||
<RuntimeEnvironmentsPane
|
||||
settings={settings}
|
||||
switchRuntimeEnvironment={switchRuntimeEnvironment}
|
||||
canGeneratePairingUrl={!isWebClient}
|
||||
allowLocalRuntime={!isWebClient}
|
||||
/>
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
{showDesktopOnlySettings ? (
|
||||
<>
|
||||
<SettingsSection
|
||||
id="ssh"
|
||||
title="SSH Hosts"
|
||||
description="Remote SSH hosts for files, terminals, and git."
|
||||
searchEntries={SSH_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('ssh') ? <SshPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="mobile"
|
||||
title="Mobile"
|
||||
badge="Beta"
|
||||
description="Control terminals and agents from your phone."
|
||||
searchEntries={MOBILE_SETTINGS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('mobile') ? (
|
||||
<MobileSettingsPane settings={settings} updateSettings={updateSettings} />
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
{showDesktopOnlySettings && isMac ? (
|
||||
<SettingsSection
|
||||
id="developer-permissions"
|
||||
title="Permissions"
|
||||
title="macOS Permissions"
|
||||
description="macOS privacy access for terminal-launched developer tools."
|
||||
searchEntries={DEVELOPER_PERMISSIONS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
|
|
@ -1271,35 +1344,6 @@ function Settings(): React.JSX.Element {
|
|||
{isSectionMounted('privacy') ? <PrivacyPane settings={settings} /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="shortcuts"
|
||||
title="Shortcuts"
|
||||
description="Keyboard shortcuts for common actions."
|
||||
searchEntries={SHORTCUTS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('shortcuts') ? <ShortcutsPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
id="stats"
|
||||
title="Stats"
|
||||
description="How much Orca has helped you."
|
||||
searchEntries={STATS_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('stats') ? <StatsPane /> : null}
|
||||
</SettingsSection>
|
||||
|
||||
{showDesktopOnlySettings ? (
|
||||
<SettingsSection
|
||||
id="ssh"
|
||||
title="SSH"
|
||||
description="Manage remote SSH connections. Connect to remote servers to browse files, run terminals, and use git."
|
||||
searchEntries={SSH_PANE_SEARCH_ENTRIES}
|
||||
>
|
||||
{isSectionMounted('ssh') ? <SshPane /> : null}
|
||||
</SettingsSection>
|
||||
) : null}
|
||||
|
||||
<SettingsSection
|
||||
id="experimental"
|
||||
title="Experimental"
|
||||
|
|
|
|||
|
|
@ -41,16 +41,14 @@ export function SettingsSection({
|
|||
id={id}
|
||||
data-settings-section={id}
|
||||
className={
|
||||
// Why: these sections already contain many internal borders and cards, so a lone divider
|
||||
// line gets lost in the visual noise. Giving each section its own padded surface creates a
|
||||
// clear outer silhouette that still works when the inner content changes.
|
||||
className ??
|
||||
'scroll-mt-6 space-y-8 rounded-2xl border border-border/60 bg-card/35 px-6 py-6 shadow-sm'
|
||||
// Why: each pane already owns internal cards and borders. A stronger unframed section
|
||||
// break keeps top-level settings pages distinct without nesting everything in cards.
|
||||
className ?? 'scroll-mt-6 space-y-6 border-b-2 border-foreground/20 pb-10 last:border-b-0'
|
||||
}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
<h2 className="flex items-center gap-2 text-xl font-semibold">
|
||||
<h2 className="flex items-center gap-2 text-lg font-semibold">
|
||||
{title}
|
||||
{badge ? (
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ type NavSection = {
|
|||
badge?: string
|
||||
}
|
||||
|
||||
type NavGroup = {
|
||||
id: string
|
||||
title: string
|
||||
sections: NavSection[]
|
||||
}
|
||||
|
||||
type RepoNavSection = NavSection & {
|
||||
badgeColor?: string
|
||||
isRemote?: boolean
|
||||
|
|
@ -20,7 +26,7 @@ type RepoNavSection = NavSection & {
|
|||
|
||||
type SettingsSidebarProps = {
|
||||
activeSectionId: string
|
||||
generalSections: NavSection[]
|
||||
generalGroups: NavGroup[]
|
||||
repoSections: RepoNavSection[]
|
||||
hasRepos: boolean
|
||||
searchQuery: string
|
||||
|
|
@ -35,7 +41,7 @@ type SettingsSidebarProps = {
|
|||
|
||||
export function SettingsSidebar({
|
||||
activeSectionId,
|
||||
generalSections,
|
||||
generalGroups,
|
||||
repoSections,
|
||||
hasRepos,
|
||||
searchQuery,
|
||||
|
|
@ -78,39 +84,46 @@ export function SettingsSidebar({
|
|||
|
||||
<div className="min-h-0 flex-1 overflow-y-auto scrollbar-sleek px-3 py-4">
|
||||
<div className="space-y-5">
|
||||
<div className="space-y-1">
|
||||
{generalSections.map((section) => {
|
||||
const Icon = section.icon
|
||||
const isActive = activeSectionId === section.id
|
||||
{generalGroups.map((group) => (
|
||||
<div key={group.id} className="space-y-2">
|
||||
<p className="px-3 text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground">
|
||||
{group.title}
|
||||
</p>
|
||||
<div className="space-y-1">
|
||||
{group.sections.map((section) => {
|
||||
const Icon = section.icon
|
||||
const isActive = activeSectionId === section.id
|
||||
|
||||
return (
|
||||
<button
|
||||
key={section.id}
|
||||
onClick={(event) =>
|
||||
onSelectSection(section.id, {
|
||||
metaKey: event.metaKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
shiftKey: event.shiftKey,
|
||||
altKey: event.altKey
|
||||
})
|
||||
}
|
||||
className={`flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors ${
|
||||
isActive
|
||||
? 'bg-accent font-medium text-accent-foreground'
|
||||
: 'text-muted-foreground hover:bg-muted/60 hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<Icon className="mr-2 size-4" />
|
||||
{section.title}
|
||||
{section.badge ? (
|
||||
<span className="ml-auto rounded-full bg-muted px-1.5 py-0.5 text-[9px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{section.badge}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
return (
|
||||
<button
|
||||
key={section.id}
|
||||
onClick={(event) =>
|
||||
onSelectSection(section.id, {
|
||||
metaKey: event.metaKey,
|
||||
ctrlKey: event.ctrlKey,
|
||||
shiftKey: event.shiftKey,
|
||||
altKey: event.altKey
|
||||
})
|
||||
}
|
||||
className={`flex w-full items-center rounded-lg px-3 py-2 text-left text-sm transition-colors ${
|
||||
isActive
|
||||
? 'bg-accent font-medium text-accent-foreground'
|
||||
: 'text-muted-foreground hover:bg-muted/60 hover:text-foreground'
|
||||
}`}
|
||||
>
|
||||
<Icon className="mr-2 size-4" />
|
||||
<span className="truncate">{section.title}</span>
|
||||
{section.badge ? (
|
||||
<span className="ml-auto rounded-full bg-muted px-1.5 py-0.5 text-[9px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
{section.badge}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="px-3 text-[11px] font-medium uppercase tracking-[0.18em] text-muted-foreground">
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@ import type { SettingsSearchEntry } from './settings-search'
|
|||
export const ACCOUNTS_CLAUDE_SEARCH_ENTRIES: SettingsSearchEntry[] = [
|
||||
{
|
||||
title: 'Claude Accounts',
|
||||
description: 'Manage which Claude account Orca uses while preserving shared chat context.',
|
||||
keywords: ['claude', 'account', 'switch', 'active', 'status bar', 'quota']
|
||||
description: 'Optional account switching for Claude while preserving shared chat context.',
|
||||
keywords: ['claude', 'account', 'switch', 'active', 'status bar', 'quota', 'optional']
|
||||
}
|
||||
]
|
||||
|
||||
export const ACCOUNTS_CODEX_SEARCH_ENTRIES: SettingsSearchEntry[] = [
|
||||
{
|
||||
title: 'Codex Accounts',
|
||||
description: 'Manage which Codex account Orca uses for live rate limit fetching.',
|
||||
keywords: ['codex', 'account', 'rate limit', 'status bar', 'quota']
|
||||
description: 'Optional account switching for Codex and live rate limit fetching.',
|
||||
keywords: ['codex', 'account', 'rate limit', 'status bar', 'quota', 'optional']
|
||||
},
|
||||
{
|
||||
title: 'Active Codex Account',
|
||||
description: 'Choose which saved Codex account powers live quota reads.',
|
||||
keywords: ['codex', 'account', 'switch', 'active', 'status bar']
|
||||
description: 'Choose which optional saved Codex account powers live quota reads.',
|
||||
keywords: ['codex', 'account', 'switch', 'active', 'status bar', 'optional']
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ export const GENERAL_WORKSPACE_SEARCH_ENTRIES: SettingsSearchEntry[] = [
|
|||
keywords: ['nested', 'subfolder', 'directory']
|
||||
},
|
||||
{
|
||||
title: 'Skip Delete Worktree Confirmation',
|
||||
description: 'Delete worktrees from the context menu without a confirmation dialog.',
|
||||
title: 'Ask Before Deleting Worktrees',
|
||||
description: 'Show a confirmation dialog before deleting a worktree.',
|
||||
keywords: ['delete', 'worktree', 'confirm', 'dialog', 'skip', 'prompt']
|
||||
},
|
||||
{
|
||||
title: 'Skip Delete Automation Confirmation',
|
||||
description: 'Delete automations without a confirmation dialog.',
|
||||
title: 'Ask Before Deleting Automations',
|
||||
description: 'Show a confirmation dialog before deleting an automation and its run history.',
|
||||
keywords: ['delete', 'automation', 'confirm', 'dialog', 'skip', 'prompt']
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ export function SshStatusSegment({
|
|||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
openSettingsTarget({ pane: 'general', repoId: null, sectionId: 'ssh' })
|
||||
openSettingsTarget({ pane: 'ssh', repoId: null, sectionId: 'ssh' })
|
||||
setActiveView('settings')
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue