@pstdio/workbench 0.5.0 → 0.7.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/README.md +66 -72
- package/dist/composition-resolver-types-CWykPyEK.js +241 -0
- package/dist/composition-resolver-types-CWykPyEK.js.map +1 -0
- package/dist/extensions.d.ts +380 -29
- package/dist/extensions.js +1366 -883
- package/dist/extensions.js.map +1 -1
- package/dist/file-renderer-view-B6_iR4EN.js +496 -0
- package/dist/file-renderer-view-B6_iR4EN.js.map +1 -0
- package/dist/file-section-navigation-CfYqVObc.js +30 -0
- package/dist/file-section-navigation-CfYqVObc.js.map +1 -0
- package/dist/index.d.ts +305 -17
- package/dist/index.js +2343 -2083
- package/dist/index.js.map +1 -1
- package/dist/layout-types-DMoZz38-.js.map +1 -1
- package/dist/react.d.ts +190 -16
- package/dist/react.js +2473 -2374
- package/dist/react.js.map +1 -1
- package/dist/storage.js +132 -155
- package/dist/storage.js.map +1 -1
- package/dist/testing.d.ts +177 -14
- package/dist/webview-runtime.js +1 -1
- package/dist/webview-runtime.js.map +1 -1
- package/dist/workbench-built-ins-BncLJfQA.js +1175 -0
- package/dist/workbench-built-ins-BncLJfQA.js.map +1 -0
- package/dist/workbench-menu-paths-Bb0ICxeG.js +20 -0
- package/dist/workbench-menu-paths-Bb0ICxeG.js.map +1 -0
- package/package.json +4 -4
- package/dist/context-key-service-B2K9LAec.js +0 -1034
- package/dist/context-key-service-B2K9LAec.js.map +0 -1
- package/dist/file-renderer-view-DrrhYnb_.js +0 -171
- package/dist/file-renderer-view-DrrhYnb_.js.map +0 -1
- package/dist/workbench-menu-paths-B_QJzcNU.js +0 -12
- package/dist/workbench-menu-paths-B_QJzcNU.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# pstdio-workbench
|
|
2
2
|
|
|
3
|
-
`pstdio-workbench` is the workbench composition layer for Prompt Studio. It provides a typed core for registering contributions and a React workbench for rendering those contributions.
|
|
3
|
+
`pstdio-workbench` is the workbench composition layer for Prompt Studio. It provides a typed core for registering contributions and a React workbench for rendering those contributions. See the [Workbench reference](../../.pstdio/docs/references/workbench/index.md) for the full public API.
|
|
4
4
|
|
|
5
|
-
## Key
|
|
5
|
+
## Key concepts
|
|
6
6
|
|
|
7
7
|
- **Core**: the headless workbench model created by `createWorkbenchCore()`. It owns registries, controllers, and shared state.
|
|
8
8
|
- **Workbench**: the React shell rendered by `Workbench`. It reads the core and turns registered contributions into visible UI.
|
|
@@ -17,29 +17,40 @@
|
|
|
17
17
|
|
|
18
18
|
Contributions are the workbench extension points. A module contributes capabilities into the core; the React workbench renders the current state from those contributions. Contributions are grouped by role: layout, views, resources, navigation, and supporting plumbing. Ownership metadata is documented in [Contribution Ownership](https://github.com/pufflyai/prompt-studio/blob/main/.pstdio/docs/references/workbench/contribution-ownership.md).
|
|
19
19
|
|
|
20
|
-
## Public
|
|
20
|
+
## Public entry points
|
|
21
21
|
|
|
22
22
|
- `@pstdio/workbench` exports the headless workbench core, registries, controllers, and contribution types.
|
|
23
23
|
- `@pstdio/workbench/react` exports the React shell, view hosts, shared hooks, and `WorkbenchModuleHost` for syncing a dynamic module list into a core.
|
|
24
24
|
- `@pstdio/workbench/storage` exports local-storage-backed layout and panel persistence adapters for hosts that want browser persistence with a namespace and scope.
|
|
25
|
+
- `@pstdio/workbench/extensions` maps checked extension metadata into workbench contributions.
|
|
26
|
+
- `@pstdio/workbench/testing` exports test fixtures and helpers.
|
|
27
|
+
- `@pstdio/workbench/webview-runtime` exports the runtime used inside extension webviews.
|
|
25
28
|
|
|
26
|
-
##
|
|
29
|
+
## Layout contributions
|
|
27
30
|
|
|
28
|
-
Layout contributions
|
|
31
|
+
Layout contributions connect regions to views. They declare where a renderer appears, what configuration it receives, and how its placements behave. They do not render UI.
|
|
29
32
|
|
|
30
33
|
### Panels
|
|
31
34
|
|
|
32
|
-
Pin a renderer to a region as a panel, editor, dashboard, inspector, status item, or overlay. Panels declare a `region`, `title`, `rendererId`, optional `resourceKinds`, optional sizing and collapsibility, and renderer-owned `config`. They do not contain render code
|
|
35
|
+
Pin a renderer to a region as a panel, editor, dashboard, inspector, status item, or overlay. Panels declare a `region`, `title`, `rendererId`, optional `resourceKinds`, optional sizing and collapsibility, and renderer-owned `config`. They do not contain render code. The shell looks up the `rendererId` in the renderer registry.
|
|
33
36
|
|
|
34
37
|
- Register with `layout.registerPanel()`
|
|
35
38
|
- Set `singleton: true` for one placement total, such as a tree, sidenav, or status view.
|
|
36
39
|
- Non-singleton Panels default to `reuse: "resource"`: one placement per resource URI, with no-resource opens reusing the Panel placement.
|
|
37
40
|
- Set `reuse: "none"` for scratch, untitled, or transient views where every open should create a new placement.
|
|
38
41
|
|
|
42
|
+
`closable` and `role` belong to an open placement, not to panel registration. Extension composition derives both from `show` and the active mode recipe. Hosts that open a panel directly can set them in `layout.openPanel(...)`.
|
|
43
|
+
|
|
44
|
+
### Composition
|
|
45
|
+
|
|
46
|
+
`workbench.composition.panelsFor(region)` returns the open placements, closed optional panels that can be added, and contribution ids that can be closed for `main`, `secondary`, or `side`. Use it to build panel controls that follow the active mode and resource. `sidenav` participates in extension placement but is not a tab-hosting panel region, so it is not accepted by this query.
|
|
47
|
+
|
|
48
|
+
See the `pstdio-workbench/API` Storybook section for live composition queries and an extension panel that moves between `main` and `sidenav`.
|
|
49
|
+
|
|
39
50
|
#### Examples
|
|
40
51
|
|
|
41
|
-
- [`hello-world`](src/examples/hello-world/module.tsx)
|
|
42
|
-
- [`foundation`](src/examples/foundation/module.tsx)
|
|
52
|
+
- [`hello-world`](src/examples/hello-world/module.tsx) has a minimal Panel pinned to `main`.
|
|
53
|
+
- [`foundation`](src/examples/foundation/module.tsx) places one shared renderer in five regions.
|
|
43
54
|
|
|
44
55
|
### Placeholders
|
|
45
56
|
|
|
@@ -49,7 +60,7 @@ Empty state for regions after every Panel in that region closes. Placeholders re
|
|
|
49
60
|
|
|
50
61
|
#### Examples
|
|
51
62
|
|
|
52
|
-
- [`hello-world`](src/examples/hello-world/module.tsx)
|
|
63
|
+
- [`hello-world`](src/examples/hello-world/module.tsx) shows a placeholder in `main` when the welcome Panel is closed.
|
|
53
64
|
|
|
54
65
|
### Menu items
|
|
55
66
|
|
|
@@ -59,14 +70,12 @@ Surface commands in the command palette, region headers, tree context menus, or
|
|
|
59
70
|
|
|
60
71
|
#### Examples
|
|
61
72
|
|
|
62
|
-
- [`hello-world`](src/examples/hello-world/module.tsx)
|
|
63
|
-
- [`foundation`](src/examples/foundation/module.tsx)
|
|
64
|
-
|
|
65
|
-
---
|
|
73
|
+
- [`hello-world`](src/examples/hello-world/module.tsx) adds a trailing menu item to the main header.
|
|
74
|
+
- [`foundation`](src/examples/foundation/module.tsx) adds menu items to header paths.
|
|
66
75
|
|
|
67
|
-
##
|
|
76
|
+
## View contributions
|
|
68
77
|
|
|
69
|
-
View contributions
|
|
78
|
+
View contributions render placements, tree nodes, and collections as interactive UI.
|
|
70
79
|
|
|
71
80
|
### Renderers
|
|
72
81
|
|
|
@@ -77,23 +86,23 @@ The React or bridge implementation for a Panel or placeholder. Renderers turn a
|
|
|
77
86
|
|
|
78
87
|
#### Examples
|
|
79
88
|
|
|
80
|
-
- [`hello-world`](src/examples/hello-world/module.tsx)
|
|
81
|
-
- [`foundation`](src/examples/foundation/module.tsx)
|
|
82
|
-
- [`renderer-types`](src/examples/renderer-types/module.tsx)
|
|
83
|
-
- [`keep-alive`](src/examples/keep-alive/module.tsx)
|
|
89
|
+
- [`hello-world`](src/examples/hello-world/module.tsx) uses one renderer per Panel and one placeholder renderer.
|
|
90
|
+
- [`foundation`](src/examples/foundation/module.tsx) reuses one renderer across five Panels through `config`.
|
|
91
|
+
- [`renderer-types`](src/examples/renderer-types/module.tsx) registers React and bridge renderers together.
|
|
92
|
+
- [`keep-alive`](src/examples/keep-alive/module.tsx) shares one keep-alive renderer between attached and floating Side Panel presentations.
|
|
84
93
|
|
|
85
|
-
### Tree
|
|
94
|
+
### Tree renderers
|
|
86
95
|
|
|
87
|
-
A tree
|
|
96
|
+
A tree renderer handles side-panel navigation, outlines, resource lists, and contextual hierarchies. It exposes body sections through `getBody`, optional footer nodes through `getFooter`, and lazy children through `getChildren`. Nodes can carry resources, descriptions, inline actions, context menus, and `contextValue`. A tree renderer registers a Panel renderer with the same id. Use `layout.registerPanel({ rendererId: <tree id> })` and `layout.openPanel(<tree id>)` to place it in a tree-hosting region.
|
|
88
97
|
|
|
89
98
|
- Register with `renderers.registerTreeRenderer()`
|
|
90
99
|
|
|
91
100
|
#### Examples
|
|
92
101
|
|
|
93
|
-
- [`dashboard`](src/examples/dashboard/modules/shell/project-nav.ts)
|
|
94
|
-
- [`dynamic-modules`](src/examples/dynamic-modules/modules/explorer-module.tsx)
|
|
102
|
+
- [`dashboard`](src/examples/dashboard/modules/shell/project-nav.ts) builds the primary tree with resource-backed nodes, footer entries, and context menus.
|
|
103
|
+
- [`dynamic-modules`](src/examples/dynamic-modules/modules/explorer-module.tsx) adds and removes a tree module at runtime.
|
|
95
104
|
|
|
96
|
-
### Kanban
|
|
105
|
+
### Kanban renderers
|
|
97
106
|
|
|
98
107
|
A Notion/Linear-style data workspace registered as a renderer. Kanban renderers contribute the schema (tag definitions, grouping/ordering/display options, filter categories), the rows via `executeQuery(state)` (which receives current settings + filters so backends can push filter/sort/pagination down), and row-mutation callbacks. Like tree renderers, a kanban renderer auto-registers a Panel renderer with the same id, so the workspace is placed via `layout.registerPanel({ rendererId: <kanban renderer id> })` and opened with `layout.openPanel(...)`.
|
|
99
108
|
|
|
@@ -101,10 +110,10 @@ A Notion/Linear-style data workspace registered as a renderer. Kanban renderers
|
|
|
101
110
|
|
|
102
111
|
#### Examples
|
|
103
112
|
|
|
104
|
-
- [`kanban-renderer`](src/examples/kanban-renderer/module.tsx)
|
|
105
|
-
- [`dashboard`](src/examples/dashboard/modules/tickets/collections/ticket-data.ts)
|
|
113
|
+
- [`kanban-renderer`](src/examples/kanban-renderer/module.tsx) shows a schema, mock rows, and renderer-owned controls.
|
|
114
|
+
- [`dashboard`](src/examples/dashboard/modules/tickets/collections/ticket-data.ts) integrates the ticket workspace into the dashboard shell.
|
|
106
115
|
|
|
107
|
-
### Data
|
|
116
|
+
### Data table renderers
|
|
108
117
|
|
|
109
118
|
A dense, query-driven table backed by `@pstdio/ui/data-table`. Data table renderers keep row ids and resources outside visible values, support declarative column labels, descriptions, icons, statistics and cell renderers, and use the table's local filtering, sorting, column controls, and pagination. They auto-register a Panel renderer with the same id and can be placed in any workbench region.
|
|
110
119
|
|
|
@@ -114,7 +123,7 @@ A dense, query-driven table backed by `@pstdio/ui/data-table`. Data table render
|
|
|
114
123
|
|
|
115
124
|
#### Examples
|
|
116
125
|
|
|
117
|
-
- [`data-table-renderer`](src/examples/data-table-renderer/module.tsx)
|
|
126
|
+
- [`data-table-renderer`](src/examples/data-table-renderer/module.tsx) builds a service-health table with statistics, color scales, formatted JSON, navigation, and row actions.
|
|
118
127
|
|
|
119
128
|
### Breadcrumb (TODO: make it a renderer as well)
|
|
120
129
|
|
|
@@ -126,10 +135,8 @@ A dense, query-driven table backed by `@pstdio/ui/data-table`. Data table render
|
|
|
126
135
|
|
|
127
136
|
#### Examples
|
|
128
137
|
|
|
129
|
-
- [`dashboard`](src/examples/dashboard/
|
|
130
|
-
- [`onboarding`](src/examples/onboarding/breadcrumb-module.tsx)
|
|
131
|
-
|
|
132
|
-
---
|
|
138
|
+
- [`dashboard`](src/examples/dashboard/shared/resource-sync.ts) updates the breadcrumb trail when the open resource changes.
|
|
139
|
+
- [`onboarding`](src/examples/onboarding/breadcrumb-module.tsx) keeps a three-level trail in sync with page navigation.
|
|
133
140
|
|
|
134
141
|
A typical surface combines both layers: a **renderer** supplies the UI, a **Panel** places it in a region with optional `config`, and `layout.openPanel()` creates the placement the user actually sees. Pick the narrowest contribution that matches what you are adding:
|
|
135
142
|
|
|
@@ -138,7 +145,7 @@ A typical surface combines both layers: a **renderer** supplies the UI, a **Pane
|
|
|
138
145
|
- Add a **tree renderer** for navigable hierarchy and resource discovery, and place it with a Panel.
|
|
139
146
|
- Add a **placeholder** for region-level empty state, not for normal content.
|
|
140
147
|
|
|
141
|
-
##
|
|
148
|
+
## Resource contributions
|
|
142
149
|
|
|
143
150
|
Resource contributions define typed objects the workbench can open, route, or resolve from product data. They keep trees, navigation, and history speaking the same resource language.
|
|
144
151
|
|
|
@@ -151,8 +158,8 @@ Declare typed things the workbench can open. Resource refs carry `kind`, `uri`,
|
|
|
151
158
|
|
|
152
159
|
#### Examples
|
|
153
160
|
|
|
154
|
-
- [`renderer-types`](src/examples/renderer-types/module.tsx)
|
|
155
|
-
- [`navigation`](src/examples/navigation/module.tsx)
|
|
161
|
+
- [`renderer-types`](src/examples/renderer-types/module.tsx) defines a module with one resource kind.
|
|
162
|
+
- [`navigation`](src/examples/navigation/module.tsx) uses several resource kinds in routing.
|
|
156
163
|
|
|
157
164
|
### Resource presenters
|
|
158
165
|
|
|
@@ -162,8 +169,8 @@ Map a Resource to the Panel instance that presents it. Presenters declare `canOp
|
|
|
162
169
|
|
|
163
170
|
#### Examples
|
|
164
171
|
|
|
165
|
-
- [`renderer-types`](src/examples/renderer-types/module.tsx)
|
|
166
|
-
- [`navigation`](src/examples/navigation/module.tsx)
|
|
172
|
+
- [`renderer-types`](src/examples/renderer-types/module.tsx) routes one resource kind to one of two Panels.
|
|
173
|
+
- [`navigation`](src/examples/navigation/module.tsx) uses presenters for parsed navigation targets.
|
|
167
174
|
|
|
168
175
|
### Resource providers
|
|
169
176
|
|
|
@@ -173,9 +180,9 @@ Look up resources by kind, uri, or search input. Providers let features resolve
|
|
|
173
180
|
|
|
174
181
|
#### Examples
|
|
175
182
|
|
|
176
|
-
- [`dashboard`](src/examples/dashboard/modules/
|
|
183
|
+
- [`dashboard`](src/examples/dashboard/modules/tickets/module.ts) provides ticket resources to the ticket board.
|
|
177
184
|
|
|
178
|
-
##
|
|
185
|
+
## Navigation contributions
|
|
179
186
|
|
|
180
187
|
Navigation contributions turn incoming locations and resolved workbench targets into concrete workbench actions. They keep URL parsing, command dispatch, view opening, and resource routing centralized.
|
|
181
188
|
|
|
@@ -187,7 +194,7 @@ Turn ingress locations into workbench targets. Parsers convert URLs or location
|
|
|
187
194
|
|
|
188
195
|
#### Examples
|
|
189
196
|
|
|
190
|
-
- [`navigation`](src/examples/navigation/module.tsx)
|
|
197
|
+
- [`navigation`](src/examples/navigation/module.tsx) parses URL-like locations into typed targets.
|
|
191
198
|
|
|
192
199
|
### Navigation navigators
|
|
193
200
|
|
|
@@ -195,7 +202,7 @@ Custom dispatch behavior for navigation targets. Navigators let modules extend h
|
|
|
195
202
|
|
|
196
203
|
- Register with `navigation.registerNavigator()`
|
|
197
204
|
|
|
198
|
-
## Other
|
|
205
|
+
## Other contributions
|
|
199
206
|
|
|
200
207
|
Supporting contributions make view, layout, resource, and navigation contributions useful. They define actions, persistence, scoping, and preferences without directly owning a workbench region.
|
|
201
208
|
|
|
@@ -207,8 +214,8 @@ Any executable workbench action. Commands are the primitive behind menus, keybin
|
|
|
207
214
|
|
|
208
215
|
#### Examples
|
|
209
216
|
|
|
210
|
-
- [`hello-world`](src/examples/hello-world/module.tsx)
|
|
211
|
-
- [`renderer-types`](src/examples/renderer-types/module.tsx)
|
|
217
|
+
- [`hello-world`](src/examples/hello-world/module.tsx) adds a command that opens a Panel.
|
|
218
|
+
- [`renderer-types`](src/examples/renderer-types/module.tsx) opens different placements with commands.
|
|
212
219
|
|
|
213
220
|
### Keybindings
|
|
214
221
|
|
|
@@ -218,8 +225,8 @@ Keyboard access to commands. Keybindings reference command ids and can include a
|
|
|
218
225
|
|
|
219
226
|
#### Examples
|
|
220
227
|
|
|
221
|
-
- [`foundation`](src/examples/foundation/module.tsx)
|
|
222
|
-
- [`random`](src/examples/random/modules/random-workbench.tsx)
|
|
228
|
+
- [`foundation`](src/examples/foundation/module.tsx) gates a keybinding with a context key.
|
|
229
|
+
- [`random`](src/examples/random/modules/random-workbench.tsx) pairs keybindings with mode-scoped commands.
|
|
223
230
|
|
|
224
231
|
### Context keys
|
|
225
232
|
|
|
@@ -229,8 +236,8 @@ Conditional command, menu, and keybinding behavior. Module contexts create scope
|
|
|
229
236
|
|
|
230
237
|
#### Examples
|
|
231
238
|
|
|
232
|
-
- [`foundation`](src/examples/foundation/module.tsx)
|
|
233
|
-
- [`keep-alive`](src/examples/keep-alive/module.tsx)
|
|
239
|
+
- [`foundation`](src/examples/foundation/module.tsx) sets `foundation.host` to gate menus and keybindings.
|
|
240
|
+
- [`keep-alive`](src/examples/keep-alive/module.tsx) uses a context key to control kept-alive subtree visibility.
|
|
234
241
|
|
|
235
242
|
### Modes
|
|
236
243
|
|
|
@@ -240,8 +247,8 @@ Temporary bundles of contributions for workspace modes such as project, settings
|
|
|
240
247
|
|
|
241
248
|
#### Examples
|
|
242
249
|
|
|
243
|
-
- [`workbench-modes`](src/examples/workbench-modes/modules/project-mode.tsx)
|
|
244
|
-
- [`dashboard`](src/examples/dashboard/modules/
|
|
250
|
+
- [`workbench-modes`](src/examples/workbench-modes/modules/project-mode.tsx) switches between mode-scoped bundles.
|
|
251
|
+
- [`dashboard`](src/examples/dashboard/modules/shell/module.tsx) defines the project mode and its navigation surfaces.
|
|
245
252
|
|
|
246
253
|
### Preference schemas
|
|
247
254
|
|
|
@@ -251,7 +258,7 @@ Typed settings contributed by a module or runtime extension. Schemas define the
|
|
|
251
258
|
|
|
252
259
|
#### Examples
|
|
253
260
|
|
|
254
|
-
- [`preferences`](src/examples/preferences/module.tsx)
|
|
261
|
+
- [`preferences`](src/examples/preferences/module.tsx) registers user and workspace-scoped preference values.
|
|
255
262
|
|
|
256
263
|
### Notifications
|
|
257
264
|
|
|
@@ -261,8 +268,8 @@ Toast-style messages from modules or extensions. Notifications can include comma
|
|
|
261
268
|
|
|
262
269
|
#### Examples
|
|
263
270
|
|
|
264
|
-
- [`dynamic-modules`](src/examples/dynamic-modules/modules/diagnostics-module.tsx)
|
|
265
|
-
- [`navigation`](src/examples/navigation/module.tsx)
|
|
271
|
+
- [`dynamic-modules`](src/examples/dynamic-modules/modules/diagnostics-module.tsx) emits toasts from a diagnostics module.
|
|
272
|
+
- [`navigation`](src/examples/navigation/module.tsx) sends a notification from a navigation flow.
|
|
266
273
|
|
|
267
274
|
### Themes
|
|
268
275
|
|
|
@@ -270,39 +277,26 @@ The React workbench owns the shared `@pstdio/ui` theme preference system through
|
|
|
270
277
|
|
|
271
278
|
#### Examples
|
|
272
279
|
|
|
273
|
-
- [`extension-themes`](src/examples/extension-themes/module.tsx)
|
|
280
|
+
- [`extension-themes`](src/examples/extension-themes/module.tsx) registers extensions as workbench modules that add and remove VS Code-compatible color themes at runtime.
|
|
274
281
|
|
|
275
282
|
### Terminal
|
|
276
283
|
|
|
277
|
-
Terminals are
|
|
284
|
+
Terminals are owned by the host. Extensions do not compose their chrome. The `workbench.terminal` controller owns the session registry. Hosts supply a session opener through `workbench.terminal.setSessionOpener(...)`. Production uses a real PTY transport. Stories and the extension testbench use `createScriptedTerminalBridge` from `@pstdio/ui/terminal`. `createWorkbenchTerminalModule()` registers the terminal Panel in the `secondary` region and the `workbench.terminal.open` command. The Panel renders the shared `Terminal` component from `@pstdio/ui/terminal`. Closing the Panel kills its session. Disposing the controller kills every live session.
|
|
278
285
|
|
|
279
|
-
Extension webviews never receive PTY handles. A webview
|
|
286
|
+
Extension webviews never receive PTY handles. A webview with the `terminal.session` capability uses serializable `open`, `write`, `resize`, `kill`, and `subscribe` operations. The host sends output and exit events over the bridge event channel. `createTerminalSessionBridge(host)` from `@pstdio/sdk/extensions` turns that protocol into a bridge accepted by the `Terminal` component.
|
|
280
287
|
|
|
281
288
|
#### Examples
|
|
282
289
|
|
|
283
|
-
- [`workbench-modes`](src/examples/workbench-modes/module.tsx)
|
|
284
|
-
- [`workbench.stories`](src/examples/workbench.stories.tsx)
|
|
285
|
-
|
|
286
|
-
---
|
|
290
|
+
- [`workbench-modes`](src/examples/workbench-modes/module.tsx) opens the host-owned terminal Panel with a scripted backend.
|
|
291
|
+
- [`workbench.stories`](src/examples/workbench.stories.tsx) drives the `HostTerminal` story with `createScriptedTerminalBridge`.
|
|
287
292
|
|
|
288
293
|
Register related contributions inside one **workbench module** so they share ownership and disposal. For example, a ticket collection module usually contributes a resource kind, resource presenter, Panel, renderer, tree renderer, commands, and menu items together.
|
|
289
294
|
|
|
290
|
-
## Core
|
|
291
|
-
|
|
292
|
-
The core is UI-independent. It is the source of truth that modules write to and the React workbench reads from.
|
|
293
|
-
A host normally creates one core, registers modules into it, and renders `<Workbench workbench={workbench} />`.
|
|
294
|
-
|
|
295
295
|
## Nomenclature
|
|
296
296
|
|
|
297
|
-
- **Workbench core**: the headless object created by `createWorkbenchCore()`. It owns the registries, controllers, and shared workbench state.
|
|
298
|
-
- **Registry**: a typed collection of contributions. The workbench has registries for commands, keybindings, resources, layout (Panels, placeholders, menu items), renderers (Panel renderers and tree renderers), modes, navigation, notifications, and preferences.
|
|
299
|
-
- **Controller**: a stateful slice of workbench UX exposed alongside the registries — breadcrumbs, command palette open/close state, focus, history, side-panel open/close state, and session-panel mode.
|
|
300
297
|
- **Contribution**: a declarative unit added to a registry, such as a command, menu item, resource kind, Panel, renderer, tree renderer, mode, or KanbanView.
|
|
301
298
|
- **Workbench module**: contribution owner registered with `workbench.registerModule(module)` and removed with `workbench.unregisterModule(moduleId)`. Module disposables are tracked and disposed together. See [Contribution Ownership](https://github.com/pufflyai/prompt-studio/blob/main/.pstdio/docs/references/workbench/contribution-ownership.md).
|
|
302
299
|
- **Runtime extension**: extension metadata from `pstdio-extensions` that a host maps into workbench modules at the trust boundary.
|
|
303
|
-
- **Workbench**: the React frame rendered by `Workbench`. It arranges the workbench regions, command palette, panels, and session surface from the workbench core only.
|
|
304
|
-
- **Region**: a named layout target. See the Regions Overview table below.
|
|
305
|
-
- **Panel contribution**: a registered view definition in the layout registry. Panels declare a region, a `rendererId`, and optional renderer-owned `config`.
|
|
306
300
|
- **Panel placement**: an opened instance of a Panel contribution in a region. Placements track active Panel, resource URI, title, pinned/closable flags, and placement ownership.
|
|
307
301
|
- **Tree renderer contribution**: a tree-shaped renderer registered under `renderers`. Provides `getBody` (sectioned body), optional `getFooter` (flat footer node list), and `getChildren` (lazy children). Auto-registers a Panel renderer with the same id so Panels place trees through `layout.registerPanel`.
|
|
308
302
|
- **Kanban renderer contribution**: a data-workspace renderer registered under `renderers`. Provides a schema, `executeQuery(state)` rows, and row-mutation callbacks. Auto-registers a Panel renderer with the same id so Panels place the workspace through `layout.registerPanel`. The presentational layer is `<KanbanRenderer>` from `@pstdio/ui`.
|
|
@@ -321,7 +315,7 @@ A host normally creates one core, registers modules into it, and renders `<Workb
|
|
|
321
315
|
- **Notification**: a transient workbench message emitted by workbench modules or extensions. Notifications can include command-backed actions and are rendered as workbench toast chrome.
|
|
322
316
|
- **Side Panel**: the project-owned carry surface controlled by `workbench.sidePanel`. It can be `attached`, `floating`, or `closed` while preserving one live host and its resource binding.
|
|
323
317
|
|
|
324
|
-
## Regions
|
|
318
|
+
## Regions overview
|
|
325
319
|
|
|
326
320
|
Workbench regions are named layout targets used by Panel contributions. They describe where a Panel belongs in the workbench; the workbench decides the exact chrome, tabs, resize handles, and visibility behavior.
|
|
327
321
|
|
|
@@ -348,7 +342,7 @@ Panels with tabs are paired with a `<panel>-header` region that the workbench re
|
|
|
348
342
|
|
|
349
343
|
The command palette, toast notifications, and resize handles are workbench chrome, not workbench regions. Use the `RegionMap` Storybook story to see the current region placement rendered through the real `Workbench`.
|
|
350
344
|
|
|
351
|
-
## Header
|
|
345
|
+
## Header actions
|
|
352
346
|
|
|
353
347
|
Each region header renders command-backed actions from two menu paths derived from `headerLeadingMenuPath(region)` and `headerTrailingMenuPath(region)`. Nav Chrome reuses these paths under `workbenchTopHeaderLeadingMenuPath` and `workbenchTopHeaderTrailingMenuPath`. It keeps history, the breadcrumb trail, the trailing breadcrumb-action slot, and Sidenav/Secondary/Side visibility controls mounted in that order. Workbench modules can register commands and add menu actions to those paths without moving navigation into a Panel Header.
|
|
354
348
|
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
const v = (e) => [.../* @__PURE__ */ new Set([e.region, ...e.allowedRegions ?? []])], _ = (e) => e.show ? Array.isArray(e.show) ? e.show : [e.show] : [], q = (e, o, s, n) => {
|
|
2
|
+
if (!o || s.resourceKind !== o.id) return !1;
|
|
3
|
+
const l = o.slots[s.slot];
|
|
4
|
+
return l ? s.extensionId !== o.extensionId && (!l.external || s.slot === "primary") ? (n.push({
|
|
5
|
+
code: "extension_resource_slot_closed",
|
|
6
|
+
message: `Slot "${s.slot}" is closed to external panels`,
|
|
7
|
+
panelId: s.panel,
|
|
8
|
+
slot: s.slot
|
|
9
|
+
}), !1) : e.composition.panels.some((r) => r.id === s.panel) ? !0 : (n.push({
|
|
10
|
+
code: "extension_panel_missing",
|
|
11
|
+
message: `Resource panel "${s.id}" names unknown panel "${s.panel}"`,
|
|
12
|
+
panelId: s.panel,
|
|
13
|
+
slot: s.slot
|
|
14
|
+
}), !1) : (n.push({
|
|
15
|
+
code: "extension_resource_slot_missing",
|
|
16
|
+
message: `Resource panel "${s.id}" names unknown slot "${s.slot}"`,
|
|
17
|
+
panelId: s.panel,
|
|
18
|
+
slot: s.slot
|
|
19
|
+
}), !1);
|
|
20
|
+
}, w = (e) => {
|
|
21
|
+
const o = e.kind?.slots[e.slotName];
|
|
22
|
+
if (!o)
|
|
23
|
+
return e.diagnostics.push({
|
|
24
|
+
code: "extension_resource_slot_missing",
|
|
25
|
+
message: `Mode "${e.input.mode.id}" places unknown slot "${e.slotName}"`,
|
|
26
|
+
slot: e.slotName
|
|
27
|
+
}), [];
|
|
28
|
+
const s = e.policy.required === !0;
|
|
29
|
+
return s && o.cardinality === "many" && e.diagnostics.push({
|
|
30
|
+
code: "extension_placement_required_invalid",
|
|
31
|
+
message: `Required slot "${e.slotName}" has cardinality many; name a specific panel instead`,
|
|
32
|
+
slot: e.slotName
|
|
33
|
+
}), e.validEdges.filter((n) => n.slot === e.slotName && !e.overriddenPanels.has(n.panel)).filter((n) => n.extensionId === e.kind?.extensionId).map((n) => ({
|
|
34
|
+
panelId: n.panel,
|
|
35
|
+
slot: e.slotName,
|
|
36
|
+
policy: e.policy,
|
|
37
|
+
required: s && o.cardinality === "one"
|
|
38
|
+
}));
|
|
39
|
+
}, P = (e, o) => {
|
|
40
|
+
const s = e.composition.resourceKinds.find((r) => r.id === e.context.resourceKind), n = _(o), l = n.find((r) => r.resourceKind === e.context.resourceKind);
|
|
41
|
+
if (l && o.extensionId === s?.extensionId) return l;
|
|
42
|
+
if (o.extensionId === e.mode.extensionId)
|
|
43
|
+
return n.find((r) => r.resourceKind === void 0);
|
|
44
|
+
}, b = (e, o, s) => {
|
|
45
|
+
const n = e.composition.resourceKinds.find((t) => t.id === e.context.resourceKind), l = [], r = e.composition.resourcePanels.filter((t) => q(e, n, t, s)), a = {
|
|
46
|
+
...o?.panels ?? {},
|
|
47
|
+
...e.mode.modePanels ?? {}
|
|
48
|
+
}, i = new Set(Object.keys(a));
|
|
49
|
+
for (const [t, c] of Object.entries(o?.slots ?? {}))
|
|
50
|
+
l.push(...w({
|
|
51
|
+
diagnostics: s,
|
|
52
|
+
kind: n,
|
|
53
|
+
input: e,
|
|
54
|
+
overriddenPanels: i,
|
|
55
|
+
policy: c,
|
|
56
|
+
slotName: t,
|
|
57
|
+
validEdges: r
|
|
58
|
+
}));
|
|
59
|
+
for (const t of e.composition.panels) {
|
|
60
|
+
const c = P(e, t);
|
|
61
|
+
if (!c) continue;
|
|
62
|
+
const p = a[t.id] ?? c;
|
|
63
|
+
l.push({
|
|
64
|
+
panelId: t.id,
|
|
65
|
+
policy: p,
|
|
66
|
+
base: c,
|
|
67
|
+
required: p.required ?? c.required ?? !1
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const u = new Set(l.map((t) => t.panelId));
|
|
71
|
+
for (const [t, c] of Object.entries(a)) {
|
|
72
|
+
if (u.has(t)) continue;
|
|
73
|
+
const p = r.find((d) => d.panel === t);
|
|
74
|
+
if (!e.composition.panels.find((d) => d.id === t) || o?.panels?.[t] && !p) {
|
|
75
|
+
s.push({
|
|
76
|
+
code: "extension_panel_missing",
|
|
77
|
+
message: `Mode "${e.mode.id}" places panel "${t}" that is not registered for the resource`,
|
|
78
|
+
panelId: t
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
l.push({
|
|
83
|
+
panelId: t,
|
|
84
|
+
slot: p?.slot,
|
|
85
|
+
policy: c,
|
|
86
|
+
required: c.required === !0
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
candidates: l,
|
|
91
|
+
validEdges: r
|
|
92
|
+
};
|
|
93
|
+
}, h = (e) => {
|
|
94
|
+
const o = e.base ? v(e.base) : v(e.policy);
|
|
95
|
+
return !e.base || !e.policy.allowedRegions ? o : v(e.policy).filter((s) => o.includes(s));
|
|
96
|
+
}, y = (e, o, s) => {
|
|
97
|
+
if (!e.composition.panels.find((r) => r.id === o.panelId)) return;
|
|
98
|
+
const l = h(o);
|
|
99
|
+
if (!l.includes(o.policy.region)) {
|
|
100
|
+
s.push({
|
|
101
|
+
code: "extension_panel_placement_unresolvable",
|
|
102
|
+
message: `Panel "${o.panelId}" cannot be placed in region "${o.policy.region}"`,
|
|
103
|
+
panelId: o.panelId
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
panelId: o.panelId,
|
|
109
|
+
region: o.policy.region,
|
|
110
|
+
slot: o.slot,
|
|
111
|
+
required: o.required,
|
|
112
|
+
defaultOpen: o.required || o.policy.defaultOpen !== !1,
|
|
113
|
+
pinned: o.policy.pinned,
|
|
114
|
+
allowedRegions: l,
|
|
115
|
+
origin: o.required ? "required" : "default"
|
|
116
|
+
};
|
|
117
|
+
}, g = (e, o, s) => {
|
|
118
|
+
const n = /* @__PURE__ */ new Map();
|
|
119
|
+
let l;
|
|
120
|
+
for (const r of o) {
|
|
121
|
+
const a = y(e, r, s);
|
|
122
|
+
if (a) {
|
|
123
|
+
n.has(a.panelId) || n.set(a.panelId, a);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
r.required && h(r).includes("main") && (l = {
|
|
127
|
+
panelId: r.panelId
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
requiredFallback: l,
|
|
132
|
+
resolvedByPanel: n
|
|
133
|
+
};
|
|
134
|
+
}, R = (e) => Object.entries(e.regions).filter((o) => !!o[1]), $ = (e) => {
|
|
135
|
+
for (const [o, s] of R(e.persisted)) {
|
|
136
|
+
for (const n of s.order) {
|
|
137
|
+
const l = e.resolvedByPanel.get(n);
|
|
138
|
+
l && e.place({
|
|
139
|
+
...l,
|
|
140
|
+
origin: "persisted"
|
|
141
|
+
}, l.allowedRegions.includes(o) ? o : l.region);
|
|
142
|
+
}
|
|
143
|
+
s.activePanelId && e.placed.has(s.activePanelId) && (e.activePanelIds[o] = s.activePanelId);
|
|
144
|
+
}
|
|
145
|
+
}, K = (e, o) => {
|
|
146
|
+
const s = [], n = {}, l = {}, r = /* @__PURE__ */ new Set(), a = (i, u) => {
|
|
147
|
+
r.has(i.panelId) || (r.add(i.panelId), s.push({
|
|
148
|
+
...i,
|
|
149
|
+
region: u
|
|
150
|
+
}), n[u] = [...n[u] ?? [], i.panelId]);
|
|
151
|
+
};
|
|
152
|
+
if (!e.persisted) {
|
|
153
|
+
for (const i of o.values()) i.defaultOpen && a(i, i.region);
|
|
154
|
+
return {
|
|
155
|
+
activePanelIds: l,
|
|
156
|
+
placed: r,
|
|
157
|
+
placements: s,
|
|
158
|
+
regionOrder: n
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
$({
|
|
162
|
+
activePanelIds: l,
|
|
163
|
+
persisted: e.persisted,
|
|
164
|
+
place: a,
|
|
165
|
+
placed: r,
|
|
166
|
+
resolvedByPanel: o
|
|
167
|
+
});
|
|
168
|
+
for (const i of o.values())
|
|
169
|
+
i.required && !r.has(i.panelId) && a(i, i.region);
|
|
170
|
+
return {
|
|
171
|
+
activePanelIds: l,
|
|
172
|
+
placed: r,
|
|
173
|
+
placements: s,
|
|
174
|
+
regionOrder: n
|
|
175
|
+
};
|
|
176
|
+
}, k = (e) => {
|
|
177
|
+
const o = [], s = e.context.resourceKind ? e.mode.resources?.[e.context.resourceKind] : void 0;
|
|
178
|
+
if (e.context.resourceKind && !s)
|
|
179
|
+
return o.push({
|
|
180
|
+
code: "extension_mode_resource_unsupported",
|
|
181
|
+
message: `Mode "${e.mode.id}" does not accept resource kind "${e.context.resourceKind}"`
|
|
182
|
+
}), {
|
|
183
|
+
placements: [],
|
|
184
|
+
regionOrder: {},
|
|
185
|
+
activePanelIds: {},
|
|
186
|
+
addablePanels: [],
|
|
187
|
+
diagnostics: o
|
|
188
|
+
};
|
|
189
|
+
const {
|
|
190
|
+
candidates: n,
|
|
191
|
+
validEdges: l
|
|
192
|
+
} = b(e, s, o), {
|
|
193
|
+
requiredFallback: r,
|
|
194
|
+
resolvedByPanel: a
|
|
195
|
+
} = g(e, n, o), {
|
|
196
|
+
activePanelIds: i,
|
|
197
|
+
placed: u,
|
|
198
|
+
placements: t,
|
|
199
|
+
regionOrder: c
|
|
200
|
+
} = K(e, a), p = Array.from(a.values()).filter((d) => !d.required && !u.has(d.panelId)).map(({
|
|
201
|
+
panelId: d,
|
|
202
|
+
region: m,
|
|
203
|
+
allowedRegions: f,
|
|
204
|
+
pinned: x
|
|
205
|
+
}) => ({
|
|
206
|
+
panelId: d,
|
|
207
|
+
region: m,
|
|
208
|
+
allowedRegions: f,
|
|
209
|
+
pinned: x
|
|
210
|
+
})), I = new Set(p.map((d) => d.panelId));
|
|
211
|
+
for (const d of l) {
|
|
212
|
+
if (u.has(d.panel) || I.has(d.panel)) continue;
|
|
213
|
+
const m = s?.panels?.[d.panel] ?? s?.slots?.[d.slot];
|
|
214
|
+
if (!m) continue;
|
|
215
|
+
const f = y(e, {
|
|
216
|
+
panelId: d.panel,
|
|
217
|
+
slot: d.slot,
|
|
218
|
+
policy: m,
|
|
219
|
+
required: !1
|
|
220
|
+
}, o);
|
|
221
|
+
f && (p.push({
|
|
222
|
+
panelId: f.panelId,
|
|
223
|
+
region: f.region,
|
|
224
|
+
allowedRegions: f.allowedRegions,
|
|
225
|
+
pinned: f.pinned
|
|
226
|
+
}), I.add(f.panelId));
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
placements: t,
|
|
230
|
+
regionOrder: c,
|
|
231
|
+
activePanelIds: i,
|
|
232
|
+
addablePanels: p,
|
|
233
|
+
diagnostics: o,
|
|
234
|
+
requiredFallback: r
|
|
235
|
+
};
|
|
236
|
+
}, O = ["sidenav", "main", "secondary", "side"];
|
|
237
|
+
export {
|
|
238
|
+
O as d,
|
|
239
|
+
k as r
|
|
240
|
+
};
|
|
241
|
+
//# sourceMappingURL=composition-resolver-types-CWykPyEK.js.map
|