Skip to content

Let a desktop theme recommend OS settings - #434

Merged
AllTerrainDeveloper merged 6 commits into
trunkfrom
add/theme-recommended-os-settings
Jul 28, 2026
Merged

Let a desktop theme recommend OS settings#434
AllTerrainDeveloper merged 6 commits into
trunkfrom
add/theme-recommended-os-settings

Conversation

@AllTerrainDeveloper

@AllTerrainDeveloper AllTerrainDeveloper commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Adds an optional recommendedOsSettings block to the desktop-theme manifest, so a theme's intended arrangement travels with it instead of living in a setup guide.

{
  "manifestVersion": 2,
  "recommendedOsSettings": {
    "dockSize": "large",
    "desktopLayout": "unified"
  }
}

The contract: applied once, the first time a user activates the theme — never on page load, never over a change the user made afterwards. A per-user ledger (appliedThemeRecommendations) records which themes have been seeded, so re-picking a theme you've already worn changes nothing. OS Settings → Themes gains an Apply recommended layout button — the only path that applies a recommendation a second time.

Allow-list. Four presentation keys only (dockSize, desktopLayout, windowRadius, dockRailRenderer), filterable via desktop_mode_desktop_theme_recommended_os_settings_schema. The enums reference the existing DESKTOP_MODE_OS_SETTINGS_* constants so they can't drift. dockRailRenderer is checked twice — charset in PHP, registry membership in the shell at apply time — so an id nothing answers to is skipped rather than written into user meta. The shell only writes a key that already exists and already holds a string, so a manifest can't introduce a setting or flip a feature toggle.

Back-compat. manifestVersion now accepts 1 or 2. v1 manifests sanitize exactly as before, and a v1 manifest carrying the block still has it honoured — dropping a valid, sanitized field over a version number would contradict the drop-and-continue rule the rest of the sanitizer follows. Themes installed before this feature shape to an empty set, not a missing key.

Also in here (flagged separately, kept in this PR by request): updateOsSettings had no writer for desktopTheme or unfocusEffect despite the docs showing them, and no windowRadius on the snapshot at all. All three are wired up, and presentation patches now run the apply pass — previously they persisted but changed nothing until the next page load, which is why the docs had to pair updateOsSettings({ desktopTheme }) with a setActive() call.

Docs updated in the same change: desktop-themes.md, hooks-reference.md, javascript-reference.md, api-index.md, examples/register-desktop-theme.md.

Testing

  • PHPUnit 1640 passing — new desktopThemesRecommendedOsSettings.php (schema, sanitizer, manifest integration, v1 back-compat, code registration, payload re-sanitization), plus ledger cases and a v2-accepted case.
  • Vitest 2400 passing — new desktop-themes-recommended.test.ts and api-update-os-settings.test.ts.
  • typecheck, lint, build clean.

Manual: activate a theme with the block → dock/layout move once, with a notice. Change the dock size, re-pick the theme → your choice holds. Hit Apply recommended layout → the author's arrangement comes back. The reference neon-glass theme ZIP ships the block and its README explains the once-only contract.

Theme:
neon-glass.zip

Open WordPress Playground Preview

Adds an optional `recommendedOsSettings` block to the desktop-theme
manifest (`dockSize`, `desktopLayout`, `windowRadius`,
`dockRailRenderer`), sanitized against a filterable allow-list of
presentation keys and exposed through the payload and the public
desktop-theme API.

Recommendations are seeded into a user's own OS Settings once, the
first time they activate the theme, and never re-asserted; the ledger
lives in `appliedThemeRecommendations`. OS Settings -> Themes gains an
"Apply recommended layout" action for going back to the author's
arrangement on purpose.

`manifestVersion` now accepts 1 or 2. v1 manifests are unchanged, and
a v1 manifest carrying the block still has it honoured.

Also closes a pre-existing gap in `updateOsSettings`: `desktopTheme`,
`unfocusEffect` and `windowRadius` were documented or snapshot-exposed
with no writer, and presentation patches persisted without applying.
A desktop theme that declares `--desktop-mode-window-radius` in its
tokens beats the Window corners preset: the compiled rule matches the
shell root, while the preset is an inline style on `:root` that only
reaches windows by inheritance. The theme winning is intended; the
picker sitting there looking operable and doing nothing was not.

The control now disables itself while a theme owns the token and names
the theme, pointing at System default. It re-enables live when the
theme is deactivated -- no Settings reopen.

`<wpd-segmented>` gains a `disabled` prop for this, which also refuses
picks rather than relying on pointer-events alone.
Applying a theme's recommended layout moves the dock and the layout
under the user's cursor. That movement is the feedback -- a notice on
top of a visible change is noise, so both are gone: the one after the
first-activation seed and the one after the button.

The button now names the theme it belongs to ("Apply Neon Glass's
recommended layout") and does exactly one thing: set the settings.

Semantics are unchanged and match the spec: seeded once on first
activation, never on page load, never over a later user change, with
the button as the only path to a second application.
apply() wrote --desktop-mode-window-radius only as an inline style on
:root, which reaches windows by INHERITANCE. A desktop theme that
declares the same token gets it compiled onto
`.desktop-mode-shell[data-desktop-mode-desktop-theme="..."]` and
`body.desktop-mode-desktop-theme-...`, both of which MATCH an ancestor
of every window -- so the theme won and clicking Sharp / Default /
Round did nothing at all for as long as that theme was worn.

The preset now also writes the property inline on the shell element.
Inline outranks any selector, so the user's corner choice is
authoritative. A theme that wants a particular radius asks through
`recommendedOsSettings.windowRadius`, which sets the preference once
and leaves it theirs.

The bundled Neon Glass theme moves its 12px token to a `round`
recommendation accordingly. Note its WINDOW_FRAME is a border-image,
and CSS does not clip a border-image to border-radius -- while that
frame is painted the artwork corners stay put whatever the preset says.
The two behaviours a theme author will actually hit while iterating:
a recommended corner preset lands, and a key added in a theme UPDATE
reaches nobody who already activated it -- the button is the way in.
@AllTerrainDeveloper
AllTerrainDeveloper merged commit 5513c5a into trunk Jul 28, 2026
5 checks passed
@AllTerrainDeveloper
AllTerrainDeveloper deleted the add/theme-recommended-os-settings branch July 28, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant