@pstdio/workbench 0.8.0 → 0.9.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.
Files changed (44) hide show
  1. package/README.md +39 -317
  2. package/dist/extensions.d.ts +1283 -774
  3. package/dist/extensions.js +1454 -1924
  4. package/dist/extensions.js.map +1 -1
  5. package/dist/file-renderer-view-kfJRY3yR.js +515 -0
  6. package/dist/file-renderer-view-kfJRY3yR.js.map +1 -0
  7. package/dist/index.d.ts +589 -644
  8. package/dist/index.js +5011 -2644
  9. package/dist/index.js.map +1 -1
  10. package/dist/react.d.ts +540 -585
  11. package/dist/react.js +3583 -3424
  12. package/dist/react.js.map +1 -1
  13. package/dist/storage.d.ts +118 -157
  14. package/dist/storage.js +135 -149
  15. package/dist/storage.js.map +1 -1
  16. package/dist/webview-runtime.d.ts +2 -5
  17. package/dist/webview-runtime.js +3 -3
  18. package/dist/webview-runtime.js.map +1 -1
  19. package/dist/workbench-menu-paths-B_QJzcNU.js +12 -0
  20. package/dist/workbench-menu-paths-B_QJzcNU.js.map +1 -0
  21. package/dist/workbench-renderers-DmsrBGC7.js +960 -0
  22. package/dist/workbench-renderers-DmsrBGC7.js.map +1 -0
  23. package/dist/workspaces-DJwFj8uq.js +1872 -0
  24. package/dist/workspaces-DJwFj8uq.js.map +1 -0
  25. package/package.json +6 -41
  26. package/dist/composition-resolver-types-CWykPyEK.js +0 -241
  27. package/dist/composition-resolver-types-CWykPyEK.js.map +0 -1
  28. package/dist/file-renderer-view-B6_iR4EN.js +0 -496
  29. package/dist/file-renderer-view-B6_iR4EN.js.map +0 -1
  30. package/dist/file-section-navigation-CfYqVObc.js +0 -30
  31. package/dist/file-section-navigation-CfYqVObc.js.map +0 -1
  32. package/dist/layout-types-DMoZz38-.js +0 -100
  33. package/dist/layout-types-DMoZz38-.js.map +0 -1
  34. package/dist/surface-map-C1pOTUmF.js +0 -52
  35. package/dist/surface-map-C1pOTUmF.js.map +0 -1
  36. package/dist/testing.d.ts +0 -2443
  37. package/dist/testing.js +0 -58
  38. package/dist/testing.js.map +0 -1
  39. package/dist/workbench-built-ins-BCJ6-fIM.js +0 -1173
  40. package/dist/workbench-built-ins-BCJ6-fIM.js.map +0 -1
  41. package/dist/workbench-menu-paths-D-e7iscN.js +0 -43
  42. package/dist/workbench-menu-paths-D-e7iscN.js.map +0 -1
  43. package/dist/workflow-status-settings-DLiu9FZ0.js +0 -144
  44. package/dist/workflow-status-settings-DLiu9FZ0.js.map +0 -1
