@hachej/boring-workspace 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +94 -0
  3. package/dist/CodeEditor-DQqOn4xz.js +266 -0
  4. package/dist/CommandPalette-aM61U-b0.js +5229 -0
  5. package/dist/FileTree-DRq_bfue.js +245 -0
  6. package/dist/MarkdownEditor-DjiHxnRv.js +349 -0
  7. package/dist/WorkspaceLoadingState-By0dZoPD.js +568 -0
  8. package/dist/agent-tool-NvxKfist.d.ts +28 -0
  9. package/dist/app-front.d.ts +485 -0
  10. package/dist/app-front.js +452 -0
  11. package/dist/app-server.d.ts +53 -0
  12. package/dist/app-server.js +769 -0
  13. package/dist/bootstrapServer-BRUqUpVW.d.ts +66 -0
  14. package/dist/boring-workspace.css +1 -0
  15. package/dist/charts.d.ts +114 -0
  16. package/dist/charts.js +143 -0
  17. package/dist/events.d.ts +178 -0
  18. package/dist/events.js +88 -0
  19. package/dist/explorer-DtLUnuah.d.ts +129 -0
  20. package/dist/panel-DnvDNQac.js +6 -0
  21. package/dist/server.d.ts +84 -0
  22. package/dist/server.js +811 -0
  23. package/dist/shared.d.ts +113 -0
  24. package/dist/shared.js +11 -0
  25. package/dist/testing-e2e.d.ts +68 -0
  26. package/dist/testing-e2e.js +45 -0
  27. package/dist/testing.d.ts +464 -0
  28. package/dist/testing.js +10984 -0
  29. package/dist/utils-B6yFEsav.js +8 -0
  30. package/dist/workspace.css +5780 -0
  31. package/dist/workspace.d.ts +2119 -0
  32. package/dist/workspace.js +1884 -0
  33. package/docs/INTERFACES.md +58 -0
  34. package/docs/PLUGIN_STRUCTURE.md +162 -0
  35. package/docs/README.md +19 -0
  36. package/docs/bridge.md +135 -0
  37. package/docs/panels.md +102 -0
  38. package/docs/plans/GENERIC_EXPLORER_PLUGIN_PLAN.md +455 -0
  39. package/docs/plans/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md +962 -0
  40. package/docs/plans/PLUGIN_OUTPUTS_ISOLATION_PLAN.md +301 -0
  41. package/docs/plans/README.md +9 -0
  42. package/docs/plans/UI_BRIDGE_OWNERSHIP_REFACTOR.md +303 -0
  43. package/docs/plans/archive/CODE_OWNERSHIP_CLEANUP_PLAN.md +387 -0
  44. package/docs/plans/archive/COMMAND_PALETTE_REGISTRY.md +814 -0
  45. package/docs/plans/archive/DECLARATIVE_LAYOUT_MIGRATION.md +277 -0
  46. package/docs/plans/archive/PLUGIN_MODEL.md +3674 -0
  47. package/docs/plans/archive/SRC_FOLDER_REORG_PLAN.md +307 -0
  48. package/docs/plans/archive/UNIFIED_EVENT_BUS.md +647 -0
  49. package/docs/plans/archive/WORKSPACE_V2_PLAN.md +2489 -0
  50. package/docs/plugins.md +158 -0
  51. package/package.json +164 -0
