Skip to content

Commit 41fd4b9

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/extract-vite-bridge-package
# Conflicts: # examples/hub-next-minimal/src/client/hub.ts # examples/hub-vite-minimal/vite.config.ts
2 parents 54e374d + 2fb9bf9 commit 41fd4b9

18 files changed

Lines changed: 141 additions & 34 deletions

File tree

design/uno.config.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,57 @@ export const shadowSurfaceSafelist: string[] = [
123123
'color-active',
124124
'border-base',
125125
]
126+
127+
/**
128+
* The primary-ramp stops a shadow-root surface's `primary-ramp.css` exposes
129+
* as overridable `--colors-primary-<stop>` custom properties (derived from
130+
* `--devframe-primary`). Must match that file's declarations exactly.
131+
*/
132+
const OVERRIDABLE_PRIMARY_STOPS = ['DEFAULT', '600', '500', '400', '300'] as const
133+
134+
function hexToRgbTriplet(hex: string): string | undefined {
135+
const match = /^#([0-9a-f]{6})$/i.exec(hex)
136+
if (!match)
137+
return undefined
138+
const int = Number.parseInt(match[1], 16)
139+
return `${(int >> 16) & 255} ${(int >> 8) & 255} ${int & 255}`
140+
}
141+
142+
/**
143+
* Rewire a Wind3-compiled shadow-root stylesheet's baked-in `primary` theme
144+
* colors into CSS relative-color syntax reading the live `--colors-primary-*`
145+
* variables `primary-ramp.css` derives from `--devframe-primary`.
146+
*
147+
* Wind3 (unlike Wind4) resolves each theme color to a literal `rgb(r g b /
148+
* <alpha>)` at compile time — the `<alpha>` slot is already dynamic (a slash
149+
* literal, or the utility's own `--un-*-opacity` variable), but the base `r g
150+
* b` triplet is baked in, so every `primary`-based utility (`text-primary`,
151+
* `bg-primary`, `btn-primary`, `ring-primary-500`, …) ignores
152+
* `--devframe-primary` entirely — only hand-written rules that already
153+
* reference `--colors-primary-*` directly (the dock's glow gradient,
154+
* `primary-ramp.css` itself) retint. Swapping the baked triplet for `from
155+
* var(--colors-primary-<stop>, <hex>) r g b` keeps that exact alpha
156+
* mechanism intact while sourcing the base color from the variable — a
157+
* rebrand's `--devframe-primary` now reaches every baked utility too.
158+
*
159+
* Call once per generated pass, after `generator.generate(...)`, passing the
160+
* resolved `generator.config.theme.colors.primary` ramp.
161+
*
162+
* @param css - The compiled Wind3 CSS (pre-`--un-*` namespacing).
163+
* @param primaryRamp - The generator's resolved `theme.colors.primary` ramp.
164+
*/
165+
export function rewireBakedPrimaryColors(css: string, primaryRamp: Record<string, string>): string {
166+
let out = css
167+
for (const stop of OVERRIDABLE_PRIMARY_STOPS) {
168+
const hex = primaryRamp[stop]
169+
const rgb = hex && hexToRgbTriplet(hex)
170+
if (!rgb)
171+
continue
172+
const varName = stop === 'DEFAULT' ? '--colors-primary-DEFAULT' : `--colors-primary-${stop}`
173+
out = out.replace(
174+
new RegExp(String.raw`rgb\(${rgb}(?!\d)`, 'g'),
175+
`rgb(from var(${varName}, ${hex}) r g b`,
176+
)
177+
}
178+
return out
179+
}

examples/hub-hono-minimal/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Open <http://localhost:5179> — the host page carries the floating dock via one
1111

1212
## How it works
1313

