@hachej/boring-workspace 0.1.16 → 0.1.18

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 (38) hide show
  1. package/README.md +36 -34
  2. package/dist/{FileTree-Dl-qUAB0.js → FileTree-DHVB9rpk.js} +15 -15
  3. package/dist/MarkdownEditor-L1KDH0bM.js +534 -0
  4. package/dist/{WorkspaceLoadingState-CSZfENWe.js → WorkspaceLoadingState-DYDxUYnx.js} +114 -110
  5. package/dist/WorkspaceProvider-CDPaAO5u.js +5971 -0
  6. package/dist/app-front.d.ts +94 -107
  7. package/dist/app-front.js +243 -233
  8. package/dist/app-server.d.ts +130 -15
  9. package/dist/app-server.js +1569 -304
  10. package/dist/{bootstrapServer-BreQ9QBc.d.ts → createInMemoryBridge-BDxDzihm.d.ts} +11 -26
  11. package/dist/manifest-CyNNdfYz.d.ts +58 -0
  12. package/dist/plugin.d.ts +199 -0
  13. package/dist/plugin.js +300 -0
  14. package/dist/server.d.ts +239 -4
  15. package/dist/server.js +901 -78
  16. package/dist/shared.d.ts +4 -112
  17. package/dist/surface-COYagY2m.d.ts +111 -0
  18. package/dist/testing.d.ts +19 -1
  19. package/dist/testing.js +2 -2
  20. package/dist/{agent-tool-DEtfQPVB.d.ts → ui-bridge-Gfh1MMgl.d.ts} +30 -30
  21. package/dist/workspace.css +36 -0
  22. package/dist/workspace.d.ts +165 -120
  23. package/dist/workspace.js +330 -377
  24. package/docs/INTERFACES.md +9 -9
  25. package/docs/PLUGIN_STRUCTURE.md +39 -145
  26. package/docs/PLUGIN_SYSTEM.md +355 -0
  27. package/docs/README.md +6 -1
  28. package/docs/plans/README.md +1 -0
  29. package/docs/plans/archive/HOT_RELOADABLE_AGENT_PLUGINS_PLAN.md +218 -0
  30. package/docs/plans/archive/RELOAD_PLUGGABILITY_PLAN.md +174 -0
  31. package/docs/plans/archive/UNIFIED_PLUGIN_SYSTEM_PLAN.md +769 -0
  32. package/package.json +11 -5
  33. package/dist/CommandPalette-NOEOVkN2.js +0 -5714
  34. package/dist/MarkdownEditor-yc6mFsnI.js +0 -533
  35. package/docs/bridge.md +0 -135
  36. package/docs/panels.md +0 -102
  37. package/docs/plugins.md +0 -158
  38. /package/docs/plans/{MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md → archive/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md} +0 -0
