2e1e3cc91d
The SDK could contribute a theme through `THEMES_AREA` but never select one, and `useTheme().setTheme` needs a component to hang the hook on. A plugin that repaints on an event — a gateway coming up, a socket message — had nowhere to call, so shipping one meant patching the app and re-patching it after every upgrade. `requestTheme(name)` writes to the same one-shot channel the backend `/skin` sync already uses, so the ThemeProvider drains it through `setTheme` and an imperative switch normalizes and persists per profile exactly like a manual pick — one policy, one owner. An unresolvable name is refused rather than coerced. `setTheme` falls back to the default skin, which is right for a person picking off a list and wrong for code reacting to an event: a gateway naming a theme the user never installed would silently reset an appearance the caller never meant to touch. The returned boolean doubles as the availability check.