@hachej/boring-workspace 0.1.0
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/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/CodeEditor-DQqOn4xz.js +266 -0
- package/dist/CommandPalette-aM61U-b0.js +5229 -0
- package/dist/FileTree-DRq_bfue.js +245 -0
- package/dist/MarkdownEditor-DjiHxnRv.js +349 -0
- package/dist/WorkspaceLoadingState-By0dZoPD.js +568 -0
- package/dist/agent-tool-NvxKfist.d.ts +28 -0
- package/dist/app-front.d.ts +485 -0
- package/dist/app-front.js +452 -0
- package/dist/app-server.d.ts +53 -0
- package/dist/app-server.js +769 -0
- package/dist/bootstrapServer-BRUqUpVW.d.ts +66 -0
- package/dist/boring-workspace.css +1 -0
- package/dist/charts.d.ts +114 -0
- package/dist/charts.js +143 -0
- package/dist/events.d.ts +178 -0
- package/dist/events.js +88 -0
- package/dist/explorer-DtLUnuah.d.ts +129 -0
- package/dist/panel-DnvDNQac.js +6 -0
- package/dist/server.d.ts +84 -0
- package/dist/server.js +811 -0
- package/dist/shared.d.ts +113 -0
- package/dist/shared.js +11 -0
- package/dist/testing-e2e.d.ts +68 -0
- package/dist/testing-e2e.js +45 -0
- package/dist/testing.d.ts +464 -0
- package/dist/testing.js +10984 -0
- package/dist/utils-B6yFEsav.js +8 -0
- package/dist/workspace.css +5780 -0
- package/dist/workspace.d.ts +2119 -0
- package/dist/workspace.js +1884 -0
- package/docs/INTERFACES.md +58 -0
- package/docs/PLUGIN_STRUCTURE.md +162 -0
- package/docs/README.md +19 -0
- package/docs/bridge.md +135 -0
- package/docs/panels.md +102 -0
- package/docs/plans/GENERIC_EXPLORER_PLUGIN_PLAN.md +455 -0
- package/docs/plans/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md +962 -0
- package/docs/plans/PLUGIN_OUTPUTS_ISOLATION_PLAN.md +301 -0
- package/docs/plans/README.md +9 -0
- package/docs/plans/UI_BRIDGE_OWNERSHIP_REFACTOR.md +303 -0
- package/docs/plans/archive/CODE_OWNERSHIP_CLEANUP_PLAN.md +387 -0
- package/docs/plans/archive/COMMAND_PALETTE_REGISTRY.md +814 -0
- package/docs/plans/archive/DECLARATIVE_LAYOUT_MIGRATION.md +277 -0
- package/docs/plans/archive/PLUGIN_MODEL.md +3674 -0
- package/docs/plans/archive/SRC_FOLDER_REORG_PLAN.md +307 -0
- package/docs/plans/archive/UNIFIED_EVENT_BUS.md +647 -0
- package/docs/plans/archive/WORKSPACE_V2_PLAN.md +2489 -0
- package/docs/plugins.md +158 -0
- package/package.json +164 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Workspace Interfaces
|
|
2
|
+
|
|
3
|
+
Last updated: 2026-05-02
|
|
4
|
+
|
|
5
|
+
`@boring/workspace` is a workspace UI and bridge package. The app shell owns
|
|
6
|
+
auth, routing, application persistence, and the concrete chat component.
|
|
7
|
+
Workspace owns layout runtime, layout preferences, plugin registries, bridge
|
|
8
|
+
commands, and default workspace plugins.
|
|
9
|
+
|
|
10
|
+
## Package Boundaries
|
|
11
|
+
|
|
12
|
+
- `src/front/` hosts React providers, layouts, Dockview chrome, registries,
|
|
13
|
+
bridge clients, and generic UI.
|
|
14
|
+
- `src/plugins/` hosts plugin-owned domain behavior. Plugin code is split by
|
|
15
|
+
layer: `front/`, `server/`, and `shared/`.
|
|
16
|
+
- `src/server/` hosts workspace UI bridge routes, UI tools, and server plugin
|
|
17
|
+
bootstrap helpers.
|
|
18
|
+
- `src/shared/` hosts browser-safe contracts only. No `node:*`, no `Buffer`,
|
|
19
|
+
and no agent package imports.
|
|
20
|
+
- `src/app/` hosts front/server composition helpers such as
|
|
21
|
+
`WorkspaceAgentFront` and `createWorkspaceAgentServer`, where workspace app
|
|
22
|
+
code may compose with documented `@boring/agent/server` APIs.
|
|
23
|
+
|
|
24
|
+
## Core Contracts
|
|
25
|
+
|
|
26
|
+
- Plugin outputs: `src/shared/plugins/types.ts`
|
|
27
|
+
- `panel`, `left-tab`, `command`, `catalog`, `binding`, `provider`,
|
|
28
|
+
`surface-resolver`, and `agent-tool`.
|
|
29
|
+
- Surface opening: `src/shared/types/surface.ts`
|
|
30
|
+
- `SurfaceOpenRequest { kind, target, meta }` is resolved by plugin
|
|
31
|
+
`surface-resolver` outputs into panel openings.
|
|
32
|
+
- UI bridge: `src/shared/ui-bridge.ts`
|
|
33
|
+
- Agents and servers post `UiCommand` values. The front-end dispatches them
|
|
34
|
+
against the workspace runtime.
|
|
35
|
+
- Filesystem data: `src/plugins/filesystemPlugin/front/data`
|
|
36
|
+
- Filesystem client, hooks, event stream, and cache invalidation are plugin
|
|
37
|
+
owned.
|
|
38
|
+
- Data catalog: `src/plugins/dataCatalogPlugin/front` and
|
|
39
|
+
`src/plugins/dataCatalogPlugin/server`
|
|
40
|
+
- Catalog rows are opened through `openSurface`; row-to-panel mapping belongs
|
|
41
|
+
to the plugin resolver.
|
|
42
|
+
- Server plugins: `src/server/plugins`
|
|
43
|
+
- `defineServerPlugin()` validates tools, routes, provisioning, and native Pi
|
|
44
|
+
package declarations.
|
|
45
|
+
- `composeServerPlugins()` combines smaller server plugin fragments.
|
|
46
|
+
- `piPackages` are passed to `@boring/agent` as in-memory Pi settings, so
|
|
47
|
+
workspace adapters can depend on native Pi packages without requiring
|
|
48
|
+
Boring-specific exports from those packages.
|
|
49
|
+
|
|
50
|
+
## Ownership Rules
|
|
51
|
+
|
|
52
|
+
- Workspace chrome must not hardcode plugin panel ids or plugin domain rules.
|
|
53
|
+
- Plugin data APIs stay under the owning plugin; there is no `front/data`
|
|
54
|
+
compatibility layer.
|
|
55
|
+
- Use `openSurface` for domain targets that need resolver selection.
|
|
56
|
+
- Use `openPanel` only when the caller intentionally names the concrete panel.
|
|
57
|
+
- Front/shared workspace code does not value-import `@boring/agent`; app/server
|
|
58
|
+
composition may import documented agent server APIs.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Workspace Plugin Structure
|
|
2
|
+
|
|
3
|
+
Last updated: 2026-05-01
|
|
4
|
+
|
|
5
|
+
Workspace plugins use a small, predictable folder shape. The goal is
|
|
6
|
+
ownership clarity, not ceremony: plugin domain behavior stays in the plugin,
|
|
7
|
+
and workspace core stays a generic host.
|
|
8
|
+
|
|
9
|
+
See `INTERFACES.md` for the package-level contracts these plugins contribute
|
|
10
|
+
to.
|
|
11
|
+
|
|
12
|
+
## Standard Layout
|
|
13
|
+
|
|
14
|
+
Start from `packages/workspace/templates/plugin/` when creating a new plugin,
|
|
15
|
+
then delete the files the plugin does not need.
|
|
16
|
+
|
|
17
|
+
```txt
|
|
18
|
+
<plugin>/
|
|
19
|
+
front/
|
|
20
|
+
index.tsx # front plugin factory and public front exports
|
|
21
|
+
panels.tsx # panel definitions
|
|
22
|
+
catalogs.ts # catalog outputs/config helpers
|
|
23
|
+
surfaceResolver.ts # openSurface target -> panel resolution
|
|
24
|
+
bindings.tsx # React side-effect bindings, if needed
|
|
25
|
+
data/ # plugin-owned client data API/hooks/cache
|
|
26
|
+
server/
|
|
27
|
+
index.ts # server plugin factory and public server exports
|
|
28
|
+
tools.ts # agent tools, if needed
|
|
29
|
+
routes.ts # server routes, if needed
|
|
30
|
+
shared/
|
|
31
|
+
constants.ts # plugin id, catalog ids, surface kinds
|
|
32
|
+
types.ts # platform-neutral shared types, if needed
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use only the files a plugin actually needs. Large component families may live
|
|
36
|
+
in subfolders such as `file-tree/`, `code-editor/`, or `empty-file-panel/`.
|
|
37
|
+
|
|
38
|
+
## Ownership Rules
|
|
39
|
+
|
|
40
|
+
- `front/index.tsx` composes front outputs; large behavior belongs in focused files.
|
|
41
|
+
- `server/index.ts` composes server outputs with `defineServerPlugin()`.
|
|
42
|
+
- Shared files must stay platform-neutral. Do not import plugin `front/` or
|
|
43
|
+
`server/` code from `shared/`.
|
|
44
|
+
- Domain search belongs in `front/catalogs.ts`.
|
|
45
|
+
- Domain open behavior belongs in `front/surfaceResolver.ts`.
|
|
46
|
+
- Domain event names live in `shared/events.ts` when both layers need the
|
|
47
|
+
contract; event names must be keyed by plugin id.
|
|
48
|
+
- Plugin data clients/hooks live in `front/data/`, not package `front/data`.
|
|
49
|
+
- Server prompts/tools/routes/provisioning live under `server/`, not mixed
|
|
50
|
+
with client code.
|
|
51
|
+
- Workspace core may host registries, providers, event transport, and bridge
|
|
52
|
+
dispatch only. It must not hardcode plugin panel ids or plugin domain rules.
|
|
53
|
+
- Catalog selection and plugin-owned routing should prefer `openSurface`.
|
|
54
|
+
`openPanel` remains available for explicit app-level panel opens.
|
|
55
|
+
- Executable agent tools should be server plugin contributions. The legacy
|
|
56
|
+
front `agentTools` field remains for migration only.
|
|
57
|
+
|
|
58
|
+
## Composed Plugins
|
|
59
|
+
|
|
60
|
+
Use `composePlugins()` when a front plugin is easier to build from smaller
|
|
61
|
+
front fragments. The composed plugin flattens child panels, commands,
|
|
62
|
+
catalogs, bindings, and outputs into one normal `WorkspaceFrontPlugin`.
|
|
63
|
+
|
|
64
|
+
Default behavior adopts child ownership to the parent plugin id. Use
|
|
65
|
+
`adoptOutputs: false` only when registry ownership must stay attached to the
|
|
66
|
+
child fragment for diagnostics or selective unregister behavior.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const macroPlugin = composePlugins({
|
|
70
|
+
id: "boring-macro",
|
|
71
|
+
label: "Macro",
|
|
72
|
+
plugins: [macroPanelsPlugin, macroSurfacesPlugin, macroSeriesExplorerPlugin],
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Use `composeServerPlugins()` for the matching server side. It concatenates
|
|
77
|
+
child tools, prompt text, pi package declarations, provisioning, and routes
|
|
78
|
+
into one normal `WorkspaceServerPlugin`.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const macroServerPlugin = composeServerPlugins({
|
|
82
|
+
id: "boring-macro",
|
|
83
|
+
label: "Macro",
|
|
84
|
+
plugins: [macroToolsPlugin, macroRoutesPlugin],
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Pi Package Adapters
|
|
89
|
+
|
|
90
|
+
Treat pi packages as implementation dependencies. The app-facing contract is a
|
|
91
|
+
workspace adapter plugin that wraps the pi package and optionally adds front
|
|
92
|
+
integration.
|
|
93
|
+
|
|
94
|
+
Do not require pi packages to export Boring-specific adapters. The pi ecosystem
|
|
95
|
+
already has its own shape, such as `package.json` `pi.extensions` entries and
|
|
96
|
+
extension functions that call `pi.registerCommand(...)`. Workspace adapters
|
|
97
|
+
should adapt to that shape.
|
|
98
|
+
|
|
99
|
+
Declare native pi package dependencies on the server plugin:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
defineServerPlugin({
|
|
103
|
+
id: "markdown-preview",
|
|
104
|
+
piPackages: ["npm:pi-markdown-preview@0.9.7"],
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Workspace passes these declarations to `@boring/agent` as in-memory Pi
|
|
109
|
+
settings. This enables Pi's native package loader without mutating
|
|
110
|
+
`.pi/settings.json`.
|
|
111
|
+
|
|
112
|
+
```txt
|
|
113
|
+
src/plugins/markdownPreview/
|
|
114
|
+
shared/
|
|
115
|
+
constants.ts # workspace ids, surface kinds, command names
|
|
116
|
+
server/
|
|
117
|
+
index.ts # defineServerPlugin(), pi package dependency wrapper
|
|
118
|
+
routes.ts # optional workspace-native render/preview routes
|
|
119
|
+
front/
|
|
120
|
+
index.tsx # defineFrontPlugin()
|
|
121
|
+
panels.tsx # workspace-native preview panel
|
|
122
|
+
surfaceResolver.ts # markdown-preview.open -> panel resolution
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
For example, a wrapper around `pi-markdown-preview` can depend on that package,
|
|
126
|
+
read or invoke its pi extension behavior on the server side, and expose a
|
|
127
|
+
workspace-native `markdown-preview.open` surface on the front side. The agent
|
|
128
|
+
prompt should teach the model to use workspace `openSurface` when a Boring app
|
|
129
|
+
is present, even if the underlying pi package also provides terminal/browser
|
|
130
|
+
slash commands.
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
## Current Plugins
|
|
134
|
+
|
|
135
|
+
- `packages/workspace/src/plugins/filesystemPlugin`
|
|
136
|
+
- `packages/workspace/src/plugins/dataCatalogPlugin`
|
|
137
|
+
- `apps/boring-macro-v2/src/plugins/macro`
|
|
138
|
+
- `apps/workspace-playground/src/plugins/playgroundDataCatalog`
|
|
139
|
+
|
|
140
|
+
## Invariants
|
|
141
|
+
|
|
142
|
+
Run:
|
|
143
|
+
|
|
144
|
+
```sh
|
|
145
|
+
pnpm --filter @boring/workspace run lint:plugin-invariants
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The scan rejects:
|
|
149
|
+
|
|
150
|
+
- `filePatterns`, `fileFallback`, `PanelRegistry.resolve`, and file-handler
|
|
151
|
+
routing metadata in source.
|
|
152
|
+
- `front/data` imports or path references in source.
|
|
153
|
+
- `@boring/agent` imports from `workspace/src/shared/plugins`.
|
|
154
|
+
- `front`/`server` imports from production `workspace/src/shared/plugins`.
|
|
155
|
+
- Production plugin `front/`, `server/`, and `shared/` layers importing across
|
|
156
|
+
the wrong layer boundary.
|
|
157
|
+
- TypeScript source files directly under a plugin root instead of
|
|
158
|
+
`front/`, `server/`, or `shared/`.
|
|
159
|
+
- Plugin-domain imports from production `workspace/src/front/chrome`, `events`,
|
|
160
|
+
and `hooks`.
|
|
161
|
+
- Legacy plugin file names such as `catalog.ts`, `surfaceTargets.ts`,
|
|
162
|
+
root-level `client.ts`, or root-level `server.ts`.
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Workspace Docs
|
|
2
|
+
|
|
3
|
+
Current package docs live here. Active ownership plans stay in `docs/plans/`.
|
|
4
|
+
Older implementation plans live in `docs/plans/archive/`.
|
|
5
|
+
|
|
6
|
+
## Current References
|
|
7
|
+
|
|
8
|
+
- `INTERFACES.md` - package boundaries and public abstractions.
|
|
9
|
+
- `PLUGIN_STRUCTURE.md` - plugin folder shape and invariant scans.
|
|
10
|
+
- `plans/PLUGIN_OUTPUTS_ISOLATION_PLAN.md` - latest plugin ownership plan.
|
|
11
|
+
- `plans/UI_BRIDGE_OWNERSHIP_REFACTOR.md` - UI bridge ownership decision.
|
|
12
|
+
- `plans/archive/` - superseded implementation plans.
|
|
13
|
+
|
|
14
|
+
## Rules
|
|
15
|
+
|
|
16
|
+
- Keep root package markdown to `README.md` and `CHANGELOG.md`.
|
|
17
|
+
- Prefer short current-reference docs over long status reports.
|
|
18
|
+
- Move old plans into `plans/archive/` instead of keeping them beside active
|
|
19
|
+
ownership plans.
|
package/docs/bridge.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
> boring-ui agents use exec_ui to open panels and interact with the workspace. Ask boring-ui to wire up a new surface.
|
|
2
|
+
|
|
3
|
+
# UI Bridge
|
|
4
|
+
|
|
5
|
+
The UI bridge is the typed pubsub channel between the agent backend and the workspace frontend. The agent calls `exec_ui` (a tool) to post commands; the frontend dispatches them against the live workspace runtime.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Opening a panel from the agent](#opening-a-panel-from-the-agent)
|
|
10
|
+
- [openSurface vs openPanel](#opensurface-vs-openpanel)
|
|
11
|
+
- [Reading current UI state](#reading-current-ui-state)
|
|
12
|
+
- [Surface resolvers](#surface-resolvers)
|
|
13
|
+
- [Posting from server code](#posting-from-server-code)
|
|
14
|
+
- [Frontend event bus](#frontend-event-bus)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Opening a panel from the agent
|
|
19
|
+
|
|
20
|
+
Use `exec_ui` with `kind: "openSurface"`:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"kind": "openSurface",
|
|
25
|
+
"params": {
|
|
26
|
+
"kind": "my-plugin.open",
|
|
27
|
+
"target": "item-123",
|
|
28
|
+
"meta": { "title": "My Item" }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The workspace routes this to the plugin's `surface-resolver` output, which maps the `kind` to a concrete panel open call.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## openSurface vs openPanel
|
|
38
|
+
|
|
39
|
+
| method | use when |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `openSurface` | you want plugin resolver selection — preferred for domain targets |
|
|
42
|
+
| `openPanel` | you intentionally name the concrete panel id |
|
|
43
|
+
|
|
44
|
+
**Prefer `openSurface`.** It keeps the agent decoupled from panel ids and lets the plugin control routing.
|
|
45
|
+
|
|
46
|
+
Open a file in the editor (built-in surface):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"kind": "openSurface",
|
|
51
|
+
"params": {
|
|
52
|
+
"kind": "workspace.open.path",
|
|
53
|
+
"target": "src/index.ts"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Reading current UI state
|
|
61
|
+
|
|
62
|
+
Use `get_ui_state` before `openPanel` to discover which panel components are registered, or to check what the user is currently viewing:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
// tool call: get_ui_state (no params)
|
|
66
|
+
// returns:
|
|
67
|
+
{
|
|
68
|
+
"workbenchOpen": true,
|
|
69
|
+
"drawerOpen": false,
|
|
70
|
+
"openTabs": [{ "id": "...", "title": "...", "params": {} }],
|
|
71
|
+
"activeTab": "tab-id-or-null",
|
|
72
|
+
"activeFile": "src/index.ts-or-null",
|
|
73
|
+
"availablePanels": ["code-editor", "chart-canvas", "..."]
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`availablePanels` lists every component id registered by the host — use these with `exec_ui openPanel`.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Surface resolvers
|
|
82
|
+
|
|
83
|
+
Register a surface resolver in your plugin to map `SurfaceOpenRequest` kinds to panel opens:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { defineFrontPlugin, type SurfacePanelResolution } from '@boring/workspace'
|
|
87
|
+
|
|
88
|
+
defineFrontPlugin({
|
|
89
|
+
outputs: [
|
|
90
|
+
{
|
|
91
|
+
type: 'surface-resolver',
|
|
92
|
+
resolve(req): SurfacePanelResolution | null {
|
|
93
|
+
if (req.kind === 'my-plugin.open') {
|
|
94
|
+
return {
|
|
95
|
+
panelId: 'my-panel',
|
|
96
|
+
params: { id: req.target },
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Posting from server code
|
|
109
|
+
|
|
110
|
+
From a Fastify route or server plugin:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { postUiCommand } from '@boring/workspace/server'
|
|
114
|
+
|
|
115
|
+
await postUiCommand(workspaceId, {
|
|
116
|
+
kind: 'openSurface',
|
|
117
|
+
params: { kind: 'my-plugin.open', target: 'item-123' },
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Frontend event bus
|
|
124
|
+
|
|
125
|
+
Subscribe to workspace events on the frontend:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { events, workspaceEvents } from '@boring/workspace'
|
|
129
|
+
|
|
130
|
+
events.on(workspaceEvents.panelOpened, (panel) => {
|
|
131
|
+
console.log('panel opened', panel.id)
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
See [plugins.md](./plugins.md) to register a surface resolver in your plugin.
|
package/docs/panels.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
> boring-ui can create panel components. Ask it to build one for your domain.
|
|
2
|
+
|
|
3
|
+
# Panels
|
|
4
|
+
|
|
5
|
+
Panels are React components rendered inside the workspace dockview layout. They receive `PaneProps<T>` and can be opened programmatically by the agent or user.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Defining a panel](#defining-a-panel)
|
|
10
|
+
- [Panel component API](#panel-component-api)
|
|
11
|
+
- [Placement](#placement)
|
|
12
|
+
- [Auto-lazy loading](#auto-lazy-loading)
|
|
13
|
+
- [Opening panels](#opening-panels)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Defining a panel
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { definePanel } from '@boring/workspace'
|
|
21
|
+
|
|
22
|
+
export const myPanel = definePanel({
|
|
23
|
+
id: 'my-panel',
|
|
24
|
+
title: 'My Panel',
|
|
25
|
+
placement: 'center',
|
|
26
|
+
// Zero-arg factory → auto-detected as lazy (code-split)
|
|
27
|
+
component: () => import('./MyPane').then(m => ({ default: m.MyPane })),
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Panel component API
|
|
34
|
+
|
|
35
|
+
Panel components receive `PaneProps<T>`:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import type { PaneProps } from '@boring/workspace'
|
|
39
|
+
|
|
40
|
+
interface Params { id?: string; query?: string }
|
|
41
|
+
|
|
42
|
+
export function MyPane({ params, api, containerApi }: PaneProps<Params>) {
|
|
43
|
+
// params — data passed when the panel is opened
|
|
44
|
+
// api — DockviewPanelApi (close, setTitle, onDidParametersChange, …)
|
|
45
|
+
// containerApi — DockviewApi (addPanel, fromJSON, …)
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
React to parameter changes (agent re-opens with new params):
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const disposable = api.onDidParametersChange(() => {
|
|
54
|
+
// params updated
|
|
55
|
+
})
|
|
56
|
+
return () => disposable.dispose()
|
|
57
|
+
}, [api])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Placement
|
|
63
|
+
|
|
64
|
+
| value | where |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `center` | main editor area |
|
|
67
|
+
| `right` | right sidebar |
|
|
68
|
+
| `bottom` | bottom panel |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Auto-lazy loading
|
|
73
|
+
|
|
74
|
+
**Do not set `lazy: true`.** The registry auto-detects it:
|
|
75
|
+
|
|
76
|
+
- Zero-arg function `() => import(...)` → lazy (code-split, loaded on first open)
|
|
77
|
+
- Component `(props) => JSX` → eager (loaded at startup)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Opening panels
|
|
82
|
+
|
|
83
|
+
From the agent via exec_ui (see [bridge.md](./bridge.md)):
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"kind": "openSurface",
|
|
88
|
+
"params": { "kind": "my-plugin.open", "target": "item-123" }
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
From React code directly:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
containerApi.addPanel({
|
|
96
|
+
id: 'my-panel',
|
|
97
|
+
component: 'my-panel',
|
|
98
|
+
params: { id: 'item-123' },
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See [plugins.md](./plugins.md) for the full plugin API.
|