package/README.md CHANGED
@@ -54,20 +54,17 @@ function App() {
54
54
  Add a panel in 8 lines:
55
55
 
56
56
  ```ts
57
- import { defineFrontPlugin, definePanel } from "@hachej/boring-workspace"
57
+ import { definePlugin } from "@hachej/boring-workspace/plugin"
58
58
 
59
- export const myPanelPlugin = defineFrontPlugin({
59
+ export const myPanelPlugin = definePlugin({
60
60
  id: "my-panel",
61
61
  label: "My Panel",
62
- outputs: [
62
+ panels: [
63
63
  {
64
- type: "panel",
65
- panel: definePanel({
66
- id: "my-widget",
67
- title: "Widget",
68
- placement: "center",
69
- component: () => import("./WidgetPane").then(m => ({ default: m.WidgetPane })),
70
- }),
64
+ id: "my-widget",
65
+ label: "Widget",
66
+ placement: "center",
67
+ component: () => import("./WidgetPane").then(m => ({ default: m.WidgetPane })),
71
68
  },
72
69
  ],
73
70
  })
@@ -153,14 +150,15 @@ UiBridgeClient receives → dispatches to FileTree plugin → expands node + foc
153
150
 
154
151
  | Import | Environment | What You Get |
155
152
  |--------|-------------|--------------|
156
- | `@hachej/boring-workspace` | Browser | `WorkspaceProvider`, `IdeLayout`, `defineFrontPlugin`, `definePanel`, all built-ins |
153
+ | `@hachej/boring-workspace` | Browser | `WorkspaceProvider`, `IdeLayout`, built-in layout primitives |
154
+ | `@hachej/boring-workspace/plugin` | Browser | `definePlugin()` and browser-safe plugin authoring types |
157
155
  | `@hachej/boring-workspace/server` | Node | `defineServerPlugin()`, server routes, UI tools, Pi package helpers |
158
156
  | `@hachej/boring-workspace/shared` | Any | `PaneProps`, `SurfaceOpenRequest`, `UiCommand`, plugin types |
159
157
  | `@hachej/boring-workspace/events` | Any | Typed event bus for bridge communication |
160
158
  | `@hachej/boring-workspace/charts` | Browser | Recharts wrappers for data visualization |
161
159
  | `@hachej/boring-workspace/testing` | Browser | Test utilities and mock providers |
162
160
  | `@hachej/boring-workspace/app/front` | Browser | App composition: `WorkspaceAgentFront` |
163
- | `@hachej/boring-workspace/app/server` | Node | App composition: `createWorkspaceAgentApp` |
161
+ | `@hachej/boring-workspace/app/server` | Node | App composition: `createWorkspaceAgentServer` |
164
162
  | `@hachej/boring-workspace/globals.css` | Browser | Global CSS for the workspace chrome |
165
163
 
166
164
  ---
@@ -176,35 +174,31 @@ UiBridgeClient receives → dispatches to FileTree plugin → expands node + foc
176
174
  | `surface-resolver` | Maps `exec_ui` → panel | `openFile` → editor panel with path |
177
175
  | `binding` | React context in provider tree | Theme, auth, workspace-scoped state |
178
176
  | `provider` | Binding + `apiBaseUrl` injection | Server-side plugin config passed to front |
179
- | `agent-tool` | New agent tool via server plugin | `deploy`, `test`, `lint` commands |
177
+ | `agent-tool` | Static agent tool via server plugin | `deploy`, `test`, `lint` commands |
180
178
 
181
179
  ### Writing a Plugin
182
180
 
183
181
  ```ts
184
- import { defineFrontPlugin, definePanel } from "@hachej/boring-workspace"
182
+ import { definePlugin } from "@hachej/boring-workspace/plugin"
185
183
  import type { PaneProps } from "@hachej/boring-workspace"
186
184
 
187
- export const statusPlugin = defineFrontPlugin({
185
+ export const statusPlugin = definePlugin({
188
186
  id: "build-status",
189
187
  label: "Build Status",
190
- outputs: [
188
+ leftTabs: [
191
189
  {
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
- }),
190
+ id: "build-status-tab",
191
+ title: "Build",
192
+ panelId: "build-status-tab",
193
+ component: () => import("./StatusTab").then(m => ({ default: m.StatusTab })),
199
194
  },
195
+ ],
196
+ panels: [
200
197
  {
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
- }),
198
+ id: "build-details",
199
+ label: "Build Details",
200
+ placement: "bottom",
201
+ component: () => import("./BuildDetails").then(m => ({ default: m.BuildDetails })),
208
202
  },
209
203
  ],
210
204
  })
