@ai-matrx/summon 0.0.0-stage → 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 (87) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +380 -2
  4. package/dist/closeBinding-C6raC3qJ.d.cts +20 -0
  5. package/dist/closeBinding-C6raC3qJ.d.ts +20 -0
  6. package/dist/dialog.cjs +60 -0
  7. package/dist/dialog.cjs.map +1 -0
  8. package/dist/dialog.d.cts +16 -0
  9. package/dist/dialog.d.ts +16 -0
  10. package/dist/dialog.js +38 -0
  11. package/dist/dialog.js.map +1 -0
  12. package/dist/errors-eJrKXq33.d.cts +30 -0
  13. package/dist/errors-eJrKXq33.d.ts +30 -0
  14. package/dist/handles.cjs +411 -0
  15. package/dist/handles.cjs.map +1 -0
  16. package/dist/handles.d.cts +166 -0
  17. package/dist/handles.d.ts +166 -0
  18. package/dist/handles.js +388 -0
  19. package/dist/handles.js.map +1 -0
  20. package/dist/index.cjs +2383 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +1259 -0
  23. package/dist/index.d.ts +1259 -0
  24. package/dist/index.js +2363 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lite.cjs +460 -0
  27. package/dist/lite.cjs.map +1 -0
  28. package/dist/lite.d.cts +56 -0
  29. package/dist/lite.d.ts +56 -0
  30. package/dist/lite.js +444 -0
  31. package/dist/lite.js.map +1 -0
  32. package/dist/next.cjs +61 -0
  33. package/dist/next.cjs.map +1 -0
  34. package/dist/next.d.cts +5 -0
  35. package/dist/next.d.ts +5 -0
  36. package/dist/next.js +39 -0
  37. package/dist/next.js.map +1 -0
  38. package/dist/persistence-data.cjs +143 -0
  39. package/dist/persistence-data.cjs.map +1 -0
  40. package/dist/persistence-data.d.cts +72 -0
  41. package/dist/persistence-data.d.ts +72 -0
  42. package/dist/persistence-data.js +120 -0
  43. package/dist/persistence-data.js.map +1 -0
  44. package/dist/persistence.cjs +1073 -0
  45. package/dist/persistence.cjs.map +1 -0
  46. package/dist/persistence.d.cts +236 -0
  47. package/dist/persistence.d.ts +236 -0
  48. package/dist/persistence.js +1077 -0
  49. package/dist/persistence.js.map +1 -0
  50. package/dist/react.cjs +733 -0
  51. package/dist/react.cjs.map +1 -0
  52. package/dist/react.d.cts +261 -0
  53. package/dist/react.d.ts +261 -0
  54. package/dist/react.js +718 -0
  55. package/dist/react.js.map +1 -0
  56. package/dist/sheet.cjs +71 -0
  57. package/dist/sheet.cjs.map +1 -0
  58. package/dist/sheet.d.cts +17 -0
  59. package/dist/sheet.d.ts +17 -0
  60. package/dist/sheet.js +49 -0
  61. package/dist/sheet.js.map +1 -0
  62. package/dist/styles.css +78 -0
  63. package/dist/testing.cjs +132 -0
  64. package/dist/testing.cjs.map +1 -0
  65. package/dist/testing.d.cts +44 -0
  66. package/dist/testing.d.ts +44 -0
  67. package/dist/testing.js +115 -0
  68. package/dist/testing.js.map +1 -0
  69. package/dist/url.cjs +362 -0
  70. package/dist/url.cjs.map +1 -0
  71. package/dist/url.d.cts +153 -0
  72. package/dist/url.d.ts +153 -0
  73. package/dist/url.js +352 -0
  74. package/dist/url.js.map +1 -0
  75. package/dist/window-host.cjs +123 -0
  76. package/dist/window-host.cjs.map +1 -0
  77. package/dist/window-host.d.cts +72 -0
  78. package/dist/window-host.d.ts +72 -0
  79. package/dist/window-host.js +104 -0
  80. package/dist/window-host.js.map +1 -0
  81. package/dist/window.cjs +3978 -0
  82. package/dist/window.cjs.map +1 -0
  83. package/dist/window.d.cts +796 -0
  84. package/dist/window.d.ts +796 -0
  85. package/dist/window.js +4003 -0
  86. package/dist/window.js.map +1 -0
  87. package/package.json +237 -4
