@hachej/boring-workspace 0.1.36 → 0.1.38

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/server.js CHANGED
@@ -1294,8 +1294,8 @@ function packagePathContainmentIssues(rootDir, pkg) {
1294
1294
  if (boring?.server !== false && boring?.server !== void 0) {
1295
1295
  push(pathPreflightIssue(rootDir, boring.server, "boring.server"));
1296
1296
  }
1297
- pi?.extensions?.forEach((value, index) => push(pathPreflightIssue(rootDir, value, `pi.extensions[${index}]`)));
1298
- pi?.skills?.forEach((value, index) => push(pathPreflightIssue(rootDir, value, `pi.skills[${index}]`)));
1297
+ pi?.extensions?.forEach((value, index) => push(pathPreflightIssue(rootDir, value, `pi.extensions[${index}]`, { mustExist: true })));
1298
+ pi?.skills?.forEach((value, index) => push(pathPreflightIssue(rootDir, value, `pi.skills[${index}]`, { mustExist: true })));
1299
1299
  return issues;
1300
1300
  }
1301
1301
  function discoverBoringPluginDirs(pluginDirs) {
@@ -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,6 @@ 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;
1632
1630
  }
1633
1631
  /**
1634
1632
  * Replace the scanned source roots. Lets hosts re-resolve discovery inputs
@@ -1646,6 +1644,17 @@ var BoringPluginAssetManager = class {
1646
1644
  list() {
1647
1645
  return [...this.loaded.values()].map((plugin) => this.toListEntry(plugin));
1648
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
+ }
1649
1658
  getError(pluginId) {
1650
1659
  const path = this.errorPath(pluginId);
1651
1660
  if (!path || !existsSync4(path)) return null;
@@ -1722,9 +1731,7 @@ ${prompts.join("\n\n")}` } : {}
1722
1731
  } catch {
1723
1732
  }
1724
1733
  }
1725
- const event = { type: "boring.plugin.unload", id, revision };
1726
- events.push(event);
1727
- this.emit(event);
1734
+ this.record(events, { type: "boring.plugin.unload", id, revision }, previous?.source);
1728
1735
  }
1729
1736
  for (const plugin of nextPlugins) {
1730
1737
  try {
@@ -1755,22 +1762,18 @@ ${prompts.join("\n\n")}` } : {}
1755
1762
  boring: plugin.boring,
1756
1763
  version: plugin.version,
1757
1764
  revision,
1758
- ...this.frontUrlPayload(plugin.frontUrl),
1759
1765
  ...frontTarget ? { frontTarget } : {},
1760
1766
  ...requiresRestart.length > 0 ? { requiresRestart } : {}
1761
1767
  };
1762
- events.push(event);
1763
- this.emit(event);
1768
+ this.record(events, event, plugin.source);
1764
1769
  } catch (error) {
1765
1770
  const revision = this.bumpRevision(plugin.id);
1766
1771
  const message = error instanceof Error ? error.stack ?? error.message : String(error);
1767
1772
  this.writeError(plugin.id, message);
1768
- const event = { type: "boring.plugin.error", id: plugin.id, revision, message };
1769
1773
  const loadError = { id: plugin.id, revision, message };
1770
1774
  this.lastErrors.set(plugin.id, loadError);
1771
1775
  errors.push(loadError);
1772
- events.push(event);
1773
- this.emit(event);
1776
+ this.record(events, { type: "boring.plugin.error", id: plugin.id, revision, message }, plugin.source);
1774
1777
  }
1775
1778
  }
1776
1779
  return { loaded: this.list(), events, errors };
@@ -1803,16 +1806,14 @@ Plugin dir: ${error.pluginDir}`;
1803
1806
  ...plugin.pi ? { pi: plugin.pi } : {},
1804
1807
  version: plugin.version,
1805
1808
  revision: plugin.revision,
1806
- ...this.frontUrlPayload(plugin.frontUrl),
1807
1809
  ...plugin.frontTarget ? { frontTarget: plugin.frontTarget } : {}
1808
1810
  };
1809
1811
  }
1810
- frontUrlPayload(frontUrl) {
1811
- if (!this.includeLegacyFrontUrl || !frontUrl) return {};
1812
- return { frontUrl };
1813
- }
1814
1812
  resolveFrontTarget(plugin, revision) {
1815
- 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
+ }
1816
1817
  const frontEntrySubpath = typeof plugin.boring.front === "string" ? plugin.boring.front.replace(/^\.\//, "") : normalizePluginSubpath(plugin.rootDir, plugin.frontPath);
1817
1818
  const frontTarget = this.frontTargetResolver(plugin, {
1818
1819
  revision,
@@ -1821,6 +1822,16 @@ Plugin dir: ${error.pluginDir}`;
1821
1822
  if (!frontTarget) return void 0;
1822
1823
  return { ...frontTarget, revision };
1823
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
+ }
1824
1835
  emit(event) {
1825
1836
  for (const listener of [...this.listeners]) {
1826
1837
  try {
@@ -1905,14 +1916,13 @@ async function boringPluginRoutes(app, opts) {
1905
1916
  }
1906
1917
  write(event.type, payload);
1907
1918
  });
1908
- for (const plugin of manager.list()) {
1919
+ for (const plugin of manager.listExternal()) {
1909
1920
  write("boring.plugin.load", {
1910
1921
  type: "boring.plugin.load",
1911
1922
  id: plugin.id,
1912
1923
  boring: plugin.boring,
1913
1924
  version: plugin.version,
1914
1925
  revision: plugin.revision,
1915
- ...plugin.frontUrl ? { frontUrl: plugin.frontUrl } : {},
1916
1926
  ...plugin.frontTarget ? { frontTarget: plugin.frontTarget } : {},
1917
1927
  replay: true
1918
1928
  });
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-CpK401MG.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";
@@ -2731,6 +2731,13 @@
2731
2731
  -webkit-user-select: none;
2732
2732
  user-select: none;
2733
2733
  }
2734
+ .group-hover\:inline-flex {
2735
+ &:is(:where(.group):hover *) {
2736
+ @media (hover: hover) {
2737
+ display: inline-flex;
2738
+ }
2739
+ }
2740
+ }
2734
2741
  .group-hover\:text-muted-foreground {
2735
2742
  &:is(:where(.group):hover *) {
2736
2743
  @media (hover: hover) {
@@ -2750,9 +2757,9 @@
2750
2757
  color: var(--accent);
2751
2758
  }
2752
2759
  }
2753
- .group-data-\[focused\=true\]\:opacity-100 {
2760
+ .group-data-\[focused\=true\]\:inline-flex {
2754
2761
  &:is(:where(.group)[data-focused="true"] *) {
2755
- opacity: 100%;
2762
+ display: inline-flex;
2756
2763
  }
2757
2764
  }
2758
2765
  .placeholder\:text-muted-foreground\/70 {
@@ -6758,18 +6765,43 @@
6758
6765
  opacity: 0.7;
6759
6766
  }
6760
6767
 
6761
- /* 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
+
6762
6777
  .dv-shell .sash-container .sash,
6763
6778
  .dv-shell .dv-sash-container .dv-sash {
6764
- 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);
6765
6798
  }
6766
6799
 
6767
- .dv-shell .sash-container .sash:hover,
6768
- .dv-shell .sash-container .sash.active,
6769
- .dv-shell .dv-sash-container .dv-sash:hover,
6770
- .dv-shell .dv-sash-container .dv-sash.dv-active {
6771
- background-color: var(--primary);
6772
- 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);
6773
6805
  }
6774
6806
 
6775
6807
  /* Drop target overlay */