package/README.md CHANGED
@@ -1,349 +1,71 @@
1
- # pstdio-workbench
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. See the [Workbench reference](../../.pstdio/docs/references/workbench/index.md) for the full public API.
3
+ The headless composition model and React shell for Prompt Studio hosts. Extension authors use the [SDK cookbook](https://github.com/pufflyai/prompt-studio/blob/main/.pstdio/docs/extensions/cookbook.md). This guide covers host integration.
4
4
 
5
- ## Key concepts
5
+ ## Entry points
6
6
 
7
- - **Core**: the headless workbench model created by `createWorkbenchCore()`. It owns registries, controllers, and shared state.
8
- - **Workbench**: the React shell rendered by `Workbench`. It reads the core and turns registered contributions into visible UI.
9
- - **Module**: an owner for related contributions. Modules register against the core and their contributions are disposed together. See [Contribution Ownership](https://github.com/pufflyai/prompt-studio/blob/main/.pstdio/docs/references/workbench/contribution-ownership.md).
10
- - **Registry**: a typed collection of contributions, such as Panels, tree views, commands, menus, resources, and renderers.
11
- - **Contribution**: a declarative unit registered by a module. Contributions describe what exists; the workbench decides how to render or route them.
12
- - **Controller**: a stateful workbench service, such as breadcrumbs, panels, focus, history, command palette, or the session panel.
13
- - **Region**: a named layout target where Panels can be placed. The workbench owns the chrome around each region.
14
- - **Resource**: a typed reference to something the workbench can open or navigate to.
7
+ | Entry point | Purpose |
8
+ | --- | --- |
9
+ | `@pstdio/workbench` | Core, registries, controllers, and host contribution types |
10
+ | `@pstdio/workbench/react` | Shell, region components, and native renderers |
11
+ | `@pstdio/workbench/storage` | Browser persistence adapters |
12
+ | `@pstdio/workbench/extensions` | Checked extension metadata registration and host adapters |
13
+ | `@pstdio/workbench/webview-runtime` | Guest runtime for webview assets |
15
14
 
16
- ## Contributions
15
+ Install the declared React, React DOM, Chakra, and Emotion peers when using the React integration. Public declaration files include the private contracts they need. Package verification installs built entries outside the repository with full declaration checking.
17
16
 
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).
17
+ The root, storage, and webview-runtime entries load without React. The React and extensions entries require the declared UI peers. Core Kanban contracts parameterize presentation values without choosing a UI framework. React host authors use `ReactAttributeDescriptor`, `ReactBoardColumnConfig`, and `ReactKanbanRendererContribution` from the React entry for checked cell and icon callbacks.
19
18
 
20
- ## Public entry points
19
+ ## Ownership
21
20
 
22
- - `@pstdio/workbench` exports the headless workbench core, registries, controllers, and contribution types.
23
- - `@pstdio/workbench/react` exports the React shell, view hosts, shared hooks, and `WorkbenchModuleHost` for syncing a dynamic module list into a core.
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.
21
+ `pageLocations` owns durable navigation. A page transition resolves the page, resource, contextual parent, and mode before publishing location, composition, breadcrumbs, and browser history.
28
22
 
29
- ## Layout contributions
23
+ A page declares an optional resource constraint separately from Main presentation. `main.kind: "view"` presents its routed resource, with one or many instances. `main.kind: "panels"` presents peer Main panels and an empty view when none remain. `slots` holds page panels. The route continues to own context when a file or auxiliary inspector becomes active.
30
24
 
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.
25
+ Page slots and mode placements share a static-view or resource-binding item. Static views declare `presence`; bindings declare `kinds`, `view`, `cardinality`, and optional `add`. Main, Side, and Secondary have the same meaning for both owners.
32
26
 
33
- ### Panels
27
+ Modes supply shared placements, chrome, and region policy. Page navigation selects a mode. The host composes its default navigation whenever the active mode keeps that chrome. A replacement view or `false` overrides the default. Shared placements retain their identity across pages in the same mode.
34
28
 
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.
29
+ Register host React views through the core view registry. Register checked extension metadata through the public extension adapter. Dispose the returned registrations when the owner is removed so its views, placements, and mounted content are released.
36
30
 
37
- - Register with `layout.registerPanel()`
38
- - Set `singleton: true` for one placement total, such as a tree, sidenav, or status view.
39
- - Non-singleton Panels default to `reuse: "resource"`: one placement per resource URI, with no-resource opens reusing the Panel placement.
40
- - Set `reuse: "none"` for scratch, untitled, or transient views where every open should create a new placement.
31
+ ## Navigation and closing
41
32
 
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(...)`.
33
+ Page targets change location. Panel targets open a page slot or mode placement while preserving the route. Its owner must be active. A compound target may contain page and panel steps only; resolve dependent targets against proposed state, then commit once. A failed preparation publishes no location, history, layout, selection, breadcrumb, or placement changes.
43
34
 
44
- ### Composition
35
+ Commands and external links are standalone actions. A command that performs work and navigates must finish that work before requesting a target. The workbench cannot undo external effects.
45
36
 
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.
37
+ Use `core.closePlacement(identity)` for owned tab closing. Native tabs and the `placement.close` webview capability use this controller. The host supplies the webview's actual placement identity. Fixed placements reject closing. Closing the last routed resource view follows the page's declared parent. Closing an auxiliary panel preserves location.
47
38
 
48
- See the `pstdio-workbench/API` Storybook section for live composition queries and an extension panel that moves between `main` and `sidenav`.
39
+ ## Resource identity
49
40
 
50
- #### Examples
41
+ Use the SDK's `ResourceRef` throughout host and extension APIs. Its required fields are `type` and `id`; `label` is presentation. Preserve optional extension and project ownership when forwarding a reference.
51
42
 
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
+ Use `resourceKey(resource)` for identity comparisons and layout indexes. Labels and metadata do not change identity. URI conversion belongs in location and persistence adapters. The default page codec preserves existing type/id locations and includes ownership when supplied.
54
44
 
55
- ### Placeholders
45
+ `core.getPrimaryResource()` and resource providers receive the routed page resource. A workspace collection therefore retains workspace context while its active Main panel edits a file. Selection identifies the file separately.
56
46
 
57
- Empty state for regions after every Panel in that region closes. Placeholders render through a `rendererId`, can provide region sizing hints, and do not create tabs, placements, or persisted layout entries.
47
+ Tab labels resolve from explicit tab presentation, then resource label, then view title. The close action uses the same label.
58
48
 
59
- - Register with `layout.registerPlaceholder()`
49
+ ## Regions and mounting
60
50
 
61
- #### Examples
51
+ Use `shell.setRegionOpen("side" | "secondary" | "sidenav", open)` to hide or show a region. Hiding retains its instances. Mode `regionSettings` owns size, collapsibility, headers, and tab visibility. `mountStrategy: "keep-mounted"` keeps an inactive tab's content mounted; removing its owner disposes it.
62
52
 
63
- - [`hello-world`](src/examples/hello-world/module.tsx) shows a placeholder in `main` when the welcome Panel is closed.
53
+ The side-panel controller owns attached, floating, or closed presentation. `floatingPanels: "hidden"` prevents floating and reattaches an already floating panel. Per-view menu preferences belong to `panelMenuState`; they do not duplicate region visibility.
64
54
 
65
- ### Menu items
55
+ ## Persistence
66
56
 
67
- Surface commands in the command palette, region headers, tree context menus, or custom menu paths. Menu items reference existing commands, can be ordered and grouped, and can be gated by context expressions through command/menu metadata.
57
+ The browser owns Back/Forward history. Page locations remain version 1. Existing resource locations and stored product data stay valid.
68
58
 
69
- - Register with `layout.registerMenuItem(path, item)`
59
+ Layout cache version 4 records resource identity keys and the new Main collection model. Old layout entries are ignored. This revision does not invalidate tree state, menu preferences, side-panel presentation, or page locations. Collection page state uses the existing location key to separate workspaces.
70
60
 
71
- #### Examples
61
+ Shared mode placements have one cache entry per project and mode. Page cache entries exclude them. Closing a shared panel therefore remains closed when another page is restored; switching projects does not restore another project's mode panels.
72
62
 
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.
63
+ ## Validation
75
64
 
76
- ## View contributions
65
+ Storybook's core guides describe host integration; extension guides use public SDK declarations. Use the repository's Docker workflow and Playwright for dashboard behavior. The release gates are `bun run validate` and `bun run --cwd scripts verify:packages`.
77
66
 
78
- View contributions render placements, tree nodes, and collections as interactive UI.
67
+ ### Commit and host effects
79
68
 
80
- ### Renderers
69
+ Page and panel compound targets resolve against proposed state before live state changes. The browser adapter must implement atomic `push` and `replace`: if it throws, its history must remain unchanged. Browser serialization and writing happen before workbench owners publish the final state. A rejected navigation leaves the workbench unchanged.
81
70
 
82
- The React or bridge implementation for a Panel or placeholder. Renderers turn a placement, resource, config, and workbench context into UI. A Panel contributes only a `rendererId`, so the same renderer can back many Panels and a renderer can be supplied by a different layer than its Panel.
83
-
84
- - Register with `renderers.registerRenderer()`
85
- - Set `keepAlive: true` on the registration to share one persistent subtree across every Panel that points at the same `rendererId`. The subtree is mounted once into a stable host that is reparented between Panel slots via DOM moves; React state, focus, scroll, and in-flight effects survive region transitions. Kept-alive renderers receive no per-Panel input from `render()`; read the active claim with `useWorkbenchClaim()` from `@pstdio/workbench/react`.
86
-
87
- #### Examples
88
-
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.
93
-
94
- ### Tree renderers
95
-
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.
97
-
98
- - Register with `renderers.registerTreeRenderer()`
99
-
100
- #### Examples
101
-
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.
104
-
105
- ### Kanban renderers
106
-
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(...)`.
108
-
109
- - Register with `renderers.registerKanbanRenderer()`
110
-
111
- #### Examples
112
-
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.
115
-
116
- ### Data table renderers
117
-
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.
119
-
120
- - Register with `renderers.registerDataTableRenderer()`
121
- - Refresh with `renderers.refreshDataTableRenderer()` or provide a contribution subscription
122
- - Extensions declare a `dataTable` view body and place that view with `placements`
123
-
124
- #### Examples
125
-
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.
127
-
128
- ### Breadcrumb (TODO: make it a renderer as well)
129
-
130
- `<WorkbenchBreadcrumbView workbench={workbench} />` is the React view that turns the breadcrumb controller state into a rendered trail. It subscribes to `workbench.breadcrumbs.store`, builds `BreadcrumbItem`s from the controller items, and returns `null` when the trail is empty. The default `Workbench` renders it in the persistent Nav Chrome; a custom `nav` Panel replaces only that trail area. Panel headers do not own breadcrumbs. Resource navigation drives the trail through `ctx.breadcrumbs.setItems(...)`; each call replaces the trail.
131
-
132
- - Render with `<WorkbenchBreadcrumbView workbench={workbench} />` from `@pstdio/workbench/react`
133
- - Drive items with `workbench.breadcrumbs.setItems([...])` from the resource navigation controller
134
- - Set `indicator: "session-status"` when a breadcrumb should show the resource's session completion status
135
-
136
- #### Examples
137
-
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.
140
-
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:
142
-
143
- - Add a **Panel** to claim a spot in a region for a renderer.
144
- - Add a **renderer** to supply the React or bridge UI for a Panel or placeholder.
145
- - Add a **tree renderer** for navigable hierarchy and resource discovery, and place it with a Panel.
146
- - Add a **placeholder** for region-level empty state, not for normal content.
147
-
148
- ## Resource contributions
149
-
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.
151
-
152
- ### Resource kinds
153
-
154
- Declare typed things the workbench can open. Resource refs carry `kind`, `uri`, optional `id`, labels, icons, and metadata. Resource kinds make navigation, presenters, and history speak the same language.
155
-
156
- - Register with `resources.registerKind()`
157
- - Use the standard icon names for common resources: project `folder-root`, workspace `computer`, worktree `git-pull-request-draft`, ticket `component`, kanban renderer `square-kanban`, settings `settings`.
158
-
159
- #### Examples
160
-
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.
163
-
164
- ### Resource presenters
165
-
166
- Map a Resource to the Panel instance that presents it. Presenters declare `canOpen(resource)` and return a Panel from `open(resource, input)`. `openResource()` uses the highest-priority presenter, then establishes the returned Panel as the active Location. Direct `layout.openPanel()` calls display supporting Panels without navigating.
167
-
168
- - Register with `resources.registerPresenter()`
169
-
170
- #### Examples
171
-
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.
174
-
175
- ### Resource providers
176
-
177
- Look up resources by kind, uri, or search input. Providers let features resolve resources without knowing where they are stored, which keeps trees and navigation decoupled from product data access.
178
-
179
- - Register with `resources.registerProvider()`
180
-
181
- #### Examples
182
-
183
- - [`dashboard`](src/examples/dashboard/modules/tickets/module.ts) provides ticket resources to the ticket board.
184
-
185
- ## Navigation contributions
186
-
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.
188
-
189
- ### Navigation parsers
190
-
191
- Turn ingress locations into workbench targets. Parsers convert URLs or location strings into resource, view, command, or compound targets. Compound targets are dispatched transactionally through a checkpoint.
192
-
193
- - Register with `navigation.registerParser()`
194
-
195
- #### Examples
196
-
197
- - [`navigation`](src/examples/navigation/module.tsx) parses URL-like locations into typed targets.
198
-
199
- ### Navigation navigators
200
-
201
- Custom dispatch behavior for navigation targets. Navigators let modules extend how resolved targets are opened while still using the shared navigation service.
202
-
203
- - Register with `navigation.registerNavigator()`
204
-
205
- ## Other contributions
206
-
207
- Supporting contributions make view, layout, resource, and navigation contributions useful. They define actions, persistence, scoping, and preferences without directly owning a workbench region.
208
-
209
- ### Commands
210
-
211
- Any executable workbench action. Commands are the primitive behind menus, keybindings, tree actions, notification actions, and command palette entries. Handlers can expose `isEnabled()` and execution failures emit `commands.onDidExecuteError`.
212
-
213
- - Register with `commands.registerCommand()`
214
-
215
- #### Examples
216
-
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.
219
-
220
- ### Keybindings
221
-
222
- Keyboard access to commands. Keybindings reference command ids and can include a `when` expression such as `project.active && !dialog.open`.
223
-
224
- - Register with `keybindings.registerKeybinding()`
225
-
226
- #### Examples
227
-
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.
230
-
231
- ### Context keys
232
-
233
- Conditional command, menu, and keybinding behavior. Module contexts create scoped context keys that are cleaned up with the module. Use context keys for current mode, focused surface, selection state, and other workbench-level conditions.
234
-
235
- - Set through the module context with `context.set()`
236
-
237
- #### Examples
238
-
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.
241
-
242
- ### Modes
243
-
244
- Temporary bundles of contributions for workspace modes such as project, settings, review, or dashboard. Activating a mode runs its contribution function; switching modes disposes the previous mode's mode-scoped contributions.
245
-
246
- - Register with `modes.registerMode()`
247
-
248
- #### Examples
249
-
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.
252
-
253
- ### Preference schemas
254
-
255
- Typed settings contributed by a module or runtime extension. Schemas define the shape and default behavior of workbench preferences, while the host can provide scoped persistence.
256
-
257
- - Register with `preferences.registerSchema()`
258
-
259
- #### Examples
260
-
261
- - [`preferences`](src/examples/preferences/module.tsx) registers user and workspace-scoped preference values.
262
-
263
- ### Notifications
264
-
265
- Toast-style messages from modules or extensions. Notifications can include command-backed actions and are rendered by workbench chrome rather than by individual views.
266
-
267
- - Show with `notifications.show()`
268
-
269
- #### Examples
270
-
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.
273
-
274
- ### Themes
275
-
276
- The React workbench owns the shared `@pstdio/ui` theme preference system through `WorkbenchThemeProvider`. The available theme set lives on the workbench: `workbench.themes` holds contributed themes, and `useWorkbenchThemePreferences(workbench)` reads the built-in themes plus those. `<Workbench />` feeds that set into its own provider, so a contributed theme appears in the picker only while its contributor stays registered. The workbench also renders the shared `Toaster` viewport for workbench notifications and toast calls from workbench modules. Hosts that render their own chrome (sizing boxes or app-level loading screens) wrap it in `WorkbenchThemeProvider` with the same set so that chrome is themed too. Workbench chrome reads VS Code-compatible color theme tokens such as `editor.background`, `sideBar.background`, `panel.background`, and `editorWidget.background` when extension themes provide them.
277
-
278
- #### Examples
279
-
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.
281
-
282
- ### Terminal
283
-
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.
285
-
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.
287
-
288
- #### Examples
289
-
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`.
292
-
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.
294
-
295
- ## Nomenclature
296
-
297
- - **Contribution**: a declarative unit added to a registry, such as a command, menu item, resource kind, Panel, renderer, tree renderer, mode, or KanbanView.
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).
299
- - **Runtime extension**: extension metadata from `pstdio-extensions` that a host maps into workbench modules at the trust boundary.
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.
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`.
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`.
303
- - **Data table renderer contribution**: a dense table renderer registered under `renderers`. Provides query rows, optional column descriptors, navigation, and row actions. Extensions place it through a native view; the presentational layer is `<DataTable>` from `@pstdio/ui/data-table`.
304
- - **Placeholder**: an empty-state contribution rendered only when a region has no Panel placements. Placeholders do not appear in tabs.
305
- - **Renderer**: code that turns a Panel placement into UI. The Panel host looks up `rendererId` in `workbench.renderers` and inserts the returned React node.
306
- - **Resource**: a typed object reference with `kind`, `id`, `uri`, and label metadata.
307
- - **Resource presenter**: routing logic that maps a resource to a Panel placement.
308
- - **Command**: an executable action registered in the command registry. Errors raised during execution emit `workbench.commands.onDidExecuteError`.
309
- - **Menu path**: a stable location where commands are surfaced, such as the command palette, a region header, or a tree node context menu.
310
- - **Keybinding**: a keyboard shortcut bound to a command, optionally gated by a context expression.
311
- - **Context key**: boolean or scalar workbench state used by commands, menus, and keybindings to decide when they are active.
312
- - **Preference schema**: typed settings contributed by workbench modules or runtime extensions.
313
- - **Mode**: a named bundle of temporary contributions activated through `workbench.modes`. Switching modes disposes the previous mode's activation result.
314
- - **Tree view section**: a group of nodes inside a tree renderer's `getBody`, with an optional label, optional collapsible flag, and inline actions.
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.
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.
317
-
318
- ## Regions overview
319
-
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.
321
-
322
- Use `layout.registerPlaceholder()` for a region empty state that should render only after all Panels in that region close. Placeholders are not Panel placements, so they do not affect tab lists.
323
-
324
- Panels with tabs are paired with a `<panel>-header` region that the workbench renders directly above the panel. Panels placed in a header region use a bottom border by default. Set `headerBorderBottom: false` on a Panel contribution to let that Panel own the header separation.
325
-
326
- | Region | Workbench location | Typical use |
327
- | ------------------ | -------------------------------------------- | ------------------------------------------------------------- |
328
- | `nav` | Nav Chrome across the resource-owned column | Project selector, breadcrumbs, history, resource actions, region controls |
329
- | `activity` | Optional rail on the leading edge | Top-level mode or workspace switching |
330
- | `sidenav-header` | Optional header above `sidenav` | Sidenav-local controls that must sit above its scrolling content |
331
- | `sidenav` | Leading Sidenav | Navigation trees, registries, outlines, resource lists |
332
- | `main-header` | Header above the Main Panel | Main Panel tabs and controls |
333
- | `main-left-menu` | Menu inside the Main Panel's leading edge | Contextual navigation and document outlines |
334
- | `main` | Central Main Panel | Editors, detail pages, dashboards, primary resource views |
335
- | `main-right-menu` | Menu inside the Main Panel's trailing edge | Inspectors, properties, and contextual details |
336
- | `secondary-header` | Header above the Secondary Panel | Tabs, add-panel controls, and collapsed-menu controls |
337
- | `secondary` | Secondary Panel below the Main Panel | Diagnostics, activity, logs, terminals, background task output |
338
- | `side-header` | Header above the Side Panel | Session tabs and Side Panel controls |
339
- | `side` | Full-height Side Panel | Assistant sessions and independent secondary workflows |
340
- | `status` | Full-width Status Bar at the viewport bottom | Compact state, counters, sync status, environment indicators |
341
- | `overlay` | Layer above the workbench | Modal flows, blocking prompts, transient overlays |
342
-
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`.
344
-
345
- ## Header actions
346
-
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.
348
-
349
- Runtime extensions should only target documented public slots through descriptors; hosts map those descriptors into workbench modules instead of giving extension packages direct workbench access.
71
+ Cache writes, mode lifecycle hooks, and subscribers are host effects after this boundary. Their exceptions are reported with the owner in the console and do not reject a committed navigation or stop other observers. A full cache can prevent restoration on the next launch, while the current location and panels remain usable. Fix the reported host effect at its source. A hook must clean up its own external effects if it fails before returning its disposables.