@@ -0,0 +1,455 @@
1
+ # Generic Explorer Plugin Plan
2
+
3
+ Last updated: 2026-05-01
4
+
5
+ Status: draft plan. No code has moved yet.
6
+
7
+ ## Problem
8
+
9
+ Workspace currently has a good generic `DataExplorer` primitive under
10
+ `src/front/components/DataExplorer`. The data-catalog-specific legacy wrappers
11
+ under `src/front/components/data-catalog` have been removed; data catalog behavior
12
+ now lives only in `dataCatalogPlugin`. Feret v2 shows the next requirement: not
13
+ just flat/faceted database rows, but mixed project trees with
14
+ filesystem rows, virtual DB rows, section filters, friendly labels, and domain
15
+ open routing.
16
+
17
+ We need a cleaner ownership model:
18
+
19
+ - `front/` contains only workspace host, chrome, registries, bridge, layout,
20
+ design primitives, and truly generic helpers.
21
+ - `plugins/` owns domain panes, domain data, domain catalogs, domain surface
22
+ resolvers, and reusable plugin-level feature families.
23
+ - Generic explorer behavior should be reusable by many plugins without making
24
+ filesystem depend on data catalog or data catalog depend on filesystem.
25
+
26
+ ## Decision
27
+
28
+ Create a dedicated generic explorer plugin/family:
29
+
30
+ ```txt
31
+ packages/workspace/src/plugins/explorerPlugin/
32
+ index.tsx
33
+ constants.ts
34
+ types.ts
35
+ Explorer.tsx
36
+ ExplorerPane.tsx
37
+ createExplorerOutputs.ts
38
+ adapters.ts
39
+ useExplorerState.ts
40
+ __tests__/
41
+ ```
42
+
43
+ This plugin is not a domain plugin. It is a reusable feature plugin that owns
44
+ explorer panes and explorer row contracts. This adds a third plugin category:
45
+ workspace-owned feature plugins. They live under `src/plugins/` because they
46
+ emit panes/outputs and own a feature contract, but they do not encode an app
47
+ domain like files, data catalog, or Feret. Domain plugins compose it:
48
+
49
+ ```txt
50
+ dataCatalogPlugin -> uses explorerPlugin for generic explorer rendering
51
+ filesystemPlugin -> may use explorerPlugin for project/friendly trees later
52
+ Feret app plugin -> uses explorerPlugin directly for Project Tree and Data/Feret
53
+ future domain plugin -> uses explorerPlugin for symbols/docs/jobs/issues/etc.
54
+ ```
55
+
56
+ Do **not** put generic explorer under `dataCatalogPlugin`. That would make other
57
+ domains depend on a data-catalog name and blur ownership.
58
+
59
+ ## Target Package Shape
60
+
61
+ ```txt
62
+ src/front/
63
+ bridge/ # workspace bridge client/dispatcher only
64
+ chrome/ # shell chrome panes: chat host, artifact surface, workbench left
65
+ components/
66
+ ui/ # shadcn-style primitives
67
+ CommandPalette.tsx # generic workspace command/catalog UI
68
+ ErrorChip.tsx
69
+ PanelErrorBoundary.tsx
70
+ WorkspaceLoadingState.tsx
71
+ recent/ # generic recent command/catalog state
72
+ dock/ # dockview shell/chrome
73
+ events/ # cross-cutting event bus
74
+ hooks/ # generic workspace hooks
75
+ layout/ # generic layouts/top bar
76
+ lib/ # generic utils/validation
77
+ plugin/ # registry consumers/inspector/error boundary
78
+ provider/ # WorkspaceProvider
79
+ registry/ # registries and core registrations
80
+ store/ # layout/workspace UI store only
81
+ testing/
82
+ theme/
83
+ toast/
84
+
85
+ src/plugins/
86
+ explorerPlugin/ # generic explorer feature family
87
+ dataCatalogPlugin/ # data catalog specialization
88
+ filesystemPlugin/ # files/editors/tree specialization
89
+ ```
90
+
91
+ ## Explorer Plugin API Shape
92
+
93
+ ### Row model
94
+
95
+ The current `ExplorerRow` is useful but too flat for Feret project trees. Keep
96
+ flat rows as the base, then add tree/section nodes.
97
+
98
+ ```ts
99
+ export type ExplorerBadge = {
100
+ code: string;
101
+ tooltip?: string;
102
+ tone?: "neutral" | "info" | "success" | "warning" | "danger";
103
+ };
104
+
105
+ export type ExplorerFilterConfig = {
106
+ key: string;
107
+ label: string;
108
+ order?: string[];
109
+ formatValue?: (value: string) => string;
110
+ };
111
+
112
+ export type DragPayload = { mimeType: string; value: string };
113
+
114
+ export type ExplorerItemRow<RowKind extends string = string> = {
115
+ kind?: "item";
116
+ /** Domain/type discriminant used by app plugins for routing. */
117
+ rowKind?: RowKind;
118
+ id: string;
119
+ title: string;
120
+ subtitle?: string;
121
+ group?: string;
122
+ leading?: ExplorerBadge;
123
+ trailing?: ExplorerBadge[];
124
+ meta?: string;
125
+ icon?: React.ComponentType<{ className?: string }>;
126
+ payload?: Record<string, unknown>;
127
+ };
128
+
129
+ export type ExplorerSectionNode = {
130
+ kind: "section";
131
+ id: string;
132
+ title: string;
133
+ subtitle?: string;
134
+ filters?: ExplorerFilterConfig[];
135
+ count?: number;
136
+ defaultExpanded?: boolean;
137
+ };
138
+
139
+ export type ExplorerFolderNode = {
140
+ kind: "folder";
141
+ id: string;
142
+ title: string;
143
+ subtitle?: string;
144
+ count?: number;
145
+ defaultExpanded?: boolean;
146
+ };
147
+
148
+ export type ExplorerNode =
149
+ | ExplorerItemRow
150
+ | ExplorerSectionNode
151
+ | ExplorerFolderNode;
152
+ ```
153
+
154
+ ### Flat adapter
155
+
156
+ Keep the current adapter semantics for global catalogs and data tabs.
157
+
158
+ ```ts
159
+ export type ExplorerSearchArgs = {
160
+ query: string;
161
+ filters: Record<string, string[]>;
162
+ group?: { key: string; value: string };
163
+ limit: number;
164
+ offset: number;
165
+ signal?: AbortSignal;
166
+ };
167
+
168
+ export type ExplorerSearchResult = {
169
+ items: ExplorerItemRow[];
170
+ total: number;
171
+ hasMore: boolean;
172
+ };
173
+
174
+ export type ExplorerFacetValue = { value: string; count: number };
175
+ export type ExplorerFacets = Record<string, ExplorerFacetValue[]>;
176
+ export type ExplorerFacetsArgs = {
177
+ filters: Record<string, string[]>;
178
+ signal?: AbortSignal;
179
+ };
180
+
181
+ export type ExplorerFlatAdapter = {
182
+ search(args: ExplorerSearchArgs): Promise<ExplorerSearchResult>;
183
+ fetchFacets?(args: ExplorerFacetsArgs): Promise<ExplorerFacets>;
184
+ };
185
+ ```
186
+
187
+ ### Tree adapter
188
+
189
+ Add tree/provider support for Feret Project Tree and future friendly project
190
+ views.
191
+
192
+ ```ts
193
+ export type ExplorerTreeArgs = {
194
+ query: string;
195
+ /** Global tree filters, if the host exposes any. */
196
+ filters: Record<string, string[]>;
197
+ /** Filters scoped to the parent/section being loaded. */
198
+ scopedFilters?: Record<string, string[]>;
199
+ parentId?: string;
200
+ limit: number;
201
+ offset: number;
202
+ signal?: AbortSignal;
203
+ };
204
+
205
+ export type ExplorerTreeResult = {
206
+ nodes: ExplorerNode[];
207
+ total?: number;
208
+ hasMore?: boolean;
209
+ };
210
+
211
+ export type ExplorerTreeAdapter = {
212
+ roots(args: ExplorerTreeArgs): Promise<ExplorerTreeResult>;
213
+ children(
214
+ parentId: string,
215
+ args: ExplorerTreeArgs,
216
+ ): Promise<ExplorerTreeResult>;
217
+ fetchNodeFacets?(
218
+ nodeId: string,
219
+ args: ExplorerFacetsArgs,
220
+ ): Promise<ExplorerFacets>;
221
+ };
222
+ ```
223
+
224
+ ### Component/pane outputs
225
+
226
+ ```ts
227
+ type ExplorerBaseProps = {
228
+ facets?: ExplorerFilterConfig[];
229
+ groupBy?: string;
230
+ onActivate?: (row: ExplorerItemRow) => void;
231
+ getDragPayload?: (row: ExplorerItemRow) => DragPayload | null | undefined;
232
+ emptyState?: React.ReactNode;
233
+ searchPlaceholder?: string;
234
+ query?: string;
235
+ searchable?: boolean;
236
+ className?: string;
237
+ };
238
+
239
+ export type ExplorerProps =
240
+ | (ExplorerBaseProps & {
241
+ mode?: "flat";
242
+ flatAdapter: ExplorerFlatAdapter;
243
+ treeAdapter?: never;
244
+ })
245
+ | (ExplorerBaseProps & {
246
+ mode: "tree";
247
+ treeAdapter: ExplorerTreeAdapter;
248
+ flatAdapter?: never;
249
+ })
250
+ | (ExplorerBaseProps & {
251
+ /** Auto means: use tree when only treeAdapter is provided, else flat. */
252
+ mode: "auto";
253
+ flatAdapter?: ExplorerFlatAdapter;
254
+ treeAdapter?: ExplorerTreeAdapter;
255
+ });
256
+
257
+ export type CreateExplorerOutputsOptions = ExplorerProps & {
258
+ id: string;
259
+ label: string;
260
+ leftTabId?: string;
261
+ includeLeftTab?: boolean;
262
+ panelId?: string;
263
+ includePanel?: boolean;
264
+ /** Existing workspace core panel config source type. */
265
+ source?: PanelConfig["source"];
266
+ };
267
+ ```
268
+
269
+ `createExplorerOutputs()` emits generic left-tab/panel outputs only. It does not
270
+ emit data-catalog catalogs or data-catalog surface resolvers.
271
+
272
+ Implementation must validate adapter/mode combinations at runtime too:
273
+
274
+ - `flat` requires `flatAdapter`.
275
+ - `tree` requires `treeAdapter`.
276
+ - `auto` chooses `tree` only when `treeAdapter` exists and `flatAdapter` does
277
+ not; otherwise it chooses `flat`.
278
+ - missing adapters throw a development-facing invariant error rather than
279
+ rendering an empty explorer. The `auto` union may be tightened to require at
280
+ least one adapter during implementation; runtime validation remains required
281
+ either way.
282
+
283
+ ## Data Catalog After Refactor
284
+
285
+ `dataCatalogPlugin` becomes a specialization that composes explorer outputs:
286
+
287
+ ```txt
288
+ dataCatalogPlugin/
289
+ index.tsx # createDataCatalogPlugin, appendDataCatalogOutputs
290
+ constants.ts
291
+ types.ts
292
+ catalogs.ts # data catalog catalog config helpers
293
+ surfaceResolver.ts
294
+ openVisualization.ts
295
+ hooks.ts
296
+ server/
297
+ ```
298
+
299
+ Responsibilities:
300
+
301
+ - choose data-catalog defaults: labels, empty state, search placeholder
302
+ - register `CatalogConfig`
303
+ - default `onSelect` posts `openSurface`
304
+ - create row visualization panel/resolver when requested
305
+ - provide server agent tool helpers for querying a catalog adapter
306
+
307
+ Non-responsibilities:
308
+
309
+ - own generic explorer rendering
310
+ - own generic explorer row state machine
311
+ - own filesystem/project tree behavior
312
+
313
+ Legacy `src/front/components/data-catalog` is removed. Consumers should use
314
+ `createDataCatalogPlugin()` / `appendDataCatalogOutputs()` or compose
315
+ `DataExplorer` directly until it moves to `explorerPlugin`.
316
+
317
+ ## Feret v2 Requirements Driving Explorer Plugin
318
+
319
+ Feret needs two explorer usages.
320
+
321
+ ### Data/Feret tab
322
+
323
+ Flat/faceted data explorer:
324
+
325
+ - DB/API-backed ingredient/document/formulation rows
326
+ - search input
327
+ - category/status/supplier facets
328
+ - row badges and cost/status metadata
329
+ - open row into domain pane
330
+ - drag ingredients into formulation builder later
331
+
332
+ This maps to `ExplorerFlatAdapter`.
333
+
334
+ ### Feret Project Tree
335
+
336
+ Mixed tree explorer:
337
+
338
+ - real markdown/source files
339
+ - virtual DB rows: ingredient, formulation, process, scenario
340
+ - sections: Brief, Sources, Ingredients, Formulations, Notes, Reports
341
+ - section-local filters
342
+ - friendly labels hiding technical paths/extensions
343
+ - click routing by row type:
344
+ - markdown -> filesystem markdown editor/open file
345
+ - source file -> source preview/extraction review
346
+ - ingredient -> ingredient detail pane
347
+ - formulation -> formulation builder/detail pane
348
+
349
+ This maps to `ExplorerTreeAdapter` plus row `payload`/`kind` routing controlled
350
+ by the Feret app plugin. Section-local filters must stay scoped: the explorer
351
+ state tracks active filters per section/folder id and passes them to
352
+ `children(parentId, { scopedFilters })` rather than flattening them into global
353
+ filters.
354
+
355
+ ## Current Component Ownership Audit
356
+
357
+ | Current location | Decision | Rationale |
358
+ | --------------------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
359
+ | `front/components/DataExplorer/*` | Move to `plugins/explorerPlugin/*` | Generic feature family, but pane-capable and adapter-owned; multiple plugins depend on it. |
360
+ | `front/components/data-catalog/*` | Removed | Data-catalog naming is domain-ish; real data catalog behavior is plugin-owned already. |
361
+ | `front/components/CommandPalette.tsx` | Keep in `front/components` | Generic workspace chrome over command/catalog registries, not a domain plugin. |
362
+ | `front/components/recent/*` | Keep in `front/components` for now | Generic command/catalog recent state used by CommandPalette. Could become `commandPalette` subfolder later. |
363
+ | `front/components/ErrorChip.tsx` | Keep generic | Generic error UI primitive. |
364
+ | `front/components/PanelErrorBoundary.tsx` | Keep generic | Generic plugin/panel safety boundary. |
365
+ | `front/components/WorkspaceLoadingState.tsx` | Keep generic | Generic shell loading UI. |
366
+ | `front/components/SessionList.tsx` | Re-evaluate after checking package-root consumers and whether it is only used by `SessionBrowser` | If it is just session chrome, consolidate under `front/chrome/session-list`; if consumers need a standalone primitive, keep a re-exported primitive. |
367
+ | `@boring/ui/*` | Keep generic | Design primitives only. |
368
+ | `front/chrome/artifact-surface/*` | Keep in front chrome | Core workspace surface host, not plugin domain. |
369
+ | `front/chrome/chat/*` | Keep in front chrome | Chat host injection point, not agent/plugin domain. |
370
+ | `front/chrome/empty-pane/*` | Keep or move to core chrome | Generic shell fallback. Not a domain plugin. |
371
+ | `front/chrome/session-list/*` | Keep in front chrome, maybe consolidate `SessionList` here | Session chrome is host-level. |
372
+ | `front/chrome/workbench-left/*` | Keep in front chrome | Renders plugin left-tabs; host chrome. |
373
+ | `plugins/filesystemPlugin/front/file-tree/*` | Keep in filesystem plugin for now | File-specific tree, path behavior, events, and open-file routing are domain-owned. May reuse explorer tree later. |
374
+ | `plugins/filesystemPlugin/front/code-editor/*` | Keep in filesystem plugin | File editor domain and file data hooks. |
375
+ | `plugins/filesystemPlugin/front/markdown-editor/*` | Keep in filesystem plugin | File editor domain and file data hooks. |
376
+ | `plugins/filesystemPlugin/front/empty-file-panel/*` | Keep in filesystem plugin | File-specific empty panel. |
377
+ | `plugins/dataCatalogPlugin/front/*` | Keep plugin-owned, but import explorer from `explorerPlugin` | Domain specialization. |
378
+
379
+ ## Public API Migration
380
+
381
+ Avoid breaking current consumers in the first pass.
382
+
383
+ 1. Add `plugins/explorerPlugin` and re-export explorer APIs from old package
384
+ root names:
385
+ - `DataExplorer` stays exported, but source moves.
386
+ - `ExplorerRow`, `ExplorerAdapter`, `FacetConfig`, etc. stay exported with
387
+ compatibility aliases.
388
+ 2. Keep `front/components/DataExplorer/index.ts` as a thin deprecated re-export
389
+ for one release if internal import churn is high.
390
+ 3. `DataCatalog` and `DataCatalogPane` root exports are removed now; use data catalog plugin helpers instead.
391
+ 4. Move app/plugin imports to `plugins/explorerPlugin` or package-root exports.
392
+
393
+ ## Implementation Beads
394
+
395
+ ### Bead 1 — Explorer plugin skeleton
396
+
397
+ - Add `src/plugins/explorerPlugin` with current `DataExplorer` code moved mostly
398
+ intact.
399
+ - Export compatibility aliases.
400
+ - Update internal imports from `front/components/DataExplorer` to
401
+ `plugins/explorerPlugin`.
402
+ - Tests: current DataExplorer tests pass unchanged after path update.
403
+
404
+ ### Bead 2 — Data catalog plugin composition
405
+
406
+ - Change `dataCatalogPlugin` to import explorer APIs from `explorerPlugin`.
407
+ - Split data catalog helpers into `catalogs.ts` if useful.
408
+ - Keep `front/components/data-catalog` removed; do not add compatibility wrappers back.
409
+ - Tests: dataCatalogPlugin tests and public API tests.
410
+
411
+ ### Bead 3 — Tree adapter design
412
+
413
+ - Add `ExplorerTreeAdapter`, `ExplorerNode`, section/folder node types.
414
+ - Add tree state hook or extend explorer state carefully, including per-section
415
+ filter maps that flow into `ExplorerTreeArgs.scopedFilters`.
416
+ - Do not rewrite filesystem tree yet.
417
+ - Tests: adapter contract, expansion, per-node loading, abort handling.
418
+
419
+ ### Bead 4 — Feret project tree spike
420
+
421
+ - In Feret v2/app playground, build a project tree using `ExplorerTreeAdapter`.
422
+ - Prove mixed rows and section filters.
423
+ - Keep row activation domain-owned by Feret plugin.
424
+ - Output is either a shippable Feret integration PR or a plan update with the
425
+ gaps found during the spike.
426
+
427
+ ### Bead 5 — Optional filesystem reuse
428
+
429
+ - Evaluate whether filesystem tree should remain custom or wrap explorer tree.
430
+ - Decision trigger: after the Feret project tree spike, compare required file
431
+ tree behavior (path validation, file events, expand-to-file, selection, editor
432
+ lifecycle) against explorer tree behavior.
433
+ - Exit criteria: record either `keep custom filesystem tree` or `migrate
434
+ filesystem tree to explorer tree` before the legacy data-catalog removal bead.
435
+ - Only migrate if benefits outweigh loss of file-tree-specific behavior.
436
+
437
+ ## Non-goals
438
+
439
+ - Do not make every visual component a plugin. `@boring/ui`, layout,
440
+ chrome hosts, registries, bridge, and provider stay front-owned.
441
+ - Do not force filesystem to depend on data catalog.
442
+ - Do not rewrite file tree in the first bead.
443
+ - Do not remove public exports without an explicit breaking cleanup bead.
444
+ - Do not allow reverse imports from `explorerPlugin` into domain plugins. Add or
445
+ extend invariant lint so allowed direction is domain plugin -> explorerPlugin,
446
+ never explorerPlugin -> dataCatalogPlugin/filesystemPlugin/app plugins.
447
+
448
+ ## Acceptance Criteria
449
+
450
+ - `front/` contains only generic host/chrome/primitives after migration.
451
+ - Domain-named components do not live under `front/components`.
452
+ - Explorer APIs are reusable by data catalog, filesystem, and Feret without
453
+ cross-domain plugin imports.
454
+ - Existing public API remains source-compatible until an approved breaking bead.
455
+ - Feret Project Tree requirements are representable without forking explorer UI.