Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**`devframe`** is the framework-neutral container for one devtool integration, portable across viewers. Build a single tool (its RPC, its SPA, its diagnostics, its CLI/build/spa/embedded outputs) without caring how it'll be displayed. A devframe app runs standalone (CLI, static deploy, embedded SPA) just as well as it mounts inside a hub.

**`@devframes/hub`** is the framework-neutral hub layer that sits on top of devframe and provides the multi-integration orchestration (docks, terminals, messages, commands). It does not ship UI — implementers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. It does ship a **headless client runtime** (`createDevframeClientHost()` from `@devframes/hub/client`): booted in the host page, it assembles the shared `DevframeClientContext` (panel, docks, commands, when) and imports each dock entry's client script (`action` / `custom-render` / iframe `clientScript`) into that page — how a plugin like the a11y inspector runs code inside the page being inspected. See `examples/minimal-vite-devframe-hub/` for a working ~120-line Vite host demonstrating the protocol end to end.
**`@devframes/hub`** is the framework-neutral hub layer that sits on top of devframe and provides the multi-integration orchestration (docks, terminals, messages, commands). It does not ship UI — implementers (e.g. `@vitejs/devtools-kit`) provide their own UI on top of the hub's RPC + shared-state protocol. It does ship a **headless client runtime** (`createDevframeClientHost()` from `@devframes/hub/client`): booted in the host page, it assembles the shared `DevframeClientContext` (panel, docks, commands, when) and imports each dock entry's client script (`action` / `custom-render` / iframe `clientScript`) into that page — how a plugin like the a11y inspector runs code inside the page being inspected. See `examples/vite-devframe-hub/` for a working ~120-line Vite host demonstrating the protocol end to end.

## Stack & Structure

Expand Down
2 changes: 1 addition & 1 deletion alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const alias = {
'@devframes/json-render-ui/components': r('json-render-ui/src/components/index.ts'),
'@devframes/json-render-ui/spa': r('json-render-ui/src/spa.ts'),
'@devframes/json-render-ui': r('json-render-ui/src/index.ts'),
'minimal-json-render/dashboard': fileURLToPath(new URL('./examples/minimal-json-render/src/dashboard.ts', import.meta.url)),
'json-render/dashboard': fileURLToPath(new URL('./examples/json-render/src/dashboard.ts', import.meta.url)),
'@devframes/plugin-code-server/client': p('code-server/src/client/index.ts'),
'@devframes/plugin-code-server/node': p('code-server/src/node/index.ts'),
'@devframes/plugin-code-server/constants': p('code-server/src/constants.ts'),
Expand Down
22 changes: 13 additions & 9 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ function examplesItems(prefix: string) {
{ text: 'Overview', link: `${prefix}/examples/` },
{ text: 'Built with Devframe', link: `${prefix}/examples/built-with` },
{ text: 'files-inspector', link: `${prefix}/examples/files-inspector` },
{ text: 'minimal-json-render', link: `${prefix}/examples/minimal-json-render` },
{ text: 'json-render', link: `${prefix}/examples/json-render` },
{ text: 'streaming-chat', link: `${prefix}/examples/streaming-chat` },
{ text: 'next-runtime-snapshot', link: `${prefix}/examples/next-runtime-snapshot` },
{ text: 'minimal-vite-devframe-hub', link: `${prefix}/examples/minimal-vite-devframe-hub` },
{ text: 'minimal-next-devframe-hub', link: `${prefix}/examples/minimal-next-devframe-hub` },
{ text: 'vite-devframe-hub', link: `${prefix}/examples/vite-devframe-hub` },
{ text: 'next-devframe-hub', link: `${prefix}/examples/next-devframe-hub` },
] satisfies DefaultTheme.NavItemWithLink[]
}

