@hachej/boring-workspace 0.1.13 → 0.1.14

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 (34) hide show
  1. package/README.md +270 -42
  2. package/dist/CommandPalette-NOEOVkN2.js +5714 -0
  3. package/dist/{FileTree-BVfqs3rR.js → FileTree-Dl-qUAB0.js} +9 -9
  4. package/dist/MarkdownEditor-yc6mFsnI.js +533 -0
  5. package/dist/{WorkspaceLoadingState-BjZGQLS_.js → WorkspaceLoadingState-CSZfENWe.js} +145 -124
  6. package/dist/agent-tool-DEtfQPVB.d.ts +100 -0
  7. package/dist/app-front.d.ts +79 -67
  8. package/dist/app-front.js +253 -241
  9. package/dist/app-server.d.ts +17 -12
  10. package/dist/app-server.js +80 -10
  11. package/dist/{bootstrapServer-BRUqUpVW.d.ts → bootstrapServer-BreQ9QBc.d.ts} +8 -2
  12. package/dist/server.d.ts +10 -32
  13. package/dist/server.js +22 -127
  14. package/dist/shared.d.ts +1 -2
  15. package/dist/testing.d.ts +0 -63
  16. package/dist/testing.js +2248 -2401
  17. package/dist/workspace.css +1616 -974
  18. package/dist/workspace.d.ts +111 -450
  19. package/dist/workspace.js +417 -1635
  20. package/docs/INTERFACES.md +2 -2
  21. package/docs/PLUGIN_STRUCTURE.md +1 -1
  22. package/docs/plans/ASK_USER_QUESTIONS_PLUGIN_SPEC.md +131 -263
  23. package/docs/plans/GENERIC_EXPLORER_PLUGIN_PLAN.md +29 -27
  24. package/docs/plans/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md +12 -12
  25. package/docs/plans/PANE_TO_AGENT_CHAT_ACTIONS_SPEC.md +366 -0
  26. package/docs/plans/README.md +2 -0
  27. package/docs/plans/archive/PLUGIN_MODEL.md +14 -14
  28. package/docs/plans/archive/SRC_FOLDER_REORG_PLAN.md +2 -3
  29. package/docs/plans/archive/WORKSPACE_V2_PLAN.md +1 -1
  30. package/package.json +3 -6
  31. package/dist/CommandPalette-Dme9em28.js +0 -5506
  32. package/dist/MarkdownEditor-CcCDF65H.js +0 -502
  33. package/dist/agent-tool-NvxKfist.d.ts +0 -28
  34. package/dist/explorer-DtLUnuah.d.ts +0 -129
package/README.md CHANGED
@@ -1,49 +1,64 @@
1
- # @boring/workspace
1
+ # @hachej/boring-workspace
2
2
 