@@ -219,14 +213,22 @@ function StatusTab({ params, api, containerApi }: PaneProps<{}>) {
219
213
 
220
214
  ### Server Plugins
221
215
 
216
+ Server plugins are boot-time/static composition. Hot-reloadable `.pi/extensions` agent tools should use Pi extensions instead.
217
+
222
218
  ```ts
223
219
  import { defineServerPlugin } from "@hachej/boring-workspace/server"
224
220
 
225
221
  export const statusServerPlugin = defineServerPlugin({
226
222
  id: "build-status",
227
- tools: [buildTool], // Agent tools
228
- routes: [statusRoutes], // Fastify routes
229
- provisioning: [seedBuildDir], // Environment setup
223
+ agentTools: [buildTool], // Agent tools
224
+ routes: async (app) => { // Fastify plugin function
225
+ app.register(statusRoutes, { prefix: "/build-status" })
226
+ },
227
+ provisioning: {
228
+ templateDirs: [
229
+ { id: "build-status-seed", path: seedBuildDir, target: "." },
230
+ ],
231
+ },
230
232
  })
231
233
  ```
232
234
 
@@ -279,7 +281,7 @@ export const statusServerPlugin = defineServerPlugin({
279
281
  | Blank panel / white screen | Lazy component threw | Check `PluginErrorBoundary` — inspect console |
280
282
  | `UiBridge not connected` | Backend not running | Verify `apiBaseUrl` and backend endpoint |
281
283
  | 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` |
284
+ | Plugin not loading | Missing `definePlugin` wrapper or `boring.front` manifest entry | Package front plugins must default-export `definePlugin({ ... })` and declare `boring.front` |
283
285
  | Duplicate panel IDs | Two plugins register same ID | Rename one panel's `id` field |
284
286
 
285
287
  ---
@@ -1,9 +1,9 @@
1
1
  import { jsx as i, jsxs as C } from "react/jsx-runtime";
2
- import { useRef as w, useEffect as R, useMemo as P, useCallback as x, createContext as H, useContext as L } from "react";
2
+ import { useRef as w, useEffect as I, useMemo as P, useCallback as x, createContext as H, useContext as L } from "react";
3
3
  import { Tree as q } from "react-arborist";
4
- import { FolderOpenIcon as z, FolderIcon as B, ChevronRightIcon as E, Loader2Icon as J } from "lucide-react";
5
- import { J as K } from "./CommandPalette-NOEOVkN2.js";
6
- import { Input as Y } from "@hachej/boring-ui-kit";
4
+ import { FolderOpenIcon as z, FolderIcon as B, ChevronRightIcon as E, Loader2Icon as K } from "lucide-react";
5
+ import { I as Y } from "./WorkspaceProvider-CDPaAO5u.js";
6
+ import { Input as G } from "@hachej/boring-ui-kit";
7
7
  import { c as v } from "./utils-B6yFEsav.js";
8
8
  const T = /* @__PURE__ */ new Set(), A = H({
9
9
  onContextMenu: void 0,
@@ -12,14 +12,14 @@ const T = /* @__PURE__ */ new Set(), A = H({
12
12
  onSubmitEdit: void 0,
13
13
  onCancelEdit: void 0
14
14
  });
15
- function G({
15
+ function J({
16
16
  initialValue: n,
17
17
  onSubmit: p,
18
18
  onCancel: h,
19
19
  isDraft: c
20
20
  }) {
21
21
  const t = w(null), u = w(!1);
22
- R(() => {
22
+ I(() => {
23
23
  const e = t.current;
24
24
  if (e)
25
25
  if (e.focus(), !c && n.includes(".")) {
@@ -36,7 +36,7 @@ function G({
36
36
  !e || e === n ? h() : p(e);
37
37
  };
38
38
  return /* @__PURE__ */ i(
39
- Y,
39
+ G,
40
40
  {
41
41
  ref: t,
42
42
  type: "text",
@@ -54,7 +54,7 @@ function G({
54
54
  );
55
55
  }
56
56
  function Q({ node: n, style: p, dragHandle: h }) {
57
- const { onContextMenu: c, editing: t, pendingPaths: u, onSubmitEdit: l, onCancelEdit: e } = L(A), o = n.data, f = o.kind === "dir", d = (t == null ? void 0 : t.path) === o.path, N = u.has(o.path), b = f ? n.isOpen ? z : B : K(o.name || "untitled");
57
+ const { onContextMenu: c, editing: t, pendingPaths: u, onSubmitEdit: l, onCancelEdit: e } = L(A), o = n.data, f = o.kind === "dir", d = (t == null ? void 0 : t.path) === o.path, N = u.has(o.path), b = f ? n.isOpen ? z : B : Y(o.name || "untitled");
58
58
  return /* @__PURE__ */ C(
59
59
  "div",
60
60
  {
@@ -95,7 +95,7 @@ function Q({ node: n, style: p, dragHandle: h }) {
95
95
  }
96
96
  ),
97
97
  d ? /* @__PURE__ */ i(
98
- G,
98
+ J,
99
99
  {
100
100
  initialValue: (t == null ? void 0 : t.initialValue) ?? o.name ?? "",
101
101
  isDraft: !!(t != null && t.isDraft),
@@ -113,7 +113,7 @@ function Q({ node: n, style: p, dragHandle: h }) {
113
113
  }
114
114
  ),
115
115
  N && !d && /* @__PURE__ */ i(
116
- J,
116
+ K,
117
117
  {
118
118
  "data-testid": "file-tree-pending-spinner",
119
119
  "aria-label": "Pending",
@@ -140,17 +140,17 @@ function te({
140
140
  onSubmitEdit: N,
141
141
  onCancelEdit: b,
142
142
  onDragDrop: s,
143
- className: I
143
+ className: R
144
144
  }) {
145
145
  const k = w(null);
146
- R(() => {
146
+ I(() => {
147
147
  if (!(t != null && t.isDraft)) return;
148
148
  const r = requestAnimationFrame(() => {
149
149
  var a;
150
150
  (a = k.current) == null || a.scrollTo(t.path);
151
151
  });
152
152
  return () => cancelAnimationFrame(r);
153
- }, [t == null ? void 0 : t.isDraft, t == null ? void 0 : t.path]), R(() => {
153
+ }, [t == null ? void 0 : t.isDraft, t == null ? void 0 : t.path]), I(() => {
154
154
  if (!u) return;
155
155
  const r = requestAnimationFrame(() => {
156
156
  var a;
@@ -212,11 +212,11 @@ function te({
212
212
  {
213
213
  className: v(
214
214
  "flex h-full items-center justify-center text-sm text-muted-foreground",
215
- I
215
+ R
216
216
  ),
217
217
  children: "No files"
218
218
  }
219
- ) : /* @__PURE__ */ i(A.Provider, { value: _, children: /* @__PURE__ */ i("div", { "data-boring-workspace-part": "file-tree", className: v("file-tree", I), children: /* @__PURE__ */ i(
219
+ ) : /* @__PURE__ */ i(A.Provider, { value: _, children: /* @__PURE__ */ i("div", { "data-boring-workspace-part": "file-tree", className: v("file-tree", R), children: /* @__PURE__ */ i(
220
220
  q,
221
221
  {
222
222
  ref: k,