@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.
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/CodeEditor-DQqOn4xz.js +266 -0
- package/dist/CommandPalette-aM61U-b0.js +5229 -0
- package/dist/FileTree-DRq_bfue.js +245 -0
- package/dist/MarkdownEditor-DjiHxnRv.js +349 -0
- package/dist/WorkspaceLoadingState-By0dZoPD.js +568 -0
- package/dist/agent-tool-NvxKfist.d.ts +28 -0
- package/dist/app-front.d.ts +485 -0
- package/dist/app-front.js +452 -0
- package/dist/app-server.d.ts +53 -0
- package/dist/app-server.js +769 -0
- package/dist/bootstrapServer-BRUqUpVW.d.ts +66 -0
- package/dist/boring-workspace.css +1 -0
- package/dist/charts.d.ts +114 -0
- package/dist/charts.js +143 -0
- package/dist/events.d.ts +178 -0
- package/dist/events.js +88 -0
- package/dist/explorer-DtLUnuah.d.ts +129 -0
- package/dist/panel-DnvDNQac.js +6 -0
- package/dist/server.d.ts +84 -0
- package/dist/server.js +811 -0
- package/dist/shared.d.ts +113 -0
- package/dist/shared.js +11 -0
- package/dist/testing-e2e.d.ts +68 -0
- package/dist/testing-e2e.js +45 -0
- package/dist/testing.d.ts +464 -0
- package/dist/testing.js +10984 -0
- package/dist/utils-B6yFEsav.js +8 -0
- package/dist/workspace.css +5780 -0
- package/dist/workspace.d.ts +2119 -0
- package/dist/workspace.js +1884 -0
- package/docs/INTERFACES.md +58 -0
- package/docs/PLUGIN_STRUCTURE.md +162 -0
- package/docs/README.md +19 -0
- package/docs/bridge.md +135 -0
- package/docs/panels.md +102 -0
- package/docs/plans/GENERIC_EXPLORER_PLUGIN_PLAN.md +455 -0
- package/docs/plans/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md +962 -0
- package/docs/plans/PLUGIN_OUTPUTS_ISOLATION_PLAN.md +301 -0
- package/docs/plans/README.md +9 -0
- package/docs/plans/UI_BRIDGE_OWNERSHIP_REFACTOR.md +303 -0
- package/docs/plans/archive/CODE_OWNERSHIP_CLEANUP_PLAN.md +387 -0
- package/docs/plans/archive/COMMAND_PALETTE_REGISTRY.md +814 -0
- package/docs/plans/archive/DECLARATIVE_LAYOUT_MIGRATION.md +277 -0
- package/docs/plans/archive/PLUGIN_MODEL.md +3674 -0
- package/docs/plans/archive/SRC_FOLDER_REORG_PLAN.md +307 -0
- package/docs/plans/archive/UNIFIED_EVENT_BUS.md +647 -0
- package/docs/plans/archive/WORKSPACE_V2_PLAN.md +2489 -0
- package/docs/plugins.md +158 -0
- package/package.json +164 -0
package/dist/shared.d.ts
ADDED
|
@@ -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,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 { }
|