@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,113 @@
1
+ export { C as CommandResult, E as ExplorerAdapter, c as ExplorerRow, S as SearchResult, U as UiBridge, a as UiCommand, b as UiState } from './explorer-DtLUnuah.js';
2
+ import { ComponentType } from 'react';
3
+ import { DockviewPanelApi, DockviewApi } from 'dockview-react';
4
+ export { A as AgentTool, J as JSONSchema, T as ToolExecContext, a as ToolResult } from './agent-tool-NvxKfist.js';
5
+
6
+ /**
7
+ * Shared panel and command types — no runtime deps beyond React/dockview.
8
+ *
9
+ * Importable from BOTH front and server bundles without dragging in
10
+ * platform-specific code.
11
+ */
12
+
13
+ /**
14
+ * Unified prop shape for panel components rendered inside DockviewShell.
15
+ *
16
+ * Structurally mirrors dockview's `IDockviewPanelProps<T>` so dockview
17
+ * can render registered components directly — no wrapper, no field
18
+ * renaming, no `as` casts. We re-state the shape (rather than re-export
19
+ * dockview's type) so the workspace package owns its public contract:
20
+ * if dockview's type ever drifts, only the wiring inside DockviewShell
21
+ * needs to change.
22
+ *
23
+ * Use {@link definePanel} for type-safe registration.
24
+ *
25
+ * @typeParam T - Shape of the panel-specific `params` payload. Defaults
26
+ * to `unknown` because layouts restored from JSON are inherently
27
+ * un-typed at runtime; use a generic param when you control the
28
+ * addPanel call site, otherwise read defensively.
29
+ */
30
+ interface PaneProps<T = unknown> {
31
+ /** App-supplied data for this panel instance (e.g. `{ path: string }`). */
32
+ params: T;
33
+ /** Per-panel control surface (close, setActive, setTitle, …). */
34
+ api: DockviewPanelApi;
35
+ /** Top-level dockview API (groups, addPanel, removePanel, fromJSON, …). */
36
+ containerApi: DockviewApi;
37
+ /** Optional className forwarded to the pane's root element. */
38
+ className?: string;
39
+ }
40
+ interface PanelConfig<T = any> {
41
+ id: string;
42
+ title: string;
43
+ icon?: ComponentType<{
44
+ className?: string;
45
+ }>;
46
+ /** Placement hint: "left" | "center" | "right" | "bottom" | "left-tab" | "right-tab" */
47
+ placement?: string;
48
+ requiresCapabilities?: string[];
49
+ essential?: boolean;
50
+ chromeless?: boolean;
51
+ /** Source: "builtin" | "app" */
52
+ source?: string;
53
+ pluginId?: string;
54
+ /**
55
+ * Whether to wrap the component with React.lazy + Suspense. Omit to let
56
+ * the registry auto-detect: zero-arg functions (factories) are treated as
57
+ * lazy; components that accept a props argument are treated as eager.
58
+ */
59
+ lazy?: boolean;
60
+ component: ComponentType<PaneProps<T>> | (() => Promise<{
61
+ default: ComponentType<PaneProps<T>>;
62
+ }>);
63
+ }
64
+ type PanelRegistration<T = any> = Omit<PanelConfig<T>, 'id'>;
65
+ /**
66
+ * Identity helper for type-safe panel registration. Pure runtime
67
+ * passthrough — the value of this is forcing TypeScript to verify that
68
+ * the registered component accepts the params shape declared on the
69
+ * config. Without it, apps tend to widen `component` to
70
+ * `ComponentType<PaneProps<unknown>>` and lose the link.
71
+ *
72
+ * ```ts
73
+ * const editorPanel = definePanel<{ path: string }>({
74
+ * id: "code-editor",
75
+ * title: "Editor",
76
+ * component: CodeEditorPane, // typechecked against PaneProps<{ path: string }>
77
+ * placement: "center",
78
+ * })
79
+ * ```
80
+ */
81
+ declare function definePanel<T = unknown>(config: PanelConfig<T>): PanelConfig<T>;
82
+ interface CommandConfig {
83
+ id: string;
84
+ title: string;
85
+ run: () => void;
86
+ keywords?: string[];
87
+ shortcut?: string;
88
+ when?: () => boolean;
89
+ pluginId?: string;
90
+ }
91
+
92
+ declare const WORKSPACE_OPEN_PATH_SURFACE_KIND = "workspace.open.path";
93
+ interface SurfaceOpenRequest {
94
+ kind: string;
95
+ target: string;
96
+ meta?: Record<string, unknown>;
97
+ }
98
+ interface SurfacePanelResolution {
99
+ component: string;
100
+ id?: string;
101
+ title?: string;
102
+ params?: Record<string, unknown>;
103
+ score?: number;
104
+ }
105
+ interface SurfaceResolverConfig {
106
+ id: string;
107
+ resolve: (request: SurfaceOpenRequest) => SurfacePanelResolution | undefined;
108
+ source?: string;
109
+ pluginId?: string;
110
+ }
111
+ type SurfaceResolverRegistration = Omit<SurfaceResolverConfig, "id">;
112
+
113
+ export { type CommandConfig, type PaneProps, type PanelConfig, type PanelRegistration, type SurfaceOpenRequest, type SurfacePanelResolution, type SurfaceResolverConfig, type SurfaceResolverRegistration, WORKSPACE_OPEN_PATH_SURFACE_KIND, definePanel };
package/dist/shared.js ADDED
@@ -0,0 +1,11 @@
1
+ // src/shared/types/surface.ts
2
+ var WORKSPACE_OPEN_PATH_SURFACE_KIND = "workspace.open.path";
3
+
4
+ // src/shared/types/panel.ts
5
+ function definePanel(config) {
6
+ return config;
7
+ }
8
+ export {
9
+ WORKSPACE_OPEN_PATH_SURFACE_KIND,
10
+ definePanel
11
+ };
@@ -0,0 +1,68 @@
1
+ import { Page } from '@playwright/test';
2
+
3
+ /**
4
+ * Land on the app with a clean localStorage so persistence-sensitive tests
5
+ * start from defaults. Pre-opens the workbench surface unless told
6
+ * otherwise — see `openWorkbenchAtBoot`.
7
+ *
8
+ * Also drains the server-side bridge command queue. The bridge is process-
9
+ * global on a shared dev server (E2E_EXTERNAL_SERVER=1); without an
10
+ * explicit drain, a leftover command from a previous test (e.g. an
11
+ * openPanel posted just before that test ended) gets re-delivered to the
12
+ * next test's SSE subscriber and mounts an unexpected pane. The drain is
13
+ * a no-op when the queue is already empty.
14
+ */
15
+ export declare function bootClean(page: Page, opts: BootCleanOptions): Promise<void>;
16
+
17
+ export declare interface BootCleanOptions {
18
+ /**
19
+ * Storage prefix used by declarative chat shells. The
20
+ * bootClean default is to pre-seed `${shellKey}:surface=1` so the
21
+ * workbench mounts at boot — required for any test that posts an
22
+ * openPanel via the bridge (the dispatcher early-returns when no
23
+ * surface is mounted).
24
+ */
25
+ shellKey: string;
26
+ /** Extra localStorage entries to seed after the clear. */
27
+ seed?: Record<string, string>;
28
+ /**
29
+ * If false, surface=1 is NOT pre-seeded — the test starts with the
30
+ * workbench closed. Default: true.
31
+ */
32
+ openWorkbenchAtBoot?: boolean;
33
+ /** Vite + dockview cold-mount allowance. Default 2500ms. */
34
+ mountSettleMs?: number;
35
+ }
36
+
37
+ /**
38
+ * Push an openPanel command through the workspace UI bridge, then poll
39
+ * until the corresponding dockview tab actually appears. The chat shell
40
+ * receives commands over an SSE stream; subscribe latency is
41
+ * non-deterministic (cold Vite, ClickHouse warm-up, EventSource reconnect
42
+ * budget), so we wait on the rendered tab rather than a fixed sleep.
43
+ */
44
+ export declare function openPaneViaBridge(page: Page, cfg: OpenPaneViaBridgeConfig): Promise<void>;
45
+
46
+ export declare interface OpenPaneViaBridgeConfig {
47
+ /** Tab/panel instance id (e.g. `"chart:CPIAUCSL"`, `"deck:intro.md"`). */
48
+ id: string;
49
+ /** Registered panel component id. Must appear in `extraPanels`. */
50
+ component: string;
51
+ /** Tab title to wait for. */
52
+ title: string;
53
+ /** Forwarded to the panel component as its `params`. */
54
+ params: Record<string, unknown>;
55
+ /** Override the dockview-tab wait timeout. Default 10s. */
56
+ paneMountTimeoutMs?: number;
57
+ }
58
+
59
+ /**
60
+ * Ensure the workbench surface is OPEN. Idempotent — checks the persisted
61
+ * flag rather than blindly toggling, so it's safe to call after bootClean
62
+ * (which pre-seeds the open flag by default) or independently.
63
+ */
64
+ export declare function openWorkbench(page: Page, opts: {
65
+ shellKey: string;
66
+ }): Promise<void>;
67
+
68
+ export { }
@@ -0,0 +1,45 @@
1
+ async function r(t, e) {
2
+ try {
3
+ await t.context().request.get("/api/v1/ui/commands/next?poll=true", {
4
+ timeout: 2e3
5
+ });
6
+ } catch {
7
+ }
8
+ const a = { ...e.seed ?? {} };
9
+ e.openWorkbenchAtBoot !== !1 && (a[`${e.shellKey}:surface`] = "1"), await t.addInitScript((o) => {
10
+ try {
11
+ localStorage.clear();
12
+ for (const [n, i] of Object.entries(o))
13
+ localStorage.setItem(n, i);
14
+ } catch {
15
+ }
16
+ }, a), await t.goto("/", { waitUntil: "domcontentloaded" }), await t.waitForTimeout(e.mountSettleMs ?? 2500);
17
+ }
18
+ async function c(t, e) {
19
+ await t.evaluate(
20
+ (o) => localStorage.getItem(`${o}:surface`) === "1",
21
+ e.shellKey
22
+ ) || (await t.locator("body").click({ position: { x: 750, y: 300 } }), await t.keyboard.press("Meta+2"), await t.waitForTimeout(800));
23
+ }
24
+ async function l(t, e) {
25
+ if (await t.waitForTimeout(700), !await t.evaluate(async (o) => (await fetch("/api/v1/ui/commands", {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/json" },
28
+ body: JSON.stringify({ kind: "openPanel", params: o })
29
+ })).ok, e)) throw new Error(`bridge openPanel failed for ${e.id}`);
30
+ await t.waitForFunction(
31
+ (o) => {
32
+ const n = document.querySelectorAll(".dv-tab");
33
+ for (const i of n)
34
+ if ((i.textContent ?? "").includes(o)) return !0;
35
+ return !1;
36
+ },
37
+ e.title,
38
+ { timeout: e.paneMountTimeoutMs ?? 1e4 }
39
+ ), await t.waitForTimeout(800);
40
+ }
41
+ export {
42
+ r as bootClean,
43
+ l as openPaneViaBridge,
44
+ c as openWorkbench
45
+ };
@@ -0,0 +1,464 @@
1
+ import { ComponentType } from 'react';
2
+ import { DockviewApi } from 'dockview-react';
3
+ import { DockviewPanelApi } from 'dockview-react';
4
+ import { JSX } from 'react/jsx-runtime';
5
+ import { Page } from '@playwright/test';
6
+ import { ReactElement } from 'react';
7
+ import { ReactNode } from 'react';
8
+ import { RenderOptions } from '@testing-library/react';
9
+ import { RenderResult } from '@testing-library/react';
10
+
11
+ declare type Badge = {
12
+ /** 1–4 char mono code rendered as a chip. */
13
+ code: string;
14
+ tooltip?: string;
15
+ };
16
+
17
+ /**
18
+ * Land on the app with a clean localStorage so persistence-sensitive tests
19
+ * start from defaults. Pre-opens the workbench surface unless told
20
+ * otherwise — see `openWorkbenchAtBoot`.
21
+ *
22
+ * Also drains the server-side bridge command queue. The bridge is process-
23
+ * global on a shared dev server (E2E_EXTERNAL_SERVER=1); without an
24
+ * explicit drain, a leftover command from a previous test (e.g. an
25
+ * openPanel posted just before that test ended) gets re-delivered to the
26
+ * next test's SSE subscriber and mounts an unexpected pane. The drain is
27
+ * a no-op when the queue is already empty.
28
+ */
29
+ export declare function bootClean(page: Page, opts: BootCleanOptions): Promise<void>;
30
+
31
+ export declare interface BootCleanOptions {
32
+ /**
33
+ * Storage prefix used by declarative chat shells. The
34
+ * bootClean default is to pre-seed `${shellKey}:surface=1` so the
35
+ * workbench mounts at boot — required for any test that posts an
36
+ * openPanel via the bridge (the dispatcher early-returns when no
37
+ * surface is mounted).
38
+ */
39
+ shellKey: string;
40
+ /** Extra localStorage entries to seed after the clear. */
41
+ seed?: Record<string, string>;
42
+ /**
43
+ * If false, surface=1 is NOT pre-seeded — the test starts with the
44
+ * workbench closed. Default: true.
45
+ */
46
+ openWorkbenchAtBoot?: boolean;
47
+ /** Vite + dockview cold-mount allowance. Default 2500ms. */
48
+ mountSettleMs?: number;
49
+ }
50
+
51
+ declare interface BridgeEventMap {
52
+ "panel:opened": {
53
+ panelId: string;
54
+ params: Record<string, unknown>;
55
+ };
56
+ "panel:closed": {
57
+ panelId: string;
58
+ };
59
+ "panel:activated": {
60
+ panelId: string;
61
+ previousPanelId: string | null;
62
+ };
63
+ "file:opened": {
64
+ path: string;
65
+ mode: "view" | "edit" | "diff";
66
+ };
67
+ "file:saved": {
68
+ path: string;
69
+ };
70
+ "file:dirty": {
71
+ path: string;
72
+ dirty: boolean;
73
+ };
74
+ "sidebar:toggled": {
75
+ collapsed: boolean;
76
+ };
77
+ "tree:expand": {
78
+ path: string;
79
+ };
80
+ "notification:shown": {
81
+ message: string;
82
+ level: "info" | "warn" | "error";
83
+ };
84
+ "pane:error": {
85
+ panelId: string;
86
+ error: string;
87
+ stack?: string;
88
+ };
89
+ }
90
+
91
+ declare interface CommandResult {
92
+ seq: number;
93
+ status: "ok" | "error";
94
+ error?: {
95
+ code: string;
96
+ message: string;
97
+ };
98
+ }
99
+
100
+ export declare function createLocalStorageSessions(opts?: CreateLocalStorageSessionsOptions): MockSessionsStore;
101
+
102
+ export declare interface CreateLocalStorageSessionsOptions {
103
+ /** localStorage key prefix. Defaults to `"workspace:sessions"`. */
104
+ storageKey?: string;
105
+ /**
106
+ * Seed used the first time we run (no value at `storageKey` yet).
107
+ * Defaults to a single "New session" row with `id = "s${Date.now()}"`.
108
+ */
109
+ initial?: () => MockSessionsState;
110
+ }
111
+
112
+ export declare function createMockBridge(options?: CreateMockBridgeOptions): MockWorkspaceBridge;
113
+
114
+ export declare interface CreateMockBridgeOptions {
115
+ state?: Partial<MockBridgeState>;
116
+ fn?: SpyFactory;
117
+ }
118
+
119
+ export declare interface CreateMockPaneOptions<T> {
120
+ params: T;
121
+ panelId?: string;
122
+ className?: string;
123
+ /** Override individual `api` fields without re-stubbing the whole surface. */
124
+ apiOverrides?: Partial<PaneProps<T>["api"]>;
125
+ /** Override individual `containerApi` fields. */
126
+ containerApiOverrides?: Partial<PaneProps<T>["containerApi"]>;
127
+ }
128
+
129
+ export declare function createMockPaneProps<T>(optsOrParams: T | CreateMockPaneOptions<T>): PaneProps<T>;
130
+
131
+ export declare function createMockRegistry(options?: CreateMockRegistryOptions): PanelRegistry;
132
+
133
+ export declare interface CreateMockRegistryOptions {
134
+ panels?: PanelConfig[];
135
+ capabilities?: Record<string, boolean>;
136
+ }
137
+
138
+ export declare function createMockSeriesAdapter(): ExplorerAdapter;
139
+
140
+ export declare function createMockSessions(opts?: CreateMockSessionsOptions): MockSessionsStore;
141
+
142
+ export declare interface CreateMockSessionsOptions {
143
+ /** Initial sessions. Defaults to a 5-row demo set with descending updatedAt. */
144
+ initial?: SessionItem[];
145
+ /** Initial active session id. Defaults to the first session's id. */
146
+ activeId?: string;
147
+ }
148
+
149
+ export declare function createMockTablesAdapter(): ExplorerAdapter;
150
+
151
+ declare interface DynamicPaneConfig {
152
+ id: string;
153
+ component: string;
154
+ params?: Record<string, unknown>;
155
+ title?: string;
156
+ }
157
+
158
+ declare type ExplorerAdapter = {
159
+ search(args: SearchArgs): Promise<SearchResult>;
160
+ /** Optional. When omitted, the explorer renders flat (no facet popover). */
161
+ fetchFacets?(args: FacetsArgs): Promise<Facets>;
162
+ };
163
+
164
+ declare type ExplorerRow = {
165
+ id: string;
166
+ title: string;
167
+ /** Optional muted second line (truncates with title). */
168
+ subtitle?: string;
169
+ /** Group key — must match one of the facet values for `groupBy`. */
170
+ group?: string;
171
+ /** Leading mono chip (e.g. type code, frequency). */
172
+ leading?: Badge;
173
+ /** Trailing mono chips for status flags (e.g. [D] derived, [LIVE]). */
174
+ trailing?: Badge[];
175
+ /** Right-aligned plain text for numeric metadata (e.g. "1.2M", "2.4s"). */
176
+ meta?: string;
177
+ };
178
+
179
+ declare type Facets = Record<string, FacetValue[]>;
180
+
181
+ declare type FacetsArgs = {
182
+ filters: Record<string, string[]>;
183
+ signal?: AbortSignal;
184
+ };
185
+
186
+ declare type FacetValue = {
187
+ value: string;
188
+ count: number;
189
+ };
190
+
191
+ export declare interface MockBridgeState {
192
+ openPanels: PanelState[];
193
+ activeFile: string | null;
194
+ dirtyFiles: string[];
195
+ visibleFiles: string[];
196
+ }
197
+
198
+ export declare interface MockDataFixtures {
199
+ files?: MockFileFixture[];
200
+ }
201
+
202
+ export declare interface MockFileFixture {
203
+ path: string;
204
+ content: string;
205
+ mtimeMs?: number;
206
+ }
207
+
208
+ export declare interface MockSessionsState {
209
+ sessions: SessionItem[];
210
+ activeId: string;
211
+ }
212
+
213
+ export declare interface MockSessionsStore {
214
+ getState: () => MockSessionsState;
215
+ subscribe: (fn: () => void) => () => void;
216
+ switchTo: (id: string) => void;
217
+ create: () => void;
218
+ remove: (id: string) => void;
219
+ }
220
+
221
+ export declare type MockWorkspaceBridge = WorkspaceBridge & {
222
+ emit<K extends keyof BridgeEventMap>(event: K, payload: BridgeEventMap[K]): void;
223
+ setState(next: Partial<MockBridgeState>): void;
224
+ getStateSnapshot(): MockBridgeState;
225
+ };
226
+
227
+ declare interface Notification_2 {
228
+ id: string;
229
+ message: string;
230
+ type: "info" | "warning" | "error";
231
+ timestamp: number;
232
+ }
233
+
234
+ /**
235
+ * Push an openPanel command through the workspace UI bridge, then poll
236
+ * until the corresponding dockview tab actually appears. The chat shell
237
+ * receives commands over an SSE stream; subscribe latency is
238
+ * non-deterministic (cold Vite, ClickHouse warm-up, EventSource reconnect
239
+ * budget), so we wait on the rendered tab rather than a fixed sleep.
240
+ */
241
+ export declare function openPaneViaBridge(page: Page, cfg: OpenPaneViaBridgeConfig): Promise<void>;
242
+
243
+ export declare interface OpenPaneViaBridgeConfig {
244
+ /** Tab/panel instance id (e.g. `"chart:CPIAUCSL"`, `"deck:intro.md"`). */
245
+ id: string;
246
+ /** Registered panel component id. Must appear in `extraPanels`. */
247
+ component: string;
248
+ /** Tab title to wait for. */
249
+ title: string;
250
+ /** Forwarded to the panel component as its `params`. */
251
+ params: Record<string, unknown>;
252
+ /** Override the dockview-tab wait timeout. Default 10s. */
253
+ paneMountTimeoutMs?: number;
254
+ }
255
+
256
+ /**
257
+ * Ensure the workbench surface is OPEN. Idempotent — checks the persisted
258
+ * flag rather than blindly toggling, so it's safe to call after bootClean
259
+ * (which pre-seeds the open flag by default) or independently.
260
+ */
261
+ export declare function openWorkbench(page: Page, opts: {
262
+ shellKey: string;
263
+ }): Promise<void>;
264
+
265
+ declare interface PanelConfig<T = any> {
266
+ id: string;
267
+ title: string;
268
+ icon?: ComponentType<{
269
+ className?: string;
270
+ }>;
271
+ /** Placement hint: "left" | "center" | "right" | "bottom" | "left-tab" | "right-tab" */
272
+ placement?: string;
273
+ requiresCapabilities?: string[];
274
+ essential?: boolean;
275
+ chromeless?: boolean;
276
+ /** Source: "builtin" | "app" */
277
+ source?: string;
278
+ pluginId?: string;
279
+ /**
280
+ * Whether to wrap the component with React.lazy + Suspense. Omit to let
281
+ * the registry auto-detect: zero-arg functions (factories) are treated as
282
+ * lazy; components that accept a props argument are treated as eager.
283
+ */
284
+ lazy?: boolean;
285
+ component: ComponentType<PaneProps<T>> | (() => Promise<{
286
+ default: ComponentType<PaneProps<T>>;
287
+ }>);
288
+ }
289
+
290
+ declare type PanelRegistration<T = any> = Omit<PanelConfig<T>, 'id'>;
291
+
292
+ declare class PanelRegistry {
293
+ private panels;
294
+ private registrationOrder;
295
+ private capabilities;
296
+ private listeners;
297
+ private snapshotCache;
298
+ constructor(capabilities?: Record<string, boolean>);
299
+ register(id: string, config: PanelRegistration): void;
300
+ unregisterByPluginId(pluginId: string): void;
301
+ get(id: string): PanelConfig | undefined;
302
+ has(id: string): boolean;
303
+ list(): PanelConfig[];
304
+ getComponents(): Record<string, ComponentType<any>>;
305
+ subscribe: (cb: () => void) => (() => void);
306
+ getSnapshot: () => readonly PanelConfig[];
307
+ private emit;
308
+ private filteredPanels;
309
+ private satisfiesCapabilities;
310
+ }
311
+
312
+ declare interface PanelState {
313
+ id: string;
314
+ component: string;
315
+ params?: Record<string, unknown>;
316
+ groupId?: string;
317
+ essential?: boolean;
318
+ }
319
+
320
+ /**
321
+ * Unified prop shape for panel components rendered inside DockviewShell.
322
+ *
323
+ * Structurally mirrors dockview's `IDockviewPanelProps<T>` so dockview
324
+ * can render registered components directly — no wrapper, no field
325
+ * renaming, no `as` casts. We re-state the shape (rather than re-export
326
+ * dockview's type) so the workspace package owns its public contract:
327
+ * if dockview's type ever drifts, only the wiring inside DockviewShell
328
+ * needs to change.
329
+ *
330
+ * Use {@link definePanel} for type-safe registration.
331
+ *
332
+ * @typeParam T - Shape of the panel-specific `params` payload. Defaults
333
+ * to `unknown` because layouts restored from JSON are inherently
334
+ * un-typed at runtime; use a generic param when you control the
335
+ * addPanel call site, otherwise read defensively.
336
+ */
337
+ declare interface PaneProps<T = unknown> {
338
+ /** App-supplied data for this panel instance (e.g. `{ path: string }`). */
339
+ params: T;
340
+ /** Per-panel control surface (close, setActive, setTitle, …). */
341
+ api: DockviewPanelApi;
342
+ /** Top-level dockview API (groups, addPanel, removePanel, fromJSON, …). */
343
+ containerApi: DockviewApi;
344
+ /** Optional className forwarded to the pane's root element. */
345
+ className?: string;
346
+ }
347
+
348
+ export declare function renderPane(ui: ReactElement, options?: RenderPaneOptions): RenderPaneResult;
349
+
350
+ export declare interface RenderPaneOptions extends Omit<RenderOptions, "wrapper"> {
351
+ fixtures?: MockDataFixtures;
352
+ bridge?: MockWorkspaceBridge;
353
+ registry?: PanelRegistry;
354
+ apiBaseUrl?: string;
355
+ authHeaders?: Record<string, string>;
356
+ defaultTheme?: "light" | "dark";
357
+ timeout?: number;
358
+ injectBridgeProp?: boolean;
359
+ }
360
+
361
+ export declare type RenderPaneResult = RenderResult & {
362
+ bridge: MockWorkspaceBridge;
363
+ registry: PanelRegistry;
364
+ };
365
+
366
+ declare type SearchArgs = {
367
+ query: string;
368
+ filters: Record<string, string[]>;
369
+ /** Scope to a single group's value (only set when paginating inside a group). */
370
+ group?: {
371
+ key: string;
372
+ value: string;
373
+ };
374
+ limit: number;
375
+ offset: number;
376
+ signal?: AbortSignal;
377
+ };
378
+
379
+ declare type SearchResult = {
380
+ items: ExplorerRow[];
381
+ /** Total count for the current scope (query + filters + optional group). */
382
+ total: number;
383
+ hasMore: boolean;
384
+ };
385
+
386
+ declare interface SessionItem {
387
+ id: string;
388
+ title: string;
389
+ updatedAt?: string | number;
390
+ }
391
+
392
+ declare interface SidebarState {
393
+ collapsed: boolean;
394
+ width: number;
395
+ }
396
+
397
+ declare type SpyFactory = <T extends (...args: any[]) => any>(implementation?: T) => any;
398
+
399
+ export declare function TestWorkspaceProvider({ children, fixtures, registry, apiBaseUrl, authHeaders, defaultTheme, timeout, }: TestWorkspaceProviderProps): JSX.Element;
400
+
401
+ export declare interface TestWorkspaceProviderProps {
402
+ children: ReactNode;
403
+ fixtures?: MockDataFixtures;
404
+ registry?: PanelRegistry;
405
+ apiBaseUrl?: string;
406
+ authHeaders?: Record<string, string>;
407
+ defaultTheme?: "light" | "dark";
408
+ timeout?: number;
409
+ }
410
+
411
+ declare type Unsubscribe = () => void;
412
+
413
+ /**
414
+ * React hook bound to a specific store instance. Identical signature to
415
+ * `useMockSessions` — both stores share the `MockSessionsStore` type.
416
+ */
417
+ export declare function useLocalStorageSessions(store: MockSessionsStore): MockSessionsState;
418
+
419
+ /**
420
+ * Convenience hook bound to a specific store instance. Subscribes via
421
+ * useSyncExternalStore so React only re-renders on actual state changes.
422
+ */
423
+ export declare function useMockSessions(store: MockSessionsStore): MockSessionsState;
424
+
425
+ declare interface WorkspaceBridge {
426
+ getOpenPanels(): PanelState[];
427
+ getActiveFile(): string | null;
428
+ getDirtyFiles(): string[];
429
+ getVisibleFiles(): string[];
430
+ openFile(path: string, opts?: {
431
+ mode?: "view" | "edit" | "diff";
432
+ }): Promise<CommandResult>;
433
+ openPanel(config: DynamicPaneConfig): Promise<CommandResult>;
434
+ closePanel(id: string): Promise<CommandResult>;
435
+ closeWorkbenchLeftPane(): Promise<CommandResult>;
436
+ showNotification(msg: string, level?: "info" | "warn" | "error"): Promise<CommandResult>;
437
+ navigateToLine(file: string, line: number): Promise<CommandResult>;
438
+ expandToFile(path: string): Promise<CommandResult>;
439
+ markDirty(path: string): void;
440
+ markClean(path: string): void;
441
+ subscribe<K extends keyof BridgeEventMap>(event: K, handler: (data: BridgeEventMap[K]) => void): Unsubscribe;
442
+ select<T>(selector: (state: WorkspaceState) => T, handler: (value: T) => void): Unsubscribe;
443
+ }
444
+
445
+ declare interface WorkspaceState {
446
+ hydrationComplete: boolean;
447
+ layout: unknown | null;
448
+ sidebar: SidebarState;
449
+ panelSizes: Record<string, number>;
450
+ preferences: {
451
+ theme: "light" | "dark";
452
+ };
453
+ panels: PanelState[];
454
+ activePanel: string | null;
455
+ activeFile: string | null;
456
+ visibleFiles: string[];
457
+ dirtyFiles: Record<string, {
458
+ panelId: string;
459
+ savedAt: number | null;
460
+ }>;
461
+ notifications: Notification_2[];
462
+ }
463
+
464
+ export { }