@ai-matrx/summon 0.0.0 → 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 +379 -1
  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 +239 -1
package/dist/window.js ADDED
@@ -0,0 +1,4003 @@
1
+ "use client";
2
+
3
+ // src/window/WindowPanel.tsx
4
+ import React, { useCallback as useCallback4, useEffect as useEffect5, useId as useId2, useLayoutEffect, useRef as useRef4, useState as useState5 } from "react";
5
+ import { announceLayerFront, FloatingLayer as FloatingLayer2, useFloatingLayerZIndex as useFloatingLayerZIndex2, usePortalContainer, PortalContainerProvider as PortalContainerProvider2 } from "@ai-matrx/design-system";
6
+ import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@ai-matrx/design-system";
7
+ import { cn as cn9 } from "@ai-matrx/design-system/cn";
8
+ import { useIsMobile } from "@ai-matrx/kit/media-query";
9
+ import { createPortal as createPortal3 } from "react-dom";
10
+ import {
11
+ arrangeActiveWindows,
12
+ clampWindowRect as clampWindowRect2,
13
+ overlaysRawActions,
14
+ DEFAULT_INSTANCE_ID as DEFAULT_INSTANCE_ID2,
15
+ dockWindow as dockWindow3,
16
+ buildDockWindowPayload as buildDockWindowPayload3,
17
+ getOverlayMeta as getOverlayMeta3,
18
+ safeViewportDims as safeViewportDims2,
19
+ selectAllWindows,
20
+ selectOverlayData as selectOverlayData2,
21
+ selectPopoutCandidateId,
22
+ selectPopoutMode as selectPopoutMode3,
23
+ selectWindowPersistence,
24
+ selectWindowsHidden,
25
+ updateWindowPersistence,
26
+ updateWindowRect as updateWindowRect2,
27
+ ackOverlayRender,
28
+ clearOverlayRender
29
+ } from "@ai-matrx/summon";
30
+ import { useOptionalOverlayInstance, useSummonDispatch as useSummonDispatch4, useSummonPorts as useSummonPorts3, useSummonSelector as useSummonSelector5, useSummonStore as useSummonStore2 } from "@ai-matrx/summon/react";
31
+
32
+ // src/window/icons.tsx
33
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
34
+ function glyph(children) {
35
+ return function Glyph(props) {
36
+ return /* @__PURE__ */ jsx(
37
+ "svg",
38
+ {
39
+ xmlns: "http://www.w3.org/2000/svg",
40
+ width: 24,
41
+ height: 24,
42
+ viewBox: "0 0 24 24",
43
+ fill: "none",
44
+ stroke: "currentColor",
45
+ strokeWidth: 2,
46
+ strokeLinecap: "round",
47
+ strokeLinejoin: "round",
48
+ "aria-hidden": true,
49
+ ...props,
50
+ children
51
+ }
52
+ );
53
+ };
54
+ }
55
+ var XIcon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
56
+ /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }),
57
+ /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
58
+ ] }));
59
+ var MinusIcon = glyph(/* @__PURE__ */ jsx("path", { d: "M5 12h14" }));
60
+ var Maximize2Icon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
61
+ /* @__PURE__ */ jsx("path", { d: "M15 3h6v6" }),
62
+ /* @__PURE__ */ jsx("path", { d: "m21 3-7 7" }),
63
+ /* @__PURE__ */ jsx("path", { d: "m3 21 7-7" }),
64
+ /* @__PURE__ */ jsx("path", { d: "M9 21H3v-6" })
65
+ ] }));
66
+ var Minimize2Icon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
67
+ /* @__PURE__ */ jsx("path", { d: "m14 10 7-7" }),
68
+ /* @__PURE__ */ jsx("path", { d: "M20 10h-6V4" }),
69
+ /* @__PURE__ */ jsx("path", { d: "m3 21 7-7" }),
70
+ /* @__PURE__ */ jsx("path", { d: "M4 14h6v6" })
71
+ ] }));
72
+ var RectangleVerticalIcon = glyph(/* @__PURE__ */ jsx("rect", { width: "12", height: "20", x: "6", y: "2", rx: "2" }));
73
+ var PanelLeftCloseIcon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
74
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
75
+ /* @__PURE__ */ jsx("path", { d: "M9 3v18" }),
76
+ /* @__PURE__ */ jsx("path", { d: "m16 15-3-3 3-3" })
77
+ ] }));
78
+ var PanelLeftIcon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
79
+ /* @__PURE__ */ jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2" }),
80
+ /* @__PURE__ */ jsx("path", { d: "M9 3v18" })
81
+ ] }));
82
+ var AppWindowIcon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
83
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "4", width: "20", height: "16", rx: "2" }),
84
+ /* @__PURE__ */ jsx("path", { d: "M10 4v4" }),
85
+ /* @__PURE__ */ jsx("path", { d: "M2 8h20" }),
86
+ /* @__PURE__ */ jsx("path", { d: "M6 4v4" })
87
+ ] }));
88
+ var SaveIcon = glyph(
89
+ /* @__PURE__ */ jsxs(Fragment, { children: [
90
+ /* @__PURE__ */ jsx("path", { d: "M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z" }),
91
+ /* @__PURE__ */ jsx("path", { d: "M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7" }),
92
+ /* @__PURE__ */ jsx("path", { d: "M7 3v4a1 1 0 0 0 1 1h7" })
93
+ ] })
94
+ );
95
+ var TriangleAlertIcon = glyph(
96
+ /* @__PURE__ */ jsxs(Fragment, { children: [
97
+ /* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
98
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4" }),
99
+ /* @__PURE__ */ jsx("path", { d: "M12 17h.01" })
100
+ ] })
101
+ );
102
+ var ArrowLeftToLineIcon = glyph(/* @__PURE__ */ jsxs(Fragment, { children: [
103
+ /* @__PURE__ */ jsx("path", { d: "M3 19V5" }),
104
+ /* @__PURE__ */ jsx("path", { d: "m13 6-6 6 6 6" }),
105
+ /* @__PURE__ */ jsx("path", { d: "M7 12h14" })
106
+ ] }));
107
+
108
+ // src/window/LayoutIcon.tsx
109
+ import { cn } from "@ai-matrx/design-system/cn";
110
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
111
+ function LayoutIcon({
112
+ type,
113
+ className
114
+ }) {
115
+ const baseBox = "w-[18px] h-[18px] border-[1.5px] border-current rounded-sm shrink-0 flex items-center justify-center p-[1px] gap-[1px] overflow-hidden";
116
+ const filled = "bg-current rounded-[1px]";
117
+ const flexRow = "flex flex-row w-full h-full gap-[1px]";
118
+ const flexCol = "flex flex-col w-full h-full gap-[1px]";
119
+ let content = null;
120
+ switch (type) {
121
+ case "left-half":
122
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
123
+ /* @__PURE__ */ jsx2("div", { className: cn("w-1/2 h-full", filled) }),
124
+ /* @__PURE__ */ jsx2("div", { className: "w-1/2 h-full" })
125
+ ] });
126
+ break;
127
+ case "right-half":
128
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
129
+ /* @__PURE__ */ jsx2("div", { className: "w-1/2 h-full" }),
130
+ /* @__PURE__ */ jsx2("div", { className: cn("w-1/2 h-full", filled) })
131
+ ] });
132
+ break;
133
+ case "top-half":
134
+ content = /* @__PURE__ */ jsxs2("div", { className: flexCol, children: [
135
+ /* @__PURE__ */ jsx2("div", { className: cn("w-full h-1/2", filled) }),
136
+ /* @__PURE__ */ jsx2("div", { className: "w-full h-1/2" })
137
+ ] });
138
+ break;
139
+ case "bottom-half":
140
+ content = /* @__PURE__ */ jsxs2("div", { className: flexCol, children: [
141
+ /* @__PURE__ */ jsx2("div", { className: "w-full h-1/2" }),
142
+ /* @__PURE__ */ jsx2("div", { className: cn("w-full h-1/2", filled) })
143
+ ] });
144
+ break;
145
+ case "centre":
146
+ content = /* @__PURE__ */ jsx2("div", { className: cn("w-1/2 h-1/2", filled) });
147
+ break;
148
+ case "full":
149
+ content = /* @__PURE__ */ jsx2("div", { className: cn("w-full h-full", filled) });
150
+ break;
151
+ // Grid variants
152
+ case "grid4":
153
+ content = /* @__PURE__ */ jsx2("div", { className: "grid grid-cols-2 grid-rows-2 gap-[1px] w-full h-full", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx2("div", { className: filled }, i)) });
154
+ break;
155
+ case "grid6":
156
+ content = /* @__PURE__ */ jsx2("div", { className: "grid grid-cols-3 grid-rows-2 gap-[1px] w-full h-full", children: [1, 2, 3, 4, 5, 6].map((i) => /* @__PURE__ */ jsx2("div", { className: filled }, i)) });
157
+ break;
158
+ case "grid8":
159
+ content = /* @__PURE__ */ jsx2("div", { className: "grid grid-cols-4 grid-rows-2 gap-[1px] w-full h-full", children: [1, 2, 3, 4, 5, 6, 7, 8].map((i) => /* @__PURE__ */ jsx2("div", { className: filled }, i)) });
160
+ break;
161
+ case "grid9":
162
+ content = /* @__PURE__ */ jsx2("div", { className: "grid grid-cols-3 grid-rows-3 gap-[1px] w-full h-full", children: [1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => /* @__PURE__ */ jsx2("div", { className: filled }, i)) });
163
+ break;
164
+ case "grid12":
165
+ content = /* @__PURE__ */ jsx2("div", { className: "grid grid-cols-4 grid-rows-3 gap-[1px] w-full h-full", children: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((i) => /* @__PURE__ */ jsx2("div", { className: filled }, i)) });
166
+ break;
167
+ // Stack variants
168
+ case "stackRight2":
169
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
170
+ /* @__PURE__ */ jsx2("div", { className: "w-1/2 h-full" }),
171
+ /* @__PURE__ */ jsxs2("div", { className: "w-1/2 h-full flex flex-col gap-[1px]", children: [
172
+ /* @__PURE__ */ jsx2("div", { className: cn("w-full h-1/2", filled) }),
173
+ /* @__PURE__ */ jsx2("div", { className: cn("w-full h-1/2", filled) })
174
+ ] })
175
+ ] });
176
+ break;
177
+ case "stackRight3":
178
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
179
+ /* @__PURE__ */ jsx2("div", { className: "w-2/3 h-full" }),
180
+ /* @__PURE__ */ jsx2("div", { className: "w-1/3 h-full flex flex-col gap-[1px]", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx2("div", { className: cn("w-full flex-1", filled) }, i)) })
181
+ ] });
182
+ break;
183
+ case "stackRight4":
184
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
185
+ /* @__PURE__ */ jsx2("div", { className: "w-3/4 h-full" }),
186
+ /* @__PURE__ */ jsx2("div", { className: "w-1/4 h-full flex flex-col gap-[1px]", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx2("div", { className: cn("w-full flex-1", filled) }, i)) })
187
+ ] });
188
+ break;
189
+ case "stackRight5":
190
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
191
+ /* @__PURE__ */ jsx2("div", { className: "w-4/5 h-full" }),
192
+ /* @__PURE__ */ jsx2("div", { className: "w-1/5 h-full flex flex-col gap-[1px]", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx2("div", { className: cn("w-full flex-1", filled) }, i)) })
193
+ ] });
194
+ break;
195
+ case "stackLeft2":
196
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
197
+ /* @__PURE__ */ jsxs2("div", { className: "w-1/2 h-full flex flex-col gap-[1px]", children: [
198
+ /* @__PURE__ */ jsx2("div", { className: cn("w-full h-1/2", filled) }),
199
+ /* @__PURE__ */ jsx2("div", { className: cn("w-full h-1/2", filled) })
200
+ ] }),
201
+ /* @__PURE__ */ jsx2("div", { className: "w-1/2 h-full" })
202
+ ] });
203
+ break;
204
+ case "stackLeft3":
205
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
206
+ /* @__PURE__ */ jsx2("div", { className: "w-1/3 h-full flex flex-col gap-[1px]", children: [1, 2, 3].map((i) => /* @__PURE__ */ jsx2("div", { className: cn("w-full flex-1", filled) }, i)) }),
207
+ /* @__PURE__ */ jsx2("div", { className: "w-2/3 h-full" })
208
+ ] });
209
+ break;
210
+ case "stackLeft4":
211
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
212
+ /* @__PURE__ */ jsx2("div", { className: "w-1/4 h-full flex flex-col gap-[1px]", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ jsx2("div", { className: cn("w-full flex-1", filled) }, i)) }),
213
+ /* @__PURE__ */ jsx2("div", { className: "w-3/4 h-full" })
214
+ ] });
215
+ break;
216
+ case "stackLeft5":
217
+ content = /* @__PURE__ */ jsxs2("div", { className: flexRow, children: [
218
+ /* @__PURE__ */ jsx2("div", { className: "w-1/5 h-full flex flex-col gap-[1px]", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx2("div", { className: cn("w-full flex-1", filled) }, i)) }),
219
+ /* @__PURE__ */ jsx2("div", { className: "w-4/5 h-full" })
220
+ ] });
221
+ break;
222
+ }
223
+ return /* @__PURE__ */ jsx2("div", { className: cn(baseBox, className), children: content });
224
+ }
225
+ function LayoutIconButton({
226
+ onClick,
227
+ type,
228
+ className
229
+ }) {
230
+ return /* @__PURE__ */ jsx2(
231
+ "button",
232
+ {
233
+ type: "button",
234
+ className: cn(
235
+ "flex items-center justify-center flex-1 h-8 rounded-md transition-colors",
236
+ "hover:bg-accent/80 text-foreground/50 hover:text-foreground",
237
+ className
238
+ ),
239
+ onClick,
240
+ onPointerDown: (e) => e.stopPropagation(),
241
+ title: type,
242
+ children: /* @__PURE__ */ jsx2(LayoutIcon, { type })
243
+ }
244
+ );
245
+ }
246
+
247
+ // src/window/useWindowPanel.ts
248
+ import { useEffect, useRef, useCallback, useId, useState } from "react";
249
+ import { useSummonDispatch, useSummonSelector, useSummonStore } from "@ai-matrx/summon/react";
250
+ import {
251
+ registerWindow,
252
+ unregisterWindow,
253
+ focusWindow,
254
+ restoreWindow,
255
+ maximizeWindow,
256
+ minimizeWindow,
257
+ updateWindowRect,
258
+ updateWindowTitle,
259
+ setPopoutCandidate,
260
+ clampWindowRect,
261
+ selectWindow,
262
+ confirmWindowRestored,
263
+ windowSessionKey
264
+ } from "@ai-matrx/summon";
265
+
266
+ // src/window/popout/popoutDragDetector.ts
267
+ var DEFAULT_DRAG_OUT_CONFIG = {
268
+ outsideThreshold: 80,
269
+ dwellMs: 250
270
+ };
271
+ var INITIAL_DRAG_OUT_STATE = {
272
+ isCandidate: false,
273
+ outsideSinceMs: null
274
+ };
275
+ function evaluateDragOut(e, prev, cfg, vpW, vpH, now) {
276
+ const { clientX, clientY } = e;
277
+ const { outsideThreshold, dwellMs } = cfg;
278
+ const pastLeft = -clientX;
279
+ const pastRight = clientX - vpW;
280
+ const pastTop = -clientY;
281
+ const pastBottom = clientY - vpH;
282
+ const maxPast = Math.max(pastLeft, pastRight, pastTop, pastBottom);
283
+ const isOutsideEnough = maxPast >= outsideThreshold;
284
+ if (!isOutsideEnough) {
285
+ if (prev.isCandidate || prev.outsideSinceMs !== null) {
286
+ return {
287
+ state: { isCandidate: false, outsideSinceMs: null },
288
+ shouldTrigger: false
289
+ };
290
+ }
291
+ return { state: prev, shouldTrigger: false };
292
+ }
293
+ const outsideSince = prev.outsideSinceMs ?? now;
294
+ const dwellMet = now - outsideSince >= dwellMs;
295
+ const wasCandidate = prev.isCandidate;
296
+ const nowCandidate = dwellMet;
297
+ return {
298
+ state: { isCandidate: nowCandidate, outsideSinceMs: outsideSince },
299
+ shouldTrigger: !wasCandidate && nowCandidate
300
+ };
301
+ }
302
+
303
+ // src/window/useWindowPanel.ts
304
+ import { safeViewportDims } from "@ai-matrx/summon";
305
+
306
+ // src/window/utils/autoOpenPlacement.ts
307
+ import { globalSlot } from "@ai-matrx/kit/global-slot";
308
+ var GAP = 12;
309
+ var SIBLING_GAP = 8;
310
+ var AUTO_OPEN_MIN_WIDTH = 280;
311
+ function marks() {
312
+ return globalSlot("ai-matrx.summon.window.autoOpenedMarks", () => /* @__PURE__ */ new Set());
313
+ }
314
+ var markKey = (overlayId, instanceId) => `${overlayId}::${instanceId}`;
315
+ function markAutoOpened(overlayId, instanceId) {
316
+ marks().add(markKey(overlayId, instanceId));
317
+ }
318
+ function isAutoOpened(overlayId, instanceId) {
319
+ return marks().has(markKey(overlayId, instanceId));
320
+ }
321
+ function clearAutoOpened(overlayId, instanceId) {
322
+ marks().delete(markKey(overlayId, instanceId));
323
+ }
324
+ function canvasWidth() {
325
+ if (typeof document === "undefined") return 0;
326
+ const raw = getComputedStyle(document.documentElement).getPropertyValue("--shell-canvas-w");
327
+ const px = Number.parseFloat(raw);
328
+ return Number.isFinite(px) && px > 0 ? px : 0;
329
+ }
330
+ var CONTROL_SELECTOR = 'button, a[href], input, textarea, select, [role="button"], [role="tab"], [data-clickable]';
331
+ function measureWorkingColumn() {
332
+ if (typeof document === "undefined") return null;
333
+ const root = document.querySelector("[data-matrx-page-scroll]") ?? document.querySelector("main") ?? document.querySelector(".shell-main");
334
+ if (!root) return null;
335
+ const rootRect = root.getBoundingClientRect();
336
+ let left = Infinity;
337
+ let right = -Infinity;
338
+ const nodes = root.querySelectorAll(CONTROL_SELECTOR);
339
+ const limit = Math.min(nodes.length, 600);
340
+ for (let i = 0; i < limit; i += 1) {
341
+ const el = nodes[i];
342
+ if (!el) continue;
343
+ if (el.closest("[data-window-panel], [data-matrx-window]")) continue;
344
+ const r = el.getBoundingClientRect();
345
+ if (r.width < 8 || r.height < 8) continue;
346
+ if (r.bottom < rootRect.top || r.top > rootRect.bottom) continue;
347
+ if (r.right < 0 || r.left > window.innerWidth) continue;
348
+ left = Math.min(left, r.left);
349
+ right = Math.max(right, r.right);
350
+ }
351
+ return Number.isFinite(left) ? { left, right } : null;
352
+ }
353
+ var AUTO_OPEN_NO_ROOM_EVENT = "matrx:auto-open-no-room";
354
+ function autoOpenHasRoom() {
355
+ if (typeof window === "undefined") return false;
356
+ const vw = window.innerWidth;
357
+ if (vw < 768) return false;
358
+ const column = measureWorkingColumn();
359
+ if (!column) return true;
360
+ const leftRoot = document.querySelector("main")?.getBoundingClientRect().left ?? 0;
361
+ const free = Math.max(vw - canvasWidth() - column.right - GAP * 2, column.left - leftRoot - GAP * 2);
362
+ return free >= AUTO_OPEN_MIN_WIDTH;
363
+ }
364
+ function resolveAutoOpenPlacement(args) {
365
+ const { vh } = args.viewport;
366
+ const vw = args.viewport.vw - canvasWidth();
367
+ const column = measureWorkingColumn();
368
+ const rightFree = column ? vw - column.right - GAP * 2 : vw;
369
+ const leftRoot = typeof document !== "undefined" ? document.querySelector("main")?.getBoundingClientRect().left ?? 0 : 0;
370
+ const leftFree = column ? column.left - leftRoot - GAP * 2 : 0;
371
+ const side = rightFree >= leftFree ? "right" : "left";
372
+ const free = Math.max(rightFree, leftFree);
373
+ const need = Math.min(args.minWidth, AUTO_OPEN_MIN_WIDTH);
374
+ const parked = {
375
+ rect: {
376
+ x: Math.max(GAP, vw - args.width - GAP),
377
+ y: args.top,
378
+ width: args.width,
379
+ height: args.height
380
+ },
381
+ minimized: true
382
+ };
383
+ if (free < need) return parked;
384
+ const width = Math.max(need, Math.min(args.width, Math.floor(free)));
385
+ const x = side === "right" ? vw - width - GAP : Math.max(GAP, Math.round(leftRoot + GAP));
386
+ const inGutter = args.siblings.filter(
387
+ (s) => s.x + s.width > x && s.x < x + width
388
+ );
389
+ const y = inGutter.length ? Math.max(...inGutter.map((s) => s.y + s.height)) + SIBLING_GAP : args.top;
390
+ const room = vh - y - GAP;
391
+ if (room < args.minHeight) return parked;
392
+ const share = inGutter.length ? room : Math.floor((vh - args.top - GAP) / 2);
393
+ const height = Math.min(room, Math.max(args.minHeight, Math.min(args.height, share)));
394
+ return { rect: { x, y, width, height }, minimized: false };
395
+ }
396
+
397
+ // src/window/useWindowPanel.ts
398
+ import { holdDragCursor } from "@ai-matrx/design-system";
399
+ var MIN_WIDTH = 180;
400
+ var MIN_HEIGHT = 80;
401
+ var DEFAULT_WIDTH = 320;
402
+ var DEFAULT_HEIGHT = 400;
403
+ var RESIZE_CURSOR = {
404
+ e: "ew-resize",
405
+ w: "ew-resize",
406
+ n: "ns-resize",
407
+ s: "ns-resize",
408
+ se: "nwse-resize",
409
+ nw: "nwse-resize",
410
+ ne: "nesw-resize",
411
+ sw: "nesw-resize"
412
+ };
413
+ function resolveSize(value, fallback) {
414
+ if (value === void 0) return fallback;
415
+ if (typeof value === "number") return value;
416
+ const { vw, vh } = safeViewportDims();
417
+ if (value.endsWith("vh")) return Math.round(parseFloat(value) / 100 * vh);
418
+ if (value.endsWith("vw")) return Math.round(parseFloat(value) / 100 * vw);
419
+ return parseFloat(value) || fallback;
420
+ }
421
+ var CASCADE_COUNT = /* @__PURE__ */ Symbol.for("matrx.windowPanels.defaultCascade");
422
+ var CASCADE_STEP = 28;
423
+ var CASCADE_WRAP = 6;
424
+ function resetDefaultWindowCascade() {
425
+ globalThis[CASCADE_COUNT] = 0;
426
+ }
427
+ function nextCascadeStep() {
428
+ const host = globalThis;
429
+ const seen = host[CASCADE_COUNT] ?? 0;
430
+ host[CASCADE_COUNT] = seen + 1;
431
+ return seen % CASCADE_WRAP * CASCADE_STEP;
432
+ }
433
+ var NARROW_VIEWPORT = 768;
434
+ var autoDocked = /* @__PURE__ */ new Set();
435
+ var autoOrder = /* @__PURE__ */ new Map();
436
+ var autoOrderNext = 0;
437
+ var earlierDocked = (id) => (other) => other.id !== id && other.state === "windowed" && autoDocked.has(other.id) && (autoOrder.get(other.id) ?? 0) < (autoOrder.get(id) ?? 0);
438
+ var AUTO_REPLACE_TICKS = 40;
439
+ function shellHeaderClearance() {
440
+ const GAP2 = 12;
441
+ if (typeof document === "undefined") return 44 + GAP2;
442
+ const root = getComputedStyle(document.documentElement);
443
+ const raw = root.getPropertyValue("--shell-header-h").trim();
444
+ const n = parseFloat(raw);
445
+ const h = raw.endsWith("rem") ? n * (parseFloat(root.fontSize) || 16) : n;
446
+ return (Number.isFinite(h) && h > 0 ? h : 44) + GAP2;
447
+ }
448
+ function resolvePosition(pos, w, h) {
449
+ const { vw, vh } = safeViewportDims();
450
+ const pad = 40;
451
+ const top = shellHeaderClearance();
452
+ switch (pos) {
453
+ case "top-left":
454
+ return { x: pad, y: top };
455
+ case "top-right":
456
+ return { x: Math.max(0, vw - w - pad), y: top };
457
+ case "bottom-left":
458
+ return { x: pad, y: Math.max(0, vh - h - pad) };
459
+ case "bottom-right":
460
+ return { x: Math.max(0, vw - w - pad), y: Math.max(0, vh - h - pad) };
461
+ case "center":
462
+ return {
463
+ x: Math.max(0, (vw - w) / 2),
464
+ y: Math.max(top, (vh - h) / 4)
465
+ };
466
+ default: {
467
+ const cascade = nextCascadeStep();
468
+ if (vw < NARROW_VIEWPORT) {
469
+ return {
470
+ x: Math.max(0, Math.min(vw - w, (vw - w) / 2)),
471
+ y: Math.max(0, Math.min(vh - h, top + cascade))
472
+ };
473
+ }
474
+ return {
475
+ x: Math.max(0, vw - w - pad - cascade),
476
+ y: Math.max(0, Math.min(vh - h, top + cascade))
477
+ };
478
+ }
479
+ }
480
+ }
481
+ function useWindowPanel(opts = {}) {
482
+ const reactId = useId();
483
+ const id = opts.id ?? reactId;
484
+ const dispatch = useSummonDispatch();
485
+ const store = useSummonStore();
486
+ const entry = useSummonSelector(selectWindow(id));
487
+ const minWidth = opts.minWidth ?? MIN_WIDTH;
488
+ const minHeight = opts.minHeight ?? MIN_HEIGHT;
489
+ const maxWidth = opts.maxWidth ?? Infinity;
490
+ const maxHeight = opts.maxHeight ?? Infinity;
491
+ const [isInteracting, setIsInteracting] = useState(false);
492
+ useEffect(() => {
493
+ const w = opts.initialRect?.width ?? resolveSize(opts.width, DEFAULT_WIDTH);
494
+ const h = opts.initialRect?.height ?? resolveSize(opts.height, DEFAULT_HEIGHT);
495
+ const { vw, vh } = safeViewportDims();
496
+ let autoPlaced = null;
497
+ if (opts.autoOpened && vw >= NARROW_VIEWPORT) {
498
+ if (!autoOrder.has(id)) autoOrder.set(id, autoOrderNext++);
499
+ const siblings = Object.values(store.getState().windowManager.windows).filter(earlierDocked(id)).map((win) => win.windowed);
500
+ autoPlaced = resolveAutoOpenPlacement({
501
+ width: w,
502
+ height: h,
503
+ minWidth,
504
+ minHeight,
505
+ top: shellHeaderClearance(),
506
+ viewport: { vw, vh },
507
+ siblings
508
+ });
509
+ if (!autoPlaced.minimized) autoDocked.add(id);
510
+ else if (opts.onAutoNoRoom) window.setTimeout(() => opts.onAutoNoRoom?.(), 0);
511
+ }
512
+ const pos = autoPlaced ? { x: autoPlaced.rect.x, y: autoPlaced.rect.y } : opts.initialRect?.x !== void 0 || opts.initialRect?.y !== void 0 ? {
513
+ x: opts.initialRect?.x ?? Math.max(0, (vw - w) / 2),
514
+ y: opts.initialRect?.y ?? Math.max(0, (vh - h) / 4)
515
+ } : resolvePosition(opts.position, w, h);
516
+ const initial = autoPlaced ? { ...autoPlaced.rect } : { ...pos, width: w, height: h };
517
+ dispatch(
518
+ registerWindow({
519
+ id,
520
+ ...opts.title === void 0 ? {} : { title: opts.title },
521
+ initial,
522
+ ...autoPlaced?.minimized ? { initialState: "minimized" } : opts.initialState === void 0 ? {} : { initialState: opts.initialState },
523
+ ...opts.persistence === void 0 ? {} : { persistence: opts.persistence },
524
+ // Sanitized dims: a degenerate 0×0 measurement must never become the
525
+ // clamp target, or every rect collapses to garbage (see safeViewportDims).
526
+ viewport: { width: vw, height: vh }
527
+ })
528
+ );
529
+ const pendingKey = opts.persistence ? windowSessionKey(
530
+ opts.persistence.overlayId,
531
+ opts.persistence.instanceId
532
+ ) : null;
533
+ const confirmationTimer = pendingKey ? window.setTimeout(() => {
534
+ dispatch(confirmWindowRestored(pendingKey));
535
+ }, 0) : null;
536
+ return () => {
537
+ if (confirmationTimer !== null) window.clearTimeout(confirmationTimer);
538
+ autoDocked.delete(id);
539
+ autoOrder.delete(id);
540
+ dispatch(unregisterWindow(id));
541
+ };
542
+ }, [id]);
543
+ const autoTouchedRef = useRef(false);
544
+ useEffect(() => {
545
+ if (isInteracting) autoTouchedRef.current = true;
546
+ }, [isInteracting]);
547
+ useEffect(() => {
548
+ if (!opts.autoOpened) return void 0;
549
+ let ticks = 0;
550
+ let lastMinimized = null;
551
+ const timer = window.setInterval(() => {
552
+ ticks += 1;
553
+ if (ticks > AUTO_REPLACE_TICKS) {
554
+ window.clearInterval(timer);
555
+ return;
556
+ }
557
+ const win = store.getState().windowManager.windows[id];
558
+ if (!win || autoTouchedRef.current) return;
559
+ const { vw, vh } = safeViewportDims();
560
+ if (vw < NARROW_VIEWPORT) return;
561
+ const isMin = win.state === "minimized";
562
+ if (lastMinimized !== null && isMin !== lastMinimized) {
563
+ autoTouchedRef.current = true;
564
+ return;
565
+ }
566
+ if (win.state !== "windowed" && !isMin) return;
567
+ const siblings = Object.values(store.getState().windowManager.windows).filter(earlierDocked(id)).map((o) => o.windowed);
568
+ const next = resolveAutoOpenPlacement({
569
+ width: win.windowed.width,
570
+ height: win.windowed.height,
571
+ minWidth,
572
+ minHeight,
573
+ top: shellHeaderClearance(),
574
+ viewport: { vw, vh },
575
+ siblings
576
+ });
577
+ if (next.minimized) {
578
+ autoDocked.delete(id);
579
+ if (opts.onAutoNoRoom) {
580
+ window.clearInterval(timer);
581
+ opts.onAutoNoRoom();
582
+ return;
583
+ }
584
+ if (!isMin) {
585
+ dispatch(minimizeWindow({ id, viewportWidth: vw, viewportHeight: vh }));
586
+ }
587
+ lastMinimized = true;
588
+ return;
589
+ }
590
+ autoDocked.add(id);
591
+ if (isMin) dispatch(restoreWindow(id));
592
+ const cur = store.getState().windowManager.windows[id]?.windowed;
593
+ if (!cur || Math.abs(cur.x - next.rect.x) > 4 || Math.abs(cur.y - next.rect.y) > 4 || Math.abs(cur.width - next.rect.width) > 4 || Math.abs(cur.height - next.rect.height) > 4) {
594
+ dispatch(updateWindowRect({ id, rect: next.rect }));
595
+ }
596
+ lastMinimized = false;
597
+ }, 1e3);
598
+ return () => window.clearInterval(timer);
599
+ }, [opts.autoOpened, id, minWidth, minHeight, store, dispatch]);
600
+ useEffect(() => {
601
+ if (opts.title !== void 0) {
602
+ dispatch(updateWindowTitle({ id, title: opts.title }));
603
+ }
604
+ }, [id, opts.title, dispatch]);
605
+ const dragStart = useRef(null);
606
+ const onTriggerPopout = opts.onTriggerPopout;
607
+ const onTriggerPopoutRef = useRef(onTriggerPopout);
608
+ useEffect(() => {
609
+ onTriggerPopoutRef.current = onTriggerPopout;
610
+ }, [onTriggerPopout]);
611
+ const onDragStart = useCallback(
612
+ (e) => {
613
+ e.preventDefault();
614
+ dispatch(focusWindow(id));
615
+ if (!entry || entry.state === "maximized") return;
616
+ dragStart.current = {
617
+ mx: e.clientX,
618
+ my: e.clientY,
619
+ wx: entry.windowed.x,
620
+ wy: entry.windowed.y
621
+ };
622
+ setIsInteracting(true);
623
+ const releaseCursor = holdDragCursor();
624
+ let dragOutState = INITIAL_DRAG_OUT_STATE;
625
+ let lastClientX = e.clientX;
626
+ let lastClientY = e.clientY;
627
+ const popoutEnabled = onTriggerPopoutRef.current !== void 0;
628
+ const onMove = (ev) => {
629
+ if (!dragStart.current) return;
630
+ const nx = dragStart.current.wx + (ev.clientX - dragStart.current.mx);
631
+ const ny = dragStart.current.wy + (ev.clientY - dragStart.current.my);
632
+ dispatch(updateWindowRect({ id, rect: { x: nx, y: ny } }));
633
+ lastClientX = ev.clientX;
634
+ lastClientY = ev.clientY;
635
+ if (!popoutEnabled) return;
636
+ const result = evaluateDragOut(
637
+ { clientX: ev.clientX, clientY: ev.clientY },
638
+ dragOutState,
639
+ DEFAULT_DRAG_OUT_CONFIG,
640
+ window.innerWidth,
641
+ window.innerHeight,
642
+ performance.now()
643
+ );
644
+ if (result.state.isCandidate !== dragOutState.isCandidate) {
645
+ dispatch(
646
+ setPopoutCandidate({ id: result.state.isCandidate ? id : null })
647
+ );
648
+ }
649
+ dragOutState = result.state;
650
+ };
651
+ const onUp = () => {
652
+ const wasCandidate = dragOutState.isCandidate;
653
+ const dragOrigin = dragStart.current;
654
+ dragStart.current = null;
655
+ setIsInteracting(false);
656
+ releaseCursor();
657
+ document.removeEventListener("pointermove", onMove);
658
+ document.removeEventListener("pointerup", onUp);
659
+ document.removeEventListener("pointercancel", onUp);
660
+ if (popoutEnabled && dragOutState.isCandidate) {
661
+ dispatch(setPopoutCandidate({ id: null }));
662
+ }
663
+ if (wasCandidate && onTriggerPopoutRef.current && entry) {
664
+ const lastRect = {
665
+ x: dragOrigin?.wx ?? entry.windowed.x,
666
+ y: dragOrigin?.wy ?? entry.windowed.y,
667
+ width: entry.windowed.width,
668
+ height: entry.windowed.height
669
+ };
670
+ onTriggerPopoutRef.current(lastRect);
671
+ } else {
672
+ const { vw, vh, degenerate } = safeViewportDims();
673
+ if (!degenerate) {
674
+ dispatch(
675
+ clampWindowRect({ id, viewportWidth: vw, viewportHeight: vh })
676
+ );
677
+ }
678
+ }
679
+ void lastClientX;
680
+ void lastClientY;
681
+ };
682
+ document.addEventListener("pointermove", onMove);
683
+ document.addEventListener("pointerup", onUp);
684
+ document.addEventListener("pointercancel", onUp);
685
+ },
686
+ [dispatch, id, entry]
687
+ );
688
+ const onResizeStart = useCallback(
689
+ (edge) => (e) => {
690
+ e.preventDefault();
691
+ e.stopPropagation();
692
+ dispatch(focusWindow(id));
693
+ if (!entry || entry.state !== "windowed") return;
694
+ const startMx = e.clientX;
695
+ const startMy = e.clientY;
696
+ const { x, y, width, height } = entry.windowed;
697
+ setIsInteracting(true);
698
+ const releaseCursor = holdDragCursor(RESIZE_CURSOR[edge]);
699
+ const onMove = (ev) => {
700
+ const dx = ev.clientX - startMx;
701
+ const dy = ev.clientY - startMy;
702
+ let nx = x, ny = y, nw = width, nh = height;
703
+ if (edge.includes("e"))
704
+ nw = Math.min(maxWidth, Math.max(minWidth, width + dx));
705
+ if (edge.includes("s"))
706
+ nh = Math.min(maxHeight, Math.max(minHeight, height + dy));
707
+ if (edge.includes("w")) {
708
+ nw = Math.min(maxWidth, Math.max(minWidth, width - dx));
709
+ nx = x + (width - nw);
710
+ }
711
+ if (edge.includes("n")) {
712
+ nh = Math.min(maxHeight, Math.max(minHeight, height - dy));
713
+ ny = y + (height - nh);
714
+ }
715
+ dispatch(
716
+ updateWindowRect({
717
+ id,
718
+ rect: { x: nx, y: ny, width: nw, height: nh }
719
+ })
720
+ );
721
+ };
722
+ const onUp = () => {
723
+ setIsInteracting(false);
724
+ releaseCursor();
725
+ document.removeEventListener("pointermove", onMove);
726
+ document.removeEventListener("pointerup", onUp);
727
+ document.removeEventListener("pointercancel", onUp);
728
+ };
729
+ document.addEventListener("pointermove", onMove);
730
+ document.addEventListener("pointerup", onUp);
731
+ document.addEventListener("pointercancel", onUp);
732
+ },
733
+ [dispatch, id, entry, minWidth, minHeight, maxWidth, maxHeight]
734
+ );
735
+ const onFocus = useCallback(() => dispatch(focusWindow(id)), [dispatch, id]);
736
+ const onRestore = useCallback(
737
+ () => dispatch(restoreWindow(id)),
738
+ [dispatch, id]
739
+ );
740
+ const onMaximize = useCallback(
741
+ () => dispatch(maximizeWindow(id)),
742
+ [dispatch, id]
743
+ );
744
+ const onMinimize = useCallback(() => {
745
+ const { vw, vh } = safeViewportDims();
746
+ dispatch(
747
+ minimizeWindow({ id, viewportWidth: vw, viewportHeight: vh })
748
+ );
749
+ }, [dispatch, id]);
750
+ const onToggleMaximize = useCallback(() => {
751
+ if (entry?.state === "maximized") dispatch(restoreWindow(id));
752
+ else dispatch(maximizeWindow(id));
753
+ }, [dispatch, id, entry]);
754
+ const rect = entry?.windowed ?? {
755
+ x: 0,
756
+ y: 0,
757
+ width: opts.initialRect?.width ?? resolveSize(opts.width, DEFAULT_WIDTH),
758
+ height: opts.initialRect?.height ?? resolveSize(opts.height, DEFAULT_HEIGHT)
759
+ };
760
+ return {
761
+ id,
762
+ windowState: entry?.state ?? "windowed",
763
+ rect,
764
+ zIndex: entry?.zIndex ?? 1e3,
765
+ onDragStart,
766
+ onResizeStart,
767
+ onFocus,
768
+ onRestore,
769
+ onMaximize,
770
+ onMinimize,
771
+ onToggleMaximize,
772
+ isInteracting
773
+ };
774
+ }
775
+
776
+ // src/window/utils/useIsClient.ts
777
+ import { useSyncExternalStore } from "react";
778
+ var subscribe = () => () => {
779
+ };
780
+ function useIsClient() {
781
+ return useSyncExternalStore(subscribe, () => true, () => false);
782
+ }
783
+
784
+ // src/window/utils/useDebugFlag.ts
785
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
786
+ var noSubscribe = () => () => {
787
+ };
788
+ function useDebugFlag(debug) {
789
+ return useSyncExternalStore2(
790
+ debug.subscribe ? (listener) => debug.subscribe?.(listener) ?? (() => {
791
+ }) : noSubscribe,
792
+ () => debug.enabled(),
793
+ () => false
794
+ );
795
+ }
796
+
797
+ // src/window/WindowPanel.tsx
798
+ import { useOverlayLink } from "@ai-matrx/summon/url";
799
+
800
+ // src/window/utils/urlSyncIdentity.ts
801
+ function resolveWindowUrlSyncKey(registryKey, explicitKey) {
802
+ return registryKey ?? explicitKey;
803
+ }
804
+
805
+ // src/window/utils/sanitizeWindowData.ts
806
+ var DROPPED = /* @__PURE__ */ Symbol("dropped-window-value");
807
+ function isRecord(value) {
808
+ return typeof value === "object" && value !== null && !Array.isArray(value);
809
+ }
810
+ function sanitizeWindowData(value, allowedKeys) {
811
+ if (!isRecord(value) || allowedKeys.length === 0) return {};
812
+ const ancestors = /* @__PURE__ */ new WeakSet();
813
+ const copy = (item) => {
814
+ if (item === void 0 || typeof item === "function" || typeof item === "symbol") return DROPPED;
815
+ if (typeof item === "bigint") return item.toString();
816
+ if (typeof item === "number" && !Number.isFinite(item)) return null;
817
+ if (item === null || typeof item !== "object") return item;
818
+ if (item instanceof Date) return item.toJSON();
819
+ if (ancestors.has(item)) return DROPPED;
820
+ ancestors.add(item);
821
+ let result;
822
+ if (Array.isArray(item)) {
823
+ result = item.map((child) => {
824
+ const copied = copy(child);
825
+ return copied === DROPPED ? null : copied;
826
+ });
827
+ } else if (Object.getPrototypeOf(item) === Object.prototype || Object.getPrototypeOf(item) === null) {
828
+ result = Object.fromEntries(
829
+ Object.entries(item).flatMap(([key, child]) => {
830
+ const copied = copy(child);
831
+ return copied === DROPPED ? [] : [[key, copied]];
832
+ })
833
+ );
834
+ } else {
835
+ result = DROPPED;
836
+ }
837
+ ancestors.delete(item);
838
+ return result;
839
+ };
840
+ try {
841
+ const allowed = new Set(allowedKeys);
842
+ const selected = Object.fromEntries(Object.entries(value).filter(([key]) => allowed.has(key)));
843
+ ancestors.add(value);
844
+ const copied = copy(selected);
845
+ const parsed = JSON.parse(JSON.stringify(copied === DROPPED ? {} : copied) || "{}");
846
+ return isRecord(parsed) ? parsed : {};
847
+ } catch {
848
+ return {};
849
+ }
850
+ }
851
+
852
+ // src/window/WindowPanel.tsx
853
+ import { useWindowConfig, WindowSelectionSurface } from "@ai-matrx/summon/window-host";
854
+
855
+ // src/window/mobile/MobileDrawerSurface.tsx
856
+ import { useState as useState2 } from "react";
857
+ import {
858
+ Drawer,
859
+ DrawerClose,
860
+ DrawerContent,
861
+ DrawerDescription,
862
+ DrawerTitle
863
+ } from "@ai-matrx/design-system";
864
+ import { Button } from "@ai-matrx/design-system";
865
+ import { cn as cn3 } from "@ai-matrx/design-system/cn";
866
+
867
+ // src/window/chrome/MobileTitleRow.tsx
868
+ import { cn as cn2 } from "@ai-matrx/design-system/cn";
869
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
870
+ var MOBILE_WINDOW_TITLE_TEXT = "line-clamp-2 break-words text-sm font-medium leading-snug text-foreground";
871
+ function isTextTitle(title) {
872
+ return typeof title === "string" || typeof title === "number";
873
+ }
874
+ function mobileWindowTitleClass(title) {
875
+ return isTextTitle(title) ? cn2("min-w-0 flex-1", MOBILE_WINDOW_TITLE_TEXT) : "flex min-w-0 flex-1 items-center overflow-hidden text-sm font-medium";
876
+ }
877
+ function MobileWindowTitle({
878
+ title,
879
+ className
880
+ }) {
881
+ if (title == null || title === "") return /* @__PURE__ */ jsx3("div", { className: cn2("min-w-0 flex-1", className) });
882
+ return /* @__PURE__ */ jsx3("div", { "data-window-mobile-title": "", className: cn2(mobileWindowTitleClass(title), className), children: title });
883
+ }
884
+ function MobileWindowTitleRow({
885
+ leading,
886
+ trailing,
887
+ children,
888
+ className
889
+ }) {
890
+ return /* @__PURE__ */ jsxs3(
891
+ "div",
892
+ {
893
+ "data-window-mobile-title-row": "",
894
+ className: cn2("flex min-h-11 items-center gap-1.5 py-1", className),
895
+ children: [
896
+ leading,
897
+ children,
898
+ trailing
899
+ ]
900
+ }
901
+ );
902
+ }
903
+ function MobileWindowActionsRow({
904
+ leading,
905
+ actions,
906
+ className
907
+ }) {
908
+ if (!leading && !actions) return null;
909
+ return /* @__PURE__ */ jsxs3(
910
+ "div",
911
+ {
912
+ "data-window-mobile-actions-row": "",
913
+ className: cn2("flex min-h-10 items-center gap-1.5 pb-1", className),
914
+ children: [
915
+ leading,
916
+ actions ? /* @__PURE__ */ jsx3("div", { className: "ml-auto flex items-center gap-1", children: actions }) : null
917
+ ]
918
+ }
919
+ );
920
+ }
921
+
922
+ // src/window/mobile/MobileDrawerSurface.tsx
923
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
924
+ function MobileDrawerSurface({
925
+ title,
926
+ children,
927
+ onClose,
928
+ sidebar,
929
+ footer,
930
+ isOpen,
931
+ actionsLeft,
932
+ actionsRight,
933
+ bodyClassName,
934
+ surfaceLayer,
935
+ sizeToContent = false,
936
+ hideClose = false,
937
+ sidebarAs = "drawer"
938
+ }) {
939
+ const [sidebarOpen, setSidebarOpen] = useState2(false);
940
+ const hasSidebar = !!sidebar;
941
+ return /* @__PURE__ */ jsxs4(
942
+ Drawer,
943
+ {
944
+ open: isOpen,
945
+ onOpenChange: (next) => {
946
+ if (!next) onClose();
947
+ },
948
+ children: [
949
+ /* @__PURE__ */ jsxs4(
950
+ DrawerContent,
951
+ {
952
+ "data-surface-layer": surfaceLayer,
953
+ className: cn3(
954
+ "max-h-[85dvh] px-0 pb-safe flex flex-col",
955
+ sizeToContent ? "h-auto" : "h-[85dvh]"
956
+ ),
957
+ children: [
958
+ /* @__PURE__ */ jsxs4("div", { className: "-mt-3 flex flex-col px-3 border-b border-border/50 shrink-0", children: [
959
+ /* @__PURE__ */ jsx4(
960
+ MobileWindowTitleRow,
961
+ {
962
+ className: "py-0",
963
+ trailing: hideClose ? null : /* @__PURE__ */ jsx4(DrawerClose, { asChild: true, children: /* @__PURE__ */ jsx4(
964
+ Button,
965
+ {
966
+ type: "button",
967
+ variant: "ghost",
968
+ size: "icon",
969
+ className: "h-8 w-8 shrink-0",
970
+ "aria-label": "Close",
971
+ children: /* @__PURE__ */ jsx4(XIcon, { className: "w-4 h-4" })
972
+ }
973
+ ) }),
974
+ children: /* @__PURE__ */ jsx4(
975
+ DrawerTitle,
976
+ {
977
+ "data-window-mobile-title": "",
978
+ className: mobileWindowTitleClass(title),
979
+ children: title
980
+ }
981
+ )
982
+ }
983
+ ),
984
+ /* @__PURE__ */ jsx4(
985
+ MobileWindowActionsRow,
986
+ {
987
+ leading: actionsLeft ? /* @__PURE__ */ jsx4("div", { className: "flex items-center gap-1 shrink-0", children: actionsLeft }) : null,
988
+ actions: actionsRight || hasSidebar ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
989
+ actionsRight,
990
+ hasSidebar && /* @__PURE__ */ jsx4(
991
+ Button,
992
+ {
993
+ type: "button",
994
+ variant: "ghost",
995
+ size: "icon",
996
+ className: "h-8 w-8",
997
+ "aria-label": sidebarOpen ? "Close sidebar" : "Open sidebar",
998
+ onClick: () => setSidebarOpen((v) => !v),
999
+ children: /* @__PURE__ */ jsx4(PanelLeftIcon, { className: "w-4 h-4" })
1000
+ }
1001
+ )
1002
+ ] }) : null
1003
+ }
1004
+ )
1005
+ ] }),
1006
+ /* @__PURE__ */ jsx4(DrawerDescription, { className: "sr-only", children: typeof title === "string" ? title : "Mobile window" }),
1007
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 flex min-h-0 overflow-hidden", children: [
1008
+ hasSidebar && sidebarAs === "inline" && sidebarOpen && /* @__PURE__ */ jsx4("div", { className: "w-2/5 min-w-[160px] border-r border-border/50 overflow-y-auto", children: sidebar }),
1009
+ /* @__PURE__ */ jsx4(
1010
+ "div",
1011
+ {
1012
+ className: cn3(
1013
+ "flex-1 min-h-0 overflow-auto",
1014
+ bodyClassName
1015
+ ),
1016
+ children
1017
+ }
1018
+ )
1019
+ ] }),
1020
+ footer && /* @__PURE__ */ jsx4("div", { className: "border-t border-border/50 shrink-0", children: footer })
1021
+ ]
1022
+ }
1023
+ ),
1024
+ hasSidebar && sidebarAs === "drawer" && /* @__PURE__ */ jsx4(
1025
+ Drawer,
1026
+ {
1027
+ open: sidebarOpen,
1028
+ onOpenChange: setSidebarOpen,
1029
+ direction: "right",
1030
+ children: /* @__PURE__ */ jsxs4(DrawerContent, { className: "h-[85dvh] max-h-[85dvh] w-[85vw] max-w-[420px] ml-auto flex flex-col", children: [
1031
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border/50 shrink-0", children: [
1032
+ /* @__PURE__ */ jsx4(DrawerTitle, { className: "flex-1 text-sm font-medium", children: "Menu" }),
1033
+ /* @__PURE__ */ jsx4(DrawerClose, { asChild: true, children: /* @__PURE__ */ jsx4(
1034
+ Button,
1035
+ {
1036
+ type: "button",
1037
+ variant: "ghost",
1038
+ size: "icon",
1039
+ className: "h-8 w-8",
1040
+ "aria-label": "Close sidebar",
1041
+ children: /* @__PURE__ */ jsx4(XIcon, { className: "w-4 h-4" })
1042
+ }
1043
+ ) })
1044
+ ] }),
1045
+ /* @__PURE__ */ jsx4(DrawerDescription, { className: "sr-only", children: "Sidebar menu" }),
1046
+ /* @__PURE__ */ jsx4("div", { className: "flex-1 min-h-0 overflow-auto", children: sidebar })
1047
+ ] })
1048
+ }
1049
+ )
1050
+ ]
1051
+ }
1052
+ );
1053
+ }
1054
+
1055
+ // src/window/mobile/MobileCardSurface.tsx
1056
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
1057
+ import { createPortal } from "react-dom";
1058
+ import { FloatingLayer, useFloatingLayerZIndex } from "@ai-matrx/design-system";
1059
+ import { Button as Button2 } from "@ai-matrx/design-system";
1060
+ import { cn as cn4 } from "@ai-matrx/design-system/cn";
1061
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1062
+ function MobileCardSurface({
1063
+ title,
1064
+ children,
1065
+ onClose,
1066
+ footer,
1067
+ isOpen,
1068
+ actionsRight,
1069
+ bodyClassName,
1070
+ surfaceLayer,
1071
+ maxHeight = "60dvh"
1072
+ }) {
1073
+ const cardRef = useRef2(null);
1074
+ useEffect2(() => {
1075
+ if (!isOpen) return void 0;
1076
+ const onKey = (e) => {
1077
+ if (e.key === "Escape" && !e.defaultPrevented) onClose();
1078
+ };
1079
+ document.addEventListener("keydown", onKey);
1080
+ return () => document.removeEventListener("keydown", onKey);
1081
+ }, [isOpen, onClose]);
1082
+ const zIndex = useFloatingLayerZIndex(9999);
1083
+ if (!isOpen) return null;
1084
+ if (typeof document === "undefined") return null;
1085
+ return createPortal(
1086
+ /* @__PURE__ */ jsx5(FloatingLayer, { children: /* @__PURE__ */ jsxs5(
1087
+ "div",
1088
+ {
1089
+ ref: cardRef,
1090
+ "data-surface-layer": surfaceLayer,
1091
+ role: "dialog",
1092
+ "aria-modal": "false",
1093
+ className: cn4(
1094
+ "fixed right-3 bottom-3 left-3 sm:left-auto sm:right-3",
1095
+ "w-auto sm:w-[92vw] sm:max-w-[420px]",
1096
+ "flex flex-col",
1097
+ "rounded-xl overflow-hidden",
1098
+ "bg-card/97 backdrop-blur-xl border border-border/60 shadow-2xl",
1099
+ // Themes can override via --window-surface-* tokens in globals.css
1100
+ "animate-[card-enter_var(--window-surface-enter-duration,220ms)_var(--window-surface-easing,ease-out)]"
1101
+ ),
1102
+ style: { maxHeight, zIndex, paddingBottom: "env(safe-area-inset-bottom)" },
1103
+ children: [
1104
+ /* @__PURE__ */ jsxs5("div", { className: "flex flex-col pl-3 pr-1 border-b border-border/50 shrink-0", children: [
1105
+ /* @__PURE__ */ jsx5(
1106
+ MobileWindowTitleRow,
1107
+ {
1108
+ trailing: /* @__PURE__ */ jsx5(
1109
+ Button2,
1110
+ {
1111
+ type: "button",
1112
+ variant: "ghost",
1113
+ size: "icon",
1114
+ className: "h-11 w-11 shrink-0",
1115
+ "aria-label": "Close",
1116
+ onClick: onClose,
1117
+ children: /* @__PURE__ */ jsx5(XIcon, { className: "w-3.5 h-3.5" })
1118
+ }
1119
+ ),
1120
+ children: /* @__PURE__ */ jsx5(MobileWindowTitle, { title })
1121
+ }
1122
+ ),
1123
+ /* @__PURE__ */ jsx5(MobileWindowActionsRow, { actions: actionsRight })
1124
+ ] }),
1125
+ /* @__PURE__ */ jsx5("div", { className: cn4("flex-1 min-h-0 overflow-auto", bodyClassName), children }),
1126
+ footer && /* @__PURE__ */ jsx5("div", { className: "border-t border-border/50 shrink-0", children: footer })
1127
+ ]
1128
+ }
1129
+ ) }),
1130
+ document.body
1131
+ );
1132
+ }
1133
+
1134
+ // src/window/chrome/DebugStrip.tsx
1135
+ import { useEffect as useEffect3, useState as useState3 } from "react";
1136
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1137
+ function DebugStrip({ rect, zIndex }) {
1138
+ const [vp, setVp] = useState3(
1139
+ () => typeof window === "undefined" ? { vw: 0, vh: 0, sw: 0, sh: 0, dpr: 1 } : {
1140
+ vw: window.innerWidth,
1141
+ vh: window.innerHeight,
1142
+ sw: window.screen.width,
1143
+ sh: window.screen.height,
1144
+ dpr: window.devicePixelRatio ?? 1
1145
+ }
1146
+ );
1147
+ useEffect3(() => {
1148
+ const update = () => setVp({
1149
+ vw: window.innerWidth,
1150
+ vh: window.innerHeight,
1151
+ sw: window.screen.width,
1152
+ sh: window.screen.height,
1153
+ dpr: window.devicePixelRatio ?? 1
1154
+ });
1155
+ window.addEventListener("resize", update);
1156
+ return () => window.removeEventListener("resize", update);
1157
+ }, []);
1158
+ const windowEntries = [
1159
+ ["x", rect.x],
1160
+ ["y", rect.y],
1161
+ ["w", rect.width],
1162
+ ["h", rect.height],
1163
+ ["z", zIndex]
1164
+ ];
1165
+ const viewportEntries = [
1166
+ ["vw", vp.vw],
1167
+ ["vh", vp.vh],
1168
+ ["sw", vp.sw],
1169
+ ["sh", vp.sh],
1170
+ ["dpr", vp.dpr]
1171
+ ];
1172
+ return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-0.5 px-3 py-1.5 border-b border-warning/20 bg-warning/5 shrink-0 font-mono text-[10px]", children: [
1173
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3 flex-wrap", children: [
1174
+ /* @__PURE__ */ jsx6("span", { className: "text-warning uppercase tracking-wide text-[9px]", children: "win" }),
1175
+ windowEntries.map(([label, val]) => /* @__PURE__ */ jsxs6(
1176
+ "span",
1177
+ {
1178
+ className: "inline-flex items-center gap-0.5 leading-none",
1179
+ children: [
1180
+ /* @__PURE__ */ jsxs6("span", { className: "text-warning", children: [
1181
+ label,
1182
+ ":"
1183
+ ] }),
1184
+ /* @__PURE__ */ jsx6("span", { className: "text-warning font-bold tabular-nums", children: Math.round(val) })
1185
+ ]
1186
+ },
1187
+ label
1188
+ ))
1189
+ ] }),
1190
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-3 flex-wrap", children: [
1191
+ /* @__PURE__ */ jsx6("span", { className: "text-info uppercase tracking-wide text-[9px]", children: "vp" }),
1192
+ viewportEntries.map(([label, val]) => /* @__PURE__ */ jsxs6(
1193
+ "span",
1194
+ {
1195
+ className: "inline-flex items-center gap-0.5 leading-none",
1196
+ children: [
1197
+ /* @__PURE__ */ jsxs6("span", { className: "text-info", children: [
1198
+ label,
1199
+ ":"
1200
+ ] }),
1201
+ /* @__PURE__ */ jsx6("span", { className: "text-info font-bold tabular-nums", children: typeof val === "number" && !Number.isInteger(val) ? val.toFixed(2) : Math.round(val) })
1202
+ ]
1203
+ },
1204
+ label
1205
+ ))
1206
+ ] })
1207
+ ] });
1208
+ }
1209
+
1210
+ // src/window/chrome/chromeClasses.ts
1211
+ import { cn as cn5 } from "@ai-matrx/design-system/cn";
1212
+ var WINDOW_CHROME_INTERACTIVE = "[&_button:not(:disabled)]:cursor-pointer [&_a]:cursor-pointer [&_[role=button]:not([aria-disabled=true])]:cursor-pointer [&_summary]:cursor-pointer [&_label:has(input,[role=checkbox],[role=radio])]:cursor-pointer";
1213
+ var WINDOW_CHROME_ACTIONS = cn5(
1214
+ "flex items-center gap-0.5 shrink-0 text-foreground/80 [&_svg]:text-foreground/80",
1215
+ WINDOW_CHROME_INTERACTIVE
1216
+ );
1217
+
1218
+ // src/window/chrome/MobileHeader.tsx
1219
+ import { SegmentedControl } from "@ai-matrx/design-system/controls";
1220
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1221
+ function MobileWindowHeader({
1222
+ title,
1223
+ actionsRight,
1224
+ onMinimize,
1225
+ onClose,
1226
+ hasSidebar,
1227
+ activePaneMobile,
1228
+ onSetActivePane
1229
+ }) {
1230
+ const trafficLights = onClose || onMinimize ? /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1.5 shrink-0", children: [
1231
+ onClose && /* @__PURE__ */ jsx7(
1232
+ "button",
1233
+ {
1234
+ type: "button",
1235
+ className: "flex h-11 w-11 shrink-0 cursor-pointer items-center justify-center",
1236
+ onClick: onClose,
1237
+ "aria-label": "Close",
1238
+ children: /* @__PURE__ */ jsx7("span", { className: "flex h-6 w-6 items-center justify-center rounded-full matrx-window-light-close", children: /* @__PURE__ */ jsx7(XIcon, { className: "h-2.5 w-2.5 matrx-window-light-glyph" }) })
1239
+ }
1240
+ ),
1241
+ onMinimize && /* @__PURE__ */ jsx7(
1242
+ "button",
1243
+ {
1244
+ type: "button",
1245
+ className: "flex h-11 w-11 shrink-0 cursor-pointer items-center justify-center",
1246
+ onClick: onMinimize,
1247
+ "aria-label": "Minimize",
1248
+ children: /* @__PURE__ */ jsx7("span", { className: "flex h-6 w-6 items-center justify-center rounded-full matrx-window-light-minimize", children: /* @__PURE__ */ jsx7(
1249
+ MinusIcon,
1250
+ {
1251
+ className: "h-2.5 w-2.5 matrx-window-light-glyph"
1252
+ }
1253
+ ) })
1254
+ }
1255
+ )
1256
+ ] }) : null;
1257
+ const paneToggle = hasSidebar ? /* @__PURE__ */ jsx7(SegmentedControl, { "aria-label": "Pane", value: activePaneMobile, onValueChange: onSetActivePane, data: [{ value: "sidebar", label: "Sidebar" }, { value: "main", label: "Content" }] }) : null;
1258
+ return /* @__PURE__ */ jsxs7("div", { className: "flex shrink-0 flex-col border-b border-border/50 bg-muted/40 px-2 select-none", children: [
1259
+ /* @__PURE__ */ jsx7(MobileWindowTitleRow, { leading: trafficLights, children: /* @__PURE__ */ jsx7(MobileWindowTitle, { title, className: "pr-1" }) }),
1260
+ /* @__PURE__ */ jsx7(
1261
+ MobileWindowActionsRow,
1262
+ {
1263
+ leading: paneToggle,
1264
+ actions: actionsRight ? /* @__PURE__ */ jsx7("div", { className: WINDOW_CHROME_ACTIONS, children: actionsRight }) : null
1265
+ }
1266
+ )
1267
+ ] });
1268
+ }
1269
+
1270
+ // src/window/chrome/DeprecationBanner.tsx
1271
+ import { useState as useState4 } from "react";
1272
+ import { cn as cn6 } from "@ai-matrx/design-system/cn";
1273
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1274
+ function DeprecationBanner({
1275
+ replacedBy,
1276
+ note,
1277
+ className
1278
+ }) {
1279
+ const [dismissed, setDismissed] = useState4(false);
1280
+ if (dismissed) return null;
1281
+ const message = note ?? (replacedBy ? `Deprecated \u2014 moved to "${replacedBy}". This window will be removed soon.` : "Deprecated \u2014 this window will be removed soon.");
1282
+ return /* @__PURE__ */ jsxs8(
1283
+ "div",
1284
+ {
1285
+ className: cn6(
1286
+ "shrink-0 flex items-start gap-2 px-2 py-1 border-b border-destructive/40 bg-destructive/10 text-destructive-ink text-[11px] leading-tight",
1287
+ className
1288
+ ),
1289
+ children: [
1290
+ /* @__PURE__ */ jsx8(TriangleAlertIcon, { className: "w-3.5 h-3.5 mt-[1px] shrink-0" }),
1291
+ /* @__PURE__ */ jsx8("span", { className: "flex-1 break-words", children: message }),
1292
+ /* @__PURE__ */ jsx8(
1293
+ "button",
1294
+ {
1295
+ type: "button",
1296
+ "aria-label": "Dismiss deprecation notice",
1297
+ className: "shrink-0 rounded hover:bg-destructive/20 p-0.5 transition-colors",
1298
+ onClick: () => setDismissed(true),
1299
+ children: /* @__PURE__ */ jsx8(XIcon, { className: "w-3 h-3" })
1300
+ }
1301
+ )
1302
+ ]
1303
+ }
1304
+ );
1305
+ }
1306
+
1307
+ // src/window/chrome/PopoutTopBar.tsx
1308
+ import { cn as cn7 } from "@ai-matrx/design-system/cn";
1309
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1310
+ function PopoutTopBar({
1311
+ title,
1312
+ actionsLeft,
1313
+ actionsRight,
1314
+ onDock
1315
+ }) {
1316
+ return /* @__PURE__ */ jsxs9(
1317
+ "div",
1318
+ {
1319
+ className: cn7(
1320
+ "flex items-center gap-1.5 px-2 py-1 min-h-[32px] shrink-0",
1321
+ "border-b border-border/50 bg-muted/40 select-none"
1322
+ ),
1323
+ children: [
1324
+ actionsLeft && /* @__PURE__ */ jsx9("div", { className: WINDOW_CHROME_ACTIONS, children: actionsLeft }),
1325
+ /* @__PURE__ */ jsx9("div", { className: "flex-1 flex items-center justify-center min-w-0", children: /* @__PURE__ */ jsx9("span", { className: "text-xs font-medium text-foreground/80 truncate", children: title ?? "" }) }),
1326
+ actionsRight && /* @__PURE__ */ jsx9("div", { className: WINDOW_CHROME_ACTIONS, children: actionsRight }),
1327
+ /* @__PURE__ */ jsx9(
1328
+ "button",
1329
+ {
1330
+ type: "button",
1331
+ className: cn7(
1332
+ "ml-1 p-1 rounded hover:bg-accent/60 transition-colors shrink-0 cursor-pointer",
1333
+ "text-foreground/60 hover:text-foreground"
1334
+ ),
1335
+ onClick: onDock,
1336
+ title: "Dock window",
1337
+ "aria-label": "Dock window back to parent",
1338
+ children: /* @__PURE__ */ jsx9(ArrowLeftToLineIcon, { className: "w-3.5 h-3.5" })
1339
+ }
1340
+ )
1341
+ ]
1342
+ }
1343
+ );
1344
+ }
1345
+
1346
+ // src/window/popout/featureDetection.ts
1347
+ function detectPopoutCapability() {
1348
+ if (typeof window === "undefined") return "none";
1349
+ const w = window;
1350
+ if (w.documentPictureInPicture && typeof w.documentPictureInPicture.requestWindow === "function") {
1351
+ return "pip";
1352
+ }
1353
+ if (typeof window.open === "function") {
1354
+ return "popup";
1355
+ }
1356
+ return "none";
1357
+ }
1358
+ function supportsDocumentPictureInPicture() {
1359
+ return detectPopoutCapability() === "pip";
1360
+ }
1361
+ function supportsAnyPopout() {
1362
+ return detectPopoutCapability() !== "none";
1363
+ }
1364
+
1365
+ // src/window/popout/usePopoutWindow.ts
1366
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef3 } from "react";
1367
+ import { useSummonDispatch as useSummonDispatch2, useSummonPorts, useSummonSelector as useSummonSelector2 } from "@ai-matrx/summon/react";
1368
+ import {
1369
+ popOutWindow,
1370
+ dockWindow,
1371
+ selectActivePipWindowId,
1372
+ selectPopoutMode
1373
+ } from "@ai-matrx/summon";
1374
+
1375
+ // src/window/popout/popoutWindowMap.ts
1376
+ import { useSyncExternalStore as useSyncExternalStore3 } from "react";
1377
+ import { globalSlot as globalSlot2 } from "@ai-matrx/kit/global-slot";
1378
+ var slot = () => globalSlot2("ai-matrx.summon.window.popoutWindows", () => ({ map: /* @__PURE__ */ new Map(), listeners: /* @__PURE__ */ new Set() }));
1379
+ function notify() {
1380
+ for (const fn of slot().listeners) fn();
1381
+ }
1382
+ function setPopoutWindow(id, win) {
1383
+ const { map } = slot();
1384
+ const prev = map.get(id);
1385
+ if (prev === win) return;
1386
+ map.set(id, win);
1387
+ notify();
1388
+ }
1389
+ function deletePopoutWindow(id) {
1390
+ const { map } = slot();
1391
+ if (!map.has(id)) return;
1392
+ map.delete(id);
1393
+ notify();
1394
+ }
1395
+ function getPopoutWindow(id) {
1396
+ return slot().map.get(id) ?? null;
1397
+ }
1398
+ function forEachPopoutWindow(cb) {
1399
+ for (const [id, win] of slot().map) cb(win, id);
1400
+ }
1401
+ function subscribePopoutWindowMap(listener) {
1402
+ const { listeners } = slot();
1403
+ listeners.add(listener);
1404
+ return () => {
1405
+ listeners.delete(listener);
1406
+ };
1407
+ }
1408
+ function usePopoutWindowRef(id) {
1409
+ return useSyncExternalStore3(
1410
+ subscribePopoutWindowMap,
1411
+ () => getPopoutWindow(id),
1412
+ // SSR snapshot — popouts are never present during SSR.
1413
+ () => null
1414
+ );
1415
+ }
1416
+
1417
+ // src/window/popout/cloneStyles.ts
1418
+ var RESET_CSS = `
1419
+ html, body {
1420
+ margin: 0;
1421
+ padding: 0;
1422
+ height: 100%;
1423
+ overflow: hidden;
1424
+ }
1425
+ body {
1426
+ font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
1427
+ background: hsl(var(--background));
1428
+ color: hsl(var(--foreground));
1429
+ -webkit-font-smoothing: antialiased;
1430
+ -moz-osx-font-smoothing: grayscale;
1431
+ }
1432
+ `.trim();
1433
+ var POPOUT_DATA_ATTR = "data-popout-clone";
1434
+ var RESET_MARKER = "reset";
1435
+ function cloneStylesIntoDocument(source, target) {
1436
+ injectResetStylesheet(target);
1437
+ cloneAllStyleAndLinkNodes(source, target);
1438
+ cloneAdoptedStylesheets(source, target);
1439
+ mirrorHtmlAttributes(source, target);
1440
+ const headObserver = new MutationObserver((mutations) => {
1441
+ handleHeadMutations(mutations, target);
1442
+ });
1443
+ headObserver.observe(source.head, {
1444
+ childList: true,
1445
+ subtree: true,
1446
+ characterData: true,
1447
+ attributes: true,
1448
+ attributeFilter: ["href", "media"]
1449
+ });
1450
+ const htmlObserver = new MutationObserver(() => {
1451
+ mirrorHtmlAttributes(source, target);
1452
+ });
1453
+ htmlObserver.observe(source.documentElement, {
1454
+ attributes: true,
1455
+ attributeFilter: ["class", "data-theme", "data-scroll-behavior"]
1456
+ });
1457
+ return {
1458
+ dispose() {
1459
+ headObserver.disconnect();
1460
+ htmlObserver.disconnect();
1461
+ }
1462
+ };
1463
+ }
1464
+ function injectResetStylesheet(target) {
1465
+ const existing = target.head.querySelector(
1466
+ `style[${POPOUT_DATA_ATTR}="${RESET_MARKER}"]`
1467
+ );
1468
+ if (existing) return;
1469
+ const style = target.createElement("style");
1470
+ style.setAttribute(POPOUT_DATA_ATTR, RESET_MARKER);
1471
+ style.textContent = RESET_CSS;
1472
+ target.head.insertBefore(style, target.head.firstChild);
1473
+ }
1474
+ function cloneAllStyleAndLinkNodes(source, target) {
1475
+ const nodes = source.head.querySelectorAll("style, link[rel='stylesheet']");
1476
+ nodes.forEach((node) => {
1477
+ cloneOneStyleOrLink(node, target);
1478
+ });
1479
+ }
1480
+ function cloneOneStyleOrLink(node, target) {
1481
+ if (node.tagName === "STYLE") {
1482
+ const clone = target.createElement("style");
1483
+ clone.textContent = node.textContent ?? "";
1484
+ copyDataAttributes(node, clone);
1485
+ clone.setAttribute(POPOUT_DATA_ATTR, getNodeKey(node));
1486
+ target.head.appendChild(clone);
1487
+ return clone;
1488
+ }
1489
+ if (node.tagName === "LINK" && node.rel === "stylesheet") {
1490
+ const link = node;
1491
+ const clone = target.createElement("link");
1492
+ clone.rel = "stylesheet";
1493
+ if (link.href) clone.href = link.href;
1494
+ if (link.media) clone.media = link.media;
1495
+ if (link.crossOrigin) clone.crossOrigin = link.crossOrigin;
1496
+ if (link.integrity) clone.integrity = link.integrity;
1497
+ copyDataAttributes(link, clone);
1498
+ clone.setAttribute(POPOUT_DATA_ATTR, getNodeKey(node));
1499
+ target.head.appendChild(clone);
1500
+ return clone;
1501
+ }
1502
+ return null;
1503
+ }
1504
+ function getNodeKey(node) {
1505
+ if (node.tagName === "STYLE") {
1506
+ return "style:" + cheapHash(node.textContent ?? "");
1507
+ }
1508
+ if (node.tagName === "LINK") {
1509
+ const href = node.href || "";
1510
+ return "link:" + href;
1511
+ }
1512
+ return "other:" + Math.random().toString(36).slice(2);
1513
+ }
1514
+ function cheapHash(s) {
1515
+ let h = 5381;
1516
+ for (let i = 0; i < s.length; i++) {
1517
+ h = (h << 5) + h + s.charCodeAt(i) | 0;
1518
+ }
1519
+ return (h >>> 0).toString(36);
1520
+ }
1521
+ function copyDataAttributes(from, to) {
1522
+ for (const attr of Array.from(from.attributes)) {
1523
+ if (attr.name.startsWith("data-")) {
1524
+ if (attr.name === POPOUT_DATA_ATTR) continue;
1525
+ to.setAttribute(attr.name, attr.value);
1526
+ }
1527
+ }
1528
+ }
1529
+ function cloneAdoptedStylesheets(source, target) {
1530
+ if (Array.isArray(source.adoptedStyleSheets) && source.adoptedStyleSheets.length > 0) {
1531
+ try {
1532
+ target.adoptedStyleSheets = [...source.adoptedStyleSheets];
1533
+ } catch {
1534
+ }
1535
+ }
1536
+ }
1537
+ function mirrorHtmlAttributes(source, target) {
1538
+ const src = source.documentElement;
1539
+ const tgt = target.documentElement;
1540
+ if (tgt.className !== src.className) {
1541
+ tgt.className = src.className;
1542
+ }
1543
+ for (const attr of ["data-theme", "data-scroll-behavior", "lang"]) {
1544
+ const v = src.getAttribute(attr);
1545
+ if (v === null) {
1546
+ if (tgt.hasAttribute(attr)) tgt.removeAttribute(attr);
1547
+ } else if (tgt.getAttribute(attr) !== v) {
1548
+ tgt.setAttribute(attr, v);
1549
+ }
1550
+ }
1551
+ }
1552
+ function handleHeadMutations(mutations, target) {
1553
+ for (const m of mutations) {
1554
+ m.addedNodes.forEach((n) => {
1555
+ if (!(n instanceof Element)) return;
1556
+ if (n.tagName === "STYLE" || n.tagName === "LINK" && n.rel === "stylesheet") {
1557
+ cloneOneStyleOrLink(n, target);
1558
+ }
1559
+ });
1560
+ m.removedNodes.forEach((n) => {
1561
+ if (!(n instanceof Element)) return;
1562
+ if (n.tagName !== "STYLE" && n.tagName !== "LINK") return;
1563
+ const key = getNodeKey(n);
1564
+ const cloneEl = target.head.querySelector(
1565
+ `[${POPOUT_DATA_ATTR}="${cssEscape(key)}"]`
1566
+ );
1567
+ if (cloneEl) cloneEl.remove();
1568
+ });
1569
+ if (m.type === "characterData" && m.target.parentElement?.tagName === "STYLE") {
1570
+ const styleEl = m.target.parentElement;
1571
+ const oldKey = findExistingMirrorByContent(target, styleEl.textContent ?? "");
1572
+ const newKey = getNodeKey(styleEl);
1573
+ if (oldKey && oldKey !== newKey) {
1574
+ const mirror = target.head.querySelector(
1575
+ `[${POPOUT_DATA_ATTR}="${cssEscape(oldKey)}"]`
1576
+ );
1577
+ if (mirror) {
1578
+ mirror.textContent = styleEl.textContent ?? "";
1579
+ mirror.setAttribute(POPOUT_DATA_ATTR, newKey);
1580
+ }
1581
+ }
1582
+ }
1583
+ if (m.type === "attributes" && m.target instanceof HTMLLinkElement && m.target.rel === "stylesheet") {
1584
+ const link = m.target;
1585
+ const allClones = target.head.querySelectorAll(
1586
+ `link[${POPOUT_DATA_ATTR}]`
1587
+ );
1588
+ cloneOneStyleOrLink(link, target);
1589
+ void allClones;
1590
+ }
1591
+ }
1592
+ }
1593
+ function findExistingMirrorByContent(target, text) {
1594
+ const styles = target.head.querySelectorAll(
1595
+ `style[${POPOUT_DATA_ATTR}^="style:"]`
1596
+ );
1597
+ for (const s of Array.from(styles)) {
1598
+ if (s.textContent === text) return null;
1599
+ }
1600
+ if (styles.length > 0) {
1601
+ return styles[0]?.getAttribute(POPOUT_DATA_ATTR) ?? null;
1602
+ }
1603
+ return null;
1604
+ }
1605
+ function cssEscape(s) {
1606
+ return s.replace(/(["\\])/g, "\\$1");
1607
+ }
1608
+
1609
+ // src/window/popout/popoutPendingStorage.ts
1610
+ var STORAGE_KEY = "matrx_popout_pending";
1611
+ function readState() {
1612
+ if (typeof window === "undefined") return { windowIds: [] };
1613
+ try {
1614
+ const raw = window.sessionStorage.getItem(STORAGE_KEY);
1615
+ if (!raw) return { windowIds: [] };
1616
+ const parsed = JSON.parse(raw);
1617
+ if (!Array.isArray(parsed.windowIds)) return { windowIds: [] };
1618
+ return {
1619
+ windowIds: parsed.windowIds.filter(
1620
+ (id) => typeof id === "string"
1621
+ )
1622
+ };
1623
+ } catch {
1624
+ return { windowIds: [] };
1625
+ }
1626
+ }
1627
+ function writeState(state2) {
1628
+ if (typeof window === "undefined") return;
1629
+ try {
1630
+ if (state2.windowIds.length === 0) {
1631
+ window.sessionStorage.removeItem(STORAGE_KEY);
1632
+ } else {
1633
+ window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state2));
1634
+ }
1635
+ } catch {
1636
+ }
1637
+ }
1638
+ function markPopoutPending(windowId) {
1639
+ const state2 = readState();
1640
+ if (!state2.windowIds.includes(windowId)) {
1641
+ state2.windowIds.push(windowId);
1642
+ writeState(state2);
1643
+ }
1644
+ }
1645
+ function clearPopoutPending(windowId) {
1646
+ const state2 = readState();
1647
+ const next = state2.windowIds.filter((id) => id !== windowId);
1648
+ if (next.length !== state2.windowIds.length) {
1649
+ writeState({ windowIds: next });
1650
+ }
1651
+ }
1652
+ function getPendingPopoutWindowIds() {
1653
+ return readState().windowIds;
1654
+ }
1655
+ function clearAllPopoutPending() {
1656
+ writeState({ windowIds: [] });
1657
+ }
1658
+
1659
+ // src/window/popout/usePopoutWindow.ts
1660
+ import { buildDockWindowPayload } from "@ai-matrx/summon";
1661
+ import { openOutbound } from "@ai-matrx/kit/outbound";
1662
+ function usePopoutWindow(windowId) {
1663
+ const dispatch = useSummonDispatch2();
1664
+ const { notify: notify3 } = useSummonPorts();
1665
+ const popoutMode = useSummonSelector2(selectPopoutMode(windowId));
1666
+ const activePipWindowId = useSummonSelector2(selectActivePipWindowId);
1667
+ const stylesDisposeRef = useRef3(null);
1668
+ const handleClose = useCallback2(() => {
1669
+ const win = getPopoutWindow(windowId);
1670
+ if (!win && process.env.NODE_ENV !== "production") {
1671
+ }
1672
+ stylesDisposeRef.current?.();
1673
+ stylesDisposeRef.current = null;
1674
+ deletePopoutWindow(windowId);
1675
+ clearPopoutPending(windowId);
1676
+ dispatch(dockWindow(buildDockWindowPayload(windowId)));
1677
+ try {
1678
+ win?.close();
1679
+ } catch {
1680
+ }
1681
+ if (process.env.NODE_ENV !== "production" && win) {
1682
+ console.info(`[popout] closed window for "${windowId}"`);
1683
+ }
1684
+ }, [dispatch, windowId]);
1685
+ const openPopout = useCallback2(
1686
+ async (opts) => {
1687
+ const capability = detectPopoutCapability();
1688
+ if (capability === "none") {
1689
+ notify3.error("Pop-out not supported", {
1690
+ description: "Your browser doesn't support floating windows."
1691
+ });
1692
+ return { ok: false, reason: "no-capability" };
1693
+ }
1694
+ const slotTakenByOther = activePipWindowId !== null && activePipWindowId !== windowId;
1695
+ let pipWin = null;
1696
+ let mode = "popup";
1697
+ if (capability === "pip" && !slotTakenByOther) {
1698
+ try {
1699
+ const w = window;
1700
+ if (w.documentPictureInPicture) {
1701
+ pipWin = await w.documentPictureInPicture.requestWindow({
1702
+ width: Math.max(200, Math.round(opts.width)),
1703
+ height: Math.max(120, Math.round(opts.height))
1704
+ });
1705
+ mode = "pip";
1706
+ }
1707
+ } catch (err) {
1708
+ console.warn(
1709
+ "[popout] Document PiP request failed, falling back to popup:",
1710
+ err
1711
+ );
1712
+ }
1713
+ }
1714
+ if (!pipWin) {
1715
+ const features = [
1716
+ "popup=yes",
1717
+ `width=${Math.max(200, Math.round(opts.width))}`,
1718
+ `height=${Math.max(120, Math.round(opts.height))}`
1719
+ ].join(",");
1720
+ try {
1721
+ pipWin = openOutbound("", `popout-${windowId}`, features);
1722
+ mode = "popup";
1723
+ } catch (err) {
1724
+ console.error("[popout] window.open failed:", err);
1725
+ }
1726
+ }
1727
+ if (!pipWin) {
1728
+ notify3.error("Pop-up blocked", {
1729
+ description: "Please allow pop-ups for this site to use floating windows."
1730
+ });
1731
+ return { ok: false, reason: "popup-blocked" };
1732
+ }
1733
+ try {
1734
+ pipWin.document.title = opts.title;
1735
+ } catch {
1736
+ }
1737
+ stylesDisposeRef.current = cloneStylesIntoDocument(
1738
+ window.document,
1739
+ pipWin.document
1740
+ ).dispose;
1741
+ pipWin.addEventListener("pagehide", handleClose, { once: true });
1742
+ setPopoutWindow(windowId, pipWin);
1743
+ markPopoutPending(windowId);
1744
+ dispatch(popOutWindow({ id: windowId, mode }));
1745
+ if (process.env.NODE_ENV !== "production") {
1746
+ console.info(
1747
+ `[popout] opened ${mode} window for "${windowId}" (${opts.width}x${opts.height})`
1748
+ );
1749
+ }
1750
+ return { ok: true, mode };
1751
+ },
1752
+ [activePipWindowId, dispatch, handleClose, notify3, windowId]
1753
+ );
1754
+ const close = useCallback2(() => {
1755
+ const win = getPopoutWindow(windowId);
1756
+ if (!win) {
1757
+ dispatch(dockWindow(buildDockWindowPayload(windowId)));
1758
+ return;
1759
+ }
1760
+ try {
1761
+ win.close();
1762
+ } catch {
1763
+ handleClose();
1764
+ }
1765
+ }, [dispatch, handleClose, windowId]);
1766
+ useEffect4(() => {
1767
+ if (popoutMode === null && getPopoutWindow(windowId) !== null) {
1768
+ try {
1769
+ getPopoutWindow(windowId)?.close();
1770
+ } catch {
1771
+ handleClose();
1772
+ }
1773
+ }
1774
+ }, [popoutMode, windowId, handleClose]);
1775
+ useEffect4(() => {
1776
+ const onBeforeUnload = () => {
1777
+ try {
1778
+ getPopoutWindow(windowId)?.close();
1779
+ } catch {
1780
+ }
1781
+ };
1782
+ window.addEventListener("beforeunload", onBeforeUnload);
1783
+ return () => {
1784
+ window.removeEventListener("beforeunload", onBeforeUnload);
1785
+ };
1786
+ }, [windowId]);
1787
+ return { openPopout, close };
1788
+ }
1789
+
1790
+ // src/window/popout/usePopoutControl.ts
1791
+ import { useCallback as useCallback3 } from "react";
1792
+ import { useSummonDispatch as useSummonDispatch3 } from "@ai-matrx/summon/react";
1793
+ import {
1794
+ dockWindow as dockWindow2
1795
+ } from "@ai-matrx/summon";
1796
+ import { buildDockWindowPayload as buildDockWindowPayload2 } from "@ai-matrx/summon";
1797
+ import { globalSlot as globalSlot3 } from "@ai-matrx/kit/global-slot";
1798
+ var openers = () => globalSlot3("ai-matrx.summon.window.popoutOpeners", () => /* @__PURE__ */ new Map());
1799
+ function registerPopoutOpener(windowId, fn) {
1800
+ openers().set(windowId, fn);
1801
+ return () => {
1802
+ if (openers().get(windowId) === fn) {
1803
+ openers().delete(windowId);
1804
+ }
1805
+ };
1806
+ }
1807
+ function getPopoutOpener(windowId) {
1808
+ return openers().get(windowId) ?? null;
1809
+ }
1810
+ function usePopoutControl() {
1811
+ const dispatch = useSummonDispatch3();
1812
+ const popOut = useCallback3(
1813
+ async (windowId, opts) => {
1814
+ const opener = getPopoutOpener(windowId);
1815
+ if (!opener) {
1816
+ return { ok: false, reason: "not-mounted" };
1817
+ }
1818
+ return opener(opts);
1819
+ },
1820
+ []
1821
+ );
1822
+ const dock = useCallback3(
1823
+ (windowId) => {
1824
+ const payload = buildDockWindowPayload2(windowId);
1825
+ dispatch(dockWindow2(payload));
1826
+ },
1827
+ [dispatch]
1828
+ );
1829
+ return { popOut, dock };
1830
+ }
1831
+ function buildPopoutOpts(rect, opts) {
1832
+ return {
1833
+ width: opts?.width ?? rect?.width ?? 480,
1834
+ height: opts?.height ?? rect?.height ?? 320,
1835
+ title: opts?.title ?? "Window"
1836
+ };
1837
+ }
1838
+
1839
+ // src/window/popout/PopoutPortal.tsx
1840
+ import { createPortal as createPortal2 } from "react-dom";
1841
+ import { useSummonSelector as useSummonSelector3 } from "@ai-matrx/summon/react";
1842
+ import { selectPopoutMode as selectPopoutMode2 } from "@ai-matrx/summon";
1843
+
1844
+ // src/window/popout/PopoutShell.tsx
1845
+ import { useMemo } from "react";
1846
+ import { PortalContainerProvider } from "@ai-matrx/design-system";
1847
+
1848
+ // src/window/popout/PopoutContext.tsx
1849
+ import { createContext, useContext } from "react";
1850
+ var DEFAULT_VALUE = {
1851
+ popoutDocument: null,
1852
+ popoutWindow: null,
1853
+ popoutContainer: null,
1854
+ mode: null
1855
+ };
1856
+ var PopoutContext = createContext(DEFAULT_VALUE);
1857
+ function usePopout() {
1858
+ return useContext(PopoutContext);
1859
+ }
1860
+
1861
+ // src/window/popout/PopoutShell.tsx
1862
+ import { jsx as jsx10 } from "react/jsx-runtime";
1863
+ function PopoutShell({
1864
+ popoutWindow,
1865
+ mode,
1866
+ children
1867
+ }) {
1868
+ const value = useMemo(
1869
+ () => ({
1870
+ popoutDocument: popoutWindow.document,
1871
+ popoutWindow,
1872
+ popoutContainer: popoutWindow.document.body,
1873
+ mode
1874
+ }),
1875
+ [popoutWindow, mode]
1876
+ );
1877
+ return /* @__PURE__ */ jsx10(PopoutContext.Provider, { value, children: /* @__PURE__ */ jsx10(PortalContainerProvider, { container: value.popoutContainer, children }) });
1878
+ }
1879
+
1880
+ // src/window/popout/PopoutPortal.tsx
1881
+ import { jsx as jsx11 } from "react/jsx-runtime";
1882
+ function PopoutPortal({ windowId, children }) {
1883
+ const popoutWin = usePopoutWindowRef(windowId);
1884
+ const popoutMode = useSummonSelector3(selectPopoutMode2(windowId));
1885
+ if (!popoutWin || popoutMode === null) return null;
1886
+ return createPortal2(
1887
+ /* @__PURE__ */ jsx11(PopoutShell, { popoutWindow: popoutWin, mode: popoutMode, children }),
1888
+ popoutWin.document.body
1889
+ );
1890
+ }
1891
+
1892
+ // src/window/tray/traySnapshotMap.ts
1893
+ import { globalSlot as globalSlot4 } from "@ai-matrx/kit/global-slot";
1894
+ var MAX_SNAPSHOTS = 16;
1895
+ var state = () => globalSlot4("ai-matrx.summon.window.traySnapshots", () => ({ map: /* @__PURE__ */ new Map(), listeners: /* @__PURE__ */ new Set() }));
1896
+ function notify2() {
1897
+ for (const fn of state().listeners) fn();
1898
+ }
1899
+ function revoke(url) {
1900
+ if (url && typeof URL !== "undefined" && typeof URL.revokeObjectURL === "function") URL.revokeObjectURL(url);
1901
+ }
1902
+ function setTraySnapshot(id, blob) {
1903
+ const { map } = state();
1904
+ revoke(map.get(id));
1905
+ const objectUrl = URL.createObjectURL(blob);
1906
+ map.delete(id);
1907
+ map.set(id, objectUrl);
1908
+ while (map.size > MAX_SNAPSHOTS) {
1909
+ const oldestId = map.keys().next().value;
1910
+ if (oldestId === void 0) break;
1911
+ revoke(map.get(oldestId));
1912
+ map.delete(oldestId);
1913
+ }
1914
+ notify2();
1915
+ }
1916
+ function clearTraySnapshot(id) {
1917
+ const { map } = state();
1918
+ const objectUrl = map.get(id);
1919
+ if (!objectUrl) return;
1920
+ map.delete(id);
1921
+ revoke(objectUrl);
1922
+ notify2();
1923
+ }
1924
+ function getTraySnapshot(id) {
1925
+ return state().map.get(id) ?? null;
1926
+ }
1927
+ function subscribeTraySnapshotMap(listener) {
1928
+ const { listeners } = state();
1929
+ listeners.add(listener);
1930
+ return () => {
1931
+ listeners.delete(listener);
1932
+ };
1933
+ }
1934
+
1935
+ // src/window/tray/defaultTraySnapshotCapture.ts
1936
+ var MAX_CAPTURE_ELEMENT_COUNT = 3e3;
1937
+ var TRANSPARENT_PIXEL = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
1938
+ async function loadToCanvas() {
1939
+ try {
1940
+ const mod = await import("html-to-image");
1941
+ return mod.toCanvas;
1942
+ } catch {
1943
+ return null;
1944
+ }
1945
+ }
1946
+ async function captureElementThumbnail(element, options = {}) {
1947
+ const toCanvas = await loadToCanvas();
1948
+ if (!toCanvas) return null;
1949
+ const sourceWidth = Math.max(1, element.offsetWidth);
1950
+ const sourceHeight = Math.max(1, element.offsetHeight);
1951
+ const maxEdge = Math.max(64, options.maxEdge ?? 320);
1952
+ const scale = Math.min(1, maxEdge / Math.max(sourceWidth, sourceHeight));
1953
+ const canvas = await toCanvas(element, {
1954
+ pixelRatio: 1,
1955
+ canvasWidth: Math.max(1, Math.round(sourceWidth * scale)),
1956
+ canvasHeight: Math.max(1, Math.round(sourceHeight * scale)),
1957
+ skipFonts: true,
1958
+ imagePlaceholder: TRANSPARENT_PIXEL,
1959
+ onImageErrorHandler: () => void 0
1960
+ });
1961
+ return new Promise((resolve) => {
1962
+ canvas.toBlob(resolve, "image/webp", options.quality ?? 0.62);
1963
+ });
1964
+ }
1965
+ async function defaultTraySnapshotCapture(bodyEl) {
1966
+ try {
1967
+ if (bodyEl.querySelectorAll("*").length > MAX_CAPTURE_ELEMENT_COUNT || bodyEl.offsetWidth < 8 || bodyEl.offsetHeight < 8) {
1968
+ return null;
1969
+ }
1970
+ const blob = await captureElementThumbnail(bodyEl);
1971
+ return blob && await isFlatImage(blob) ? null : blob;
1972
+ } catch {
1973
+ return null;
1974
+ }
1975
+ }
1976
+ async function isFlatImage(blob) {
1977
+ try {
1978
+ const bitmap = await createImageBitmap(blob);
1979
+ const size = 24;
1980
+ const canvas = document.createElement("canvas");
1981
+ canvas.width = size;
1982
+ canvas.height = size;
1983
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
1984
+ if (!ctx) return false;
1985
+ ctx.drawImage(bitmap, 0, 0, size, size);
1986
+ bitmap.close();
1987
+ const { data } = ctx.getImageData(0, 0, size, size);
1988
+ const r0 = data[0] ?? 0;
1989
+ const g0 = data[1] ?? 0;
1990
+ const b0 = data[2] ?? 0;
1991
+ const a0 = data[3] ?? 0;
1992
+ for (let i = 0; i < data.length; i += 4) {
1993
+ if (Math.abs((data[i] ?? 0) - r0) > 6 || Math.abs((data[i + 1] ?? 0) - g0) > 6 || Math.abs((data[i + 2] ?? 0) - b0) > 6 || Math.abs((data[i + 3] ?? 0) - a0) > 6) {
1994
+ return false;
1995
+ }
1996
+ }
1997
+ return true;
1998
+ } catch {
1999
+ return false;
2000
+ }
2001
+ }
2002
+
2003
+ // src/window/tray/MinimizedWindowContent.tsx
2004
+ import { getOverlayMeta as getOverlayMeta2 } from "@ai-matrx/summon";
2005
+
2006
+ // src/window/tray/TrayChipPreview.tsx
2007
+ import { Component, memo, useSyncExternalStore as useSyncExternalStore4 } from "react";
2008
+ import { cn as cn8 } from "@ai-matrx/design-system/cn";
2009
+ import { getOverlayMeta, selectOverlayData, DEFAULT_INSTANCE_ID } from "@ai-matrx/summon";
2010
+ import { useSummonPorts as useSummonPorts2, useSummonSelector as useSummonSelector4 } from "@ai-matrx/summon/react";
2011
+ import { getTrayPreviewEntry } from "@ai-matrx/summon/window-host";
2012
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
2013
+ function isPlainRecord(value) {
2014
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2015
+ }
2016
+ var TrayChipPreview = memo(function TrayChipPreview2({
2017
+ registryKey,
2018
+ snapshotKey,
2019
+ overlayInstanceId = DEFAULT_INSTANCE_ID,
2020
+ previewData,
2021
+ title
2022
+ }) {
2023
+ const meta = getOverlayMeta(registryKey);
2024
+ const { Preview } = getTrayPreviewEntry(registryKey);
2025
+ const ports = useSummonPorts2();
2026
+ const overlayData = useSummonSelector4((state2) => meta ? selectOverlayData(state2, meta.id, overlayInstanceId) : null);
2027
+ const collectedData = useSummonSelector4((state2) => state2.windowManager.windows[snapshotKey]?.persistence?.data);
2028
+ const registryLabel = meta?.label ?? null;
2029
+ const fallback = /* @__PURE__ */ jsx12(TraySnapshotImage, { snapshotKey, registryLabel, title });
2030
+ if (Preview) {
2031
+ const ctx = {
2032
+ data: previewData ?? collectedData ?? (isPlainRecord(overlayData) ? overlayData : {}),
2033
+ overlayId: meta?.id ?? registryKey,
2034
+ instanceId: overlayInstanceId,
2035
+ title
2036
+ };
2037
+ return /* @__PURE__ */ jsx12(
2038
+ PreviewBoundary,
2039
+ {
2040
+ fallback,
2041
+ onError: (error) => ports.errors.capture(error, { where: "tray-preview", overlayId: registryKey }),
2042
+ children: /* @__PURE__ */ jsx12("div", { className: "flex-1 px-3 py-1 overflow-hidden text-xs text-muted-foreground", children: /* @__PURE__ */ jsx12(Preview, { ...ctx }) })
2043
+ }
2044
+ );
2045
+ }
2046
+ return fallback;
2047
+ });
2048
+ var PreviewBoundary = class extends Component {
2049
+ state = { failed: false };
2050
+ static getDerivedStateFromError() {
2051
+ return { failed: true };
2052
+ }
2053
+ componentDidCatch(error) {
2054
+ this.props.onError(error);
2055
+ }
2056
+ render() {
2057
+ return this.state.failed ? this.props.fallback : this.props.children;
2058
+ }
2059
+ };
2060
+ var DefaultTrayChipBody = memo(function DefaultTrayChipBody2({ label }) {
2061
+ return /* @__PURE__ */ jsxs10("div", { className: "flex flex-1 flex-col justify-between overflow-hidden bg-gradient-to-br from-muted/45 via-background to-primary/5 px-3 py-2.5", children: [
2062
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 items-center gap-2.5", children: [
2063
+ /* @__PURE__ */ jsxs10(
2064
+ "div",
2065
+ {
2066
+ "aria-hidden": "true",
2067
+ className: "flex h-8 w-8 shrink-0 flex-col gap-1 rounded-md border border-border/60 bg-background/70 p-1.5 shadow-sm",
2068
+ children: [
2069
+ /* @__PURE__ */ jsx12("span", { className: "h-1 w-4 rounded-full bg-primary/45" }),
2070
+ /* @__PURE__ */ jsx12("span", { className: "h-1 w-3 rounded-full bg-muted-foreground/25" }),
2071
+ /* @__PURE__ */ jsx12("span", { className: "h-1 w-4 rounded-full bg-muted-foreground/15" })
2072
+ ]
2073
+ }
2074
+ ),
2075
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 truncate text-xs font-medium text-foreground", children: label })
2076
+ ] }),
2077
+ /* @__PURE__ */ jsxs10("div", { className: "matrx-window-tile-meta flex items-center justify-between gap-2 text-muted-foreground", children: [
2078
+ /* @__PURE__ */ jsx12("span", { className: "truncate uppercase tracking-wide", children: "Window" }),
2079
+ /* @__PURE__ */ jsx12("span", { className: "shrink-0", children: "Click to restore" })
2080
+ ] })
2081
+ ] });
2082
+ });
2083
+ var TraySnapshotImage = memo(function TraySnapshotImage2({
2084
+ snapshotKey,
2085
+ registryLabel,
2086
+ title
2087
+ }) {
2088
+ const snapshot = useSyncExternalStore4(
2089
+ subscribeTraySnapshotMap,
2090
+ () => getTraySnapshot(snapshotKey),
2091
+ () => null
2092
+ );
2093
+ if (!snapshot) return /* @__PURE__ */ jsx12(DefaultTrayChipBody, { label: registryLabel ?? title });
2094
+ return /* @__PURE__ */ jsx12("div", { className: "flex-1 overflow-hidden bg-muted/30 relative", children: /* @__PURE__ */ jsx12(
2095
+ "img",
2096
+ {
2097
+ src: snapshot,
2098
+ alt: "",
2099
+ className: cn8("w-full h-full object-cover", "opacity-90 hover:opacity-100 transition-opacity"),
2100
+ draggable: false
2101
+ }
2102
+ ) });
2103
+ });
2104
+
2105
+ // src/window/tray/MinimizedWindowContent.tsx
2106
+ import { jsx as jsx13 } from "react/jsx-runtime";
2107
+ function MinimizedWindowContent({
2108
+ registryKey,
2109
+ snapshotKey,
2110
+ overlayInstanceId,
2111
+ previewData,
2112
+ title,
2113
+ onRestore
2114
+ }) {
2115
+ const label = title ?? getOverlayMeta2(registryKey)?.label ?? "";
2116
+ return /* @__PURE__ */ jsx13(
2117
+ "button",
2118
+ {
2119
+ type: "button",
2120
+ onClick: onRestore,
2121
+ title: "Click to restore",
2122
+ className: "flex min-h-0 flex-1 flex-col text-left focus:outline-none",
2123
+ children: /* @__PURE__ */ jsx13(
2124
+ TrayChipPreview,
2125
+ {
2126
+ registryKey,
2127
+ snapshotKey,
2128
+ overlayInstanceId,
2129
+ previewData,
2130
+ title: label
2131
+ }
2132
+ )
2133
+ }
2134
+ );
2135
+ }
2136
+
2137
+ // src/window/WindowPanel.tsx
2138
+ import { getTrayPreviewEntry as getTrayPreviewEntry2 } from "@ai-matrx/summon/window-host";
2139
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
2140
+ var GLIDE_SETTLE_MS = 800;
2141
+ var HANDLES = [
2142
+ {
2143
+ edge: "e",
2144
+ className: "absolute right-0 top-2 bottom-2 w-2.5 translate-x-1/2 cursor-ew-resize"
2145
+ },
2146
+ {
2147
+ edge: "w",
2148
+ className: "absolute left-0 top-2 bottom-2 w-2.5 -translate-x-1/2 cursor-ew-resize"
2149
+ },
2150
+ {
2151
+ edge: "s",
2152
+ className: "absolute bottom-0 left-2 right-2 h-2.5 translate-y-1/2 cursor-ns-resize"
2153
+ },
2154
+ {
2155
+ // Start after the traffic-light hot zone (w-28) — top-left is close/minimize/maximize.
2156
+ edge: "n",
2157
+ className: "absolute top-0 left-2 right-2 h-2.5 -translate-y-1/2 cursor-ns-resize"
2158
+ },
2159
+ {
2160
+ edge: "se",
2161
+ className: "absolute bottom-0 right-0 w-2.5 h-2.5 -translate-x-1/4 -translate-y-1/4 cursor-nwse-resize"
2162
+ },
2163
+ {
2164
+ edge: "sw",
2165
+ className: "absolute bottom-0 left-0 w-2.5 h-2.5 -translate-x-1/4 -translate-y-1/4 cursor-nesw-resize"
2166
+ },
2167
+ {
2168
+ edge: "ne",
2169
+ className: "absolute top-0 right-0 w-2.5 h-2.5 -translate-x-1/4 -translate-y-1/4 cursor-nesw-resize"
2170
+ },
2171
+ {
2172
+ edge: "nw",
2173
+ className: "absolute top-0 left-0 w-2.5 h-2.5 -translate-x-1/4 -translate-y-1/4 cursor-nwse-resize"
2174
+ }
2175
+ ];
2176
+ function WindowPanelBodyShell({
2177
+ bodyRef,
2178
+ fitContent,
2179
+ isMaximized,
2180
+ bodyClassName,
2181
+ captureDimensions,
2182
+ children
2183
+ }) {
2184
+ return /* @__PURE__ */ jsx14(
2185
+ "div",
2186
+ {
2187
+ ref: bodyRef,
2188
+ className: cn9(
2189
+ // The structural guard ring is part of the body surface, not window
2190
+ // chrome. Painting it with the canonical page background keeps the
2191
+ // 2px resize-handle gutter visually continuous with full-bleed bodies
2192
+ // such as Chat instead of exposing the card-coloured window shell.
2193
+ "relative z-0 min-h-0 flex-1 overflow-hidden bg-background pointer-events-none",
2194
+ !isMaximized && "p-0.5",
2195
+ fitContent && "overflow-visible",
2196
+ captureDimensions && "matrx-window-body-offscreen"
2197
+ ),
2198
+ "data-window-panel-body-shell": true,
2199
+ style: captureDimensions ?? void 0,
2200
+ children: /* @__PURE__ */ jsx14(
2201
+ "div",
2202
+ {
2203
+ className: cn9(
2204
+ "h-full min-h-0 overflow-auto pointer-events-auto",
2205
+ !isMaximized && "matrx-window-body-inner",
2206
+ bodyClassName
2207
+ ),
2208
+ children
2209
+ }
2210
+ )
2211
+ }
2212
+ );
2213
+ }
2214
+ var MINIMIZED_TILE_Z_FLOOR = 5e3;
2215
+ function WindowPanel({
2216
+ children,
2217
+ title,
2218
+ titleNode,
2219
+ actions,
2220
+ actionsLeft,
2221
+ actionsRight,
2222
+ onClose,
2223
+ bodyClassName,
2224
+ className,
2225
+ mobilePresentationOverride,
2226
+ surfaceLayer,
2227
+ mobileSizeToContent,
2228
+ closeOnEscape = false,
2229
+ mobileHideClose = false,
2230
+ minWidth,
2231
+ minHeight,
2232
+ urlSyncKey,
2233
+ urlSyncId,
2234
+ urlSyncArgs,
2235
+ sidebar,
2236
+ sidebarDefaultSize = 200,
2237
+ sidebarMinSize = 100,
2238
+ defaultSidebarOpen = true,
2239
+ sidebarClassName,
2240
+ sidebarExpandsWindow = false,
2241
+ secondaryPanel,
2242
+ secondaryPanelOpen,
2243
+ secondaryPanelDefaultSize = 360,
2244
+ secondaryPanelMinSize = 240,
2245
+ secondaryPanelClassName,
2246
+ footer,
2247
+ footerLeft,
2248
+ footerCenter,
2249
+ footerRight,
2250
+ footerVariant = "bar",
2251
+ fitContent = false,
2252
+ overlayId: overlayIdProp,
2253
+ boundToOverlay,
2254
+ onCollectData,
2255
+ captureTraySnapshot,
2256
+ overlayInstanceId: overlayInstanceIdProp,
2257
+ hidePopOutButton = false,
2258
+ ...hookOpts
2259
+ }) {
2260
+ const instance = useOptionalOverlayInstance();
2261
+ const instanceMatches = instance !== null && (boundToOverlay === true || instance.overlayId === overlayIdProp);
2262
+ const overlayId = overlayIdProp ?? (boundToOverlay && instance ? instance.overlayId : void 0);
2263
+ const overlayInstanceId = overlayInstanceIdProp ?? (instanceMatches ? instance.instanceId : void 0);
2264
+ const ports = useSummonPorts3();
2265
+ const windowConfig = useWindowConfig();
2266
+ const reactId = useId2();
2267
+ const id = hookOpts.id ?? reactId;
2268
+ const trayRegistryKey = overlayId ?? id;
2269
+ const isMobile = useIsMobile();
2270
+ const surfaceLayerAttrs = surfaceLayer ? { "data-surface-layer": surfaceLayer } : void 0;
2271
+ const isMounted = useIsClient();
2272
+ const registryMeta = overlayId ? getOverlayMeta3(overlayId) : void 0;
2273
+ const registryEntry = registryMeta?.window;
2274
+ const effectiveInstanceId = overlayInstanceId ?? DEFAULT_INSTANCE_ID2;
2275
+ const [autoOpened] = useState5(
2276
+ () => Boolean(overlayId) && isAutoOpened(overlayId, effectiveInstanceId)
2277
+ );
2278
+ useEffect5(() => {
2279
+ if (!overlayId) return void 0;
2280
+ let timer;
2281
+ return () => {
2282
+ timer = window.setTimeout(
2283
+ () => clearAutoOpened(overlayId, effectiveInstanceId),
2284
+ 100
2285
+ );
2286
+ void timer;
2287
+ };
2288
+ }, [overlayId, effectiveInstanceId]);
2289
+ const preservationEnabled = Boolean(
2290
+ overlayId && registryEntry?.preservation && !registryEntry.ephemeral && (registryMeta?.instance !== "multi" || overlayInstanceId)
2291
+ );
2292
+ const overlayLaunchData = useSummonSelector5(
2293
+ (state2) => overlayId ? selectOverlayData2(state2, overlayId, effectiveInstanceId) : null
2294
+ );
2295
+ const initialPreservationData = registryEntry?.preservation ? sanitizeWindowData(overlayLaunchData, registryEntry.preservation.dataKeys) : {};
2296
+ const popoutCapability = isMounted ? detectPopoutCapability() : null;
2297
+ const popout = usePopoutWindow(id);
2298
+ useEffect5(() => {
2299
+ return registerPopoutOpener(id, popout.openPopout);
2300
+ }, [id, popout.openPopout]);
2301
+ const handleDragOutPopout = useCallback4(
2302
+ (draggedRect) => {
2303
+ void popout.openPopout({
2304
+ width: draggedRect.width,
2305
+ height: draggedRect.height,
2306
+ title: typeof title === "string" ? title : "Window"
2307
+ });
2308
+ },
2309
+ [popout, title]
2310
+ );
2311
+ const onTriggerPopout = !isMobile && popoutCapability !== "none" ? handleDragOutPopout : void 0;
2312
+ const autoDispatch = useSummonDispatch4();
2313
+ const {
2314
+ windowState,
2315
+ rect,
2316
+ zIndex: managerZIndex,
2317
+ onDragStart,
2318
+ onResizeStart,
2319
+ onFocus,
2320
+ onRestore,
2321
+ onMaximize,
2322
+ onMinimize,
2323
+ onToggleMaximize,
2324
+ isInteracting
2325
+ } = useWindowPanel({
2326
+ ...hookOpts,
2327
+ id,
2328
+ title,
2329
+ autoOpened,
2330
+ onAutoNoRoom: autoOpened ? () => {
2331
+ window.dispatchEvent(
2332
+ new CustomEvent(AUTO_OPEN_NO_ROOM_EVENT, { detail: { overlayId, instanceId: effectiveInstanceId } })
2333
+ );
2334
+ if (overlayId) autoDispatch(overlaysRawActions.closeOverlay({ overlayId, instanceId: effectiveInstanceId }));
2335
+ } : void 0,
2336
+ minWidth,
2337
+ minHeight,
2338
+ onTriggerPopout,
2339
+ ...preservationEnabled && overlayId ? {
2340
+ persistence: {
2341
+ overlayId,
2342
+ instanceId: effectiveInstanceId,
2343
+ data: initialPreservationData,
2344
+ sidebarOpen: defaultSidebarOpen,
2345
+ sidebarSize: sidebarDefaultSize
2346
+ }
2347
+ } : {}
2348
+ });
2349
+ const liftedZIndex = useFloatingLayerZIndex2(managerZIndex);
2350
+ const zIndex = windowState === "minimized" ? Math.max(liftedZIndex, MINIMIZED_TILE_Z_FLOOR + managerZIndex) : liftedZIndex;
2351
+ const dispatch = useSummonDispatch4();
2352
+ const windowsHidden = useSummonSelector5(selectWindowsHidden);
2353
+ const isDebugMode = useDebugFlag(ports.debug);
2354
+ useEffect5(() => {
2355
+ announceLayerFront(
2356
+ document.querySelector(`[data-window-id="${CSS.escape(id)}"]`)
2357
+ );
2358
+ }, [id, managerZIndex]);
2359
+ const claimFront = (event) => {
2360
+ announceLayerFront(event.currentTarget.closest("[data-window-id]") ?? event.currentTarget);
2361
+ };
2362
+ const allWindows = useSummonSelector5(selectAllWindows);
2363
+ const isTopWindow = !isMobile ? true : allWindows.find((w) => w.state !== "minimized")?.id === id;
2364
+ const [activePaneMobile, setActivePaneMobile] = useState5(
2365
+ "main"
2366
+ );
2367
+ const effectiveUrlSyncKey = resolveWindowUrlSyncKey(registryMeta?.link?.key, urlSyncKey);
2368
+ const effectiveUrlSyncId = urlSyncId ?? (effectiveUrlSyncKey ? overlayId : void 0);
2369
+ useOverlayLink(effectiveUrlSyncKey, effectiveUrlSyncId, urlSyncArgs);
2370
+ const deprecation = registryEntry?.deprecated;
2371
+ const isDeprecated = !!deprecation;
2372
+ const deprecatedRingClass = isDeprecated ? "ring-2 ring-destructive ring-offset-2 ring-offset-background" : "";
2373
+ const hasSidebar = !!sidebar;
2374
+ const [sidebarOpen, setSidebarOpen] = useState5(defaultSidebarOpen);
2375
+ const [sidebarSize, setSidebarSize] = useState5(
2376
+ sidebarDefaultSize
2377
+ );
2378
+ const restoredPersistence = useSummonSelector5(selectWindowPersistence(id));
2379
+ const sidebarPanelRef = useRef4(null);
2380
+ const restoredChromeAppliedRef = useRef4(false);
2381
+ const handleSaveWindowState = useCallback4(() => {
2382
+ if (!overlayId || !preservationEnabled || !registryEntry?.preservation) {
2383
+ return;
2384
+ }
2385
+ let collected = {};
2386
+ try {
2387
+ collected = onCollectData?.() ?? {};
2388
+ } catch (error) {
2389
+ ports.errors.capture(error, { where: "window-preservation collector", overlayId, instanceId: effectiveInstanceId });
2390
+ }
2391
+ const base = sanitizeWindowData({ ...initialPreservationData, ...collected }, registryEntry.preservation.dataKeys);
2392
+ dispatch(
2393
+ updateWindowPersistence({
2394
+ id,
2395
+ data: base,
2396
+ sidebarOpen,
2397
+ ...sidebarSize === null ? {} : { sidebarSize }
2398
+ })
2399
+ );
2400
+ }, [
2401
+ overlayId,
2402
+ preservationEnabled,
2403
+ registryEntry,
2404
+ effectiveInstanceId,
2405
+ initialPreservationData,
2406
+ id,
2407
+ sidebarOpen,
2408
+ sidebarSize,
2409
+ onCollectData,
2410
+ ports,
2411
+ dispatch
2412
+ ]);
2413
+ const saveRef = useRef4(handleSaveWindowState);
2414
+ useEffect5(() => {
2415
+ saveRef.current = handleSaveWindowState;
2416
+ }, [handleSaveWindowState]);
2417
+ useEffect5(() => {
2418
+ if (!preservationEnabled) return;
2419
+ saveRef.current();
2420
+ }, [preservationEnabled, overlayLaunchData, onCollectData]);
2421
+ const handleClose = useCallback4(() => {
2422
+ if (instanceMatches && instance && instance.overlayId === overlayId) {
2423
+ instance.close();
2424
+ } else if (boundToOverlay) {
2425
+ ports.errors.capture(new Error("[summon] WindowPanel boundToOverlay rendered outside an overlay instance"), { windowId: id });
2426
+ } else if (overlayId) {
2427
+ const hasExactOverlayIdentity = registryMeta?.instance !== "multi" || Boolean(overlayInstanceId);
2428
+ if (hasExactOverlayIdentity) {
2429
+ dispatch(overlaysRawActions.closeOverlay({ overlayId, instanceId: effectiveInstanceId }));
2430
+ } else if (!onClose) {
2431
+ ports.errors.capture(new Error(`[summon] Cannot close multi-instance ${overlayId} without overlayInstanceId or onClose.`), { overlayId });
2432
+ }
2433
+ }
2434
+ onClose?.();
2435
+ }, [boundToOverlay, dispatch, effectiveInstanceId, id, instance, instanceMatches, onClose, overlayId, overlayInstanceId, ports, registryMeta?.instance]);
2436
+ useEffect5(() => {
2437
+ if (!closeOnEscape || isMobile) return void 0;
2438
+ const onKey = (e) => {
2439
+ if (e.key !== "Escape" || e.defaultPrevented) return;
2440
+ const layered = Array.from(
2441
+ document.querySelectorAll(
2442
+ `[data-radix-popper-content-wrapper], [role="menu"], [role="listbox"], [role="dialog"][data-state="open"], [role="alertdialog"], ${windowConfig.escapeYieldSelector}`
2443
+ )
2444
+ ).some(
2445
+ (el2) => el2.getClientRects().length > 0 && getComputedStyle(el2).visibility !== "hidden" && !el2.closest(`[data-window-id="${CSS.escape(id)}"]`)
2446
+ );
2447
+ if (layered) return;
2448
+ const mine = document.querySelector(
2449
+ `[data-window-panel][data-window-id="${CSS.escape(id)}"]`
2450
+ );
2451
+ if (!mine) return;
2452
+ const z = (el2) => Number(getComputedStyle(el2).zIndex) || 0;
2453
+ const top = Array.from(
2454
+ document.querySelectorAll("[data-window-panel][data-window-id]")
2455
+ ).reduce((best, el2) => !best || z(el2) > z(best) ? el2 : best, null);
2456
+ if (top !== mine) return;
2457
+ e.preventDefault();
2458
+ handleClose();
2459
+ };
2460
+ document.addEventListener("keydown", onKey);
2461
+ return () => document.removeEventListener("keydown", onKey);
2462
+ }, [closeOnEscape, isMobile, id, handleClose, windowConfig.escapeYieldSelector]);
2463
+ useEffect5(() => {
2464
+ if (!restoredPersistence || restoredChromeAppliedRef.current) return;
2465
+ restoredChromeAppliedRef.current = true;
2466
+ setSidebarOpen(restoredPersistence.sidebarOpen);
2467
+ setSidebarSize(restoredPersistence.sidebarSize);
2468
+ if (!restoredPersistence.sidebarOpen) {
2469
+ sidebarPanelRef.current?.collapse();
2470
+ } else if (restoredPersistence.sidebarSize) {
2471
+ sidebarPanelRef.current?.resize(restoredPersistence.sidebarSize);
2472
+ }
2473
+ }, [restoredPersistence]);
2474
+ const [fitContentEl, fitContentRef] = useState5(null);
2475
+ const fitContentStore = useSummonStore2();
2476
+ const fitPlacedRef = useRef4(false);
2477
+ const [glideReady, setGlideReady] = useState5(false);
2478
+ useEffect5(() => {
2479
+ const timer = window.setTimeout(() => setGlideReady(true), GLIDE_SETTLE_MS);
2480
+ return () => window.clearTimeout(timer);
2481
+ }, []);
2482
+ useEffect5(() => {
2483
+ if (isInteracting) fitPlacedRef.current = true;
2484
+ }, [isInteracting]);
2485
+ const fitPresetPosition = hookOpts.position;
2486
+ const fitMayReplace = hookOpts.initialRect?.x === void 0 && hookOpts.initialRect?.y === void 0;
2487
+ useEffect5(() => {
2488
+ if (!fitContent || isMobile || windowState !== "windowed") return void 0;
2489
+ const el2 = fitContentEl;
2490
+ if (!el2) return void 0;
2491
+ let active = true;
2492
+ const ro = new ResizeObserver((entries) => {
2493
+ if (!active || fitContentStore.getState().windowManager.windows[id]?.state !== "windowed") return;
2494
+ const entry = entries[0];
2495
+ if (!entry) return;
2496
+ const { width, height } = entry.contentRect;
2497
+ const borderH = el2.offsetHeight - el2.clientHeight;
2498
+ const borderW = el2.offsetWidth - el2.clientWidth;
2499
+ const fitWidth = Math.ceil(width + borderW);
2500
+ const fitHeight = Math.ceil(height + borderH);
2501
+ const placeNow = !fitPlacedRef.current && fitMayReplace && fitPresetPosition !== void 0;
2502
+ dispatch(
2503
+ updateWindowRect2({
2504
+ id,
2505
+ rect: {
2506
+ width: fitWidth,
2507
+ height: fitHeight,
2508
+ ...placeNow ? resolvePosition(fitPresetPosition, fitWidth, fitHeight) : {}
2509
+ }
2510
+ })
2511
+ );
2512
+ });
2513
+ ro.observe(el2);
2514
+ return () => {
2515
+ active = false;
2516
+ ro.disconnect();
2517
+ };
2518
+ }, [fitContent, fitContentEl, isMobile, windowState, id, dispatch, fitContentStore, fitPresetPosition, fitMayReplace]);
2519
+ const toggleSidebar = useCallback4(() => {
2520
+ const panel = sidebarPanelRef.current;
2521
+ if (!panel) return;
2522
+ if (sidebarOpen) {
2523
+ if (sidebarExpandsWindow) {
2524
+ dispatch(
2525
+ updateWindowRect2({
2526
+ id,
2527
+ rect: { width: rect.width - sidebarDefaultSize }
2528
+ })
2529
+ );
2530
+ requestAnimationFrame(() => panel.collapse());
2531
+ } else {
2532
+ panel.collapse();
2533
+ }
2534
+ } else {
2535
+ if (sidebarExpandsWindow) {
2536
+ dispatch(
2537
+ updateWindowRect2({
2538
+ id,
2539
+ rect: { width: rect.width + sidebarDefaultSize }
2540
+ })
2541
+ );
2542
+ requestAnimationFrame(() => panel.resize(sidebarDefaultSize));
2543
+ } else {
2544
+ panel.resize(sidebarDefaultSize);
2545
+ }
2546
+ }
2547
+ }, [
2548
+ sidebarOpen,
2549
+ sidebarDefaultSize,
2550
+ sidebarExpandsWindow,
2551
+ dispatch,
2552
+ id,
2553
+ rect.width
2554
+ ]);
2555
+ const handleSidebarResize = useCallback4(
2556
+ (panelSize) => {
2557
+ setSidebarOpen(panelSize.asPercentage > 0);
2558
+ if (panelSize.inPixels > 0) setSidebarSize(panelSize.inPixels);
2559
+ if (preservationEnabled) {
2560
+ dispatch(
2561
+ updateWindowPersistence({
2562
+ id,
2563
+ sidebarOpen: panelSize.asPercentage > 0,
2564
+ sidebarSize: panelSize.inPixels > 0 ? panelSize.inPixels : sidebarSize
2565
+ })
2566
+ );
2567
+ }
2568
+ },
2569
+ [dispatch, id, preservationEnabled, sidebarSize]
2570
+ );
2571
+ const portalTarget = isMounted ? document.body : null;
2572
+ const [bodyHost] = useState5(() => {
2573
+ if (typeof document === "undefined") return null;
2574
+ const host = document.createElement("div");
2575
+ host.className = "matrx-window-body-host";
2576
+ host.setAttribute("data-window-body-host", "");
2577
+ return host;
2578
+ });
2579
+ useEffect5(() => {
2580
+ if (!overlayId) return void 0;
2581
+ ackOverlayRender(overlayId, id);
2582
+ return () => clearOverlayRender(overlayId, id);
2583
+ }, [overlayId, id]);
2584
+ const resolvedActionsRight = actionsRight ?? actions ?? null;
2585
+ const snapLeft = useCallback4(() => {
2586
+ const { vw, vh } = safeViewportDims2();
2587
+ dispatch(
2588
+ updateWindowRect2({
2589
+ id,
2590
+ rect: { x: 0, y: 0, width: Math.round(vw / 2), height: vh }
2591
+ })
2592
+ );
2593
+ }, [dispatch, id]);
2594
+ const snapRight = useCallback4(() => {
2595
+ const { vw, vh } = safeViewportDims2();
2596
+ const half = Math.round(vw / 2);
2597
+ dispatch(
2598
+ updateWindowRect2({
2599
+ id,
2600
+ rect: { x: half, y: 0, width: vw - half, height: vh }
2601
+ })
2602
+ );
2603
+ }, [dispatch, id]);
2604
+ const snapTop = useCallback4(() => {
2605
+ const { vw, vh } = safeViewportDims2();
2606
+ dispatch(
2607
+ updateWindowRect2({
2608
+ id,
2609
+ rect: { x: 0, y: 0, width: vw, height: Math.round(vh / 2) }
2610
+ })
2611
+ );
2612
+ }, [dispatch, id]);
2613
+ const snapBottom = useCallback4(() => {
2614
+ const { vw, vh } = safeViewportDims2();
2615
+ const half = Math.round(vh / 2);
2616
+ dispatch(
2617
+ updateWindowRect2({
2618
+ id,
2619
+ rect: { x: 0, y: half, width: vw, height: vh - half }
2620
+ })
2621
+ );
2622
+ }, [dispatch, id]);
2623
+ const snapCentre = useCallback4(() => {
2624
+ const { vw, vh } = safeViewportDims2();
2625
+ const w = Math.min(rect.width, vw);
2626
+ const h = Math.min(rect.height, vh);
2627
+ dispatch(
2628
+ updateWindowRect2({
2629
+ id,
2630
+ rect: {
2631
+ x: Math.round((vw - w) / 2),
2632
+ y: Math.round((vh - h) / 2),
2633
+ width: w,
2634
+ height: h
2635
+ }
2636
+ })
2637
+ );
2638
+ }, [dispatch, id, rect.width, rect.height]);
2639
+ const arrangeAll = useCallback4(
2640
+ (layout) => {
2641
+ const { vw, vh } = safeViewportDims2();
2642
+ dispatch(
2643
+ arrangeActiveWindows({
2644
+ layout,
2645
+ viewportWidth: vw,
2646
+ viewportHeight: vh
2647
+ })
2648
+ );
2649
+ },
2650
+ [dispatch]
2651
+ );
2652
+ const popoutMode = useSummonSelector5(selectPopoutMode3(id));
2653
+ const outerPopout = usePopout();
2654
+ const outerPortalContainer = usePortalContainer();
2655
+ const popoutWin = usePopoutWindowRef(id);
2656
+ const ownPopout = popoutMode !== null && popoutWin !== null;
2657
+ const bodyPopoutValue = React.useMemo(
2658
+ () => ownPopout && popoutWin ? { popoutDocument: popoutWin.document, popoutWindow: popoutWin, popoutContainer: popoutWin.document.body, mode: popoutMode } : outerPopout,
2659
+ [ownPopout, popoutWin, popoutMode, outerPopout]
2660
+ );
2661
+ const bodyPortalContainer = ownPopout && popoutWin ? popoutWin.document.body : outerPortalContainer;
2662
+ const popoutCandidateId = useSummonSelector5(selectPopoutCandidateId);
2663
+ const canShowPopOut = !hidePopOutButton && !isMobile && popoutCapability !== "none" && windowState !== "minimized" && popoutMode === null;
2664
+ const handlePopOut = useCallback4(() => {
2665
+ void popout.openPopout({
2666
+ width: rect.width,
2667
+ height: rect.height,
2668
+ title: typeof title === "string" ? title : "Window"
2669
+ });
2670
+ }, [popout, rect.width, rect.height, title]);
2671
+ const handleDockBack = useCallback4(() => {
2672
+ dispatch(dockWindow3(buildDockWindowPayload3(id)));
2673
+ }, [dispatch, id]);
2674
+ const bodyRef = useRef4(null);
2675
+ const retainedBodyDimensionsRef = useRef4(null);
2676
+ const captureGenerationRef = useRef4(0);
2677
+ const trayPreviewEntry = getTrayPreviewEntry2(trayRegistryKey);
2678
+ const effectiveTrayCapture = captureTraySnapshot ?? trayPreviewEntry.captureTraySnapshot ?? (trayPreviewEntry.Preview ? void 0 : defaultTraySnapshotCapture);
2679
+ const [pendingTrayCapture, setPendingTrayCapture] = useState5(null);
2680
+ const [trayPreviewData, setTrayPreviewData] = useState5(void 0);
2681
+ const handleMinimize = useCallback4(() => {
2682
+ let collected;
2683
+ try {
2684
+ collected = onCollectData?.();
2685
+ } catch (error) {
2686
+ ports.errors.capture(error, { where: "window preview collector", windowId: trayRegistryKey });
2687
+ }
2688
+ if (collected && typeof collected === "object") {
2689
+ setTrayPreviewData(collected);
2690
+ }
2691
+ if (preservationEnabled) saveRef.current();
2692
+ const bodyEl = bodyRef.current;
2693
+ const generation = ++captureGenerationRef.current;
2694
+ if (bodyEl) {
2695
+ const bounds = bodyEl.getBoundingClientRect();
2696
+ retainedBodyDimensionsRef.current = {
2697
+ width: Math.max(1, Math.round(bounds.width)),
2698
+ height: Math.max(1, Math.round(bounds.height))
2699
+ };
2700
+ }
2701
+ if (effectiveTrayCapture && bodyEl) {
2702
+ const bounds = bodyEl.getBoundingClientRect();
2703
+ setPendingTrayCapture({
2704
+ capture: effectiveTrayCapture,
2705
+ width: Math.max(1, Math.round(bounds.width)),
2706
+ height: Math.max(1, Math.round(bounds.height)),
2707
+ generation
2708
+ });
2709
+ } else {
2710
+ setPendingTrayCapture(null);
2711
+ }
2712
+ onMinimize();
2713
+ }, [
2714
+ effectiveTrayCapture,
2715
+ onCollectData,
2716
+ onMinimize,
2717
+ ports,
2718
+ preservationEnabled,
2719
+ trayRegistryKey
2720
+ ]);
2721
+ useEffect5(() => {
2722
+ if (windowState !== "minimized" || !pendingTrayCapture) return void 0;
2723
+ const bodyEl = bodyRef.current;
2724
+ if (!bodyEl) {
2725
+ setPendingTrayCapture(null);
2726
+ return void 0;
2727
+ }
2728
+ let cancelled = false;
2729
+ const { capture, generation } = pendingTrayCapture;
2730
+ let timeoutId;
2731
+ const timeout = new Promise((resolve) => {
2732
+ timeoutId = setTimeout(() => resolve(null), 800);
2733
+ });
2734
+ void Promise.race([capture(bodyEl), timeout]).then((blob) => {
2735
+ if (blob && !cancelled && captureGenerationRef.current === generation) {
2736
+ setTraySnapshot(id, blob);
2737
+ }
2738
+ }).catch((err) => {
2739
+ if (!cancelled) ports.errors.capture(err, { where: "captureTraySnapshot", windowId: id });
2740
+ }).finally(() => {
2741
+ if (timeoutId) clearTimeout(timeoutId);
2742
+ if (!cancelled && captureGenerationRef.current === generation) {
2743
+ setPendingTrayCapture(
2744
+ (current) => current?.generation === generation ? null : current
2745
+ );
2746
+ }
2747
+ });
2748
+ return () => {
2749
+ cancelled = true;
2750
+ if (timeoutId) clearTimeout(timeoutId);
2751
+ };
2752
+ }, [id, pendingTrayCapture, ports, windowState]);
2753
+ const handleRestoreClearingSnapshot = useCallback4(() => {
2754
+ captureGenerationRef.current += 1;
2755
+ setPendingTrayCapture(null);
2756
+ clearTraySnapshot(id);
2757
+ onRestore();
2758
+ }, [id, onRestore]);
2759
+ useEffect5(() => {
2760
+ return () => {
2761
+ captureGenerationRef.current += 1;
2762
+ clearTraySnapshot(id);
2763
+ };
2764
+ }, [id]);
2765
+ const previousTrayStateRef = useRef4(windowState);
2766
+ useEffect5(() => {
2767
+ const wasMinimized = previousTrayStateRef.current === "minimized";
2768
+ previousTrayStateRef.current = windowState;
2769
+ if (!wasMinimized || windowState === "minimized") return;
2770
+ const invalidGeneration = ++captureGenerationRef.current;
2771
+ clearTraySnapshot(id);
2772
+ queueMicrotask(() => {
2773
+ setPendingTrayCapture(
2774
+ (current) => current && current.generation < invalidGeneration ? null : current
2775
+ );
2776
+ });
2777
+ }, [id, windowState]);
2778
+ const prevStateRef = useRef4(windowState);
2779
+ const prevPopoutModeRef = useRef4(popoutMode);
2780
+ useEffect5(() => {
2781
+ const wasNonWindowed = prevStateRef.current !== "windowed" || prevPopoutModeRef.current !== null;
2782
+ const isNowWindowed = windowState === "windowed" && popoutMode === null;
2783
+ prevStateRef.current = windowState;
2784
+ prevPopoutModeRef.current = popoutMode;
2785
+ if (wasNonWindowed && isNowWindowed) {
2786
+ const { vw, vh, degenerate } = safeViewportDims2();
2787
+ if (degenerate) return;
2788
+ dispatch(clampWindowRect2({ id, viewportWidth: vw, viewportHeight: vh }));
2789
+ }
2790
+ }, [dispatch, id, windowState, popoutMode]);
2791
+ const isMinimized = windowState === "minimized";
2792
+ const isMaximized = windowState === "maximized";
2793
+ const isPopoutCandidate = popoutCandidateId === id;
2794
+ useEffect5(() => {
2795
+ if (!isMobile || !isMinimized) return;
2796
+ let cancelled = false;
2797
+ queueMicrotask(() => {
2798
+ if (!cancelled) handleRestoreClearingSnapshot();
2799
+ });
2800
+ return () => {
2801
+ cancelled = true;
2802
+ };
2803
+ }, [handleRestoreClearingSnapshot, isMinimized, isMobile]);
2804
+ const minimizedTitle = title?.trim() || getOverlayMeta3(trayRegistryKey)?.label || "Window";
2805
+ const header = /* @__PURE__ */ jsx14(
2806
+ WindowHeader,
2807
+ {
2808
+ title: titleNode ?? title,
2809
+ minimizedTitle,
2810
+ actionsLeft,
2811
+ actionsRight: resolvedActionsRight,
2812
+ onDragStart,
2813
+ onMinimize: handleMinimize,
2814
+ onToggleMaximize,
2815
+ onClose: handleClose,
2816
+ onRestore: handleRestoreClearingSnapshot,
2817
+ isMaximized,
2818
+ isMinimized,
2819
+ snapLeft,
2820
+ snapRight,
2821
+ snapTop,
2822
+ snapBottom,
2823
+ snapCentre,
2824
+ arrangeAll,
2825
+ hasSidebar,
2826
+ sidebarOpen,
2827
+ onToggleSidebar: toggleSidebar,
2828
+ onSaveWindowState: overlayId ? handleSaveWindowState : void 0,
2829
+ onPopOut: canShowPopOut ? handlePopOut : void 0
2830
+ }
2831
+ );
2832
+ const hasSecondaryPanel = secondaryPanel != null && (secondaryPanelOpen ?? true);
2833
+ const innerBody = hasSidebar || hasSecondaryPanel ? /* @__PURE__ */ jsxs11(ResizablePanelGroup, { orientation: "horizontal", className: "h-full min-h-0", children: [
2834
+ hasSidebar && /* @__PURE__ */ jsxs11(Fragment3, { children: [
2835
+ /* @__PURE__ */ jsx14(
2836
+ ResizablePanel,
2837
+ {
2838
+ id: "sidebar",
2839
+ panelRef: sidebarPanelRef,
2840
+ defaultSize: sidebarOpen ? sidebarDefaultSize : 0,
2841
+ minSize: sidebarMinSize,
2842
+ collapsible: true,
2843
+ collapsedSize: 0,
2844
+ groupResizeBehavior: "preserve-pixel-size",
2845
+ onResize: handleSidebarResize,
2846
+ style: { overflow: "hidden" },
2847
+ children: /* @__PURE__ */ jsx14(
2848
+ "div",
2849
+ {
2850
+ className: cn9(
2851
+ "h-full flex flex-col min-h-0 overflow-y-auto scrollbar-thin",
2852
+ sidebarClassName
2853
+ ),
2854
+ children: sidebar
2855
+ }
2856
+ )
2857
+ }
2858
+ ),
2859
+ /* @__PURE__ */ jsx14(
2860
+ ResizableHandle,
2861
+ {
2862
+ className: sidebarOpen ? void 0 : "w-0 opacity-0 pointer-events-none"
2863
+ }
2864
+ )
2865
+ ] }),
2866
+ /* @__PURE__ */ jsx14(ResizablePanel, { id: "body", minSize: 200, className: "min-h-0", children: /* @__PURE__ */ jsx14("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", children }) }),
2867
+ hasSecondaryPanel && /* @__PURE__ */ jsxs11(Fragment3, { children: [
2868
+ /* @__PURE__ */ jsx14(ResizableHandle, {}),
2869
+ /* @__PURE__ */ jsx14(
2870
+ ResizablePanel,
2871
+ {
2872
+ id: "secondary",
2873
+ defaultSize: secondaryPanelDefaultSize,
2874
+ minSize: secondaryPanelMinSize,
2875
+ groupResizeBehavior: "preserve-pixel-size",
2876
+ style: { overflow: "hidden" },
2877
+ children: /* @__PURE__ */ jsx14(
2878
+ "div",
2879
+ {
2880
+ className: cn9(
2881
+ "h-full flex flex-col min-h-0",
2882
+ secondaryPanelClassName
2883
+ ),
2884
+ children: secondaryPanel
2885
+ }
2886
+ )
2887
+ }
2888
+ )
2889
+ ] })
2890
+ ] }) : children;
2891
+ const bodyContent = /* @__PURE__ */ jsx14(WindowSelectionSurface, { children: isDeprecated ? /* @__PURE__ */ jsxs11("div", { className: "h-full flex flex-col min-h-0", children: [
2892
+ /* @__PURE__ */ jsx14(
2893
+ DeprecationBanner,
2894
+ {
2895
+ replacedBy: deprecation?.replacement,
2896
+ note: deprecation?.reason
2897
+ }
2898
+ ),
2899
+ /* @__PURE__ */ jsx14("div", { className: "flex-1 min-h-0", children: innerBody })
2900
+ ] }) : innerBody });
2901
+ const hasZonedFooter = footerLeft || footerCenter || footerRight;
2902
+ const hasFooter = footer || hasZonedFooter;
2903
+ const zonedFooter = /* @__PURE__ */ jsxs11(Fragment3, { children: [
2904
+ /* @__PURE__ */ jsx14("div", { className: "flex items-center gap-1 shrink-0", children: footerLeft }),
2905
+ /* @__PURE__ */ jsx14("div", { className: "flex-1 flex items-center justify-center gap-1", children: footerCenter }),
2906
+ /* @__PURE__ */ jsx14("div", { className: "flex items-center gap-1 shrink-0", children: footerRight })
2907
+ ] });
2908
+ const footerBar = hasFooter ? /* @__PURE__ */ jsx14(
2909
+ "div",
2910
+ {
2911
+ className: cn9(
2912
+ // Always: detach from scroll + separate from the body with a top border.
2913
+ "shrink-0 border-t border-border/50",
2914
+ WINDOW_CHROME_INTERACTIVE,
2915
+ footerVariant === "bar" && // Compact metadata-bar chrome — thin chips, tiny buttons/icons. Crushes
2916
+ // rich content; consumers with a composer/input bar pass footerVariant="rich".
2917
+ "flex items-center gap-1 px-2 py-1 rounded-b-xl bg-muted/40 select-none text-xs [&_svg]:h-3 [&_svg]:w-3 [&_button]:h-5 [&_button]:text-xs"
2918
+ ),
2919
+ onPointerDown: (e) => e.stopPropagation(),
2920
+ children: footer ?? (footerVariant === "bar" ? zonedFooter : /* @__PURE__ */ jsx14("div", { className: "flex items-center gap-1", children: zonedFooter }))
2921
+ }
2922
+ ) : null;
2923
+ const bodyPortal = isMounted && bodyHost && !isMobile ? createPortal3(
2924
+ /* @__PURE__ */ jsx14(PopoutContext.Provider, { value: bodyPopoutValue, children: /* @__PURE__ */ jsx14(PortalContainerProvider2, { container: bodyPortalContainer, children: /* @__PURE__ */ jsx14("div", { className: "contents", onPointerDown: onFocus, onPointerDownCapture: claimFront, onFocusCapture: claimFront, children: bodyContent }) }) }),
2925
+ bodyHost
2926
+ ) : null;
2927
+ if (isMobile) {
2928
+ const mobilePresentation = mobilePresentationOverride ?? registryEntry?.mobile ?? "fullscreen";
2929
+ const mobileSidebarAs = registryEntry?.mobileSidebarAs ?? "drawer";
2930
+ if (mobilePresentation === "hidden") {
2931
+ if (process.env.NODE_ENV !== "production" && overlayId) {
2932
+ console.warn(
2933
+ `[WindowPanel] overlay "${overlayId}" has window.mobile: "hidden" but was opened on a phone. Give its definition another presentation or gate opening.`
2934
+ );
2935
+ }
2936
+ return null;
2937
+ }
2938
+ if (mobilePresentation === "drawer") {
2939
+ return /* @__PURE__ */ jsx14(
2940
+ MobileDrawerSurface,
2941
+ {
2942
+ isOpen: true,
2943
+ title: titleNode ?? title,
2944
+ onClose: handleClose,
2945
+ sidebar,
2946
+ sidebarAs: mobileSidebarAs,
2947
+ footer: footerBar,
2948
+ actionsLeft,
2949
+ actionsRight: resolvedActionsRight,
2950
+ bodyClassName,
2951
+ surfaceLayer,
2952
+ sizeToContent: mobileSizeToContent,
2953
+ hideClose: mobileHideClose,
2954
+ children: /* @__PURE__ */ jsx14(WindowSelectionSurface, { children })
2955
+ }
2956
+ );
2957
+ }
2958
+ if (mobilePresentation === "card") {
2959
+ return /* @__PURE__ */ jsx14(
2960
+ MobileCardSurface,
2961
+ {
2962
+ isOpen: true,
2963
+ title: titleNode ?? title,
2964
+ onClose: handleClose,
2965
+ footer: footerBar,
2966
+ actionsRight: resolvedActionsRight,
2967
+ bodyClassName,
2968
+ surfaceLayer,
2969
+ children: /* @__PURE__ */ jsx14(WindowSelectionSurface, { children })
2970
+ }
2971
+ );
2972
+ }
2973
+ const mobileBody = hasSidebar && activePaneMobile === "sidebar" ? /* @__PURE__ */ jsx14("div", { className: cn9("h-full overflow-y-auto", sidebarClassName), children: sidebar }) : children;
2974
+ const mobileEl = /* @__PURE__ */ jsxs11(
2975
+ "div",
2976
+ {
2977
+ ...surfaceLayerAttrs,
2978
+ className: cn9(
2979
+ "fixed inset-0 flex flex-col",
2980
+ "bg-card/98 backdrop-blur-md",
2981
+ "overflow-hidden",
2982
+ deprecatedRingClass,
2983
+ className
2984
+ ),
2985
+ style: {
2986
+ top: windowConfig.mobileTopOffset,
2987
+ zIndex,
2988
+ display: isTopWindow && !isMinimized ? void 0 : "none",
2989
+ visibility: windowsHidden ? "hidden" : void 0
2990
+ },
2991
+ onPointerDown: onFocus,
2992
+ onPointerDownCapture: claimFront,
2993
+ onFocusCapture: claimFront,
2994
+ children: [
2995
+ /* @__PURE__ */ jsx14(
2996
+ MobileWindowHeader,
2997
+ {
2998
+ title: titleNode ?? title,
2999
+ actionsRight: resolvedActionsRight,
3000
+ onClose: handleClose,
3001
+ hasSidebar,
3002
+ activePaneMobile,
3003
+ onSetActivePane: setActivePaneMobile
3004
+ }
3005
+ ),
3006
+ isDebugMode && /* @__PURE__ */ jsx14(DebugStrip, { rect, zIndex }),
3007
+ /* @__PURE__ */ jsx14("div", { className: cn9("flex-1 overflow-auto min-h-0", bodyClassName), children: /* @__PURE__ */ jsx14(WindowSelectionSurface, { children: mobileBody }) }),
3008
+ footerBar
3009
+ ]
3010
+ }
3011
+ );
3012
+ return portalTarget ? createPortal3(/* @__PURE__ */ jsx14(FloatingLayer2, { children: mobileEl }), portalTarget) : null;
3013
+ }
3014
+ if (popoutMode !== null) {
3015
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
3016
+ /* @__PURE__ */ jsx14(PopoutPortal, { windowId: id, children: /* @__PURE__ */ jsxs11(
3017
+ "div",
3018
+ {
3019
+ ...surfaceLayerAttrs,
3020
+ className: "h-full w-full flex flex-col bg-card text-foreground",
3021
+ children: [
3022
+ /* @__PURE__ */ jsx14(
3023
+ PopoutTopBar,
3024
+ {
3025
+ title: titleNode ?? title,
3026
+ actionsRight: resolvedActionsRight,
3027
+ onDock: handleDockBack
3028
+ }
3029
+ ),
3030
+ isDebugMode && /* @__PURE__ */ jsx14(DebugStrip, { rect, zIndex }),
3031
+ /* @__PURE__ */ jsx14("div", { className: cn9("flex-1 overflow-auto min-h-0", bodyClassName), children: /* @__PURE__ */ jsx14(BodyMount, { host: bodyHost }) }),
3032
+ footerBar
3033
+ ]
3034
+ }
3035
+ ) }),
3036
+ bodyPortal
3037
+ ] });
3038
+ }
3039
+ const el = /* @__PURE__ */ jsxs11(
3040
+ "div",
3041
+ {
3042
+ ref: fitContent ? fitContentRef : void 0,
3043
+ "data-window-panel": "",
3044
+ "data-window-id": id,
3045
+ ...surfaceLayerAttrs,
3046
+ ...isMinimized ? { "data-matrx-floating-bottom": "" } : {},
3047
+ className: cn9(
3048
+ "fixed",
3049
+ isMaximized ? "overflow-hidden" : "overflow-visible",
3050
+ "matrx-window-enter",
3051
+ // Programmatic rect changes glide; pointer drag/resize stays 1:1.
3052
+ glideReady && !isInteracting && "matrx-window-glide",
3053
+ // Drag-out candidate: ring-2 highlight signals "release here to pop out".
3054
+ isPopoutCandidate && "ring-2 ring-primary ring-offset-2 ring-offset-background transition-shadow",
3055
+ deprecatedRingClass,
3056
+ className
3057
+ ),
3058
+ style: {
3059
+ ...isMaximized ? { inset: 0, width: "100vw", height: "100dvh" } : {
3060
+ left: rect.x,
3061
+ top: rect.y,
3062
+ ...fitContent && !isMinimized ? { width: "max-content", height: "auto" } : { width: rect.width, height: rect.height }
3063
+ },
3064
+ zIndex,
3065
+ // Cap CSS mins to the current rect so arrange/snap geometry wins.
3066
+ // Uncapped minWidth (e.g. 640) was overriding tile widths and stacking
3067
+ // every left-edge window on top of each other.
3068
+ minWidth: isMaximized || isMinimized ? 0 : fitContent ? minWidth ?? 180 : Math.min(minWidth ?? 180, rect.width),
3069
+ minHeight: isMaximized || isMinimized ? 0 : fitContent ? minHeight ?? 80 : Math.min(minHeight ?? 80, rect.height),
3070
+ visibility: windowsHidden ? "hidden" : void 0
3071
+ },
3072
+ onPointerDown: onFocus,
3073
+ onPointerDownCapture: claimFront,
3074
+ onFocusCapture: claimFront,
3075
+ onDoubleClick: isMinimized ? handleRestoreClearingSnapshot : void 0,
3076
+ children: [
3077
+ !isMinimized && !isMaximized && HANDLES.map((h) => /* @__PURE__ */ jsx14(
3078
+ "div",
3079
+ {
3080
+ className: cn9(
3081
+ h.className,
3082
+ "z-50 hover:bg-primary/20 transition-colors"
3083
+ ),
3084
+ style: { touchAction: "none" },
3085
+ onPointerDown: onResizeStart(h.edge)
3086
+ },
3087
+ h.edge
3088
+ )),
3089
+ /* @__PURE__ */ jsxs11(
3090
+ "div",
3091
+ {
3092
+ className: cn9(
3093
+ "flex h-full w-full min-h-0 flex-col",
3094
+ // Opaque window surface: a window is a solid card, never glass —
3095
+ // glass belongs only on bars floating over moving content (tap-target
3096
+ // placement rule 1). A see-through window let the page underneath
3097
+ // read through its content (Watch AI, 2026-10-12).
3098
+ isMaximized ? "bg-card border border-border shadow-2xl" : "rounded-xl bg-card border border-border shadow-xl",
3099
+ "overflow-hidden"
3100
+ ),
3101
+ children: [
3102
+ header,
3103
+ isDebugMode && !isMinimized && /* @__PURE__ */ jsx14(DebugStrip, { rect, zIndex }),
3104
+ /* @__PURE__ */ jsx14(
3105
+ WindowPanelBodyShell,
3106
+ {
3107
+ bodyRef,
3108
+ fitContent,
3109
+ isMaximized,
3110
+ bodyClassName,
3111
+ captureDimensions: isMinimized ? pendingTrayCapture ? {
3112
+ width: pendingTrayCapture.width,
3113
+ height: pendingTrayCapture.height
3114
+ } : retainedBodyDimensionsRef.current ?? {
3115
+ width: Math.max(1, rect.width),
3116
+ height: Math.max(1, rect.height)
3117
+ } : null,
3118
+ children: /* @__PURE__ */ jsx14(BodyMount, { host: bodyHost })
3119
+ }
3120
+ ),
3121
+ isMinimized && /* @__PURE__ */ jsx14(
3122
+ MinimizedWindowContent,
3123
+ {
3124
+ registryKey: trayRegistryKey,
3125
+ snapshotKey: id,
3126
+ overlayInstanceId,
3127
+ previewData: trayPreviewData,
3128
+ title: minimizedTitle,
3129
+ onRestore: handleRestoreClearingSnapshot
3130
+ }
3131
+ ),
3132
+ !isMinimized && footerBar,
3133
+ isPopoutCandidate && /* @__PURE__ */ jsx14("div", { className: "absolute inset-0 z-30 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsx14("div", { className: "px-3 py-1.5 rounded-md bg-primary text-primary-foreground text-xs font-medium shadow-lg", children: "Release to pop out" }) })
3134
+ ]
3135
+ }
3136
+ )
3137
+ ]
3138
+ }
3139
+ );
3140
+ return /* @__PURE__ */ jsxs11(Fragment3, { children: [
3141
+ portalTarget ? createPortal3(/* @__PURE__ */ jsx14(FloatingLayer2, { children: el }), portalTarget) : null,
3142
+ bodyPortal
3143
+ ] });
3144
+ }
3145
+ function BodyMount({ host }) {
3146
+ const slotRef = useRef4(null);
3147
+ useLayoutEffect(() => {
3148
+ const slot2 = slotRef.current;
3149
+ if (!slot2 || !host) return void 0;
3150
+ slot2.appendChild(host);
3151
+ return () => {
3152
+ if (host.parentNode === slot2) slot2.removeChild(host);
3153
+ };
3154
+ }, [host]);
3155
+ return /* @__PURE__ */ jsx14("div", { ref: slotRef, className: "contents", "data-window-body-slot": "" });
3156
+ }
3157
+ function PopOutHeaderButton({ onClick }) {
3158
+ return /* @__PURE__ */ jsx14(
3159
+ "button",
3160
+ {
3161
+ type: "button",
3162
+ className: "p-0.5 rounded hover:bg-accent/60 transition-colors text-foreground/60 hover:text-foreground cursor-pointer",
3163
+ onClick,
3164
+ onPointerDown: (e) => e.stopPropagation(),
3165
+ title: "Pop out into a separate window",
3166
+ "aria-label": "Pop out into a separate window",
3167
+ children: /* @__PURE__ */ jsx14(AppWindowIcon, { className: "w-3.5 h-3.5" })
3168
+ }
3169
+ );
3170
+ }
3171
+ function WindowHeader({
3172
+ title,
3173
+ minimizedTitle,
3174
+ actionsLeft,
3175
+ actionsRight,
3176
+ onDragStart,
3177
+ onMinimize,
3178
+ onToggleMaximize,
3179
+ onRestore,
3180
+ onClose,
3181
+ isMaximized,
3182
+ isMinimized,
3183
+ snapLeft,
3184
+ snapRight,
3185
+ snapTop,
3186
+ snapBottom,
3187
+ snapCentre,
3188
+ arrangeAll,
3189
+ hasSidebar,
3190
+ sidebarOpen,
3191
+ onToggleSidebar,
3192
+ onSaveWindowState,
3193
+ onPopOut
3194
+ }) {
3195
+ return /* @__PURE__ */ jsxs11(
3196
+ "div",
3197
+ {
3198
+ className: cn9(
3199
+ // 🚨 NEW-8 — the header is a CONTAINER, so a consumer's title/actions can
3200
+ // answer the WINDOW's width instead of the viewport's (`sm:` cannot know
3201
+ // a window is 360px on a 1440px screen — that is how D4 stayed invisible).
3202
+ // The name is `window-header`; `lib/detail/core/headerGeometry.ts` holds it.
3203
+ "@container/window-header",
3204
+ // Open: three in-flow columns — left zone, title, right zone. The two
3205
+ // side columns share the free space equally (never less than their
3206
+ // real content), so the title sits at the WINDOW's centre whatever
3207
+ // each side carries, and still truncates instead of running under an
3208
+ // action cluster when space is tight (D4). Minimized has no title
3209
+ // column, so it keeps the plain row.
3210
+ isMinimized ? "matrx-window-header-row relative flex items-center justify-between gap-1 px-2 py-1 z-20 shrink-0" : "matrx-window-header-grid relative items-center gap-1 px-2 py-1 z-20 shrink-0",
3211
+ "border-b border-border/50 bg-muted/40 select-none",
3212
+ isMaximized || isMinimized ? "cursor-default" : "cursor-grab active:cursor-grabbing",
3213
+ isMinimized && "h-8 min-h-8 py-0 border-b border-border/50"
3214
+ ),
3215
+ "data-window-panel-state": isMinimized ? "minimized" : void 0,
3216
+ style: isMaximized || isMinimized ? void 0 : { touchAction: "none" },
3217
+ onPointerDown: isMaximized || isMinimized ? void 0 : onDragStart,
3218
+ children: [
3219
+ /* @__PURE__ */ jsx14(
3220
+ "div",
3221
+ {
3222
+ className: cn9(
3223
+ "group/tl absolute top-0 left-0 bottom-0 flex items-center z-20",
3224
+ hasSidebar ? "w-28" : "w-24"
3225
+ ),
3226
+ onPointerDown: (e) => e.stopPropagation(),
3227
+ children: /* @__PURE__ */ jsx14("div", { className: "pl-2", children: /* @__PURE__ */ jsx14(
3228
+ TrafficLightGroup,
3229
+ {
3230
+ isMinimized,
3231
+ isMaximized,
3232
+ onClose,
3233
+ onMinimize,
3234
+ onRestore,
3235
+ onToggleMaximize,
3236
+ snapLeft,
3237
+ snapRight,
3238
+ snapTop,
3239
+ snapBottom,
3240
+ snapCentre,
3241
+ arrangeAll,
3242
+ hasSidebar,
3243
+ sidebarOpen,
3244
+ onToggleSidebar,
3245
+ onSaveWindowState,
3246
+ onPopOut
3247
+ }
3248
+ ) })
3249
+ }
3250
+ ),
3251
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 z-10 shrink-0", children: [
3252
+ /* @__PURE__ */ jsx14("div", { className: hasSidebar ? "w-28" : "w-24" }),
3253
+ !isMinimized && actionsLeft && /* @__PURE__ */ jsx14(
3254
+ "div",
3255
+ {
3256
+ className: WINDOW_CHROME_ACTIONS,
3257
+ onPointerDown: (e) => e.stopPropagation(),
3258
+ children: actionsLeft
3259
+ }
3260
+ )
3261
+ ] }),
3262
+ isMinimized ? /* @__PURE__ */ jsx14("div", { className: "absolute top-0 bottom-0 left-20 right-2 flex items-center justify-start pointer-events-none", children: /* @__PURE__ */ jsx14(
3263
+ "span",
3264
+ {
3265
+ className: "matrx-window-minimized-title block min-w-0 truncate leading-none font-semibold text-foreground",
3266
+ "data-minimized-window-title": true,
3267
+ children: minimizedTitle
3268
+ }
3269
+ ) }) : null,
3270
+ !isMinimized && /* @__PURE__ */ jsx14("div", { className: "flex min-w-0 flex-1 items-center justify-center overflow-hidden", children: typeof title === "string" || title == null ? /* @__PURE__ */ jsx14("span", { className: "min-w-0 truncate text-xs font-medium text-foreground/80", children: title ?? "" }) : /* @__PURE__ */ jsx14(
3271
+ "div",
3272
+ {
3273
+ className: "flex min-w-0 max-w-full items-center overflow-hidden whitespace-nowrap",
3274
+ onPointerDown: (event) => {
3275
+ const target = event.target;
3276
+ if (!(target instanceof Element)) return;
3277
+ if (target.closest(
3278
+ 'a, button, input, select, textarea, [role="button"], [role="menuitem"], [data-window-no-drag]'
3279
+ )) {
3280
+ event.stopPropagation();
3281
+ }
3282
+ },
3283
+ children: title
3284
+ }
3285
+ ) }),
3286
+ /* @__PURE__ */ jsx14("div", { className: "flex items-center justify-end gap-1 z-10 shrink-0", children: !isMinimized && (onPopOut || actionsRight) && /* @__PURE__ */ jsxs11(
3287
+ "div",
3288
+ {
3289
+ className: WINDOW_CHROME_ACTIONS,
3290
+ onPointerDown: (e) => e.stopPropagation(),
3291
+ children: [
3292
+ onPopOut ? /* @__PURE__ */ jsx14(PopOutHeaderButton, { onClick: onPopOut }) : null,
3293
+ actionsRight
3294
+ ]
3295
+ }
3296
+ ) })
3297
+ ]
3298
+ }
3299
+ );
3300
+ }
3301
+ function TrafficLightGroup({
3302
+ isMinimized,
3303
+ isMaximized,
3304
+ onClose,
3305
+ onMinimize,
3306
+ onRestore,
3307
+ onToggleMaximize,
3308
+ snapLeft,
3309
+ snapRight,
3310
+ snapTop,
3311
+ snapBottom,
3312
+ snapCentre,
3313
+ arrangeAll,
3314
+ hasSidebar,
3315
+ sidebarOpen,
3316
+ onToggleSidebar,
3317
+ onSaveWindowState,
3318
+ onPopOut
3319
+ }) {
3320
+ return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5 shrink-0 cursor-default", children: [
3321
+ /* @__PURE__ */ jsx14(
3322
+ TrafficLight,
3323
+ {
3324
+ color: "red",
3325
+ icon: /* @__PURE__ */ jsx14(XIcon, { className: "w-2 h-2 matrx-window-light-glyph" }),
3326
+ onClick: onClose ?? void 0,
3327
+ disabled: !onClose,
3328
+ "aria-label": "Close"
3329
+ }
3330
+ ),
3331
+ /* @__PURE__ */ jsx14(
3332
+ TrafficLight,
3333
+ {
3334
+ color: "yellow",
3335
+ icon: isMinimized ? /* @__PURE__ */ jsx14(
3336
+ Maximize2Icon,
3337
+ {
3338
+ className: "w-2 h-2 matrx-window-light-glyph"
3339
+ }
3340
+ ) : /* @__PURE__ */ jsx14(
3341
+ MinusIcon,
3342
+ {
3343
+ className: "w-2 h-2 matrx-window-light-glyph"
3344
+ }
3345
+ ),
3346
+ onClick: isMinimized ? onRestore : onMinimize,
3347
+ "aria-label": isMinimized ? "Restore" : "Minimize"
3348
+ }
3349
+ ),
3350
+ !isMinimized && /* @__PURE__ */ jsx14(
3351
+ GreenTrafficLight,
3352
+ {
3353
+ isMaximized,
3354
+ onToggleMaximize,
3355
+ onRestore,
3356
+ snapLeft,
3357
+ snapRight,
3358
+ snapTop,
3359
+ snapBottom,
3360
+ snapCentre,
3361
+ arrangeAll,
3362
+ onSaveWindowState,
3363
+ onPopOut
3364
+ }
3365
+ ),
3366
+ hasSidebar && !isMinimized && /* @__PURE__ */ jsx14(
3367
+ "button",
3368
+ {
3369
+ type: "button",
3370
+ className: "ml-0.5 p-0.5 rounded hover:bg-accent/60 transition-colors text-foreground/60 group-hover/tl:text-foreground cursor-pointer",
3371
+ onClick: onToggleSidebar,
3372
+ title: sidebarOpen ? "Hide sidebar" : "Show sidebar",
3373
+ "aria-label": sidebarOpen ? "Hide sidebar" : "Show sidebar",
3374
+ children: sidebarOpen ? /* @__PURE__ */ jsx14(PanelLeftCloseIcon, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsx14(PanelLeftIcon, { className: "w-3.5 h-3.5" })
3375
+ }
3376
+ )
3377
+ ] });
3378
+ }
3379
+ function TrafficLight({
3380
+ color,
3381
+ icon,
3382
+ onClick,
3383
+ disabled,
3384
+ "aria-label": label
3385
+ }) {
3386
+ const base = "w-3.5 h-3.5 rounded-full flex items-center justify-center transition-colors shrink-0 relative";
3387
+ const colours = color === "red" ? disabled ? "matrx-window-light-disabled cursor-default" : "matrx-window-light-close cursor-pointer" : "matrx-window-light-minimize cursor-pointer";
3388
+ return /* @__PURE__ */ jsx14(
3389
+ "button",
3390
+ {
3391
+ type: "button",
3392
+ className: cn9(base, colours),
3393
+ onClick: disabled ? void 0 : onClick,
3394
+ onPointerDown: (e) => e.stopPropagation(),
3395
+ "aria-label": label,
3396
+ disabled,
3397
+ children: /* @__PURE__ */ jsx14(
3398
+ "span",
3399
+ {
3400
+ className: cn9(
3401
+ "opacity-0 transition-opacity duration-100",
3402
+ !disabled && "group-hover/tl:opacity-100"
3403
+ ),
3404
+ children: icon
3405
+ }
3406
+ )
3407
+ }
3408
+ );
3409
+ }
3410
+ function GreenTrafficLight({
3411
+ isMaximized,
3412
+ onToggleMaximize,
3413
+ onRestore,
3414
+ snapLeft,
3415
+ snapRight,
3416
+ snapTop,
3417
+ snapBottom,
3418
+ snapCentre,
3419
+ arrangeAll,
3420
+ onSaveWindowState,
3421
+ onPopOut
3422
+ }) {
3423
+ const [dropdownOpen, setDropdownOpen] = useState5(false);
3424
+ const leaveTimer = useRef4(null);
3425
+ const containerRef = useRef4(null);
3426
+ const openDropdown = () => {
3427
+ if (leaveTimer.current) clearTimeout(leaveTimer.current);
3428
+ setDropdownOpen(true);
3429
+ };
3430
+ const scheduleClose = () => {
3431
+ leaveTimer.current = setTimeout(() => {
3432
+ setDropdownOpen(false);
3433
+ }, 120);
3434
+ };
3435
+ const handleAction = (fn) => {
3436
+ fn();
3437
+ setDropdownOpen(false);
3438
+ };
3439
+ useEffect5(() => {
3440
+ if (!dropdownOpen) return void 0;
3441
+ const onPointerOutside = (e) => {
3442
+ const target = e.target;
3443
+ if (containerRef.current && !containerRef.current.contains(target)) {
3444
+ if (target.closest?.("[data-radix-portal]")) return;
3445
+ setDropdownOpen(false);
3446
+ }
3447
+ };
3448
+ document.addEventListener("pointerdown", onPointerOutside);
3449
+ return () => document.removeEventListener("pointerdown", onPointerOutside);
3450
+ }, [dropdownOpen]);
3451
+ return /* @__PURE__ */ jsxs11(
3452
+ "div",
3453
+ {
3454
+ ref: containerRef,
3455
+ className: "relative",
3456
+ onMouseEnter: openDropdown,
3457
+ onMouseLeave: scheduleClose,
3458
+ children: [
3459
+ /* @__PURE__ */ jsx14(
3460
+ "button",
3461
+ {
3462
+ type: "button",
3463
+ className: cn9(
3464
+ "w-3.5 h-3.5 rounded-full flex items-center justify-center transition-colors shrink-0",
3465
+ "matrx-window-light-maximize cursor-pointer"
3466
+ ),
3467
+ onClick: (e) => {
3468
+ if ("ontouchstart" in window || navigator.maxTouchPoints > 0) {
3469
+ setDropdownOpen((prev) => !prev);
3470
+ } else {
3471
+ handleAction(onToggleMaximize);
3472
+ }
3473
+ },
3474
+ onPointerDown: (e) => e.stopPropagation(),
3475
+ "aria-label": isMaximized ? "Restore" : "Maximize",
3476
+ children: /* @__PURE__ */ jsx14("span", { className: "opacity-0 group-hover/tl:opacity-100 transition-opacity duration-100 flex items-center justify-center relative w-full h-full", children: isMaximized ? /* @__PURE__ */ jsx14(
3477
+ Minimize2Icon,
3478
+ {
3479
+ className: "w-2 h-2 matrx-window-light-glyph absolute"
3480
+ }
3481
+ ) : /* @__PURE__ */ jsx14(
3482
+ Maximize2Icon,
3483
+ {
3484
+ className: "w-2 h-2 matrx-window-light-glyph absolute"
3485
+ }
3486
+ ) })
3487
+ }
3488
+ ),
3489
+ dropdownOpen && /* @__PURE__ */ jsxs11(
3490
+ "div",
3491
+ {
3492
+ className: cn9(
3493
+ "absolute left-0 top-full mt-1.5 z-50",
3494
+ "w-52 rounded-xl overflow-hidden",
3495
+ "bg-card/95 backdrop-blur-xl border border-border shadow-2xl",
3496
+ "py-1 text-xs"
3497
+ ),
3498
+ onMouseEnter: openDropdown,
3499
+ onMouseLeave: scheduleClose,
3500
+ children: [
3501
+ !isMaximized && /* @__PURE__ */ jsxs11(Fragment3, { children: [
3502
+ /* @__PURE__ */ jsx14("div", { className: "matrx-window-tile-meta px-3 pt-1.5 pb-1 font-semibold text-muted-foreground uppercase tracking-wider", children: "Move & Resize" }),
3503
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-1 px-2 pb-2", children: [
3504
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1 justify-center", children: [
3505
+ /* @__PURE__ */ jsx14(
3506
+ LayoutIconButton,
3507
+ {
3508
+ onClick: () => handleAction(snapLeft),
3509
+ type: "left-half"
3510
+ }
3511
+ ),
3512
+ /* @__PURE__ */ jsx14(
3513
+ LayoutIconButton,
3514
+ {
3515
+ onClick: () => handleAction(snapRight),
3516
+ type: "right-half"
3517
+ }
3518
+ ),
3519
+ /* @__PURE__ */ jsx14(
3520
+ LayoutIconButton,
3521
+ {
3522
+ onClick: () => handleAction(snapTop),
3523
+ type: "top-half"
3524
+ }
3525
+ ),
3526
+ /* @__PURE__ */ jsx14(
3527
+ LayoutIconButton,
3528
+ {
3529
+ onClick: () => handleAction(snapBottom),
3530
+ type: "bottom-half"
3531
+ }
3532
+ )
3533
+ ] }),
3534
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1 justify-center", children: [
3535
+ /* @__PURE__ */ jsx14(
3536
+ LayoutIconButton,
3537
+ {
3538
+ onClick: () => handleAction(snapCentre),
3539
+ type: "centre"
3540
+ }
3541
+ ),
3542
+ /* @__PURE__ */ jsx14(
3543
+ LayoutIconButton,
3544
+ {
3545
+ onClick: () => handleAction(onToggleMaximize),
3546
+ type: "full"
3547
+ }
3548
+ )
3549
+ ] })
3550
+ ] }),
3551
+ /* @__PURE__ */ jsx14("div", { className: "border-t border-border/50 my-1" }),
3552
+ /* @__PURE__ */ jsx14("div", { className: "matrx-window-tile-meta px-3 pt-1.5 pb-1 font-semibold text-muted-foreground uppercase tracking-wider", children: "Arrange All" }),
3553
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-1 px-2 pb-2", children: [
3554
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1 justify-center", children: [
3555
+ /* @__PURE__ */ jsx14(
3556
+ LayoutIconButton,
3557
+ {
3558
+ onClick: () => handleAction(() => arrangeAll("grid4")),
3559
+ type: "grid4"
3560
+ }
3561
+ ),
3562
+ /* @__PURE__ */ jsx14(
3563
+ LayoutIconButton,
3564
+ {
3565
+ onClick: () => handleAction(() => arrangeAll("grid6")),
3566
+ type: "grid6"
3567
+ }
3568
+ ),
3569
+ /* @__PURE__ */ jsx14(
3570
+ LayoutIconButton,
3571
+ {
3572
+ onClick: () => handleAction(() => arrangeAll("grid8")),
3573
+ type: "grid8"
3574
+ }
3575
+ ),
3576
+ /* @__PURE__ */ jsx14(
3577
+ LayoutIconButton,
3578
+ {
3579
+ onClick: () => handleAction(() => arrangeAll("grid9")),
3580
+ type: "grid9"
3581
+ }
3582
+ ),
3583
+ /* @__PURE__ */ jsx14(
3584
+ LayoutIconButton,
3585
+ {
3586
+ onClick: () => handleAction(() => arrangeAll("grid12")),
3587
+ type: "grid12"
3588
+ }
3589
+ )
3590
+ ] }),
3591
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1 justify-center", children: [
3592
+ /* @__PURE__ */ jsx14(
3593
+ LayoutIconButton,
3594
+ {
3595
+ onClick: () => handleAction(() => arrangeAll("stackRight2")),
3596
+ type: "stackRight2"
3597
+ }
3598
+ ),
3599
+ /* @__PURE__ */ jsx14(
3600
+ LayoutIconButton,
3601
+ {
3602
+ onClick: () => handleAction(() => arrangeAll("stackRight3")),
3603
+ type: "stackRight3"
3604
+ }
3605
+ ),
3606
+ /* @__PURE__ */ jsx14(
3607
+ LayoutIconButton,
3608
+ {
3609
+ onClick: () => handleAction(() => arrangeAll("stackRight4")),
3610
+ type: "stackRight4"
3611
+ }
3612
+ ),
3613
+ /* @__PURE__ */ jsx14(
3614
+ LayoutIconButton,
3615
+ {
3616
+ onClick: () => handleAction(() => arrangeAll("stackRight5")),
3617
+ type: "stackRight5"
3618
+ }
3619
+ )
3620
+ ] }),
3621
+ /* @__PURE__ */ jsxs11("div", { className: "flex gap-1 justify-center", children: [
3622
+ /* @__PURE__ */ jsx14(
3623
+ LayoutIconButton,
3624
+ {
3625
+ onClick: () => handleAction(() => arrangeAll("stackLeft2")),
3626
+ type: "stackLeft2"
3627
+ }
3628
+ ),
3629
+ /* @__PURE__ */ jsx14(
3630
+ LayoutIconButton,
3631
+ {
3632
+ onClick: () => handleAction(() => arrangeAll("stackLeft3")),
3633
+ type: "stackLeft3"
3634
+ }
3635
+ ),
3636
+ /* @__PURE__ */ jsx14(
3637
+ LayoutIconButton,
3638
+ {
3639
+ onClick: () => handleAction(() => arrangeAll("stackLeft4")),
3640
+ type: "stackLeft4"
3641
+ }
3642
+ ),
3643
+ /* @__PURE__ */ jsx14(
3644
+ LayoutIconButton,
3645
+ {
3646
+ onClick: () => handleAction(() => arrangeAll("stackLeft5")),
3647
+ type: "stackLeft5"
3648
+ }
3649
+ )
3650
+ ] })
3651
+ ] }),
3652
+ /* @__PURE__ */ jsx14("div", { className: "border-t border-border/50 my-1" })
3653
+ ] }),
3654
+ /* @__PURE__ */ jsx14(
3655
+ "button",
3656
+ {
3657
+ type: "button",
3658
+ className: "flex items-center gap-2.5 w-full px-3 py-1.5 hover:bg-accent transition-colors text-foreground/80",
3659
+ onClick: () => handleAction(onToggleMaximize),
3660
+ onPointerDown: (e) => e.stopPropagation(),
3661
+ children: isMaximized ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
3662
+ /* @__PURE__ */ jsx14(Minimize2Icon, { className: "w-3.5 h-3.5 shrink-0" }),
3663
+ " Exit Full Screen"
3664
+ ] }) : /* @__PURE__ */ jsxs11(Fragment3, { children: [
3665
+ /* @__PURE__ */ jsx14(RectangleVerticalIcon, { className: "w-3.5 h-3.5 shrink-0" }),
3666
+ " Enter Full Screen"
3667
+ ] })
3668
+ }
3669
+ ),
3670
+ isMaximized && /* @__PURE__ */ jsxs11(
3671
+ "button",
3672
+ {
3673
+ type: "button",
3674
+ className: "flex items-center gap-2.5 w-full px-3 py-1.5 hover:bg-accent transition-colors text-foreground/80",
3675
+ onClick: () => handleAction(onRestore),
3676
+ onPointerDown: (e) => e.stopPropagation(),
3677
+ children: [
3678
+ /* @__PURE__ */ jsx14(Maximize2Icon, { className: "w-3.5 h-3.5 shrink-0" }),
3679
+ "Restore window"
3680
+ ]
3681
+ }
3682
+ ),
3683
+ onPopOut && /* @__PURE__ */ jsxs11(Fragment3, { children: [
3684
+ /* @__PURE__ */ jsx14("div", { className: "border-t border-border/50 my-1" }),
3685
+ /* @__PURE__ */ jsxs11(
3686
+ "button",
3687
+ {
3688
+ type: "button",
3689
+ className: "flex items-center gap-2.5 w-full px-3 py-1.5 transition-colors text-foreground/80 hover:bg-accent",
3690
+ onClick: () => handleAction(onPopOut),
3691
+ onPointerDown: (e) => e.stopPropagation(),
3692
+ title: "Pop out into a separate window",
3693
+ children: [
3694
+ /* @__PURE__ */ jsx14(AppWindowIcon, { className: "w-3.5 h-3.5 shrink-0" }),
3695
+ /* @__PURE__ */ jsx14("span", { className: "flex-1 text-left", children: "Pop out" })
3696
+ ]
3697
+ }
3698
+ )
3699
+ ] }),
3700
+ onSaveWindowState && /* @__PURE__ */ jsxs11(Fragment3, { children: [
3701
+ /* @__PURE__ */ jsx14("div", { className: "border-t border-border/50 my-1" }),
3702
+ /* @__PURE__ */ jsxs11(
3703
+ "button",
3704
+ {
3705
+ type: "button",
3706
+ className: "flex items-center gap-2.5 w-full px-3 py-1.5 hover:bg-accent transition-colors text-foreground/80",
3707
+ onClick: () => handleAction(onSaveWindowState),
3708
+ onPointerDown: (e) => e.stopPropagation(),
3709
+ children: [
3710
+ /* @__PURE__ */ jsx14(SaveIcon, { className: "w-3.5 h-3.5 shrink-0" }),
3711
+ "Save window state"
3712
+ ]
3713
+ }
3714
+ )
3715
+ ] })
3716
+ ]
3717
+ }
3718
+ )
3719
+ ]
3720
+ }
3721
+ );
3722
+ }
3723
+
3724
+ // src/window.ts
3725
+ import {
3726
+ WindowsProvider,
3727
+ useWindowConfig as useWindowConfig2,
3728
+ WindowSelectionSurface as WindowSelectionSurface2,
3729
+ DEFAULT_WINDOW_CONFIG,
3730
+ DEFAULT_ESCAPE_YIELD_SELECTOR,
3731
+ DEFAULT_MOBILE_TOP_OFFSET
3732
+ } from "@ai-matrx/summon/window-host";
3733
+ import { WindowTraySync } from "@ai-matrx/summon/window-host";
3734
+
3735
+ // src/window/WindowSidebar.tsx
3736
+ import { cn as cn10 } from "@ai-matrx/design-system/cn";
3737
+ import { ScrollArea } from "@ai-matrx/design-system";
3738
+ import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
3739
+ function WindowSidebar({
3740
+ items,
3741
+ activeItem,
3742
+ onSelect,
3743
+ className
3744
+ }) {
3745
+ return /* @__PURE__ */ jsx15(ScrollArea, { className: cn10("flex-1 w-full", className), children: /* @__PURE__ */ jsx15("div", { className: "py-2", children: items.map((item) => {
3746
+ const Icon = item.icon;
3747
+ const isActive = item.value === activeItem;
3748
+ return /* @__PURE__ */ jsxs12(
3749
+ "button",
3750
+ {
3751
+ onClick: () => onSelect(item.value),
3752
+ title: item.description,
3753
+ className: cn10(
3754
+ "w-full flex items-center gap-2.5 px-3 py-2 text-left text-xs transition-colors",
3755
+ isActive ? "bg-primary/10 text-primary-ink border-r-2 border-primary" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
3756
+ ),
3757
+ children: [
3758
+ /* @__PURE__ */ jsx15(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
3759
+ /* @__PURE__ */ jsx15("span", { className: "truncate", children: item.label })
3760
+ ]
3761
+ },
3762
+ item.value
3763
+ );
3764
+ }) }) });
3765
+ }
3766
+
3767
+ // src/window/useOverlaySurfaceRenderAck.ts
3768
+ import { useEffect as useEffect6 } from "react";
3769
+ import { ackOverlaySurfaceRender, clearOverlaySurfaceRender } from "@ai-matrx/summon";
3770
+ function useOverlaySurfaceRenderAck(overlayId, active) {
3771
+ useEffect6(() => {
3772
+ if (!active) return void 0;
3773
+ ackOverlaySurfaceRender(overlayId);
3774
+ return () => clearOverlaySurfaceRender(overlayId);
3775
+ }, [active, overlayId]);
3776
+ }
3777
+
3778
+ // src/window.ts
3779
+ import {
3780
+ registerTrayPreview,
3781
+ getTrayPreviewEntry as getTrayPreviewEntry3,
3782
+ resetTrayPreviews
3783
+ } from "@ai-matrx/summon/window-host";
3784
+
3785
+ // src/window/tray/TrayStatusChip.tsx
3786
+ import { cn as cn11 } from "@ai-matrx/design-system/cn";
3787
+ import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
3788
+ var TONE_CLASSES = {
3789
+ neutral: {
3790
+ fg: "text-muted-foreground",
3791
+ dot: "bg-muted-foreground/50"
3792
+ },
3793
+ info: {
3794
+ fg: "text-info",
3795
+ dot: "bg-info"
3796
+ },
3797
+ warning: {
3798
+ fg: "text-warning",
3799
+ dot: "bg-warning"
3800
+ },
3801
+ elevated: {
3802
+ fg: "text-warning",
3803
+ dot: "bg-warning"
3804
+ },
3805
+ critical: {
3806
+ fg: "text-destructive",
3807
+ dot: "bg-destructive"
3808
+ }
3809
+ };
3810
+ function TrayStatusChip({
3811
+ icon: Icon,
3812
+ tone,
3813
+ count,
3814
+ caption,
3815
+ segments,
3816
+ pulse
3817
+ }) {
3818
+ const t = TONE_CLASSES[tone];
3819
+ const visibleSegments = (segments ?? []).filter((s) => s.count > 0);
3820
+ const showBreakdown = visibleSegments.length > 1;
3821
+ const hasCount = typeof count === "number";
3822
+ return /* @__PURE__ */ jsxs13("div", { className: "flex h-full items-center gap-1.5 overflow-hidden", children: [
3823
+ /* @__PURE__ */ jsx16(
3824
+ Icon,
3825
+ {
3826
+ className: cn11("h-3.5 w-3.5 shrink-0", t.fg, pulse && "animate-pulse"),
3827
+ "aria-hidden": true
3828
+ }
3829
+ ),
3830
+ /* @__PURE__ */ jsxs13("div", { className: "flex min-w-0 items-baseline gap-1", children: [
3831
+ hasCount ? /* @__PURE__ */ jsx16("span", { className: cn11("text-xs font-semibold tabular-nums", t.fg), children: count }) : null,
3832
+ caption ? /* @__PURE__ */ jsx16("span", { className: "truncate text-[11px] text-muted-foreground", children: caption }) : null
3833
+ ] }),
3834
+ showBreakdown ? /* @__PURE__ */ jsx16("div", { className: "ml-auto flex shrink-0 items-center gap-1", children: visibleSegments.map((s, i) => /* @__PURE__ */ jsxs13(
3835
+ "span",
3836
+ {
3837
+ className: "flex items-center gap-0.5",
3838
+ title: s.label ? `${s.count} ${s.label}` : void 0,
3839
+ children: [
3840
+ /* @__PURE__ */ jsx16(
3841
+ "span",
3842
+ {
3843
+ className: cn11(
3844
+ "h-1.5 w-1.5 rounded-full",
3845
+ TONE_CLASSES[s.tone].dot
3846
+ )
3847
+ }
3848
+ ),
3849
+ /* @__PURE__ */ jsx16("span", { className: "text-[10px] tabular-nums text-muted-foreground", children: s.count })
3850
+ ]
3851
+ },
3852
+ i
3853
+ )) }) : null
3854
+ ] });
3855
+ }
3856
+
3857
+ // src/window/popout/usePopoutContainer.ts
3858
+ function usePopoutContainer() {
3859
+ return usePopout().popoutContainer ?? void 0;
3860
+ }
3861
+
3862
+ // src/window/utils/mayRaiseUnbidden.ts
3863
+ import { getOverlayMeta as getOverlayMeta4 } from "@ai-matrx/summon";
3864
+ function isUnderRoute(pathname, route) {
3865
+ if (pathname === route) return true;
3866
+ const base = route.endsWith("/") ? route.slice(0, -1) : route;
3867
+ return pathname === base || pathname.startsWith(`${base}/`);
3868
+ }
3869
+ function describeHome(h) {
3870
+ return typeof h === "string" ? h : `${h.route} (exact)`;
3871
+ }
3872
+ function mayRaiseUnbidden(overlayId, pathname) {
3873
+ const entry = getOverlayMeta4(overlayId);
3874
+ const declared = entry?.window?.unbiddenHome ?? [];
3875
+ const home = declared.map(describeHome);
3876
+ if (home.length === 0) {
3877
+ const reason = entry ? `window "${overlayId}" declared no unbiddenHome in the window registry, so it may not open itself on any route` : `overlay "${overlayId}" is not in the window registry, so it has no declared home and may not open itself anywhere`;
3878
+ console.warn(`[window-panels] unbidden raise refused \u2014 ${reason}`);
3879
+ return { allowed: false, reason, code: "no-declared-home", home };
3880
+ }
3881
+ if (!pathname) {
3882
+ const reason = `window "${overlayId}" was asked to open itself with no current route, so its home (${home.join(", ")}) could not be confirmed`;
3883
+ console.warn(`[window-panels] unbidden raise refused \u2014 ${reason}`);
3884
+ return { allowed: false, reason, code: "away-from-home", home };
3885
+ }
3886
+ const matched = declared.find(
3887
+ (h) => typeof h === "string" ? isUnderRoute(pathname, h) : pathname === h.route
3888
+ );
3889
+ const match = matched === void 0 ? void 0 : describeHome(matched);
3890
+ if (!match) {
3891
+ const reason = `window "${overlayId}" lives on ${home.join(", ")} \u2014 not on "${pathname}" \u2014 so it is deferred until the viewer is on one of its own surfaces`;
3892
+ console.info(`[window-panels] unbidden raise deferred \u2014 ${reason}`);
3893
+ return { allowed: false, reason, code: "away-from-home", home };
3894
+ }
3895
+ return {
3896
+ allowed: true,
3897
+ reason: `window "${overlayId}" is on its declared home "${match}"`,
3898
+ home
3899
+ };
3900
+ }
3901
+
3902
+ // src/window/utils/lazy-bundle-guard.ts
3903
+ import { globalSlot as globalSlot5 } from "@ai-matrx/kit/global-slot";
3904
+ var guard = () => globalSlot5("ai-matrx.summon.lazyBundleGuard", () => {
3905
+ const state2 = { bootInProgress: typeof window !== "undefined", reported: /* @__PURE__ */ new Set() };
3906
+ if (state2.bootInProgress) {
3907
+ setTimeout(() => {
3908
+ state2.bootInProgress = false;
3909
+ }, 0);
3910
+ }
3911
+ return state2;
3912
+ });
3913
+ guard();
3914
+ function assertLazyLoaded(moduleId, report) {
3915
+ if (typeof window === "undefined") return;
3916
+ const callSiteStack = (new Error("call site").stack ?? "").split("\n").filter((line) => !/lazy-bundle-guard\.|at Error\s*$|^Error/i.test(line)).join("\n");
3917
+ queueMicrotask(() => {
3918
+ const state2 = guard();
3919
+ if (!state2.bootInProgress || state2.reported.has(moduleId)) return;
3920
+ state2.reported.add(moduleId);
3921
+ const message = `${moduleId} was parsed during initial app boot. Lazily rendered modules MUST load through their definition's render import \u2014 never statically from a route, layout, provider, or any module in the boot graph. (Reported once per session per module.)`;
3922
+ if (report) {
3923
+ report(message, callSiteStack);
3924
+ return;
3925
+ }
3926
+ console.warn(
3927
+ "[overlays bundle leak]",
3928
+ `
3929
+ ${message}${callSiteStack ? `
3930
+
3931
+ Eager-import chain (top frame is the file that statically imported it):
3932
+ ${callSiteStack}` : ""}`
3933
+ );
3934
+ });
3935
+ }
3936
+ export {
3937
+ AUTO_OPEN_MIN_WIDTH,
3938
+ AUTO_OPEN_NO_ROOM_EVENT,
3939
+ DEFAULT_DRAG_OUT_CONFIG,
3940
+ DEFAULT_ESCAPE_YIELD_SELECTOR,
3941
+ DEFAULT_MOBILE_TOP_OFFSET,
3942
+ DEFAULT_WINDOW_CONFIG,
3943
+ INITIAL_DRAG_OUT_STATE,
3944
+ LayoutIcon,
3945
+ LayoutIconButton,
3946
+ MOBILE_WINDOW_TITLE_TEXT,
3947
+ MinimizedWindowContent,
3948
+ MobileCardSurface,
3949
+ MobileDrawerSurface,
3950
+ MobileWindowActionsRow,
3951
+ MobileWindowTitle,
3952
+ MobileWindowTitleRow,
3953
+ PopoutContext,
3954
+ TrayChipPreview,
3955
+ TrayStatusChip,
3956
+ WindowPanel,
3957
+ WindowSelectionSurface2 as WindowSelectionSurface,
3958
+ WindowSidebar,
3959
+ WindowTraySync,
3960
+ WindowsProvider,
3961
+ assertLazyLoaded,
3962
+ autoOpenHasRoom,
3963
+ buildPopoutOpts,
3964
+ captureElementThumbnail,
3965
+ clearAllPopoutPending,
3966
+ clearAutoOpened,
3967
+ clearTraySnapshot,
3968
+ defaultTraySnapshotCapture,
3969
+ detectPopoutCapability,
3970
+ evaluateDragOut,
3971
+ forEachPopoutWindow,
3972
+ getPendingPopoutWindowIds,
3973
+ getPopoutOpener,
3974
+ getPopoutWindow,
3975
+ getTrayPreviewEntry3 as getTrayPreviewEntry,
3976
+ getTraySnapshot,
3977
+ isAutoOpened,
3978
+ isUnderRoute,
3979
+ markAutoOpened,
3980
+ mayRaiseUnbidden,
3981
+ measureWorkingColumn,
3982
+ mobileWindowTitleClass,
3983
+ registerTrayPreview,
3984
+ resetDefaultWindowCascade,
3985
+ resetTrayPreviews,
3986
+ resolveAutoOpenPlacement,
3987
+ resolvePosition,
3988
+ sanitizeWindowData,
3989
+ setTraySnapshot,
3990
+ subscribePopoutWindowMap,
3991
+ subscribeTraySnapshotMap,
3992
+ supportsAnyPopout,
3993
+ supportsDocumentPictureInPicture,
3994
+ useOverlaySurfaceRenderAck,
3995
+ usePopout,
3996
+ usePopoutContainer,
3997
+ usePopoutControl,
3998
+ usePopoutWindow,
3999
+ usePopoutWindowRef,
4000
+ useWindowConfig2 as useWindowConfig,
4001
+ useWindowPanel
4002
+ };
4003
+ //# sourceMappingURL=window.js.map