diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index 883d9061562..39c73d8af2b 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -2172,6 +2172,38 @@ html.native-shell .app-layout { } } +/* Search and deep-link target: a tint out to the enclosing card's edges that fades. + An inset shadow carries the tint so a highlighted card keeps its own fill and corners. + The bleed vars are measured per target, since nested and sub-row targets sit further in. */ +.settings-target-highlight { + --settings-target-tint: color-mix(in srgb, var(--foreground) 8%, transparent); + box-shadow: + inset 0 0 0 100vmax var(--settings-target-tint), + calc(-1 * var(--settings-target-bleed-left, 0px)) 0 0 var(--settings-target-tint), + var(--settings-target-bleed-right, 0px) 0 0 var(--settings-target-tint); +} + +@media (prefers-reduced-motion: no-preference) { + .settings-target-highlight { + animation: settings-target-highlight-fade 3s ease-out forwards; + } +} + +@keyframes settings-target-highlight-fade { + 60% { + box-shadow: + inset 0 0 0 100vmax var(--settings-target-tint), + calc(-1 * var(--settings-target-bleed-left, 0px)) 0 0 var(--settings-target-tint), + var(--settings-target-bleed-right, 0px) 0 0 var(--settings-target-tint); + } + 100% { + box-shadow: + inset 0 0 0 100vmax transparent, + calc(-1 * var(--settings-target-bleed-left, 0px)) 0 0 transparent, + var(--settings-target-bleed-right, 0px) 0 0 transparent; + } +} + @keyframes settings-shell-enter { from { opacity: 0; diff --git a/src/renderer/src/components/settings/accounts/accounts-search.ts b/src/renderer/src/components/settings/accounts/accounts-search.ts index dbf6bdb196f..01f6909a2bd 100644 --- a/src/renderer/src/components/settings/accounts/accounts-search.ts +++ b/src/renderer/src/components/settings/accounts/accounts-search.ts @@ -4,6 +4,14 @@ import { translateSearchKeyword } from '../search/settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' import { getAccountsClaudeSearchEntries } from './accounts-claude-search' +export const ACCOUNTS_SECTION_IDS = { + codex: 'accounts-codex', + minimax: 'accounts-minimax', + grok: 'accounts-grok', + antigravity: 'accounts-antigravity', + cursor: 'accounts-cursor' +} as const + export const getAccountsLocationSearchEntries = createLocalizedCatalog(() => [ { title: translate('auto.components.settings.accounts.search.5e2101f61f', 'Account location'), @@ -55,6 +63,7 @@ export const getAccountsCodexSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: ACCOUNTS_SECTION_IDS.codex, title: translate('auto.components.settings.accounts.search.4518496456', 'Active Codex account'), description: translate( 'auto.components.settings.accounts.search.87a4a8584e', @@ -153,6 +162,7 @@ export const getAccountsOpencodeSearchEntries = createLocalizedCatalog(() => [ export const getAccountsMiniMaxSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: ACCOUNTS_SECTION_IDS.minimax, title: translate('auto.components.settings.accounts.search.9b6ce52c36', 'MiniMax usage'), description: translate( 'auto.components.settings.accounts.search.f8374c3151', @@ -177,6 +187,7 @@ export const getAccountsMiniMaxSearchEntries = createLocalizedCatalog(() => [ export const getAccountsGrokSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: ACCOUNTS_SECTION_IDS.grok, title: translate('auto.components.settings.accounts.search.f4a8c2e1b7', 'Grok (xAI) Usage'), description: translate( 'auto.components.settings.accounts.search.e3b7d1f9a2', @@ -198,6 +209,7 @@ export const getAccountsGrokSearchEntries = createLocalizedCatalog(() => [ export const getAccountsAntigravitySearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: ACCOUNTS_SECTION_IDS.antigravity, title: translate('accounts.antigravity.fd47b5a5dd', 'Antigravity accounts'), description: translate( 'accounts.antigravity.searchDescription', @@ -216,6 +228,7 @@ export const getAccountsAntigravitySearchEntries = createLocalizedCatalog(() => export const getAccountsCursorSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: ACCOUNTS_SECTION_IDS.cursor, title: translate('auto.components.settings.accounts.search.cursor.39bbdebe13', 'Cursor usage'), description: translate( 'auto.components.settings.accounts.search.cursor.description', diff --git a/src/renderer/src/components/settings/accounts/codex/accounts-pane-codex-section.tsx b/src/renderer/src/components/settings/accounts/codex/accounts-pane-codex-section.tsx index 7da61add753..9d225a2e59d 100644 --- a/src/renderer/src/components/settings/accounts/codex/accounts-pane-codex-section.tsx +++ b/src/renderer/src/components/settings/accounts/codex/accounts-pane-codex-section.tsx @@ -8,7 +8,7 @@ import { Label } from '../../../ui/label' import { OpenAIIcon } from '../../../status-bar/icons' import { CodexLoginLinkNotice } from './CodexLoginLinkNotice' import { SearchableSetting } from '../../section-layout/SearchableSetting' -import { getAccountsCodexSearchEntries } from '../accounts-search' +import { ACCOUNTS_SECTION_IDS, getAccountsCodexSearchEntries } from '../accounts-search' import { getCodexSystemDefaultSubtitle } from '../scope/accounts-pane-runtime' import type { AccountsPaneSectionModel } from '../accounts-pane-types' import { renderCodexAccountRow } from './accounts-pane-codex-account-row' @@ -47,7 +47,7 @@ export function renderCodexAccountsSection( wslCapabilitiesLoading } = model return ( -
+
diff --git a/src/renderer/src/components/settings/accounts/providers/AntigravityAccountsSection.tsx b/src/renderer/src/components/settings/accounts/providers/AntigravityAccountsSection.tsx index 8e85a588be5..2b7a98de921 100644 --- a/src/renderer/src/components/settings/accounts/providers/AntigravityAccountsSection.tsx +++ b/src/renderer/src/components/settings/accounts/providers/AntigravityAccountsSection.tsx @@ -26,6 +26,7 @@ import { useAntigravityChatSignIn } from '../../../provider-accounts/use-antigra import { ChatUnavailableOnWindowsNote } from '../../../agent/launch-settings/AgentChatChoiceControls' import { structuredChatUnavailableOnPlatform } from '../../../../../../shared/agent-session/structured-chat-platform-support' import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { ACCOUNTS_SECTION_IDS } from '../accounts-search' import { AntigravityTerminalAccountRows, type AntigravityAccountAction @@ -204,7 +205,7 @@ export function AntigravityAccountsSection({ ) : null return ( -
+
diff --git a/src/renderer/src/components/settings/accounts/providers/CursorAccountsSection.tsx b/src/renderer/src/components/settings/accounts/providers/CursorAccountsSection.tsx index 2795b2555cb..611afe84edb 100644 --- a/src/renderer/src/components/settings/accounts/providers/CursorAccountsSection.tsx +++ b/src/renderer/src/components/settings/accounts/providers/CursorAccountsSection.tsx @@ -11,6 +11,7 @@ import type { CursorAccountStatus } from '../../../../../../shared/rate-limit-ty import { SearchableSetting } from '../../section-layout/SearchableSetting' import { openAppHttpLinkFromAnchor } from '@/lib/browser/navigation/app-http-link-open' import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { ACCOUNTS_SECTION_IDS } from '../accounts-search' const CURSOR_USAGE_DASHBOARD_URL = 'https://cursor.com/dashboard/spending' const SEARCH_KEYWORDS = ['cursor', 'usage', 'plan', 'spend', 'billing', 'rate limit'] @@ -105,7 +106,7 @@ export function CursorAccountsSection(): React.JSX.Element { : null return ( -
+
diff --git a/src/renderer/src/components/settings/accounts/providers/GrokAccountsSection.tsx b/src/renderer/src/components/settings/accounts/providers/GrokAccountsSection.tsx index c60572732bf..4f3875e5ca2 100644 --- a/src/renderer/src/components/settings/accounts/providers/GrokAccountsSection.tsx +++ b/src/renderer/src/components/settings/accounts/providers/GrokAccountsSection.tsx @@ -10,6 +10,7 @@ import type { GrokAccountStatus } from '../../../../../../shared/rate-limit-type import { SearchableSetting } from '../../section-layout/SearchableSetting' import { openAppHttpLinkFromAnchor } from '@/lib/browser/navigation/app-http-link-open' import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { ACCOUNTS_SECTION_IDS } from '../accounts-search' const GROK_CLI_DOCS_URL = 'https://docs.x.ai/build/overview' export function GrokAccountsSection(): React.JSX.Element { @@ -65,7 +66,7 @@ export function GrokAccountsSection(): React.JSX.Element { : null return ( -
+
diff --git a/src/renderer/src/components/settings/accounts/usage/MiniMaxAccountsSection.tsx b/src/renderer/src/components/settings/accounts/usage/MiniMaxAccountsSection.tsx index 66135b5fdce..561c70fad8a 100644 --- a/src/renderer/src/components/settings/accounts/usage/MiniMaxAccountsSection.tsx +++ b/src/renderer/src/components/settings/accounts/usage/MiniMaxAccountsSection.tsx @@ -17,6 +17,7 @@ import { } from './usage-credential-host' import type { UsageHostSettingsModel } from './use-usage-host-settings' import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { ACCOUNTS_SECTION_IDS } from '../accounts-search' export function MiniMaxAccountsSection({ host, @@ -49,7 +50,7 @@ export function MiniMaxAccountsSection({ void usageSettings.update({ minimaxEndpoint: value }) } return ( -
+
diff --git a/src/renderer/src/components/settings/advanced/network/AdvancedNetworkSettingsSection.tsx b/src/renderer/src/components/settings/advanced/network/AdvancedNetworkSettingsSection.tsx index 3141bc949e0..7897dce49fb 100644 --- a/src/renderer/src/components/settings/advanced/network/AdvancedNetworkSettingsSection.tsx +++ b/src/renderer/src/components/settings/advanced/network/AdvancedNetworkSettingsSection.tsx @@ -18,6 +18,8 @@ import { Textarea } from '../../../ui/textarea' import { getAdvancedNetworkSearchEntries } from './advanced-network-search' import { NodeDistMirrorSettingField } from './NodeDistMirrorSettingField' import { SearchableSetting } from '../../section-layout/SearchableSetting' +import { useSettingsTargetReveal } from '../../section-layout/settings-target-highlight' +import { NODE_DIST_MIRROR_SETTINGS_TARGET_ID } from '../../navigation/settings-navigation-types' import { matchesSettingsSearch, normalizeSettingsSearchQuery } from '../../search/settings-search' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' @@ -152,6 +154,7 @@ export function AdvancedNetworkSettingsSection({ const t = useTranslate() const searchQuery = useAppStore((s) => s.settingsSearchQuery) const [proxyConfigOpen, setProxyConfigOpen] = useState(false) + const proxyRevealRef = useSettingsTargetReveal(() => setProxyConfigOpen(true)) const proxyConfigForcedOpen = shouldOpenNetworkProxyConfig(searchQuery) || hasConfiguredNetworkProxy(settings) const proxyConfigExpanded = proxyConfigOpen || proxyConfigForcedOpen @@ -256,7 +259,11 @@ export function AdvancedNetworkSettingsSection({
- + - + {/* Mounted while closed so a settings jump can find and reveal the proxy fields. */} +
- updateSettings({ nodeDistMirrorUrl })} - /> +
+ updateSettings({ nodeDistMirrorUrl })} + /> +
diff --git a/src/renderer/src/components/settings/agents/agents-search.ts b/src/renderer/src/components/settings/agents/agents-search.ts index 0acfc7eb207..39c19793892 100644 --- a/src/renderer/src/components/settings/agents/agents-search.ts +++ b/src/renderer/src/components/settings/agents/agents-search.ts @@ -101,6 +101,7 @@ const AGENT_CHAT_CHOICE_SEARCH_ENTRY_ID = 'agent-chat-choice' export const AGENTS_INSTALLED_SECTION_ID = 'agents-installed' /** The top Chat or Terminal tiles that set this computer's default. */ export const AGENT_DEFAULT_SURFACE_SECTION_ID = 'agents-default-surface' +export const AGENT_PERMISSIONS_SECTION_ID = 'agents-permissions' const AGENT_WORKSPACE_TRUST_SEARCH_ENTRY_ID = 'agent-workspace-trust' const AGENT_RUNTIME_SEARCH_ENTRY_ID = 'agent-runtime' const CODEX_TERMINAL_SERVER_ISOLATION_SEARCH_ENTRY_ID = 'codex-terminal-server-isolation' @@ -212,6 +213,7 @@ const getAllAgentsPaneSearchEntries = createLocalizedCatalog(() => [ keywords: getAgentAwakeSearchKeywords() }, { + targetSectionId: AGENT_PERMISSIONS_SECTION_ID, title: translate('auto.components.settings.agents.search.788559d398', 'Agent permissions'), description: translate( 'settings.agents.permissionsSearchDescription', diff --git a/src/renderer/src/components/settings/agents/launch/AgentLaunchDefaultsGroup.tsx b/src/renderer/src/components/settings/agents/launch/AgentLaunchDefaultsGroup.tsx index f87d0a357aa..a6c67b3a2f8 100644 --- a/src/renderer/src/components/settings/agents/launch/AgentLaunchDefaultsGroup.tsx +++ b/src/renderer/src/components/settings/agents/launch/AgentLaunchDefaultsGroup.tsx @@ -1,5 +1,6 @@ import { useTranslate } from '@/i18n/use-translate' import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { AGENT_PERMISSIONS_SECTION_ID } from '../agents-search' export function AgentLaunchDefaultsGroup({ description, @@ -14,7 +15,7 @@ export function AgentLaunchDefaultsGroup({ title={t('settings.agents.newAgentsTitle', 'New agents')} description={description} > -
+
{permissions}
diff --git a/src/renderer/src/components/settings/appearance/AppearanceInterfaceSection.tsx b/src/renderer/src/components/settings/appearance/AppearanceInterfaceSection.tsx index b58d5a5e154..eed1e8fd0c7 100644 --- a/src/renderer/src/components/settings/appearance/AppearanceInterfaceSection.tsx +++ b/src/renderer/src/components/settings/appearance/AppearanceInterfaceSection.tsx @@ -17,6 +17,7 @@ import { FontAutocomplete } from './fonts/FontAutocomplete' import { SettingsRow, SettingsSwitchRow } from '../form-controls/SettingsFormControls' import { DEFAULT_APP_FONT_FAMILY } from '../../../../../shared/constants' import { + APPEARANCE_EDITOR_THEME_SETTING_ID, getCodeEditorThemeEntries, getLanguageEntries, getThemeEntries, @@ -200,6 +201,8 @@ export function AppearanceInterfaceSection({ {editorThemeSlots.map((slot) => ( [ { + targetSectionId: APPEARANCE_EDITOR_THEME_SETTING_ID, title: translate('settings.appearance.editorTheme.title', 'Editor theme'), description: translate( 'settings.appearance.editorTheme.description', diff --git a/src/renderer/src/components/settings/browser/use/BrowserUsePane.tsx b/src/renderer/src/components/settings/browser/use/BrowserUsePane.tsx index c9afe1943f1..320547ff864 100644 --- a/src/renderer/src/components/settings/browser/use/BrowserUsePane.tsx +++ b/src/renderer/src/components/settings/browser/use/BrowserUsePane.tsx @@ -12,7 +12,10 @@ import '../../../../../../shared/constants' import { BROWSER_FAMILY_LABELS } from '../../../../../../shared/browser/browser-family-labels' import { SearchableSetting } from '../../section-layout/SearchableSetting' import { matchesSettingsSearch } from '../../search/settings-search' -import { getBrowserUsePaneSearchEntries } from './browser-use-search' +import { + BROWSER_USE_SETTINGS_TARGET_ID, + getBrowserUsePaneSearchEntries +} from './browser-use-search' import { BrowserUseExamples } from './BrowserUseExamples' import { BrowserUseComputerUseNotice } from './BrowserUseComputerUseNotice' import { BrowserUseEnableSwitch } from './BrowserUseEnableSwitch' @@ -88,7 +91,10 @@ export function BrowserUseSetup({ if (!browserUseEnabled) { return ( -
+

{t('auto.components.settings.BrowserUsePane.507fdae102', 'Agent browser use')} @@ -109,7 +115,7 @@ export function BrowserUseSetup({ } return ( -

+

diff --git a/src/renderer/src/components/settings/browser/use/browser-use-search.ts b/src/renderer/src/components/settings/browser/use/browser-use-search.ts index 1fbecec44a0..875cae9b2e3 100644 --- a/src/renderer/src/components/settings/browser/use/browser-use-search.ts +++ b/src/renderer/src/components/settings/browser/use/browser-use-search.ts @@ -2,8 +2,12 @@ import { translate } from '@/i18n/i18n' import { translateSearchKeyword } from '../../search/settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' +// The steps render only after Agent browser use is on, so both results land on its block. +export const BROWSER_USE_SETTINGS_TARGET_ID = 'browser-use' + export const getBrowserUsePaneSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: BROWSER_USE_SETTINGS_TARGET_ID, title: translate( 'auto.components.settings.browser.use.search.e72d500ca5', 'Install browser use skill' @@ -38,6 +42,7 @@ export const getBrowserUsePaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: BROWSER_USE_SETTINGS_TARGET_ID, title: translate( 'auto.components.settings.browser.use.search.9e2bfa1405', 'Import browser cookies' diff --git a/src/renderer/src/components/settings/cli/install/CliSection.tsx b/src/renderer/src/components/settings/cli/install/CliSection.tsx index c332c64cfe4..0df47b98d3a 100644 --- a/src/renderer/src/components/settings/cli/install/CliSection.tsx +++ b/src/renderer/src/components/settings/cli/install/CliSection.tsx @@ -22,6 +22,7 @@ import { } from '../../../../lib/skills/runtime/skill-runtime-command' import { WslCliRegistration } from './WslCliRegistration' import { SettingsGroup } from '../../section-layout/SettingsGroup' +import { GENERAL_SETTING_ANCHORS } from '../../general/general-setting-anchors' import { useCliRegistrationActions } from './use-cli-registration-actions' import { useLocalCliSkillFreshnessName } from '../../../../hooks/skills/freshness/use-local-cli-skill-freshness-name' import { translate } from '@/i18n/i18n' @@ -303,23 +304,25 @@ export function CliSection({

{!isBrowserManaged ? ( - +
+ +
) : null} diff --git a/src/renderer/src/components/settings/computer-use/ComputerUsePane.tsx b/src/renderer/src/components/settings/computer-use/ComputerUsePane.tsx index f71244a5ffe..af9871efc93 100644 --- a/src/renderer/src/components/settings/computer-use/ComputerUsePane.tsx +++ b/src/renderer/src/components/settings/computer-use/ComputerUsePane.tsx @@ -11,6 +11,7 @@ import { Button } from '../../ui/button' import { Badge } from '../../ui/badge' import { ComputerUseSkillSetupPanel } from './ComputerUseSkillSetupPanel' import { SettingsGroup } from '../section-layout/SettingsGroup' +import { COMPUTER_USE_SETTINGS_TARGET_ID } from './computer-use-search' import { translate } from '@/i18n/i18n' import { useTranslate, type Translate } from '@/i18n/use-translate' @@ -292,7 +293,7 @@ export function ComputerUsePane(): React.JSX.Element { return (
{isMac ? ( - +
@@ -384,7 +385,7 @@ export function ComputerUsePane(): React.JSX.Element { ) : null} - + {/* The inline panel adds pt-1.5 of its own; this evens the top inset with the bottom. */}
diff --git a/src/renderer/src/components/settings/computer-use/computer-use-search.ts b/src/renderer/src/components/settings/computer-use/computer-use-search.ts index 20b2d24b443..cd223e2a9ce 100644 --- a/src/renderer/src/components/settings/computer-use/computer-use-search.ts +++ b/src/renderer/src/components/settings/computer-use/computer-use-search.ts @@ -2,8 +2,12 @@ import { translate } from '@/i18n/i18n' import { translateSearchKeyword } from '../search/settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' +// The pane overview lands on its first card: macOS permissions, else the skill setup. +export const COMPUTER_USE_SETTINGS_TARGET_ID = 'computer-use-overview' + export const getComputerUsePaneSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: COMPUTER_USE_SETTINGS_TARGET_ID, title: translate('auto.components.settings.computer.use.search.c3c3d70b5e', 'Computer use'), description: translate( 'auto.components.settings.computer.use.search.9210db582b', diff --git a/src/renderer/src/components/settings/developer-permissions/DeveloperPermissionsPane.tsx b/src/renderer/src/components/settings/developer-permissions/DeveloperPermissionsPane.tsx index 8c12f0677bb..b4a7eedaec6 100644 --- a/src/renderer/src/components/settings/developer-permissions/DeveloperPermissionsPane.tsx +++ b/src/renderer/src/components/settings/developer-permissions/DeveloperPermissionsPane.tsx @@ -30,10 +30,10 @@ import { showDeveloperPermissionRequestNotice } from './developer-permission-req import { TerminalTccAttributionNotice } from './TerminalTccAttributionNotice' import { SETTINGS_GROUP_ROW_BLEED_CLASS, SettingsGroup } from '../section-layout/SettingsGroup' import { cn } from '@/lib/class-names' - -type DeveloperPermissionsPaneProps = { - highlightedSettingId?: string | null -} +import { + DEVELOPER_PERMISSIONS_OVERVIEW_ID, + getDeveloperPermissionSettingId +} from './developer-permissions-search' type PermissionDefinition = { id: DeveloperPermissionId @@ -223,9 +223,7 @@ const PERMISSIONS: PermissionDefinition[] = [ } ] -export function DeveloperPermissionsPane({ - highlightedSettingId = null -}: DeveloperPermissionsPaneProps): React.JSX.Element { +export function DeveloperPermissionsPane(): React.JSX.Element { const t = useTranslate() const [states, setStates] = useState([]) const [loading, setLoading] = useState(true) @@ -318,6 +316,7 @@ export function DeveloperPermissionsPane({
@@ -342,16 +341,15 @@ export function DeveloperPermissionsPane({ const state = stateById.get(permission.id) const status = state?.status const pending = pendingId === permission.id - const settingId = `developer-permissions-${permission.id}` + const settingId = getDeveloperPermissionSettingId(permission.id) return (
diff --git a/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.ts b/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.ts index 25ad8d28707..56786177a4e 100644 --- a/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.ts +++ b/src/renderer/src/components/settings/developer-permissions/developer-permissions-search.ts @@ -1,9 +1,17 @@ import { translate } from '@/i18n/i18n' import { translateSearchKeyword } from '../search/settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' +import type { DeveloperPermissionId } from '../../../../../shared/developer-permissions-types' + +export const DEVELOPER_PERMISSIONS_OVERVIEW_ID = 'developer-permissions-overview' + +export function getDeveloperPermissionSettingId(id: DeveloperPermissionId): string { + return `developer-permissions-${id}` +} export const getDeveloperPermissionsPaneSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: DEVELOPER_PERMISSIONS_OVERVIEW_ID, title: translate( 'auto.components.settings.developer.permissions.search.ad085fa291', 'Developer permissions' @@ -36,6 +44,7 @@ export const getDeveloperPermissionsPaneSearchEntries = createLocalizedCatalog(( ] }, { + targetSectionId: getDeveloperPermissionSettingId('microphone'), title: translate( 'auto.components.settings.developer.permissions.search.16490ec4cd', 'Microphone and camera' @@ -80,6 +89,7 @@ export const getDeveloperPermissionsPaneSearchEntries = createLocalizedCatalog(( ] }, { + targetSectionId: getDeveloperPermissionSettingId('screen'), title: translate( 'auto.components.settings.developer.permissions.search.70ffdc218d', 'Screen recording and accessibility' @@ -144,6 +154,7 @@ export const getDeveloperPermissionsPaneSearchEntries = createLocalizedCatalog(( ] }, { + targetSectionId: getDeveloperPermissionSettingId('local-network'), title: translate( 'auto.components.settings.developer.permissions.search.3363889768', 'LAN, USB, and Bluetooth' diff --git a/src/renderer/src/components/settings/form-controls/SettingsFormControls.tsx b/src/renderer/src/components/settings/form-controls/SettingsFormControls.tsx index b88f93d1bb8..995a0a52a82 100644 --- a/src/renderer/src/components/settings/form-controls/SettingsFormControls.tsx +++ b/src/renderer/src/components/settings/form-controls/SettingsFormControls.tsx @@ -17,6 +17,7 @@ type SettingsRowProps = { className?: string labelId?: string alignTop?: boolean + id?: string } export function SettingsRow({ @@ -25,10 +26,12 @@ export function SettingsRow({ control, className, labelId, - alignTop + alignTop, + id }: SettingsRowProps): React.JSX.Element { return (
- state.settingsSearchInputQuery)} - onChange={(event) => setQuery(event.target.value)} - /> [0] & { initialQuery?: string } = {}) { - useAppStore.setState({ - settingsSearchQuery: initialQuery, - settingsSearchInputQuery: initialQuery - }) +function renderNavigationHarness(props: Parameters[0] = {}) { return render() } -it('opens an old subsection link from another child with a conflicting query, including repeated selection', async () => { +it('opens an old subsection link from another child, including repeated selection', async () => { const scroll = vi.spyOn(HTMLElement.prototype, 'scrollIntoView') - const { container } = renderNavigationHarness({ initialQuery: 'font' }) + const { container } = renderNavigationHarness() fireEvent.click(screen.getByRole('button', { name: 'Open app settings link' })) const add = await screen.findByRole('button', { name: 'Add app' }) const target = container.querySelector('#general-open-in-apps') @@ -131,21 +119,3 @@ it('opens an old subsection link from another child with a conflicting query, in fireEvent.click(screen.getByRole('button', { name: 'Open app settings link' })) await waitFor(() => expect(scroll.mock.contexts).toContain(target)) }) - -it('keeps an incomplete Open in draft when search temporarily replaces its form', async () => { - const onWrite = vi.fn() - renderNavigationHarness({ initialPage: 'open-in', initialQuery: '', onWrite }) - fireEvent.keyDown(screen.getByRole('button', { name: 'Add app' }), { key: 'ArrowDown' }) - fireEvent.click(await screen.findByRole('menuitem', { name: /Custom/ })) - fireEvent.change(screen.getByPlaceholderText('App name'), { target: { value: 'Draft editor' } }) - fireEvent.blur(screen.getByPlaceholderText('App name')) - fireEvent.change(screen.getByRole('textbox', { name: 'Search settings' }), { - target: { value: 'open in' } - }) - await screen.findByRole('button', { name: /Open in apps/i }) - fireEvent.change(screen.getByRole('textbox', { name: 'Search settings' }), { - target: { value: '' } - }) - expect(await screen.findByDisplayValue('Draft editor')).toBeTruthy() - expect(onWrite).not.toHaveBeenCalled() -}) diff --git a/src/renderer/src/components/settings/general/general-search.ts b/src/renderer/src/components/settings/general/general-search.ts index 6824faaaf5e..d2162087bb9 100644 --- a/src/renderer/src/components/settings/general/general-search.ts +++ b/src/renderer/src/components/settings/general/general-search.ts @@ -167,6 +167,7 @@ export const getGeneralCliSearchEntries = createLocalizedCatalog(() => [ targetSectionId: 'cli' }, { + targetSectionId: GENERAL_SETTING_ANCHORS.cliSkill, title: translate('settings.general.cli.skillTitle', 'Orca skill for agents'), description: translate( 'auto.components.settings.general.search.244e3fb4c8', diff --git a/src/renderer/src/components/settings/general/general-setting-anchors.ts b/src/renderer/src/components/settings/general/general-setting-anchors.ts index aa8acd95649..6f8fb32e979 100644 --- a/src/renderer/src/components/settings/general/general-setting-anchors.ts +++ b/src/renderer/src/components/settings/general/general-setting-anchors.ts @@ -6,6 +6,7 @@ export const GENERAL_SETTING_ANCHORS = { nesting: 'general-workspace-nesting', worktrees: 'general-global-worktree-visibility', openIn: 'general-open-in-apps', + cliSkill: 'general-cli-skill', autoSave: 'general-auto-save', autoSaveDelay: 'general-editor-auto-save-delay', formatOnSave: 'general-editor-format-on-save', @@ -28,6 +29,14 @@ export const GENERAL_SETTING_ANCHORS = { reviewNotes: 'general-editor-review-notes' } as const +// A search result for a General sub-page opens that page at this row. +export const GENERAL_SUB_PAGE_ENTRY_ANCHORS: readonly (readonly [GeneralSubPage, string])[] = [ + ['folder', GENERAL_SETTING_ANCHORS.folder], + ['worktrees', GENERAL_SETTING_ANCHORS.worktrees], + ['open-in', GENERAL_SETTING_ANCHORS.openIn], + ['editor', GENERAL_SETTING_ANCHORS.autoSaveDelay] +] + export const GENERAL_SETTING_OWNERS: Readonly> = { [GENERAL_SETTING_ANCHORS.folder]: 'folder', [GENERAL_SETTING_ANCHORS.nesting]: 'folder', diff --git a/src/renderer/src/components/settings/integrations/IntegrationsPane.tsx b/src/renderer/src/components/settings/integrations/IntegrationsPane.tsx index 821ebd8249b..f107475bc32 100644 --- a/src/renderer/src/components/settings/integrations/IntegrationsPane.tsx +++ b/src/renderer/src/components/settings/integrations/IntegrationsPane.tsx @@ -12,12 +12,20 @@ import { LinearIntegrationCard } from '../../integrations/task-trackers/linear-i import { IntegrationCardPresentationProvider } from '../../integrations/presentation/integration-card-presentation' import { useIntegrationProviderStatusRefresh } from '../../../hooks/use-integration-provider-status-refresh' import { SettingsGroup } from '../section-layout/SettingsGroup' +import { INTEGRATION_SETTING_TARGET_IDS } from './integrations-search' import { useTranslate } from '@/i18n/use-translate' // Flat presentation turns each provider into one row: -mx-4 cancels its inset, and inner rules are dropped so only rows divide. -function ProviderRow({ children }: { children: React.ReactNode }): React.JSX.Element { +function ProviderRow({ + id, + children +}: { + /** Search anchor; task-provider cards carry their own. */ + id?: string + children: React.ReactNode +}): React.JSX.Element { return ( -
+
{children}
) @@ -37,19 +45,19 @@ export function IntegrationsPane(): React.JSX.Element { 'Connect the source hosts Orca can use for pull requests, merge requests, checks, and review status.' )} > - + - + - + - + - + diff --git a/src/renderer/src/components/settings/integrations/integrations-search.ts b/src/renderer/src/components/settings/integrations/integrations-search.ts index 0ce31126680..b828997fd68 100644 --- a/src/renderer/src/components/settings/integrations/integrations-search.ts +++ b/src/renderer/src/components/settings/integrations/integrations-search.ts @@ -1,9 +1,25 @@ import { translate } from '@/i18n/i18n' import { translateSearchKeyword } from '../search/settings-search-keywords' import { createLocalizedCatalog } from '@/i18n/localized-catalog' +import { + JIRA_INTEGRATION_SECTION_ID, + LINEAR_INTEGRATION_SECTION_ID +} from '../../integrations/task-trackers/task-provider-integration-section-ids' + +// Provider rows carry these ids so search results can scroll to and highlight them. +export const INTEGRATION_SETTING_TARGET_IDS = { + github: 'integrations-github', + gitlab: 'integrations-gitlab', + bitbucket: 'integrations-bitbucket', + azuredevops: 'integrations-azure-devops', + gitea: 'integrations-gitea', + jira: JIRA_INTEGRATION_SECTION_ID, + linear: LINEAR_INTEGRATION_SECTION_ID +} as const export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.github, title: translate( 'auto.components.settings.integrations.search.a17a94d0b0', 'GitHub integration' @@ -25,6 +41,7 @@ export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.gitlab, title: translate( 'auto.components.settings.integrations.search.cd74b2f587', 'GitLab integration' @@ -51,6 +68,7 @@ export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.bitbucket, title: translate( 'auto.components.settings.integrations.search.9e238582e8', 'Bitbucket integration' @@ -95,6 +113,7 @@ export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.azuredevops, title: translate( 'auto.components.settings.integrations.search.b3efe7ab71', 'Azure DevOps integration' @@ -128,6 +147,7 @@ export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.gitea, title: translate( 'auto.components.settings.integrations.search.86a08aa60c', 'Gitea integration' @@ -157,6 +177,7 @@ export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.jira, title: translate('auto.components.settings.integrations.search.d8906d60a9', 'Jira integration'), description: translate( 'auto.components.settings.integrations.search.76f6af7c57', @@ -191,6 +212,7 @@ export const getIntegrationsPaneSearchEntries = createLocalizedCatalog(() => [ ] }, { + targetSectionId: INTEGRATION_SETTING_TARGET_IDS.linear, title: translate( 'auto.components.settings.integrations.search.d905b3ecee', 'Linear integration' diff --git a/src/renderer/src/components/settings/mobile/MobilePane.tsx b/src/renderer/src/components/settings/mobile/MobilePane.tsx index 466f7ea4dde..df14e8be444 100644 --- a/src/renderer/src/components/settings/mobile/MobilePane.tsx +++ b/src/renderer/src/components/settings/mobile/MobilePane.tsx @@ -27,7 +27,7 @@ import type { MobileRelayMintFailure } from '../../../../../shared/remote-runtim import { useMobilePairingConnectionMode } from '../../mobile/use-mobile-pairing-connection-mode' import { useMobilePairingAddressPreference } from '../../mobile/use-mobile-pairing-address-preference' import { usePairingOfferExpiry } from '../../mobile/use-pairing-offer-expiry' -import { shouldOpenMobilePairingAddress } from './mobile-pane-search' +import { MOBILE_SETTING_ANCHORS, shouldOpenMobilePairingAddress } from './mobile-pane-search' export function MobilePane(): React.JSX.Element { const t = useTranslate() @@ -349,7 +349,7 @@ export function MobilePane(): React.JSX.Element {
-
+
-
+
updateSettings({ mobileEmulatorEnabled: !enabled })} /> -
+