Skip to content

Commit 67c7c94

Browse files
committed
fix(panels): inherit startup branding through shared CSS
Replace the proposed public theme APIs and message channel with existing connection metadata and the shared hub UI primary ramp. Apply one CSS input at startup and preserve standalone defaults. Use the existing active-color token for query matches and track shared CSS and adapter inputs in all nine panel build tasks. Replace protocol tests with startup branding and cleanup coverage.
1 parent e813c3d commit 67c7c94

27 files changed

Lines changed: 141 additions & 398 deletions

File tree

‎design/panel-theme.ts‎

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,35 @@
1-
import { watchDevframeTheme } from 'devframe/client'
1+
import type { DevframeBranding } from '../packages/hub-ui/src/types'
2+
import { setupDevframeConnection } from 'devframe/client'
23

3-
// Match the reference dock's primary ramp. Only accent tokens change; semantic
4-
// success/error/warning colors and each SPA's chart palettes stay independent.
5-
const primaryStops = {
6-
DEFAULT: 0,
7-
50: 95,
8-
100: 90,
9-
200: 75,
10-
600: 9,
11-
500: 18,
12-
400: 34,
13-
300: 54,
14-
700: -8,
15-
800: -25,
16-
900: -45,
17-
950: -70,
18-
} as const
4+
/** Apply startup branding to a built-in devframe's iframe SPA. */
5+
export function applyPanelBranding(): () => void {
6+
if (window.parent === window)
7+
return () => {}
198

20-
/** Opt a built-in devframe SPA into the hub UI provider's primary accent. */
21-
export function syncPanelTheme(): () => void {
22-
const style = document.documentElement.style
23-
const previous = Object.keys(primaryStops).map((stop) => {
24-
const name = `--colors-primary-${stop}`
25-
return { name, value: style.getPropertyValue(name), priority: style.getPropertyPriority(name) }
26-
})
27-
function restore() {
28-
for (const { name, value, priority } of previous) {
29-
if (value)
30-
style.setProperty(name, value, priority)
31-
else
32-
style.removeProperty(name)
33-
}
34-
}
35-
const stop = watchDevframeTheme(({ primaryColor }) => {
36-
if (!primaryColor || !CSS.supports('color', primaryColor)) {
37-
restore()
9+
const root = document.documentElement
10+
const previous = root.style.getPropertyValue('--devframe-primary')
11+
const priority = root.style.getPropertyPriority('--devframe-primary')
12+
let disposed = false
13+
let applied = false
14+
15+
void setupDevframeConnection().then(({ connectionMeta }) => {
16+
const configs = connectionMeta.configs as { ui?: { branding?: DevframeBranding } } | undefined
17+
const color = configs?.ui?.branding?.primaryColor
18+
if (disposed || !color || !CSS.supports('color', color))
3819
return
39-
}
40-
for (const [stop, white] of Object.entries(primaryStops)) {
41-
style.setProperty(`--colors-primary-${stop}`, white
42-
? `color-mix(in oklab, ${primaryColor}, ${white > 0 ? 'white' : 'black'} ${Math.abs(white)}%)`
43-
: primaryColor)
44-
}
20+
root.style.setProperty('--devframe-primary', color)
21+
applied = true
22+
}).catch(() => {
23+
// Connection failures leave the SPA's default palette intact.
4524
})
25+
4626
return () => {
47-
stop()
48-
restore()
27+
disposed = true
28+
if (!applied)
29+
return
30+
if (previous)
31+
root.style.setProperty('--devframe-primary', previous, priority)
32+
else
33+
root.style.removeProperty('--devframe-primary')
4934
}
5035
}

‎docs/content/1.guide/11.client.md‎

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -333,35 +333,3 @@ async function reconnect() {
333333
```
334334

335335
In a hub, a hub UI provider reads this status from [`context.connection`](/guide/client-context#the-client-context).
336-
337-
## Panel accent
338-
339-
A SPA can adopt its hub UI provider's primary accent with `watchDevframeTheme()`. Map the hint to your own UI token and retain separate tokens for brand graphics, status colors, and charts:
340-
341-
```ts
342-
import { watchDevframeTheme } from 'devframe/client'
343-
344-
const stopTheme = watchDevframeTheme(({ primaryColor }) => {
345-
const style = document.documentElement.style
346-
if (primaryColor && CSS.supports('color', primaryColor))
347-
style.setProperty('--panel-accent', primaryColor)
348-
else
349-
style.removeProperty('--panel-accent')
350-
})
351-
352-
// Call stopTheme() when the SPA unmounts.
353-
```
354-
355-
Use `var(--panel-accent, #3a6a45)` in the SPA's accent styles. A standalone SPA keeps its fallback. Subscription is independent of the RPC connection and works with static builds. The callback receives presentation hints from the direct embedding window; each SPA chooses the UI tokens it changes.
356-
357-
`@devframes/hub-ui` offers its `branding.primaryColor` to panels automatically. A custom hub UI provider can use the same protocol:
358-
359-
```ts
360-
import { provideDevframeTheme } from 'devframe/client'
361-
362-
const theme = provideDevframeTheme(iframe, { primaryColor: '#6b84fd' })
363-
theme.update({ primaryColor: '#8250df' })
364-
// Call theme.dispose() when the iframe view unmounts.
365-
```
366-
367-
The provider supports both startup orders, reloads, and remounting a preserved iframe. The [Panel theme reference](/references/browser-api#panel-theme) lists the API and origin contract.

‎docs/content/1.guide/18.hub-initiate.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ interface DevframeHubUi {
5353
`@devframes/hub-ui`'s `createUi()` is the reference (standalone `viewer` SPA + floating dock); its `setup(ctx)` publishes config to `ctx.staticConfig.ui` (`ConnectionMeta.configs.ui`):
5454

5555
- **`viewer`**: set to `false` to disable the standalone viewer.
56-
- **`branding`**: rebrand the UI (logo, name, primary color). `background` accepts any CSS `background` value (color, gradient, image, or `transparent`) or `{ light, dark }` variants. These flat forms apply everywhere. Use `{ standalone, iframe? }` to specialize the framed viewer; an omitted `iframe` value falls back to `standalone`.
56+
- **`branding`**: rebrand the UI (logo, name, primary color). `background` accepts any CSS `background` value (color, gradient, image, or `transparent`) or `{ light, dark }` variants. These flat forms apply everywhere. Use `{ standalone, iframe? }` to specialize the framed viewer; an omitted `iframe` value falls back to `standalone`. Built-in devframe panels read `branding.primaryColor` from the existing connection metadata at startup and map it to CSS primary tokens. Standalone SPAs keep their default accent; status and preview palettes remain independent. Reload panels after changing startup branding.
5757
- **`dockPreferences`** tunes the dock rail: `categoryOrder`, floating-dock `maxVisibleItems`, first-run `defaultMode` (`'float'`/`'edge'`) and `defaultPosition`.
5858
- **`embeddedVisibility`** sets the floating dock's reveal policy:
5959
- `'normal'` (default): shows immediately.

‎docs/content/8.references/3.events.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,3 @@ Pushed over each panel's [in-page channel](/guide/in-page-channel) port; the pai
127127
|---|---|---|
128128
| `devframe:remote-assets-error` | the remote-assets fallback page, to `window.parent` | The failed package/version/reason, so an embedding hub UI provider can replace the 502 page. |
129129
| `devframe:in-page-channel` | both [in-page channel](/guide/in-page-channel) endpoints, across window boundaries | The versioned handshake envelope (panel hello, page-script port grant). |
130-
| `devframe:theme` | the embedding window and its panel iframe | Optional accent handshake: `available`, `request`, and `update` with a `DevframeTheme`. |

‎docs/content/8.references/5.browser-api.md‎

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,6 @@ The values of `rpc.status`: [Handling connection and auth errors](/guide/client#
4646
| `disconnected` | Socket closed (dropped mid-session or never opened). |
4747
| `error` | Fatal: the socket errored or connection meta couldn't load. |
4848

49-
## Panel theme
50-
51-
Optional presentation hints from the embedding window: [Panel accent](/guide/client#panel-accent). All exports come from `devframe/client`.
52-
53-
| API | Contract |
54-
|-----|----------|
55-
| `DevframeTheme.primaryColor` | Optional CSS color. Omitted means use the SPA's default accent. |
56-
| `watchDevframeTheme(onChange)` | Opt the panel into updates from its direct parent window. Returns a listener cleanup function. Standalone SPAs and SSR return a no-op cleanup. |
57-
| `provideDevframeTheme(iframe, theme)` | Offer hints to one iframe; only an opted-in panel receives theme data. Returns a `DevframeThemeProvider`. |
58-
| `provider.update(theme)` | Replace the hints; `update({})` clears the accent override. |
59-
| `provider.dispose()` | Remove the provider's message and load listeners. |
60-
61-
The protocol supports same-origin and cross-origin iframe documents with a non-opaque origin. Each side checks the sending window; updates target the requesting document's exact origin. A newly loaded iframe document opts in again. The subscription trusts its direct embedding window for presentation hints; the SPA controls how it applies them.
62-
6349
## In-page channel endpoints
6450

6551
The browser-only endpoint methods of the [in-page channel](/guide/in-page-channel). `emit()` sends to the opposite endpoint; `on()` handles events arriving from that endpoint.

‎docs/content/8.references/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Lookup pages the guides link into:
1111
- [When Clauses](/references/when-clauses): the contexts and operators that gate docks, commands, and custom UI.
1212
- [Events Reference](/references/events): every event, broadcast, shared-state key, and channel name, by direction and reach.
1313
- [Node-Side API](/references/node-api): `DevframeDefinition` fields, CLI options, storage scopes, RPC function types, broadcast options, streaming lifecycle, remote assets, the `ctx.services` host and wire-service fields, diagnostics prefixes, and the auth surface.
14-
- [Browser-Side API](/references/browser-api): `connectDevframe` options, RPC client events, connection statuses, panel theme APIs, and in-page channel error codes.
14+
- [Browser-Side API](/references/browser-api): `connectDevframe` options, RPC client events, connection statuses, and in-page channel error codes.
1515
- [Hub API](/references/hub-api): hub subsystems, launcher fields, duplication strategies, dock categories, the hub UI protocol, the namespace routes, the client runtime, the client context, and dock entry types.
1616
- [Utilities](/references/utilities): the small, stable helpers under `devframe/utils/*`, bundled into `devframe`.
1717
- [Interactive Auth](/references/interactive-auth): the OTP auth recipe: handshake, resolver gate, connect-time trust, banner.

‎packages/devframe/src/client/index.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export * from './rpc-streaming'
99
export { resolveWsUrl, type WsUrlLocation } from './rpc-ws'
1010
export * from './scope'
1111
export * from './settings'
12-
export * from './theme'
1312
export * from './webmcp'
1413

1514
export const connectDevframe = getDevframeRpcClient

‎packages/devframe/src/client/theme.test.ts‎

Lines changed: 0 additions & 161 deletions
This file was deleted.

0 commit comments

Comments
 (0)