package/dist/index.cjs ADDED
@@ -0,0 +1,2383 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ DEFAULT_INSTANCE_ID: () => DEFAULT_INSTANCE_ID,
24
+ DEFAULT_PORTS: () => DEFAULT_PORTS,
25
+ LAZY_WINDOW_MOUNT_DEADLINE_MS: () => LAZY_WINDOW_MOUNT_DEADLINE_MS,
26
+ RENDER_WATCHDOG_SLOT: () => RENDER_WATCHDOG_SLOT,
27
+ TRAY_CHIP_H: () => TRAY_CHIP_H,
28
+ TRAY_CHIP_H_DESKTOP: () => TRAY_CHIP_H_DESKTOP,
29
+ TRAY_CHIP_H_MOBILE: () => TRAY_CHIP_H_MOBILE,
30
+ TRAY_CHIP_W: () => TRAY_CHIP_W,
31
+ TRAY_CHIP_W_DESKTOP: () => TRAY_CHIP_W_DESKTOP,
32
+ TRAY_CHIP_W_MOBILE_MAX: () => TRAY_CHIP_W_MOBILE_MAX,
33
+ TRAY_COMPACT_GAP: () => TRAY_COMPACT_GAP,
34
+ TRAY_COMPACT_H: () => TRAY_COMPACT_H,
35
+ TRAY_GAP_X: () => TRAY_GAP_X,
36
+ TRAY_GAP_Y: () => TRAY_GAP_Y,
37
+ TRAY_MARGIN_B: () => TRAY_MARGIN_B,
38
+ TRAY_MARGIN_L: () => TRAY_MARGIN_L,
39
+ TRAY_MARGIN_R: () => TRAY_MARGIN_R,
40
+ TRAY_MOBILE_BREAKPOINT: () => TRAY_MOBILE_BREAKPOINT,
41
+ WINDOW_SESSION_SCHEMA_VERSION: () => WINDOW_SESSION_SCHEMA_VERSION,
42
+ ackOverlayRender: () => ackOverlayRender,
43
+ ackOverlaySurfaceRender: () => ackOverlaySurfaceRender,
44
+ allOverlayMeta: () => allOverlayMeta,
45
+ anonymousIdentity: () => anonymousIdentity,
46
+ arrangeActiveWindows: () => arrangeActiveWindows,
47
+ buildDockWindowPayload: () => buildDockWindowPayload,
48
+ centerRectInViewport: () => centerRectInViewport,
49
+ clampAllWindowRects: () => clampAllWindowRects,
50
+ clampRectToCurrentViewport: () => clampRectToCurrentViewport,
51
+ clampRectToViewport: () => clampRectToViewport,
52
+ clampWindowRect: () => clampWindowRect,
53
+ clearOverlayRender: () => clearOverlayRender,
54
+ clearOverlayState: () => clearOverlayState,
55
+ clearOverlaySurfaceRender: () => clearOverlaySurfaceRender,
56
+ clearWindowPersistenceState: () => clearWindowPersistenceState,
57
+ closeAllInstancesOfOverlay: () => closeAllInstancesOfOverlay,
58
+ closeAllOverlays: () => closeAllOverlays,
59
+ closeLifecycleMiddleware: () => closeLifecycleMiddleware,
60
+ closeOverlay: () => closeOverlay,
61
+ closeOverlaysBoundToPage: () => closeOverlaysBoundToPage,
62
+ computeGlobalArrangement: () => computeGlobalArrangement,
63
+ computeTrayChipHeight: () => computeTrayChipHeight,
64
+ computeTrayChipWidth: () => computeTrayChipWidth,
65
+ confirmWindowRestored: () => confirmWindowRestored,
66
+ consoleErrorSink: () => consoleErrorSink,
67
+ consoleNotify: () => consoleNotify,
68
+ createOverlayRenderWatchdogMiddleware: () => createOverlayRenderWatchdogMiddleware,
69
+ createSummonMiddleware: () => createSummonMiddleware,
70
+ createSummonStore: () => createSummonStore,
71
+ debugOff: () => debugOff,
72
+ defaultWindowIdFor: () => defaultWindowIdFor,
73
+ diagnoseOverlayRender: () => diagnoseOverlayRender,
74
+ discardPendingWindowSession: () => discardPendingWindowSession,
75
+ dockWindow: () => dockWindow,
76
+ focusWindow: () => focusWindow,
77
+ getGrid12Rect: () => getGrid12Rect,
78
+ getGrid4Rect: () => getGrid4Rect,
79
+ getGrid6Rect: () => getGrid6Rect,
80
+ getGrid8Rect: () => getGrid8Rect,
81
+ getGrid9Rect: () => getGrid9Rect,
82
+ getOverlayMeta: () => getOverlayMeta,
83
+ getOverlayMetaByLinkKey: () => getOverlayMetaByLinkKey,
84
+ getStackedLeft2Rect: () => getStackedLeft2Rect,
85
+ getStackedLeft3Rect: () => getStackedLeft3Rect,
86
+ getStackedLeft4Rect: () => getStackedLeft4Rect,
87
+ getStackedLeft5Rect: () => getStackedLeft5Rect,
88
+ getStackedRight2Rect: () => getStackedRight2Rect,
89
+ getStackedRight3Rect: () => getStackedRight3Rect,
90
+ getStackedRight4Rect: () => getStackedRight4Rect,
91
+ getStackedRight5Rect: () => getStackedRight5Rect,
92
+ hydrateOverlayState: () => hydrateOverlayState,
93
+ hydrateWindowSessions: () => hydrateWindowSessions,
94
+ instanceKey: () => instanceKey,
95
+ isRegisteredOverlay: () => isRegisteredOverlay,
96
+ linksReducer: () => linksReducer,
97
+ listOpenInstanceKeys: () => listOpenInstanceKeys,
98
+ locationRouter: () => locationRouter,
99
+ markWindowClosing: () => markWindowClosing,
100
+ maximizeWindow: () => maximizeWindow,
101
+ minimizeAll: () => minimizeAll,
102
+ minimizeWindow: () => minimizeWindow,
103
+ moveTraySlot: () => moveTraySlot,
104
+ openOverlay: () => openOverlay,
105
+ overlayIdsBoundToPage: () => overlayIdsBoundToPage,
106
+ overlayStateReducer: () => overlayStateReducer,
107
+ overlaysRawActions: () => overlaysRawActions,
108
+ overlaysReducer: () => overlaysReducer,
109
+ parseInstanceKey: () => parseInstanceKey,
110
+ popOutWindow: () => popOutWindow,
111
+ pruneStaleInstances: () => pruneStaleInstances,
112
+ raiseTransientWindow: () => raiseTransientWindow,
113
+ recomputeTrayPositions: () => recomputeTrayPositions,
114
+ rectOnScreen: () => rectOnScreen,
115
+ rectsEqual: () => rectsEqual,
116
+ registerCloseFlusher: () => registerCloseFlusher,
117
+ registerOverlayMeta: () => registerOverlayMeta,
118
+ registerSyncEntry: () => registerSyncEntry,
119
+ registerWindow: () => registerWindow,
120
+ releaseOverlayInstance: () => releaseOverlayInstance,
121
+ releaseTransientWindow: () => releaseTransientWindow,
122
+ replaceOverlayState: () => replaceOverlayState,
123
+ resetOverlayRegistry: () => resetOverlayRegistry,
124
+ resetOverlayRenderWatchdog: () => resetOverlayRenderWatchdog,
125
+ resolvePorts: () => resolvePorts,
126
+ restoreAll: () => restoreAll,
127
+ restoreWindow: () => restoreWindow,
128
+ restoreWindowState: () => restoreWindowState,
129
+ revealWindow: () => revealWindow,
130
+ safeViewportDims: () => safeViewportDims,
131
+ selectActivePipWindowId: () => selectActivePipWindowId,
132
+ selectAllMinimized: () => selectAllMinimized,
133
+ selectAllWindows: () => selectAllWindows,
134
+ selectDockedWindows: () => selectDockedWindows,
135
+ selectIsOverlayOpen: () => selectIsOverlayOpen,
136
+ selectIsPoppedOut: () => selectIsPoppedOut,
137
+ selectIsUrlHydrated: () => selectIsUrlHydrated,
138
+ selectOpenInstances: () => selectOpenInstances,
139
+ selectOverlay: () => selectOverlay,
140
+ selectOverlayData: () => selectOverlayData,
141
+ selectOverlayStateFor: () => selectOverlayStateFor,
142
+ selectOverlayStateValue: () => selectOverlayStateValue,
143
+ selectPendingWindowRestores: () => selectPendingWindowRestores,
144
+ selectPopoutCandidateId: () => selectPopoutCandidateId,
145
+ selectPopoutMode: () => selectPopoutMode,
146
+ selectPrePopoutRect: () => selectPrePopoutRect,
147
+ selectTransientZIndex: () => selectTransientZIndex,
148
+ selectTraySlotWidth: () => selectTraySlotWidth,
149
+ selectTrayWindows: () => selectTrayWindows,
150
+ selectUrlSyncEntries: () => selectUrlSyncEntries,
151
+ selectWindow: () => selectWindow,
152
+ selectWindowPersistence: () => selectWindowPersistence,
153
+ selectWindowRect: () => selectWindowRect,
154
+ selectWindowState: () => selectWindowState,
155
+ selectWindowTitle: () => selectWindowTitle,
156
+ selectWindowZIndex: () => selectWindowZIndex,
157
+ selectWindowsHidden: () => selectWindowsHidden,
158
+ setHydrated: () => setHydrated,
159
+ setOverlayStateValue: () => setOverlayStateValue,
160
+ setPopoutCandidate: () => setPopoutCandidate,
161
+ subscribeToOverlayRegistry: () => subscribeToOverlayRegistry,
162
+ summonReducers: () => summonReducers,
163
+ toggleOverlay: () => toggleOverlay,
164
+ toggleWindowsHidden: () => toggleWindowsHidden,
165
+ trayChipsPerRow: () => trayChipsPerRow,
166
+ traySlotRect: () => traySlotRect,
167
+ unregisterSyncEntry: () => unregisterSyncEntry,
168
+ unregisterWindow: () => unregisterWindow,
169
+ updateWindowPersistence: () => updateWindowPersistence,
170
+ updateWindowRect: () => updateWindowRect,
171
+ updateWindowTitle: () => updateWindowTitle,
172
+ watchdogTracksOpen: () => watchdogTracksOpen,
173
+ windowManagerReducer: () => windowManagerReducer,
174
+ windowSessionKey: () => windowSessionKey
175
+ });
176
+ module.exports = __toCommonJS(src_exports);
177
+
178
+ // src/core/types.ts
179
+ var DEFAULT_INSTANCE_ID = "default";
180
+ function instanceKey(overlayId, instanceId = DEFAULT_INSTANCE_ID) {
181
+ return `${overlayId}:${instanceId}`;
182
+ }
183
+ function parseInstanceKey(key) {
184
+ const at = key.indexOf(":");
185
+ if (at <= 0 || at === key.length - 1) return null;
186
+ return { overlayId: key.slice(0, at), instanceId: key.slice(at + 1) };
187
+ }
188
+ function defaultWindowIdFor(meta) {
189
+ return meta.window?.windowId ?? meta.id;
190
+ }
191
+
192
+ // src/core/ports.ts
193
+ var noop = () => {
194
+ };
195
+ var anonymousIdentity = {
196
+ current: () => ({ key: "anonymous", ready: true }),
197
+ subscribe: () => noop
198
+ };
199
+ var locationRouter = {
200
+ pathname: () => typeof window === "undefined" ? "/" : window.location.pathname,
201
+ search: () => typeof window === "undefined" ? "" : window.location.search,
202
+ replaceSearch: (nextSearch) => {
203
+ if (typeof window === "undefined") return;
204
+ const query = nextSearch && !nextSearch.startsWith("?") ? `?${nextSearch}` : nextSearch;
205
+ const url = `${window.location.pathname}${query}${window.location.hash}`;
206
+ window.history.replaceState(window.history.state, "", url);
207
+ },
208
+ subscribe: (listener) => {
209
+ if (typeof window === "undefined") return noop;
210
+ window.addEventListener("popstate", listener);
211
+ return () => window.removeEventListener("popstate", listener);
212
+ }
213
+ };
214
+ var consoleNotify = {
215
+ info: (message) => console.info(`[summon] ${message}`),
216
+ warn: (message) => console.warn(`[summon] ${message}`),
217
+ error: (message) => console.error(`[summon] ${message}`)
218
+ };
219
+ var consoleErrorSink = {
220
+ capture: (error, context) => console.error("[summon]", error, context)
221
+ };
222
+ var debugOff = { enabled: () => false };
223
+ var DEFAULT_PORTS = {
224
+ identity: anonymousIdentity,
225
+ router: locationRouter,
226
+ notify: consoleNotify,
227
+ errors: consoleErrorSink,
228
+ debug: debugOff,
229
+ persistence: null
230
+ };
231
+ function resolvePorts(overrides) {
232
+ return { ...DEFAULT_PORTS, ...overrides };
233
+ }
234
+
235
+ // src/core/registry.ts
236
+ var import_global_slot = require("@ai-matrx/kit/global-slot");
237
+ var state = () => (0, import_global_slot.globalSlot)("ai-matrx.summon.registry", () => ({
238
+ byId: /* @__PURE__ */ new Map(),
239
+ byLinkKey: /* @__PURE__ */ new Map(),
240
+ listeners: /* @__PURE__ */ new Set()
241
+ }));
242
+ function registerOverlayMeta(definitions) {
243
+ const s = state();
244
+ for (const def of definitions) {
245
+ s.byId.set(def.id, def);
246
+ const keys = def.link ? [def.link.key, ...def.link.aliases ?? []] : [];
247
+ for (const key of keys) {
248
+ const holder = s.byLinkKey.get(key);
249
+ if (holder && holder.id !== def.id) {
250
+ throw new Error(`[summon] link key "${key}" is claimed by both "${holder.id}" and "${def.id}"`);
251
+ }
252
+ s.byLinkKey.set(key, def);
253
+ }
254
+ }
255
+ for (const listener of s.listeners) listener();
256
+ }
257
+ function getOverlayMeta(overlayId) {
258
+ return state().byId.get(overlayId);
259
+ }
260
+ function getOverlayMetaByLinkKey(linkKey) {
261
+ return state().byLinkKey.get(linkKey);
262
+ }
263
+ function allOverlayMeta() {
264
+ return [...state().byId.values()];
265
+ }
266
+ function isRegisteredOverlay(overlayId) {
267
+ return state().byId.has(overlayId);
268
+ }
269
+ function subscribeToOverlayRegistry(listener) {
270
+ const s = state();
271
+ s.listeners.add(listener);
272
+ return () => s.listeners.delete(listener);
273
+ }
274
+ function resetOverlayRegistry() {
275
+ const s = state();
276
+ s.byId.clear();
277
+ s.byLinkKey.clear();
278
+ }
279
+
280
+ // src/core/overlaysSlice.ts
281
+ var import_toolkit = require("@reduxjs/toolkit");
282
+ var import_global_slot2 = require("@ai-matrx/kit/global-slot");
283
+ var initialState = { overlays: {} };
284
+ function isPlainRecord(value) {
285
+ return typeof value === "object" && value !== null && !Array.isArray(value);
286
+ }
287
+ function withDefaults(data, defaults) {
288
+ if (!defaults) return data ?? null;
289
+ if (data === void 0 || data === null) return { ...defaults };
290
+ if (isPlainRecord(data)) return { ...defaults, ...data };
291
+ return data;
292
+ }
293
+ function closeInstance(state2, overlayId, instanceId) {
294
+ const bucket = state2.overlays[overlayId];
295
+ if (!bucket) return;
296
+ const instance = bucket[instanceId];
297
+ if (!instance) return;
298
+ if (instanceId !== DEFAULT_INSTANCE_ID) {
299
+ delete bucket[instanceId];
300
+ if (Object.keys(bucket).length === 0) delete state2.overlays[overlayId];
301
+ return;
302
+ }
303
+ instance.isOpen = false;
304
+ instance.data = null;
305
+ }
306
+ var overlaysSlice = (0, import_toolkit.createSlice)({
307
+ name: "overlays",
308
+ initialState,
309
+ reducers: {
310
+ openOverlay: (state2, action) => {
311
+ const { overlayId, instanceId = DEFAULT_INSTANCE_ID, data, defaults } = action.payload;
312
+ const bucket = state2.overlays[overlayId] ??= {};
313
+ bucket[instanceId] = { isOpen: true, data: withDefaults(data, defaults), lastUsedAt: Date.now() };
314
+ },
315
+ closeOverlay: (state2, action) => {
316
+ const { overlayId, instanceId = DEFAULT_INSTANCE_ID } = action.payload;
317
+ closeInstance(state2, overlayId, instanceId);
318
+ },
319
+ closeAllOverlays: (state2) => {
320
+ for (const [overlayId, bucket] of Object.entries(state2.overlays)) {
321
+ for (const [instanceId, instance] of Object.entries(bucket)) {
322
+ if (instanceId === DEFAULT_INSTANCE_ID) {
323
+ instance.isOpen = false;
324
+ instance.data = null;
325
+ } else {
326
+ delete bucket[instanceId];
327
+ }
328
+ }
329
+ if (Object.keys(bucket).length === 0) delete state2.overlays[overlayId];
330
+ }
331
+ },
332
+ toggleOverlay: (state2, action) => {
333
+ const { overlayId, instanceId = DEFAULT_INSTANCE_ID, data, defaults } = action.payload;
334
+ const bucket = state2.overlays[overlayId] ??= {};
335
+ const existing = bucket[instanceId];
336
+ if (!existing) {
337
+ bucket[instanceId] = { isOpen: true, data: withDefaults(data, defaults), lastUsedAt: Date.now() };
338
+ } else if (existing.isOpen) {
339
+ closeInstance(state2, overlayId, instanceId);
340
+ } else {
341
+ existing.isOpen = true;
342
+ existing.data = data === void 0 || data === null ? existing.data : withDefaults(data, defaults);
343
+ existing.lastUsedAt = Date.now();
344
+ }
345
+ },
346
+ /** Removes every instance of an overlay id (e.g. an editor that spawned many tabs). */
347
+ closeAllInstancesOfOverlay: (state2, action) => {
348
+ delete state2.overlays[action.payload.overlayId];
349
+ },
350
+ /**
351
+ * Route change: close every open instance of the listed overlays — each shows a piece of the
352
+ * page that opened it, so after navigating away it would float over a page it does not
353
+ * belong to. Every other overlay is a workbench tool and stays open.
354
+ */
355
+ closeOverlaysBoundToPage: (state2, action) => {
356
+ for (const overlayId of action.payload.overlayIds) {
357
+ const bucket = state2.overlays[overlayId];
358
+ if (!bucket) continue;
359
+ for (const [instanceId, instance] of Object.entries(bucket)) {
360
+ if (!instance.isOpen) continue;
361
+ if (instanceId === DEFAULT_INSTANCE_ID) {
362
+ instance.isOpen = false;
363
+ instance.data = null;
364
+ } else {
365
+ delete bucket[instanceId];
366
+ }
367
+ }
368
+ if (Object.keys(bucket).length === 0) delete state2.overlays[overlayId];
369
+ }
370
+ },
371
+ /**
372
+ * GC pass — removes closed instances last used more than `olderThanMs` ago. Singleton slots
373
+ * are kept regardless so selectors returning stable references don't thrash.
374
+ */
375
+ pruneStaleInstances: (state2, action) => {
376
+ const cutoff = Date.now() - action.payload.olderThanMs;
377
+ for (const [overlayId, bucket] of Object.entries(state2.overlays)) {
378
+ for (const [instanceId, instance] of Object.entries(bucket)) {
379
+ if (instanceId === DEFAULT_INSTANCE_ID) continue;
380
+ if (instance.isOpen) continue;
381
+ if (!instance.lastUsedAt || instance.lastUsedAt < cutoff) delete bucket[instanceId];
382
+ }
383
+ if (Object.keys(bucket).length === 0) delete state2.overlays[overlayId];
384
+ }
385
+ }
386
+ }
387
+ });
388
+ var overlaysReducer = overlaysSlice.reducer;
389
+ var overlaysRawActions = overlaysSlice.actions;
390
+ function stripFunctionsFromOverlayData(overlayId, data) {
391
+ if (!isPlainRecord(data)) return data;
392
+ const offenders = Object.keys(data).filter((key) => typeof data[key] === "function");
393
+ if (offenders.length === 0) return data;
394
+ console.error(
395
+ `[summon] BUG: function value(s) in openOverlay data for "${overlayId}" (keys: ${offenders.join(", ")}). Functions cannot travel through Redux \u2014 the handler would NEVER fire. Register a handle (@ai-matrx/summon/handles) and pass its id string instead. The offending key(s) were stripped from state.`
396
+ );
397
+ const cleaned = { ...data };
398
+ for (const key of offenders) delete cleaned[key];
399
+ return cleaned;
400
+ }
401
+ function rawOpenPayload(payload) {
402
+ const raw = { overlayId: payload.overlayId };
403
+ if (payload.instanceId !== void 0) raw.instanceId = payload.instanceId;
404
+ if (payload.data !== void 0) raw.data = stripFunctionsFromOverlayData(payload.overlayId, payload.data);
405
+ const defaults = getOverlayMeta(payload.overlayId)?.defaults;
406
+ if (isPlainRecord(defaults)) {
407
+ raw.defaults = stripFunctionsFromOverlayData(payload.overlayId, defaults);
408
+ }
409
+ return raw;
410
+ }
411
+ function withMatcher(create, raw) {
412
+ return Object.assign(create, { type: raw.type, match: raw.match });
413
+ }
414
+ var openOverlay = withMatcher(
415
+ (payload) => overlaysSlice.actions.openOverlay(rawOpenPayload(payload)),
416
+ overlaysSlice.actions.openOverlay
417
+ );
418
+ var closeOverlay = withMatcher(
419
+ (payload) => overlaysSlice.actions.closeOverlay(
420
+ payload.instanceId === void 0 ? { overlayId: payload.overlayId } : { overlayId: payload.overlayId, instanceId: payload.instanceId }
421
+ ),
422
+ overlaysSlice.actions.closeOverlay
423
+ );
424
+ var toggleOverlay = withMatcher(
425
+ (payload) => overlaysSlice.actions.toggleOverlay(rawOpenPayload(payload)),
426
+ overlaysSlice.actions.toggleOverlay
427
+ );
428
+ var closeAllInstancesOfOverlay = withMatcher(
429
+ (payload) => overlaysSlice.actions.closeAllInstancesOfOverlay({ overlayId: payload.overlayId }),
430
+ overlaysSlice.actions.closeAllInstancesOfOverlay
431
+ );
432
+ function overlayIdsBoundToPage() {
433
+ return allOverlayMeta().filter((meta) => meta.closesOnNavigation === true).map((meta) => meta.id);
434
+ }
435
+ var closeOverlaysBoundToPage = withMatcher(
436
+ (payload) => overlaysSlice.actions.closeOverlaysBoundToPage({
437
+ overlayIds: [...payload?.overlayIds ?? overlayIdsBoundToPage()]
438
+ }),
439
+ overlaysSlice.actions.closeOverlaysBoundToPage
440
+ );
441
+ var { closeAllOverlays, pruneStaleInstances } = overlaysSlice.actions;
442
+ var CLOSED_OVERLAY_INSTANCE = Object.freeze({ isOpen: false, data: null });
443
+ var EMPTY_INSTANCES = Object.freeze([]);
444
+ var selectOverlay = (state2, overlayId, instanceId = DEFAULT_INSTANCE_ID) => state2.overlays.overlays[overlayId]?.[instanceId] ?? CLOSED_OVERLAY_INSTANCE;
445
+ var selectIsOverlayOpen = (state2, overlayId, instanceId = DEFAULT_INSTANCE_ID) => selectOverlay(state2, overlayId, instanceId).isOpen;
446
+ var selectOverlayData = (state2, overlayId, instanceId = DEFAULT_INSTANCE_ID) => selectOverlay(state2, overlayId, instanceId).data;
447
+ var openInstancesCache = () => (0, import_global_slot2.globalSlot)("ai-matrx.summon.openInstancesSelectors", () => /* @__PURE__ */ new Map());
448
+ var selectOpenInstances = (state2, overlayId) => {
449
+ const cache = openInstancesCache();
450
+ let selector = cache.get(overlayId);
451
+ if (!selector) {
452
+ selector = (0, import_toolkit.createSelector)(
453
+ (s) => s.overlays.overlays[overlayId],
454
+ (instances) => {
455
+ if (!instances) return EMPTY_INSTANCES;
456
+ const result = [];
457
+ for (const [instanceId, inst] of Object.entries(instances)) {
458
+ if (inst.isOpen) result.push({ instanceId, data: inst.data });
459
+ }
460
+ return result.length === 0 ? EMPTY_INSTANCES : result;
461
+ }
462
+ );
463
+ cache.set(overlayId, selector);
464
+ }
465
+ return selector(state2);
466
+ };
467
+ function listOpenInstanceKeys(state2) {
468
+ const keys = [];
469
+ for (const [overlayId, bucket] of Object.entries(state2.overlays.overlays)) {
470
+ for (const [instanceId, inst] of Object.entries(bucket)) {
471
+ if (inst.isOpen) keys.push(`${overlayId}:${instanceId}`);
472
+ }
473
+ }
474
+ return keys;
475
+ }
476
+
477
+ // src/core/windowManagerSlice.ts
478
+ var import_toolkit2 = require("@reduxjs/toolkit");
479
+
480
+ // src/core/geometry/windowArrangements.ts
481
+ function getGrid4Rect(index, vw, vh) {
482
+ const w = Math.round(vw / 2);
483
+ const h = Math.round(vh / 2);
484
+ return {
485
+ x: index % 2 * w,
486
+ y: Math.floor(index / 2) * h,
487
+ width: w,
488
+ height: h
489
+ };
490
+ }
491
+ function getGrid6Rect(index, vw, vh) {
492
+ const w = Math.round(vw / 3);
493
+ const h = Math.round(vh / 2);
494
+ return {
495
+ x: index % 3 * w,
496
+ y: Math.floor(index / 3) * h,
497
+ width: w,
498
+ height: h
499
+ };
500
+ }
501
+ function getGrid8Rect(index, vw, vh) {
502
+ const w = Math.round(vw / 4);
503
+ const h = Math.round(vh / 2);
504
+ return {
505
+ x: index % 4 * w,
506
+ y: Math.floor(index % 8 / 4) * h,
507
+ width: w,
508
+ height: h
509
+ };
510
+ }
511
+ function getGrid9Rect(index, vw, vh) {
512
+ const w = Math.round(vw / 3);
513
+ const h = Math.round(vh / 3);
514
+ return {
515
+ x: index % 3 * w,
516
+ y: Math.floor(index % 9 / 3) * h,
517
+ width: w,
518
+ height: h
519
+ };
520
+ }
521
+ function getGrid12Rect(index, vw, vh) {
522
+ const w = Math.round(vw / 4);
523
+ const h = Math.round(vh / 3);
524
+ return {
525
+ x: index % 4 * w,
526
+ y: Math.floor(index % 12 / 4) * h,
527
+ width: w,
528
+ height: h
529
+ };
530
+ }
531
+ function getStackedRight2Rect(index, vw, vh) {
532
+ const w = Math.round(vw / 2);
533
+ const h = Math.round(vh / 2);
534
+ return {
535
+ x: w,
536
+ y: index % 2 * h,
537
+ width: w,
538
+ height: h
539
+ };
540
+ }
541
+ function getStackedRight3Rect(index, vw, vh) {
542
+ const w = Math.round(vw / 3);
543
+ const h = Math.round(vh / 3);
544
+ return {
545
+ x: vw - w,
546
+ y: index % 3 * h,
547
+ width: w,
548
+ height: h
549
+ };
550
+ }
551
+ function getStackedRight4Rect(index, vw, vh) {
552
+ const w = Math.round(vw / 4);
553
+ const h = Math.round(vh / 4);
554
+ return {
555
+ x: vw - w,
556
+ y: index % 4 * h,
557
+ width: w,
558
+ height: h
559
+ };
560
+ }
561
+ function getStackedRight5Rect(index, vw, vh) {
562
+ const w = Math.round(vw / 5);
563
+ const h = Math.round(vh / 5);
564
+ return {
565
+ x: vw - w,
566
+ y: index % 5 * h,
567
+ width: w,
568
+ height: h
569
+ };
570
+ }
571
+ function getStackedLeft2Rect(index, vw, vh) {
572
+ const w = Math.round(vw / 2);
573
+ const h = Math.round(vh / 2);
574
+ return {
575
+ x: 0,
576
+ y: index % 2 * h,
577
+ width: w,
578
+ height: h
579
+ };
580
+ }
581
+ function getStackedLeft3Rect(index, vw, vh) {
582
+ const w = Math.round(vw / 3);
583
+ const h = Math.round(vh / 3);
584
+ return {
585
+ x: 0,
586
+ y: index % 3 * h,
587
+ width: w,
588
+ height: h
589
+ };
590
+ }
591
+ function getStackedLeft4Rect(index, vw, vh) {
592
+ const w = Math.round(vw / 4);
593
+ const h = Math.round(vh / 4);
594
+ return {
595
+ x: 0,
596
+ y: index % 4 * h,
597
+ width: w,
598
+ height: h
599
+ };
600
+ }
601
+ function getStackedLeft5Rect(index, vw, vh) {
602
+ const w = Math.round(vw / 5);
603
+ const h = Math.round(vh / 5);
604
+ return {
605
+ x: 0,
606
+ y: index % 5 * h,
607
+ width: w,
608
+ height: h
609
+ };
610
+ }
611
+ function computeGlobalArrangement(layout, windowIds, vw, vh, dirX = "rtl", dirY = "ttb", primary = "vertical") {
612
+ const specs = {
613
+ grid4: { cols: 2, rows: 2, fn: getGrid4Rect },
614
+ grid6: { cols: 3, rows: 2, fn: getGrid6Rect },
615
+ grid8: { cols: 4, rows: 2, fn: getGrid8Rect },
616
+ grid9: { cols: 3, rows: 3, fn: getGrid9Rect },
617
+ grid12: { cols: 4, rows: 3, fn: getGrid12Rect },
618
+ stackRight2: { cols: 1, rows: 2, fn: getStackedRight2Rect },
619
+ stackRight3: { cols: 1, rows: 3, fn: getStackedRight3Rect },
620
+ stackRight4: { cols: 1, rows: 4, fn: getStackedRight4Rect },
621
+ stackRight5: { cols: 1, rows: 5, fn: getStackedRight5Rect },
622
+ stackLeft2: { cols: 1, rows: 2, fn: getStackedLeft2Rect },
623
+ stackLeft3: { cols: 1, rows: 3, fn: getStackedLeft3Rect },
624
+ stackLeft4: { cols: 1, rows: 4, fn: getStackedLeft4Rect },
625
+ stackLeft5: { cols: 1, rows: 5, fn: getStackedLeft5Rect }
626
+ };
627
+ const spec = specs[layout];
628
+ const slotCount = spec.cols * spec.rows;
629
+ const placed = windowIds.slice(0, slotCount);
630
+ return placed.map((id, i) => {
631
+ let col;
632
+ let row;
633
+ if (primary === "horizontal") {
634
+ col = i % spec.cols;
635
+ row = Math.floor(i / spec.cols);
636
+ } else {
637
+ row = i % spec.rows;
638
+ col = Math.floor(i / spec.rows);
639
+ }
640
+ if (dirX === "rtl") {
641
+ col = spec.cols - 1 - col;
642
+ }
643
+ if (dirY === "btt") {
644
+ row = spec.rows - 1 - row;
645
+ }
646
+ const targetIndex = row * spec.cols + col;
647
+ return {
648
+ id,
649
+ rect: spec.fn(targetIndex, vw, vh)
650
+ };
651
+ });
652
+ }
653
+
654
+ // src/core/geometry/rectClamp.ts
655
+ var VIEWPORT_PADDING = 8;
656
+ var FALLBACK_W = 480;
657
+ var FALLBACK_H = 360;
658
+ var CLAMP_WARNED = /* @__PURE__ */ Symbol.for("ai-matrx.summon.clampDegenerateViewportWarned");
659
+ var FALLBACK_VIEWPORT_W = 1280;
660
+ var FALLBACK_VIEWPORT_H = 800;
661
+ var DEGENERATE_WARNED = /* @__PURE__ */ Symbol.for(
662
+ "ai-matrx.summon.degenerateViewportWarned"
663
+ );
664
+ function screamOnceAboutDegenerateMeasurement(rawW, rawH) {
665
+ const host = globalThis;
666
+ if (host[DEGENERATE_WARNED]) return;
667
+ host[DEGENERATE_WARNED] = true;
668
+ console.warn(
669
+ "[window-panels] degenerate viewport measurement",
670
+ { innerWidth: rawW, innerHeight: rawH },
671
+ "\u2014 using fallback dimensions so window geometry cannot collapse to zero. Logged once per page; state writers bail out instead of persisting invented dims."
672
+ );
673
+ }
674
+ function safeViewportDims() {
675
+ if (typeof window === "undefined") {
676
+ return {
677
+ vw: FALLBACK_VIEWPORT_W,
678
+ vh: FALLBACK_VIEWPORT_H,
679
+ degenerate: true
680
+ };
681
+ }
682
+ const rawW = window.innerWidth;
683
+ const rawH = window.innerHeight;
684
+ const degenerate = !Number.isFinite(rawW) || !Number.isFinite(rawH) || rawW <= 0 || rawH <= 0;
685
+ if (degenerate) screamOnceAboutDegenerateMeasurement(rawW, rawH);
686
+ return {
687
+ vw: Number.isFinite(rawW) && rawW > 0 ? rawW : FALLBACK_VIEWPORT_W,
688
+ vh: Number.isFinite(rawH) && rawH > 0 ? rawH : FALLBACK_VIEWPORT_H,
689
+ degenerate
690
+ };
691
+ }
692
+ function clampRectToViewport(rect, viewport) {
693
+ if (!Number.isFinite(viewport.width) || !Number.isFinite(viewport.height) || viewport.width <= 0 || viewport.height <= 0) {
694
+ const host = globalThis;
695
+ if (!host[CLAMP_WARNED]) {
696
+ host[CLAMP_WARNED] = true;
697
+ console.warn(
698
+ "[window-panels] clampRectToViewport received a degenerate viewport",
699
+ viewport,
700
+ "\u2014 skipping clamp so real window geometry is not destroyed."
701
+ );
702
+ }
703
+ return {
704
+ x: Number.isFinite(rect.x) ? rect.x : 0,
705
+ y: Number.isFinite(rect.y) ? rect.y : 0,
706
+ width: Number.isFinite(rect.width) && rect.width > 0 ? rect.width : FALLBACK_W,
707
+ height: Number.isFinite(rect.height) && rect.height > 0 ? rect.height : FALLBACK_H
708
+ };
709
+ }
710
+ const maxW = Math.max(120, viewport.width - VIEWPORT_PADDING * 2);
711
+ const maxH = Math.max(80, viewport.height - VIEWPORT_PADDING * 2);
712
+ let width = rect.width;
713
+ if (!Number.isFinite(width) || width <= 0 || width > viewport.width * 4) {
714
+ width = FALLBACK_W;
715
+ }
716
+ width = Math.min(width, maxW);
717
+ let height = rect.height;
718
+ if (!Number.isFinite(height) || height <= 0 || height > viewport.height * 4) {
719
+ height = FALLBACK_H;
720
+ }
721
+ height = Math.min(height, maxH);
722
+ let x = Number.isFinite(rect.x) ? rect.x : 0;
723
+ let y = Number.isFinite(rect.y) ? rect.y : 0;
724
+ const maxX = Math.max(0, viewport.width - width - VIEWPORT_PADDING);
725
+ const maxY = Math.max(0, viewport.height - height - VIEWPORT_PADDING);
726
+ x = Math.max(0, Math.min(maxX, x));
727
+ y = Math.max(0, Math.min(maxY, y));
728
+ return { x, y, width, height };
729
+ }
730
+ function centerRectInViewport(rect, viewport) {
731
+ const sized = clampRectToViewport({ ...rect, x: 0, y: 0 }, viewport);
732
+ const x = Math.max(0, Math.round((viewport.width - sized.width) / 2));
733
+ const y = Math.max(0, Math.round((viewport.height - sized.height) / 2));
734
+ return { x, y, width: sized.width, height: sized.height };
735
+ }
736
+ function clampRectToCurrentViewport(rect) {
737
+ if (typeof window === "undefined") return rect;
738
+ const { vw, vh } = safeViewportDims();
739
+ return clampRectToViewport(rect, { width: vw, height: vh });
740
+ }
741
+ function rectsEqual(a, b) {
742
+ return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
743
+ }
744
+
745
+ // src/core/geometry/tray.ts
746
+ var TRAY_CHIP_W_DESKTOP = 240;
747
+ var TRAY_CHIP_H_DESKTOP = 160;
748
+ var TRAY_GAP_X = 8;
749
+ var TRAY_GAP_Y = 8;
750
+ var TRAY_MARGIN_R = 20;
751
+ var TRAY_MARGIN_B = 20;
752
+ var TRAY_MARGIN_L = 8;
753
+ var TRAY_COMPACT_H = 32;
754
+ var TRAY_COMPACT_GAP = 4;
755
+ var TRAY_CHIP_W_MOBILE_MAX = 180;
756
+ var TRAY_CHIP_H_MOBILE = 72;
757
+ var TRAY_MOBILE_BREAKPOINT = 768;
758
+ function computeTrayChipWidth(viewportWidth) {
759
+ if (viewportWidth < TRAY_MOBILE_BREAKPOINT) {
760
+ return Math.min(
761
+ TRAY_CHIP_W_MOBILE_MAX,
762
+ Math.max(120, Math.floor(viewportWidth / 2) - 24)
763
+ );
764
+ }
765
+ return TRAY_CHIP_W_DESKTOP;
766
+ }
767
+ function computeTrayChipHeight(viewportWidth) {
768
+ return viewportWidth < TRAY_MOBILE_BREAKPOINT ? TRAY_CHIP_H_MOBILE : TRAY_CHIP_H_DESKTOP;
769
+ }
770
+ function trayChipsPerRow(viewportWidth) {
771
+ const chipWidth = computeTrayChipWidth(viewportWidth);
772
+ const usable = viewportWidth - TRAY_MARGIN_R - TRAY_MARGIN_L;
773
+ return Math.max(
774
+ 1,
775
+ Math.floor((usable + TRAY_GAP_X) / (chipWidth + TRAY_GAP_X))
776
+ );
777
+ }
778
+ function traySlotRect(slot, viewportWidth, viewportHeight, totalSlots = slot + 1) {
779
+ const chipWidth = computeTrayChipWidth(viewportWidth);
780
+ const chipHeight = computeTrayChipHeight(viewportWidth);
781
+ const perRow = trayChipsPerRow(viewportWidth);
782
+ const safeSlot = Math.max(0, Math.floor(slot));
783
+ const usableHeight = Math.max(1, viewportHeight - TRAY_MARGIN_B - 8);
784
+ const fullRows = Math.max(
785
+ 1,
786
+ Math.floor((usableHeight + TRAY_GAP_Y) / (chipHeight + TRAY_GAP_Y))
787
+ );
788
+ const fullCardCapacity = perRow * fullRows;
789
+ if (totalSlots > fullCardCapacity) {
790
+ const compactRows = Math.max(
791
+ 1,
792
+ Math.floor(
793
+ (viewportHeight - 16 + TRAY_COMPACT_GAP) / (TRAY_COMPACT_H + TRAY_COMPACT_GAP)
794
+ )
795
+ );
796
+ const compactColumns = Math.max(1, Math.ceil(totalSlots / compactRows));
797
+ const availableWidth = Math.max(
798
+ compactColumns,
799
+ viewportWidth - 16 - TRAY_COMPACT_GAP * (compactColumns - 1)
800
+ );
801
+ const compactWidth = Math.min(
802
+ TRAY_CHIP_W_DESKTOP,
803
+ Math.max(1, Math.floor(availableWidth / compactColumns))
804
+ );
805
+ const compactCol = safeSlot % compactColumns;
806
+ const compactRow = Math.floor(safeSlot / compactColumns);
807
+ return {
808
+ x: viewportWidth - 8 - compactWidth - compactCol * (compactWidth + TRAY_COMPACT_GAP),
809
+ y: viewportHeight - 8 - TRAY_COMPACT_H - compactRow * (TRAY_COMPACT_H + TRAY_COMPACT_GAP),
810
+ width: compactWidth,
811
+ height: TRAY_COMPACT_H
812
+ };
813
+ }
814
+ const col = safeSlot % perRow;
815
+ const row = Math.floor(safeSlot / perRow);
816
+ return {
817
+ x: viewportWidth - TRAY_MARGIN_R - chipWidth - col * (chipWidth + TRAY_GAP_X),
818
+ y: viewportHeight - TRAY_MARGIN_B - chipHeight - row * (chipHeight + TRAY_GAP_Y),
819
+ width: chipWidth,
820
+ height: chipHeight
821
+ };
822
+ }
823
+
824
+ // src/core/windowManagerSlice.ts
825
+ var WINDOW_SESSION_SCHEMA_VERSION = 1;
826
+ function windowSessionKey(overlayId, instanceId) {
827
+ return `${overlayId}:${instanceId}`;
828
+ }
829
+ var TRAY_CHIP_W = TRAY_CHIP_W_DESKTOP;
830
+ var TRAY_CHIP_H = TRAY_CHIP_H_DESKTOP;
831
+ var BASE_Z = 1e3;
832
+ var TRAY_SLOT_WIDTH = TRAY_CHIP_W;
833
+ var initialState2 = {
834
+ windows: {},
835
+ pendingRestores: {},
836
+ nextZIndex: BASE_Z,
837
+ trayCount: 0,
838
+ trayViewport: null,
839
+ windowsHidden: false,
840
+ activePipWindowId: null,
841
+ popoutCandidateId: null,
842
+ transientZ: {}
843
+ };
844
+ function recomputeAllTrayRects(state2) {
845
+ const viewport = state2.trayViewport;
846
+ if (!viewport) return;
847
+ Object.values(state2.windows).forEach((entry) => {
848
+ if (entry.traySlot === null) return;
849
+ entry.windowed = traySlotRect(
850
+ entry.traySlot,
851
+ viewport.width,
852
+ viewport.height,
853
+ state2.trayCount
854
+ );
855
+ });
856
+ Object.values(state2.pendingRestores).forEach((session) => {
857
+ if (session.traySlot === null) return;
858
+ session.renderRect = traySlotRect(
859
+ session.traySlot,
860
+ viewport.width,
861
+ viewport.height,
862
+ state2.trayCount
863
+ );
864
+ });
865
+ }
866
+ function hasMatchingLiveTrayReservation(state2, overlayId, instanceId, traySlot) {
867
+ if (traySlot === null) return false;
868
+ return Object.values(state2.windows).some(
869
+ (entry) => entry.persistence?.overlayId === overlayId && entry.persistence.instanceId === instanceId && entry.traySlot === traySlot
870
+ );
871
+ }
872
+ function compactReservedTraySlot(state2, freedSlot, freedRect) {
873
+ const rectBySlot = /* @__PURE__ */ new Map();
874
+ rectBySlot.set(freedSlot, freedRect);
875
+ Object.values(state2.windows).forEach((entry) => {
876
+ if (entry.traySlot !== null) {
877
+ rectBySlot.set(entry.traySlot, { ...entry.windowed });
878
+ }
879
+ });
880
+ Object.values(state2.pendingRestores).forEach((session) => {
881
+ if (session.traySlot !== null) {
882
+ rectBySlot.set(session.traySlot, { ...session.renderRect });
883
+ }
884
+ });
885
+ state2.trayCount = Math.max(0, state2.trayCount - 1);
886
+ if (state2.trayCount === 0) state2.trayViewport = null;
887
+ Object.values(state2.windows).forEach((entry) => {
888
+ if (entry.traySlot === null || entry.traySlot <= freedSlot) return;
889
+ const nextSlot = entry.traySlot - 1;
890
+ entry.traySlot = nextSlot;
891
+ const nextRect = rectBySlot.get(nextSlot);
892
+ if (nextRect) entry.windowed = nextRect;
893
+ });
894
+ Object.values(state2.pendingRestores).forEach((session) => {
895
+ if (session.traySlot === null || session.traySlot <= freedSlot) return;
896
+ const nextSlot = session.traySlot - 1;
897
+ session.traySlot = nextSlot;
898
+ const nextRect = rectBySlot.get(nextSlot);
899
+ if (nextRect) session.renderRect = nextRect;
900
+ });
901
+ recomputeAllTrayRects(state2);
902
+ }
903
+ function releaseTraySlot(state2, win) {
904
+ const freedSlot = win.traySlot;
905
+ if (freedSlot === null) return;
906
+ const freedRect = { ...win.windowed };
907
+ win.traySlot = null;
908
+ compactReservedTraySlot(state2, freedSlot, freedRect);
909
+ }
910
+ var windowManagerSlice = (0, import_toolkit2.createSlice)({
911
+ name: "windowManager",
912
+ initialState: initialState2,
913
+ reducers: {
914
+ /**
915
+ * Put a transient (lite) window on top of the shared stack, or raise one
916
+ * that is already there. Same counter as `focusWindow`, so heavy and lite
917
+ * windows interleave correctly.
918
+ */
919
+ raiseTransientWindow(state2, action) {
920
+ state2.transientZ[action.payload] = state2.nextZIndex++;
921
+ },
922
+ /** Drop a transient window's z registration when it unmounts. */
923
+ releaseTransientWindow(state2, action) {
924
+ delete state2.transientZ[action.payload];
925
+ },
926
+ /** Register a new window. Idempotent — ignored if id already exists. */
927
+ registerWindow(state2, action) {
928
+ const { id, title, initial, initialState: initialState5, persistence, viewport } = action.payload;
929
+ if (state2.windows[id]) return;
930
+ const safeInitial = viewport ? clampRectToViewport(initial, viewport) : initial;
931
+ const pending = persistence ? state2.pendingRestores[windowSessionKey(persistence.overlayId, persistence.instanceId)] : void 0;
932
+ const pendingRenderRect = pending ? pending.state === "minimized" && pending.traySlot !== null && viewport ? traySlotRect(
933
+ pending.traySlot,
934
+ viewport.width,
935
+ viewport.height,
936
+ state2.trayCount
937
+ ) : viewport ? clampRectToViewport(pending.windowedRect, viewport) : pending.renderRect : void 0;
938
+ const startsMinimized = !pending && initialState5 === "minimized" && viewport !== void 0;
939
+ const initialTraySlot = startsMinimized ? state2.trayCount : null;
940
+ state2.windows[id] = {
941
+ id,
942
+ title: title ?? pending?.title ?? id,
943
+ state: pending?.state ?? (startsMinimized ? "minimized" : "windowed"),
944
+ windowed: pendingRenderRect ?? safeInitial,
945
+ preMinimizedRect: pending?.state === "minimized" ? pending.windowedRect : startsMinimized ? safeInitial : null,
946
+ zIndex: pending?.zIndex ?? state2.nextZIndex++,
947
+ traySlot: pending?.state === "minimized" ? pending.traySlot : initialTraySlot,
948
+ popoutMode: null,
949
+ prePopoutRect: null,
950
+ ...persistence ? {
951
+ persistence: {
952
+ ...persistence,
953
+ data: { ...pending?.data ?? {}, ...persistence.data },
954
+ sidebarOpen: pending?.sidebarOpen ?? persistence.sidebarOpen,
955
+ sidebarSize: pending?.sidebarSize ?? persistence.sidebarSize,
956
+ closing: false
957
+ }
958
+ } : {}
959
+ };
960
+ if (pending?.state === "minimized" && pending.traySlot !== null) {
961
+ state2.trayCount = Math.max(state2.trayCount, pending.traySlot + 1);
962
+ } else if (startsMinimized && viewport) {
963
+ state2.trayCount += 1;
964
+ state2.trayViewport = viewport;
965
+ recomputeAllTrayRects(state2);
966
+ }
967
+ if (pending && pending.zIndex >= state2.nextZIndex) {
968
+ state2.nextZIndex = pending.zIndex + 1;
969
+ }
970
+ if (state2.windowsHidden) state2.windowsHidden = false;
971
+ },
972
+ /** Stage validated local sessions without creating phantom live windows. */
973
+ hydrateWindowSessions(state2, action) {
974
+ const liveEntries = Object.values(state2.windows).sort(
975
+ (a, b) => a.zIndex - b.zIndex || a.id.localeCompare(b.id)
976
+ );
977
+ const accepted = [];
978
+ action.payload.sessions.forEach((session) => {
979
+ const alreadyLive = Object.values(state2.windows).some(
980
+ (entry) => entry.persistence?.overlayId === session.overlayId && entry.persistence.instanceId === session.instanceId && !entry.persistence.closing
981
+ );
982
+ if (alreadyLive) return;
983
+ accepted.push(session);
984
+ });
985
+ const trayBase = state2.trayCount;
986
+ const acceptedTraySlots = new Map(
987
+ accepted.filter(
988
+ (session) => session.state === "minimized" && session.traySlot !== null
989
+ ).sort(
990
+ (a, b) => (a.traySlot ?? Number.MAX_SAFE_INTEGER) - (b.traySlot ?? Number.MAX_SAFE_INTEGER) || a.sessionKey.localeCompare(b.sessionKey)
991
+ ).map(
992
+ (session, index) => [session.sessionKey, trayBase + index]
993
+ )
994
+ );
995
+ accepted.sort(
996
+ (a, b) => a.zIndex - b.zIndex || a.sessionKey.localeCompare(b.sessionKey)
997
+ ).forEach((session, index) => {
998
+ const key = windowSessionKey(session.overlayId, session.instanceId);
999
+ const traySlot = session.state === "minimized" ? acceptedTraySlots.get(session.sessionKey) ?? null : null;
1000
+ state2.pendingRestores[key] = {
1001
+ ...session,
1002
+ sessionKey: key,
1003
+ traySlot,
1004
+ zIndex: BASE_Z + index,
1005
+ renderRect: traySlot !== null ? traySlotRect(
1006
+ traySlot,
1007
+ action.payload.viewportWidth,
1008
+ action.payload.viewportHeight
1009
+ ) : clampRectToViewport(session.windowedRect, {
1010
+ width: action.payload.viewportWidth,
1011
+ height: action.payload.viewportHeight
1012
+ })
1013
+ };
1014
+ });
1015
+ state2.trayCount = trayBase + acceptedTraySlots.size;
1016
+ state2.trayViewport = {
1017
+ width: action.payload.viewportWidth,
1018
+ height: action.payload.viewportHeight
1019
+ };
1020
+ recomputeAllTrayRects(state2);
1021
+ liveEntries.forEach((entry, index) => {
1022
+ entry.zIndex = BASE_Z + accepted.length + index;
1023
+ });
1024
+ state2.nextZIndex = BASE_Z + accepted.length + liveEntries.length;
1025
+ },
1026
+ /** StrictMode-safe: the first mount cleanup may run before the real mount settles. */
1027
+ confirmWindowRestored(state2, action) {
1028
+ delete state2.pendingRestores[action.payload];
1029
+ },
1030
+ discardPendingWindowSession(state2, action) {
1031
+ const pending = state2.pendingRestores[action.payload];
1032
+ if (pending?.traySlot !== null && pending?.traySlot !== void 0 && !hasMatchingLiveTrayReservation(
1033
+ state2,
1034
+ pending.overlayId,
1035
+ pending.instanceId,
1036
+ pending.traySlot
1037
+ )) {
1038
+ compactReservedTraySlot(state2, pending.traySlot, pending.renderRect);
1039
+ }
1040
+ delete state2.pendingRestores[action.payload];
1041
+ },
1042
+ updateWindowPersistence(state2, action) {
1043
+ const entry = state2.windows[action.payload.id];
1044
+ if (!entry?.persistence || entry.persistence.closing) return;
1045
+ if (action.payload.data !== void 0 && JSON.stringify(entry.persistence.data) !== JSON.stringify(action.payload.data)) {
1046
+ entry.persistence.data = action.payload.data;
1047
+ }
1048
+ if (action.payload.sidebarOpen !== void 0 && entry.persistence.sidebarOpen !== action.payload.sidebarOpen) {
1049
+ entry.persistence.sidebarOpen = action.payload.sidebarOpen;
1050
+ }
1051
+ if (action.payload.sidebarSize !== void 0 && entry.persistence.sidebarSize !== action.payload.sidebarSize) {
1052
+ entry.persistence.sidebarSize = action.payload.sidebarSize;
1053
+ }
1054
+ },
1055
+ /** Synchronous tombstone: close can never be undone by an unmount snapshot. */
1056
+ markWindowClosing(state2, action) {
1057
+ const key = windowSessionKey(
1058
+ action.payload.overlayId,
1059
+ action.payload.instanceId
1060
+ );
1061
+ const pending = state2.pendingRestores[key];
1062
+ const hasMatchingLiveReservation = pending ? hasMatchingLiveTrayReservation(
1063
+ state2,
1064
+ action.payload.overlayId,
1065
+ action.payload.instanceId,
1066
+ pending.traySlot
1067
+ ) : false;
1068
+ if (!hasMatchingLiveReservation && pending?.traySlot !== null && pending?.traySlot !== void 0) {
1069
+ compactReservedTraySlot(state2, pending.traySlot, pending.renderRect);
1070
+ }
1071
+ delete state2.pendingRestores[key];
1072
+ Object.values(state2.windows).forEach((entry) => {
1073
+ if (entry.persistence?.overlayId === action.payload.overlayId && entry.persistence.instanceId === action.payload.instanceId) {
1074
+ entry.persistence.closing = true;
1075
+ }
1076
+ });
1077
+ },
1078
+ clearWindowPersistenceState(state2) {
1079
+ Object.values(state2.pendingRestores).filter(
1080
+ (session) => session.traySlot !== null && session.traySlot !== void 0 && !hasMatchingLiveTrayReservation(
1081
+ state2,
1082
+ session.overlayId,
1083
+ session.instanceId,
1084
+ session.traySlot
1085
+ )
1086
+ ).sort((a, b) => (b.traySlot ?? -1) - (a.traySlot ?? -1)).forEach((session) => {
1087
+ const traySlot = session.traySlot;
1088
+ if (traySlot === null) return;
1089
+ compactReservedTraySlot(state2, traySlot, session.renderRect);
1090
+ });
1091
+ state2.pendingRestores = {};
1092
+ Object.values(state2.windows).forEach((entry) => {
1093
+ if (entry.persistence) entry.persistence.closing = true;
1094
+ });
1095
+ },
1096
+ /** Update just the title (e.g. when prop changes). */
1097
+ updateWindowTitle(state2, action) {
1098
+ const win = state2.windows[action.payload.id];
1099
+ if (win) win.title = action.payload.title;
1100
+ },
1101
+ /** Remove a window from tracking entirely. */
1102
+ unregisterWindow(state2, action) {
1103
+ const win = state2.windows[action.payload];
1104
+ if (!win) return;
1105
+ const pending = win.persistence ? state2.pendingRestores[windowSessionKey(
1106
+ win.persistence.overlayId,
1107
+ win.persistence.instanceId
1108
+ )] : void 0;
1109
+ const pendingOwnsLiveReservation = Boolean(
1110
+ win.persistence && pending && hasMatchingLiveTrayReservation(
1111
+ state2,
1112
+ win.persistence.overlayId,
1113
+ win.persistence.instanceId,
1114
+ pending.traySlot
1115
+ )
1116
+ );
1117
+ if (!pendingOwnsLiveReservation) releaseTraySlot(state2, win);
1118
+ if (state2.activePipWindowId === action.payload) {
1119
+ state2.activePipWindowId = null;
1120
+ }
1121
+ if (state2.popoutCandidateId === action.payload) {
1122
+ state2.popoutCandidateId = null;
1123
+ }
1124
+ delete state2.windows[action.payload];
1125
+ if (Object.keys(state2.windows).length === 0) {
1126
+ state2.windowsHidden = false;
1127
+ }
1128
+ },
1129
+ /** Bring a window to the top of the z stack. */
1130
+ focusWindow(state2, action) {
1131
+ const win = state2.windows[action.payload];
1132
+ if (!win) return;
1133
+ win.zIndex = state2.nextZIndex++;
1134
+ },
1135
+ /** Switch to windowed (restore) state. */
1136
+ restoreWindow(state2, action) {
1137
+ const win = state2.windows[action.payload];
1138
+ if (!win) return;
1139
+ releaseTraySlot(state2, win);
1140
+ if (win.preMinimizedRect) {
1141
+ win.windowed = win.preMinimizedRect;
1142
+ win.preMinimizedRect = null;
1143
+ }
1144
+ win.state = "windowed";
1145
+ win.zIndex = state2.nextZIndex++;
1146
+ },
1147
+ /**
1148
+ * Bring an already-registered window unconditionally into view — the
1149
+ * single "show me this panel" primitive used by the open path.
1150
+ *
1151
+ * Re-triggering a menu/opener for a window that is already open must never
1152
+ * be a silent no-op. Before this existed, an open dispatch only updated
1153
+ * overlay-slice data; if the window was minimized, dragged off-screen, or
1154
+ * suppressed by `windowsHidden`, the user saw nothing. `revealWindow`:
1155
+ * - restores a minimized window to its pre-minimized rect,
1156
+ * - clamps the rect back into the current viewport (off-screen rescue),
1157
+ * - raises it to the top of the z-stack,
1158
+ * - clears the global hide-all flag.
1159
+ *
1160
+ * No-op when the window isn't registered yet (first open — `registerWindow`
1161
+ * handles that). Popped-out windows keep their OS-managed frame; we only
1162
+ * clear the global hide so the rest of the desktop is visible.
1163
+ *
1164
+ * Caller supplies viewport dimensions (reducers can't read `window`).
1165
+ */
1166
+ revealWindow(state2, action) {
1167
+ const { id, viewportWidth, viewportHeight } = action.payload;
1168
+ const win = state2.windows[id];
1169
+ if (!win) return;
1170
+ if (state2.windowsHidden) state2.windowsHidden = false;
1171
+ if (win.popoutMode !== null) return;
1172
+ releaseTraySlot(state2, win);
1173
+ if (win.state === "minimized") {
1174
+ if (win.preMinimizedRect) {
1175
+ win.windowed = win.preMinimizedRect;
1176
+ win.preMinimizedRect = null;
1177
+ }
1178
+ win.state = "windowed";
1179
+ }
1180
+ if (win.state === "windowed") {
1181
+ const clamped = clampRectToViewport(win.windowed, {
1182
+ width: viewportWidth,
1183
+ height: viewportHeight
1184
+ });
1185
+ if (!rectsEqual(win.windowed, clamped)) win.windowed = clamped;
1186
+ }
1187
+ win.zIndex = state2.nextZIndex++;
1188
+ },
1189
+ /** Switch to maximized state. */
1190
+ maximizeWindow(state2, action) {
1191
+ const win = state2.windows[action.payload];
1192
+ if (!win) return;
1193
+ releaseTraySlot(state2, win);
1194
+ win.state = "maximized";
1195
+ win.zIndex = state2.nextZIndex++;
1196
+ },
1197
+ /**
1198
+ * Minimize a window — parks it in the tray grid at the bottom of the
1199
+ * viewport. Caller must supply current viewport dimensions so the reducer
1200
+ * can compute the exact position without touching window/document.
1201
+ */
1202
+ minimizeWindow(state2, action) {
1203
+ const { id, viewportWidth, viewportHeight } = action.payload;
1204
+ const win = state2.windows[id];
1205
+ if (!win || win.state === "minimized") return;
1206
+ if (win.popoutMode !== null) return;
1207
+ win.preMinimizedRect = { ...win.windowed };
1208
+ const slot = state2.trayCount;
1209
+ win.traySlot = slot;
1210
+ state2.trayCount += 1;
1211
+ state2.trayViewport = { width: viewportWidth, height: viewportHeight };
1212
+ win.state = "minimized";
1213
+ recomputeAllTrayRects(state2);
1214
+ },
1215
+ /**
1216
+ * Minimize ALL non-minimized, non-maximized windows in one shot.
1217
+ * Use for a "collapse all" button. Caller supplies viewport dimensions.
1218
+ */
1219
+ minimizeAll(state2, action) {
1220
+ const { viewportWidth, viewportHeight } = action.payload;
1221
+ state2.trayViewport = { width: viewportWidth, height: viewportHeight };
1222
+ Object.values(state2.windows).forEach((win) => {
1223
+ if (win.state !== "windowed") return;
1224
+ if (win.popoutMode !== null) return;
1225
+ win.preMinimizedRect = { ...win.windowed };
1226
+ const slot = state2.trayCount;
1227
+ win.traySlot = slot;
1228
+ state2.trayCount += 1;
1229
+ win.state = "minimized";
1230
+ });
1231
+ recomputeAllTrayRects(state2);
1232
+ },
1233
+ /** Update the windowed rect (called during drag or resize). */
1234
+ updateWindowRect(state2, action) {
1235
+ const win = state2.windows[action.payload.id];
1236
+ if (!win) return;
1237
+ win.windowed = { ...win.windowed, ...action.payload.rect };
1238
+ },
1239
+ /**
1240
+ * Clamp a window's `windowed` rect into the current viewport so its body
1241
+ * and footer remain reachable.
1242
+ *
1243
+ * **Why this exists:** drag-and-release outside the viewport (without a
1244
+ * popout firing), restore-from-minimize where `preMinimizedRect` was
1245
+ * captured at a larger viewport, and dock-from-popout where the docked
1246
+ * rect was off-screen all leave the window in a position the user can't
1247
+ * reach. We clamp at the boundary where the user expects to "see" the
1248
+ * window again.
1249
+ *
1250
+ * **Skips popped-out windows** — their rect is OS-managed, not our
1251
+ * concern. Skips minimized + maximized windows — those have their own
1252
+ * rect logic that owners shouldn't fight.
1253
+ *
1254
+ * Caller passes viewport dimensions because reducers can't read
1255
+ * `window.innerWidth` (consistent with `minimizeWindow` /
1256
+ * `arrangeActiveWindows` patterns).
1257
+ */
1258
+ clampWindowRect(state2, action) {
1259
+ const { id, viewportWidth, viewportHeight } = action.payload;
1260
+ const win = state2.windows[id];
1261
+ if (!win) return;
1262
+ if (win.state !== "windowed") return;
1263
+ if (win.popoutMode !== null) return;
1264
+ const clamped = clampRectToViewport(win.windowed, {
1265
+ width: viewportWidth,
1266
+ height: viewportHeight
1267
+ });
1268
+ if (!rectsEqual(win.windowed, clamped)) win.windowed = clamped;
1269
+ },
1270
+ /**
1271
+ * Clamp every docked windowed window into the viewport. Dispatched by
1272
+ * a global resize listener so windows that were positioned for a larger
1273
+ * viewport get nudged back into reach.
1274
+ *
1275
+ * Mirrors `recomputeTrayPositions`'s coverage for minimized chips.
1276
+ */
1277
+ clampAllWindowRects(state2, action) {
1278
+ const { viewportWidth, viewportHeight } = action.payload;
1279
+ Object.values(state2.windows).forEach((win) => {
1280
+ if (win.state !== "windowed") return;
1281
+ if (win.popoutMode !== null) return;
1282
+ const clamped = clampRectToViewport(win.windowed, {
1283
+ width: viewportWidth,
1284
+ height: viewportHeight
1285
+ });
1286
+ if (!rectsEqual(win.windowed, clamped)) win.windowed = clamped;
1287
+ });
1288
+ },
1289
+ /** Arranges all non-minimized windows globally */
1290
+ arrangeActiveWindows(state2, action) {
1291
+ const { layout, viewportWidth, viewportHeight, dirX, dirY, primary } = action.payload;
1292
+ const eligibleWindows = Object.values(state2.windows).filter((w) => w.state === "windowed" && w.popoutMode === null).sort((a, b) => b.zIndex - a.zIndex).map((w) => w.id);
1293
+ if (eligibleWindows.length === 0) return;
1294
+ const updates = computeGlobalArrangement(
1295
+ layout,
1296
+ eligibleWindows,
1297
+ viewportWidth,
1298
+ viewportHeight,
1299
+ dirX,
1300
+ dirY,
1301
+ primary
1302
+ );
1303
+ updates.forEach(({ id, rect }) => {
1304
+ if (state2.windows[id]) {
1305
+ state2.windows[id].windowed = {
1306
+ ...state2.windows[id].windowed,
1307
+ ...rect
1308
+ };
1309
+ }
1310
+ });
1311
+ },
1312
+ /**
1313
+ * Recompute tray positions for all minimized windows after a viewport
1314
+ * resize. Each window keeps its existing traySlot number — only x/y/w/h
1315
+ * are recalculated. No-op if nothing is minimized.
1316
+ */
1317
+ recomputeTrayPositions(state2, action) {
1318
+ const { viewportWidth, viewportHeight } = action.payload;
1319
+ if (!Number.isFinite(viewportWidth) || !Number.isFinite(viewportHeight) || viewportWidth <= 0 || viewportHeight <= 0) {
1320
+ return;
1321
+ }
1322
+ state2.trayViewport = { width: viewportWidth, height: viewportHeight };
1323
+ const minimized = Object.values(state2.windows).filter(
1324
+ (w) => w.state === "minimized" && w.traySlot !== null
1325
+ );
1326
+ if (minimized.length === 0) return;
1327
+ recomputeAllTrayRects(state2);
1328
+ },
1329
+ /**
1330
+ * Restore ALL minimized windows to their pre-minimized rects in one shot.
1331
+ * Maximized windows are left alone — only minimized ones are affected.
1332
+ */
1333
+ restoreAll(state2) {
1334
+ Object.values(state2.windows).forEach((win) => {
1335
+ if (win.state !== "minimized") return;
1336
+ if (win.preMinimizedRect) {
1337
+ win.windowed = win.preMinimizedRect;
1338
+ win.preMinimizedRect = null;
1339
+ }
1340
+ win.traySlot = null;
1341
+ win.state = "windowed";
1342
+ win.zIndex = state2.nextZIndex++;
1343
+ });
1344
+ Object.values(state2.pendingRestores).forEach((session) => {
1345
+ if (session.state !== "minimized") return;
1346
+ session.state = "windowed";
1347
+ session.traySlot = null;
1348
+ session.renderRect = session.windowedRect;
1349
+ });
1350
+ state2.trayCount = 0;
1351
+ state2.trayViewport = null;
1352
+ },
1353
+ /** Show or hide all windows (they stay mounted). */
1354
+ toggleWindowsHidden(state2) {
1355
+ state2.windowsHidden = !state2.windowsHidden;
1356
+ },
1357
+ /** Move a minimized chip to a new tray slot (drag-within-tray). */
1358
+ moveTraySlot(state2, action) {
1359
+ const { id, toSlot } = action.payload;
1360
+ const win = state2.windows[id];
1361
+ if (!win || win.traySlot === null) return;
1362
+ const fromSlot = win.traySlot;
1363
+ const boundedSlot = Math.min(
1364
+ Math.max(0, Math.floor(toSlot)),
1365
+ Math.max(0, state2.trayCount - 1)
1366
+ );
1367
+ if (fromSlot === boundedSlot) return;
1368
+ const rectBySlot = /* @__PURE__ */ new Map();
1369
+ Object.values(state2.windows).forEach((entry) => {
1370
+ if (entry.traySlot !== null) {
1371
+ rectBySlot.set(entry.traySlot, { ...entry.windowed });
1372
+ }
1373
+ });
1374
+ Object.values(state2.pendingRestores).forEach((session) => {
1375
+ if (session.traySlot !== null) {
1376
+ rectBySlot.set(session.traySlot, { ...session.renderRect });
1377
+ }
1378
+ });
1379
+ Object.values(state2.windows).forEach((w) => {
1380
+ if (w.id === id || w.traySlot === null) return;
1381
+ if (fromSlot < boundedSlot && w.traySlot > fromSlot && w.traySlot <= boundedSlot) {
1382
+ w.traySlot -= 1;
1383
+ } else if (fromSlot > boundedSlot && w.traySlot >= boundedSlot && w.traySlot < fromSlot) {
1384
+ w.traySlot += 1;
1385
+ }
1386
+ });
1387
+ Object.values(state2.pendingRestores).forEach((session) => {
1388
+ if (session.traySlot === null) return;
1389
+ if (fromSlot < boundedSlot && session.traySlot > fromSlot && session.traySlot <= boundedSlot) {
1390
+ session.traySlot -= 1;
1391
+ } else if (fromSlot > boundedSlot && session.traySlot >= boundedSlot && session.traySlot < fromSlot) {
1392
+ session.traySlot += 1;
1393
+ }
1394
+ });
1395
+ win.traySlot = boundedSlot;
1396
+ Object.values(state2.windows).forEach((entry) => {
1397
+ if (entry.traySlot === null) return;
1398
+ const nextRect = rectBySlot.get(entry.traySlot);
1399
+ if (nextRect) entry.windowed = nextRect;
1400
+ });
1401
+ Object.values(state2.pendingRestores).forEach((session) => {
1402
+ if (session.traySlot === null) return;
1403
+ const nextRect = rectBySlot.get(session.traySlot);
1404
+ if (nextRect) session.renderRect = nextRect;
1405
+ });
1406
+ recomputeAllTrayRects(state2);
1407
+ },
1408
+ /**
1409
+ * Transition a window into popped-out mode.
1410
+ *
1411
+ * Caller is responsible for actually opening the Document PiP / popup
1412
+ * window FIRST (it requires a synchronous user gesture). This reducer is
1413
+ * dispatched only after the window resolves successfully so Redux state
1414
+ * always matches reality.
1415
+ *
1416
+ * If the window is currently minimized, the tray slot is freed first
1417
+ * (same compaction logic as `restoreWindow`). The current `windowed`
1418
+ * rect is saved to `prePopoutRect` so dock-back returns to the original
1419
+ * dimensions.
1420
+ *
1421
+ * Single-PiP enforcement: callers MUST check `selectActivePipWindowId`
1422
+ * before requesting `mode: "pip"`. This reducer is defense-in-depth and
1423
+ * silently no-ops if a different window already holds the PiP slot.
1424
+ */
1425
+ popOutWindow(state2, action) {
1426
+ const { id, mode } = action.payload;
1427
+ const win = state2.windows[id];
1428
+ if (!win) return;
1429
+ if (mode === "pip" && state2.activePipWindowId !== null && state2.activePipWindowId !== id) {
1430
+ return;
1431
+ }
1432
+ if (win.traySlot !== null) {
1433
+ releaseTraySlot(state2, win);
1434
+ if (win.preMinimizedRect) {
1435
+ win.windowed = win.preMinimizedRect;
1436
+ win.preMinimizedRect = null;
1437
+ }
1438
+ win.state = "windowed";
1439
+ }
1440
+ if (win.prePopoutRect === null) {
1441
+ win.prePopoutRect = { ...win.windowed };
1442
+ }
1443
+ win.popoutMode = mode;
1444
+ if (mode === "pip") {
1445
+ state2.activePipWindowId = id;
1446
+ }
1447
+ if (state2.popoutCandidateId === id) {
1448
+ state2.popoutCandidateId = null;
1449
+ }
1450
+ },
1451
+ /**
1452
+ * Transition a popped-out window back into the parent viewport.
1453
+ *
1454
+ * Centers the window in the parent viewport using `prePopoutRect`
1455
+ * width/height (falls back to current `windowed` size), clears
1456
+ * `popoutMode`, releases the PiP slot if held, and bumps z-index.
1457
+ * Caller is responsible for closing the actual browser popout window.
1458
+ */
1459
+ dockWindow(state2, action) {
1460
+ const { id, viewportWidth, viewportHeight } = action.payload;
1461
+ const win = state2.windows[id];
1462
+ if (!win || win.popoutMode === null) return;
1463
+ if (state2.activePipWindowId === win.id) {
1464
+ state2.activePipWindowId = null;
1465
+ }
1466
+ const savedRect = win.prePopoutRect ?? win.windowed;
1467
+ const centered = centerRectInViewport(savedRect, {
1468
+ width: viewportWidth,
1469
+ height: viewportHeight
1470
+ });
1471
+ win.windowed = centered;
1472
+ win.prePopoutRect = null;
1473
+ win.popoutMode = null;
1474
+ win.zIndex = state2.nextZIndex++;
1475
+ },
1476
+ /**
1477
+ * Set or clear the drag-out candidate window. Used purely for visual
1478
+ * feedback during a drag: when set, the WindowPanel renders a
1479
+ * "Release to pop out" outline. Cleared on every pointerup regardless
1480
+ * of whether popout actually fires.
1481
+ */
1482
+ setPopoutCandidate(state2, action) {
1483
+ state2.popoutCandidateId = action.payload.id;
1484
+ },
1485
+ /** Restore window geometry and state from localStorage */
1486
+ restoreWindowState(state2, action) {
1487
+ const restored = action.payload;
1488
+ let maxZ = state2.nextZIndex;
1489
+ Object.entries(restored).forEach(([id, win]) => {
1490
+ if (!state2.windows[id]) {
1491
+ return;
1492
+ }
1493
+ state2.windows[id] = {
1494
+ ...state2.windows[id],
1495
+ ...win,
1496
+ popoutMode: null,
1497
+ prePopoutRect: null
1498
+ };
1499
+ if (win.zIndex >= maxZ) maxZ = win.zIndex + 1;
1500
+ });
1501
+ state2.nextZIndex = Math.max(state2.nextZIndex, maxZ);
1502
+ state2.activePipWindowId = null;
1503
+ state2.popoutCandidateId = null;
1504
+ state2.trayCount = Object.values(state2.windows).filter(
1505
+ (w) => w.traySlot !== null
1506
+ ).length;
1507
+ }
1508
+ },
1509
+ extraReducers: (builder) => {
1510
+ builder.addMatcher(
1511
+ (action) => action.type === "overlays/closeOverlay",
1512
+ (state2, action) => {
1513
+ const payload = action.payload;
1514
+ if (!payload || typeof payload !== "object") return;
1515
+ const overlayId = payload.overlayId;
1516
+ const instanceId = payload.instanceId ?? "default";
1517
+ if (typeof overlayId !== "string" || typeof instanceId !== "string") {
1518
+ return;
1519
+ }
1520
+ const pending = Object.values(state2.pendingRestores).find(
1521
+ (session) => session.overlayId === overlayId && session.instanceId === instanceId
1522
+ );
1523
+ const hasMatchingLive = pending ? hasMatchingLiveTrayReservation(
1524
+ state2,
1525
+ overlayId,
1526
+ instanceId,
1527
+ pending.traySlot
1528
+ ) : false;
1529
+ if (!hasMatchingLive && pending?.traySlot != null) {
1530
+ compactReservedTraySlot(state2, pending.traySlot, pending.renderRect);
1531
+ }
1532
+ if (pending) delete state2.pendingRestores[pending.sessionKey];
1533
+ Object.values(state2.windows).forEach((entry) => {
1534
+ if (entry.persistence?.overlayId === overlayId && entry.persistence.instanceId === instanceId) {
1535
+ entry.persistence.closing = true;
1536
+ }
1537
+ });
1538
+ }
1539
+ );
1540
+ builder.addMatcher(
1541
+ (action) => action.type === "overlays/closeAllOverlays",
1542
+ (state2) => {
1543
+ Object.values(state2.pendingRestores).filter(
1544
+ (session) => session.traySlot !== null && !hasMatchingLiveTrayReservation(
1545
+ state2,
1546
+ session.overlayId,
1547
+ session.instanceId,
1548
+ session.traySlot
1549
+ )
1550
+ ).sort((a, b) => (b.traySlot ?? -1) - (a.traySlot ?? -1)).forEach((session) => {
1551
+ if (session.traySlot !== null) {
1552
+ compactReservedTraySlot(
1553
+ state2,
1554
+ session.traySlot,
1555
+ session.renderRect
1556
+ );
1557
+ }
1558
+ });
1559
+ state2.pendingRestores = {};
1560
+ Object.values(state2.windows).forEach((entry) => {
1561
+ if (entry.persistence) entry.persistence.closing = true;
1562
+ });
1563
+ }
1564
+ );
1565
+ builder.addMatcher(
1566
+ (action) => action.type === "overlays/closeAllInstancesOfOverlay",
1567
+ (state2, action) => {
1568
+ const payload = action.payload;
1569
+ const overlayId = payload && typeof payload === "object" && "overlayId" in payload ? payload.overlayId : null;
1570
+ if (typeof overlayId !== "string") return;
1571
+ Object.values(state2.pendingRestores).filter(
1572
+ (session) => session.overlayId === overlayId && session.traySlot !== null && !hasMatchingLiveTrayReservation(
1573
+ state2,
1574
+ session.overlayId,
1575
+ session.instanceId,
1576
+ session.traySlot
1577
+ )
1578
+ ).sort((a, b) => (b.traySlot ?? -1) - (a.traySlot ?? -1)).forEach((session) => {
1579
+ if (session.traySlot !== null) {
1580
+ compactReservedTraySlot(
1581
+ state2,
1582
+ session.traySlot,
1583
+ session.renderRect
1584
+ );
1585
+ }
1586
+ });
1587
+ Object.entries(state2.pendingRestores).forEach(([key, session]) => {
1588
+ if (session.overlayId === overlayId)
1589
+ delete state2.pendingRestores[key];
1590
+ });
1591
+ Object.values(state2.windows).forEach((entry) => {
1592
+ if (entry.persistence?.overlayId === overlayId) {
1593
+ entry.persistence.closing = true;
1594
+ }
1595
+ });
1596
+ }
1597
+ );
1598
+ }
1599
+ });
1600
+ var {
1601
+ raiseTransientWindow,
1602
+ releaseTransientWindow,
1603
+ registerWindow,
1604
+ unregisterWindow,
1605
+ focusWindow,
1606
+ restoreWindow,
1607
+ revealWindow,
1608
+ maximizeWindow,
1609
+ minimizeWindow,
1610
+ minimizeAll,
1611
+ restoreAll,
1612
+ recomputeTrayPositions,
1613
+ updateWindowRect,
1614
+ updateWindowTitle,
1615
+ toggleWindowsHidden,
1616
+ moveTraySlot,
1617
+ arrangeActiveWindows,
1618
+ popOutWindow,
1619
+ dockWindow,
1620
+ setPopoutCandidate,
1621
+ clampWindowRect,
1622
+ clampAllWindowRects,
1623
+ restoreWindowState,
1624
+ hydrateWindowSessions,
1625
+ confirmWindowRestored,
1626
+ discardPendingWindowSession,
1627
+ updateWindowPersistence,
1628
+ markWindowClosing,
1629
+ clearWindowPersistenceState
1630
+ } = windowManagerSlice.actions;
1631
+ var selectWindowsMap = (state2) => state2.windowManager.windows;
1632
+ var selectPendingWindowRestores = (state2) => state2.windowManager.pendingRestores;
1633
+ var selectWindowPersistence = (id) => (state2) => state2.windowManager.windows[id]?.persistence;
1634
+ var selectWindow = (id) => (state2) => state2.windowManager.windows[id];
1635
+ var selectWindowState = (id) => (state2) => state2.windowManager.windows[id]?.state;
1636
+ var selectWindowRect = (id) => (state2) => state2.windowManager.windows[id]?.windowed;
1637
+ var selectWindowZIndex = (id) => (state2) => state2.windowManager.windows[id]?.zIndex ?? BASE_Z;
1638
+ var selectTransientZIndex = (id) => (state2) => state2.windowManager.transientZ[id] ?? BASE_Z;
1639
+ var selectWindowTitle = (id) => (state2) => state2.windowManager.windows[id]?.title ?? id;
1640
+ var selectTraySlotWidth = () => TRAY_SLOT_WIDTH;
1641
+ var selectWindowsHidden = (state2) => state2.windowManager.windowsHidden;
1642
+ var selectTrayWindows = (0, import_toolkit2.createSelector)(
1643
+ [selectWindowsMap],
1644
+ (windows) => Object.values(windows).filter((w) => w.state === "minimized").sort((a, b) => (a.traySlot ?? 0) - (b.traySlot ?? 0))
1645
+ );
1646
+ var selectAllWindows = (0, import_toolkit2.createSelector)(
1647
+ [selectWindowsMap],
1648
+ (windows) => Object.values(windows).sort((a, b) => b.zIndex - a.zIndex)
1649
+ );
1650
+ var selectAllMinimized = (0, import_toolkit2.createSelector)(
1651
+ [selectWindowsMap],
1652
+ (windows) => {
1653
+ const wins = Object.values(windows);
1654
+ return wins.length > 0 && wins.every((w) => w.state === "minimized");
1655
+ }
1656
+ );
1657
+ var selectPopoutMode = (id) => (state2) => state2.windowManager.windows[id]?.popoutMode ?? null;
1658
+ var selectIsPoppedOut = (id) => (state2) => (state2.windowManager.windows[id]?.popoutMode ?? null) !== null;
1659
+ var selectActivePipWindowId = (state2) => state2.windowManager.activePipWindowId;
1660
+ var selectPopoutCandidateId = (state2) => state2.windowManager.popoutCandidateId;
1661
+ var selectPrePopoutRect = (id) => (state2) => state2.windowManager.windows[id]?.prePopoutRect ?? null;
1662
+ var selectDockedWindows = (0, import_toolkit2.createSelector)(
1663
+ [selectWindowsMap],
1664
+ (windows) => Object.values(windows).filter((w) => w.popoutMode === null).sort((a, b) => b.zIndex - a.zIndex)
1665
+ );
1666
+ var windowManagerReducer = windowManagerSlice.reducer;
1667
+
1668
+ // src/core/linksSlice.ts
1669
+ var import_toolkit3 = require("@reduxjs/toolkit");
1670
+ var initialState3 = {
1671
+ entries: {},
1672
+ isHydrated: false
1673
+ };
1674
+ var urlSyncSlice = (0, import_toolkit3.createSlice)({
1675
+ name: "urlSync",
1676
+ initialState: initialState3,
1677
+ reducers: {
1678
+ setHydrated: (state2) => {
1679
+ state2.isHydrated = true;
1680
+ },
1681
+ registerSyncEntry: (state2, action) => {
1682
+ const key = `${action.payload.typeKey}:${action.payload.instanceId}`;
1683
+ state2.entries[key] = action.payload;
1684
+ },
1685
+ unregisterSyncEntry: (state2, action) => {
1686
+ const key = `${action.payload.typeKey}:${action.payload.instanceId}`;
1687
+ delete state2.entries[key];
1688
+ }
1689
+ }
1690
+ });
1691
+ var linksReducer = urlSyncSlice.reducer;
1692
+ var { setHydrated, registerSyncEntry, unregisterSyncEntry } = urlSyncSlice.actions;
1693
+ var selectUrlSyncEntries = (state2) => state2.urlSync.entries;
1694
+ var selectIsUrlHydrated = (state2) => state2.urlSync.isHydrated;
1695
+
1696
+ // src/core/overlayStateSlice.ts
1697
+ var import_toolkit4 = require("@reduxjs/toolkit");
1698
+ var initialState4 = { entries: {} };
1699
+ var overlayStateSlice = (0, import_toolkit4.createSlice)({
1700
+ name: "overlayState",
1701
+ initialState: initialState4,
1702
+ reducers: {
1703
+ setOverlayStateValue(state2, action) {
1704
+ const { key, name, value } = action.payload;
1705
+ const entry = state2.entries[key] ??= { values: {}, updatedAt: 0 };
1706
+ entry.values[name] = value;
1707
+ entry.updatedAt = Date.now();
1708
+ },
1709
+ replaceOverlayState(state2, action) {
1710
+ const { key, values } = action.payload;
1711
+ state2.entries[key] = { values: { ...values }, updatedAt: Date.now() };
1712
+ },
1713
+ clearOverlayState(state2, action) {
1714
+ delete state2.entries[action.payload.key];
1715
+ },
1716
+ hydrateOverlayState(state2, action) {
1717
+ const now = Date.now();
1718
+ for (const [key, entry] of Object.entries(action.payload.entries)) {
1719
+ state2.entries[key] = { values: { ...entry.values }, updatedAt: entry.updatedAt ?? now };
1720
+ }
1721
+ }
1722
+ }
1723
+ });
1724
+ var overlayStateReducer = overlayStateSlice.reducer;
1725
+ var { setOverlayStateValue, replaceOverlayState, clearOverlayState, hydrateOverlayState } = overlayStateSlice.actions;
1726
+ var EMPTY_VALUES = Object.freeze({});
1727
+ var selectOverlayStateValue = (state2, key, name) => state2.overlayState.entries[key]?.values[name];
1728
+ var selectOverlayStateFor = (state2, key) => state2.overlayState.entries[key]?.values ?? EMPTY_VALUES;
1729
+
1730
+ // src/core/store.ts
1731
+ var import_toolkit5 = require("@reduxjs/toolkit");
1732
+
1733
+ // src/core/middleware/closeLifecycle.ts
1734
+ var import_global_slot4 = require("@ai-matrx/kit/global-slot");
1735
+
1736
+ // src/handles/callbackManager.ts
1737
+ var import_global_slot3 = require("@ai-matrx/kit/global-slot");
1738
+
1739
+ // src/handles/ids.ts
1740
+ function newHandleId() {
1741
+ const c = typeof globalThis.crypto === "undefined" ? void 0 : globalThis.crypto;
1742
+ if (c && typeof c.randomUUID === "function") return c.randomUUID();
1743
+ const bytes = new Uint8Array(16);
1744
+ if (c && typeof c.getRandomValues === "function") {
1745
+ c.getRandomValues(bytes);
1746
+ } else {
1747
+ for (let i = 0; i < 16; i += 1) bytes[i] = Math.floor(Math.random() * 256);
1748
+ }
1749
+ bytes[6] = (bytes[6] ?? 0) & 15 | 64;
1750
+ bytes[8] = (bytes[8] ?? 0) & 63 | 128;
1751
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1752
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
1753
+ }
1754
+
1755
+ // src/handles/callbackManager.ts
1756
+ var HANDLES_SLOT = "ai-matrx.summon.handles";
1757
+ var handlesState = () => (0, import_global_slot3.globalSlot)(HANDLES_SLOT, () => ({
1758
+ callbacks: /* @__PURE__ */ new Map(),
1759
+ groups: /* @__PURE__ */ new Map(),
1760
+ owners: /* @__PURE__ */ new Map(),
1761
+ registryListeners: /* @__PURE__ */ new Set()
1762
+ }));
1763
+ function invoke(slot, data, context) {
1764
+ if (typeof slot !== "function") return void 0;
1765
+ return slot(data, context);
1766
+ }
1767
+ function invokeListener(listener, data, context) {
1768
+ return listener(data, context);
1769
+ }
1770
+ var CallbackManager = class {
1771
+ get s() {
1772
+ return handlesState();
1773
+ }
1774
+ /**
1775
+ * Observe the registry itself: `listener` runs after any entry is added or removed. This lets a
1776
+ * view that resolves ids with `get` re-render when an id goes away (`useSyncExternalStore`).
1777
+ */
1778
+ subscribeToRegistry(listener) {
1779
+ const listeners = this.s.registryListeners;
1780
+ listeners.add(listener);
1781
+ return () => {
1782
+ listeners.delete(listener);
1783
+ };
1784
+ }
1785
+ notifyRegistry() {
1786
+ for (const listener of [...this.s.registryListeners]) listener();
1787
+ }
1788
+ /** Register a callback; returns its id. */
1789
+ register(callback) {
1790
+ return this.registerEntry(callback, {});
1791
+ }
1792
+ /** Register with optional context, group and owner. */
1793
+ registerWithContext(callback, options = {}) {
1794
+ return this.registerEntry(callback, options);
1795
+ }
1796
+ /** @internal The one insertion path (typed helpers pass a fixed id and a removal hook). */
1797
+ registerEntry(slot, options) {
1798
+ const s = this.s;
1799
+ const callbackId = options.id ?? newHandleId();
1800
+ if (s.callbacks.has(callbackId)) this.deleteEntry(callbackId, false);
1801
+ const entry = { callback: slot };
1802
+ if (options.context !== void 0) entry.context = options.context;
1803
+ if (options.groupId !== void 0) entry.groupId = options.groupId;
1804
+ if (options.owner !== void 0) entry.owner = options.owner;
1805
+ if (options.onRemove !== void 0) entry.onRemove = options.onRemove;
1806
+ s.callbacks.set(callbackId, entry);
1807
+ if (options.groupId !== void 0) {
1808
+ const group = s.groups.get(options.groupId) ?? /* @__PURE__ */ new Set();
1809
+ group.add(callbackId);
1810
+ s.groups.set(options.groupId, group);
1811
+ }
1812
+ if (options.owner !== void 0) {
1813
+ const owned = s.owners.get(options.owner) ?? /* @__PURE__ */ new Set();
1814
+ owned.add(callbackId);
1815
+ s.owners.set(options.owner, owned);
1816
+ }
1817
+ this.notifyRegistry();
1818
+ return callbackId;
1819
+ }
1820
+ /** Subscribe to results for a callback id. */
1821
+ subscribe(callbackId, listener) {
1822
+ const entry = this.s.callbacks.get(callbackId);
1823
+ if (!entry) return false;
1824
+ (entry.listeners ??= []).push(listener);
1825
+ return true;
1826
+ }
1827
+ /** Unsubscribe a listener from a callback id. */
1828
+ unsubscribe(callbackId, listener) {
1829
+ const entry = this.s.callbacks.get(callbackId);
1830
+ if (!entry || !entry.listeners) return false;
1831
+ const index = entry.listeners.indexOf(listener);
1832
+ if (index === -1) return false;
1833
+ entry.listeners.splice(index, 1);
1834
+ return true;
1835
+ }
1836
+ /** Run a callback once, notify its listeners, then remove it. */
1837
+ trigger(callbackId, data) {
1838
+ const entry = this.s.callbacks.get(callbackId);
1839
+ if (!entry) return;
1840
+ void invoke(entry.callback, data);
1841
+ if (entry.listeners && entry.listeners.length > 0) {
1842
+ for (const listener of [...entry.listeners]) void invokeListener(listener, data);
1843
+ }
1844
+ this.deleteEntry(callbackId, true);
1845
+ }
1846
+ /** Trigger with merged context and progress; removes unless `removeAfterTrigger: false`. */
1847
+ triggerWithContext(callbackId, data, options) {
1848
+ const entry = this.s.callbacks.get(callbackId);
1849
+ if (!entry) return;
1850
+ const mergedContext = {
1851
+ ...entry.context,
1852
+ ...options?.context,
1853
+ ...options?.progress && { progress: options.progress }
1854
+ };
1855
+ void invoke(entry.callback, data, mergedContext);
1856
+ if (entry.listeners && entry.listeners.length > 0) {
1857
+ for (const listener of [...entry.listeners]) void invokeListener(listener, data, mergedContext);
1858
+ }
1859
+ if (options?.removeAfterTrigger !== false) this.deleteEntry(callbackId, true);
1860
+ }
1861
+ /** Trigger every callback in a group. */
1862
+ triggerGroup(groupId, data, options) {
1863
+ const group = this.s.groups.get(groupId);
1864
+ if (!group) return;
1865
+ for (const callbackId of [...group]) this.triggerWithContext(callbackId, data, options);
1866
+ if (options?.removeAfterTrigger !== false) this.s.groups.delete(groupId);
1867
+ }
1868
+ /**
1869
+ * Run the one durable command registered for a group. A command is retained on rejection so its
1870
+ * caller can retry, and is removed only after its promise settles successfully.
1871
+ */
1872
+ async triggerGroupCommand(groupId, data, options) {
1873
+ const group = this.s.groups.get(groupId);
1874
+ const callbackIds = group ? [...group] : [];
1875
+ const callbackId = callbackIds[0];
1876
+ if (callbackIds.length !== 1 || callbackId === void 0) {
1877
+ throw new Error(`Expected exactly one command callback for group "${groupId}", found ${callbackIds.length}`);
1878
+ }
1879
+ const entry = this.s.callbacks.get(callbackId);
1880
+ if (!entry) throw new Error(`Command callback "${callbackId}" is no longer available`);
1881
+ const mergedContext = { ...entry.context, ...options?.context };
1882
+ await invoke(entry.callback, data, mergedContext);
1883
+ for (const listener of entry.listeners ?? []) {
1884
+ try {
1885
+ await invokeListener(listener, data, mergedContext);
1886
+ } catch (error) {
1887
+ console.error("[overlays/handles] command observer failed", error);
1888
+ }
1889
+ }
1890
+ if (options?.removeAfterSuccess !== false) {
1891
+ this.deleteEntry(callbackId, true);
1892
+ this.s.groups.delete(groupId);
1893
+ }
1894
+ }
1895
+ /** Report progress to a callback (or every callback of a group) without removing it. */
1896
+ updateProgress(id, progress, options) {
1897
+ const progressInfo = { progress, status: options?.status || "running" };
1898
+ if (options?.error !== void 0) progressInfo.error = options.error;
1899
+ if (options?.groupId) {
1900
+ this.triggerGroup(id, null, { progress: progressInfo, removeAfterTrigger: false });
1901
+ } else {
1902
+ this.triggerWithContext(id, null, { progress: progressInfo, removeAfterTrigger: false });
1903
+ }
1904
+ }
1905
+ /** Create a new, empty group and return its id. */
1906
+ createGroup() {
1907
+ const groupId = newHandleId();
1908
+ this.s.groups.set(groupId, /* @__PURE__ */ new Set());
1909
+ return groupId;
1910
+ }
1911
+ /** Remove one entry (and its group/owner membership). */
1912
+ remove(callbackId) {
1913
+ this.deleteEntry(callbackId, true);
1914
+ }
1915
+ /** Alias of `remove` — the preferred name when unregistering a handle on unmount. */
1916
+ unregister(callbackId) {
1917
+ this.remove(callbackId);
1918
+ }
1919
+ /** The stored callback or object handle for an id, without invoking it. */
1920
+ get(callbackId) {
1921
+ return this.s.callbacks.get(callbackId)?.callback;
1922
+ }
1923
+ /** True when the id is registered. */
1924
+ has(callbackId) {
1925
+ return this.s.callbacks.has(callbackId);
1926
+ }
1927
+ /**
1928
+ * Register an object handle (a widget handle, an editor API, …). The object occupies the slot;
1929
+ * it is never invoked by `trigger`, only retrieved with `get`.
1930
+ */
1931
+ registerWidgetHandle(handle, options = {}) {
1932
+ return this.registerEntry(handle, options.owner === void 0 ? {} : { owner: options.owner });
1933
+ }
1934
+ /** Remove a group and all its callbacks. */
1935
+ removeGroup(groupId) {
1936
+ const s = this.s;
1937
+ const group = s.groups.get(groupId);
1938
+ if (!group) return;
1939
+ s.groups.delete(groupId);
1940
+ for (const callbackId of [...group]) this.deleteEntry(callbackId, false);
1941
+ this.notifyRegistry();
1942
+ }
1943
+ /** Remove everything registered with `owner` (an InstanceKey). Returns how many were removed. */
1944
+ disposeOwner(owner) {
1945
+ const s = this.s;
1946
+ const owned = s.owners.get(owner);
1947
+ if (!owned) return 0;
1948
+ s.owners.delete(owner);
1949
+ const ids = [...owned];
1950
+ for (const callbackId of ids) this.deleteEntry(callbackId, false);
1951
+ if (ids.length > 0) this.notifyRegistry();
1952
+ return ids.length;
1953
+ }
1954
+ /** Ids currently owned by `owner`. */
1955
+ ownedBy(owner) {
1956
+ return [...this.s.owners.get(owner) ?? []];
1957
+ }
1958
+ /** The member ids of a group (empty when unknown). */
1959
+ groupMembers(groupId) {
1960
+ return [...this.s.groups.get(groupId) ?? []];
1961
+ }
1962
+ /** Total registered entries (diagnostics, leak tests). */
1963
+ size() {
1964
+ return this.s.callbacks.size;
1965
+ }
1966
+ /**
1967
+ * The one removal path. Reads the entry BEFORE deleting it so group and owner membership shrink
1968
+ * (the old `removeFromGroups`-after-delete bug), then runs the entry's removal hook.
1969
+ */
1970
+ deleteEntry(callbackId, notify) {
1971
+ const s = this.s;
1972
+ const entry = s.callbacks.get(callbackId);
1973
+ if (!entry) return;
1974
+ s.callbacks.delete(callbackId);
1975
+ if (entry.groupId !== void 0) {
1976
+ const group = s.groups.get(entry.groupId);
1977
+ group?.delete(callbackId);
1978
+ if (group?.size === 0) s.groups.delete(entry.groupId);
1979
+ }
1980
+ if (entry.owner !== void 0) {
1981
+ const owned = s.owners.get(entry.owner);
1982
+ owned?.delete(callbackId);
1983
+ if (owned?.size === 0) s.owners.delete(entry.owner);
1984
+ }
1985
+ if (notify) this.notifyRegistry();
1986
+ entry.onRemove?.();
1987
+ }
1988
+ };
1989
+ var callbackManager = new CallbackManager();
1990
+
1991
+ // src/core/middleware/closeLifecycle.ts
1992
+ var closeState = () => (0, import_global_slot4.globalSlot)("ai-matrx.summon.closeLifecycle", () => ({ flushers: /* @__PURE__ */ new Set() }));
1993
+ function registerCloseFlusher(flush) {
1994
+ const { flushers } = closeState();
1995
+ flushers.add(flush);
1996
+ return () => {
1997
+ flushers.delete(flush);
1998
+ };
1999
+ }
2000
+ function dedupe(identities) {
2001
+ return [...new Map(identities.map((identity) => [`${identity.overlayId}:${identity.instanceId}`, identity])).values()];
2002
+ }
2003
+ function preservedIdentities(state2, overlayId) {
2004
+ const wm = state2.windowManager;
2005
+ if (!wm) return [];
2006
+ const identities = [];
2007
+ for (const entry of Object.values(wm.windows)) {
2008
+ if (!entry.persistence || entry.persistence.closing) continue;
2009
+ if (overlayId && entry.persistence.overlayId !== overlayId) continue;
2010
+ identities.push({ overlayId: entry.persistence.overlayId, instanceId: entry.persistence.instanceId });
2011
+ }
2012
+ for (const session of Object.values(wm.pendingRestores)) {
2013
+ if (overlayId && session.overlayId !== overlayId) continue;
2014
+ identities.push({ overlayId: session.overlayId, instanceId: session.instanceId });
2015
+ }
2016
+ return identities;
2017
+ }
2018
+ function isPlainRecord2(value) {
2019
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2020
+ }
2021
+ var CLOSE_TYPES = /* @__PURE__ */ new Set([
2022
+ overlaysRawActions.closeOverlay.type,
2023
+ overlaysRawActions.toggleOverlay.type,
2024
+ overlaysRawActions.closeAllOverlays.type,
2025
+ overlaysRawActions.closeAllInstancesOfOverlay.type,
2026
+ overlaysRawActions.closeOverlaysBoundToPage.type
2027
+ ]);
2028
+ function releaseOverlayInstance(dispatch, key, options = {}) {
2029
+ callbackManager.disposeOwner(key);
2030
+ if (!options.keepState) dispatch(clearOverlayState({ key }));
2031
+ }
2032
+ function closeInstanceResources(api, key) {
2033
+ const parsed = parseInstanceKey(key);
2034
+ const keepAlive = parsed ? getOverlayMeta(parsed.overlayId)?.keepAlive : void 0;
2035
+ if (keepAlive === "hidden") return;
2036
+ const hasState = api.getState().overlayState?.entries[key] !== void 0;
2037
+ callbackManager.disposeOwner(key);
2038
+ if (keepAlive !== "restore" && hasState) api.dispatch(clearOverlayState({ key }));
2039
+ }
2040
+ var closeLifecycleMiddleware = (api) => (next) => (action) => {
2041
+ if (!isPlainRecord2(action) || typeof action.type !== "string" || !CLOSE_TYPES.has(action.type)) {
2042
+ return next(action);
2043
+ }
2044
+ const type = action.type;
2045
+ const payload = isPlainRecord2(action.payload) ? action.payload : null;
2046
+ const before = api.getState();
2047
+ const openBefore = new Set(listOpenInstanceKeys(before));
2048
+ let closing = [];
2049
+ const intents = [];
2050
+ const overlayId = typeof payload?.overlayId === "string" ? payload.overlayId : null;
2051
+ if (type === overlaysRawActions.closeAllOverlays.type) {
2052
+ closing = preservedIdentities(before);
2053
+ intents.push({ scope: "all" });
2054
+ } else if (type === overlaysRawActions.closeAllInstancesOfOverlay.type && overlayId) {
2055
+ closing = preservedIdentities(before, overlayId);
2056
+ intents.push({ scope: "overlay", overlayId });
2057
+ } else if ((type === overlaysRawActions.closeOverlay.type || type === overlaysRawActions.toggleOverlay.type) && overlayId) {
2058
+ const instanceId = typeof payload?.instanceId === "string" ? payload.instanceId : DEFAULT_INSTANCE_ID;
2059
+ const wasOpen = before.overlays.overlays[overlayId]?.[instanceId]?.isOpen === true;
2060
+ if (type === overlaysRawActions.closeOverlay.type || wasOpen) {
2061
+ closing = [{ overlayId, instanceId }];
2062
+ intents.push({ scope: "exact", overlayId, instanceId });
2063
+ }
2064
+ }
2065
+ const result = next(action);
2066
+ const openAfter = new Set(listOpenInstanceKeys(api.getState()));
2067
+ const closedKeys = [...openBefore].filter((key) => !openAfter.has(key));
2068
+ if (type === overlaysRawActions.closeOverlaysBoundToPage.type) {
2069
+ for (const key of closedKeys) {
2070
+ const parsed = parseInstanceKey(key);
2071
+ if (parsed) closing.push(parsed);
2072
+ }
2073
+ }
2074
+ const exact = dedupe(closing);
2075
+ if (api.getState().windowManager) {
2076
+ for (const identity of exact) api.dispatch(markWindowClosing(identity));
2077
+ }
2078
+ const released = new Set(closedKeys);
2079
+ for (const intent of intents) {
2080
+ if (intent.scope === "exact") released.add(`${intent.overlayId}:${intent.instanceId}`);
2081
+ }
2082
+ for (const key of released) closeInstanceResources(api, key);
2083
+ if (exact.length > 0 || closedKeys.length > 0 || type === overlaysRawActions.closeAllOverlays.type || type === overlaysRawActions.closeAllInstancesOfOverlay.type) {
2084
+ const event = { intents, closedKeys, actionType: type };
2085
+ for (const flush of [...closeState().flushers]) flush(event);
2086
+ }
2087
+ return result;
2088
+ };
2089
+
2090
+ // src/core/middleware/renderWatchdog.ts
2091
+ var import_global_slot5 = require("@ai-matrx/kit/global-slot");
2092
+ var LAZY_WINDOW_MOUNT_DEADLINE_MS = process.env.NODE_ENV === "production" ? 12e3 : 45e3;
2093
+ var OPEN_TYPE = "overlays/openOverlay";
2094
+ var TOGGLE_TYPE = "overlays/toggleOverlay";
2095
+ var DEFAULT_INSTANCE_ID2 = "default";
2096
+ function isPlainRecord3(value) {
2097
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2098
+ }
2099
+ var CHECK_DELAY_MS = 2500;
2100
+ var ACK_SETTLE_MS = 300;
2101
+ var ACK_SETTLE_RETRY_MS = 700;
2102
+ var NO_MOUNT_DEADLINE_MS = LAZY_WINDOW_MOUNT_DEADLINE_MS;
2103
+ var TOAST_DURATION_MS = 8e3;
2104
+ var RECOVERY_POLL_MS = 500;
2105
+ var RENDER_WATCHDOG_SLOT = "ai-matrx.summon.renderWatchdog";
2106
+ var watchdogState = () => (0, import_global_slot5.globalSlot)(RENDER_WATCHDOG_SLOT, () => ({
2107
+ renderAcks: /* @__PURE__ */ new Map(),
2108
+ ackWaiters: /* @__PURE__ */ new Map(),
2109
+ watches: /* @__PURE__ */ new Map()
2110
+ }));
2111
+ function notifyRenderAck(overlayId) {
2112
+ const notify = watchdogState().ackWaiters.get(overlayId);
2113
+ if (notify) {
2114
+ watchdogState().ackWaiters.delete(overlayId);
2115
+ notify();
2116
+ }
2117
+ }
2118
+ function ackOverlayRender(overlayId, windowId) {
2119
+ watchdogState().renderAcks.set(overlayId, { kind: "window", windowId });
2120
+ notifyRenderAck(overlayId);
2121
+ }
2122
+ function clearOverlayRender(overlayId, windowId) {
2123
+ const ack = watchdogState().renderAcks.get(overlayId);
2124
+ if (ack?.kind === "window" && ack.windowId === windowId) {
2125
+ watchdogState().renderAcks.delete(overlayId);
2126
+ }
2127
+ }
2128
+ function ackOverlaySurfaceRender(overlayId) {
2129
+ watchdogState().renderAcks.set(overlayId, { kind: "surface" });
2130
+ notifyRenderAck(overlayId);
2131
+ }
2132
+ function clearOverlaySurfaceRender(overlayId) {
2133
+ if (watchdogState().renderAcks.get(overlayId)?.kind === "surface") {
2134
+ watchdogState().renderAcks.delete(overlayId);
2135
+ }
2136
+ }
2137
+ function acknowledgedWindowId(overlayId, fallback) {
2138
+ const ack = watchdogState().renderAcks.get(overlayId);
2139
+ return ack?.kind === "window" ? ack.windowId : fallback;
2140
+ }
2141
+ function rectOnScreen(rect, viewportWidth, viewportHeight) {
2142
+ const MARGIN = 24;
2143
+ const intersectsX = rect.x < viewportWidth - MARGIN && rect.x + rect.width > MARGIN;
2144
+ const intersectsY = rect.y < viewportHeight - MARGIN && rect.y + rect.height > MARGIN;
2145
+ return intersectsX && intersectsY;
2146
+ }
2147
+ function diagnoseOverlayRender(args) {
2148
+ const {
2149
+ entry,
2150
+ windowsHidden,
2151
+ viewportWidth,
2152
+ viewportHeight,
2153
+ surfaceAcknowledged = false
2154
+ } = args;
2155
+ if (surfaceAcknowledged) return { ok: true, reason: null };
2156
+ if (!entry) return { ok: false, reason: "no-window-registered" };
2157
+ if (entry.popoutMode !== null) return { ok: true, reason: null };
2158
+ if (windowsHidden) return { ok: false, reason: "all-windows-hidden" };
2159
+ if (entry.state === "minimized") return { ok: true, reason: null };
2160
+ if (entry.state === "maximized") return { ok: true, reason: null };
2161
+ if (entry.windowed.width <= 1 || entry.windowed.height <= 1) {
2162
+ return { ok: false, reason: "zero-size" };
2163
+ }
2164
+ if (!rectOnScreen(entry.windowed, viewportWidth, viewportHeight)) {
2165
+ return { ok: false, reason: "off-screen" };
2166
+ }
2167
+ return { ok: true, reason: null };
2168
+ }
2169
+ var REASON_HINT = {
2170
+ "no-window-registered": "no <WindowPanel> mounted for this overlay \u2014 check its definition's render module, the lazy import, and that the component returns a <WindowPanel> when open",
2171
+ "all-windows-hidden": "the global 'Hide all windows' flag is on \u2014 opening should have cleared it (revealWindow)",
2172
+ "zero-size": "the window rect collapsed to zero size",
2173
+ "off-screen": "the window rect is outside the viewport \u2014 opening should have clamped it"
2174
+ };
2175
+ function endWatch(watch) {
2176
+ if (watch.timer !== null) globalThis.clearTimeout(watch.timer);
2177
+ if (watch.deadlineTimer !== null) globalThis.clearTimeout(watch.deadlineTimer);
2178
+ watchdogState().ackWaiters.delete(watch.overlayId);
2179
+ watchdogState().watches.delete(watch.overlayId);
2180
+ }
2181
+ function evaluate(store, watch) {
2182
+ const state2 = store.getState();
2183
+ if (!selectIsOverlayOpen(state2, watch.overlayId, DEFAULT_INSTANCE_ID2)) {
2184
+ return null;
2185
+ }
2186
+ const ack = watchdogState().renderAcks.get(watch.overlayId);
2187
+ const windowId = ack?.kind === "window" ? ack.windowId : watch.slug;
2188
+ const entry = state2.windowManager.windows[windowId];
2189
+ const { vw, vh, degenerate } = safeViewportDims();
2190
+ return {
2191
+ viewportWidth: vw,
2192
+ viewportHeight: vh,
2193
+ viewportDegenerate: degenerate,
2194
+ renderAcknowledgement: ack?.kind ?? "none",
2195
+ diag: diagnoseOverlayRender({
2196
+ entry,
2197
+ windowsHidden: state2.windowManager.windowsHidden,
2198
+ viewportWidth: vw,
2199
+ viewportHeight: vh,
2200
+ surfaceAcknowledged: ack?.kind === "surface"
2201
+ }),
2202
+ windowId,
2203
+ entry,
2204
+ windowsHidden: state2.windowManager.windowsHidden
2205
+ };
2206
+ }
2207
+ function startWatch(store, ports, overlayId, slug, label) {
2208
+ if (watchdogState().watches.has(overlayId)) return;
2209
+ const watch = { overlayId, slug, label, ports, timer: null, deadlineTimer: null };
2210
+ watchdogState().watches.set(overlayId, watch);
2211
+ watch.timer = globalThis.setTimeout(() => {
2212
+ watch.timer = null;
2213
+ const res = evaluate(store, watch);
2214
+ if (!res) return endWatch(watch);
2215
+ if (res.diag.ok) return endWatch(watch);
2216
+ if (res.diag.reason === "no-window-registered" && !watchdogState().renderAcks.has(overlayId)) {
2217
+ waitForMount(store, watch);
2218
+ return;
2219
+ }
2220
+ scream(store, watch, res);
2221
+ endWatch(watch);
2222
+ }, CHECK_DELAY_MS);
2223
+ }
2224
+ function waitForMount(store, watch) {
2225
+ const checkAfterAck = (delay, isRetry) => {
2226
+ watch.timer = globalThis.setTimeout(() => {
2227
+ watch.timer = null;
2228
+ const res = evaluate(store, watch);
2229
+ if (!res) return endWatch(watch);
2230
+ if (res.diag.ok) return endWatch(watch);
2231
+ if (res.diag.reason === "no-window-registered" && !isRetry) {
2232
+ checkAfterAck(ACK_SETTLE_RETRY_MS, true);
2233
+ return;
2234
+ }
2235
+ scream(store, watch, res);
2236
+ endWatch(watch);
2237
+ }, delay);
2238
+ };
2239
+ watchdogState().ackWaiters.set(watch.overlayId, () => {
2240
+ if (watch.deadlineTimer !== null) {
2241
+ globalThis.clearTimeout(watch.deadlineTimer);
2242
+ watch.deadlineTimer = null;
2243
+ }
2244
+ checkAfterAck(ACK_SETTLE_MS, false);
2245
+ });
2246
+ watch.deadlineTimer = globalThis.setTimeout(() => {
2247
+ watch.deadlineTimer = null;
2248
+ watchdogState().ackWaiters.delete(watch.overlayId);
2249
+ const res = evaluate(store, watch);
2250
+ if (!res) return endWatch(watch);
2251
+ if (res.diag.ok) return endWatch(watch);
2252
+ scream(store, watch, res);
2253
+ endWatch(watch);
2254
+ }, NO_MOUNT_DEADLINE_MS);
2255
+ }
2256
+ function scream(store, watch, res) {
2257
+ const { overlayId, label } = watch;
2258
+ const reason = res.diag.ok ? null : res.diag.reason;
2259
+ if (reason === null) return;
2260
+ watch.ports.errors.capture(
2261
+ new Error(
2262
+ `[summon] SILENT RENDER FAILURE \u2014 overlay "${overlayId}" was opened but no visible panel is on screen (reason: ${reason}). ${REASON_HINT[reason]}.`
2263
+ ),
2264
+ {
2265
+ overlayId,
2266
+ windowId: res.windowId,
2267
+ viewportWidth: res.viewportWidth,
2268
+ viewportHeight: res.viewportHeight,
2269
+ viewportDegenerate: res.viewportDegenerate,
2270
+ renderAcknowledgement: res.renderAcknowledgement,
2271
+ entry: res.entry,
2272
+ windowsHidden: res.windowsHidden
2273
+ }
2274
+ );
2275
+ const toastId = `window-watchdog-${overlayId}`;
2276
+ watch.ports.notify.error(`"${label}" didn't appear`, {
2277
+ id: toastId,
2278
+ description: "The panel was opened but isn't visible. Click to show it.",
2279
+ duration: TOAST_DURATION_MS,
2280
+ action: {
2281
+ label: "Show it",
2282
+ onClick: () => {
2283
+ const dims = safeViewportDims();
2284
+ store.dispatch(
2285
+ revealWindow({
2286
+ id: acknowledgedWindowId(overlayId, watch.slug),
2287
+ viewportWidth: dims.vw,
2288
+ viewportHeight: dims.vh
2289
+ })
2290
+ );
2291
+ }
2292
+ }
2293
+ });
2294
+ let elapsed = 0;
2295
+ const interval = globalThis.setInterval(() => {
2296
+ elapsed += RECOVERY_POLL_MS;
2297
+ const check = evaluate(store, watch);
2298
+ if (check === null || check.diag.ok) {
2299
+ globalThis.clearInterval(interval);
2300
+ watch.ports.notify.dismiss?.(toastId);
2301
+ if (check?.diag.ok) {
2302
+ console.info(
2303
+ `[summon] overlay "${overlayId}" became visible after the recovery scream \u2014 toast withdrawn. If this recurs, the pre-scream wait is ending too early for this panel.`
2304
+ );
2305
+ }
2306
+ return;
2307
+ }
2308
+ if (elapsed >= TOAST_DURATION_MS) globalThis.clearInterval(interval);
2309
+ }, RECOVERY_POLL_MS);
2310
+ }
2311
+ function watchdogTracksOpen(meta, instanceId) {
2312
+ if (!meta || !meta.window) return false;
2313
+ if (meta.instance === "multi") return false;
2314
+ return instanceId === DEFAULT_INSTANCE_ID2;
2315
+ }
2316
+ function createOverlayRenderWatchdogMiddleware(ports) {
2317
+ return (store) => (next) => (action) => {
2318
+ const result = next(action);
2319
+ if (typeof window === "undefined") return result;
2320
+ const typed = action;
2321
+ if (typed.type !== OPEN_TYPE && typed.type !== TOGGLE_TYPE) return result;
2322
+ const payload = typed.payload;
2323
+ const overlayId = isPlainRecord3(payload) && typeof payload.overlayId === "string" ? payload.overlayId : void 0;
2324
+ if (!overlayId) return result;
2325
+ const instanceIdRaw = isPlainRecord3(payload) && typeof payload.instanceId === "string" ? payload.instanceId : void 0;
2326
+ const meta = getOverlayMeta(overlayId);
2327
+ if (!meta || !watchdogTracksOpen(meta, instanceIdRaw ?? DEFAULT_INSTANCE_ID2)) {
2328
+ return result;
2329
+ }
2330
+ const state2 = store.getState();
2331
+ if (!selectIsOverlayOpen(state2, overlayId, DEFAULT_INSTANCE_ID2)) {
2332
+ return result;
2333
+ }
2334
+ const slug = defaultWindowIdFor(meta);
2335
+ const revealDims = safeViewportDims();
2336
+ store.dispatch(
2337
+ revealWindow({
2338
+ id: acknowledgedWindowId(overlayId, slug),
2339
+ viewportWidth: revealDims.vw,
2340
+ viewportHeight: revealDims.vh
2341
+ })
2342
+ );
2343
+ startWatch(store, ports, overlayId, slug, meta.label || overlayId);
2344
+ return result;
2345
+ };
2346
+ }
2347
+ function resetOverlayRenderWatchdog() {
2348
+ const s = watchdogState();
2349
+ for (const watch of [...s.watches.values()]) endWatch(watch);
2350
+ s.renderAcks.clear();
2351
+ s.ackWaiters.clear();
2352
+ }
2353
+
2354
+ // src/core/store.ts
2355
+ var summonReducers = {
2356
+ overlays: overlaysReducer,
2357
+ windowManager: windowManagerReducer,
2358
+ urlSync: linksReducer,
2359
+ overlayState: overlayStateReducer
2360
+ };
2361
+ function createSummonMiddleware(ports) {
2362
+ const resolved = resolvePorts(ports);
2363
+ return [closeLifecycleMiddleware, createOverlayRenderWatchdogMiddleware(resolved)];
2364
+ }
2365
+ function createSummonStore(options = {}) {
2366
+ const reducer = (0, import_toolkit5.combineReducers)({ ...options.extraReducers, ...summonReducers });
2367
+ const middleware = createSummonMiddleware(options.ports);
2368
+ return (0, import_toolkit5.configureStore)({
2369
+ reducer,
2370
+ ...options.preloadedState === void 0 ? {} : { preloadedState: options.preloadedState },
2371
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(middleware)
2372
+ });
2373
+ }
2374
+
2375
+ // src/core/geometry/dockWindowPayload.ts
2376
+ function buildDockWindowPayload(windowId) {
2377
+ return {
2378
+ id: windowId,
2379
+ viewportWidth: window.innerWidth,
2380
+ viewportHeight: window.innerHeight
2381
+ };
2382
+ }
2383
+ //# sourceMappingURL=index.cjs.map