@vitejs/devtools-kit 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/{index-Bl6l5FTi.d.ts → index-WxzZW3L-.d.ts} +39 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +6 -1
- package/package.json +2 -2
- package/skills/vite-devtools-kit/SKILL.md +70 -1
- package/skills/vite-devtools-kit/references/dock-entry-types.md +87 -0
- package/skills/vite-devtools-kit/references/json-render-patterns.md +284 -0
- package/skills/vite-devtools-kit/references/terminals-patterns.md +123 -0
package/dist/client.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as DevToolsRpcClientOptions, a as DockEntryState, c as DocksContext, d as RpcClientEvents, f as DevToolsRpcClient, g as DevToolsRpcClientMode, h as DevToolsRpcClientCallOptional, i as DockClientType, l as DocksEntriesContext, m as DevToolsRpcClientCallEvent, n as DevToolsClientContext, o as DockEntryStateEvents, p as DevToolsRpcClientCall, r as DevToolsClientRpcHost, s as DockPanelStorage, t as DockClientScriptContext, u as DocksPanelContext, v as getDevToolsRpcClient } from "./index-
|
|
1
|
+
import { _ as DevToolsRpcClientOptions, a as DockEntryState, c as DocksContext, d as RpcClientEvents, f as DevToolsRpcClient, g as DevToolsRpcClientMode, h as DevToolsRpcClientCallOptional, i as DockClientType, l as DocksEntriesContext, m as DevToolsRpcClientCallEvent, n as DevToolsClientContext, o as DockEntryStateEvents, p as DevToolsRpcClientCall, r as DevToolsClientRpcHost, s as DockPanelStorage, t as DockClientScriptContext, u as DocksPanelContext, v as getDevToolsRpcClient } from "./index-WxzZW3L-.js";
|
|
2
2
|
export { DevToolsClientContext, DevToolsClientRpcHost, DevToolsRpcClient, DevToolsRpcClientCall, DevToolsRpcClientCallEvent, DevToolsRpcClientCallOptional, DevToolsRpcClientMode, DevToolsRpcClientOptions, DockClientScriptContext, DockClientType, DockEntryState, DockEntryStateEvents, DockPanelStorage, DocksContext, DocksEntriesContext, DocksPanelContext, RpcClientEvents, getDevToolsRpcClient };
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as DevToolsDocksUserSettings } from "./index-
|
|
1
|
+
import { T as DevToolsDocksUserSettings } from "./index-WxzZW3L-.js";
|
|
2
2
|
//#region src/constants.d.ts
|
|
3
3
|
declare const DEVTOOLS_MOUNT_PATH = "/.devtools/";
|
|
4
4
|
declare const DEVTOOLS_MOUNT_PATH_NO_TRAILING_SLASH = "/.devtools";
|
|
@@ -246,7 +246,39 @@ interface DevToolsViewBuiltin extends DevToolsDockEntryBase {
|
|
|
246
246
|
type: '~builtin';
|
|
247
247
|
id: '~terminals' | '~logs' | '~client-auth-notice' | '~settings' | '~popup';
|
|
248
248
|
}
|
|
249
|
-
|
|
249
|
+
interface JsonRenderElement {
|
|
250
|
+
type: string;
|
|
251
|
+
props?: Record<string, unknown>;
|
|
252
|
+
children?: string[];
|
|
253
|
+
/** json-render event bindings (e.g. `{ press: { action: "my:action" } }`) */
|
|
254
|
+
on?: Record<string, unknown>;
|
|
255
|
+
/** json-render visibility condition */
|
|
256
|
+
visible?: unknown;
|
|
257
|
+
/** json-render repeat binding */
|
|
258
|
+
repeat?: unknown;
|
|
259
|
+
/** Allow additional json-render element fields */
|
|
260
|
+
[key: string]: unknown;
|
|
261
|
+
}
|
|
262
|
+
interface JsonRenderSpec {
|
|
263
|
+
root: string;
|
|
264
|
+
elements: Record<string, JsonRenderElement>;
|
|
265
|
+
/** Initial client-side state model for $state/$bindState expressions */
|
|
266
|
+
state?: Record<string, unknown>;
|
|
267
|
+
}
|
|
268
|
+
interface JsonRenderer {
|
|
269
|
+
/** Replace the entire spec */
|
|
270
|
+
updateSpec: (spec: JsonRenderSpec) => void | Promise<void>;
|
|
271
|
+
/** Update json-render state values (shallow merge into spec.state) */
|
|
272
|
+
updateState: (state: Record<string, unknown>) => void | Promise<void>;
|
|
273
|
+
/** Internal: shared state key used by the client to subscribe */
|
|
274
|
+
readonly _stateKey: string;
|
|
275
|
+
}
|
|
276
|
+
interface DevToolsViewJsonRender extends DevToolsDockEntryBase {
|
|
277
|
+
type: 'json-render';
|
|
278
|
+
/** JsonRenderer handle created by ctx.createJsonRenderer() */
|
|
279
|
+
ui: JsonRenderer;
|
|
280
|
+
}
|
|
281
|
+
type DevToolsDockUserEntry = DevToolsViewIframe | DevToolsViewAction | DevToolsViewCustomRender | DevToolsViewLauncher | DevToolsViewJsonRender;
|
|
250
282
|
type DevToolsDockEntry = DevToolsDockUserEntry | DevToolsViewBuiltin;
|
|
251
283
|
type DevToolsDockEntriesGrouped = [category: string, entries: DevToolsDockEntry[]][];
|
|
252
284
|
//#endregion
|
|
@@ -382,6 +414,11 @@ interface DevToolsNodeContext {
|
|
|
382
414
|
* Logs host, for emitting and managing structured log entries
|
|
383
415
|
*/
|
|
384
416
|
logs: DevToolsLogsHost;
|
|
417
|
+
/**
|
|
418
|
+
* Create a JsonRenderer handle for building json-render powered UIs.
|
|
419
|
+
* Pass the returned handle as `ui` when registering a `json-render` dock entry.
|
|
420
|
+
*/
|
|
421
|
+
createJsonRenderer: (spec: JsonRenderSpec) => JsonRenderer;
|
|
385
422
|
}
|
|
386
423
|
interface DevToolsNodeUtils {
|
|
387
424
|
/**
|
|
@@ -639,4 +676,4 @@ interface DockClientScriptContext extends DocksContext {
|
|
|
639
676
|
logs: DevToolsLogsClient;
|
|
640
677
|
}
|
|
641
678
|
//#endregion
|
|
642
|
-
export { DevToolsDockEntryIcon as $, RpcSharedStateGetOptions as A, DevToolsTerminalHost as B, PartialWithoutId as C, DevToolsNodeRpcSessionMeta as D, DevToolsNodeRpcSession as E, DevToolsNodeUtils as F, DevToolsRpcClientFunctions as G, DevToolsTerminalSessionBase as H, DevToolsPluginOptions as I, ClientScriptEntry as J, DevToolsRpcServerFunctions as K, DevToolsViewHost as L, ConnectionMeta as M, DevToolsCapabilities as N, RpcBroadcastOptions as O, DevToolsNodeContext as P, DevToolsDockEntryCategory as Q, DevToolsChildProcessExecuteOptions as R, EntriesToObject as S, DevToolsDocksUserSettings as T, DevToolsTerminalSessionStreamChunkEvent as U, DevToolsTerminalSession as V, DevToolsTerminalStatus as W, DevToolsDockEntry as X, DevToolsDockEntriesGrouped as Y, DevToolsDockEntryBase as Z, DevToolsRpcClientOptions as _, DockEntryState as a, DevToolsViewIframe as at, RpcDefinitionsToFunctions as b, DocksContext as c,
|
|
679
|
+
export { DevToolsDockEntryIcon as $, RpcSharedStateGetOptions as A, DevToolsTerminalHost as B, PartialWithoutId as C, DevToolsNodeRpcSessionMeta as D, DevToolsNodeRpcSession as E, DevToolsNodeUtils as F, DevToolsRpcClientFunctions as G, DevToolsTerminalSessionBase as H, DevToolsPluginOptions as I, ClientScriptEntry as J, DevToolsRpcServerFunctions as K, DevToolsViewHost as L, ConnectionMeta as M, DevToolsCapabilities as N, RpcBroadcastOptions as O, DevToolsNodeContext as P, DevToolsDockEntryCategory as Q, DevToolsChildProcessExecuteOptions as R, EntriesToObject as S, DevToolsDocksUserSettings as T, DevToolsTerminalSessionStreamChunkEvent as U, DevToolsTerminalSession as V, DevToolsTerminalStatus as W, DevToolsDockEntry as X, DevToolsDockEntriesGrouped as Y, DevToolsDockEntryBase as Z, DevToolsRpcClientOptions as _, DevToolsLogHandle as _t, DockEntryState as a, DevToolsViewIframe as at, RpcDefinitionsToFunctions as b, DevToolsLogsHost as bt, DocksContext as c, DevToolsViewLauncherStatus as ct, RpcClientEvents as d, JsonRenderer as dt, DevToolsDockHost as et, DevToolsRpcClient as f, DevToolsLogElementPosition as ft, DevToolsRpcClientMode as g, DevToolsLogFilePosition as gt, DevToolsRpcClientCallOptional as h, DevToolsLogEntryInput as ht, DockClientType as i, DevToolsViewCustomRender as it, RpcSharedStateHost as j, RpcFunctionsHost as k, DocksEntriesContext as l, JsonRenderElement as lt, DevToolsRpcClientCallEvent as m, DevToolsLogEntryFrom as mt, DevToolsClientContext as n, DevToolsViewAction as nt, DockEntryStateEvents as o, DevToolsViewJsonRender as ot, DevToolsRpcClientCall as p, DevToolsLogEntry as pt, DevToolsRpcSharedStates as q, DevToolsClientRpcHost as r, DevToolsViewBuiltin as rt, DockPanelStorage as s, DevToolsViewLauncher as st, DockClientScriptContext as t, DevToolsDockUserEntry as tt, DocksPanelContext as u, JsonRenderSpec as ut, getDevToolsRpcClient as v, DevToolsLogLevel as vt, Thenable as w, PluginWithDevTools as x, RpcDefinitionsFilter as y, DevToolsLogsClient as yt, DevToolsChildProcessTerminalSession as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { n as EventUnsubscribe, r as EventsMap, t as EventEmitter } from "./events-B41U-zeg.js";
|
|
2
|
-
import { $ as DevToolsDockEntryIcon, A as RpcSharedStateGetOptions, B as DevToolsTerminalHost, C as PartialWithoutId, D as DevToolsNodeRpcSessionMeta, E as DevToolsNodeRpcSession, F as DevToolsNodeUtils, G as DevToolsRpcClientFunctions, H as DevToolsTerminalSessionBase, I as DevToolsPluginOptions, J as ClientScriptEntry, K as DevToolsRpcServerFunctions, L as DevToolsViewHost, M as ConnectionMeta, N as DevToolsCapabilities, O as RpcBroadcastOptions, P as DevToolsNodeContext, Q as DevToolsDockEntryCategory, R as DevToolsChildProcessExecuteOptions, S as EntriesToObject, T as DevToolsDocksUserSettings, U as DevToolsTerminalSessionStreamChunkEvent, V as DevToolsTerminalSession, W as DevToolsTerminalStatus, X as DevToolsDockEntry, Y as DevToolsDockEntriesGrouped, Z as DevToolsDockEntryBase, at as DevToolsViewIframe, b as RpcDefinitionsToFunctions, ct as
|
|
2
|
+
import { $ as DevToolsDockEntryIcon, A as RpcSharedStateGetOptions, B as DevToolsTerminalHost, C as PartialWithoutId, D as DevToolsNodeRpcSessionMeta, E as DevToolsNodeRpcSession, F as DevToolsNodeUtils, G as DevToolsRpcClientFunctions, H as DevToolsTerminalSessionBase, I as DevToolsPluginOptions, J as ClientScriptEntry, K as DevToolsRpcServerFunctions, L as DevToolsViewHost, M as ConnectionMeta, N as DevToolsCapabilities, O as RpcBroadcastOptions, P as DevToolsNodeContext, Q as DevToolsDockEntryCategory, R as DevToolsChildProcessExecuteOptions, S as EntriesToObject, T as DevToolsDocksUserSettings, U as DevToolsTerminalSessionStreamChunkEvent, V as DevToolsTerminalSession, W as DevToolsTerminalStatus, X as DevToolsDockEntry, Y as DevToolsDockEntriesGrouped, Z as DevToolsDockEntryBase, _t as DevToolsLogHandle, at as DevToolsViewIframe, b as RpcDefinitionsToFunctions, bt as DevToolsLogsHost, ct as DevToolsViewLauncherStatus, dt as JsonRenderer, et as DevToolsDockHost, ft as DevToolsLogElementPosition, gt as DevToolsLogFilePosition, ht as DevToolsLogEntryInput, it as DevToolsViewCustomRender, j as RpcSharedStateHost, k as RpcFunctionsHost, lt as JsonRenderElement, mt as DevToolsLogEntryFrom, nt as DevToolsViewAction, ot as DevToolsViewJsonRender, pt as DevToolsLogEntry, q as DevToolsRpcSharedStates, rt as DevToolsViewBuiltin, st as DevToolsViewLauncher, tt as DevToolsDockUserEntry, ut as JsonRenderSpec, vt as DevToolsLogLevel, w as Thenable, x as PluginWithDevTools, y as RpcDefinitionsFilter, yt as DevToolsLogsClient, z as DevToolsChildProcessTerminalSession } from "./index-WxzZW3L-.js";
|
|
3
3
|
import * as _vitejs_devtools_rpc0 from "@vitejs/devtools-rpc";
|
|
4
4
|
|
|
5
5
|
//#region src/utils/define.d.ts
|
|
6
6
|
declare const defineRpcFunction: <NAME extends string, TYPE extends _vitejs_devtools_rpc0.RpcFunctionType, ARGS extends any[], RETURN = void, const AS extends _vitejs_devtools_rpc0.RpcArgsSchema | undefined = undefined, const RS extends _vitejs_devtools_rpc0.RpcReturnSchema | undefined = undefined>(definition: _vitejs_devtools_rpc0.RpcFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS, DevToolsNodeContext>) => _vitejs_devtools_rpc0.RpcFunctionDefinition<NAME, TYPE, ARGS, RETURN, AS, RS, DevToolsNodeContext>;
|
|
7
7
|
//#endregion
|
|
8
|
-
|
|
8
|
+
//#region src/utils/json-render.d.ts
|
|
9
|
+
declare function defineJsonRenderSpec(spec: JsonRenderSpec): JsonRenderSpec;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { ClientScriptEntry, ConnectionMeta, DevToolsCapabilities, DevToolsChildProcessExecuteOptions, DevToolsChildProcessTerminalSession, DevToolsDockEntriesGrouped, DevToolsDockEntry, DevToolsDockEntryBase, DevToolsDockEntryCategory, DevToolsDockEntryIcon, DevToolsDockHost, DevToolsDockUserEntry, DevToolsDocksUserSettings, DevToolsLogElementPosition, DevToolsLogEntry, DevToolsLogEntryFrom, DevToolsLogEntryInput, DevToolsLogFilePosition, DevToolsLogHandle, DevToolsLogLevel, DevToolsLogsClient, DevToolsLogsHost, DevToolsNodeContext, DevToolsNodeRpcSession, DevToolsNodeRpcSessionMeta, DevToolsNodeUtils, DevToolsPluginOptions, DevToolsRpcClientFunctions, DevToolsRpcServerFunctions, DevToolsRpcSharedStates, DevToolsTerminalHost, DevToolsTerminalSession, DevToolsTerminalSessionBase, DevToolsTerminalSessionStreamChunkEvent, DevToolsTerminalStatus, DevToolsViewAction, DevToolsViewBuiltin, DevToolsViewCustomRender, DevToolsViewHost, DevToolsViewIframe, DevToolsViewJsonRender, DevToolsViewLauncher, DevToolsViewLauncherStatus, EntriesToObject, EventEmitter, EventUnsubscribe, EventsMap, JsonRenderElement, JsonRenderSpec, JsonRenderer, PartialWithoutId, PluginWithDevTools, RpcBroadcastOptions, RpcDefinitionsFilter, RpcDefinitionsToFunctions, RpcFunctionsHost, RpcSharedStateGetOptions, RpcSharedStateHost, Thenable, defineJsonRenderSpec, defineRpcFunction };
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,9 @@ import { createDefineWrapperWithContext } from "@vitejs/devtools-rpc";
|
|
|
2
2
|
//#region src/utils/define.ts
|
|
3
3
|
const defineRpcFunction = createDefineWrapperWithContext();
|
|
4
4
|
//#endregion
|
|
5
|
-
|
|
5
|
+
//#region src/utils/json-render.ts
|
|
6
|
+
function defineJsonRenderSpec(spec) {
|
|
7
|
+
return spec;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { defineJsonRenderSpec, defineRpcFunction };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitejs/devtools-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"description": "Vite DevTools Kit",
|
|
6
6
|
"author": "VoidZero Inc.",
|
|
7
7
|
"license": "MIT",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"birpc": "^4.0.0",
|
|
40
40
|
"immer": "^11.1.4",
|
|
41
|
-
"@vitejs/devtools-rpc": "0.1.
|
|
41
|
+
"@vitejs/devtools-rpc": "0.1.2"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsdown": "^0.21.2",
|
|
@@ -18,11 +18,13 @@ A DevTools plugin extends a Vite plugin with a `devtools.setup(ctx)` hook. The c
|
|
|
18
18
|
|
|
19
19
|
| Property | Purpose |
|
|
20
20
|
|----------|---------|
|
|
21
|
-
| `ctx.docks` | Register dock entries (iframe, action, custom-render, launcher) |
|
|
21
|
+
| `ctx.docks` | Register dock entries (iframe, action, custom-render, launcher, json-render) |
|
|
22
22
|
| `ctx.views` | Host static files for UI |
|
|
23
23
|
| `ctx.rpc` | Register RPC functions, broadcast to clients |
|
|
24
24
|
| `ctx.rpc.sharedState` | Synchronized server-client state |
|
|
25
25
|
| `ctx.logs` | Emit structured log entries and toast notifications |
|
|
26
|
+
| `ctx.terminals` | Spawn and manage child processes with streaming terminal output |
|
|
27
|
+
| `ctx.createJsonRenderer` | Create server-side JSON render specs for zero-client-code UIs |
|
|
26
28
|
| `ctx.viteConfig` | Resolved Vite configuration |
|
|
27
29
|
| `ctx.viteServer` | Dev server instance (dev mode only) |
|
|
28
30
|
| `ctx.mode` | `'dev'` or `'build'` |
|
|
@@ -122,6 +124,7 @@ export default function myAnalyzer(): Plugin {
|
|
|
122
124
|
| Type | Use Case |
|
|
123
125
|
|------|----------|
|
|
124
126
|
| `iframe` | Full UI panels, dashboards (most common) |
|
|
127
|
+
| `json-render` | Server-side JSON specs — zero client code needed |
|
|
125
128
|
| `action` | Buttons that trigger client-side scripts (inspectors, toggles) |
|
|
126
129
|
| `custom-render` | Direct DOM access in panel (framework mounting) |
|
|
127
130
|
| `launcher` | Actionable setup cards for initialization tasks |
|
|
@@ -168,6 +171,44 @@ ctx.docks.register({
|
|
|
168
171
|
})
|
|
169
172
|
```
|
|
170
173
|
|
|
174
|
+
### JSON Render Entry
|
|
175
|
+
|
|
176
|
+
Build UIs entirely from server-side TypeScript — no client code needed:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
const ui = ctx.createJsonRenderer({
|
|
180
|
+
root: 'root',
|
|
181
|
+
elements: {
|
|
182
|
+
root: {
|
|
183
|
+
type: 'Stack',
|
|
184
|
+
props: { direction: 'vertical', gap: 12 },
|
|
185
|
+
children: ['heading', 'info'],
|
|
186
|
+
},
|
|
187
|
+
heading: {
|
|
188
|
+
type: 'Text',
|
|
189
|
+
props: { content: 'Hello from JSON!', variant: 'heading' },
|
|
190
|
+
},
|
|
191
|
+
info: {
|
|
192
|
+
type: 'KeyValueTable',
|
|
193
|
+
props: {
|
|
194
|
+
entries: [
|
|
195
|
+
{ key: 'Version', value: '1.0.0' },
|
|
196
|
+
{ key: 'Status', value: 'Running' },
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
ctx.docks.register({
|
|
204
|
+
id: 'my-panel',
|
|
205
|
+
title: 'My Panel',
|
|
206
|
+
icon: 'ph:chart-bar-duotone',
|
|
207
|
+
type: 'json-render',
|
|
208
|
+
ui,
|
|
209
|
+
})
|
|
210
|
+
```
|
|
211
|
+
|
|
171
212
|
### Launcher Entry
|
|
172
213
|
|
|
173
214
|
```ts
|
|
@@ -188,6 +229,31 @@ const entry = ctx.docks.register({
|
|
|
188
229
|
})
|
|
189
230
|
```
|
|
190
231
|
|
|
232
|
+
## Terminals & Subprocesses
|
|
233
|
+
|
|
234
|
+
Spawn and manage child processes with streaming terminal output:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
const session = await ctx.terminals.startChildProcess(
|
|
238
|
+
{
|
|
239
|
+
command: 'vite',
|
|
240
|
+
args: ['build', '--watch'],
|
|
241
|
+
cwd: process.cwd(),
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: 'my-plugin:build-watcher',
|
|
245
|
+
title: 'Build Watcher',
|
|
246
|
+
icon: 'ph:terminal-duotone',
|
|
247
|
+
},
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
// Lifecycle controls
|
|
251
|
+
await session.terminate()
|
|
252
|
+
await session.restart()
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
A common pattern is combining with launcher docks — see [Terminals Patterns](./references/terminals-patterns.md).
|
|
256
|
+
|
|
191
257
|
## Logs & Notifications
|
|
192
258
|
|
|
193
259
|
Plugins can emit structured log entries from both server and client contexts. Logs appear in the built-in **Logs** panel and can optionally show as toast notifications.
|
|
@@ -426,6 +492,7 @@ Real-world example plugins in the repo — reference their code structure and pa
|
|
|
426
492
|
|
|
427
493
|
- **A11y Checker** ([`examples/plugin-a11y-checker`](https://github.com/vitejs/devtools/tree/main/examples/plugin-a11y-checker)) — Action dock entry, client-side axe-core audits, logs with severity levels and element positions, log handle updates
|
|
428
494
|
- **File Explorer** ([`examples/plugin-file-explorer`](https://github.com/vitejs/devtools/tree/main/examples/plugin-file-explorer)) — Iframe dock entry, RPC functions (static/query/action), hosted UI panel, RPC dump for static builds, backend mode detection
|
|
495
|
+
- **Git UI** ([`examples/plugin-git-ui`](https://github.com/vitejs/devtools/tree/main/examples/plugin-git-ui)) — JSON render dock entry, server-side JSON specs, `$bindState` two-way binding, `$state` in action params, dynamic badge updates
|
|
429
496
|
|
|
430
497
|
## Further Reading
|
|
431
498
|
|
|
@@ -433,4 +500,6 @@ Real-world example plugins in the repo — reference their code structure and pa
|
|
|
433
500
|
- [Dock Entry Types](./references/dock-entry-types.md) - Detailed dock configuration options
|
|
434
501
|
- [Shared State Patterns](./references/shared-state-patterns.md) - Framework integration examples
|
|
435
502
|
- [Project Structure](./references/project-structure.md) - Recommended file organization
|
|
503
|
+
- [JSON Render Patterns](./references/json-render-patterns.md) - Server-side JSON specs, components, state binding
|
|
504
|
+
- [Terminals Patterns](./references/terminals-patterns.md) - Child processes, custom streams, session lifecycle
|
|
436
505
|
- [Logs Patterns](./references/logs-patterns.md) - Log entries, toast notifications, and handle patterns
|
|
@@ -201,6 +201,93 @@ export default function setup(ctx: DevToolsClientScriptContext) {
|
|
|
201
201
|
}
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
+
## JSON Render Entries
|
|
205
|
+
|
|
206
|
+
Server-side JSON specs rendered by the built-in component library. No client code needed.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
interface JsonRenderEntry extends DockEntryBase {
|
|
210
|
+
type: 'json-render'
|
|
211
|
+
ui: JsonRenderer // Handle from ctx.createJsonRenderer()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Registration
|
|
215
|
+
const ui = ctx.createJsonRenderer({
|
|
216
|
+
root: 'root',
|
|
217
|
+
state: { query: '' },
|
|
218
|
+
elements: {
|
|
219
|
+
root: {
|
|
220
|
+
type: 'Stack',
|
|
221
|
+
props: { direction: 'vertical', gap: 12 },
|
|
222
|
+
children: ['heading', 'info'],
|
|
223
|
+
},
|
|
224
|
+
heading: {
|
|
225
|
+
type: 'Text',
|
|
226
|
+
props: { content: 'My Panel', variant: 'heading' },
|
|
227
|
+
},
|
|
228
|
+
info: {
|
|
229
|
+
type: 'KeyValueTable',
|
|
230
|
+
props: {
|
|
231
|
+
entries: [
|
|
232
|
+
{ key: 'Status', value: 'Running' },
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
ctx.docks.register({
|
|
240
|
+
id: 'my-panel',
|
|
241
|
+
title: 'My Panel',
|
|
242
|
+
icon: 'ph:chart-bar-duotone',
|
|
243
|
+
type: 'json-render',
|
|
244
|
+
ui,
|
|
245
|
+
})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Dynamic Updates
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// Replace the entire spec
|
|
252
|
+
await ui.updateSpec(buildSpec(newData))
|
|
253
|
+
|
|
254
|
+
// Shallow-merge into spec.state
|
|
255
|
+
await ui.updateState({ query: 'vue' })
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Action Handling
|
|
259
|
+
|
|
260
|
+
Buttons trigger server-side RPC functions via `on.press.action`:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
// In spec element
|
|
264
|
+
{
|
|
265
|
+
type: 'Button',
|
|
266
|
+
props: { label: 'Refresh', icon: 'ph:arrows-clockwise' },
|
|
267
|
+
on: { press: { action: 'my-plugin:refresh' } },
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Matching RPC function
|
|
271
|
+
ctx.rpc.register(defineRpcFunction({
|
|
272
|
+
name: 'my-plugin:refresh',
|
|
273
|
+
type: 'action',
|
|
274
|
+
setup: ctx => ({
|
|
275
|
+
handler: async () => {
|
|
276
|
+
await ui.updateSpec(buildSpec(await fetchData()))
|
|
277
|
+
},
|
|
278
|
+
}),
|
|
279
|
+
}))
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### JSON Render Use Cases
|
|
283
|
+
|
|
284
|
+
- **Build reports** — Display build stats, module lists, timing data
|
|
285
|
+
- **Configuration viewers** — Show resolved config with key-value tables
|
|
286
|
+
- **Status dashboards** — Progress bars, badges, real-time updates
|
|
287
|
+
- **Simple forms** — Text inputs with state binding + action buttons
|
|
288
|
+
|
|
289
|
+
See [JSON Render Patterns](./json-render-patterns.md) for the full component library and state binding details.
|
|
290
|
+
|
|
204
291
|
## Launcher Entries
|
|
205
292
|
|
|
206
293
|
Actionable setup cards for running initialization tasks. Shows a card with title, description, and a launch button.
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# JSON Render Patterns
|
|
2
|
+
|
|
3
|
+
Build DevTools UIs entirely from server-side TypeScript — no client code needed. Describe your UI as a JSON spec, and the DevTools client renders it with the built-in component library.
|
|
4
|
+
|
|
5
|
+
## Spec Structure
|
|
6
|
+
|
|
7
|
+
A JSON render spec has three parts: a `root` element ID, an `elements` map, and an optional `state` object for two-way bindings.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
ctx.createJsonRenderer({
|
|
11
|
+
root: 'root',
|
|
12
|
+
state: {
|
|
13
|
+
searchQuery: '',
|
|
14
|
+
},
|
|
15
|
+
elements: {
|
|
16
|
+
root: {
|
|
17
|
+
type: 'Stack',
|
|
18
|
+
props: { direction: 'vertical', gap: 12 },
|
|
19
|
+
children: ['title', 'content'],
|
|
20
|
+
},
|
|
21
|
+
title: {
|
|
22
|
+
type: 'Text',
|
|
23
|
+
props: { content: 'My Panel', variant: 'heading' },
|
|
24
|
+
},
|
|
25
|
+
content: {
|
|
26
|
+
type: 'Text',
|
|
27
|
+
props: { content: 'Hello world' },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Every element has a `type` (component name), `props`, and optionally `children` (array of element IDs) or `on` (event handlers).
|
|
34
|
+
|
|
35
|
+
## Registration
|
|
36
|
+
|
|
37
|
+
Pass the renderer handle as `ui` when registering a `json-render` dock entry:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const ui = ctx.createJsonRenderer(spec)
|
|
41
|
+
|
|
42
|
+
ctx.docks.register({
|
|
43
|
+
id: 'my-panel',
|
|
44
|
+
title: 'My Panel',
|
|
45
|
+
icon: 'ph:chart-bar-duotone',
|
|
46
|
+
type: 'json-render',
|
|
47
|
+
ui,
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Dynamic Updates
|
|
52
|
+
|
|
53
|
+
The `JsonRenderer` handle provides two methods for updating the UI reactively:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const ui = ctx.createJsonRenderer(buildSpec(initialData))
|
|
57
|
+
|
|
58
|
+
// Replace the entire spec (e.g. after fetching new data)
|
|
59
|
+
await ui.updateSpec(buildSpec(newData))
|
|
60
|
+
|
|
61
|
+
// Shallow-merge into spec.state (updates client-side state values)
|
|
62
|
+
await ui.updateState({ searchQuery: 'vue' })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Update the dock entry badge when data changes:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
ctx.docks.update({
|
|
69
|
+
id: 'my-panel',
|
|
70
|
+
type: 'json-render',
|
|
71
|
+
title: 'My Panel',
|
|
72
|
+
icon: 'ph:chart-bar-duotone',
|
|
73
|
+
ui,
|
|
74
|
+
badge: hasWarnings ? '!' : undefined,
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Handling Actions via RPC
|
|
79
|
+
|
|
80
|
+
Buttons in the spec trigger RPC functions on the server via the `on` property:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
// In the spec — Button with an action
|
|
84
|
+
const ui = ctx.createJsonRenderer({
|
|
85
|
+
root: 'refresh-btn',
|
|
86
|
+
elements: {
|
|
87
|
+
'refresh-btn': {
|
|
88
|
+
type: 'Button',
|
|
89
|
+
props: { label: 'Refresh', icon: 'ph:arrows-clockwise' },
|
|
90
|
+
on: { press: { action: 'my-plugin:refresh' } },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// On the server — register the matching RPC function
|
|
96
|
+
ctx.rpc.register(defineRpcFunction({
|
|
97
|
+
name: 'my-plugin:refresh',
|
|
98
|
+
type: 'action',
|
|
99
|
+
setup: ctx => ({
|
|
100
|
+
handler: async () => {
|
|
101
|
+
const data = await fetchData()
|
|
102
|
+
await ui.updateSpec(buildSpec(data))
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
}))
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Pass parameters from the spec to the action handler:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
on: {
|
|
112
|
+
press: {
|
|
113
|
+
action: 'my-plugin:delete',
|
|
114
|
+
params: { id: 'some-id' },
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## State and Two-Way Binding
|
|
120
|
+
|
|
121
|
+
Use `$bindState` on TextInput `value` to create two-way binding with a state key. Use `$state` to read the bound value in action params:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const ui = ctx.createJsonRenderer({
|
|
125
|
+
root: 'root',
|
|
126
|
+
state: { message: '' },
|
|
127
|
+
elements: {
|
|
128
|
+
root: {
|
|
129
|
+
type: 'Stack',
|
|
130
|
+
props: { direction: 'horizontal', gap: 8 },
|
|
131
|
+
children: ['input', 'submit'],
|
|
132
|
+
},
|
|
133
|
+
input: {
|
|
134
|
+
type: 'TextInput',
|
|
135
|
+
props: {
|
|
136
|
+
placeholder: 'Type here...',
|
|
137
|
+
value: { $bindState: '/message' },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
submit: {
|
|
141
|
+
type: 'Button',
|
|
142
|
+
props: { label: 'Submit', variant: 'primary' },
|
|
143
|
+
on: {
|
|
144
|
+
press: {
|
|
145
|
+
action: 'my-plugin:submit',
|
|
146
|
+
params: { text: { $state: '/message' } },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The server-side handler receives the resolved state values:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
ctx.rpc.register(defineRpcFunction({
|
|
158
|
+
name: 'my-plugin:submit',
|
|
159
|
+
type: 'action',
|
|
160
|
+
setup: ctx => ({
|
|
161
|
+
handler: async (params: { text?: string }) => {
|
|
162
|
+
console.log('User submitted:', params.text)
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
}))
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Built-in Components
|
|
169
|
+
|
|
170
|
+
### Layout
|
|
171
|
+
|
|
172
|
+
| Component | Props | Description |
|
|
173
|
+
|-----------|-------|-------------|
|
|
174
|
+
| `Stack` | `direction`, `gap`, `align`, `justify`, `padding` | Flex layout container |
|
|
175
|
+
| `Card` | `title`, `collapsible` | Container with optional title, collapsible |
|
|
176
|
+
| `Divider` | `label` | Separator line with optional label |
|
|
177
|
+
|
|
178
|
+
### Typography
|
|
179
|
+
|
|
180
|
+
| Component | Props | Description |
|
|
181
|
+
|-----------|-------|-------------|
|
|
182
|
+
| `Text` | `content`, `variant` (`heading`/`body`/`caption`/`code`) | Display text |
|
|
183
|
+
| `Icon` | `name`, `size` | Iconify icon by name |
|
|
184
|
+
| `Badge` | `text`, `variant` (`info`/`success`/`warning`/`error`/`default`) | Status label |
|
|
185
|
+
|
|
186
|
+
### Inputs
|
|
187
|
+
|
|
188
|
+
| Component | Props | Description |
|
|
189
|
+
|-----------|-------|-------------|
|
|
190
|
+
| `Button` | `label`, `icon`, `variant` (`primary`/`secondary`/`ghost`/`danger`), `disabled` | Clickable button, fires `press` event |
|
|
191
|
+
| `TextInput` | `placeholder`, `value`, `label`, `disabled` | Text input, supports `$bindState` on `value` |
|
|
192
|
+
|
|
193
|
+
### Data Display
|
|
194
|
+
|
|
195
|
+
| Component | Props | Description |
|
|
196
|
+
|-----------|-------|-------------|
|
|
197
|
+
| `KeyValueTable` | `title`, `entries` (`Array<{ key, value }>`) | Two-column key-value table |
|
|
198
|
+
| `DataTable` | `columns`, `rows`, `maxHeight` | Tabular data with configurable columns |
|
|
199
|
+
| `CodeBlock` | `code`, `language`, `filename`, `maxHeight` | Code snippet with optional filename header |
|
|
200
|
+
| `Progress` | `value`, `max`, `label` | Progress bar with percentage |
|
|
201
|
+
| `Tree` | `data`, `expandLevel` | Expandable tree for nested objects |
|
|
202
|
+
|
|
203
|
+
## Full Example
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import type { JsonRenderSpec, PluginWithDevTools } from '@vitejs/devtools-kit'
|
|
207
|
+
import { defineRpcFunction } from '@vitejs/devtools-kit'
|
|
208
|
+
|
|
209
|
+
function buildSpec(data: { modules: number, time: string, size: string }): JsonRenderSpec {
|
|
210
|
+
return {
|
|
211
|
+
root: 'root',
|
|
212
|
+
state: { filter: '' },
|
|
213
|
+
elements: {
|
|
214
|
+
'root': {
|
|
215
|
+
type: 'Stack',
|
|
216
|
+
props: { direction: 'vertical', gap: 12, padding: 8 },
|
|
217
|
+
children: ['header', 'divider', 'stats'],
|
|
218
|
+
},
|
|
219
|
+
'header': {
|
|
220
|
+
type: 'Stack',
|
|
221
|
+
props: { direction: 'horizontal', gap: 8, align: 'center', justify: 'space-between' },
|
|
222
|
+
children: ['title', 'refresh-btn'],
|
|
223
|
+
},
|
|
224
|
+
'title': {
|
|
225
|
+
type: 'Text',
|
|
226
|
+
props: { content: 'Build Report', variant: 'heading' },
|
|
227
|
+
},
|
|
228
|
+
'refresh-btn': {
|
|
229
|
+
type: 'Button',
|
|
230
|
+
props: { label: 'Refresh', icon: 'ph:arrows-clockwise' },
|
|
231
|
+
on: { press: { action: 'build-report:refresh' } },
|
|
232
|
+
},
|
|
233
|
+
'divider': {
|
|
234
|
+
type: 'Divider',
|
|
235
|
+
props: {},
|
|
236
|
+
},
|
|
237
|
+
'stats': {
|
|
238
|
+
type: 'KeyValueTable',
|
|
239
|
+
props: {
|
|
240
|
+
title: 'Summary',
|
|
241
|
+
entries: [
|
|
242
|
+
{ key: 'Total Modules', value: String(data.modules) },
|
|
243
|
+
{ key: 'Build Time', value: data.time },
|
|
244
|
+
{ key: 'Output Size', value: data.size },
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function BuildReportPlugin(): PluginWithDevTools {
|
|
253
|
+
return {
|
|
254
|
+
name: 'build-report',
|
|
255
|
+
devtools: {
|
|
256
|
+
setup(ctx) {
|
|
257
|
+
const data = { modules: 142, time: '1.2s', size: '48 KB' }
|
|
258
|
+
const ui = ctx.createJsonRenderer(buildSpec(data))
|
|
259
|
+
|
|
260
|
+
ctx.docks.register({
|
|
261
|
+
id: 'build-report',
|
|
262
|
+
title: 'Build Report',
|
|
263
|
+
icon: 'ph:chart-bar-duotone',
|
|
264
|
+
type: 'json-render',
|
|
265
|
+
ui,
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
ctx.rpc.register(defineRpcFunction({
|
|
269
|
+
name: 'build-report:refresh',
|
|
270
|
+
type: 'action',
|
|
271
|
+
setup: ctx => ({
|
|
272
|
+
handler: async () => {
|
|
273
|
+
const newData = { modules: 145, time: '1.1s', size: '47 KB' }
|
|
274
|
+
await ui.updateSpec(buildSpec(newData))
|
|
275
|
+
},
|
|
276
|
+
}),
|
|
277
|
+
}))
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
> See the [Git UI example](https://github.com/vitejs/devtools/tree/main/examples/plugin-git-ui) for a more advanced plugin using json-render with per-file actions, text input with state binding, and dynamic badge updates.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Terminals & Subprocesses
|
|
2
|
+
|
|
3
|
+
Spawn and manage child processes from your plugin. Output streams in real-time to an xterm.js terminal inside DevTools.
|
|
4
|
+
|
|
5
|
+
## Starting a Child Process
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
const session = await ctx.terminals.startChildProcess(
|
|
9
|
+
{
|
|
10
|
+
command: 'vite',
|
|
11
|
+
args: ['build', '--watch'],
|
|
12
|
+
cwd: process.cwd(),
|
|
13
|
+
env: { NODE_ENV: 'development' },
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 'my-plugin:build-watcher',
|
|
17
|
+
title: 'Build Watcher',
|
|
18
|
+
icon: 'ph:terminal-duotone',
|
|
19
|
+
},
|
|
20
|
+
)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Execute Options
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
interface DevToolsChildProcessExecuteOptions {
|
|
27
|
+
command: string
|
|
28
|
+
args: string[]
|
|
29
|
+
cwd?: string
|
|
30
|
+
env?: Record<string, string>
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The second argument provides terminal metadata (`id`, `title`, and optional `description`/`icon`).
|
|
35
|
+
|
|
36
|
+
> Color output is enabled automatically — `FORCE_COLOR` and `COLORS` environment variables are set to `'true'` by default.
|
|
37
|
+
|
|
38
|
+
## Session Lifecycle
|
|
39
|
+
|
|
40
|
+
`startChildProcess()` returns a `DevToolsChildProcessTerminalSession` with lifecycle controls:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// Terminate the process
|
|
44
|
+
await session.terminate()
|
|
45
|
+
|
|
46
|
+
// Restart (kill + re-spawn)
|
|
47
|
+
await session.restart()
|
|
48
|
+
|
|
49
|
+
// Access the underlying Node.js ChildProcess
|
|
50
|
+
const cp = session.getChildProcess()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Session Status
|
|
54
|
+
|
|
55
|
+
| Status | Description |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| `running` | Process is active and streaming output |
|
|
58
|
+
| `stopped` | Process exited normally |
|
|
59
|
+
| `error` | Process exited with an error |
|
|
60
|
+
|
|
61
|
+
Update a session's metadata or status at any time:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
ctx.terminals.update({
|
|
65
|
+
id: 'my-plugin:build-watcher',
|
|
66
|
+
status: 'stopped',
|
|
67
|
+
title: 'Build Watcher (done)',
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Combining with Launcher Docks
|
|
72
|
+
|
|
73
|
+
A common pattern is pairing a launcher dock entry with a terminal session. The launcher gives the user a button to start the process on demand:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
ctx.docks.register({
|
|
77
|
+
id: 'my-plugin:launcher',
|
|
78
|
+
title: 'My App',
|
|
79
|
+
icon: 'ph:rocket-launch-duotone',
|
|
80
|
+
type: 'launcher',
|
|
81
|
+
launcher: {
|
|
82
|
+
title: 'Start My App',
|
|
83
|
+
description: 'Launch the dev server',
|
|
84
|
+
onLaunch: async () => {
|
|
85
|
+
await ctx.terminals.startChildProcess(
|
|
86
|
+
{
|
|
87
|
+
command: 'vite',
|
|
88
|
+
args: ['dev'],
|
|
89
|
+
cwd: process.cwd(),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'my-plugin:dev-server',
|
|
93
|
+
title: 'Dev Server',
|
|
94
|
+
},
|
|
95
|
+
)
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Custom Terminal Sessions
|
|
102
|
+
|
|
103
|
+
For scenarios that don't involve spawning a child process (e.g. streaming logs from an external source), register a session directly with a custom `ReadableStream`:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
let controller: ReadableStreamDefaultController<string>
|
|
107
|
+
|
|
108
|
+
const stream = new ReadableStream<string>({
|
|
109
|
+
start(c) {
|
|
110
|
+
controller = c
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
ctx.terminals.register({
|
|
115
|
+
id: 'my-plugin:custom-stream',
|
|
116
|
+
title: 'Custom Output',
|
|
117
|
+
status: 'running',
|
|
118
|
+
stream,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Push data to the terminal
|
|
122
|
+
controller.enqueue('Hello from custom stream!\n')
|
|
123
|
+
```
|