3
- Plugin system, panel registry, and IDE-style layout for boring-ui apps.
3
+ <div align="center">
4
+
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![npm](https://img.shields.io/npm/v/@hachej/boring-workspace.svg)](https://www.npmjs.com/package/@hachej/boring-workspace)
7
+
8
+ </div>
9
+
10
+ Plugin system, panel registry, Dockview-based IDE layout, and typed agent-to-browser bridge for boring-ui apps. Everything the user touches — chat, files, catalogs, custom panes — composes here.
4
11
 
5
12
  ```bash
6
- pnpm add @boring/workspace
13
+ pnpm add @hachej/boring-workspace
7
14
  ```
8
15
 
9
16
  ---
10
17
 
11
- ## What it provides
18
+ ## TL;DR
19
+
20
+ **The Problem**: Agent apps need more than a chat window. You want file trees, editors, data tables, custom panes, keyboard shortcuts — but wiring all that chrome yourself means fighting layout engines, managing panel lifecycles, and building bridges between backend commands and frontend actions.
21
+
22
+ **The Solution**: `@hachej/boring-workspace` gives you a complete IDE-style workbench with a plugin system that contributes panes, tabs, commands, catalogs, surface resolvers, and React context. The agent backend talks to the UI through a typed pubsub bus. You write the domain logic; the workspace handles the chrome.
23
+
24
+ ### Why Use @hachej/boring-workspace?
12
25
 
13
- - **Plugin system** contribute panels, commands, catalogs, sidebar tabs, and surface resolvers
14
- - **Layouts** — `ChatLayout`, `IdeLayout`, `ResponsiveDockviewShell`
15
- - **Panel registry** auto-lazy code splitting, error boundaries, dockview integration
16
- - **Bridge** — typed pubsub between agent backend and frontend panels
17
- - **Built-in plugins** file tree, editor, artifact surface, command palette
26
+ | Feature | What It Does |
27
+ |---------|--------------|
28
+ | **Plugin system** | Contribute panels, left-tabs, commands, catalogs, surface resolvers, and React bindings through a single manifest |
29
+ | **Dockview layout** | Split, resize, drag, and dock panels VS Code–style behavior out of the box |
30
+ | **Auto code-splitting** | Lazy panels (`() => import(...)`) are automatically wrapped in `React.lazy + Suspense + ErrorBoundary` |
31
+ | **Typed UI bridge** | Agent calls `exec_ui({ kind: "openFile", params })` → panel opens in the workbench. SSE + HTTP fallback. |
32
+ | **Surface resolver** | Map agent-emitted `SurfaceOpenRequest` to panel opens — domain-specific routing without hardcoding panel IDs |
33
+ | **Built-in plugins** | File tree, editor, command palette, session management — ready on mount |
34
+ | **Composable** | Three levels: full layout (`IdeLayout`), provider + layout primitives, or headless hooks — pick what you need |
18
35
 
19
36
  ---
20
37
 
21
- ## Quickstart
38
+ ## Quick Example
22
39
 
23
40
  ```tsx
24
- import { WorkspaceProvider, IdeLayout } from "@boring/workspace"
25
- import { ChatPanel } from "@boring/agent"
41
+ import { WorkspaceProvider, IdeLayout } from "@hachej/boring-workspace"
42
+ import { ChatPanel } from "@hachej/boring-agent"
26
43
 
27
- export function App() {
44
+ // Full shell — plug in your chat and plugins
45
+ function App() {
28
46
  return (
29
- <WorkspaceProvider chatPanel={ChatPanel} plugins={[myPlugin]}>
47
+ <WorkspaceProvider chatPanel={ChatPanel} workspaceId="default" plugins={[myPanelPlugin, dataCatalogPlugin]}>
30
48
  <IdeLayout />
31
49
  </WorkspaceProvider>
32
50
  )
33
51
  }
34
52
  ```
35
53
 
36
- ---
37
-
38
- ## Writing a plugin
54
+ Add a panel in 8 lines:
39
55
 
40
56
  ```ts
41
- import { defineFrontPlugin, definePanel } from "@boring/workspace"
57
+ import { defineFrontPlugin, definePanel } from "@hachej/boring-workspace"
42
58
 
43
- export const myPlugin = defineFrontPlugin({
44
- id: "my-plugin",
45
- label: "My Plugin",
46
- systemPrompt: "You can open widgets with the 'open-widget' tool.",
59
+ export const myPanelPlugin = defineFrontPlugin({
60
+ id: "my-panel",
61
+ label: "My Panel",
47
62
  outputs: [
48
63
  {
49
64
  type: "panel",
@@ -58,37 +73,250 @@ export const myPlugin = defineFrontPlugin({
58
73
  })
59
74
  ```
60
75
 
61
- Panel components receive `PaneProps<T>`:
76
+ ---
77
+
78
+ ## Installation
79
+
80
+ ```bash
81
+ # pnpm
82
+ pnpm add @hachej/boring-workspace
83
+
84
+ # npm
85
+ npm install @hachej/boring-workspace
86
+
87
+ # from source
88
+ git clone https://github.com/hachej/boring-ui.git
89
+ cd boring-ui && pnpm install
90
+ pnpm --filter @hachej/boring-workspace build
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Architecture
96
+
97
+ ### Plugin System
98
+
99
+ Plugins bootstrap into the workspace through a single `registerPlugin` call. `WorkspaceProvider` creates a `PanelRegistry`, calls `bootstrap()` with all plugins, and the registry auto-wraps lazy components.
100
+
101
+ ```
102
+ WorkspaceProvider
103
+ ├── bootstrap(plugins)
104
+ │ ├── registry.register(panel outputs)
105
+ │ ├── registry.register(left-tab outputs)
106
+ │ └── registry.register(command outputs)
107
+
108
+ ├── DockviewShell
109
+ │ ├── registry.getComponents() → lazy-wrapped panels
110
+ │ └── DockviewReact (layout chrome)
111
+
112
+ ├── UiBridgeClient
113
+ │ ├── SSE command stream
114
+ │ └── HTTP poll fallback
115
+
116
+ └── Layout (IdeLayout / ChatLayout / ResponsiveDockviewShell)
117
+ ```
118
+
119
+ ### UI Bridge
120
+
121
+ ```
122
+ Agent Backend Frontend
123
+ ────────────── ────────
124
+ POST /api/v1/ui/commands ──► UiBridge dispatches
125
+ { kind, params } │ ├── openFile
126
+ │ ├── openPanel
127
+ │ └── showNotification
128
+ ```
129
+
130
+ Agent plugins emit `UiCommand` values on the server. The workbench has a typed event bus that dispatches them to the right handler.
131
+
132
+ ```
133
+ POST /api/v1/ui/commands
134
+ { kind: "openFile", params: { path: "src/index.ts" } }
135
+
136
+
137
+
138
+ UiBridgeClient receives → dispatches to FileTree plugin → expands node + focuses editor
139
+ ```
140
+
141
+ ### Built-in Plugins
142
+
143
+ | Plugin | What It Adds |
144
+ |--------|-------------|
145
+ | Filesystem plugin | File tree (left tab), editor (center panel), file navigation |
146
+ | Chat plugin | Integrates the injected `ChatPanel` into the layout |
147
+ | Command palette | `⌘K`-driven search across commands, panels, and surfaces |
148
+ | Session plugin | Lightweight session toolbar (current session, dropdown, new chat) |
149
+
150
+ ---
151
+
152
+ ## Package Surfaces
153
+
154
+ | Import | Environment | What You Get |
155
+ |--------|-------------|--------------|
156
+ | `@hachej/boring-workspace` | Browser | `WorkspaceProvider`, `IdeLayout`, `defineFrontPlugin`, `definePanel`, all built-ins |
157
+ | `@hachej/boring-workspace/server` | Node | `defineServerPlugin()`, server routes, UI tools, Pi package helpers |
158
+ | `@hachej/boring-workspace/shared` | Any | `PaneProps`, `SurfaceOpenRequest`, `UiCommand`, plugin types |
159
+ | `@hachej/boring-workspace/events` | Any | Typed event bus for bridge communication |
160
+ | `@hachej/boring-workspace/charts` | Browser | Recharts wrappers for data visualization |
161
+ | `@hachej/boring-workspace/testing` | Browser | Test utilities and mock providers |
162
+ | `@hachej/boring-workspace/app/front` | Browser | App composition: `WorkspaceAgentFront` |
163
+ | `@hachej/boring-workspace/app/server` | Node | App composition: `createWorkspaceAgentApp` |
164
+ | `@hachej/boring-workspace/globals.css` | Browser | Global CSS for the workspace chrome |
165
+
166
+ ---
167
+
168
+ ## Plugin Output Types
169
+
170
+ | Output Type | Contributed Surface | Example |
171
+ |-------------|--------------------|---------|
172
+ | `panel` | Center/right/bottom pane | Code editor, data table, settings page |
173
+ | `left-tab` | Persistent sidebar tab | File tree, data catalog, status panel |
174
+ | `command` | Command palette entry | "Toggle dark mode", "Format files" |
175
+ | `catalog` | Searchable data explorer | Customer list with faceted filters |
176
+ | `surface-resolver` | Maps `exec_ui` → panel | `openFile` → editor panel with path |
177
+ | `binding` | React context in provider tree | Theme, auth, workspace-scoped state |
178
+ | `provider` | Binding + `apiBaseUrl` injection | Server-side plugin config passed to front |
179
+ | `agent-tool` | New agent tool via server plugin | `deploy`, `test`, `lint` commands |
180
+
181
+ ### Writing a Plugin
62
182
 
63
183
  ```ts
64
- import type { PaneProps } from "@boring/workspace"
184
+ import { defineFrontPlugin, definePanel } from "@hachej/boring-workspace"
185
+ import type { PaneProps } from "@hachej/boring-workspace"
186
+
187
+ export const statusPlugin = defineFrontPlugin({
188
+ id: "build-status",
189
+ label: "Build Status",
190
+ outputs: [
191
+ {
192
+ type: "left-tab",
193
+ panel: definePanel({
194
+ id: "build-status-tab",
195
+ title: "Build",
196
+ placement: "left",
197
+ component: () => import("./StatusTab").then(m => ({ default: m.StatusTab })),
198
+ }),
199
+ },
200
+ {
201
+ type: "panel",
202
+ panel: definePanel({
203
+ id: "build-details",
204
+ title: "Build Details",
205
+ placement: "bottom",
206
+ component: () => import("./BuildDetails").then(m => ({ default: m.BuildDetails })),
207
+ }),
208
+ },
209
+ ],
210
+ })
65
211
 
66
- export function WidgetPane({ params, api }: PaneProps<{ id: string }>) {
67
- // params data passed when the panel is opened
68
- // api DockviewPanelApi (close, setTitle, …)
212
+ // Panel components receive PaneProps<T>:
213
+ function StatusTab({ params, api, containerApi }: PaneProps<{}>) {
214
+ // params data when panel is opened
215
+ // api — DockviewPanelApi (close, setTitle, …)
216
+ // containerApi — DockviewApi (addPanel, layout, …)
69
217
  }
70
218
  ```
71
219
 
72
- Panels are auto-lazy: a zero-arg factory `() => import(...)` is code-split automatically.
220
+ ### Server Plugins
221
+
222
+ ```ts
223
+ import { defineServerPlugin } from "@hachej/boring-workspace/server"
224
+
225
+ export const statusServerPlugin = defineServerPlugin({
226
+ id: "build-status",
227
+ tools: [buildTool], // Agent tools
228
+ routes: [statusRoutes], // Fastify routes
229
+ provisioning: [seedBuildDir], // Environment setup
230
+ })
231
+ ```
73
232
 
74
233
  ---
75
234
 
76
- ## Output types
235
+ ## Configuration
236
+
237
+ ### WorkspaceProvider Props
238
+
239
+ ```tsx
240
+ <WorkspaceProvider
241
+ chatPanel={ChatPanel} // Required: React component for chat
242
+ workspaceId={id} // Required: workspace identifier
243
+ plugins={[pluginA, pluginB]} // Optional: extend with custom plugins
244
+ apiBaseUrl="http://localhost:3000" // Optional: backend URL
245
+ authHeaders={...} // Optional: auth for HTTP requests
246
+ layoutPreferences={...} // Optional: initial Dockview layout JSON
247
+ />
248
+ ```
249
+
250
+ ---
251
+
252
+ ## How @hachej/boring-workspace Compares
253
+
254
+ | Feature | @hachej/boring-workspace | VS Code (Theia) | Custom layout |
255
+ |---------|--------------------------|-----------------|---------------|
256
+ | Panel layout | ✅ Dockview, drag/drop/split | ✅ Tabbed | ⚠️ Build yourself |
257
+ | Plugin system | ✅ Panels + tabs + commands + catalogs | ✅ Extension API | ❌ DIY |
258
+ | Agent bridge | ✅ Typed UiBridge pubsub | ❌ Not agent-native | ❌ DIY |
259
+ | Code splitting | ✅ Auto-detects lazy factories | ⚠️ Require-based | ⚠️ Manual |
260
+ | Setup time | ✅ ~10 lines | ❌ Heavy framework | ❌ Weeks |
261
+
262
+ **When to use @hachej/boring-workspace:**
263
+ - Building an agent-powered IDE with customizable panes
264
+ - You need plugins to add panels without touching the shell
265
+ - You want the agent to open things (files, data, charts) in the workbench
266
+
267
+ **When it might not fit:**
268
+ - You just want a chat box (use `@hachej/boring-agent` standalone)
269
+ - You need a full VS Code replacement (no language server protocol support)
270
+ - You want to control every pixel of the layout (use Dockview directly)
271
+
272
+ ---
273
+
274
+ ## Troubleshooting
275
+
276
+ | Error | Cause | Fix |
277
+ |-------|-------|-----|
278
+ | `Panel not found: <id>` | Plugin not registered | Check `plugins` prop on `WorkspaceProvider` |
279
+ | Blank panel / white screen | Lazy component threw | Check `PluginErrorBoundary` — inspect console |
280
+ | `UiBridge not connected` | Backend not running | Verify `apiBaseUrl` and backend endpoint |
281
+ | Commands not arriving | SSE blocked by proxy | Use `?poll=true` on `/api/v1/ui/commands/next` |
282
+ | Plugin not loading | Missing `defineFrontPlugin` | All plugins must be wrapped in `defineFrontPlugin` |
283
+ | Duplicate panel IDs | Two plugins register same ID | Rename one panel's `id` field |
284
+
285
+ ---
286
+
287
+ ## Limitations
288
+
289
+ - **Plugin panels share the same registry** — name collisions between plugins cause the last-registered panel to win. Namespace your IDs.
290
+ - **No code editor language server** — The editor ships CodeMirror6 with syntax highlighting but no LSP. Semantic features (go-to-definition, rename) are not available.
291
+ - **Rich text editor is TipTap-based** — It's included but opt-in via peer dependencies. Not all TipTap extensions are wired up.
292
+ - **Frontend code must not value-import `@hachej/boring-agent`** — Package-neutral workspace code stays agent-free. Use the `chatPanel` injection pattern instead.
293
+ - **Layout state persistence is the shell's job** — Workspace doesn't save/restore layouts between sessions. The shell owns `layoutPreferences`.
294
+
295
+ ---
296
+
297
+ ## FAQ
298
+
299
+ **Q: What's the difference between a `panel` and a `left-tab`?**
300
+ A: `panel` opens in the main Dockview area (center/right/bottom). `left-tab` is a persistent sidebar element that stays docked to the left. Users open panels programmatically; left-tabs are always visible.
301
+
302
+ **Q: How do I lazy-load a panel?**
303
+ A: Pass a zero-arg arrow function `() => import("./Pane").then(m => ({ default: m.Pane }))`. The registry auto-detects this pattern and wraps it in `React.lazy + Suspense`. Don't set `lazy: true`.
304
+
305
+ **Q: Can the agent open my plugin's panel?**
306
+ A: Yes. Register a `surface-resolver` that maps the agent's `SurfaceOpenRequest` to your panel ID. Then the agent can use `exec_ui` to open it.
307
+
308
+ **Q: Why can't frontend workspace code import from `@hachej/boring-agent`?**
309
+ A: The workspace package stays package-neutral so it can be used without the agent. The `chatPanel` prop injects the agent's UI, keeping the dependency inverted.
310
+
311
+ **Q: What's `surface-resolver` for?**
312
+ A: It decouples agent-side "open X" requests from frontend panel IDs. The agent says `{ kind: "open-series", seriesId: "GDPC1" }`, and the surface resolver maps that to `{ panelId: "series-chart", params: { seriesId: "GDPC1" } }`.
313
+
314
+ ---
77
315
 
78
- | type | contributes |
79
- |---|---|
80
- | `panel` | a center/right/bottom pane |
81
- | `left-tab` | a persistent sidebar tab |
82
- | `command` | a command palette entry |
83
- | `catalog` | a searchable data explorer |
84
- | `surface-resolver` | maps agent `exec_ui` calls to panel opens |
316
+ *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
85
317
 
86
318
  ---
87
319
 
88
- ## Part of [boring-ui](https://github.com/hachej/boring-ui)
320
+ ## License
89
321
 
90
- | Package | Role |
91
- |---|---|
92
- | `@boring/core` | DB, auth, app factory |
93
- | `@boring/workspace` | Plugin system, layouts |
94
- | `@boring/agent` | Agent runtime + tools |
322
+ MIT