@forgeax/app-shell 0.8.0 → 0.9.1

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/README.md CHANGED
@@ -84,6 +84,20 @@ import { PanelSurface } from '@forgeax/app-shell/react';
84
84
  </PanelSurface>
85
85
  ```
86
86
 
87
+ Dock state adapters are available without a Dockview runtime dependency:
88
+
89
+ ```ts
90
+ import {
91
+ hasMountedPanelPlacement,
92
+ isDockPanelVisible,
93
+ pruneSerializedDockLayout,
94
+ trackDockPanelVisibility,
95
+ } from '@forgeax/app-shell/dock';
96
+ ```
97
+
98
+ Products own event subscription and layout persistence; the adapters only track
99
+ stable panel ids and transform structural serialized snapshots.
100
+
87
101
  ## Ownership boundary
88
102
 
89
103
  | Package | Owns |
package/dist/dock.d.ts CHANGED
@@ -25,6 +25,24 @@ interface DockRegionEntry {
25
25
  }
26
26
  declare function registerDockRegion(entry: DockRegionEntry): () => void;
27
27
  declare function getDockRegions(): DockRegionEntry[];
28
+ /** Track one live mount of a dock panel and return an idempotent release. */
29
+ declare function trackDockPanelVisibility(panelId: string): () => void;
30
+ declare function isDockPanelVisible(panelId: string): boolean;
31
+ declare function hasMountedPanelPlacement(panelIds: readonly string[], mountedPanelIds: ReadonlySet<string>): boolean;
32
+ interface SerializedDockPanelLike {
33
+ readonly contentComponent?: string;
34
+ }
35
+ interface SerializedDockLayoutLike {
36
+ readonly panels?: Readonly<Record<string, SerializedDockPanelLike>>;
37
+ readonly grid?: Readonly<{
38
+ readonly root?: unknown;
39
+ }>;
40
+ }
41
+ /**
42
+ * Remove panels that the current shell cannot render, then prune empty grid
43
+ * leaves and branches. The input snapshot is never mutated.
44
+ */
45
+ declare function pruneSerializedDockLayout<T extends SerializedDockLayoutLike>(layout: T, knownComponents: ReadonlySet<string>, allowedPanelIds?: ReadonlySet<string>): T | null;
28
46
  type SideEdge = 'left' | 'right';
29
47
  interface RectLike {
30
48
  left: number;
@@ -62,4 +80,4 @@ declare function handleCrossInstanceDrop(event: CrossInstanceDropEvent, targetRe
62
80
  titleFor?: (panelId: string) => string | undefined;
63
81
  }): void;
64
82
 
65
- export { type CrossInstanceDropEvent, DOCK_REGIONS, type DockRegion, type DockRegionEntry, type DockviewApiLike, type PanelDescriptorLite, REGIONS, type RectLike, type Region, type SideEdge, getDockRegions, getDockviewApi, handleCrossInstanceDrop, isDockRegion, isOnSideEdge, nearerSideEdge, registerDockRegion, registerDockviewApi, resolveRegion };
83
+ export { type CrossInstanceDropEvent, DOCK_REGIONS, type DockRegion, type DockRegionEntry, type DockviewApiLike, type PanelDescriptorLite, REGIONS, type RectLike, type Region, type SerializedDockLayoutLike, type SerializedDockPanelLike, type SideEdge, getDockRegions, getDockviewApi, handleCrossInstanceDrop, hasMountedPanelPlacement, isDockPanelVisible, isDockRegion, isOnSideEdge, nearerSideEdge, pruneSerializedDockLayout, registerDockRegion, registerDockviewApi, resolveRegion, trackDockPanelVisibility };
package/dist/dock.js CHANGED
@@ -27,6 +27,63 @@ function registerDockRegion(entry) {
27
27
  function getDockRegions() {
28
28
  return [...dockRegions.values()];
29
29
  }
30
+ var visibleDockPanels = /* @__PURE__ */ new Map();
31
+ function trackDockPanelVisibility(panelId) {
32
+ visibleDockPanels.set(panelId, (visibleDockPanels.get(panelId) ?? 0) + 1);
33
+ let active = true;
34
+ return () => {
35
+ if (!active) return;
36
+ active = false;
37
+ const next = (visibleDockPanels.get(panelId) ?? 1) - 1;
38
+ if (next > 0) visibleDockPanels.set(panelId, next);
39
+ else visibleDockPanels.delete(panelId);
40
+ };
41
+ }
42
+ function isDockPanelVisible(panelId) {
43
+ return visibleDockPanels.has(panelId);
44
+ }
45
+ function hasMountedPanelPlacement(panelIds, mountedPanelIds) {
46
+ return panelIds.some((id) => mountedPanelIds.has(id));
47
+ }
48
+ function pruneSerializedDockLayout(layout, knownComponents, allowedPanelIds) {
49
+ if (!layout.panels) return layout;
50
+ const removed = new Set(Object.entries(layout.panels).filter(([id, panel]) => !knownComponents.has(panel.contentComponent ?? id) || allowedPanelIds !== void 0 && !allowedPanelIds.has(id)).map(([id]) => id));
51
+ if (removed.size === 0) return layout;
52
+ const panels = Object.fromEntries(
53
+ Object.entries(layout.panels).filter(([id]) => !removed.has(id))
54
+ );
55
+ if (Object.keys(panels).length === 0) return null;
56
+ const pruneNode = (node) => {
57
+ if (!node || typeof node !== "object") return node;
58
+ const value = node;
59
+ if (value.type === "leaf") {
60
+ const data = value.data;
61
+ if (!Array.isArray(data?.views)) return node;
62
+ const views = data.views.filter((id) => !removed.has(id));
63
+ if (views.length === 0) return null;
64
+ return {
65
+ ...value,
66
+ data: {
67
+ ...data,
68
+ views,
69
+ activeView: views.includes(data.activeView ?? "") ? data.activeView : views[0]
70
+ }
71
+ };
72
+ }
73
+ if (value.type === "branch" && Array.isArray(value.data)) {
74
+ const children = value.data.map(pruneNode).filter((child) => child !== null);
75
+ return children.length === 0 ? null : { ...value, data: children };
76
+ }
77
+ return node;
78
+ };
79
+ const root = pruneNode(layout.grid?.root);
80
+ if (root === null) return null;
81
+ return {
82
+ ...layout,
83
+ panels,
84
+ grid: layout.grid ? { ...layout.grid, root } : layout.grid
85
+ };
86
+ }
30
87
  function isOnSideEdge(location) {
31
88
  return location.type === "edge" && (location.position === "left" || location.position === "right");
32
89
  }
@@ -84,10 +141,14 @@ export {
84
141
  getDockRegions,
85
142
  getDockviewApi,
86
143
  handleCrossInstanceDrop,
144
+ hasMountedPanelPlacement,
145
+ isDockPanelVisible,
87
146
  isDockRegion,
88
147
  isOnSideEdge,
89
148
  nearerSideEdge,
149
+ pruneSerializedDockLayout,
90
150
  registerDockRegion,
91
151
  registerDockviewApi,
92
- resolveRegion
152
+ resolveRegion,
153
+ trackDockPanelVisibility
93
154
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgeax/app-shell",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Generic Dock, Panel, Window, and Slot composition primitives",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",