@ai-matrx/summon 0.0.0-stage → 0.1.0

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