Skip to content

Commit dabe215

Browse files
committed
feat(hub-ui): mount devframes, an action dock, and a group in the playground
Mounts two tiny static devframes (Alpha/Beta, devframes.ts) via initHub's devframes option, so the playground's dock bar has real mounted content to switch between rather than only client-only entries. Both collapse under a new "Playground Tools" group dock, alongside a "Ping" action dock whose client script runs on click - exercising the grouped-dock popover and the action-dock mechanism the playground didn't previously touch. Declares playground/client-scripts/*.ts as a knip entry - action dock scripts are loaded by their runtime URL string, not a static import.
1 parent e30c5c7 commit dabe215

8 files changed

Lines changed: 186 additions & 3 deletions

File tree

knip.jsonc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@
107107
"packages/hub": {
108108
"entry": ["src/{index,constants}.ts", "src/{client,node,types}/index.ts", "src/node/{index,initiate}.ts"]
109109
},
110+
"packages/hub-ui": {
111+
// `playground/client-scripts/*.ts` are dock `action` entries the
112+
// playground's `seed.ts` points at by their runtime URL string
113+
// (`action.importFrom`), not a static import knip's graph can see —
114+
// the same shape as a devframe's `clientScript`/`clientScripts` entry.
115+
"entry": ["playground/client-scripts/*.ts"]
116+
},
110117
"packages/json-render": {
111118
// `src/node/index.ts` is already picked up via `tsdown.config.ts`
112119
// (its literal `entry` object parses cleanly); only `core.ts`/`hub.ts`
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { DockClientScriptContext } from '@devframes/hub/client'
2+
3+
/**
4+
* The "Ping" action dock's client script — `seed.ts` points its `action`
5+
* entry at this file's dev URL (served straight from the playground's own
6+
* Vite root, no build). Actions re-run their script on every click (see
7+
* `executeSetupScript`), so each click posts a fresh message.
8+
*
9+
* `messages.add` writes over the hub's built-in `hub:messages:add` RPC (it
10+
* always exists), so this succeeds — but nothing renders it: the message
11+
* feed / toasts read back through `devframes:plugin:messages:list`, which
12+
* only `@devframes/plugin-messages` registers, and this playground doesn't
13+
* mount it (its SPA needs a build, which would need this very package's own
14+
* dist — see `hub-plugin.ts`'s doc comment). Confirm a click ran by watching
15+
* the network tab for `client-scripts/ping-action.ts`, or breakpoint here.
16+
*/
17+
export default async function ping(context: DockClientScriptContext): Promise<void> {
18+
await context.messages.add({
19+
level: 'success',
20+
message: 'Pong!',
21+
description: 'The "Ping" action dock ran its client script just now.',
22+
})
23+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Shared between `devframes.ts` (the group's iframe members) and `seed.ts`
3+
* (the group entry itself and its action member) — a group entry and its
4+
* members are separate `docks.register()` calls that only line up through
5+
* this matching `groupId`.
6+
*/
7+
export const PLAYGROUND_GROUP_ID = 'playground-tools'
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { DevframeHubContext } from '@devframes/hub/node'
2+
import { fileURLToPath } from 'node:url'
3+
import { defineDevframe } from 'devframe'
4+
import pkg from '../package.json' with { type: 'json' }
5+
6+
/**
7+
* Two tiny static devframes — proving the playground's hub instance mounts
8+
* real devframes (`ctx.install` under the hood, via `initHub({ devframes })`),
9+
* not just the client-only dock entries `seed.ts` registers directly. Each is
10+
* a single static `index.html` (`devframes/<dir>/`), no build step.
11+
*
12+
* `hub-plugin.ts` mounts both with `dock: { groupId: PLAYGROUND_GROUP_ID }`,
13+
* collapsing them under the "Playground Tools" group `seed.ts` registers.
14+
*/
15+
function createPlaygroundDevframe(id: string, name: string, dir: string) {
16+
return defineDevframe({
17+
id,
18+
name,
19+
version: pkg.version,
20+
packageName: pkg.name,
21+
homepage: pkg.homepage,
22+
description: `A tiny static devframe ("${name}") mounted by the hub-ui playground.`,
23+
icon: 'ph:flask-duotone',
24+
basePath: `/__${id}/`,
25+
cli: {
26+
distDir: fileURLToPath(new URL(`./devframes/${dir}/`, import.meta.url)),
27+
},
28+
async setup(rawCtx) {
29+
const ctx = rawCtx as unknown as DevframeHubContext
30+
await ctx.messages.add({
31+
level: 'info',
32+
message: `${name} devframe mounted`,
33+
description: 'Registered via the hub `devframes` list, like a real integration would be.',
34+
})
35+
},
36+
})
37+
}
38+
39+
export const playgroundAlphaDevframe = createPlaygroundDevframe('playground-alpha', 'Alpha', 'alpha')
40+
export const playgroundBetaDevframe = createPlaygroundDevframe('playground-beta', 'Beta', 'beta')
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Playground · Alpha</title>
6+
<style>
7+
html,
8+
body {
9+
margin: 0;
10+
height: 100%;
11+
background: #10140f;
12+
color: #d7e3cd;
13+
font-family: system-ui, sans-serif;
14+
display: flex;
15+
align-items: center;
16+
justify-content: center;
17+
text-align: center;
18+
}
19+
h1 {
20+
color: #8fbc74;
21+
}
22+
</style>
23+
</head>
24+
<body>
25+
<div>
26+
<h1>Alpha</h1>
27+
<p>A real devframe, mounted by <code>hub-plugin.ts</code> and grouped under "Playground Tools".</p>
28+
</div>
29+
</body>
30+
</html>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>Playground · Beta</title>
6+
<style>
7+
html,
8+
body {
9+
margin: 0;
10+
height: 100%;
11+
background: #12111a;
12+
color: #d9d5ec;
13+
font-family: system-ui, sans-serif;
14+
display: flex;
15+
align-items: center;
16+
justify-content: center;
17+
text-align: center;
18+
}
19+
h1 {
20+
color: #9c8fe0;
21+
}
22+
</style>
23+
</head>
24+
<body>
25+
<div>
26+
<h1>Beta</h1>
27+
<p>A second devframe sharing the "Playground Tools" group with Alpha.</p>
28+
</div>
29+
</body>
30+
</html>

packages/hub-ui/playground/hub-plugin.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ import type { HubInstance } from '@devframes/hub/initiate'
22
import type { Plugin, ViteDevServer } from 'vite'
33
import { Server as NodeHttpServer } from 'node:http'
44
import { DEVFRAMES_HUB_BASE, initHub } from '@devframes/hub/initiate'
5+
import { PLAYGROUND_GROUP_ID } from './constants'
6+
import { playgroundAlphaDevframe, playgroundBetaDevframe } from './devframes'
57
import { seedPlayground } from './seed'
68

79
/**
810
* Mounts a bare, headless hub instance as Vite dev-server middleware — just
911
* enough backend for `main.ts`'s `DockStandalone`/`DockEmbedded` to connect
10-
* to (RPC, WebSocket, `__connection.json`). No `devframes`, no `ui` slot, no
11-
* renderer manifest: this playground is developing hub-ui itself, not
12-
* exercising the wider hub protocol (`examples/hub-vite` already does that).
12+
* to (RPC, WebSocket, `__connection.json`), plus two tiny static devframes
13+
* (`devframes.ts`) so the dock bar has real mounted content to switch
14+
* between, not just the client-only entries `seed.ts` registers. No `ui`
15+
* slot, no renderer manifest: this playground is developing hub-ui itself,
16+
* not exercising the wider hub protocol (`examples/hub-vite` already does
17+
* that).
1318
*
1419
* A hand-rolled slice of `@devframes/vite/hub` rather than that package
1520
* itself — pulling it in here would make `@devframes/hub-ui` and
@@ -50,6 +55,12 @@ export function hubUiPlaygroundHub(): Plugin {
5055
// `@devframes/vite/hub` does.
5156
server: httpServer,
5257
...(httpServer ? {} : { ws: { sidecar: true } }),
58+
// Collapsed under the "Playground Tools" group `seed.ts`'s
59+
// `configure` registers below, alongside the "Ping" action.
60+
devframes: [
61+
{ devframe: playgroundAlphaDevframe, dock: { groupId: PLAYGROUND_GROUP_ID } },
62+
{ devframe: playgroundBetaDevframe, dock: { groupId: PLAYGROUND_GROUP_ID } },
63+
],
5364
configure: seedPlayground,
5465
})
5566
instance = hub

packages/hub-ui/playground/seed.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import type { DevframeViewAction, DevframeViewGroup } from '@devframes/hub'
12
import type { DevframeHubContext } from '@devframes/hub/node'
23
import type { DevframeDockEntryBase } from '@devframes/hub/types'
4+
import { PLAYGROUND_GROUP_ID } from './constants'
35

46
/**
57
* A dock type no renderer covers — registering it exercises the viewer's
@@ -25,6 +27,37 @@ const unrenderedDockEntry: PlaygroundUnrenderedDockEntry = {
2527
category: 'app',
2628
}
2729

30+
/**
31+
* The dock-bar button collapsing the Alpha/Beta devframes (grouped by
32+
* `hub-plugin.ts`'s `devframes` entries) and the "Ping" action below —
33+
* exercises the grouped-dock UI (`DockGroupButton`/`DockGroupPopover`) the
34+
* playground otherwise never touches.
35+
*/
36+
const playgroundGroup: DevframeViewGroup = {
37+
type: 'group',
38+
id: PLAYGROUND_GROUP_ID,
39+
title: 'Playground Tools',
40+
icon: 'ph:flask-duotone',
41+
category: 'app',
42+
// No `defaultChildId` — clicking reveals the member popover instead of
43+
// jumping straight to one, exercising that UI too (`DockGroupPopover`).
44+
}
45+
46+
/**
47+
* A one-shot action dock — no panel of its own, just a client script
48+
* (`client-scripts/ping-action.ts`) the viewer imports and runs on click.
49+
* Grouped alongside the Alpha/Beta devframes above.
50+
*/
51+
const pingAction: DevframeViewAction = {
52+
type: 'action',
53+
id: 'playground:ping',
54+
title: 'Ping',
55+
icon: 'ph:hand-waving-duotone',
56+
category: 'app',
57+
groupId: PLAYGROUND_GROUP_ID,
58+
action: { importFrom: '/client-scripts/ping-action.ts' },
59+
}
60+
2861
/**
2962
* Seeds the playground's hub context with just enough content to exercise
3063
* hub-ui's own surfaces — the dock bar, message center, and command palette —
@@ -33,6 +66,8 @@ const unrenderedDockEntry: PlaygroundUnrenderedDockEntry = {
3366
*/
3467
export async function seedPlayground(ctx: DevframeHubContext): Promise<void> {
3568
ctx.docks.register(unrenderedDockEntry)
69+
ctx.docks.register(playgroundGroup)
70+
ctx.docks.register(pingAction)
3671

3772
ctx.commands.register({
3873
id: 'playground:say-hello',

0 commit comments

Comments
 (0)