@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,66 @@
1
+ import { PiPackageSource, RuntimeProvisioningContribution } from '@hachej/boring-agent/server';
2
+ import { FastifyPluginAsync } from 'fastify';
3
+ import { A as AgentTool } from './agent-tool-NvxKfist.js';
4
+
5
+ interface WorkspaceServerPlugin {
6
+ id: string;
7
+ label?: string;
8
+ /**
9
+ * Native Pi package sources required by this workspace integration.
10
+ * Workspace declares them; @hachej/boring-agent applies them through Pi's native
11
+ * resource loader without asking Pi packages to export Boring adapters.
12
+ */
13
+ piPackages?: PiPackageSource[];
14
+ systemPrompt?: string;
15
+ agentTools?: AgentTool[];
16
+ provisioning?: RuntimeProvisioningContribution;
17
+ routes?: FastifyPluginAsync;
18
+ }
19
+ declare class ServerPluginError extends Error {
20
+ constructor(message: string);
21
+ }
22
+ declare function validateServerPlugin(plugin: WorkspaceServerPlugin): void;
23
+ declare function defineServerPlugin<T extends WorkspaceServerPlugin>(plugin: T): T;
24
+
25
+ interface ComposeServerPluginsOptions {
26
+ id: string;
27
+ label?: string;
28
+ plugins: WorkspaceServerPlugin[];
29
+ piPackages?: PiPackageSource[];
30
+ systemPrompt?: string;
31
+ agentTools?: AgentTool[];
32
+ provisioning?: RuntimeProvisioningContribution;
33
+ routes?: FastifyPluginAsync;
34
+ }
35
+ /**
36
+ * Compose a server plugin from smaller server plugin fragments. Child
37
+ * contributions are concatenated before parent contributions. Composed routes
38
+ * are registered without extra Fastify options; use an explicit routes plugin
39
+ * when a child needs scoped registration options.
40
+ */
41
+ declare function composeServerPlugins(options: ComposeServerPluginsOptions): WorkspaceServerPlugin;
42
+
43
+ interface ServerBootstrapOptions {
44
+ plugins?: WorkspaceServerPlugin[];
45
+ defaults?: WorkspaceServerPlugin[];
46
+ excludeDefaults?: string[];
47
+ }
48
+ type WorkspaceProvisioningContribution = {
49
+ id: string;
50
+ provisioning: RuntimeProvisioningContribution;
51
+ };
52
+ type WorkspaceRouteContribution = {
53
+ id: string;
54
+ routes: FastifyPluginAsync;
55
+ };
56
+ interface ServerBootstrapResult {
57
+ registered: string[];
58
+ systemPromptAppend: string;
59
+ piPackages: PiPackageSource[];
60
+ agentTools: AgentTool[];
61
+ provisioningContributions: WorkspaceProvisioningContribution[];
62
+ routeContributions: WorkspaceRouteContribution[];
63
+ }
64
+ declare function bootstrapServer(options: ServerBootstrapOptions): ServerBootstrapResult;
65
+
66
+ export { type ComposeServerPluginsOptions as C, type ServerBootstrapOptions as S, type WorkspaceProvisioningContribution as W, type WorkspaceRouteContribution as a, type WorkspaceServerPlugin as b, composeServerPlugins as c, defineServerPlugin as d, type ServerBootstrapResult as e, ServerPluginError as f, bootstrapServer as g, validateServerPlugin as v };
@@ -0,0 +1 @@
1
+ .dv-shell{--dv-background-color: var(--background);--dv-paneview-header-border-color: var(--border);--dv-tabs-and-actions-container-font-size: .8125rem;--dv-tabs-and-actions-container-height: 52px;--dv-tab-close-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");--dv-group-view-background-color: var(--background);--dv-tabs-and-actions-container-background-color: var(--background);--dv-activegroup-visiblepanel-tab-background-color: var(--background);--dv-activegroup-hiddenpanel-tab-background-color: var(--muted);--dv-inactivegroup-visiblepanel-tab-background-color: var(--background);--dv-inactivegroup-hiddenpanel-tab-background-color: var(--muted);--dv-activegroup-visiblepanel-tab-color: var(--foreground);--dv-activegroup-hiddenpanel-tab-color: var(--muted-foreground);--dv-inactivegroup-visiblepanel-tab-color: var(--foreground);--dv-inactivegroup-hiddenpanel-tab-color: var(--muted-foreground);--dv-tab-divider-color: transparent;--dv-drag-over-background-color: oklch(from var(--accent) l c h / .3);--dv-drag-over-border-color: var(--accent);--dv-separator-border: var(--border);color:var(--foreground);background-color:var(--background)}.dv-shell .dv-tabs-and-actions-container,.dv-shell .tabs-and-actions-container{background-color:var(--background)!important;border-bottom:1px solid oklch(from var(--border) l c h / .4);padding:8px 6px 0;gap:2px;align-items:flex-end}.dv-shell .dv-tabs-container,.dv-shell .tab-container{background-color:transparent!important;gap:4px}.workbench-dockview .dv-shell .dv-tabs-and-actions-container,.workbench-dockview .dv-shell .tabs-and-actions-container,.workbench-dockview .dv-tabs-and-actions-container,.workbench-dockview .tabs-and-actions-container{height:44px!important}.workbench-dockview[data-collapsed-sources=true] .dv-tabs-and-actions-container,.workbench-dockview[data-collapsed-sources=true] .tabs-and-actions-container{padding-left:44px!important}.dv-shell .dv-tab,.dv-shell .tab{color:var(--muted-foreground)!important;background-color:transparent!important;border:1px solid transparent;border-top-left-radius:7px;border-top-right-radius:7px;transition:color .18s cubic-bezier(.22,1,.36,1),background-color .18s cubic-bezier(.22,1,.36,1),border-color .18s cubic-bezier(.22,1,.36,1);min-width:120px;max-width:220px;padding:0;height:32px;align-self:end;font-size:12.5px;letter-spacing:-.01em}.dv-shell .dv-tab:hover,.dv-shell .tab:hover{color:var(--foreground)!important;background-color:oklch(from var(--foreground) l c h / .04)!important}.dv-shell .dv-tab>*,.dv-shell .tab>*{width:100%;height:100%}.dv-shell .dv-tab.dv-active-tab,.dv-shell .tab.active-tab{color:var(--foreground)!important;background-color:var(--background)!important;border-color:oklch(from var(--border) l c h / .5);border-bottom-color:var(--background);margin-bottom:-1px;position:relative;z-index:1;font-weight:500}.dv-shell .dv-tab.dv-active-tab:before,.dv-shell .tab.active-tab:before{content:"";position:absolute;left:10px;right:10px;top:-1px;height:2px;background:var(--accent);border-radius:0 0 2px 2px;opacity:0;transition:opacity .2s cubic-bezier(.22,1,.36,1)}.dv-shell .dv-tab.dv-active-tab:hover:before,.dv-shell .tab.active-tab:hover:before{opacity:.7}.dv-shell .sash-container .sash,.dv-shell .dv-sash-container .dv-sash{transition:background-color .2s}.dv-shell .sash-container .sash:hover,.dv-shell .sash-container .sash.active,.dv-shell .dv-sash-container .dv-sash:hover,.dv-shell .dv-sash-container .dv-sash.dv-active{background-color:var(--primary);transition-delay:.15s}.dv-shell .drop-target-dropzone>.drop-target-selection,.dv-shell .dv-drop-target-dropzone>.dv-drop-target-selection{background-color:oklch(from var(--primary) l c h / .15);border:2px dashed var(--primary);border-radius:calc(var(--radius) - 2px)}.dv-shell .watermark,.dv-shell .dv-watermark{color:var(--muted-foreground)}.dv-shell,.dv-shell .dv-dockview,.dv-shell .dv-groupview,.dv-shell .groupview,.dv-shell .groupview>.content-container,.dv-shell .dv-view-container,.dv-shell .view-container{background-color:var(--background)}.dv-shell .dv-groupview,.dv-shell .groupview{color:var(--foreground)}.dv-shell .right-actions-container,.dv-shell .left-actions-container,.dv-shell .dv-right-actions-container,.dv-shell .dv-left-actions-container{color:var(--muted-foreground);display:flex;align-items:center}.dv-shell .right-actions-container:hover,.dv-shell .left-actions-container:hover,.dv-shell .dv-right-actions-container:hover,.dv-shell .dv-left-actions-container:hover{color:var(--foreground)}.dv-shell .dv-drag-image{opacity:.85;border:1px solid var(--border);border-radius:calc(var(--radius) - 2px);box-shadow:0 4px 12px #00000026}
@@ -0,0 +1,114 @@
1
+ import { JSX } from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ export declare const boringAreaProps: {
5
+ readonly type: "monotone";
6
+ readonly strokeWidth: 2;
7
+ readonly dot: false;
8
+ readonly connectNulls: true;
9
+ readonly isAnimationActive: false;
10
+ };
11
+
12
+ export declare const boringBarProps: {
13
+ readonly radius: [number, number, number, number];
14
+ readonly isAnimationActive: false;
15
+ };
16
+
17
+ export declare const boringCartesianAxisProps: {
18
+ readonly tick: {
19
+ readonly fill: string;
20
+ readonly fontSize: 11;
21
+ };
22
+ readonly axisLine: {
23
+ readonly stroke: string;
24
+ };
25
+ readonly tickLine: {
26
+ readonly stroke: string;
27
+ };
28
+ };
29
+
30
+ export declare const boringCartesianGridProps: {
31
+ readonly stroke: string;
32
+ readonly strokeDasharray: "3 3";
33
+ readonly vertical: false;
34
+ };
35
+
36
+ export declare function BoringChartFrame({ title, subtitle, source, height, className, children, }: BoringChartFrameProps): JSX.Element;
37
+
38
+ export declare interface BoringChartFrameProps {
39
+ title?: string;
40
+ subtitle?: string;
41
+ source?: string;
42
+ height?: number | string;
43
+ className?: string;
44
+ children: ReactNode;
45
+ }
46
+
47
+ export declare const boringChartPalette: readonly string[];
48
+
49
+ export declare interface BoringChartTheme {
50
+ background: string;
51
+ foreground: string;
52
+ mutedForeground: string;
53
+ border: string;
54
+ grid: string;
55
+ tooltipBackground: string;
56
+ tooltipBorder: string;
57
+ tooltipForeground: string;
58
+ palette: readonly string[];
59
+ }
60
+
61
+ export declare const boringChartTheme: BoringChartTheme;
62
+
63
+ export declare type BoringChartValueFormatter = (value: unknown) => string;
64
+
65
+ export declare const boringLegendProps: {
66
+ readonly wrapperStyle: {
67
+ readonly color: string;
68
+ readonly fontSize: 12;
69
+ };
70
+ };
71
+
72
+ export declare const boringLineProps: {
73
+ readonly type: "monotone";
74
+ readonly strokeWidth: 2;
75
+ readonly dot: false;
76
+ readonly activeDot: {
77
+ readonly r: 4;
78
+ };
79
+ readonly connectNulls: true;
80
+ readonly isAnimationActive: false;
81
+ };
82
+
83
+ export declare const boringPieProps: {
84
+ readonly innerRadius: "58%";
85
+ readonly outerRadius: "82%";
86
+ readonly paddingAngle: 2;
87
+ readonly isAnimationActive: false;
88
+ };
89
+
90
+ export declare const boringReferenceAreaProps: {
91
+ readonly fill: "oklch(from var(--accent) l c h / 0.20)";
92
+ };
93
+
94
+ export declare function BoringTooltip({ active, payload, label, valueFormatter, }: BoringTooltipProps): JSX.Element | null;
95
+
96
+ export declare interface BoringTooltipPayloadItem {
97
+ color?: string;
98
+ dataKey?: string | number;
99
+ name?: string | number;
100
+ value?: unknown;
101
+ }
102
+
103
+ export declare interface BoringTooltipProps {
104
+ active?: boolean;
105
+ payload?: BoringTooltipPayloadItem[];
106
+ label?: unknown;
107
+ valueFormatter?: BoringChartValueFormatter;
108
+ }
109
+
110
+ export declare function defaultBoringChartValueFormatter(value: unknown): string;
111
+
112
+ export declare function getBoringChartColor(index: number, theme?: BoringChartTheme): string;
113
+
114
+ export { }
package/dist/charts.js ADDED
@@ -0,0 +1,143 @@
1
+ import { jsxs as l, jsx as n } from "react/jsx-runtime";
2
+ import { c as d } from "./utils-B6yFEsav.js";
3
+ const c = [
4
+ "var(--chart-1, var(--accent))",
5
+ "var(--chart-2, #60a5fa)",
6
+ "var(--chart-3, #34d399)",
7
+ "var(--chart-4, #f59e0b)",
8
+ "var(--chart-5, #f472b6)",
9
+ "var(--chart-6, #a78bfa)",
10
+ "var(--chart-7, #22d3ee)",
11
+ "var(--chart-8, #fb7185)"
12
+ ], r = {
13
+ background: "var(--background)",
14
+ foreground: "var(--foreground)",
15
+ mutedForeground: "var(--muted-foreground)",
16
+ border: "var(--border)",
17
+ grid: "oklch(from var(--border) l c h / 0.48)",
18
+ tooltipBackground: "var(--popover, var(--background))",
19
+ tooltipBorder: "var(--border)",
20
+ tooltipForeground: "var(--popover-foreground, var(--foreground))",
21
+ palette: c
22
+ };
23
+ function u(o, e = r) {
24
+ if (e.palette.length === 0) return "var(--accent)";
25
+ const i = ((Number.isFinite(o) ? Math.trunc(o) : 0) % e.palette.length + e.palette.length) % e.palette.length;
26
+ return e.palette[i] ?? e.palette[0] ?? "var(--accent)";
27
+ }
28
+ function g(o) {
29
+ return typeof o == "number" ? new Intl.NumberFormat(void 0, { maximumFractionDigits: 2 }).format(o) : o == null ? "—" : String(o);
30
+ }
31
+ function m({
32
+ active: o,
33
+ payload: e,
34
+ label: a,
35
+ valueFormatter: i = g
36
+ }) {
37
+ return !o || !e || e.length === 0 ? null : /* @__PURE__ */ l(
38
+ "div",
39
+ {
40
+ className: "min-w-32 rounded-md border px-2.5 py-2 text-xs shadow-xl",
41
+ style: {
42
+ background: r.tooltipBackground,
43
+ borderColor: r.tooltipBorder,
44
+ color: r.tooltipForeground
45
+ },
46
+ children: [
47
+ a != null && /* @__PURE__ */ n("div", { className: "mb-1.5 font-medium", style: { color: r.foreground }, children: String(a) }),
48
+ /* @__PURE__ */ n("div", { className: "space-y-1", children: e.map((t, s) => /* @__PURE__ */ l("div", { className: "flex items-center justify-between gap-4", children: [
49
+ /* @__PURE__ */ l("span", { className: "inline-flex items-center gap-1.5", style: { color: r.mutedForeground }, children: [
50
+ /* @__PURE__ */ n(
51
+ "span",
52
+ {
53
+ "aria-hidden": "true",
54
+ className: "size-2 rounded-full",
55
+ style: { background: t.color ?? u(s) }
56
+ }
57
+ ),
58
+ String(t.name ?? t.dataKey ?? "Series")
59
+ ] }),
60
+ /* @__PURE__ */ n("span", { className: "font-mono tabular-nums", style: { color: r.foreground }, children: i(t.value) })
61
+ ] }, `${t.dataKey ?? t.name ?? "series"}:${s}`)) })
62
+ ]
63
+ }
64
+ );
65
+ }
66
+ function b({
67
+ title: o,
68
+ subtitle: e,
69
+ source: a,
70
+ height: i = 320,
71
+ className: t,
72
+ children: s
73
+ }) {
74
+ return /* @__PURE__ */ l(
75
+ "figure",
76
+ {
77
+ className: d(
78
+ "flex min-h-0 w-full flex-col rounded-xl border bg-card/60 text-card-foreground",
79
+ "shadow-[0_1px_2px_-1px_oklch(0_0_0/0.08),0_12px_32px_-22px_oklch(0_0_0/0.28)]",
80
+ t
81
+ ),
82
+ style: { borderColor: r.border },
83
+ children: [
84
+ (o || e) && /* @__PURE__ */ l("figcaption", { className: "border-b px-3 py-2", style: { borderColor: r.border }, children: [
85
+ o && /* @__PURE__ */ n("div", { className: "text-sm font-semibold tracking-[-0.01em]", children: o }),
86
+ e && /* @__PURE__ */ n("div", { className: "mt-0.5 text-xs text-muted-foreground", children: e })
87
+ ] }),
88
+ /* @__PURE__ */ n("div", { className: "min-h-0 flex-1 p-3", style: { height: i }, children: s }),
89
+ a && /* @__PURE__ */ n("div", { className: "border-t px-3 py-1.5 text-[11px] text-muted-foreground", style: { borderColor: r.border }, children: a })
90
+ ]
91
+ }
92
+ );
93
+ }
94
+ const h = {
95
+ tick: { fill: r.mutedForeground, fontSize: 11 },
96
+ axisLine: { stroke: r.border },
97
+ tickLine: { stroke: r.border }
98
+ }, v = {
99
+ stroke: r.grid,
100
+ strokeDasharray: "3 3",
101
+ vertical: !1
102
+ }, x = {
103
+ wrapperStyle: { color: r.mutedForeground, fontSize: 12 }
104
+ }, k = {
105
+ type: "monotone",
106
+ strokeWidth: 2,
107
+ dot: !1,
108
+ activeDot: { r: 4 },
109
+ connectNulls: !0,
110
+ isAnimationActive: !1
111
+ }, y = {
112
+ type: "monotone",
113
+ strokeWidth: 2,
114
+ dot: !1,
115
+ connectNulls: !0,
116
+ isAnimationActive: !1
117
+ }, N = {
118
+ radius: [6, 6, 2, 2],
119
+ isAnimationActive: !1
120
+ }, A = {
121
+ innerRadius: "58%",
122
+ outerRadius: "82%",
123
+ paddingAngle: 2,
124
+ isAnimationActive: !1
125
+ }, C = {
126
+ fill: "oklch(from var(--accent) l c h / 0.20)"
127
+ };
128
+ export {
129
+ b as BoringChartFrame,
130
+ m as BoringTooltip,
131
+ y as boringAreaProps,
132
+ N as boringBarProps,
133
+ h as boringCartesianAxisProps,
134
+ v as boringCartesianGridProps,
135
+ c as boringChartPalette,
136
+ r as boringChartTheme,
137
+ x as boringLegendProps,
138
+ k as boringLineProps,
139
+ A as boringPieProps,
140
+ C as boringReferenceAreaProps,
141
+ g as defaultBoringChartValueFormatter,
142
+ u as getBoringChartColor
143
+ };
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Tiny typed event bus. ~30 lines, no runtime deps.
3
+ *
4
+ * Design constraints (locked by reviewer feedback in
5
+ * `docs/plans/UNIFIED_EVENT_BUS.md`):
6
+ *
7
+ * - Synchronous emit only. Slow subscribers fire-and-forget their own async work.
8
+ * - Snapshot listeners before iterating so subscribe / unsubscribe
9
+ * during dispatch is safe.
10
+ * - One thrown listener does not stop the chain. Errors go to console.error.
11
+ * - Bus emits transitions only — no replay-on-subscribe.
12
+ */
13
+ interface EventBus<TMap extends Record<string, any>> {
14
+ /** Subscribe to one event name. Returns an unsubscribe function. */
15
+ on<K extends keyof TMap>(name: K, fn: (payload: TMap[K]) => void): () => void;
16
+ /** Synchronously dispatch to every matching listener. */
17
+ emit<K extends keyof TMap>(name: K, payload: TMap[K]): void;
18
+ /** Test-only — never call from production code. Underscore-prefixed by convention. */
19
+ _reset(): void;
20
+ }
21
+
22
+ interface UiCommand {
23
+ v?: number;
24
+ seq?: number;
25
+ kind: string;
26
+ params: Record<string, unknown>;
27
+ }
28
+
29
+ /**
30
+ * Canonical event map for the workspace event bus.
31
+ *
32
+ * All in-process cross-cutting signals flow through one typed map.
33
+ * Adding a new event = adding a key here.
34
+ *
35
+ * See `docs/plans/UNIFIED_EVENT_BUS.md` for the design rationale and
36
+ * the planned future events. Those are intentionally NOT pre-declared
37
+ * — they get added when their concrete emitter and consumer land in
38
+ * the same step.
39
+ */
40
+
41
+ declare const WORKSPACE_PLUGIN_ID = "workspace";
42
+ declare const WORKSPACE_UI_COMMAND_EVENT = "workspace:ui.command";
43
+ declare const WORKSPACE_EDITOR_SAVE_START_EVENT = "workspace:editor.save.start";
44
+ declare const WORKSPACE_EDITOR_SAVE_END_EVENT = "workspace:editor.save.end";
45
+ declare const WORKSPACE_PANEL_UPDATE_EVENT = "workspace:panel.update";
46
+ declare const WORKSPACE_PANEL_CLOSE_EVENT = "workspace:panel.close";
47
+ declare const WORKSPACE_AGENT_DATA_EVENT = "workspace:agent.data";
48
+ declare const workspaceEvents: {
49
+ readonly uiCommand: "workspace:ui.command";
50
+ readonly editorSaveStart: "workspace:editor.save.start";
51
+ readonly editorSaveEnd: "workspace:editor.save.end";
52
+ readonly panelUpdate: "workspace:panel.update";
53
+ readonly panelClose: "workspace:panel.close";
54
+ readonly agentData: "workspace:agent.data";
55
+ };
56
+ type WorkspacePanelMatch = {
57
+ id: string;
58
+ } | {
59
+ param: string;
60
+ value: unknown;
61
+ };
62
+ /**
63
+ * Discriminated origin metadata. Encoded as a union (rather than a
64
+ * flat `cause` + optional `toolCallId`) so the type system enforces
65
+ * that agent-originated events always carry a tool call id.
66
+ */
67
+ type Origin = {
68
+ cause: "user";
69
+ } | {
70
+ cause: "agent";
71
+ toolCallId: string;
72
+ }
73
+ /**
74
+ * Anything observed via the server-side fs watcher: a collaborator
75
+ * editing the same workspace, a git pull, an external editor, the
76
+ * agent in another tab. Carries the tool call id ONLY when the
77
+ * server can attribute the change (sandbox emits its own writes
78
+ * with attribution; chokidar can't). Consumers that want to
79
+ * suppress UX side-effects on self-echo compare `actorClientId` (a
80
+ * future field) against their own.
81
+ */
82
+ | {
83
+ cause: "remote";
84
+ toolCallId?: string;
85
+ };
86
+ /** Common envelope on every payload. */
87
+ type EventMeta = Origin & {
88
+ ts: number;
89
+ };
90
+ /** Helper for emitting a user-originated event payload. */
91
+ declare function userMeta(): {
92
+ cause: "user";
93
+ ts: number;
94
+ };
95
+ declare function agentMeta(toolCallId: string): {
96
+ cause: "agent";
97
+ toolCallId: string;
98
+ ts: number;
99
+ };
100
+ declare function remoteMeta(toolCallId?: string): {
101
+ cause: "remote";
102
+ toolCallId?: string;
103
+ ts: number;
104
+ };
105
+ interface WorkspaceHostEventMap {
106
+ /** Shared UI manipulation contract used by the agent stream and plugin bindings. */
107
+ [WORKSPACE_UI_COMMAND_EVENT]: EventMeta & {
108
+ command: UiCommand;
109
+ };
110
+ [WORKSPACE_EDITOR_SAVE_START_EVENT]: {
111
+ panelId: string;
112
+ };
113
+ [WORKSPACE_EDITOR_SAVE_END_EVENT]: {
114
+ panelId: string;
115
+ ok?: boolean;
116
+ error?: string;
117
+ };
118
+ [WORKSPACE_PANEL_UPDATE_EVENT]: EventMeta & {
119
+ match: WorkspacePanelMatch | WorkspacePanelMatch[];
120
+ params?: Record<string, unknown>;
121
+ title?: string;
122
+ };
123
+ [WORKSPACE_PANEL_CLOSE_EVENT]: EventMeta & {
124
+ match: WorkspacePanelMatch | WorkspacePanelMatch[];
125
+ };
126
+ /**
127
+ * Raw agent stream data observed by ChatPanelHost. Core treats this as an
128
+ * opaque packet; plugins translate packets they understand into their own
129
+ * plugin-keyed events.
130
+ */
131
+ [WORKSPACE_AGENT_DATA_EVENT]: {
132
+ ts: number;
133
+ part: unknown;
134
+ };
135
+ }
136
+ /**
137
+ * Built-in plugin events baked into the public workspace event map.
138
+ *
139
+ * Filesystem events are declared inline (using EventMeta which is structurally
140
+ * identical to FilesystemEventMeta) so the keys survive vite's rollupTypes
141
+ * bundling without importing plugin-domain modules.
142
+ *
143
+ * Third-party plugins can extend this interface via declare module augmentation,
144
+ * though that only works in source compilation — it does not survive dist bundling.
145
+ */
146
+ interface WorkspacePluginEventMap {
147
+ "filesystem:file.changed": EventMeta & {
148
+ path: string;
149
+ };
150
+ "filesystem:file.created": EventMeta & {
151
+ path: string;
152
+ kind: "file" | "dir";
153
+ };
154
+ "filesystem:file.moved": EventMeta & {
155
+ from: string;
156
+ to: string;
157
+ };
158
+ "filesystem:file.deleted": EventMeta & {
159
+ path: string;
160
+ };
161
+ }
162
+ interface WorkspaceEventMap extends WorkspaceHostEventMap, WorkspacePluginEventMap {
163
+ }
164
+ /** Names that share a prefix can be filtered with `startsWith`. */
165
+ type WorkspaceEventName = keyof WorkspaceEventMap;
166
+
167
+ /**
168
+ * React hook: subscribe to a workspace event for the lifetime of the
169
+ * component. The handler ref is stable across renders so changing the
170
+ * handler doesn't tear down and re-subscribe.
171
+ */
172
+ declare function useEvent<K extends keyof WorkspaceEventMap>(name: K, handler: (payload: WorkspaceEventMap[K]) => void): void;
173
+
174
+ declare function emitAgentData(part: unknown): void;
175
+
176
+ declare const events: EventBus<WorkspaceEventMap>;
177
+
178
+ export { type EventMeta, type Origin, WORKSPACE_AGENT_DATA_EVENT, WORKSPACE_EDITOR_SAVE_END_EVENT, WORKSPACE_EDITOR_SAVE_START_EVENT, WORKSPACE_PANEL_CLOSE_EVENT, WORKSPACE_PANEL_UPDATE_EVENT, WORKSPACE_PLUGIN_ID, WORKSPACE_UI_COMMAND_EVENT, type WorkspaceEventMap, type WorkspaceEventName, type WorkspacePanelMatch, type WorkspacePluginEventMap, agentMeta, emitAgentData, events, remoteMeta, useEvent, userMeta, workspaceEvents };
package/dist/events.js ADDED
@@ -0,0 +1,88 @@
1
+ // src/front/events/bus.ts
2
+ function createEventBus() {
3
+ const named = /* @__PURE__ */ new Map();
4
+ function on(name, fn) {
5
+ let bucket = named.get(name);
6
+ if (!bucket) {
7
+ bucket = /* @__PURE__ */ new Set();
8
+ named.set(name, bucket);
9
+ }
10
+ bucket.add(fn);
11
+ return () => bucket.delete(fn);
12
+ }
13
+ function emit(name, payload) {
14
+ const bucket = named.get(name);
15
+ if (!bucket) return;
16
+ for (const listener of [...bucket]) {
17
+ try {
18
+ listener(payload);
19
+ } catch (err) {
20
+ console.error(`[events] listener for "${String(name)}" threw:`, err);
21
+ }
22
+ }
23
+ }
24
+ function _reset() {
25
+ named.clear();
26
+ }
27
+ return { on, emit, _reset };
28
+ }
29
+
30
+ // src/front/events/types.ts
31
+ var WORKSPACE_PLUGIN_ID = "workspace";
32
+ var WORKSPACE_UI_COMMAND_EVENT = "workspace:ui.command";
33
+ var WORKSPACE_EDITOR_SAVE_START_EVENT = "workspace:editor.save.start";
34
+ var WORKSPACE_EDITOR_SAVE_END_EVENT = "workspace:editor.save.end";
35
+ var WORKSPACE_PANEL_UPDATE_EVENT = "workspace:panel.update";
36
+ var WORKSPACE_PANEL_CLOSE_EVENT = "workspace:panel.close";
37
+ var WORKSPACE_AGENT_DATA_EVENT = "workspace:agent.data";
38
+ var workspaceEvents = {
39
+ uiCommand: WORKSPACE_UI_COMMAND_EVENT,
40
+ editorSaveStart: WORKSPACE_EDITOR_SAVE_START_EVENT,
41
+ editorSaveEnd: WORKSPACE_EDITOR_SAVE_END_EVENT,
42
+ panelUpdate: WORKSPACE_PANEL_UPDATE_EVENT,
43
+ panelClose: WORKSPACE_PANEL_CLOSE_EVENT,
44
+ agentData: WORKSPACE_AGENT_DATA_EVENT
45
+ };
46
+ function userMeta() {
47
+ return { cause: "user", ts: Date.now() };
48
+ }
49
+ function agentMeta(toolCallId) {
50
+ return { cause: "agent", toolCallId, ts: Date.now() };
51
+ }
52
+ function remoteMeta(toolCallId) {
53
+ return { cause: "remote", toolCallId, ts: Date.now() };
54
+ }
55
+
56
+ // src/front/events/useEvent.ts
57
+ import { useEffect, useRef } from "react";
58
+ function useEvent(name, handler) {
59
+ const ref = useRef(handler);
60
+ ref.current = handler;
61
+ useEffect(() => {
62
+ return events.on(name, (payload) => ref.current(payload));
63
+ }, [name]);
64
+ }
65
+
66
+ // src/front/events/agentBridge.ts
67
+ function emitAgentData(part) {
68
+ events.emit(workspaceEvents.agentData, { ts: Date.now(), part });
69
+ }
70
+
71
+ // src/front/events/index.ts
72
+ var events = createEventBus();
73
+ export {
74
+ WORKSPACE_AGENT_DATA_EVENT,
75
+ WORKSPACE_EDITOR_SAVE_END_EVENT,
76
+ WORKSPACE_EDITOR_SAVE_START_EVENT,
77
+ WORKSPACE_PANEL_CLOSE_EVENT,
78
+ WORKSPACE_PANEL_UPDATE_EVENT,
79
+ WORKSPACE_PLUGIN_ID,
80
+ WORKSPACE_UI_COMMAND_EVENT,
81
+ agentMeta,
82
+ emitAgentData,
83
+ events,
84
+ remoteMeta,
85
+ useEvent,
86
+ userMeta,
87
+ workspaceEvents
88
+ };