Agentic UI: Application Settings - #3979
Conversation
There was a problem hiding this comment.
Pull request overview
This PR brings Studio’s application-level settings into the new agentic UI (apps/ui) by adding a dedicated /settings route with tabbed sections (Settings/Usage/Keyboard/Skills/MCP), wiring the necessary connector/query plumbing, and updating sidebar navigation so the user row acts as the primary entry point to Settings.
Changes:
- Adds a new Settings route and a redesigned SettingsView with tabs, save UX (including unsaved-change blocking), and keyboard shortcut handling.
- Introduces new data-layer capabilities (app globals, snapshot usage + delete-all, WordPress skills, offline detection) across connectors and queries.
- Updates sidebar/user-menu/site-list behavior and styling to treat Settings as a first-class navigation destination.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/ui/src/ui-classic/router/route-settings/index.tsx | Updates settings route search param parsing to normalize tab ids. |
| apps/ui/src/ui-classic/router/layout-root/index.tsx | Adds root-level listener to open Settings from connector events. |
| apps/ui/src/lib/docs-links.ts | Adds new docs link keys for CLI/MCP/Skills. |
| apps/ui/src/index.css | Adds select z-index token + tweaks highlighted listbox row styling for dark chrome. |
| apps/ui/src/hooks/use-offline.ts | Introduces a hook for online/offline guards in UI flows. |
| apps/ui/src/data/queries/use-wordpress-skills.ts | Adds query + mutations for global WordPress skill status/actions. |
| apps/ui/src/data/queries/use-snapshots.ts | Adds per-user query keys + snapshot usage query + delete-all mutation helpers. |
| apps/ui/src/data/queries/use-app-globals.ts | Adds app globals query (platform/app metadata) with infinite staleness. |
| apps/ui/src/data/core/types.ts | Extends Connector contract + adds new types (AppGlobals, SnapshotUsage, SkillStatus, etc.). |
| apps/ui/src/data/core/index.ts | Re-exports newly added data-core types. |
| apps/ui/src/data/core/connectors/ipc/index.ts | Implements new Connector methods in IPC mode (snapshot usage, prefs, skills, settings events, etc.). |
| apps/ui/src/data/core/connectors/hosted/index.ts | Implements hosted/browser-mode behavior for new Connector methods (mostly no-ops / placeholders). |
| apps/ui/src/components/user-menu/style.module.css | Restyles user menu row to behave like a first-class selected/active navigation row. |
| apps/ui/src/components/user-menu/index.tsx | Makes clicking the user row open Settings directly; adds “active” state and removes theme toggle menu. |
| apps/ui/src/components/user-menu/index.test.tsx | Adds coverage for Settings navigation + active highlighting behavior. |
| apps/ui/src/components/site-list/style.module.css | Aligns site row hover/active colors with new sidebar tokens. |
| apps/ui/src/components/site-list/index.tsx | Prevents MRU auto-expansion when not on the dashboard root (e.g., in Settings). |
| apps/ui/src/components/site-list/index.test.tsx | Adds tests for MRU expansion behavior on / vs /settings. |
| apps/ui/src/components/sidebar-layout/style.module.css | Adds shared sidebar row background tokens; fixes collapsed toggle interaction via pointer-events. |
| apps/ui/src/components/sidebar-layout/index.tsx | Adjusts children rendering order to keep overlay/toggle layering correct. |
| apps/ui/src/components/settings-view/style.module.css | Major styling update for new settings layout (header tabs, panels, rows, meters, code block). |
| apps/ui/src/components/settings-view/preferences.ts | Extracts preferences form normalization/diff logic into helpers. |
| apps/ui/src/components/settings-view/preferences.test.ts | Adds focused tests for new preferences helper behavior. |
| apps/ui/src/components/settings-view/index.tsx | Replaces legacy preferences DataForm with full tabbed settings experience and new panels (Usage/Keyboard/Skills/MCP). |
| apps/ui/src/components/settings-view/index.test.tsx | Adds broad coverage for settings save/dirty-blocking/tab rendering/hosted hiding/usage/skills/mcp copy/account actions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
📊 Performance Test ResultsComparing 0f64d77 vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
|
|
||
| fireEvent.click( screen.getByRole( 'button', { name: 'Install' } ) ); | ||
| fireEvent.click( screen.getByRole( 'button', { name: 'Remove' } ) ); | ||
| fireEvent.click( screen.getByRole( 'button', { name: 'Install all' } ) ); |
| async confirmDeleteAllPreviewSites(): Promise< boolean > { | ||
| const CANCEL_BUTTON_INDEX = 0; | ||
| const DELETE_BUTTON_INDEX = 1; | ||
| const { response } = ( await ipcApi.showMessageBox( { |
katinthehatsite
left a comment
There was a problem hiding this comment.
Some general notes:
- Changing languages did nothing for me: after I saved my settings, the app remained in English
- I also think we could adjust the position of the
Report an issueandDocs- I would expect these to go in some sort of general settings rather than in my account settings - Finally, I am finding this to be quite a big PR so it is hard to review - do you think it could be split into smaller PRs?
bcotrim
left a comment
There was a problem hiding this comment.
Some feedback from my testing:
- Do we need the "Save" button? Couldn't we save the changes automatically as the user changes it? I am aware that some options like changing language, would have some bigger side effects, but I consider it to be an UX improvement.
- Language setting doesn't appear to be working
- ⌘ ↩ is adding a newline, ↩ send the message
- ⌘ [ and ⌘ ] are not so easy to use in non US keyboards, could we use arrows instead?
- Should we have a back button to navigate away from the settings and back to where the user was?
- The settings icon in the left corner only appears for logged out users, could we make it consistent?
| [] | ||
| const handleColorSchemeChange = useCallback( | ||
| ( colorScheme: ColorScheme ) => { | ||
| if ( ! isColorScheme( colorScheme ) ) { |
There was a problem hiding this comment.
isColorScheme can’t fire — colorScheme is already typed ColorScheme and the only caller passes a literal from colorSchemeElements(). Drop the function + guard.
|
Heads up: #4140 also touches the Agentic UI settings by adding a dropdown menu that lets users decide what happens to running sites when quitting Studio. |
) ## Related issues - Related to STU-1882 ## How AI was used in this PR Implemented with Claude Code (design ported from the settings redesign explorations in #3975/#3979, adapted to auto-save). All changes were reviewed, and behavior was verified manually in the app plus unit tests. ## Proposed Changes First PR of the application-settings series for the new Studio experience. - **Settings now save instantly.** Changing any option persists it immediately — no more Save button, dirty-state tracking, or losing changes when navigating away. Per the settings design post: "options are saved when changed." - **Redesigned settings screen.** A title-bar-height header with a centered tab list replaces the in-content title and tabs, and the preferences are grouped into cards: a General section (Appearance as a System/Light/Dark segmented control, Language, Preferred editor, Preferred terminal, When quitting with running sites) and a Studio experience section with the "Switch to classic" action. Save errors surface inline in the affected section. - **Settings no longer show stale values.** The screen previously trusted a forever-fresh persisted cache, so preferences changed elsewhere (or fields added since) could show wrong/empty values until the cache expired. It now reconciles with the main process on mount/focus. - **Editor/terminal selections match the classic UI's behavior.** Only installed apps are offered and the effective (fallback-resolved) app is always shown, so a brand-new user sees a working "open in" option rather than nothing. The site actions menu now names the apps ("Open in Cursor", "Open in Terminal") and hides an action only when no supported app is available. - **"Stop, restart on next launch" now works in the new Studio experience.** The relaunch half of that behavior only lived in the classic UI; the new shell now restarts the flagged sites on boot too. | Dark | Light | |--------|--------| | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/9f95f2d0-1964-4c7f-8849-fffac00fe9f4" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/767a4950-8a6e-4216-9c9d-c2e612eadbe9" /> | **Note:** Some settings are intentionally left out and will be addressed in follow-up PRs ## Testing Instructions 1. Enable the new Studio experience (beta feature flag) and open **Settings**. 2. Change each preference (appearance, language, editor, terminal, quit behavior) — each should apply immediately with no Save button. Language reloads the app in the new locale. 3. Navigate away and back, and restart the app — values should persist and match what the classic UI's settings dialog shows. 4. Right-click a site in the sidebar — the menu should read "Open in <your editor>" / "Open in <your terminal>" and both should open. 5. Set "When quitting with running sites" to "Stop, restart on next launch", start a site, quit Studio, relaunch — the site should start again automatically. 6. Verify the settings screen in both light and dark mode. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Related issues - Related to STU-1882 ## How AI was used in this PR Implemented with Claude Code (part of the STU-1882 settings series), guided by the exploration in #3979. Code, styles, and tests were reviewed before pushing; ## Proposed Changes - Adds an **MCP** tab to the Agentic UI settings so users can connect external AI assistants to Studio without hunting through docs: a short explanation of what MCP is for, a "Learn more" link to the Studio MCP docs, and the ready-to-paste `wordpress-studio` server configuration JSON with a one-click copy button. - The copy button now only shows "Copied" after the text has actually landed on the clipboard, and announces it to screen readers. If copying fails, the button no longer claims success — this also fixes the same silent-failure behavior for copying chat messages and markdown code blocks, which shared the button. ## Testing Instructions 1. Launch Studio with the Agentic UI enabled and open **Settings**. 2. Switch to the new **MCP** tab: verify the intro text, the "Learn more" link (opens the MCP docs in your browser), and the config JSON code block. 3. Click the copy icon over the code block: a "Copied" check state should appear for ~2s, and pasting should produce the JSON. 4. Check the tab in both light and dark mode. 5. Copying a chat message or a markdown code block in a session should still work as before. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Related issues - Related to [STU-1882](https://linear.app/a8c/issue/STU-1882) ## How AI was used in this PR Implemented with Claude Code following the auto-save settings patterns from #4219, using the Save-button-era exploration in #3979 as a reference. All code reviewed, linted, typechecked, and unit-tested locally. ## Proposed Changes - A new **Default site directory** row in Settings shows the current folder and opens the native folder picker; picking a folder saves immediately, matching the auto-save behavior of the other preferences. The value is shared with the classic UI, so changes made in either stay in sync. | Light | Dark | |--------|--------| | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/4e22e5f9-3232-4b93-9514-de67bf116d7d" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/1de28d2a-c9b8-4f91-b181-c8a10e94ac1c" /> | ## Testing Instructions 1. Enable the new Studio experience and open **Settings**. 2. Verify the **Default site directory** row shows your current default (or "Choose a folder…" if unset). 3. Click it, pick a folder — the choice should persist immediately (no Save button), and survive an app restart. 4. Cancel the picker — nothing should change or save. 5. Switch to the classic UI → Preferences: the same directory should appear there (and vice versa). 6. Create a new site in the agentic UI: the proposed path should live under the chosen directory. 8. Check the settings screen in both light and dark mode. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Related issues - Related to STU-1882 (PR 4 of the Agentic UI application-settings series) ## How AI was used in this PR Implemented with Claude Code, adapting the `AccountInformationSection` from the #3979 exploration to trunk's current auto-save settings layout. Code, styles, and tests reviewed and verified by me in the running app. ## Proposed Changes - Adds an **Account** card to the Agentic UI Settings (preferences) tab, so account state and quick help links are visible right where the rest of the app configuration lives. - The card header offers **Docs** and **Report an issue** shortcuts that open in the browser. - The sidebar user-menu is intentionally untouched — its dropdown keeps offering the same actions. | Light | Dark | |--------|--------| | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/e33672f7-a2e2-42e8-841e-51f82fb133a9" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/8267c73a-77af-413a-9374-af045be7c415" /> | | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/fd36d6bb-10e9-489a-af27-2c866f27c12d" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/91e3e19f-ef4f-490b-8662-76c6f717b52f" /> | ## Testing Instructions 1. Launch Studio with the Agentic UI enabled and open **Settings**. 2. Signed out: the Account card shows the "WordPress.com account" heading, the login blurb, and a **Log in** button that starts the OAuth flow. 3. Signed in: the card shows your Gravatar, display name, and email; **Log out** signs you out and the card flips back to the signed-out state. 4. Click **Docs** and **Report an issue** — each opens the expected page in your default browser. 5. Verify the card in both light and dark mode, and at a narrow window width (the card stacks below 640px). ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors?
## Related issues - Related to STU-1882 ## How AI was used in this PR Implemented with Claude Code from the STU-1882 series plan, porting the Skills panel from the exploration PR #3975/#3979 and reworking it per review feedback (toggles instead of Install/Remove buttons, optimistic updates). All code reviewed and visually verified in light + dark mode. ## Proposed Changes Adds a **Skills** tab to the Agentic UI application settings, letting users manage WordPress agent skills globally — applied to all existing and future sites. - Each skill is a single row with a toggle: on installs it to every site, off removes it. State is optimistic, so toggles respond instantly and roll back if the operation fails. - An **Install all** button in the panel header installs every remaining skill at once; it disappears once everything is installed (with reserved space, so the layout doesn't jump). - Intro copy explains what skills are, with a Learn more link to the docs. | Light | Dark | |--------|--------| | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/184e9ba6-4b6b-4e18-9f9c-50e1101bb5df" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/fc435d36-f988-4519-a07f-e704afdecaac" /> | ## Testing Instructions 1. Enable the Agentic UI and open **Settings → Skills**. 2. Toggle a skill on: within a moment, the skill appears under `<site>/.agents/skills/<skill-id>/` for each site (e.g. `ls ~/Studio/<site>/.agents/skills/`) and its id is added to `selectedSkills` in `~/.studio/shared.json`. 3. Toggle it off: the files and the `selectedSkills` entry are removed. 4. Click **Install all**: all remaining skills install sequentially; toggles disable and the button shows a loading state while it runs; the button disappears when everything is installed (no layout jump in the header, no flashing on individual toggles). 5. Verify light + dark mode. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? --------- Co-authored-by: Volodymyr Makukha <nei.css@gmail.com>
## Related issues - Related to STU-1882 ## How AI was used in this PR Implemented with Claude Code (connector contract, queries, panel UI, and tests), following the patterns established in #3979 and reviewed by the author. ## Proposed Changes Adds a **Usage** tab to the agentic UI settings so users can see what they're consuming without leaving Studio: - An **AI credits** section explaining credits are free while Studio Code is in Alpha, with a full placeholder meter. - A **Preview sites** section showing "N of M active preview sites" with a progress bar, backed by the WordPress.com usage endpoint (with a fallback to counting local snapshots when usage isn't available). An overflow menu offers **Delete all preview sites**, guarded by a native confirmation dialog, with an inline error if deletion fails. - The delete action is disabled when there's nothing to delete, while loading, when the account can't create previews, or when offline (the menu label explains the offline case). - Signed-out users see a prompt to log in to view their usage. ## Testing Instructions 1. Enable the agentic UI and open **Settings → Usage**. 2. Signed out: verify the "Log in to view preview site usage for your account." prompt and that the Log in button starts the OAuth flow. 3. Signed in with preview sites: verify the "N of M active preview sites" count and progress bar match `studio preview list`. 4. Open the ⋯ menu → **Delete all preview sites** → verify the native warning dialog; cancel leaves everything untouched, confirming deletes all previews and the meter drops to 0. 5. With 0 preview sites (or offline via Network Link Conditioner / Wi-Fi off): verify the menu action is disabled; when offline the label explains why. 6. Check the tab in both light and dark mode. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors? --------- Co-authored-by: Antonio Sejas <antonio.sejas@automattic.com>
## Related issues - Related to STU-1882 (PR 3 of the agentic-UI application-settings series; ported from #3979) ## How AI was used in this PR Implemented with Claude Code from a scoped brief: it ported the `StudioCliSection` from #3979 to the auto-save settings model, extended the connector contract across the three connectors, and wrote the tests. Two behaviors were hardened based on manual testing feedback: a "cancel behind the native dialog leaves the toggle stale" bug (fixed by refetching the installed state after saving) and standalone-CLI handling (toggle disabled with an explanatory tooltip). All code was human-reviewed and manually tested on macOS. ## Proposed Changes Adds a **Studio CLI** section to the new (agentic UI) application settings, so users can install or uninstall the `studio` terminal command without switching back to the classic UI — the last preference from the classic settings that had no equivalent in the new experience. - The toggle saves immediately (same auto-save behavior as the rest of the settings) and links to the CLI docs. - Install/uninstall can be declined or fail behind native OS dialogs (permissions, dev-build warnings) while the IPC call still resolves, so after every change the UI re-checks the actual installed state and the toggle settles on reality — cancelling the native dialog returns the toggle to its saved position instead of showing a state that never happened. Failures surface an inline error in the section. - When the `studio` command is a standalone (curl) install, the app never installs over or uninstalls it — previously the toggle would appear to work but silently no-op. The toggle is now disabled in that case, with a tooltip explaining that the CLI is managed outside the app and pointing at `studio uninstall`. This exposes the existing externally-managed detection from the installation managers over IPC (`isStudioCliExternallyManaged`). - The section is hidden where installing a CLI isn't possible: browser hosts (`studio ui` / hosted) and Windows Store builds. This introduces a minimal `getAppGlobals()` connector method (platform + Windows Store flag) for that gating, since this part of #3979 hadn't landed with the earlier settings PRs. | Light | Dark | |--------|--------| | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/40d2aa41-30a9-4e09-adb3-bcac072e856b" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/520e4452-cb26-4eee-b560-4a54108f26e3" /> | | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/7510ed2c-ce07-46b4-a4b4-c970cc3b9506" /> | <img width="3248" height="2122" alt="image" src="https://github.com/user-attachments/assets/69dcf281-8ddd-4e67-9a34-081bb188a4eb" /> | ## Testing Instructions Note: this PR adds a main-process IPC handler, so a full app restart (not hot reload) is required. 1. Enable the new Studio experience and open **Settings**. 2. Confirm a **Studio CLI** card appears below **Account**, with the toggle reflecting whether `~/.local/bin/studio` exists (`where studio`). 3. Turn the toggle **on**, accept the dev-build warning → `studio` resolves in a new terminal and the toggle stays on. 4. Turn it **off**, accept → the symlink is removed and the toggle stays off. 5. Toggle again but press **Cancel** in the native dialog → the toggle returns to its previous position (this was the stale-state bug fixed here). 6. With a standalone curl-installed CLI (symlink at `~/.local/bin/studio` pointing outside the app bundle): the toggle shows on but is disabled. Hovering it shows a tooltip pointing at `studio uninstall` — it should open quickly (~200ms, faster than the app's default tooltip delay, since the disabled control gives no other feedback) and wrap as a compact multi-line bubble (max width 280px) instead of one long line. 7. Check both light and dark mode, including the toggle's checked/unchecked, disabled, and focus styles, plus the tooltip in step 6. ## Pre-merge Checklist - [x] Have you checked for TypeScript, React or other console errors?


Related issues
How AI was used in this PR
Codex helped port the existing Studio application preferences into the new agentic UI, iterate on visual polish with repeated light/dark checks, add focused tests, and run code review passes with a subagent and Claude. I reviewed the resulting behavior and asked for targeted revisions throughout the implementation.
Executive summary
This PR brings app-level Studio settings into the new agentic UI as a dedicated
/settingsroute. It replaces the temporary user-menu preferences surface with a first-class tabbed settings screen covering Settings, Usage, Keyboard, Skills, and MCP.Reviewers should focus on whether the new settings surface fits the agentic UI architecture, keeps desktop-only behavior correctly gated, and preserves existing preferences behavior while adding the new Usage, Skills, and MCP affordances.
Proposed Changes
Review guide
Please review carefully:
appGlobals.platform !== 'browser'; hosted/browser mode should not briefly expose unavailable native controls.diffPreferencesFromSaved()should only send changed fields, preserve null/unset behavior for editor and terminal, and handle unsupported saved locales safely.copyText()resolves and should handle failures without unhandled rejections.apps/uishould use--wpds-color-*tokens, not Studio legacy--color-frame-*tokens.Diff breakdown
Settings surface:
apps/ui/src/components/settings-view/index.tsxapps/ui/src/components/settings-view/style.module.cssapps/ui/src/components/settings-view/preferences.tsNavigation and sidebar:
apps/ui/src/ui-classic/router/route-settings/index.tsxapps/ui/src/ui-classic/router/layout-root/index.tsxapps/ui/src/components/user-menu/*apps/ui/src/components/site-list/*apps/ui/src/components/sidebar-layout/*Data and connector plumbing:
apps/ui/src/data/core/types.tsapps/ui/src/data/core/connectors/ipc/index.tsapps/ui/src/data/core/connectors/hosted/index.tsapps/ui/src/data/queries/use-app-globals.tsapps/ui/src/data/queries/use-snapshots.tsapps/ui/src/data/queries/use-wordpress-skills.tsapps/ui/src/hooks/use-offline.tsTests:
apps/ui/src/components/settings-view/index.test.tsxapps/ui/src/components/settings-view/preferences.test.tsapps/ui/src/components/user-menu/index.test.tsxapps/ui/src/components/site-list/index.test.tsxScreenshots
The hosted UI screenshots below cover the high-level settings surfaces in both light and dark mode, captured at 900px wide with seeded sites in the sidebar. Native-only desktop controls are hidden in hosted mode and are covered by tests.
Settings
Usage
Keyboard
Skills
MCP
Known tradeoffs and follow-ups
apps/ui; it does not migrate legacyapps/studiorenderer settings.Safety checklist
Connectorcontract.Testing Instructions
Pre-merge Checklist