14-
- [`src/app.ts`](./src/app.ts) — runtime-agnostic: `initHub({ devframes, ui: createUi() })` plus `app.all('/__devframes/*', c => hub.handler(c.req.raw))`. Everything — frame SPAs, `__connection.json`, `__index.json`, `embedded.js`, `__client-imports.js` — flows through that one route. The instance is memoized on `globalThis` so a dev-time reload reuses the live hub. It configures no WebSocket transport, so each entry below wires the socket its runtime's way; both end up serving `/__devframes/__ws` on the app's own origin, which is what the hub advertises either way.
14+
- [`src/app.ts`](./src/app.ts) — runtime-agnostic: `initHub({ devframes, ui: createUi({ branding }) })` (rebranded to Hono's own orange, `#e36002`) plus `app.all('/__devframes/*', c => hub.handler(c.req.raw))`. Everything — frame SPAs, `__connection.json`, `__index.json`, `embedded.js`, `__client-imports.js` — flows through that one route. The instance is memoized on `globalThis` so a dev-time reload reuses the live hub. It configures no WebSocket transport, so each entry below wires the socket its runtime's way; both end up serving `/__devframes/__ws` on the app's own origin, which is what the hub advertises either way.
1515
- [`src/server.ts`](./src/server.ts) — Node: `@hono/node-server`'s `serve()` returns the `node:http` server, and `hub.attach(server)` routes its upgrade events to the shared RPC socket.
1616
- [`src/bun.ts`](./src/bun.ts) — Bun: upgrades arrive as fetch requests, so this entry binds Bun's own transport to the hub context with `createContextRpcServer` + `attachBunWsTransport` and answers the upgrade route inside `Bun.serve({ fetch, websocket })`.
1717

examples/hub-hono-minimal/src/app.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ export const hub: HubInstance = globalRef.__hubHonoMinimal ??= initHub({
3838
createOgDevframe(),
3939
createAssetsDevframe({ watch: false }),
4040
],
41-
ui: createUi(),
41+
// Rebrand the reference UI to Hono's own orange — one field, no CSS:
42+
// `createUi`'s `branding` option publishes `branding.json`, which the dock
43+
// fetches at boot and feeds into `--devframe-primary` (see
44+
// `@devframes/hub-ui`'s `primary-ramp.css`).
45+
ui: createUi({ branding: { primaryColor: '#e36002', productName: 'Devframes on Hono' } }),
4246
// Single-user localhost demo: reachable only on loopback, so it opts out
4347
// of the gate for a no-friction dev experience. A hub reachable beyond
4448
// localhost should gate (see docs/guide/security.md).

examples/hub-next-minimal/src/client/hub.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { createUi as CreateUi } from '@devframes/hub-ui'
12
import type { HubInstance } from '@devframes/hub/initiate'
23
import type { DevframeJsonRenderSpec } from '@devframes/json-render'
34
import type { jsonRenderUiRenderer as JsonRenderUiRenderer } from '@devframes/json-render-ui/hub'
@@ -51,7 +52,8 @@ const BUILTIN_PLUGIN_PACKAGES = [
5152
] as const
5253

5354
async function loadHub(): Promise<HubInstance> {
54-
const [jsonRenderUi, dataInspector, assets, ...builtins] = await Promise.all([
55+
const [hubUi, jsonRenderUi, dataInspector, assets, ...builtins] = await Promise.all([
56+
import(/* webpackIgnore: true */ /* turbopackIgnore: true */ '@devframes/hub-ui'),
5557
import(/* webpackIgnore: true */ /* turbopackIgnore: true */ '@devframes/json-render-ui/hub'),
5658
import(/* webpackIgnore: true */ /* turbopackIgnore: true */ '@devframes/plugin-data-inspector'),
5759
import(/* webpackIgnore: true */ /* turbopackIgnore: true */ '@devframes/plugin-assets'),
@@ -69,11 +71,14 @@ async function loadHub(): Promise<HubInstance> {
6971
(assets.createAssetsDevframe as (options: { watch: boolean }) => DevframeDefinition)({ watch: false }),
7072
]
7173
// `@devframes/next/hub` runs the socket on a side-car (Next routes can't
72-
// accept WS upgrades) and defaults the UI to `@devframes/hub-ui` (loaded
73-
// through its own bundler-ignored dynamic import) — the minimal host needs
74-
// no client code, just the injected `embedded.js`.
74+
// accept WS upgrades). This host overrides the default UI slot to rebrand
75+
// the reference viewer to Next.js/Vercel's monochrome black — one field, no
76+
// CSS: `createUi`'s `branding` option publishes `branding.json`, which the
77+
// dock fetches at boot and feeds into `--devframe-primary` (see
78+
// `@devframes/hub-ui`'s `primary-ramp.css`).
7579
return createNextDevframeHub({
7680
devframes,
81+
ui: (hubUi.createUi as typeof CreateUi)({ branding: { primaryColor: '#000000', productName: 'Devframes on Next.js' } }),
7782
// Serve the reference json-render frontend as a prebuilt renderer module
7883
// — the one-liner that makes `'json-render'` docks render in the prebuilt
7984
// viewer. Swap it for any community implementation of the same contract.

examples/hub-nitro-minimal/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Open <http://localhost:3000> - the host page carries the floating dock via one s
1010

1111
## How it works
1212

13-
- [`hub.ts`](./hub.ts) - `initHub({ devframes, ui: createUi(), key })`: mounts the Inspect and Messages plugins against one shared hub context, fills the hub's `ui` slot with `@devframes/hub-ui`'s prebuilt viewer + floating-dock bootstrap, and memoizes the instance across Nitro's dev-time module reloads.
13+
- [`hub.ts`](./hub.ts) - `initHub({ devframes, ui: createUi({ branding }) })`: mounts the Inspect and Messages plugins against one shared hub context, fills the hub's `ui` slot with `@devframes/hub-ui`'s prebuilt viewer + floating-dock bootstrap (rebranded to Nitro's own pink/red, `#ff2056`), and memoizes the instance across Nitro's dev-time module reloads.
1414
- [`routes/__devframes/[...path].ts`](./routes/__devframes/%5B...path%5D.ts) (and its `index.ts` sibling for the namespace root) - the delegation: every request under `/__devframes/` becomes `hub.handler(event.req)`, web-standard Request in, Response out. Everything - frame SPAs, `__connection.json`, `__index.json`, `embedded.js`, `__client-imports.js` - flows through it.
1515
- [`nitro.config.ts`](./nitro.config.ts) - keeps the devframe packages external so their prebuilt client assets resolve from the packages themselves rather than Nitro's build output.
1616
- The RPC WebSocket runs on a side-car port - Nitro handlers hand over `Request`s, so `ws: { sidecar: true }` asks for one - advertised through `__connection.json`; the browser client discovers it automatically.

examples/hub-nitro-minimal/hub.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ export const hub: HubInstance = globalRef.__hubNitroMinimal ??= initHub({
4141
createOgDevframe(),
4242
createAssetsDevframe({ watch: false }),
4343
],
44-
ui: createUi(),
44+
// Rebrand the reference UI to Nitro's own pink/red — one field, no CSS:
45+
// `createUi`'s `branding` option publishes `branding.json`, which the dock
46+
// fetches at boot and feeds into `--devframe-primary` (see
47+
// `@devframes/hub-ui`'s `primary-ramp.css`).
48+
ui: createUi({ branding: { primaryColor: '#ff2056', productName: 'Devframes on Nitro' } }),
4549
// Single-user localhost demo: reachable only on loopback, so it opts out
4650
// of the gate for a no-friction dev experience. A hub reachable beyond
4751
// localhost should gate (see docs/guide/security.md).

examples/hub-rsbuild-minimal/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Open the printed URL - the host page carries the floating dock via one injected
1212

1313
[`rsbuild.config.ts`](./rsbuild.config.ts) is the entire host:
1414

15-
- `initHub({ devframes: [inspect, messages], ui: createUi() })` runs in Rsbuild's Node config process (never bundled into the browser), so `createUi()`'s prebuilt viewer/dock and the plugins' node code work unchanged.
15+
- `initHub({ devframes: [inspect, messages], ui: createUi({ branding }) })` runs in Rsbuild's Node config process (never bundled into the browser), so `createUi()`'s prebuilt viewer/dock and the plugins' node code work unchanged. `branding.primaryColor` is Rsbuild's own orange (`#ff5e00`) — a rebrand reaches every `primary`-based color in the dock, no CSS required.
1616
- `dev.setupMiddlewares` unshifts `hub.nodeMiddleware`, which owns the whole `/__devframes/` namespace and hands everything else back to Rsbuild.
1717
- The RPC WebSocket runs on a side-car port (`ws: { sidecar: true }`, since Rsbuild's middleware stack never hands over upgrades), advertised through `__connection.json`; the browser client discovers it automatically.
1818
- `html.tags` injects `<script type="module" src="/__devframes/embedded.js">`, so the floating dock mounts itself.

examples/hub-rsbuild-minimal/rsbuild.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ export default defineConfig({
7676
hub ??= initHub({
7777
base,
7878
devframes: builtinDevframes,
79-
ui: createUi(),
79+
// Rebrand the reference UI to Rsbuild's own orange — one field, no
80+
// CSS: `createUi`'s `branding` option publishes `branding.json`,
81+
// which the dock fetches at boot and feeds into `--devframe-primary`
82+
// (see `@devframes/hub-ui`'s `primary-ramp.css`).
83+
ui: createUi({ branding: { primaryColor: '#ff5e00', productName: 'Devframes on Rsbuild' } }),
8084
// Serve the reference json-render frontend as a prebuilt renderer
8185
// module — the one-liner that makes `'json-render'` docks render in
8286
// the prebuilt viewer. Swap it for any community implementation of

examples/hub-vite-minimal/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Open the printed URL - the host page carries the floating dock via one injected
1212

1313
[`vite.config.ts`](./vite.config.ts) is the entire host:
1414

15-
- `initHub({ devframes: [inspect, messages], ui: createUi() })` runs in Vite's Node config process (never bundled into the browser), so `createUi()`'s prebuilt viewer/dock and the plugins' node code work unchanged.
15+
- `initHub({ devframes: [inspect, messages], ui: createUi({ branding }) })` runs in Vite's Node config process (never bundled into the browser), so `createUi()`'s prebuilt viewer/dock and the plugins' node code work unchanged. `branding.primaryColor` is Vite's own purple (`#646cff`) — a rebrand reaches every `primary`-based color in the dock, no CSS required.
1616
- `server.middlewares.use(hub.nodeMiddleware)` mounts the whole `/__devframes/` namespace; the middleware self-filters by base and hands everything else back to Vite.
1717
- The RPC WebSocket shares Vite's own dev server at `/__devframes/__ws` - zero extra ports.
1818
- `transformIndexHtml` injects `<script type="module" src="/__devframes/embedded.js">` into the host page, so the floating dock mounts itself.

examples/hub-vite-minimal/vite.config.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { DevframeJsonRenderSpec } from '@devframes/json-render'
22
import type { DevframeJsonRenderDockEntry } from '@devframes/json-render/hub'
3+
import { createUi } from '@devframes/hub-ui'
34
import { jsonRenderUiRenderer } from '@devframes/json-render-ui/hub'
45
import { createA11yDevframe } from '@devframes/plugin-a11y'
56
import { createAssetsDevframe } from '@devframes/plugin-assets'
@@ -54,10 +55,9 @@ const jsonRenderDock: DevframeJsonRenderDockEntry = {
5455
// The minimal Vite host: one `viteDevframeHub()` plugin from
5556
// `@devframes/vite/hub`. It wraps `initHub` (mounted as connect middleware on
5657
// Vite's dev server, sharing its HTTP server for the WS upgrade at
57-
// `/__devframes/__ws`), defaults the dock UI to `@devframes/hub-ui`, and
58-
// injects its `embedded.js` bootstrap into the host page — the whole embedded
59-
// integration in one call. `quiet` silences the Vite-DevTools recommendation
60-
// for this reference example.
58+
// `/__devframes/__ws`) and injects the UI's `embedded.js` bootstrap into the
59+
// host page — the whole embedded integration in one call. `quiet` silences the
60+
// Vite-DevTools recommendation for this reference example.
6161
export default defineConfig({
6262
// Dev tooling reached from arbitrary hostnames (LAN IPs, tunnels): accept
6363
// any Host header and fall back to the next free port when busy.
@@ -66,6 +66,12 @@ export default defineConfig({
6666
viteDevframeHub({
6767
quiet: true,
6868
devframes: builtinDevframes,
69+
// Rebrand the reference UI to Vite's own purple — one field, no CSS:
70+
// `createUi`'s `branding` option publishes `branding.json`, which the
71+
// dock fetches at boot and feeds into `--devframe-primary` (see
72+
// `@devframes/hub-ui`'s `primary-ramp.css`). Passing `ui` overrides the
73+
// default `createUi()` the plugin would otherwise use.
74+
ui: createUi({ branding: { primaryColor: '#646cff', productName: 'Devframes on Vite' } }),
6975
// Serve the reference json-render frontend as a prebuilt renderer
7076
// module — the one-liner that makes `'json-render'` docks render in
7177
// the prebuilt viewer. Swap it for any community implementation of

0 commit comments

Comments
 (0)