diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 446f2260f..59d927600 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -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; } } diff --git a/src/renderer/src/components/settings/AccountsPane.tsx b/src/renderer/src/components/settings/AccountsPane.tsx index 0437beb2b..9da2052a6 100644 --- a/src/renderer/src/components/settings/AccountsPane.tsx +++ b/src/renderer/src/components/settings/AccountsPane.tsx @@ -252,14 +252,14 @@ export function AccountsPane({ settings, updateSettings }: AccountsPaneProps): R Claude

- 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.

@@ -419,7 +419,8 @@ export function AccountsPane({ settings, updateSettings }: AccountsPaneProps): R Codex

- 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.

Each account keeps its own local sign-in context in Orca. Account auth stays on this diff --git a/src/renderer/src/components/settings/GeneralPane.tsx b/src/renderer/src/components/settings/GeneralPane.tsx index 3fb6b902c..58d9b72ad 100644 --- a/src/renderer/src/components/settings/GeneralPane.tsx +++ b/src/renderer/src/components/settings/GeneralPane.tsx @@ -278,33 +278,33 @@ export function GeneralPane({ settings, updateSettings }: GeneralPaneProps): Rea breaks that toast action even though this pane still renders fine. */}

- +

- 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.

@@ -313,32 +313,32 @@ export function GeneralPane({ settings, updateSettings }: GeneralPaneProps): Rea
- +

- Delete automations and their run history without a confirmation dialog. + Show a confirmation before deleting automations and their run history.

diff --git a/src/renderer/src/components/settings/Settings.tsx b/src/renderer/src/components/settings/Settings.tsx index e41640d71..5f66a1cab 100644 --- a/src/renderer/src/components/settings/Settings.tsx +++ b/src/renderer/src/components/settings/Settings.tsx @@ -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 {
0} searchQuery={settingsSearchInputQuery} @@ -992,7 +1033,7 @@ function Settings(): React.JSX.Element { {isSectionMounted('general') ? ( @@ -1000,15 +1041,6 @@ function Settings(): React.JSX.Element { ) : null} - - {isSectionMounted('integrations') ? : null} - - {isSectionMounted('accounts') ? ( @@ -1031,10 +1064,19 @@ function Settings(): React.JSX.Element { ) : null} + + {isSectionMounted('integrations') ? : null} + + @@ -1069,35 +1111,10 @@ function Settings(): React.JSX.Element { ) : null} - - {isSectionMounted('appearance') ? ( - - ) : null} - - - - - - {showDesktopOnlySettings ? ( - <> - - {isSectionMounted('browser') ? ( - - ) : null} - - - - {isSectionMounted('notifications') ? ( - - ) : null} - - + + {isSectionMounted('browser') ? ( + + ) : null} + ) : null} + + {isSectionMounted('appearance') ? ( + + ) : null} + + + + + + + {showDesktopOnlySettings ? ( + + {isSectionMounted('notifications') ? ( + + ) : null} + + ) : null} + + + {isSectionMounted('shortcuts') ? : null} + + + + {isSectionMounted('stats') ? : null} + + : null} - - {isSectionMounted('servers') ? ( - - ) : null} - - {showDesktopOnlySettings ? ( <> - - {isSectionMounted('mobile') ? ( - - ) : null} - - ) : null} + + {isSectionMounted('servers') ? ( + + ) : null} + + + {showDesktopOnlySettings ? ( + <> + + {isSectionMounted('ssh') ? : null} + + + + {isSectionMounted('mobile') ? ( + + ) : null} + + + ) : null} + {showDesktopOnlySettings && isMac ? ( @@ -1271,35 +1344,6 @@ function Settings(): React.JSX.Element { {isSectionMounted('privacy') ? : null} - - {isSectionMounted('shortcuts') ? : null} - - - - {isSectionMounted('stats') ? : null} - - - {showDesktopOnlySettings ? ( - - {isSectionMounted('ssh') ? : null} - - ) : null} -
-

+

{title} {badge ? ( diff --git a/src/renderer/src/components/settings/SettingsSidebar.tsx b/src/renderer/src/components/settings/SettingsSidebar.tsx index ba10f88c5..76ca7f959 100644 --- a/src/renderer/src/components/settings/SettingsSidebar.tsx +++ b/src/renderer/src/components/settings/SettingsSidebar.tsx @@ -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({
-
- {generalSections.map((section) => { - const Icon = section.icon - const isActive = activeSectionId === section.id + {generalGroups.map((group) => ( +
+

+ {group.title} +

+
+ {group.sections.map((section) => { + const Icon = section.icon + const isActive = activeSectionId === section.id - return ( - - ) - })} -
+ return ( + + ) + })} +
+
+ ))}

diff --git a/src/renderer/src/components/settings/accounts-search.ts b/src/renderer/src/components/settings/accounts-search.ts index df61ce8b9..6166af302 100644 --- a/src/renderer/src/components/settings/accounts-search.ts +++ b/src/renderer/src/components/settings/accounts-search.ts @@ -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'] } ] diff --git a/src/renderer/src/components/settings/general-search.ts b/src/renderer/src/components/settings/general-search.ts index 8989ae42c..f847e7873 100644 --- a/src/renderer/src/components/settings/general-search.ts +++ b/src/renderer/src/components/settings/general-search.ts @@ -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'] }, { diff --git a/src/renderer/src/components/status-bar/SshStatusSegment.tsx b/src/renderer/src/components/status-bar/SshStatusSegment.tsx index 1d7c365f0..c422fe9cb 100644 --- a/src/renderer/src/components/status-bar/SshStatusSegment.tsx +++ b/src/renderer/src/components/status-bar/SshStatusSegment.tsx @@ -300,7 +300,7 @@ export function SshStatusSegment({ { - openSettingsTarget({ pane: 'general', repoId: null, sectionId: 'ssh' }) + openSettingsTarget({ pane: 'ssh', repoId: null, sectionId: 'ssh' }) setActiveView('settings') }} >