@hachej/boring-workspace 0.1.35 → 0.1.37

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/dist/plugin.js CHANGED
@@ -297,12 +297,51 @@ function validateBoringPluginManifest(raw) {
297
297
 
298
298
  // src/shared/types/surface.ts
299
299
  var WORKSPACE_OPEN_PATH_SURFACE_KIND = "workspace.open.path";
300
+
301
+ // src/shared/plugins/uiBridgeRegistry.ts
302
+ var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@hachej/boring-workspace:active-ui-bridge");
303
+ function slot() {
304
+ return globalThis;
305
+ }
306
+ function getWorkspaceUiBridge() {
307
+ return slot()[REGISTRY_KEY];
308
+ }
309
+ var NoWorkspaceUiBridgeError = class extends Error {
310
+ constructor() {
311
+ super(
312
+ "No workspace UI bridge is active. This plugin command must run inside a boring-ui workspace agent (it cannot open panels from a bare Pi CLI)."
313
+ );
314
+ this.name = "NoWorkspaceUiBridgeError";
315
+ }
316
+ };
317
+ function requireBridge() {
318
+ const bridge = getWorkspaceUiBridge();
319
+ if (!bridge) throw new NoWorkspaceUiBridgeError();
320
+ return bridge;
321
+ }
322
+ async function execWorkspaceUi(command) {
323
+ return requireBridge().postCommand(command);
324
+ }
325
+ async function openPanel(args) {
326
+ return execWorkspaceUi({
327
+ kind: "openPanel",
328
+ params: { id: args.id, component: args.component, params: args.params }
329
+ });
330
+ }
331
+ async function notify(msg, level = "info") {
332
+ return execWorkspaceUi({ kind: "showNotification", params: { msg, level } });
333
+ }
300
334
  export {
335
+ NoWorkspaceUiBridgeError,
301
336
  WORKSPACE_OPEN_PATH_SURFACE_KIND,
302
337
  captureFrontPlugin,
303
338
  createCapturingBoringFrontAPI,
304
339
  definePlugin,
340
+ execWorkspaceUi,
341
+ getWorkspaceUiBridge,
305
342
  isSafePluginRelativePath,
306
343
  isValidBoringPluginId,
344
+ notify,
345
+ openPanel,
307
346
  validateBoringPluginManifest
308
347
  };
package/dist/server.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { f as WorkspaceServerPluginAsset, g as BoringServerPluginManifest, B as BoringPluginSourceInput, a as BoringPluginFrontTargetResolver, h as BoringPluginListEntry, i as BoringPluginFrontTarget, j as BoringPluginSource, k as BoringPluginEvent, P as PluginRestartSurface } from './createInMemoryBridge-zb8MpO60.js';
2
- export { l as BoringPluginFrontTargetResolverContext, m as BoringPluginNativeFrontTarget, n as BoringPluginNativeFrontTargetTrust, o as BoringPluginSourceKind, S as ServerBootstrapOptions, p as ServerBootstrapResult, b as WorkspaceProvisioningContribution, c as WorkspaceRouteContribution, W as WorkspaceServerPlugin, q as bootstrapServer, d as createInMemoryBridge, r as defineServerPlugin, v as validateServerPlugin } from './createInMemoryBridge-zb8MpO60.js';
1
+ import { f as WorkspaceServerPluginAsset, g as BoringServerPluginManifest, B as BoringPluginSourceInput, a as BoringPluginFrontTargetResolver, h as BoringPluginListEntry, i as BoringPluginFrontTarget, j as BoringPluginSource, k as BoringPluginEvent, P as PluginRestartSurface } from './createInMemoryBridge-DSjZ9efK.js';
2
+ export { l as BoringPluginFrontTargetResolverContext, m as BoringPluginNativeFrontTarget, n as BoringPluginNativeFrontTargetTrust, o as BoringPluginSourceKind, S as ServerBootstrapOptions, p as ServerBootstrapResult, b as WorkspaceProvisioningContribution, c as WorkspaceRouteContribution, W as WorkspaceServerPlugin, q as bootstrapServer, d as createInMemoryBridge, r as defineServerPlugin, v as validateServerPlugin } from './createInMemoryBridge-DSjZ9efK.js';
3
3
  import { FastifyRequest, FastifyInstance } from 'fastify';
4
- import { U as UiBridge, A as AgentTool } from './ui-bridge-DFNem0df.js';
5
- export { C as CommandResult, a as UiCommand, b as UiState } from './ui-bridge-DFNem0df.js';
4
+ import { U as UiBridge } from './ui-bridge-LeBuZqfA.js';
5
+ export { C as CommandResult, a as UiCommand, b as UiState } from './ui-bridge-LeBuZqfA.js';
6
+ import { A as AgentTool } from './agent-tool-CB0RQyx9.js';
6
7
  import { PiPackageSource } from '@hachej/boring-agent/server';
7
8
  export { PiPackageSource as WorkspacePiPackageSource } from '@hachej/boring-agent/server';
8
9
  import { PluginLogger } from './runtime-server.js';
@@ -188,15 +189,11 @@ interface BoringPluginAssetManagerOptions {
188
189
  */
189
190
  errorRoot?: string;
190
191
  /**
191
- * Optional host-owned runtime front-target resolver. When omitted, list/event
192
- * payloads preserve the existing `frontUrl` (`/@fs/...`) fallback only.
192
+ * Optional host-owned runtime front-target resolver (the CLI's runtime
193
+ * module host mints `native` targets). When omitted, plugins with a front
194
+ * entry get a `module-url` target pointing at the Vite-dev `/@fs/...` URL.
193
195
  */
194
196
  frontTargetResolver?: BoringPluginFrontTargetResolver;
195
- /**
196
- * Keep legacy `/@fs/...` frontUrl payloads alongside frontTarget. Defaults
197
- * to true for back-compat; packaged CLI folder/workspaces mode can disable it.
198
- */
199
- includeLegacyFrontUrl?: boolean;
200
197
  }
201
198
  interface LoadBoringAssetsError {
202
199
  id: string;
@@ -226,10 +223,9 @@ interface LoadedBoringPluginPiSnapshot {
226
223
  }
227
224
  type Listener = (event: BoringPluginEvent) => void;
228
225
  declare class BoringPluginAssetManager {
229
- private readonly pluginDirs;
226
+ private pluginDirs;
230
227
  private readonly errorRoot;
231
228
  private readonly frontTargetResolver?;
232
- private readonly includeLegacyFrontUrl;
233
229
  private readonly loaded;
234
230
  private readonly revisions;
235
231
  private readonly listeners;
@@ -237,8 +233,25 @@ declare class BoringPluginAssetManager {
237
233
  private loading;
238
234
  private reloadQueued;
239
235
  constructor(options: BoringPluginAssetManagerOptions);
236
+ /**
237
+ * Replace the scanned source roots. Lets hosts re-resolve discovery inputs
238
+ * (e.g. workspace `.pi/settings.json` package sources, which can gain
239
+ * entries after `boring-ui-plugin install`) before the next load() so
240
+ * `/reload` picks up newly installed plugin sources without a process
241
+ * restart.
242
+ */
243
+ setPluginDirs(pluginDirs: BoringPluginSourceInput[]): void;
240
244
  preflight(): BoringPluginPreflightResult;
241
245
  list(): BoringPluginListEntry[];
246
+ /**
247
+ * Plugins whose front lifecycle the SSE channel owns. Internal plugins are
248
+ * app code — their front is statically bundled by the host app and must
249
+ * never be re-imported through the hot-reload pipeline (a second module
250
+ * instance would carry a fresh React context identity, breaking
251
+ * provider ↔ panel lookups). They are loaded server-side (routes, agent
252
+ * tools, Pi snapshot) but excluded from SSE replay and live events.
253
+ */
254
+ listExternal(): BoringPluginListEntry[];
242
255
  getError(pluginId: string): string | null;
243
256
  getErrors(): LoadBoringAssetsError[];
244
257
  inspectLoaded(): LoadedBoringPluginInspection[];
@@ -250,8 +263,14 @@ declare class BoringPluginAssetManager {
250
263
  private collectPreflightErrors;
251
264
  private bumpRevision;
252
265
  private toListEntry;
253
- private frontUrlPayload;
254
266
  private resolveFrontTarget;
267
+ /**
268
+ * Append to the load result's events array and emit on the SSE channel —
269
+ * unless the source is internal. Internal plugins are app code: their
270
+ * events stay in the load result (for /reload diagnostics and restart
271
+ * warnings) but never reach SSE subscribers (see listExternal).
272
+ */
273
+ private record;
255
274
  private emit;
256
275
  private errorPath;
257
276
  private writeError;
@@ -359,10 +378,18 @@ declare class RuntimeBackendRegistry {
359
378
  private closeOnce;
360
379
  }
361
380
 
381
+ /**
382
+ * Minimal dispatch surface the gateway needs. RuntimeBackendRegistry satisfies
383
+ * it directly; multi-workspace hosts can pass a facade that routes the request
384
+ * to a per-workspace registry by `request.workspaceId`.
385
+ */
386
+ interface RuntimeBackendDispatcher {
387
+ dispatch(request: RuntimeBackendDispatchRequest): Promise<RuntimeBackendDispatchResponse>;
388
+ }
362
389
  interface RuntimeBackendGatewayOptions {
363
- registry: RuntimeBackendRegistry;
390
+ registry: RuntimeBackendDispatcher;
364
391
  defaultWorkspaceId?: string;
365
392
  }
366
393
  declare function runtimeBackendGateway(app: FastifyInstance, opts: RuntimeBackendGatewayOptions): Promise<void>;
367
394
 
368
- export { BoringPluginAssetManager, BoringPluginEvent, BoringPluginFrontTarget, BoringPluginFrontTargetResolver, BoringPluginListEntry, type BoringPluginScanResult, BoringPluginSource, BoringPluginSourceInput, BoringServerPluginManifest, type PluginRestartWarning, type RuntimeBackendDiagnostic, type RuntimeBackendDispatchRequest, type RuntimeBackendDispatchResponse, RuntimeBackendError, type RuntimeBackendGatewayOptions, RuntimeBackendRegistry, type RuntimeBackendReloadResult, UiBridge, type UiRoutesOptions, WorkspaceServerPluginAsset, aggregatePluginPrompts, boringPluginRoutes, buildBoringSystemPrompt, collectRestartWarnings, createExecUiTool, createGetUiStateTool, createWorkspaceUiTools, definePluginAsset, pluginFileSignature, preflightBoringPlugins, readBoringPlugins, readPluginSignatureCache, resolvePluginAssetPath, runtimeBackendGateway, scanBoringPlugins, uiRoutes, writePluginSignatureCache };
395
+ export { BoringPluginAssetManager, BoringPluginEvent, BoringPluginFrontTarget, BoringPluginFrontTargetResolver, BoringPluginListEntry, type BoringPluginScanResult, BoringPluginSource, BoringPluginSourceInput, BoringServerPluginManifest, type PluginRestartWarning, type RuntimeBackendDiagnostic, type RuntimeBackendDispatchRequest, type RuntimeBackendDispatchResponse, type RuntimeBackendDispatcher, RuntimeBackendError, type RuntimeBackendGatewayOptions, RuntimeBackendRegistry, type RuntimeBackendReloadResult, UiBridge, type UiRoutesOptions, WorkspaceServerPluginAsset, aggregatePluginPrompts, boringPluginRoutes, buildBoringSystemPrompt, collectRestartWarnings, createExecUiTool, createGetUiStateTool, createWorkspaceUiTools, definePluginAsset, pluginFileSignature, preflightBoringPlugins, readBoringPlugins, readPluginSignatureCache, resolvePluginAssetPath, runtimeBackendGateway, scanBoringPlugins, uiRoutes, writePluginSignatureCache };
package/dist/server.js CHANGED
@@ -1617,7 +1617,6 @@ var BoringPluginAssetManager = class {
1617
1617
  pluginDirs;
1618
1618
  errorRoot;
1619
1619
  frontTargetResolver;
1620
- includeLegacyFrontUrl;
1621
1620
  loaded = /* @__PURE__ */ new Map();
1622
1621
  revisions = /* @__PURE__ */ new Map();
1623
1622
  listeners = /* @__PURE__ */ new Set();
@@ -1628,7 +1627,16 @@ var BoringPluginAssetManager = class {
1628
1627
  this.pluginDirs = options.pluginDirs;
1629
1628
  this.errorRoot = options.errorRoot ?? join5(process.cwd(), ".pi", "extensions");
1630
1629
  this.frontTargetResolver = options.frontTargetResolver;
1631
- this.includeLegacyFrontUrl = options.includeLegacyFrontUrl ?? true;
1630
+ }
1631
+ /**
1632
+ * Replace the scanned source roots. Lets hosts re-resolve discovery inputs
1633
+ * (e.g. workspace `.pi/settings.json` package sources, which can gain
1634
+ * entries after `boring-ui-plugin install`) before the next load() so
1635
+ * `/reload` picks up newly installed plugin sources without a process
1636
+ * restart.
1637
+ */
1638
+ setPluginDirs(pluginDirs) {
1639
+ this.pluginDirs = pluginDirs;
1632
1640
  }
1633
1641
  preflight() {
1634
1642
  return preflightBoringPlugins(this.pluginDirs);
@@ -1636,6 +1644,17 @@ var BoringPluginAssetManager = class {
1636
1644
  list() {
1637
1645
  return [...this.loaded.values()].map((plugin) => this.toListEntry(plugin));
1638
1646
  }
1647
+ /**
1648
+ * Plugins whose front lifecycle the SSE channel owns. Internal plugins are
1649
+ * app code — their front is statically bundled by the host app and must
1650
+ * never be re-imported through the hot-reload pipeline (a second module
1651
+ * instance would carry a fresh React context identity, breaking
1652
+ * provider ↔ panel lookups). They are loaded server-side (routes, agent
1653
+ * tools, Pi snapshot) but excluded from SSE replay and live events.
1654
+ */
1655
+ listExternal() {
1656
+ return [...this.loaded.values()].filter((plugin) => plugin.source.kind === "external").map((plugin) => this.toListEntry(plugin));
1657
+ }
1639
1658
  getError(pluginId) {
1640
1659
  const path = this.errorPath(pluginId);
1641
1660
  if (!path || !existsSync4(path)) return null;
@@ -1712,9 +1731,7 @@ ${prompts.join("\n\n")}` } : {}
1712
1731
  } catch {
1713
1732
  }
1714
1733
  }
1715
- const event = { type: "boring.plugin.unload", id, revision };
1716
- events.push(event);
1717
- this.emit(event);
1734
+ this.record(events, { type: "boring.plugin.unload", id, revision }, previous?.source);
1718
1735
  }
1719
1736
  for (const plugin of nextPlugins) {
1720
1737
  try {
@@ -1745,22 +1762,18 @@ ${prompts.join("\n\n")}` } : {}
1745
1762
  boring: plugin.boring,
1746
1763
  version: plugin.version,
1747
1764
  revision,
1748
- ...this.frontUrlPayload(plugin.frontUrl),
1749
1765
  ...frontTarget ? { frontTarget } : {},
1750
1766
  ...requiresRestart.length > 0 ? { requiresRestart } : {}
1751
1767
  };
1752
- events.push(event);
1753
- this.emit(event);
1768
+ this.record(events, event, plugin.source);
1754
1769
  } catch (error) {
1755
1770
  const revision = this.bumpRevision(plugin.id);
1756
1771
  const message = error instanceof Error ? error.stack ?? error.message : String(error);
1757
1772
  this.writeError(plugin.id, message);
1758
- const event = { type: "boring.plugin.error", id: plugin.id, revision, message };
1759
1773
  const loadError = { id: plugin.id, revision, message };
1760
1774
  this.lastErrors.set(plugin.id, loadError);
1761
1775
  errors.push(loadError);
1762
- events.push(event);
1763
- this.emit(event);
1776
+ this.record(events, { type: "boring.plugin.error", id: plugin.id, revision, message }, plugin.source);
1764
1777
  }
1765
1778
  }
1766
1779
  return { loaded: this.list(), events, errors };
@@ -1793,16 +1806,14 @@ Plugin dir: ${error.pluginDir}`;
1793
1806
  ...plugin.pi ? { pi: plugin.pi } : {},
1794
1807
  version: plugin.version,
1795
1808
  revision: plugin.revision,
1796
- ...this.frontUrlPayload(plugin.frontUrl),
1797
1809
  ...plugin.frontTarget ? { frontTarget: plugin.frontTarget } : {}
1798
1810
  };
1799
1811
  }
1800
- frontUrlPayload(frontUrl) {
1801
- if (!this.includeLegacyFrontUrl || !frontUrl) return {};
1802
- return { frontUrl };
1803
- }
1804
1812
  resolveFrontTarget(plugin, revision) {
1805
- if (!plugin.frontPath || !this.frontTargetResolver) return void 0;
1813
+ if (!plugin.frontPath) return void 0;
1814
+ if (!this.frontTargetResolver) {
1815
+ return plugin.frontUrl ? { kind: "module-url", entryUrl: plugin.frontUrl, revision } : void 0;
1816
+ }
1806
1817
  const frontEntrySubpath = typeof plugin.boring.front === "string" ? plugin.boring.front.replace(/^\.\//, "") : normalizePluginSubpath(plugin.rootDir, plugin.frontPath);
1807
1818
  const frontTarget = this.frontTargetResolver(plugin, {
1808
1819
  revision,
@@ -1811,6 +1822,16 @@ Plugin dir: ${error.pluginDir}`;
1811
1822
  if (!frontTarget) return void 0;
1812
1823
  return { ...frontTarget, revision };
1813
1824
  }
1825
+ /**
1826
+ * Append to the load result's events array and emit on the SSE channel —
1827
+ * unless the source is internal. Internal plugins are app code: their
1828
+ * events stay in the load result (for /reload diagnostics and restart
1829
+ * warnings) but never reach SSE subscribers (see listExternal).
1830
+ */
1831
+ record(events, event, source) {
1832
+ events.push(event);
1833
+ if (source?.kind !== "internal") this.emit(event);
1834
+ }
1814
1835
  emit(event) {
1815
1836
  for (const listener of [...this.listeners]) {
1816
1837
  try {
@@ -1895,14 +1916,13 @@ async function boringPluginRoutes(app, opts) {
1895
1916
  }
1896
1917
  write(event.type, payload);
1897
1918
  });
1898
- for (const plugin of manager.list()) {
1919
+ for (const plugin of manager.listExternal()) {
1899
1920
  write("boring.plugin.load", {
1900
1921
  type: "boring.plugin.load",
1901
1922
  id: plugin.id,
1902
1923
  boring: plugin.boring,
1903
1924
  version: plugin.version,
1904
1925
  revision: plugin.revision,
1905
- ...plugin.frontUrl ? { frontUrl: plugin.frontUrl } : {},
1906
1926
  ...plugin.frontTarget ? { frontTarget: plugin.frontTarget } : {},
1907
1927
  replay: true
1908
1928
  });
package/dist/shared.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { A as AgentTool, C as CommandResult, J as JSONSchema, T as ToolExecContext, c as ToolResult, U as UiBridge, a as UiCommand, b as UiState } from './ui-bridge-DFNem0df.js';
1
+ export { C as CommandResult, U as UiBridge, a as UiCommand, b as UiState } from './ui-bridge-LeBuZqfA.js';
2
2
  export { C as CommandConfig, P as PaneProps, a as PanelConfig, b as PanelRegistration, S as SurfaceOpenRequest, c as SurfacePanelResolution, d as SurfaceResolverConfig, e as SurfaceResolverRegistration, W as WORKSPACE_OPEN_PATH_SURFACE_KIND, f as definePanel } from './surface-obE7YwJk.js';
3
+ export { A as AgentTool, J as JSONSchema, T as ToolExecContext, a as ToolResult } from './agent-tool-CB0RQyx9.js';
3
4
  import 'react';
4
5
  import 'dockview-react';
5
6
 
package/dist/testing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as Ba } from "react/jsx-runtime";
2
2
  import * as Pa from "react";
3
3
  import { createElement as is, useMemo as wn, useLayoutEffect as us, isValidElement as ss, cloneElement as ds, useSyncExternalStore as Gi } from "react";
4
- import { h as cs, q as fs, o as ps } from "./WorkspaceProvider-BhRPFy5R.js";
4
+ import { h as cs, q as fs, o as ps } from "./WorkspaceProvider-CX_4aV6Z.js";
5
5
  import { d as ms } from "./panel-DnvDNQac.js";
6
6
  import * as bs from "react-dom/test-utils";
7
7
  import ka from "react-dom";
@@ -1,34 +1,3 @@
1
- type JSONSchema = Record<string, unknown>;
2
- type ToolReadinessRequirement = 'workspace-fs' | 'sandbox-exec' | 'ui-bridge' | 'runtime-dependencies' | `runtime:${string}`;
3
- interface ToolExecContext {
4
- abortSignal: AbortSignal;
5
- toolCallId: string;
6
- onUpdate?: (partial: string) => void;
7
- /** Agent chat/session id executing this tool, when known. */
8
- sessionId?: string;
9
- }
10
- interface ToolResult {
11
- content: Array<{
12
- type: "text";
13
- text: string;
14
- }>;
15
- isError?: boolean;
16
- details?: unknown;
17
- }
18
- /**
19
- * Structural tool contract accepted from workspace plugins and UI tool
20
- * factories. Kept agent-runtime-neutral so only the app integration layer
21
- * needs to import @hachej/boring-agent.
22
- */
23
- interface AgentTool {
24
- name: string;
25
- description: string;
26
- promptSnippet?: string;
27
- readinessRequirements?: ToolReadinessRequirement[];
28
- parameters: JSONSchema;
29
- execute(params: Record<string, unknown>, ctx: ToolExecContext): Promise<ToolResult>;
30
- }
31
-
32
1
  interface UiBridge {
33
2
  getState(): Promise<UiState | null>;
34
3
  setState(state: UiState): Promise<void>;
@@ -99,4 +68,4 @@ interface CommandResult {
99
68
  };
100
69
  }
101
70
 
102
- export type { AgentTool as A, CommandResult as C, JSONSchema as J, ToolExecContext as T, UiBridge as U, UiCommand as a, UiState as b, ToolResult as c };
71
+ export type { CommandResult as C, UiBridge as U, UiCommand as a, UiState as b };
@@ -1817,18 +1817,12 @@
1817
1817
  .min-w-\[10rem\] {
1818
1818
  min-width: 10rem;
1819
1819
  }
1820
- .min-w-\[350px\] {
1821
- min-width: 350px;
1822
- }
1823
1820
  .flex-1 {
1824
1821
  flex: 1;
1825
1822
  }
1826
1823
  .flex-\[0_0_0px\] {
1827
1824
  flex: 0 0 0px;
1828
1825
  }
1829
- .flex-\[1_0_350px\] {
1830
- flex: 1 0 350px;
1831
- }
1832
1826
  .shrink-0 {
1833
1827
  flex-shrink: 0;
1834
1828
  }
@@ -1960,9 +1954,6 @@
1960
1954
  .self-center {
1961
1955
  align-self: center;
1962
1956
  }
1963
- .self-stretch {
1964
- align-self: stretch;
1965
- }
1966
1957
  .truncate {
1967
1958
  overflow: hidden;
1968
1959
  text-overflow: ellipsis;
@@ -1974,15 +1965,9 @@
1974
1965
  .overflow-hidden {
1975
1966
  overflow: hidden;
1976
1967
  }
1977
- .overflow-x-auto {
1978
- overflow-x: auto;
1979
- }
1980
1968
  .overflow-y-auto {
1981
1969
  overflow-y: auto;
1982
1970
  }
1983
- .overflow-y-hidden {
1984
- overflow-y: hidden;
1985
- }
1986
1971
  .rounded {
1987
1972
  border-radius: 0.25rem;
1988
1973
  }
@@ -2098,9 +2083,6 @@
2098
2083
  .bg-\[color\:oklch\(from_var\(--background\)_calc\(l-0\.01\)_c_h\)\] {
2099
2084
  background-color: oklch(from var(--background) calc(l - 0.01) c h);
2100
2085
  }
2101
- .bg-\[color\:oklch\(from_var\(--border\)_l_c_h\/0\.7\)\] {
2102
- background-color: oklch(from var(--border) l c h/0.7);
2103
- }
2104
2086
  .bg-\[color\:oklch\(from_var\(--foreground\)_l_c_h\/0\.035\)\] {
2105
2087
  background-color: oklch(from var(--foreground) l c h/0.035);
2106
2088
  }
@@ -2496,12 +2478,6 @@
2496
2478
  .text-foreground {
2497
2479
  color: var(--boring-foreground);
2498
2480
  }
2499
- .text-foreground\/40 {
2500
- color: var(--boring-foreground);
2501
- @supports (color: color-mix(in lab, red, red)) {
2502
- color: color-mix(in oklab, var(--boring-foreground) 40%, transparent);
2503
- }
2504
- }
2505
2481
  .text-foreground\/65 {
2506
2482
  color: var(--boring-foreground);
2507
2483
  @supports (color: color-mix(in lab, red, red)) {
@@ -2693,11 +2669,6 @@
2693
2669
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
2694
2670
  transition-duration: var(--tw-duration, var(--default-transition-duration));
2695
2671
  }
2696
- .transition-\[flex-grow\,flex-basis\,min-width\] {
2697
- transition-property: flex-grow,flex-basis,min-width;
2698
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
2699
- transition-duration: var(--tw-duration, var(--default-transition-duration));
2700
- }
2701
2672
  .transition-\[flex-grow\,flex-basis\,width\,min-width\,max-width\] {
2702
2673
  transition-property: flex-grow,flex-basis,width,min-width,max-width;
2703
2674
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
@@ -2740,10 +2711,6 @@
2740
2711
  --tw-duration: 200ms;
2741
2712
  transition-duration: 200ms;
2742
2713
  }
2743
- .duration-\[240ms\] {
2744
- --tw-duration: 240ms;
2745
- transition-duration: 240ms;
2746
- }
2747
2714
  .duration-\[280ms\] {
2748
2715
  --tw-duration: 280ms;
2749
2716
  transition-duration: 280ms;
@@ -2764,6 +2731,13 @@
2764
2731
  -webkit-user-select: none;
2765
2732
  user-select: none;
2766
2733
  }
2734
+ .group-hover\:inline-flex {
2735
+ &:is(:where(.group):hover *) {
2736
+ @media (hover: hover) {
2737
+ display: inline-flex;
2738
+ }
2739
+ }
2740
+ }
2767
2741
  .group-hover\:text-muted-foreground {
2768
2742
  &:is(:where(.group):hover *) {
2769
2743
  @media (hover: hover) {
@@ -2783,9 +2757,9 @@
2783
2757
  color: var(--accent);
2784
2758
  }
2785
2759
  }
2786
- .group-data-\[focused\=true\]\:opacity-100 {
2760
+ .group-data-\[focused\=true\]\:inline-flex {
2787
2761
  &:is(:where(.group)[data-focused="true"] *) {
2788
- opacity: 100%;
2762
+ display: inline-flex;
2789
2763
  }
2790
2764
  }
2791
2765
  .placeholder\:text-muted-foreground\/70 {
@@ -6791,18 +6765,43 @@
6791
6765
  opacity: 0.7;
6792
6766
  }
6793
6767
 
6794
- /* Sash (resize handle) */
6768
+ /* Sash (resize handle)
6769
+ Dockview's default 4px grab zone is easy to miss. Keep the visible divider
6770
+ subtle, but expand the pointer target to 12px so pane resizing is forgiving. */
6771
+ .dv-shell {
6772
+ --dv-sash-color: transparent;
6773
+ --dv-active-sash-color: transparent;
6774
+ --dv-active-sash-transition-delay: 0s;
6775
+ }
6776
+
6795
6777
  .dv-shell .sash-container .sash,
6796
6778
  .dv-shell .dv-sash-container .dv-sash {
6797
- transition: background-color 0.2s;
6779
+ transition: box-shadow 0.16s cubic-bezier(0.22, 1, 0.36, 1),
6780
+ background-color 0.16s cubic-bezier(0.22, 1, 0.36, 1);
6781
+ }
6782
+
6783
+ .dv-shell .dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash {
6784
+ width: 12px;
6785
+ margin-left: -4px;
6786
+ }
6787
+
6788
+ .dv-shell .dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash {
6789
+ height: 12px;
6790
+ margin-top: -4px;
6791
+ }
6792
+
6793
+ .dv-shell .dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash:hover,
6794
+ .dv-shell .dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash:active {
6795
+ box-shadow: inset 0 0 0 1px oklch(from var(--primary) l c h / 0.45),
6796
+ inset 5px 0 0 oklch(from var(--primary) l c h / 0.35),
6797
+ inset 6px 0 0 oklch(from var(--primary) l c h / 0.35);
6798
6798
  }
6799
6799
 
6800
- .dv-shell .sash-container .sash:hover,
6801
- .dv-shell .sash-container .sash.active,
6802
- .dv-shell .dv-sash-container .dv-sash:hover,
6803
- .dv-shell .dv-sash-container .dv-sash.dv-active {
6804
- background-color: var(--primary);
6805
- transition-delay: 0.15s;
6800
+ .dv-shell .dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash:hover,
6801
+ .dv-shell .dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash:active {
6802
+ box-shadow: inset 0 0 0 1px oklch(from var(--primary) l c h / 0.45),
6803
+ inset 0 5px 0 oklch(from var(--primary) l c h / 0.35),
6804
+ inset 0 6px 0 oklch(from var(--primary) l c h / 0.35);
6806
6805
  }
6807
6806
 
6808
6807
  /* Drop target overlay */
@@ -383,13 +383,6 @@ export declare interface ChatLayoutProps {
383
383
  onCreateChatPaneAfter?: (id: string) => void;
384
384
  onDropChatSession?: (sessionId: string) => void;
385
385
  flashChatPaneId?: string | null;
386
- /**
387
- * Chat stage layout engine. `dock` enables the dockview-backed stage
388
- * (drag headers to split in any direction); defaults to the flex row.
389
- * The `boring-workspace:chat-pane-engine` localStorage key overrides
390
- * when no explicit value is passed.
391
- */
392
- chatPaneEngine?: ChatPaneEngine | null;
393
386
  surface?: string | null;
394
387
  surfaceParams?: Record<string, unknown>;
395
388
  surfaceOverlay?: ReactNode;
@@ -410,18 +403,6 @@ declare interface ChatPaneDescriptor {
410
403
  params?: Record<string, unknown>;
411
404
  }
412
405
 
413
- /**
414
- * Layout engine for the chat pane stage.
415
- *
416
- * - `flex` (default): panes lay out as a single row of vertical splits.
417
- * - `dock`: dockview-backed stage — drag pane headers to split in any
418
- * direction and resize; geometry persists per workspace.
419
- *
420
- * Resolution order: explicit prop, then the
421
- * `boring-workspace:chat-pane-engine` localStorage override, then `flex`.
422
- */
423
- declare type ChatPaneEngine = "flex" | "dock";
424
-
425
406
  export declare const closePanelSchema: z.ZodObject<{
426
407
  id: z.ZodString;
427
408
  }, "strip", z.ZodTypeAny, {