@ai-matrx/summon 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +379 -1
- package/dist/closeBinding-C6raC3qJ.d.cts +20 -0
- package/dist/closeBinding-C6raC3qJ.d.ts +20 -0
- package/dist/dialog.cjs +60 -0
- package/dist/dialog.cjs.map +1 -0
- package/dist/dialog.d.cts +16 -0
- package/dist/dialog.d.ts +16 -0
- package/dist/dialog.js +38 -0
- package/dist/dialog.js.map +1 -0
- package/dist/errors-eJrKXq33.d.cts +30 -0
- package/dist/errors-eJrKXq33.d.ts +30 -0
- package/dist/handles.cjs +411 -0
- package/dist/handles.cjs.map +1 -0
- package/dist/handles.d.cts +166 -0
- package/dist/handles.d.ts +166 -0
- package/dist/handles.js +388 -0
- package/dist/handles.js.map +1 -0
- package/dist/index.cjs +2383 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1259 -0
- package/dist/index.d.ts +1259 -0
- package/dist/index.js +2363 -0
- package/dist/index.js.map +1 -0
- package/dist/lite.cjs +460 -0
- package/dist/lite.cjs.map +1 -0
- package/dist/lite.d.cts +56 -0
- package/dist/lite.d.ts +56 -0
- package/dist/lite.js +444 -0
- package/dist/lite.js.map +1 -0
- package/dist/next.cjs +61 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +5 -0
- package/dist/next.d.ts +5 -0
- package/dist/next.js +39 -0
- package/dist/next.js.map +1 -0
- package/dist/persistence-data.cjs +143 -0
- package/dist/persistence-data.cjs.map +1 -0
- package/dist/persistence-data.d.cts +72 -0
- package/dist/persistence-data.d.ts +72 -0
- package/dist/persistence-data.js +120 -0
- package/dist/persistence-data.js.map +1 -0
- package/dist/persistence.cjs +1073 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.cts +236 -0
- package/dist/persistence.d.ts +236 -0
- package/dist/persistence.js +1077 -0
- package/dist/persistence.js.map +1 -0
- package/dist/react.cjs +738 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +266 -0
- package/dist/react.d.ts +266 -0
- package/dist/react.js +723 -0
- package/dist/react.js.map +1 -0
- package/dist/sheet.cjs +71 -0
- package/dist/sheet.cjs.map +1 -0
- package/dist/sheet.d.cts +17 -0
- package/dist/sheet.d.ts +17 -0
- package/dist/sheet.js +49 -0
- package/dist/sheet.js.map +1 -0
- package/dist/styles.css +78 -0
- package/dist/testing.cjs +132 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +44 -0
- package/dist/testing.d.ts +44 -0
- package/dist/testing.js +115 -0
- package/dist/testing.js.map +1 -0
- package/dist/url.cjs +362 -0
- package/dist/url.cjs.map +1 -0
- package/dist/url.d.cts +153 -0
- package/dist/url.d.ts +153 -0
- package/dist/url.js +352 -0
- package/dist/url.js.map +1 -0
- package/dist/window-host.cjs +123 -0
- package/dist/window-host.cjs.map +1 -0
- package/dist/window-host.d.cts +72 -0
- package/dist/window-host.d.ts +72 -0
- package/dist/window-host.js +104 -0
- package/dist/window-host.js.map +1 -0
- package/dist/window.cjs +3977 -0
- package/dist/window.cjs.map +1 -0
- package/dist/window.d.cts +796 -0
- package/dist/window.d.ts +796 -0
- package/dist/window.js +4002 -0
- package/dist/window.js.map +1 -0
- package/package.json +239 -1
package/dist/window.cjs
ADDED
|
@@ -0,0 +1,3977 @@
|
|
|
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
|
+
onMinimize,
|
|
2375
|
+
onToggleMaximize,
|
|
2376
|
+
isInteracting
|
|
2377
|
+
} = useWindowPanel({
|
|
2378
|
+
...hookOpts,
|
|
2379
|
+
id,
|
|
2380
|
+
title,
|
|
2381
|
+
autoOpened,
|
|
2382
|
+
onAutoNoRoom: autoOpened ? () => {
|
|
2383
|
+
window.dispatchEvent(
|
|
2384
|
+
new CustomEvent(AUTO_OPEN_NO_ROOM_EVENT, { detail: { overlayId, instanceId: effectiveInstanceId } })
|
|
2385
|
+
);
|
|
2386
|
+
if (overlayId) autoDispatch(import_summon10.overlaysRawActions.closeOverlay({ overlayId, instanceId: effectiveInstanceId }));
|
|
2387
|
+
} : void 0,
|
|
2388
|
+
minWidth,
|
|
2389
|
+
minHeight,
|
|
2390
|
+
onTriggerPopout,
|
|
2391
|
+
...preservationEnabled && overlayId ? {
|
|
2392
|
+
persistence: {
|
|
2393
|
+
overlayId,
|
|
2394
|
+
instanceId: effectiveInstanceId,
|
|
2395
|
+
data: initialPreservationData,
|
|
2396
|
+
sidebarOpen: defaultSidebarOpen,
|
|
2397
|
+
sidebarSize: sidebarDefaultSize
|
|
2398
|
+
}
|
|
2399
|
+
} : {}
|
|
2400
|
+
});
|
|
2401
|
+
const liftedZIndex = (0, import_design_system7.useFloatingLayerZIndex)(managerZIndex);
|
|
2402
|
+
const zIndex = windowState === "minimized" ? Math.max(liftedZIndex, MINIMIZED_TILE_Z_FLOOR + managerZIndex) : liftedZIndex;
|
|
2403
|
+
const dispatch = (0, import_react20.useSummonDispatch)();
|
|
2404
|
+
const windowsHidden = (0, import_react20.useSummonSelector)(import_summon10.selectWindowsHidden);
|
|
2405
|
+
const isDebugMode = useDebugFlag(ports.debug);
|
|
2406
|
+
(0, import_react19.useEffect)(() => {
|
|
2407
|
+
(0, import_design_system7.announceLayerFront)(
|
|
2408
|
+
document.querySelector(`[data-window-id="${CSS.escape(id)}"]`)
|
|
2409
|
+
);
|
|
2410
|
+
}, [id, managerZIndex]);
|
|
2411
|
+
const claimFront = (event) => {
|
|
2412
|
+
(0, import_design_system7.announceLayerFront)(event.currentTarget.closest("[data-window-id]") ?? event.currentTarget);
|
|
2413
|
+
};
|
|
2414
|
+
const allWindows = (0, import_react20.useSummonSelector)(import_summon10.selectAllWindows);
|
|
2415
|
+
const isTopWindow = !isMobile ? true : allWindows.find((w) => w.state !== "minimized")?.id === id;
|
|
2416
|
+
const [activePaneMobile, setActivePaneMobile] = (0, import_react19.useState)(
|
|
2417
|
+
"main"
|
|
2418
|
+
);
|
|
2419
|
+
const effectiveUrlSyncKey = resolveWindowUrlSyncKey(registryMeta?.link?.key, urlSyncKey);
|
|
2420
|
+
const effectiveUrlSyncId = urlSyncId ?? (effectiveUrlSyncKey ? overlayId : void 0);
|
|
2421
|
+
(0, import_url.useOverlayLink)(effectiveUrlSyncKey, effectiveUrlSyncId, urlSyncArgs);
|
|
2422
|
+
const deprecation = registryEntry?.deprecated;
|
|
2423
|
+
const isDeprecated = !!deprecation;
|
|
2424
|
+
const deprecatedRingClass = isDeprecated ? "ring-2 ring-destructive ring-offset-2 ring-offset-background" : "";
|
|
2425
|
+
const hasSidebar = !!sidebar;
|
|
2426
|
+
const [sidebarOpen, setSidebarOpen] = (0, import_react19.useState)(defaultSidebarOpen);
|
|
2427
|
+
const [sidebarSize, setSidebarSize] = (0, import_react19.useState)(
|
|
2428
|
+
sidebarDefaultSize
|
|
2429
|
+
);
|
|
2430
|
+
const restoredPersistence = (0, import_react20.useSummonSelector)((0, import_summon10.selectWindowPersistence)(id));
|
|
2431
|
+
const sidebarPanelRef = (0, import_react19.useRef)(null);
|
|
2432
|
+
const restoredChromeAppliedRef = (0, import_react19.useRef)(false);
|
|
2433
|
+
const handleSaveWindowState = (0, import_react19.useCallback)(() => {
|
|
2434
|
+
if (!overlayId || !preservationEnabled || !registryEntry?.preservation) {
|
|
2435
|
+
return;
|
|
2436
|
+
}
|
|
2437
|
+
let collected = {};
|
|
2438
|
+
try {
|
|
2439
|
+
collected = onCollectData?.() ?? {};
|
|
2440
|
+
} catch (error) {
|
|
2441
|
+
ports.errors.capture(error, { where: "window-preservation collector", overlayId, instanceId: effectiveInstanceId });
|
|
2442
|
+
}
|
|
2443
|
+
const base = sanitizeWindowData({ ...initialPreservationData, ...collected }, registryEntry.preservation.dataKeys);
|
|
2444
|
+
dispatch(
|
|
2445
|
+
(0, import_summon10.updateWindowPersistence)({
|
|
2446
|
+
id,
|
|
2447
|
+
data: base,
|
|
2448
|
+
sidebarOpen,
|
|
2449
|
+
...sidebarSize === null ? {} : { sidebarSize }
|
|
2450
|
+
})
|
|
2451
|
+
);
|
|
2452
|
+
}, [
|
|
2453
|
+
overlayId,
|
|
2454
|
+
preservationEnabled,
|
|
2455
|
+
registryEntry,
|
|
2456
|
+
effectiveInstanceId,
|
|
2457
|
+
initialPreservationData,
|
|
2458
|
+
id,
|
|
2459
|
+
sidebarOpen,
|
|
2460
|
+
sidebarSize,
|
|
2461
|
+
onCollectData,
|
|
2462
|
+
ports,
|
|
2463
|
+
dispatch
|
|
2464
|
+
]);
|
|
2465
|
+
const saveRef = (0, import_react19.useRef)(handleSaveWindowState);
|
|
2466
|
+
(0, import_react19.useEffect)(() => {
|
|
2467
|
+
saveRef.current = handleSaveWindowState;
|
|
2468
|
+
}, [handleSaveWindowState]);
|
|
2469
|
+
(0, import_react19.useEffect)(() => {
|
|
2470
|
+
if (!preservationEnabled) return;
|
|
2471
|
+
saveRef.current();
|
|
2472
|
+
}, [preservationEnabled, overlayLaunchData, onCollectData]);
|
|
2473
|
+
const handleClose = (0, import_react19.useCallback)(() => {
|
|
2474
|
+
if (instanceMatches && instance && instance.overlayId === overlayId) {
|
|
2475
|
+
instance.close();
|
|
2476
|
+
} else if (boundToOverlay) {
|
|
2477
|
+
ports.errors.capture(new Error("[summon] WindowPanel boundToOverlay rendered outside an overlay instance"), { windowId: id });
|
|
2478
|
+
} else if (overlayId) {
|
|
2479
|
+
const hasExactOverlayIdentity = registryMeta?.instance !== "multi" || Boolean(overlayInstanceId);
|
|
2480
|
+
if (hasExactOverlayIdentity) {
|
|
2481
|
+
dispatch(import_summon10.overlaysRawActions.closeOverlay({ overlayId, instanceId: effectiveInstanceId }));
|
|
2482
|
+
} else if (!onClose) {
|
|
2483
|
+
ports.errors.capture(new Error(`[summon] Cannot close multi-instance ${overlayId} without overlayInstanceId or onClose.`), { overlayId });
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
onClose?.();
|
|
2487
|
+
}, [boundToOverlay, dispatch, effectiveInstanceId, id, instance, instanceMatches, onClose, overlayId, overlayInstanceId, ports, registryMeta?.instance]);
|
|
2488
|
+
(0, import_react19.useEffect)(() => {
|
|
2489
|
+
if (!closeOnEscape || isMobile) return void 0;
|
|
2490
|
+
const onKey = (e) => {
|
|
2491
|
+
if (e.key !== "Escape" || e.defaultPrevented) return;
|
|
2492
|
+
const layered = Array.from(
|
|
2493
|
+
document.querySelectorAll(
|
|
2494
|
+
`[data-radix-popper-content-wrapper], [role="menu"], [role="listbox"], [role="dialog"][data-state="open"], [role="alertdialog"], ${windowConfig.escapeYieldSelector}`
|
|
2495
|
+
)
|
|
2496
|
+
).some(
|
|
2497
|
+
(el2) => el2.getClientRects().length > 0 && getComputedStyle(el2).visibility !== "hidden" && !el2.closest(`[data-window-id="${CSS.escape(id)}"]`)
|
|
2498
|
+
);
|
|
2499
|
+
if (layered) return;
|
|
2500
|
+
const mine = document.querySelector(
|
|
2501
|
+
`[data-window-panel][data-window-id="${CSS.escape(id)}"]`
|
|
2502
|
+
);
|
|
2503
|
+
if (!mine) return;
|
|
2504
|
+
const z = (el2) => Number(getComputedStyle(el2).zIndex) || 0;
|
|
2505
|
+
const top = Array.from(
|
|
2506
|
+
document.querySelectorAll("[data-window-panel][data-window-id]")
|
|
2507
|
+
).reduce((best, el2) => !best || z(el2) > z(best) ? el2 : best, null);
|
|
2508
|
+
if (top !== mine) return;
|
|
2509
|
+
e.preventDefault();
|
|
2510
|
+
handleClose();
|
|
2511
|
+
};
|
|
2512
|
+
document.addEventListener("keydown", onKey);
|
|
2513
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
2514
|
+
}, [closeOnEscape, isMobile, id, handleClose, windowConfig.escapeYieldSelector]);
|
|
2515
|
+
(0, import_react19.useEffect)(() => {
|
|
2516
|
+
if (!restoredPersistence || restoredChromeAppliedRef.current) return;
|
|
2517
|
+
restoredChromeAppliedRef.current = true;
|
|
2518
|
+
setSidebarOpen(restoredPersistence.sidebarOpen);
|
|
2519
|
+
setSidebarSize(restoredPersistence.sidebarSize);
|
|
2520
|
+
if (!restoredPersistence.sidebarOpen) {
|
|
2521
|
+
sidebarPanelRef.current?.collapse();
|
|
2522
|
+
} else if (restoredPersistence.sidebarSize) {
|
|
2523
|
+
sidebarPanelRef.current?.resize(restoredPersistence.sidebarSize);
|
|
2524
|
+
}
|
|
2525
|
+
}, [restoredPersistence]);
|
|
2526
|
+
const [fitContentEl, fitContentRef] = (0, import_react19.useState)(null);
|
|
2527
|
+
const fitContentStore = (0, import_react20.useSummonStore)();
|
|
2528
|
+
const fitPlacedRef = (0, import_react19.useRef)(false);
|
|
2529
|
+
const [glideReady, setGlideReady] = (0, import_react19.useState)(false);
|
|
2530
|
+
(0, import_react19.useEffect)(() => {
|
|
2531
|
+
const timer = window.setTimeout(() => setGlideReady(true), GLIDE_SETTLE_MS);
|
|
2532
|
+
return () => window.clearTimeout(timer);
|
|
2533
|
+
}, []);
|
|
2534
|
+
(0, import_react19.useEffect)(() => {
|
|
2535
|
+
if (isInteracting) fitPlacedRef.current = true;
|
|
2536
|
+
}, [isInteracting]);
|
|
2537
|
+
const fitPresetPosition = hookOpts.position;
|
|
2538
|
+
const fitMayReplace = hookOpts.initialRect?.x === void 0 && hookOpts.initialRect?.y === void 0;
|
|
2539
|
+
(0, import_react19.useEffect)(() => {
|
|
2540
|
+
if (!fitContent || isMobile || windowState !== "windowed") return void 0;
|
|
2541
|
+
const el2 = fitContentEl;
|
|
2542
|
+
if (!el2) return void 0;
|
|
2543
|
+
let active = true;
|
|
2544
|
+
const ro = new ResizeObserver((entries) => {
|
|
2545
|
+
if (!active || fitContentStore.getState().windowManager.windows[id]?.state !== "windowed") return;
|
|
2546
|
+
const entry = entries[0];
|
|
2547
|
+
if (!entry) return;
|
|
2548
|
+
const { width, height } = entry.contentRect;
|
|
2549
|
+
const borderH = el2.offsetHeight - el2.clientHeight;
|
|
2550
|
+
const borderW = el2.offsetWidth - el2.clientWidth;
|
|
2551
|
+
const fitWidth = Math.ceil(width + borderW);
|
|
2552
|
+
const fitHeight = Math.ceil(height + borderH);
|
|
2553
|
+
const placeNow = !fitPlacedRef.current && fitMayReplace && fitPresetPosition !== void 0;
|
|
2554
|
+
dispatch(
|
|
2555
|
+
(0, import_summon10.updateWindowRect)({
|
|
2556
|
+
id,
|
|
2557
|
+
rect: {
|
|
2558
|
+
width: fitWidth,
|
|
2559
|
+
height: fitHeight,
|
|
2560
|
+
...placeNow ? resolvePosition(fitPresetPosition, fitWidth, fitHeight) : {}
|
|
2561
|
+
}
|
|
2562
|
+
})
|
|
2563
|
+
);
|
|
2564
|
+
});
|
|
2565
|
+
ro.observe(el2);
|
|
2566
|
+
return () => {
|
|
2567
|
+
active = false;
|
|
2568
|
+
ro.disconnect();
|
|
2569
|
+
};
|
|
2570
|
+
}, [fitContent, fitContentEl, isMobile, windowState, id, dispatch, fitContentStore, fitPresetPosition, fitMayReplace]);
|
|
2571
|
+
const toggleSidebar = (0, import_react19.useCallback)(() => {
|
|
2572
|
+
const panel = sidebarPanelRef.current;
|
|
2573
|
+
if (!panel) return;
|
|
2574
|
+
if (sidebarOpen) {
|
|
2575
|
+
if (sidebarExpandsWindow) {
|
|
2576
|
+
dispatch(
|
|
2577
|
+
(0, import_summon10.updateWindowRect)({
|
|
2578
|
+
id,
|
|
2579
|
+
rect: { width: rect.width - sidebarDefaultSize }
|
|
2580
|
+
})
|
|
2581
|
+
);
|
|
2582
|
+
requestAnimationFrame(() => panel.collapse());
|
|
2583
|
+
} else {
|
|
2584
|
+
panel.collapse();
|
|
2585
|
+
}
|
|
2586
|
+
} else {
|
|
2587
|
+
if (sidebarExpandsWindow) {
|
|
2588
|
+
dispatch(
|
|
2589
|
+
(0, import_summon10.updateWindowRect)({
|
|
2590
|
+
id,
|
|
2591
|
+
rect: { width: rect.width + sidebarDefaultSize }
|
|
2592
|
+
})
|
|
2593
|
+
);
|
|
2594
|
+
requestAnimationFrame(() => panel.resize(sidebarDefaultSize));
|
|
2595
|
+
} else {
|
|
2596
|
+
panel.resize(sidebarDefaultSize);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}, [
|
|
2600
|
+
sidebarOpen,
|
|
2601
|
+
sidebarDefaultSize,
|
|
2602
|
+
sidebarExpandsWindow,
|
|
2603
|
+
dispatch,
|
|
2604
|
+
id,
|
|
2605
|
+
rect.width
|
|
2606
|
+
]);
|
|
2607
|
+
const handleSidebarResize = (0, import_react19.useCallback)(
|
|
2608
|
+
(panelSize) => {
|
|
2609
|
+
setSidebarOpen(panelSize.asPercentage > 0);
|
|
2610
|
+
if (panelSize.inPixels > 0) setSidebarSize(panelSize.inPixels);
|
|
2611
|
+
if (preservationEnabled) {
|
|
2612
|
+
dispatch(
|
|
2613
|
+
(0, import_summon10.updateWindowPersistence)({
|
|
2614
|
+
id,
|
|
2615
|
+
sidebarOpen: panelSize.asPercentage > 0,
|
|
2616
|
+
sidebarSize: panelSize.inPixels > 0 ? panelSize.inPixels : sidebarSize
|
|
2617
|
+
})
|
|
2618
|
+
);
|
|
2619
|
+
}
|
|
2620
|
+
},
|
|
2621
|
+
[dispatch, id, preservationEnabled, sidebarSize]
|
|
2622
|
+
);
|
|
2623
|
+
const portalTarget = isMounted ? document.body : null;
|
|
2624
|
+
const [bodyHost] = (0, import_react19.useState)(() => {
|
|
2625
|
+
if (typeof document === "undefined") return null;
|
|
2626
|
+
const host = document.createElement("div");
|
|
2627
|
+
host.className = "matrx-window-body-host";
|
|
2628
|
+
host.setAttribute("data-window-body-host", "");
|
|
2629
|
+
return host;
|
|
2630
|
+
});
|
|
2631
|
+
(0, import_react19.useEffect)(() => {
|
|
2632
|
+
if (!overlayId) return void 0;
|
|
2633
|
+
(0, import_summon10.ackOverlayRender)(overlayId, id);
|
|
2634
|
+
return () => (0, import_summon10.clearOverlayRender)(overlayId, id);
|
|
2635
|
+
}, [overlayId, id]);
|
|
2636
|
+
const resolvedActionsRight = actionsRight ?? actions ?? null;
|
|
2637
|
+
const snapLeft = (0, import_react19.useCallback)(() => {
|
|
2638
|
+
const { vw, vh } = (0, import_summon10.safeViewportDims)();
|
|
2639
|
+
dispatch(
|
|
2640
|
+
(0, import_summon10.updateWindowRect)({
|
|
2641
|
+
id,
|
|
2642
|
+
rect: { x: 0, y: 0, width: Math.round(vw / 2), height: vh }
|
|
2643
|
+
})
|
|
2644
|
+
);
|
|
2645
|
+
}, [dispatch, id]);
|
|
2646
|
+
const snapRight = (0, import_react19.useCallback)(() => {
|
|
2647
|
+
const { vw, vh } = (0, import_summon10.safeViewportDims)();
|
|
2648
|
+
const half = Math.round(vw / 2);
|
|
2649
|
+
dispatch(
|
|
2650
|
+
(0, import_summon10.updateWindowRect)({
|
|
2651
|
+
id,
|
|
2652
|
+
rect: { x: half, y: 0, width: vw - half, height: vh }
|
|
2653
|
+
})
|
|
2654
|
+
);
|
|
2655
|
+
}, [dispatch, id]);
|
|
2656
|
+
const snapTop = (0, import_react19.useCallback)(() => {
|
|
2657
|
+
const { vw, vh } = (0, import_summon10.safeViewportDims)();
|
|
2658
|
+
dispatch(
|
|
2659
|
+
(0, import_summon10.updateWindowRect)({
|
|
2660
|
+
id,
|
|
2661
|
+
rect: { x: 0, y: 0, width: vw, height: Math.round(vh / 2) }
|
|
2662
|
+
})
|
|
2663
|
+
);
|
|
2664
|
+
}, [dispatch, id]);
|
|
2665
|
+
const snapBottom = (0, import_react19.useCallback)(() => {
|
|
2666
|
+
const { vw, vh } = (0, import_summon10.safeViewportDims)();
|
|
2667
|
+
const half = Math.round(vh / 2);
|
|
2668
|
+
dispatch(
|
|
2669
|
+
(0, import_summon10.updateWindowRect)({
|
|
2670
|
+
id,
|
|
2671
|
+
rect: { x: 0, y: half, width: vw, height: vh - half }
|
|
2672
|
+
})
|
|
2673
|
+
);
|
|
2674
|
+
}, [dispatch, id]);
|
|
2675
|
+
const snapCentre = (0, import_react19.useCallback)(() => {
|
|
2676
|
+
const { vw, vh } = (0, import_summon10.safeViewportDims)();
|
|
2677
|
+
const w = Math.min(rect.width, vw);
|
|
2678
|
+
const h = Math.min(rect.height, vh);
|
|
2679
|
+
dispatch(
|
|
2680
|
+
(0, import_summon10.updateWindowRect)({
|
|
2681
|
+
id,
|
|
2682
|
+
rect: {
|
|
2683
|
+
x: Math.round((vw - w) / 2),
|
|
2684
|
+
y: Math.round((vh - h) / 2),
|
|
2685
|
+
width: w,
|
|
2686
|
+
height: h
|
|
2687
|
+
}
|
|
2688
|
+
})
|
|
2689
|
+
);
|
|
2690
|
+
}, [dispatch, id, rect.width, rect.height]);
|
|
2691
|
+
const arrangeAll = (0, import_react19.useCallback)(
|
|
2692
|
+
(layout) => {
|
|
2693
|
+
const { vw, vh } = (0, import_summon10.safeViewportDims)();
|
|
2694
|
+
dispatch(
|
|
2695
|
+
(0, import_summon10.arrangeActiveWindows)({
|
|
2696
|
+
layout,
|
|
2697
|
+
viewportWidth: vw,
|
|
2698
|
+
viewportHeight: vh
|
|
2699
|
+
})
|
|
2700
|
+
);
|
|
2701
|
+
},
|
|
2702
|
+
[dispatch]
|
|
2703
|
+
);
|
|
2704
|
+
const popoutMode = (0, import_react20.useSummonSelector)((0, import_summon10.selectPopoutMode)(id));
|
|
2705
|
+
const outerPopout = usePopout();
|
|
2706
|
+
const outerPortalContainer = (0, import_design_system7.usePortalContainer)();
|
|
2707
|
+
const popoutWin = usePopoutWindowRef(id);
|
|
2708
|
+
const ownPopout = popoutMode !== null && popoutWin !== null;
|
|
2709
|
+
const bodyPopoutValue = import_react19.default.useMemo(
|
|
2710
|
+
() => ownPopout && popoutWin ? { popoutDocument: popoutWin.document, popoutWindow: popoutWin, popoutContainer: popoutWin.document.body, mode: popoutMode } : outerPopout,
|
|
2711
|
+
[ownPopout, popoutWin, popoutMode, outerPopout]
|
|
2712
|
+
);
|
|
2713
|
+
const bodyPortalContainer = ownPopout && popoutWin ? popoutWin.document.body : outerPortalContainer;
|
|
2714
|
+
const popoutCandidateId = (0, import_react20.useSummonSelector)(import_summon10.selectPopoutCandidateId);
|
|
2715
|
+
const canShowPopOut = !hidePopOutButton && !isMobile && popoutCapability !== "none" && windowState !== "minimized" && popoutMode === null;
|
|
2716
|
+
const handlePopOut = (0, import_react19.useCallback)(() => {
|
|
2717
|
+
void popout.openPopout({
|
|
2718
|
+
width: rect.width,
|
|
2719
|
+
height: rect.height,
|
|
2720
|
+
title: typeof title === "string" ? title : "Window"
|
|
2721
|
+
});
|
|
2722
|
+
}, [popout, rect.width, rect.height, title]);
|
|
2723
|
+
const handleDockBack = (0, import_react19.useCallback)(() => {
|
|
2724
|
+
dispatch((0, import_summon10.dockWindow)((0, import_summon10.buildDockWindowPayload)(id)));
|
|
2725
|
+
}, [dispatch, id]);
|
|
2726
|
+
const bodyRef = (0, import_react19.useRef)(null);
|
|
2727
|
+
const retainedBodyDimensionsRef = (0, import_react19.useRef)(null);
|
|
2728
|
+
const captureGenerationRef = (0, import_react19.useRef)(0);
|
|
2729
|
+
const trayPreviewEntry = (0, import_window_host3.getTrayPreviewEntry)(trayRegistryKey);
|
|
2730
|
+
const effectiveTrayCapture = captureTraySnapshot ?? trayPreviewEntry.captureTraySnapshot ?? (trayPreviewEntry.Preview ? void 0 : defaultTraySnapshotCapture);
|
|
2731
|
+
const [pendingTrayCapture, setPendingTrayCapture] = (0, import_react19.useState)(null);
|
|
2732
|
+
const [trayPreviewData, setTrayPreviewData] = (0, import_react19.useState)(void 0);
|
|
2733
|
+
const handleMinimize = (0, import_react19.useCallback)(() => {
|
|
2734
|
+
let collected;
|
|
2735
|
+
try {
|
|
2736
|
+
collected = onCollectData?.();
|
|
2737
|
+
} catch (error) {
|
|
2738
|
+
ports.errors.capture(error, { where: "window preview collector", windowId: trayRegistryKey });
|
|
2739
|
+
}
|
|
2740
|
+
if (collected && typeof collected === "object") {
|
|
2741
|
+
setTrayPreviewData(collected);
|
|
2742
|
+
}
|
|
2743
|
+
if (preservationEnabled) saveRef.current();
|
|
2744
|
+
const bodyEl = bodyRef.current;
|
|
2745
|
+
const generation = ++captureGenerationRef.current;
|
|
2746
|
+
if (bodyEl) {
|
|
2747
|
+
const bounds = bodyEl.getBoundingClientRect();
|
|
2748
|
+
retainedBodyDimensionsRef.current = {
|
|
2749
|
+
width: Math.max(1, Math.round(bounds.width)),
|
|
2750
|
+
height: Math.max(1, Math.round(bounds.height))
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
if (effectiveTrayCapture && bodyEl) {
|
|
2754
|
+
const bounds = bodyEl.getBoundingClientRect();
|
|
2755
|
+
setPendingTrayCapture({
|
|
2756
|
+
capture: effectiveTrayCapture,
|
|
2757
|
+
width: Math.max(1, Math.round(bounds.width)),
|
|
2758
|
+
height: Math.max(1, Math.round(bounds.height)),
|
|
2759
|
+
generation
|
|
2760
|
+
});
|
|
2761
|
+
} else {
|
|
2762
|
+
setPendingTrayCapture(null);
|
|
2763
|
+
}
|
|
2764
|
+
onMinimize();
|
|
2765
|
+
}, [
|
|
2766
|
+
effectiveTrayCapture,
|
|
2767
|
+
onCollectData,
|
|
2768
|
+
onMinimize,
|
|
2769
|
+
ports,
|
|
2770
|
+
preservationEnabled,
|
|
2771
|
+
trayRegistryKey
|
|
2772
|
+
]);
|
|
2773
|
+
(0, import_react19.useEffect)(() => {
|
|
2774
|
+
if (windowState !== "minimized" || !pendingTrayCapture) return void 0;
|
|
2775
|
+
const bodyEl = bodyRef.current;
|
|
2776
|
+
if (!bodyEl) {
|
|
2777
|
+
setPendingTrayCapture(null);
|
|
2778
|
+
return void 0;
|
|
2779
|
+
}
|
|
2780
|
+
let cancelled = false;
|
|
2781
|
+
const { capture, generation } = pendingTrayCapture;
|
|
2782
|
+
let timeoutId;
|
|
2783
|
+
const timeout = new Promise((resolve) => {
|
|
2784
|
+
timeoutId = setTimeout(() => resolve(null), 800);
|
|
2785
|
+
});
|
|
2786
|
+
void Promise.race([capture(bodyEl), timeout]).then((blob) => {
|
|
2787
|
+
if (blob && !cancelled && captureGenerationRef.current === generation) {
|
|
2788
|
+
setTraySnapshot(id, blob);
|
|
2789
|
+
}
|
|
2790
|
+
}).catch((err) => {
|
|
2791
|
+
if (!cancelled) ports.errors.capture(err, { where: "captureTraySnapshot", windowId: id });
|
|
2792
|
+
}).finally(() => {
|
|
2793
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2794
|
+
if (!cancelled && captureGenerationRef.current === generation) {
|
|
2795
|
+
setPendingTrayCapture(
|
|
2796
|
+
(current) => current?.generation === generation ? null : current
|
|
2797
|
+
);
|
|
2798
|
+
}
|
|
2799
|
+
});
|
|
2800
|
+
return () => {
|
|
2801
|
+
cancelled = true;
|
|
2802
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2803
|
+
};
|
|
2804
|
+
}, [id, pendingTrayCapture, ports, windowState]);
|
|
2805
|
+
const handleRestoreClearingSnapshot = (0, import_react19.useCallback)(() => {
|
|
2806
|
+
captureGenerationRef.current += 1;
|
|
2807
|
+
setPendingTrayCapture(null);
|
|
2808
|
+
clearTraySnapshot(id);
|
|
2809
|
+
onRestore();
|
|
2810
|
+
}, [id, onRestore]);
|
|
2811
|
+
(0, import_react19.useEffect)(() => {
|
|
2812
|
+
return () => {
|
|
2813
|
+
captureGenerationRef.current += 1;
|
|
2814
|
+
clearTraySnapshot(id);
|
|
2815
|
+
};
|
|
2816
|
+
}, [id]);
|
|
2817
|
+
const previousTrayStateRef = (0, import_react19.useRef)(windowState);
|
|
2818
|
+
(0, import_react19.useEffect)(() => {
|
|
2819
|
+
const wasMinimized = previousTrayStateRef.current === "minimized";
|
|
2820
|
+
previousTrayStateRef.current = windowState;
|
|
2821
|
+
if (!wasMinimized || windowState === "minimized") return;
|
|
2822
|
+
const invalidGeneration = ++captureGenerationRef.current;
|
|
2823
|
+
clearTraySnapshot(id);
|
|
2824
|
+
queueMicrotask(() => {
|
|
2825
|
+
setPendingTrayCapture(
|
|
2826
|
+
(current) => current && current.generation < invalidGeneration ? null : current
|
|
2827
|
+
);
|
|
2828
|
+
});
|
|
2829
|
+
}, [id, windowState]);
|
|
2830
|
+
const prevStateRef = (0, import_react19.useRef)(windowState);
|
|
2831
|
+
const prevPopoutModeRef = (0, import_react19.useRef)(popoutMode);
|
|
2832
|
+
(0, import_react19.useEffect)(() => {
|
|
2833
|
+
const wasNonWindowed = prevStateRef.current !== "windowed" || prevPopoutModeRef.current !== null;
|
|
2834
|
+
const isNowWindowed = windowState === "windowed" && popoutMode === null;
|
|
2835
|
+
prevStateRef.current = windowState;
|
|
2836
|
+
prevPopoutModeRef.current = popoutMode;
|
|
2837
|
+
if (wasNonWindowed && isNowWindowed) {
|
|
2838
|
+
const { vw, vh, degenerate } = (0, import_summon10.safeViewportDims)();
|
|
2839
|
+
if (degenerate) return;
|
|
2840
|
+
dispatch((0, import_summon10.clampWindowRect)({ id, viewportWidth: vw, viewportHeight: vh }));
|
|
2841
|
+
}
|
|
2842
|
+
}, [dispatch, id, windowState, popoutMode]);
|
|
2843
|
+
const isMinimized = windowState === "minimized";
|
|
2844
|
+
const isMaximized = windowState === "maximized";
|
|
2845
|
+
const isPopoutCandidate = popoutCandidateId === id;
|
|
2846
|
+
(0, import_react19.useEffect)(() => {
|
|
2847
|
+
if (!isMobile || !isMinimized) return;
|
|
2848
|
+
let cancelled = false;
|
|
2849
|
+
queueMicrotask(() => {
|
|
2850
|
+
if (!cancelled) handleRestoreClearingSnapshot();
|
|
2851
|
+
});
|
|
2852
|
+
return () => {
|
|
2853
|
+
cancelled = true;
|
|
2854
|
+
};
|
|
2855
|
+
}, [handleRestoreClearingSnapshot, isMinimized, isMobile]);
|
|
2856
|
+
const minimizedTitle = title?.trim() || (0, import_summon10.getOverlayMeta)(trayRegistryKey)?.label || "Window";
|
|
2857
|
+
const header = /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2858
|
+
WindowHeader,
|
|
2859
|
+
{
|
|
2860
|
+
title: titleNode ?? title,
|
|
2861
|
+
minimizedTitle,
|
|
2862
|
+
actionsLeft,
|
|
2863
|
+
actionsRight: resolvedActionsRight,
|
|
2864
|
+
onDragStart,
|
|
2865
|
+
onMinimize: handleMinimize,
|
|
2866
|
+
onToggleMaximize,
|
|
2867
|
+
onClose: handleClose,
|
|
2868
|
+
onRestore: handleRestoreClearingSnapshot,
|
|
2869
|
+
isMaximized,
|
|
2870
|
+
isMinimized,
|
|
2871
|
+
snapLeft,
|
|
2872
|
+
snapRight,
|
|
2873
|
+
snapTop,
|
|
2874
|
+
snapBottom,
|
|
2875
|
+
snapCentre,
|
|
2876
|
+
arrangeAll,
|
|
2877
|
+
hasSidebar,
|
|
2878
|
+
sidebarOpen,
|
|
2879
|
+
onToggleSidebar: toggleSidebar,
|
|
2880
|
+
onSaveWindowState: overlayId ? handleSaveWindowState : void 0,
|
|
2881
|
+
onPopOut: canShowPopOut ? handlePopOut : void 0
|
|
2882
|
+
}
|
|
2883
|
+
);
|
|
2884
|
+
const hasSecondaryPanel = secondaryPanel != null && (secondaryPanelOpen ?? true);
|
|
2885
|
+
const innerBody = hasSidebar || hasSecondaryPanel ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_design_system8.ResizablePanelGroup, { orientation: "horizontal", className: "h-full min-h-0", children: [
|
|
2886
|
+
hasSidebar && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2887
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2888
|
+
import_design_system8.ResizablePanel,
|
|
2889
|
+
{
|
|
2890
|
+
id: "sidebar",
|
|
2891
|
+
panelRef: sidebarPanelRef,
|
|
2892
|
+
defaultSize: sidebarOpen ? sidebarDefaultSize : 0,
|
|
2893
|
+
minSize: sidebarMinSize,
|
|
2894
|
+
collapsible: true,
|
|
2895
|
+
collapsedSize: 0,
|
|
2896
|
+
groupResizeBehavior: "preserve-pixel-size",
|
|
2897
|
+
onResize: handleSidebarResize,
|
|
2898
|
+
style: { overflow: "hidden" },
|
|
2899
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2900
|
+
"div",
|
|
2901
|
+
{
|
|
2902
|
+
className: (0, import_cn9.cn)(
|
|
2903
|
+
"h-full flex flex-col min-h-0 overflow-y-auto scrollbar-thin",
|
|
2904
|
+
sidebarClassName
|
|
2905
|
+
),
|
|
2906
|
+
children: sidebar
|
|
2907
|
+
}
|
|
2908
|
+
)
|
|
2909
|
+
}
|
|
2910
|
+
),
|
|
2911
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2912
|
+
import_design_system8.ResizableHandle,
|
|
2913
|
+
{
|
|
2914
|
+
className: sidebarOpen ? void 0 : "w-0 opacity-0 pointer-events-none"
|
|
2915
|
+
}
|
|
2916
|
+
)
|
|
2917
|
+
] }),
|
|
2918
|
+
/* @__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 }) }),
|
|
2919
|
+
hasSecondaryPanel && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2920
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system8.ResizableHandle, {}),
|
|
2921
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2922
|
+
import_design_system8.ResizablePanel,
|
|
2923
|
+
{
|
|
2924
|
+
id: "secondary",
|
|
2925
|
+
defaultSize: secondaryPanelDefaultSize,
|
|
2926
|
+
minSize: secondaryPanelMinSize,
|
|
2927
|
+
groupResizeBehavior: "preserve-pixel-size",
|
|
2928
|
+
style: { overflow: "hidden" },
|
|
2929
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2930
|
+
"div",
|
|
2931
|
+
{
|
|
2932
|
+
className: (0, import_cn9.cn)(
|
|
2933
|
+
"h-full flex flex-col min-h-0",
|
|
2934
|
+
secondaryPanelClassName
|
|
2935
|
+
),
|
|
2936
|
+
children: secondaryPanel
|
|
2937
|
+
}
|
|
2938
|
+
)
|
|
2939
|
+
}
|
|
2940
|
+
)
|
|
2941
|
+
] })
|
|
2942
|
+
] }) : children;
|
|
2943
|
+
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: [
|
|
2944
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2945
|
+
DeprecationBanner,
|
|
2946
|
+
{
|
|
2947
|
+
replacedBy: deprecation?.replacement,
|
|
2948
|
+
note: deprecation?.reason
|
|
2949
|
+
}
|
|
2950
|
+
),
|
|
2951
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex-1 min-h-0", children: innerBody })
|
|
2952
|
+
] }) : innerBody });
|
|
2953
|
+
const hasZonedFooter = footerLeft || footerCenter || footerRight;
|
|
2954
|
+
const hasFooter = footer || hasZonedFooter;
|
|
2955
|
+
const zonedFooter = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2956
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center gap-1 shrink-0", children: footerLeft }),
|
|
2957
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex-1 flex items-center justify-center gap-1", children: footerCenter }),
|
|
2958
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center gap-1 shrink-0", children: footerRight })
|
|
2959
|
+
] });
|
|
2960
|
+
const footerBar = hasFooter ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2961
|
+
"div",
|
|
2962
|
+
{
|
|
2963
|
+
className: (0, import_cn9.cn)(
|
|
2964
|
+
// Always: detach from scroll + separate from the body with a top border.
|
|
2965
|
+
"shrink-0 border-t border-border/50",
|
|
2966
|
+
WINDOW_CHROME_INTERACTIVE,
|
|
2967
|
+
footerVariant === "bar" && // Compact metadata-bar chrome — thin chips, tiny buttons/icons. Crushes
|
|
2968
|
+
// rich content; consumers with a composer/input bar pass footerVariant="rich".
|
|
2969
|
+
"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"
|
|
2970
|
+
),
|
|
2971
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
2972
|
+
children: footer ?? (footerVariant === "bar" ? zonedFooter : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center gap-1", children: zonedFooter }))
|
|
2973
|
+
}
|
|
2974
|
+
) : null;
|
|
2975
|
+
const bodyPortal = isMounted && bodyHost && !isMobile ? (0, import_react_dom3.createPortal)(
|
|
2976
|
+
/* @__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 }) }) }),
|
|
2977
|
+
bodyHost
|
|
2978
|
+
) : null;
|
|
2979
|
+
if (isMobile) {
|
|
2980
|
+
const mobilePresentation = mobilePresentationOverride ?? registryEntry?.mobile ?? "fullscreen";
|
|
2981
|
+
const mobileSidebarAs = registryEntry?.mobileSidebarAs ?? "drawer";
|
|
2982
|
+
if (mobilePresentation === "hidden") {
|
|
2983
|
+
if (process.env.NODE_ENV !== "production" && overlayId) {
|
|
2984
|
+
console.warn(
|
|
2985
|
+
`[WindowPanel] overlay "${overlayId}" has window.mobile: "hidden" but was opened on a phone. Give its definition another presentation or gate opening.`
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
return null;
|
|
2989
|
+
}
|
|
2990
|
+
if (mobilePresentation === "drawer") {
|
|
2991
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2992
|
+
MobileDrawerSurface,
|
|
2993
|
+
{
|
|
2994
|
+
isOpen: true,
|
|
2995
|
+
title: titleNode ?? title,
|
|
2996
|
+
onClose: handleClose,
|
|
2997
|
+
sidebar,
|
|
2998
|
+
sidebarAs: mobileSidebarAs,
|
|
2999
|
+
footer: footerBar,
|
|
3000
|
+
actionsLeft,
|
|
3001
|
+
actionsRight: resolvedActionsRight,
|
|
3002
|
+
bodyClassName,
|
|
3003
|
+
surfaceLayer,
|
|
3004
|
+
sizeToContent: mobileSizeToContent,
|
|
3005
|
+
hideClose: mobileHideClose,
|
|
3006
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_window_host2.WindowSelectionSurface, { children })
|
|
3007
|
+
}
|
|
3008
|
+
);
|
|
3009
|
+
}
|
|
3010
|
+
if (mobilePresentation === "card") {
|
|
3011
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3012
|
+
MobileCardSurface,
|
|
3013
|
+
{
|
|
3014
|
+
isOpen: true,
|
|
3015
|
+
title: titleNode ?? title,
|
|
3016
|
+
onClose: handleClose,
|
|
3017
|
+
footer: footerBar,
|
|
3018
|
+
actionsRight: resolvedActionsRight,
|
|
3019
|
+
bodyClassName,
|
|
3020
|
+
surfaceLayer,
|
|
3021
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_window_host2.WindowSelectionSurface, { children })
|
|
3022
|
+
}
|
|
3023
|
+
);
|
|
3024
|
+
}
|
|
3025
|
+
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;
|
|
3026
|
+
const mobileEl = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3027
|
+
"div",
|
|
3028
|
+
{
|
|
3029
|
+
...surfaceLayerAttrs,
|
|
3030
|
+
className: (0, import_cn9.cn)(
|
|
3031
|
+
"fixed inset-0 flex flex-col",
|
|
3032
|
+
"bg-card/98 backdrop-blur-md",
|
|
3033
|
+
"overflow-hidden",
|
|
3034
|
+
deprecatedRingClass,
|
|
3035
|
+
className
|
|
3036
|
+
),
|
|
3037
|
+
style: {
|
|
3038
|
+
top: windowConfig.mobileTopOffset,
|
|
3039
|
+
zIndex,
|
|
3040
|
+
display: isTopWindow && !isMinimized ? void 0 : "none",
|
|
3041
|
+
visibility: windowsHidden ? "hidden" : void 0
|
|
3042
|
+
},
|
|
3043
|
+
onPointerDown: onFocus,
|
|
3044
|
+
onPointerDownCapture: claimFront,
|
|
3045
|
+
onFocusCapture: claimFront,
|
|
3046
|
+
children: [
|
|
3047
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3048
|
+
MobileWindowHeader,
|
|
3049
|
+
{
|
|
3050
|
+
title: titleNode ?? title,
|
|
3051
|
+
actionsRight: resolvedActionsRight,
|
|
3052
|
+
onClose: handleClose,
|
|
3053
|
+
hasSidebar,
|
|
3054
|
+
activePaneMobile,
|
|
3055
|
+
onSetActivePane: setActivePaneMobile
|
|
3056
|
+
}
|
|
3057
|
+
),
|
|
3058
|
+
isDebugMode && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DebugStrip, { rect, zIndex }),
|
|
3059
|
+
/* @__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 }) }),
|
|
3060
|
+
footerBar
|
|
3061
|
+
]
|
|
3062
|
+
}
|
|
3063
|
+
);
|
|
3064
|
+
return portalTarget ? (0, import_react_dom3.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system7.FloatingLayer, { children: mobileEl }), portalTarget) : null;
|
|
3065
|
+
}
|
|
3066
|
+
if (popoutMode !== null) {
|
|
3067
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3068
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopoutPortal, { windowId: id, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3069
|
+
"div",
|
|
3070
|
+
{
|
|
3071
|
+
...surfaceLayerAttrs,
|
|
3072
|
+
className: "h-full w-full flex flex-col bg-card text-foreground",
|
|
3073
|
+
children: [
|
|
3074
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3075
|
+
PopoutTopBar,
|
|
3076
|
+
{
|
|
3077
|
+
title: titleNode ?? title,
|
|
3078
|
+
actionsRight: resolvedActionsRight,
|
|
3079
|
+
onDock: handleDockBack
|
|
3080
|
+
}
|
|
3081
|
+
),
|
|
3082
|
+
isDebugMode && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DebugStrip, { rect, zIndex }),
|
|
3083
|
+
/* @__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 }) }),
|
|
3084
|
+
footerBar
|
|
3085
|
+
]
|
|
3086
|
+
}
|
|
3087
|
+
) }),
|
|
3088
|
+
bodyPortal
|
|
3089
|
+
] });
|
|
3090
|
+
}
|
|
3091
|
+
const el = /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3092
|
+
"div",
|
|
3093
|
+
{
|
|
3094
|
+
ref: fitContent ? fitContentRef : void 0,
|
|
3095
|
+
"data-window-panel": "",
|
|
3096
|
+
"data-window-id": id,
|
|
3097
|
+
...surfaceLayerAttrs,
|
|
3098
|
+
...isMinimized ? { "data-matrx-floating-bottom": "" } : {},
|
|
3099
|
+
className: (0, import_cn9.cn)(
|
|
3100
|
+
"fixed",
|
|
3101
|
+
isMaximized ? "overflow-hidden" : "overflow-visible",
|
|
3102
|
+
"matrx-window-enter",
|
|
3103
|
+
// Programmatic rect changes glide; pointer drag/resize stays 1:1.
|
|
3104
|
+
glideReady && !isInteracting && "matrx-window-glide",
|
|
3105
|
+
// Drag-out candidate: ring-2 highlight signals "release here to pop out".
|
|
3106
|
+
isPopoutCandidate && "ring-2 ring-primary ring-offset-2 ring-offset-background transition-shadow",
|
|
3107
|
+
deprecatedRingClass,
|
|
3108
|
+
className
|
|
3109
|
+
),
|
|
3110
|
+
style: {
|
|
3111
|
+
...isMaximized ? { inset: 0, width: "100vw", height: "100dvh" } : {
|
|
3112
|
+
left: rect.x,
|
|
3113
|
+
top: rect.y,
|
|
3114
|
+
...fitContent && !isMinimized ? { width: "max-content", height: "auto" } : { width: rect.width, height: rect.height }
|
|
3115
|
+
},
|
|
3116
|
+
zIndex,
|
|
3117
|
+
// Cap CSS mins to the current rect so arrange/snap geometry wins.
|
|
3118
|
+
// Uncapped minWidth (e.g. 640) was overriding tile widths and stacking
|
|
3119
|
+
// every left-edge window on top of each other.
|
|
3120
|
+
minWidth: isMaximized || isMinimized ? 0 : fitContent ? minWidth ?? 180 : Math.min(minWidth ?? 180, rect.width),
|
|
3121
|
+
minHeight: isMaximized || isMinimized ? 0 : fitContent ? minHeight ?? 80 : Math.min(minHeight ?? 80, rect.height),
|
|
3122
|
+
visibility: windowsHidden ? "hidden" : void 0
|
|
3123
|
+
},
|
|
3124
|
+
onPointerDown: onFocus,
|
|
3125
|
+
onPointerDownCapture: claimFront,
|
|
3126
|
+
onFocusCapture: claimFront,
|
|
3127
|
+
onDoubleClick: isMinimized ? handleRestoreClearingSnapshot : void 0,
|
|
3128
|
+
children: [
|
|
3129
|
+
!isMinimized && !isMaximized && HANDLES.map((h) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3130
|
+
"div",
|
|
3131
|
+
{
|
|
3132
|
+
className: (0, import_cn9.cn)(
|
|
3133
|
+
h.className,
|
|
3134
|
+
"z-50 hover:bg-primary/20 transition-colors"
|
|
3135
|
+
),
|
|
3136
|
+
style: { touchAction: "none" },
|
|
3137
|
+
onPointerDown: onResizeStart(h.edge)
|
|
3138
|
+
},
|
|
3139
|
+
h.edge
|
|
3140
|
+
)),
|
|
3141
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3142
|
+
"div",
|
|
3143
|
+
{
|
|
3144
|
+
className: (0, import_cn9.cn)(
|
|
3145
|
+
"flex h-full w-full min-h-0 flex-col",
|
|
3146
|
+
// Opaque window surface: a window is a solid card, never glass —
|
|
3147
|
+
// glass belongs only on bars floating over moving content (tap-target
|
|
3148
|
+
// placement rule 1). A see-through window let the page underneath
|
|
3149
|
+
// read through its content (Watch AI, 2026-10-12).
|
|
3150
|
+
isMaximized ? "bg-card border border-border shadow-2xl" : "rounded-xl bg-card border border-border shadow-xl",
|
|
3151
|
+
"overflow-hidden"
|
|
3152
|
+
),
|
|
3153
|
+
children: [
|
|
3154
|
+
header,
|
|
3155
|
+
isDebugMode && !isMinimized && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DebugStrip, { rect, zIndex }),
|
|
3156
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3157
|
+
WindowPanelBodyShell,
|
|
3158
|
+
{
|
|
3159
|
+
bodyRef,
|
|
3160
|
+
fitContent,
|
|
3161
|
+
isMaximized,
|
|
3162
|
+
bodyClassName,
|
|
3163
|
+
captureDimensions: isMinimized ? pendingTrayCapture ? {
|
|
3164
|
+
width: pendingTrayCapture.width,
|
|
3165
|
+
height: pendingTrayCapture.height
|
|
3166
|
+
} : retainedBodyDimensionsRef.current ?? {
|
|
3167
|
+
width: Math.max(1, rect.width),
|
|
3168
|
+
height: Math.max(1, rect.height)
|
|
3169
|
+
} : null,
|
|
3170
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(BodyMount, { host: bodyHost })
|
|
3171
|
+
}
|
|
3172
|
+
),
|
|
3173
|
+
isMinimized && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3174
|
+
MinimizedWindowContent,
|
|
3175
|
+
{
|
|
3176
|
+
registryKey: trayRegistryKey,
|
|
3177
|
+
snapshotKey: id,
|
|
3178
|
+
overlayInstanceId,
|
|
3179
|
+
previewData: trayPreviewData,
|
|
3180
|
+
title: minimizedTitle,
|
|
3181
|
+
onRestore: handleRestoreClearingSnapshot
|
|
3182
|
+
}
|
|
3183
|
+
),
|
|
3184
|
+
!isMinimized && footerBar,
|
|
3185
|
+
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" }) })
|
|
3186
|
+
]
|
|
3187
|
+
}
|
|
3188
|
+
)
|
|
3189
|
+
]
|
|
3190
|
+
}
|
|
3191
|
+
);
|
|
3192
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3193
|
+
portalTarget ? (0, import_react_dom3.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_design_system7.FloatingLayer, { children: el }), portalTarget) : null,
|
|
3194
|
+
bodyPortal
|
|
3195
|
+
] });
|
|
3196
|
+
}
|
|
3197
|
+
function BodyMount({ host }) {
|
|
3198
|
+
const slotRef = (0, import_react19.useRef)(null);
|
|
3199
|
+
(0, import_react19.useLayoutEffect)(() => {
|
|
3200
|
+
const slot2 = slotRef.current;
|
|
3201
|
+
if (!slot2 || !host) return void 0;
|
|
3202
|
+
slot2.appendChild(host);
|
|
3203
|
+
return () => {
|
|
3204
|
+
if (host.parentNode === slot2) slot2.removeChild(host);
|
|
3205
|
+
};
|
|
3206
|
+
}, [host]);
|
|
3207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: slotRef, className: "contents", "data-window-body-slot": "" });
|
|
3208
|
+
}
|
|
3209
|
+
function PopOutHeaderButton({ onClick }) {
|
|
3210
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3211
|
+
"button",
|
|
3212
|
+
{
|
|
3213
|
+
type: "button",
|
|
3214
|
+
className: "p-0.5 rounded hover:bg-accent/60 transition-colors text-foreground/60 hover:text-foreground cursor-pointer",
|
|
3215
|
+
onClick,
|
|
3216
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3217
|
+
title: "Pop out into a separate window",
|
|
3218
|
+
"aria-label": "Pop out into a separate window",
|
|
3219
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AppWindowIcon, { className: "w-3.5 h-3.5" })
|
|
3220
|
+
}
|
|
3221
|
+
);
|
|
3222
|
+
}
|
|
3223
|
+
function WindowHeader({
|
|
3224
|
+
title,
|
|
3225
|
+
minimizedTitle,
|
|
3226
|
+
actionsLeft,
|
|
3227
|
+
actionsRight,
|
|
3228
|
+
onDragStart,
|
|
3229
|
+
onMinimize,
|
|
3230
|
+
onToggleMaximize,
|
|
3231
|
+
onRestore,
|
|
3232
|
+
onClose,
|
|
3233
|
+
isMaximized,
|
|
3234
|
+
isMinimized,
|
|
3235
|
+
snapLeft,
|
|
3236
|
+
snapRight,
|
|
3237
|
+
snapTop,
|
|
3238
|
+
snapBottom,
|
|
3239
|
+
snapCentre,
|
|
3240
|
+
arrangeAll,
|
|
3241
|
+
hasSidebar,
|
|
3242
|
+
sidebarOpen,
|
|
3243
|
+
onToggleSidebar,
|
|
3244
|
+
onSaveWindowState,
|
|
3245
|
+
onPopOut
|
|
3246
|
+
}) {
|
|
3247
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3248
|
+
"div",
|
|
3249
|
+
{
|
|
3250
|
+
className: (0, import_cn9.cn)(
|
|
3251
|
+
// 🚨 NEW-8 — the header is a CONTAINER, so a consumer's title/actions can
|
|
3252
|
+
// answer the WINDOW's width instead of the viewport's (`sm:` cannot know
|
|
3253
|
+
// a window is 360px on a 1440px screen — that is how D4 stayed invisible).
|
|
3254
|
+
// The name is `window-header`; `lib/detail/core/headerGeometry.ts` holds it.
|
|
3255
|
+
"@container/window-header",
|
|
3256
|
+
// Open: three in-flow columns — left zone, title, right zone. The two
|
|
3257
|
+
// side columns share the free space equally (never less than their
|
|
3258
|
+
// real content), so the title sits at the WINDOW's centre whatever
|
|
3259
|
+
// each side carries, and still truncates instead of running under an
|
|
3260
|
+
// action cluster when space is tight (D4). Minimized has no title
|
|
3261
|
+
// column, so it keeps the plain row.
|
|
3262
|
+
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",
|
|
3263
|
+
"border-b border-border/50 bg-muted/40 select-none",
|
|
3264
|
+
isMaximized || isMinimized ? "cursor-default" : "cursor-grab active:cursor-grabbing",
|
|
3265
|
+
isMinimized && "h-8 min-h-8 py-0 border-b border-border/50"
|
|
3266
|
+
),
|
|
3267
|
+
"data-window-panel-state": isMinimized ? "minimized" : void 0,
|
|
3268
|
+
style: isMaximized || isMinimized ? void 0 : { touchAction: "none" },
|
|
3269
|
+
onPointerDown: isMaximized || isMinimized ? void 0 : onDragStart,
|
|
3270
|
+
children: [
|
|
3271
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3272
|
+
"div",
|
|
3273
|
+
{
|
|
3274
|
+
className: (0, import_cn9.cn)(
|
|
3275
|
+
"group/tl absolute top-0 left-0 bottom-0 flex items-center z-20",
|
|
3276
|
+
hasSidebar ? "w-28" : "w-24"
|
|
3277
|
+
),
|
|
3278
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3279
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "pl-2", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3280
|
+
TrafficLightGroup,
|
|
3281
|
+
{
|
|
3282
|
+
isMinimized,
|
|
3283
|
+
isMaximized,
|
|
3284
|
+
onClose,
|
|
3285
|
+
onMinimize,
|
|
3286
|
+
onRestore,
|
|
3287
|
+
onToggleMaximize,
|
|
3288
|
+
snapLeft,
|
|
3289
|
+
snapRight,
|
|
3290
|
+
snapTop,
|
|
3291
|
+
snapBottom,
|
|
3292
|
+
snapCentre,
|
|
3293
|
+
arrangeAll,
|
|
3294
|
+
hasSidebar,
|
|
3295
|
+
sidebarOpen,
|
|
3296
|
+
onToggleSidebar,
|
|
3297
|
+
onSaveWindowState,
|
|
3298
|
+
onPopOut
|
|
3299
|
+
}
|
|
3300
|
+
) })
|
|
3301
|
+
}
|
|
3302
|
+
),
|
|
3303
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1 z-10 shrink-0", children: [
|
|
3304
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: hasSidebar ? "w-28" : "w-24" }),
|
|
3305
|
+
!isMinimized && actionsLeft && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3306
|
+
"div",
|
|
3307
|
+
{
|
|
3308
|
+
className: WINDOW_CHROME_ACTIONS,
|
|
3309
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3310
|
+
children: actionsLeft
|
|
3311
|
+
}
|
|
3312
|
+
)
|
|
3313
|
+
] }),
|
|
3314
|
+
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)(
|
|
3315
|
+
"span",
|
|
3316
|
+
{
|
|
3317
|
+
className: "matrx-window-minimized-title block min-w-0 truncate leading-none font-semibold text-foreground",
|
|
3318
|
+
"data-minimized-window-title": true,
|
|
3319
|
+
children: minimizedTitle
|
|
3320
|
+
}
|
|
3321
|
+
) }) : null,
|
|
3322
|
+
!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)(
|
|
3323
|
+
"div",
|
|
3324
|
+
{
|
|
3325
|
+
className: "flex min-w-0 max-w-full items-center overflow-hidden whitespace-nowrap",
|
|
3326
|
+
onPointerDown: (event) => {
|
|
3327
|
+
const target = event.target;
|
|
3328
|
+
if (!(target instanceof Element)) return;
|
|
3329
|
+
if (target.closest(
|
|
3330
|
+
'a, button, input, select, textarea, [role="button"], [role="menuitem"], [data-window-no-drag]'
|
|
3331
|
+
)) {
|
|
3332
|
+
event.stopPropagation();
|
|
3333
|
+
}
|
|
3334
|
+
},
|
|
3335
|
+
children: title
|
|
3336
|
+
}
|
|
3337
|
+
) }),
|
|
3338
|
+
/* @__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)(
|
|
3339
|
+
"div",
|
|
3340
|
+
{
|
|
3341
|
+
className: WINDOW_CHROME_ACTIONS,
|
|
3342
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3343
|
+
children: [
|
|
3344
|
+
onPopOut ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(PopOutHeaderButton, { onClick: onPopOut }) : null,
|
|
3345
|
+
actionsRight
|
|
3346
|
+
]
|
|
3347
|
+
}
|
|
3348
|
+
) })
|
|
3349
|
+
]
|
|
3350
|
+
}
|
|
3351
|
+
);
|
|
3352
|
+
}
|
|
3353
|
+
function TrafficLightGroup({
|
|
3354
|
+
isMinimized,
|
|
3355
|
+
isMaximized,
|
|
3356
|
+
onClose,
|
|
3357
|
+
onMinimize,
|
|
3358
|
+
onRestore,
|
|
3359
|
+
onToggleMaximize,
|
|
3360
|
+
snapLeft,
|
|
3361
|
+
snapRight,
|
|
3362
|
+
snapTop,
|
|
3363
|
+
snapBottom,
|
|
3364
|
+
snapCentre,
|
|
3365
|
+
arrangeAll,
|
|
3366
|
+
hasSidebar,
|
|
3367
|
+
sidebarOpen,
|
|
3368
|
+
onToggleSidebar,
|
|
3369
|
+
onSaveWindowState,
|
|
3370
|
+
onPopOut
|
|
3371
|
+
}) {
|
|
3372
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center gap-1.5 shrink-0 cursor-default", children: [
|
|
3373
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3374
|
+
TrafficLight,
|
|
3375
|
+
{
|
|
3376
|
+
color: "red",
|
|
3377
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(XIcon, { className: "w-2 h-2 matrx-window-light-glyph" }),
|
|
3378
|
+
onClick: onClose ?? void 0,
|
|
3379
|
+
disabled: !onClose,
|
|
3380
|
+
"aria-label": "Close"
|
|
3381
|
+
}
|
|
3382
|
+
),
|
|
3383
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3384
|
+
TrafficLight,
|
|
3385
|
+
{
|
|
3386
|
+
color: "yellow",
|
|
3387
|
+
icon: isMinimized ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3388
|
+
Maximize2Icon,
|
|
3389
|
+
{
|
|
3390
|
+
className: "w-2 h-2 matrx-window-light-glyph"
|
|
3391
|
+
}
|
|
3392
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3393
|
+
MinusIcon,
|
|
3394
|
+
{
|
|
3395
|
+
className: "w-2 h-2 matrx-window-light-glyph"
|
|
3396
|
+
}
|
|
3397
|
+
),
|
|
3398
|
+
onClick: isMinimized ? onRestore : onMinimize,
|
|
3399
|
+
"aria-label": isMinimized ? "Restore" : "Minimize"
|
|
3400
|
+
}
|
|
3401
|
+
),
|
|
3402
|
+
!isMinimized && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3403
|
+
GreenTrafficLight,
|
|
3404
|
+
{
|
|
3405
|
+
isMaximized,
|
|
3406
|
+
onToggleMaximize,
|
|
3407
|
+
onRestore,
|
|
3408
|
+
snapLeft,
|
|
3409
|
+
snapRight,
|
|
3410
|
+
snapTop,
|
|
3411
|
+
snapBottom,
|
|
3412
|
+
snapCentre,
|
|
3413
|
+
arrangeAll,
|
|
3414
|
+
onSaveWindowState,
|
|
3415
|
+
onPopOut
|
|
3416
|
+
}
|
|
3417
|
+
),
|
|
3418
|
+
hasSidebar && !isMinimized && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3419
|
+
"button",
|
|
3420
|
+
{
|
|
3421
|
+
type: "button",
|
|
3422
|
+
className: "ml-0.5 p-0.5 rounded hover:bg-accent/60 transition-colors text-foreground/60 group-hover/tl:text-foreground cursor-pointer",
|
|
3423
|
+
onClick: onToggleSidebar,
|
|
3424
|
+
title: sidebarOpen ? "Hide sidebar" : "Show sidebar",
|
|
3425
|
+
"aria-label": sidebarOpen ? "Hide sidebar" : "Show sidebar",
|
|
3426
|
+
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" })
|
|
3427
|
+
}
|
|
3428
|
+
)
|
|
3429
|
+
] });
|
|
3430
|
+
}
|
|
3431
|
+
function TrafficLight({
|
|
3432
|
+
color,
|
|
3433
|
+
icon,
|
|
3434
|
+
onClick,
|
|
3435
|
+
disabled,
|
|
3436
|
+
"aria-label": label
|
|
3437
|
+
}) {
|
|
3438
|
+
const base = "w-3.5 h-3.5 rounded-full flex items-center justify-center transition-colors shrink-0 relative";
|
|
3439
|
+
const colours = color === "red" ? disabled ? "matrx-window-light-disabled cursor-default" : "matrx-window-light-close cursor-pointer" : "matrx-window-light-minimize cursor-pointer";
|
|
3440
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3441
|
+
"button",
|
|
3442
|
+
{
|
|
3443
|
+
type: "button",
|
|
3444
|
+
className: (0, import_cn9.cn)(base, colours),
|
|
3445
|
+
onClick: disabled ? void 0 : onClick,
|
|
3446
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3447
|
+
"aria-label": label,
|
|
3448
|
+
disabled,
|
|
3449
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3450
|
+
"span",
|
|
3451
|
+
{
|
|
3452
|
+
className: (0, import_cn9.cn)(
|
|
3453
|
+
"opacity-0 transition-opacity duration-100",
|
|
3454
|
+
!disabled && "group-hover/tl:opacity-100"
|
|
3455
|
+
),
|
|
3456
|
+
children: icon
|
|
3457
|
+
}
|
|
3458
|
+
)
|
|
3459
|
+
}
|
|
3460
|
+
);
|
|
3461
|
+
}
|
|
3462
|
+
function GreenTrafficLight({
|
|
3463
|
+
isMaximized,
|
|
3464
|
+
onToggleMaximize,
|
|
3465
|
+
onRestore,
|
|
3466
|
+
snapLeft,
|
|
3467
|
+
snapRight,
|
|
3468
|
+
snapTop,
|
|
3469
|
+
snapBottom,
|
|
3470
|
+
snapCentre,
|
|
3471
|
+
arrangeAll,
|
|
3472
|
+
onSaveWindowState,
|
|
3473
|
+
onPopOut
|
|
3474
|
+
}) {
|
|
3475
|
+
const [dropdownOpen, setDropdownOpen] = (0, import_react19.useState)(false);
|
|
3476
|
+
const leaveTimer = (0, import_react19.useRef)(null);
|
|
3477
|
+
const containerRef = (0, import_react19.useRef)(null);
|
|
3478
|
+
const openDropdown = () => {
|
|
3479
|
+
if (leaveTimer.current) clearTimeout(leaveTimer.current);
|
|
3480
|
+
setDropdownOpen(true);
|
|
3481
|
+
};
|
|
3482
|
+
const scheduleClose = () => {
|
|
3483
|
+
leaveTimer.current = setTimeout(() => {
|
|
3484
|
+
setDropdownOpen(false);
|
|
3485
|
+
}, 120);
|
|
3486
|
+
};
|
|
3487
|
+
const handleAction = (fn) => {
|
|
3488
|
+
fn();
|
|
3489
|
+
setDropdownOpen(false);
|
|
3490
|
+
};
|
|
3491
|
+
(0, import_react19.useEffect)(() => {
|
|
3492
|
+
if (!dropdownOpen) return void 0;
|
|
3493
|
+
const onPointerOutside = (e) => {
|
|
3494
|
+
const target = e.target;
|
|
3495
|
+
if (containerRef.current && !containerRef.current.contains(target)) {
|
|
3496
|
+
if (target.closest?.("[data-radix-portal]")) return;
|
|
3497
|
+
setDropdownOpen(false);
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
document.addEventListener("pointerdown", onPointerOutside);
|
|
3501
|
+
return () => document.removeEventListener("pointerdown", onPointerOutside);
|
|
3502
|
+
}, [dropdownOpen]);
|
|
3503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3504
|
+
"div",
|
|
3505
|
+
{
|
|
3506
|
+
ref: containerRef,
|
|
3507
|
+
className: "relative",
|
|
3508
|
+
onMouseEnter: openDropdown,
|
|
3509
|
+
onMouseLeave: scheduleClose,
|
|
3510
|
+
children: [
|
|
3511
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3512
|
+
"button",
|
|
3513
|
+
{
|
|
3514
|
+
type: "button",
|
|
3515
|
+
className: (0, import_cn9.cn)(
|
|
3516
|
+
"w-3.5 h-3.5 rounded-full flex items-center justify-center transition-colors shrink-0",
|
|
3517
|
+
"matrx-window-light-maximize cursor-pointer"
|
|
3518
|
+
),
|
|
3519
|
+
onClick: () => {
|
|
3520
|
+
if ("ontouchstart" in window || navigator.maxTouchPoints > 0) {
|
|
3521
|
+
setDropdownOpen((prev) => !prev);
|
|
3522
|
+
} else {
|
|
3523
|
+
handleAction(onToggleMaximize);
|
|
3524
|
+
}
|
|
3525
|
+
},
|
|
3526
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3527
|
+
"aria-label": isMaximized ? "Restore" : "Maximize",
|
|
3528
|
+
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)(
|
|
3529
|
+
Minimize2Icon,
|
|
3530
|
+
{
|
|
3531
|
+
className: "w-2 h-2 matrx-window-light-glyph absolute"
|
|
3532
|
+
}
|
|
3533
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3534
|
+
Maximize2Icon,
|
|
3535
|
+
{
|
|
3536
|
+
className: "w-2 h-2 matrx-window-light-glyph absolute"
|
|
3537
|
+
}
|
|
3538
|
+
) })
|
|
3539
|
+
}
|
|
3540
|
+
),
|
|
3541
|
+
dropdownOpen && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3542
|
+
"div",
|
|
3543
|
+
{
|
|
3544
|
+
className: (0, import_cn9.cn)(
|
|
3545
|
+
"absolute left-0 top-full mt-1.5 z-50",
|
|
3546
|
+
"w-52 rounded-xl overflow-hidden",
|
|
3547
|
+
"bg-card/95 backdrop-blur-xl border border-border shadow-2xl",
|
|
3548
|
+
"py-1 text-xs"
|
|
3549
|
+
),
|
|
3550
|
+
onMouseEnter: openDropdown,
|
|
3551
|
+
onMouseLeave: scheduleClose,
|
|
3552
|
+
children: [
|
|
3553
|
+
!isMaximized && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3554
|
+
/* @__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" }),
|
|
3555
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1 px-2 pb-2", children: [
|
|
3556
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1 justify-center", children: [
|
|
3557
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3558
|
+
LayoutIconButton,
|
|
3559
|
+
{
|
|
3560
|
+
onClick: () => handleAction(snapLeft),
|
|
3561
|
+
type: "left-half"
|
|
3562
|
+
}
|
|
3563
|
+
),
|
|
3564
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3565
|
+
LayoutIconButton,
|
|
3566
|
+
{
|
|
3567
|
+
onClick: () => handleAction(snapRight),
|
|
3568
|
+
type: "right-half"
|
|
3569
|
+
}
|
|
3570
|
+
),
|
|
3571
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3572
|
+
LayoutIconButton,
|
|
3573
|
+
{
|
|
3574
|
+
onClick: () => handleAction(snapTop),
|
|
3575
|
+
type: "top-half"
|
|
3576
|
+
}
|
|
3577
|
+
),
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3579
|
+
LayoutIconButton,
|
|
3580
|
+
{
|
|
3581
|
+
onClick: () => handleAction(snapBottom),
|
|
3582
|
+
type: "bottom-half"
|
|
3583
|
+
}
|
|
3584
|
+
)
|
|
3585
|
+
] }),
|
|
3586
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1 justify-center", children: [
|
|
3587
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3588
|
+
LayoutIconButton,
|
|
3589
|
+
{
|
|
3590
|
+
onClick: () => handleAction(snapCentre),
|
|
3591
|
+
type: "centre"
|
|
3592
|
+
}
|
|
3593
|
+
),
|
|
3594
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3595
|
+
LayoutIconButton,
|
|
3596
|
+
{
|
|
3597
|
+
onClick: () => handleAction(onToggleMaximize),
|
|
3598
|
+
type: "full"
|
|
3599
|
+
}
|
|
3600
|
+
)
|
|
3601
|
+
] })
|
|
3602
|
+
] }),
|
|
3603
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "border-t border-border/50 my-1" }),
|
|
3604
|
+
/* @__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" }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-1 px-2 pb-2", children: [
|
|
3606
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1 justify-center", children: [
|
|
3607
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3608
|
+
LayoutIconButton,
|
|
3609
|
+
{
|
|
3610
|
+
onClick: () => handleAction(() => arrangeAll("grid4")),
|
|
3611
|
+
type: "grid4"
|
|
3612
|
+
}
|
|
3613
|
+
),
|
|
3614
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3615
|
+
LayoutIconButton,
|
|
3616
|
+
{
|
|
3617
|
+
onClick: () => handleAction(() => arrangeAll("grid6")),
|
|
3618
|
+
type: "grid6"
|
|
3619
|
+
}
|
|
3620
|
+
),
|
|
3621
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3622
|
+
LayoutIconButton,
|
|
3623
|
+
{
|
|
3624
|
+
onClick: () => handleAction(() => arrangeAll("grid8")),
|
|
3625
|
+
type: "grid8"
|
|
3626
|
+
}
|
|
3627
|
+
),
|
|
3628
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3629
|
+
LayoutIconButton,
|
|
3630
|
+
{
|
|
3631
|
+
onClick: () => handleAction(() => arrangeAll("grid9")),
|
|
3632
|
+
type: "grid9"
|
|
3633
|
+
}
|
|
3634
|
+
),
|
|
3635
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3636
|
+
LayoutIconButton,
|
|
3637
|
+
{
|
|
3638
|
+
onClick: () => handleAction(() => arrangeAll("grid12")),
|
|
3639
|
+
type: "grid12"
|
|
3640
|
+
}
|
|
3641
|
+
)
|
|
3642
|
+
] }),
|
|
3643
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1 justify-center", children: [
|
|
3644
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3645
|
+
LayoutIconButton,
|
|
3646
|
+
{
|
|
3647
|
+
onClick: () => handleAction(() => arrangeAll("stackRight2")),
|
|
3648
|
+
type: "stackRight2"
|
|
3649
|
+
}
|
|
3650
|
+
),
|
|
3651
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3652
|
+
LayoutIconButton,
|
|
3653
|
+
{
|
|
3654
|
+
onClick: () => handleAction(() => arrangeAll("stackRight3")),
|
|
3655
|
+
type: "stackRight3"
|
|
3656
|
+
}
|
|
3657
|
+
),
|
|
3658
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3659
|
+
LayoutIconButton,
|
|
3660
|
+
{
|
|
3661
|
+
onClick: () => handleAction(() => arrangeAll("stackRight4")),
|
|
3662
|
+
type: "stackRight4"
|
|
3663
|
+
}
|
|
3664
|
+
),
|
|
3665
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3666
|
+
LayoutIconButton,
|
|
3667
|
+
{
|
|
3668
|
+
onClick: () => handleAction(() => arrangeAll("stackRight5")),
|
|
3669
|
+
type: "stackRight5"
|
|
3670
|
+
}
|
|
3671
|
+
)
|
|
3672
|
+
] }),
|
|
3673
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-1 justify-center", children: [
|
|
3674
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3675
|
+
LayoutIconButton,
|
|
3676
|
+
{
|
|
3677
|
+
onClick: () => handleAction(() => arrangeAll("stackLeft2")),
|
|
3678
|
+
type: "stackLeft2"
|
|
3679
|
+
}
|
|
3680
|
+
),
|
|
3681
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3682
|
+
LayoutIconButton,
|
|
3683
|
+
{
|
|
3684
|
+
onClick: () => handleAction(() => arrangeAll("stackLeft3")),
|
|
3685
|
+
type: "stackLeft3"
|
|
3686
|
+
}
|
|
3687
|
+
),
|
|
3688
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3689
|
+
LayoutIconButton,
|
|
3690
|
+
{
|
|
3691
|
+
onClick: () => handleAction(() => arrangeAll("stackLeft4")),
|
|
3692
|
+
type: "stackLeft4"
|
|
3693
|
+
}
|
|
3694
|
+
),
|
|
3695
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3696
|
+
LayoutIconButton,
|
|
3697
|
+
{
|
|
3698
|
+
onClick: () => handleAction(() => arrangeAll("stackLeft5")),
|
|
3699
|
+
type: "stackLeft5"
|
|
3700
|
+
}
|
|
3701
|
+
)
|
|
3702
|
+
] })
|
|
3703
|
+
] }),
|
|
3704
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "border-t border-border/50 my-1" })
|
|
3705
|
+
] }),
|
|
3706
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
3707
|
+
"button",
|
|
3708
|
+
{
|
|
3709
|
+
type: "button",
|
|
3710
|
+
className: "flex items-center gap-2.5 w-full px-3 py-1.5 hover:bg-accent transition-colors text-foreground/80",
|
|
3711
|
+
onClick: () => handleAction(onToggleMaximize),
|
|
3712
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3713
|
+
children: isMaximized ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3714
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Minimize2Icon, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
3715
|
+
" Exit Full Screen"
|
|
3716
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3717
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(RectangleVerticalIcon, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
3718
|
+
" Enter Full Screen"
|
|
3719
|
+
] })
|
|
3720
|
+
}
|
|
3721
|
+
),
|
|
3722
|
+
isMaximized && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3723
|
+
"button",
|
|
3724
|
+
{
|
|
3725
|
+
type: "button",
|
|
3726
|
+
className: "flex items-center gap-2.5 w-full px-3 py-1.5 hover:bg-accent transition-colors text-foreground/80",
|
|
3727
|
+
onClick: () => handleAction(onRestore),
|
|
3728
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3729
|
+
children: [
|
|
3730
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Maximize2Icon, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
3731
|
+
"Restore window"
|
|
3732
|
+
]
|
|
3733
|
+
}
|
|
3734
|
+
),
|
|
3735
|
+
onPopOut && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3736
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "border-t border-border/50 my-1" }),
|
|
3737
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3738
|
+
"button",
|
|
3739
|
+
{
|
|
3740
|
+
type: "button",
|
|
3741
|
+
className: "flex items-center gap-2.5 w-full px-3 py-1.5 transition-colors text-foreground/80 hover:bg-accent",
|
|
3742
|
+
onClick: () => handleAction(onPopOut),
|
|
3743
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3744
|
+
title: "Pop out into a separate window",
|
|
3745
|
+
children: [
|
|
3746
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AppWindowIcon, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
3747
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "flex-1 text-left", children: "Pop out" })
|
|
3748
|
+
]
|
|
3749
|
+
}
|
|
3750
|
+
)
|
|
3751
|
+
] }),
|
|
3752
|
+
onSaveWindowState && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
3753
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "border-t border-border/50 my-1" }),
|
|
3754
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
3755
|
+
"button",
|
|
3756
|
+
{
|
|
3757
|
+
type: "button",
|
|
3758
|
+
className: "flex items-center gap-2.5 w-full px-3 py-1.5 hover:bg-accent transition-colors text-foreground/80",
|
|
3759
|
+
onClick: () => handleAction(onSaveWindowState),
|
|
3760
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3761
|
+
children: [
|
|
3762
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SaveIcon, { className: "w-3.5 h-3.5 shrink-0" }),
|
|
3763
|
+
"Save window state"
|
|
3764
|
+
]
|
|
3765
|
+
}
|
|
3766
|
+
)
|
|
3767
|
+
] })
|
|
3768
|
+
]
|
|
3769
|
+
}
|
|
3770
|
+
)
|
|
3771
|
+
]
|
|
3772
|
+
}
|
|
3773
|
+
);
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
// src/window.ts
|
|
3777
|
+
var import_window_host4 = require("@ai-matrx/summon/window-host");
|
|
3778
|
+
var import_window_host5 = require("@ai-matrx/summon/window-host");
|
|
3779
|
+
|
|
3780
|
+
// src/window/WindowSidebar.tsx
|
|
3781
|
+
var import_cn10 = require("@ai-matrx/design-system/cn");
|
|
3782
|
+
var import_design_system9 = require("@ai-matrx/design-system");
|
|
3783
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3784
|
+
function WindowSidebar({
|
|
3785
|
+
items,
|
|
3786
|
+
activeItem,
|
|
3787
|
+
onSelect,
|
|
3788
|
+
className
|
|
3789
|
+
}) {
|
|
3790
|
+
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) => {
|
|
3791
|
+
const Icon = item.icon;
|
|
3792
|
+
const isActive = item.value === activeItem;
|
|
3793
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
3794
|
+
"button",
|
|
3795
|
+
{
|
|
3796
|
+
onClick: () => onSelect(item.value),
|
|
3797
|
+
title: item.description,
|
|
3798
|
+
className: (0, import_cn10.cn)(
|
|
3799
|
+
"w-full flex items-center gap-2.5 px-3 py-2 text-left text-xs transition-colors",
|
|
3800
|
+
isActive ? "bg-primary/10 text-primary-ink border-r-2 border-primary" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
3801
|
+
),
|
|
3802
|
+
children: [
|
|
3803
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
3804
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "truncate", children: item.label })
|
|
3805
|
+
]
|
|
3806
|
+
},
|
|
3807
|
+
item.value
|
|
3808
|
+
);
|
|
3809
|
+
}) }) });
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3812
|
+
// src/window/useOverlaySurfaceRenderAck.ts
|
|
3813
|
+
var import_react21 = require("react");
|
|
3814
|
+
var import_summon11 = require("@ai-matrx/summon");
|
|
3815
|
+
function useOverlaySurfaceRenderAck(overlayId, active) {
|
|
3816
|
+
(0, import_react21.useEffect)(() => {
|
|
3817
|
+
if (!active) return void 0;
|
|
3818
|
+
(0, import_summon11.ackOverlaySurfaceRender)(overlayId);
|
|
3819
|
+
return () => (0, import_summon11.clearOverlaySurfaceRender)(overlayId);
|
|
3820
|
+
}, [active, overlayId]);
|
|
3821
|
+
}
|
|
3822
|
+
|
|
3823
|
+
// src/window.ts
|
|
3824
|
+
var import_window_host6 = require("@ai-matrx/summon/window-host");
|
|
3825
|
+
|
|
3826
|
+
// src/window/tray/TrayStatusChip.tsx
|
|
3827
|
+
var import_cn11 = require("@ai-matrx/design-system/cn");
|
|
3828
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
3829
|
+
var TONE_CLASSES = {
|
|
3830
|
+
neutral: {
|
|
3831
|
+
fg: "text-muted-foreground",
|
|
3832
|
+
dot: "bg-muted-foreground/50"
|
|
3833
|
+
},
|
|
3834
|
+
info: {
|
|
3835
|
+
fg: "text-info",
|
|
3836
|
+
dot: "bg-info"
|
|
3837
|
+
},
|
|
3838
|
+
warning: {
|
|
3839
|
+
fg: "text-warning",
|
|
3840
|
+
dot: "bg-warning"
|
|
3841
|
+
},
|
|
3842
|
+
elevated: {
|
|
3843
|
+
fg: "text-warning",
|
|
3844
|
+
dot: "bg-warning"
|
|
3845
|
+
},
|
|
3846
|
+
critical: {
|
|
3847
|
+
fg: "text-destructive",
|
|
3848
|
+
dot: "bg-destructive"
|
|
3849
|
+
}
|
|
3850
|
+
};
|
|
3851
|
+
function TrayStatusChip({
|
|
3852
|
+
icon: Icon,
|
|
3853
|
+
tone,
|
|
3854
|
+
count,
|
|
3855
|
+
caption,
|
|
3856
|
+
segments,
|
|
3857
|
+
pulse
|
|
3858
|
+
}) {
|
|
3859
|
+
const t = TONE_CLASSES[tone];
|
|
3860
|
+
const visibleSegments = (segments ?? []).filter((s) => s.count > 0);
|
|
3861
|
+
const showBreakdown = visibleSegments.length > 1;
|
|
3862
|
+
const hasCount = typeof count === "number";
|
|
3863
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex h-full items-center gap-1.5 overflow-hidden", children: [
|
|
3864
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3865
|
+
Icon,
|
|
3866
|
+
{
|
|
3867
|
+
className: (0, import_cn11.cn)("h-3.5 w-3.5 shrink-0", t.fg, pulse && "animate-pulse"),
|
|
3868
|
+
"aria-hidden": true
|
|
3869
|
+
}
|
|
3870
|
+
),
|
|
3871
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex min-w-0 items-baseline gap-1", children: [
|
|
3872
|
+
hasCount ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: (0, import_cn11.cn)("text-xs font-semibold tabular-nums", t.fg), children: count }) : null,
|
|
3873
|
+
caption ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: caption }) : null
|
|
3874
|
+
] }),
|
|
3875
|
+
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)(
|
|
3876
|
+
"span",
|
|
3877
|
+
{
|
|
3878
|
+
className: "flex items-center gap-0.5",
|
|
3879
|
+
title: s.label ? `${s.count} ${s.label}` : void 0,
|
|
3880
|
+
children: [
|
|
3881
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3882
|
+
"span",
|
|
3883
|
+
{
|
|
3884
|
+
className: (0, import_cn11.cn)(
|
|
3885
|
+
"h-1.5 w-1.5 rounded-full",
|
|
3886
|
+
TONE_CLASSES[s.tone].dot
|
|
3887
|
+
)
|
|
3888
|
+
}
|
|
3889
|
+
),
|
|
3890
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[10px] tabular-nums text-muted-foreground", children: s.count })
|
|
3891
|
+
]
|
|
3892
|
+
},
|
|
3893
|
+
i
|
|
3894
|
+
)) }) : null
|
|
3895
|
+
] });
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3898
|
+
// src/window/popout/usePopoutContainer.ts
|
|
3899
|
+
function usePopoutContainer() {
|
|
3900
|
+
return usePopout().popoutContainer ?? void 0;
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
// src/window/utils/mayRaiseUnbidden.ts
|
|
3904
|
+
var import_summon12 = require("@ai-matrx/summon");
|
|
3905
|
+
function isUnderRoute(pathname, route) {
|
|
3906
|
+
if (pathname === route) return true;
|
|
3907
|
+
const base = route.endsWith("/") ? route.slice(0, -1) : route;
|
|
3908
|
+
return pathname === base || pathname.startsWith(`${base}/`);
|
|
3909
|
+
}
|
|
3910
|
+
function describeHome(h) {
|
|
3911
|
+
return typeof h === "string" ? h : `${h.route} (exact)`;
|
|
3912
|
+
}
|
|
3913
|
+
function mayRaiseUnbidden(overlayId, pathname) {
|
|
3914
|
+
const entry = (0, import_summon12.getOverlayMeta)(overlayId);
|
|
3915
|
+
const declared = entry?.window?.unbiddenHome ?? [];
|
|
3916
|
+
const home = declared.map(describeHome);
|
|
3917
|
+
if (home.length === 0) {
|
|
3918
|
+
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`;
|
|
3919
|
+
console.warn(`[window-panels] unbidden raise refused \u2014 ${reason}`);
|
|
3920
|
+
return { allowed: false, reason, code: "no-declared-home", home };
|
|
3921
|
+
}
|
|
3922
|
+
if (!pathname) {
|
|
3923
|
+
const reason = `window "${overlayId}" was asked to open itself with no current route, so its home (${home.join(", ")}) could not be confirmed`;
|
|
3924
|
+
console.warn(`[window-panels] unbidden raise refused \u2014 ${reason}`);
|
|
3925
|
+
return { allowed: false, reason, code: "away-from-home", home };
|
|
3926
|
+
}
|
|
3927
|
+
const matched = declared.find(
|
|
3928
|
+
(h) => typeof h === "string" ? isUnderRoute(pathname, h) : pathname === h.route
|
|
3929
|
+
);
|
|
3930
|
+
const match = matched === void 0 ? void 0 : describeHome(matched);
|
|
3931
|
+
if (!match) {
|
|
3932
|
+
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`;
|
|
3933
|
+
console.info(`[window-panels] unbidden raise deferred \u2014 ${reason}`);
|
|
3934
|
+
return { allowed: false, reason, code: "away-from-home", home };
|
|
3935
|
+
}
|
|
3936
|
+
return {
|
|
3937
|
+
allowed: true,
|
|
3938
|
+
reason: `window "${overlayId}" is on its declared home "${match}"`,
|
|
3939
|
+
home
|
|
3940
|
+
};
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
// src/window/utils/lazy-bundle-guard.ts
|
|
3944
|
+
var import_global_slot5 = require("@ai-matrx/kit/global-slot");
|
|
3945
|
+
var guard = () => (0, import_global_slot5.globalSlot)("ai-matrx.summon.lazyBundleGuard", () => {
|
|
3946
|
+
const state2 = { bootInProgress: typeof window !== "undefined", reported: /* @__PURE__ */ new Set() };
|
|
3947
|
+
if (state2.bootInProgress) {
|
|
3948
|
+
setTimeout(() => {
|
|
3949
|
+
state2.bootInProgress = false;
|
|
3950
|
+
}, 0);
|
|
3951
|
+
}
|
|
3952
|
+
return state2;
|
|
3953
|
+
});
|
|
3954
|
+
guard();
|
|
3955
|
+
function assertLazyLoaded(moduleId, report) {
|
|
3956
|
+
if (typeof window === "undefined") return;
|
|
3957
|
+
const callSiteStack = (new Error("call site").stack ?? "").split("\n").filter((line) => !/lazy-bundle-guard\.|at Error\s*$|^Error/i.test(line)).join("\n");
|
|
3958
|
+
queueMicrotask(() => {
|
|
3959
|
+
const state2 = guard();
|
|
3960
|
+
if (!state2.bootInProgress || state2.reported.has(moduleId)) return;
|
|
3961
|
+
state2.reported.add(moduleId);
|
|
3962
|
+
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.)`;
|
|
3963
|
+
if (report) {
|
|
3964
|
+
report(message, callSiteStack);
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3967
|
+
console.warn(
|
|
3968
|
+
"[overlays bundle leak]",
|
|
3969
|
+
`
|
|
3970
|
+
${message}${callSiteStack ? `
|
|
3971
|
+
|
|
3972
|
+
Eager-import chain (top frame is the file that statically imported it):
|
|
3973
|
+
${callSiteStack}` : ""}`
|
|
3974
|
+
);
|
|
3975
|
+
});
|
|
3976
|
+
}
|
|
3977
|
+
//# sourceMappingURL=window.cjs.map
|