Repository navigation
Conversation
Skin and light/dark mode are saved per profile (NousResearch#42286), so on a device with several profiles or gateways every switch repaints the window in that profile's look. That is the right default for people who want each bot to feel distinct, but someone using one desktop for all their bots has no way to say "one theme, everywhere" — they have to re-pick the same theme in every profile and it drifts again the moment one is changed. Add a Theme Scope setting under Appearance → Theme, shown only when the device has more than one profile: - Per profile (default, unchanged): each profile keeps its own skin + mode. - Shared: every profile reads and writes the one global slot — the same slot the default profile already uses — so a pick on any profile paints all of them, including on other gateways. Flipping to Shared promotes the look you are on to the shared one (read through the live profile before the flip, written to the global slot after), so the window never changes under you. Flipping back finds every profile's earlier assignment intact: Shared only ever writes the global slot, never the per-profile record. - themes/context.tsx: `$themeScope` atom persisted at `hermes-desktop-theme-scope-v1`; `skinPref`/`modePref` route through `slotFor()`, so the boot paint and the cross-window `storage` sync pick the scope up without further wiring; `setThemeScope()` does the promote. - settings/appearance-settings.tsx: the scope row (segmented control) and a caption under the theme grid that flips with it. - settings search: the row is a Cmd-K target (theme, scope, shared, sync…). - i18n: en, zh, zh-hant, ja, ar. - themes/profile-theme.test.ts: promotion, routing under Shared, restore on flipping back, no-op on same scope, persistence + junk normalisation. - e2e/theme-scope.spec.ts: real Electron + gateway with three profiles, driving switches through the statusbar rail — isolation, promotion, shared painting, restore, mode, and survival across a relaunch. Every other Appearance setting (scale, fonts, density, backdrop, …) is already global; only theme and mode diverged, so the row is scoped to them and says so.
1 of 7 tasks
# Conflicts: # apps/desktop/src/themes/context.tsx
Preserve shared theme routing alongside named-profile fallback seeding and legacy migration. Update scope regressions and regenerate the desktop locale key catalog.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds a Theme Scope setting to Desktop → Settings → Appearance so the theme and light/dark mode can be shared across every profile and gateway instead of saved per profile.
Since #42286 the desktop skin and mode are assigned per profile, and switching profiles (or gateways) repaints the window in that profile's look. That's a good default for people who want each bot to feel distinct — but on one desktop used for several bots there is currently no way to say "one theme, everywhere": you have to re-pick the same theme in every profile, and it drifts again the moment one of them changes.
The new row sits directly under the theme grid and only renders when the device has more than one profile (single-profile installs see nothing new):
defaultprofile already uses — so a pick on any profile paints all of them, on every gateway.Behaviour details:
storagesync (HUD, quick entry) go through the same resolver, so the choice holds across relaunch and across windows with no extra wiring.Every other Appearance setting (UI scale, fonts, density, tab strip, backdrop, translucency, …) is already global — only theme and mode diverged — so the row is scoped to those two and its description says so.
Screenshots
Per profile (default):
Shared:
Full Appearance page (Shared)
Related Issue
No existing issue. Builds on #42286 (per-profile themes). Related discussion: #50190 (desktop ↔ dashboard skin sync), #91107 (accent override per profile).
Type of Change
Changes Made
apps/desktop/src/themes/context.tsx—$themeScopeatom persisted athermes-desktop-theme-scope-v1;skinPref/modePrefroute throughslotFor()(under Shared every profile maps to the global slot);setThemeScope()performs the promote; scope key added to the cross-windowstoragesync.apps/desktop/src/app/settings/appearance-settings.tsx— the Theme Scope row (segmented control) and the scope-aware caption under the theme grid.apps/desktop/src/app/settings/settings-search.ts,use-settings-search.ts— stable setting idappearance.theme-scopeand a search catalog entry.apps/desktop/src/i18n/{types,en,zh,zh-hant,ja,ar}.ts— six new strings.apps/desktop/src/themes/profile-theme.test.ts— promotion on flip, routing under Shared, restore on flipping back, no-op on same scope, persistence and junk-value normalisation (table-driven over skin and mode).apps/desktop/e2e/theme-scope.spec.ts— real Electron + real gateway with three profiles, switching through the statusbar rail: per-profile isolation, promote-without-repaint, shared painting across profiles, restore, light/dark following the scope, and survival across a relaunch.How to Test
hermes profile create work), open Desktop → Settings → Appearance. A Theme Scope row appears under the theme grid, on Per profile.default, switch toworkin the statusbar rail, pick a different theme — switch back and forth: each profile keeps its own (unchanged behaviour).work, click Shared — the window does not change. Switch todefault: it now showswork's theme. Pick another theme or toggle Dark anywhere; every profile follows.workgets its earlier theme back.Automated:
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests pass — N/A, desktop-only change (no Python touched); desktop unit + e2e suites run insteadDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A