Expand Down Expand Up @@ -114,17 +114,21 @@ export function devframeSidebar(prefix = ''): DefaultTheme.SidebarItem[] {

export function devframeNav(prefix = ''): DefaultTheme.NavItem[] {
return [
{ text: 'Guide', items: guideItems(prefix) },
{ text: 'Adapters', items: adaptersItems(prefix) },
{
text: 'Resources',
text: 'Guide',
items: [
...guideItems(prefix),
{ text: 'Examples', items: examplesItems(prefix) },
],
},
{
text: 'Adapters',
items: [
{ text: 'Examples', link: `${prefix}/examples/` },
{ text: 'Built with Devframe', link: `${prefix}/examples/built-with` },
...adaptersItems(prefix),
{ text: 'Helpers', items: helpersItems(prefix) },
{ text: 'Plugins', items: pluginsItems(prefix) },
],
},
{ text: 'Plugins', items: pluginsItems(prefix) },
{ text: 'Errors', link: `${prefix}/errors/` },
{
text: `v${pkg.version}`,
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ End-to-end examples that exercise the full adapter surface, each a runnable app
| Example | UI framework | What it shows |
|---------|--------------|---------------|
| [files-inspector](./files-inspector) | Preact | Lists files in the cwd via RPC; exercises the CLI dev / build / spa surfaces. |
| [minimal-json-render](./minimal-json-render) | Vue | A server-authored JSON-render view rendered by `@devframes/json-render-ui`, with live state and an action bridge. |
| [json-render](./json-render) | Vue | A server-authored JSON-render view rendered by `@devframes/json-render-ui`, with live state and an action bridge. |
| [streaming-chat](./streaming-chat) | Preact | Streams synthetic chat tokens server → client, with history kept in shared state. |
| [next-runtime-snapshot](./next-runtime-snapshot) | React (Next.js) | A Next.js App Router SPA over RPC, surfacing the host Node runtime. |
| [minimal-vite-devframe-hub](./minimal-vite-devframe-hub) | Vanilla TypeScript (Vite) | A ~120-line Vite host wiring `@devframes/hub` end to end. |
| [minimal-next-devframe-hub](./minimal-next-devframe-hub) | React (Next.js) | The same hub protocol, hosted from a Next.js route handler. |
| [vite-devframe-hub](./vite-devframe-hub) | Vanilla TypeScript (Vite) | A ~120-line Vite host wiring `@devframes/hub` end to end. |
| [next-devframe-hub](./next-devframe-hub) | React (Next.js) | The same hub protocol, hosted from a Next.js route handler. |

## Run any example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
outline: deep
---

# minimal-json-render
# json-render

A standalone devframe that serves a **JSON-render view**: the server authors an
`@json-render/core` spec once, and the prebuilt `@devframes/json-render-ui` SPA
renders it — the app ships no client build of its own.

Package: `minimal-json-render` · frontend: **prebuilt `@devframes/json-render-ui/spa`**
Package: `json-render` · frontend: **prebuilt `@devframes/json-render-ui/spa`**

## What it shows

Expand All @@ -29,12 +29,12 @@ Package: `minimal-json-render` · frontend: **prebuilt `@devframes/json-render-u
## Run it

```sh
pnpm --filter minimal-json-render dev # CLI dev server (live RPC)
pnpm --filter minimal-json-render cli:build # static deploy → dist/static
pnpm --filter json-render dev # CLI dev server (live RPC)
pnpm --filter json-render cli:build # static deploy → dist/static
```

The dev server serves the SPA at `/__minimal-json-render/`.
The dev server serves the SPA at `/__json-render/`.

## Source

[`examples/minimal-json-render`](https://github.com/devframes/devframe/tree/main/examples/minimal-json-render)
[`examples/json-render`](https://github.com/devframes/devframe/tree/main/examples/json-render)
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
outline: deep
---

# minimal-next-devframe-hub
# next-devframe-hub

The same hub protocol as the [Vite host](./minimal-vite-devframe-hub), hosted from a **Next.js** App Router app. It wires [`@devframes/hub`](/guide/hub) by lazily starting a side-car RPC / WebSocket server from a Node route handler — proof that the hub is host-runtime-agnostic.
The same hub protocol as the [Vite host](./vite-devframe-hub), hosted from a **Next.js** App Router app. It wires [`@devframes/hub`](/guide/hub) by lazily starting a side-car RPC / WebSocket server from a Node route handler — proof that the hub is host-runtime-agnostic.

Package: `minimal-next-devframe-hub` · framework: **React (Next.js)**
Package: `next-devframe-hub` · framework: **React (Next.js)**

## What it proves

Expand All @@ -22,7 +22,7 @@ Package: `minimal-next-devframe-hub` · framework: **React (Next.js)**

```sh
pnpm install
pnpm --filter minimal-next-devframe-hub dev
pnpm --filter next-devframe-hub dev
```

Open the printed URL to see the docks, commands, messages, and terminals lists, plus a button that dispatches a sample command through `hub:commands:execute`.
Expand All @@ -33,4 +33,4 @@ Open the printed URL to see the docks, commands, messages, and terminals lists,

## Source

[`examples/minimal-next-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/minimal-next-devframe-hub)
[`examples/next-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/next-devframe-hub)
2 changes: 1 addition & 1 deletion docs/examples/streaming-chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Package: `streaming-chat-example` · framework: **Preact + Vite**

## What it shows

- A scoped context (`ctx.scope('devframe-streaming-chat')`) auto-namespaces every id.
- A scoped context (`ctx.scope('example:streaming-chat')`) auto-namespaces every id.
- `my.rpc.streaming.create('tokens', …)` registers a streaming channel for low-latency token rendering.
- `my.rpc.sharedState('history', …)` keeps the message log on the server; each `send` appends a user + assistant pair atomically.
- The producer streams tokens live, then commits the joined content back to shared state when done — so refreshes and new clients see the finished message immediately.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
outline: deep
---

# minimal-vite-devframe-hub
# vite-devframe-hub

A protocol-witness host: roughly 120 lines of Vite plugin code that wire [`@devframes/hub`](/guide/hub) into a Vite dev server. The browser UI is plain **vanilla TypeScript**, so nothing distracts from the hub protocol itself. Every framework's hub host follows the same shape.

Package: `minimal-vite-devframe-hub` · framework: **Vanilla TypeScript (Vite)**
Package: `vite-devframe-hub` · framework: **Vanilla TypeScript (Vite)**

## What it proves

Expand All @@ -22,11 +22,11 @@ Package: `minimal-vite-devframe-hub` · framework: **Vanilla TypeScript (Vite)**

```sh
pnpm install
pnpm --filter minimal-vite-devframe-hub dev
pnpm --filter vite-devframe-hub dev
```

Open the printed URL to see the docks, commands, messages, and terminals lists the hub exposes, plus a button that dispatches a sample command through `hub:commands:execute`.

## Source

[`examples/minimal-vite-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/minimal-vite-devframe-hub)
[`examples/vite-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/vite-devframe-hub)
2 changes: 1 addition & 1 deletion docs/guide/client-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,4 @@ state.domElements.iframe = iframeEl
state.events.emit('dom:iframe:mounted', iframeEl)
```

That announcement is what the adapter attaches to. Both minimal hubs wire this end to end — see the "Tabbed Tool" in [`examples/minimal-vite-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/minimal-vite-devframe-hub) and [`examples/minimal-next-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/minimal-next-devframe-hub), including the SPA's `postMessage` shim.
That announcement is what the adapter attaches to. Both minimal hubs wire this end to end — see the "Tabbed Tool" in [`examples/vite-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/vite-devframe-hub) and [`examples/next-devframe-hub`](https://github.com/devframes/devframe/tree/main/examples/next-devframe-hub), including the SPA's `postMessage` shim.
6 changes: 3 additions & 3 deletions docs/guide/hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ Each mounted SPA is served at `/__<id>/` and references its assets relatively (`
export default { skipTrailingSlashRedirect: true }
```

[`examples/minimal-next-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/minimal-next-devframe-hub) is a working Next.js App Router host that mounts the built-in plugins this way.
[`examples/next-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/next-devframe-hub) is a working Next.js App Router host that mounts the built-in plugins this way.

### Duplicate devframes

Expand Down Expand Up @@ -260,8 +260,8 @@ The hub also ships a headless browser runtime, `createDevframeClientHost()` from

Two minimal, copyable hubs mount every built-in plugin (git, terminals, code-server, inspect, a11y) behind an icon dock — the same shape [vite-devtools](https://github.com/vitejs/devtools) wears as the full Vite viewer, shrunk to the smallest thing you can build your own viewer from:

- [`examples/minimal-vite-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/minimal-vite-devframe-hub) — a ~120-line Vite plugin host with a vanilla DOM UI.
- [`examples/minimal-next-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/minimal-next-devframe-hub) — the same protocol hosted from a Next.js App Router app.
- [`examples/vite-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/vite-devframe-hub) — a ~120-line Vite plugin host with a vanilla DOM UI.
- [`examples/next-devframe-hub/`](https://github.com/devframes/devframe/tree/main/examples/next-devframe-hub) — the same protocol hosted from a Next.js App Router app.

Both also mount a "Tabbed Tool" that demonstrates [shared-iframe soft navigation](./client-context#shared-iframe-soft-navigation) — one SPA whose tabs surface as separate docks sharing a single iframe.

Expand Down
6 changes: 3 additions & 3 deletions docs/guide/json-render.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ the browser and render it with no shared state at all (see [client-only
docks](./client-context#client-only-docks)). The client host disposes the
renderer when the dock deactivates.

Both hub example shells dogfood this end to end: the [Vite hub](/examples/minimal-vite-devframe-hub)
registers `@devframes/json-render-ui` (Vue), and the [Next hub](/examples/minimal-next-devframe-hub)
Both hub example shells dogfood this end to end: the [Vite hub](/examples/vite-devframe-hub)
registers `@devframes/json-render-ui` (Vue), and the [Next hub](/examples/next-devframe-hub)
registers a small in-example React registry — the same dock, two frontends.

## Swapping the frontend
Expand All @@ -228,5 +228,5 @@ placeholder — showing the component type and a gist of its prop keys (`{ label
onPress }`) — and logs a `console.warn`, while the rest of the view renders
normally.

See the [`minimal-json-render` example](/examples/minimal-json-render) for a
See the [`json-render` example](/examples/json-render) for a
runnable end-to-end app.
2 changes: 1 addition & 1 deletion docs/helpers/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,4 @@ Route handlers that call `fetch` pin `export const runtime = 'nodejs'`: the stat

- [Vite Bridge](./vite-bridge) — the equivalent for Vite-based hosts
- [Hub](/guide/hub) — `createHubContext`, `mountDevframe`, and `DevframeHost`
- [minimal-next-devframe-hub](/examples/minimal-next-devframe-hub) — a full working host
- [next-devframe-hub](/examples/next-devframe-hub) — a full working host
2 changes: 1 addition & 1 deletion examples/a11y-messages-playground/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A focused hub playground for testing **[`@devframes/plugin-a11y`](../../plugins/
alongside **[`@devframes/plugin-messages`](../../plugins/messages)** — and, above
all, the **message → dock navigation** they share.

Where [`minimal-vite-devframe-hub`](../minimal-vite-devframe-hub) mounts every
Where [`vite-devframe-hub`](../vite-devframe-hub) mounts every
built-in plugin against the hub's own (mostly accessible) UI, this example ships a
deliberately **inaccessible, multi-route app under test** so the a11y scanner
always has real violations to find, track per route, and link back to from the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface A11yMessagesPlaygroundOptions {

/**
* A tiny Vite plugin that runs `@devframes/hub` inside the Vite dev server —
* the same shape as `examples/minimal-vite-devframe-hub`, trimmed to the two
* the same shape as `examples/vite-devframe-hub`, trimmed to the two
* plugins this playground pairs (a11y + messages). It creates a hub context,
* implements the framework-neutral `DevframeHost` surface, mounts each devframe
* as a dock (attaching the a11y agent as its client script), and exposes the
Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# devframe-files-inspector
# files-inspector

A simplified [node-modules-inspector](https://github.com/antfu/node-modules-inspector)-style example built on [devframe](../../packages/devframe). Lists files in the current working directory and renders them through a Preact SPA. Exercises every devframe surface end-to-end:

Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/src/client/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { nav, navBrand, tab as tabClass, tabsList } from './design'
import { About } from './routes/about'
import { Home } from './routes/home'

const NAMESPACE = 'devframe-files-inspector'
const NAMESPACE = 'example:files-inspector'
export type InspectorCtx = DevframeScopedClientContext<typeof NAMESPACE>

const NAV_ITEMS = [
Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/src/client/routes/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function Home({ ctx }: { ctx: InspectorCtx }) {
async function refresh() {
setLoading(true)
try {
// Scoped call — `list-files` resolves to `devframe-files-inspector:list-files`.
// Scoped call — `list-files` resolves to `example:files-inspector:list-files`.
const result = await ctx.rpc.call('list-files')
setFiles(result)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/src/devframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const BASE_PATH = '/__devframe-files-inspector/'
const distDir = fileURLToPath(new URL('../dist/client', import.meta.url))

export default defineDevframe({
id: 'devframe-files-inspector',
id: 'example:files-inspector',
name: 'Files Inspector',
version: pkg.version,
packageName: pkg.name,
Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/src/rpc/functions/get-cwd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import process from 'node:process'
import { defineRpcFunction } from 'devframe'

export const getCwd = defineRpcFunction({
name: 'get-cwd', // scoped registration namespaces it to `devframe-files-inspector:get-cwd`
name: 'get-cwd', // scoped registration namespaces it to `example:files-inspector:get-cwd`
type: 'static',
jsonSerializable: true,
setup: ctx => ({
Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/src/rpc/functions/list-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineRpcFunction } from 'devframe'
import { glob } from 'tinyglobby'

export const listFiles = defineRpcFunction({
name: 'list-files', // scoped registration namespaces it to `devframe-files-inspector:list-files`
name: 'list-files', // scoped registration namespaces it to `example:files-inspector:list-files`
type: 'query',
jsonSerializable: true,
snapshot: true,
Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/src/rpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { RpcDefinitionsToFunctionsWithNamespace } from 'devframe/rpc'
import { getCwd } from './functions/get-cwd.ts'
import { listFiles } from './functions/list-files.ts'

export const NAMESPACE = 'devframe-files-inspector'
export const NAMESPACE = 'example:files-inspector'

export const serverFunctions = [getCwd, listFiles] as const

Expand Down
2 changes: 1 addition & 1 deletion examples/files-inspector/tests/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const CLIENT_DIST = resolve(HERE, '../dist/client')
export function assertClientBuilt(): void {
if (!existsSync(path.join(CLIENT_DIST, 'index.html'))) {
throw new Error(
`[devframe-files-inspector] dist/client missing — run \`pnpm -C examples/files-inspector run build\` first.`,
`[example:files-inspector] dist/client missing — run \`pnpm -C examples/files-inspector run build\` first.`,
)
}
}
Expand Down
4 changes: 2 additions & 2 deletions examples/files-inspector/tests/dev-server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ describe('dev-server (CLI surface)', () => {
})
const rpc = createRpcClient<any, any>({}, { channel })

const cwdResult = await rpc.$call('devframe-files-inspector:get-cwd')
const cwdResult = await rpc.$call('example:files-inspector:get-cwd')
expect(cwdResult).toEqual({ cwd })

const files = await rpc.$call('devframe-files-inspector:list-files')
const files = await rpc.$call('example:files-inspector:list-files')
expect(files).toEqual(['README.md', 'package.json', 'sample.txt'])
})
})
Loading
Loading