---
title: Settings — Virtual Project Entry
---

# Settings — Virtual Project Entry

## What it is

Settings is reachable from two places, but **both activate the same "Settings" virtual project** — there is no separate full-screen modal:

1. **Gear icon (toolbar)** — `toggle-settings-from-toolbar.ts` → `activate-settings-with-retry.ts` activates the `SETTINGS_PROJECT_ID` virtual project (clicking the gear while already in Settings bounces back to where you came from).

2. **Omniscio group → Settings (projects sidebar)** — selects the same virtual project named "Settings" at the bottom of the Omniscio group. The default sessions sub-sidebar is suppressed; Settings's own sidebar+content composition takes over the panel.

Both entries route to one mounted `<Settings />` panel, so there is only ever a single instance — picking a section from either entry lands in the same place.

## Where to find it

Settings opens from two places, and both land in the same screen. The gear icon in the toolbar opens it, and clicking the gear while you are already in Settings takes you back to where you came from. The second route is the sidebar, where a Settings entry sits at the bottom of the Omniscio group.

Because both routes arrive at one screen, a section picked from either entry always lands in the same place, and there is never a second copy of Settings to keep in step.

On a phone, Settings brings its own two-pane layout when it is shown inside Omniscio's mobile sidebar: a back arrow returns you to the section list within Settings, while the app's usual back gesture returns you to the projects bar. On a normal desktop open the cursor lands in the settings search box so you can type straight away — unless you arrived through a link that points at a particular section, setting or automation, in which case the cursor stays where you navigated.

## How it behaves

### Deep-link callbacks

Deep-link flows reach the single Settings panel through `SettingsPanelAdapter.tsx`, which renders the `<Settings />` component for the VP:

- `automations` section: `automationInitialConfig` + `onAutomationConfigUsed` come from `useWizardNavigationStore` (the adapter SUBSCRIBES, so re-entry while already on the Settings VP still surfaces a new section/config).
- `setup-assistant` section: `onRunAppTour`, `onRunDemoTour` are pulled from a module-level ref (`settings-tour-callbacks.ts`) that `App.tsx` sets, keeping the existing view-switch timing at the App level.

### Mobile behaviour

`Settings.tsx` ships its own two-pane mobile UX driven by `showMobileContent`. When mounted inside the Omniscio mobile sub-sidebar slot, that internal UX renders directly — back-arrow returns to the section list within Settings; Omniscio's back-swipe returns to the projects bar.

### Auto-focus on open (desktop)

On a normal desktop open the cursor lands in the "Search settings…" box so the user can type immediately. The "should we focus?" decision is computed once at first render — before the deep-link consuming effects clear their sources — by `should-autofocus-search.ts`, then applied in a mount effect in `Settings.tsx`. Settings remounts on every open, so it fires each time. It is skipped when:

- on mobile (auto-focus would pop the on-screen keyboard; the mobile variant also never receives `searchInputRef`), or
- the open is deep-linked to a destination — a section (`initialSection`), a specific setting (`pendingSettingId`), or an automation config (`automationInitialConfig`) — so the cursor stays where the user navigated rather than jumping to search.

Locked by `tests/unit/features/settings/should-autofocus-search.test.ts` (decision matrix), render tests in `tests/unit/features/settings/Settings-search-keynav.test.tsx`, and `tests/e2e/ui/settings-search.spec.ts`.

### Settings load

Both entries route to the same mounted `<Settings />` panel, so there is only one instance — no dual-instance copies to keep in sync. The panel uses `useSettingsStore`, which is initialized at app startup; `loadSettings` short-circuits via `useSettingsStore.isInitialized` after the first run, so re-activating Settings never double-fires the IPC call. Settings remounts on every open, re-seeding its React-local `useState<AppSettings>` copy from the store each time.

## For agents

### Where the wiring lives

- Sentinel constant: `SETTINGS_PROJECT_ID` in `src/shared/types.ts`
- Omniscio group membership: `AMC_BUILTIN_PROJECT_PATHS` in `src/shared/types.ts`
- Startup registration: `ensureVirtualProject('Settings', SETTINGS_PROJECT_ID)` in `src/main/index.ts`
- Icon: `ProjectIcon.tsx` (gear, `text-surface-400`)
- Render: integration registry (`ui-registry.ts`) → `SettingsPanelAdapter` (lazy) via `RoutedPanel`. Both the toolbar gear (`toggle-settings-from-toolbar.ts` → `activate-settings-with-retry.ts`) and the sidebar item activate the same `SETTINGS_PROJECT_ID` virtual project — there is no separate always-mounted modal.

## Related

Nothing else in the library documents this screen's plumbing, so begin at [INDEX.md](INDEX.md), the library index, and look for the pages about Settings and about the sidebar's virtual projects.
