@surdeddd/wmkit 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -16
- package/README.ru.md +154 -10
- package/dist/angular.cjs +4 -3
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.d.cts +2 -2
- package/dist/angular.d.ts +2 -2
- package/dist/angular.js +2 -1
- package/dist/angular.js.map +1 -1
- package/dist/{binder-0Oku22Nc.d.ts → binder-B2A2IZGh.d.ts} +7 -2
- package/dist/{binder-Dq_AZRbo.d.cts → binder-Dl07SXIM.d.cts} +7 -2
- package/dist/{chunk-YCHJXSTC.js → chunk-OOEDFA7K.js} +404 -135
- package/dist/chunk-OOEDFA7K.js.map +1 -0
- package/dist/{chunk-B35DANTX.cjs → chunk-TJ6QOOJ2.cjs} +405 -134
- package/dist/chunk-TJ6QOOJ2.cjs.map +1 -0
- package/dist/index.cjs +24 -16
- package/dist/index.d.cts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +1 -1
- package/dist/persist.cjs +22 -3
- package/dist/persist.cjs.map +1 -1
- package/dist/persist.d.cts +3 -1
- package/dist/persist.d.ts +3 -1
- package/dist/persist.js +22 -3
- package/dist/persist.js.map +1 -1
- package/dist/popout.cjs +3 -4
- package/dist/popout.cjs.map +1 -1
- package/dist/popout.d.cts +1 -1
- package/dist/popout.d.ts +1 -1
- package/dist/popout.js +3 -4
- package/dist/popout.js.map +1 -1
- package/dist/react.cjs +4 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +2 -1
- package/dist/react.js.map +1 -1
- package/dist/solid.cjs +4 -3
- package/dist/solid.cjs.map +1 -1
- package/dist/solid.d.cts +2 -2
- package/dist/solid.d.ts +2 -2
- package/dist/solid.js +2 -1
- package/dist/solid.js.map +1 -1
- package/dist/svelte.cjs +8 -5
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.d.cts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +6 -3
- package/dist/svelte.js.map +1 -1
- package/dist/themes/retro.css +2 -0
- package/dist/{types-DdCtDHgt.d.cts → types-t6pyVYKN.d.cts} +21 -7
- package/dist/{types-DdCtDHgt.d.ts → types-t6pyVYKN.d.ts} +21 -7
- package/dist/vue.cjs +4 -3
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.cts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +2 -1
- package/dist/vue.js.map +1 -1
- package/package.json +7 -7
- package/dist/chunk-B35DANTX.cjs.map +0 -1
- package/dist/chunk-YCHJXSTC.js.map +0 -1
|
@@ -40,19 +40,48 @@ function clampSize(size, min, max) {
|
|
|
40
40
|
}
|
|
41
41
|
function clampToViewport(bounds, viewport, minVisible) {
|
|
42
42
|
if (viewport.width <= 0 || viewport.height <= 0) return bounds;
|
|
43
|
-
const
|
|
44
|
-
const
|
|
43
|
+
const visibleX = Math.min(minVisible, bounds.width);
|
|
44
|
+
const visibleY = Math.min(minVisible, bounds.height);
|
|
45
|
+
const x = clamp(bounds.x, visibleX - bounds.width, Math.max(0, viewport.width - visibleX));
|
|
46
|
+
const y = clamp(bounds.y, 0, Math.max(0, viewport.height - visibleY));
|
|
45
47
|
return x === bounds.x && y === bounds.y ? bounds : { ...bounds, x, y };
|
|
46
48
|
}
|
|
47
49
|
function boundsEqual(a, b) {
|
|
48
50
|
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
49
51
|
}
|
|
52
|
+
function applyAspect(size, ratio, min, max, drive) {
|
|
53
|
+
if (!Number.isFinite(ratio) || ratio <= 0) return clampSize(size, min, max);
|
|
54
|
+
const maxWidth = max ? max.width : Number.POSITIVE_INFINITY;
|
|
55
|
+
const maxHeight = max ? max.height : Number.POSITIVE_INFINITY;
|
|
56
|
+
if (drive === "height") {
|
|
57
|
+
const height = clamp(
|
|
58
|
+
size.height,
|
|
59
|
+
Math.max(min.height, min.width / ratio),
|
|
60
|
+
Math.min(maxHeight, maxWidth / ratio)
|
|
61
|
+
);
|
|
62
|
+
return { width: height * ratio, height };
|
|
63
|
+
}
|
|
64
|
+
const width = clamp(
|
|
65
|
+
size.width,
|
|
66
|
+
Math.max(min.width, min.height * ratio),
|
|
67
|
+
Math.min(maxWidth, maxHeight * ratio)
|
|
68
|
+
);
|
|
69
|
+
return { width, height: width / ratio };
|
|
70
|
+
}
|
|
50
71
|
function zoneBounds(zone, viewport) {
|
|
51
72
|
const w = viewport.width;
|
|
52
73
|
const h = viewport.height;
|
|
53
74
|
const halfW = Math.round(w / 2);
|
|
54
75
|
const halfH = Math.round(h / 2);
|
|
76
|
+
const oneThird = Math.round(w / 3);
|
|
77
|
+
const twoThirds = Math.round(w * 2 / 3);
|
|
55
78
|
switch (zone) {
|
|
79
|
+
case "left-third":
|
|
80
|
+
return { x: 0, y: 0, width: oneThird, height: h };
|
|
81
|
+
case "center-third":
|
|
82
|
+
return { x: oneThird, y: 0, width: twoThirds - oneThird, height: h };
|
|
83
|
+
case "right-third":
|
|
84
|
+
return { x: twoThirds, y: 0, width: w - twoThirds, height: h };
|
|
56
85
|
case "left":
|
|
57
86
|
return { x: 0, y: 0, width: halfW, height: h };
|
|
58
87
|
case "right":
|
|
@@ -148,8 +177,14 @@ var ZONES = [
|
|
|
148
177
|
"top-left",
|
|
149
178
|
"top-right",
|
|
150
179
|
"bottom-left",
|
|
151
|
-
"bottom-right"
|
|
180
|
+
"bottom-right",
|
|
181
|
+
"left-third",
|
|
182
|
+
"center-third",
|
|
183
|
+
"right-third"
|
|
152
184
|
];
|
|
185
|
+
function normalizeWorkspace(value, fallback) {
|
|
186
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
187
|
+
}
|
|
153
188
|
function createWindowManager(options = {}) {
|
|
154
189
|
const emitter = createEmitter();
|
|
155
190
|
const keepInViewport = options.keepInViewport ?? true;
|
|
@@ -163,12 +198,14 @@ function createWindowManager(options = {}) {
|
|
|
163
198
|
let order = [];
|
|
164
199
|
let focusedId = null;
|
|
165
200
|
let viewport = options.viewport ?? { width: 0, height: 0 };
|
|
201
|
+
let workspace = normalizeWorkspace(options.workspace, 0);
|
|
166
202
|
let seq = 0;
|
|
167
203
|
let idCounter = 0;
|
|
168
204
|
let cascadeIndex = 0;
|
|
169
205
|
let snapshot = null;
|
|
170
206
|
let batchDepth = 0;
|
|
171
207
|
let batchDirty = false;
|
|
208
|
+
let historyDirty = false;
|
|
172
209
|
const pendingEvents = [];
|
|
173
210
|
const past = [];
|
|
174
211
|
let future = [];
|
|
@@ -179,13 +216,18 @@ function createWindowManager(options = {}) {
|
|
|
179
216
|
const layouts = /* @__PURE__ */ new Map();
|
|
180
217
|
function getState() {
|
|
181
218
|
if (!snapshot) {
|
|
182
|
-
snapshot = { windows, order, focusedId, viewport };
|
|
219
|
+
snapshot = { windows, order, focusedId, viewport, workspace };
|
|
183
220
|
}
|
|
184
221
|
return snapshot;
|
|
185
222
|
}
|
|
186
223
|
function commit() {
|
|
187
224
|
snapshot = null;
|
|
188
225
|
batchDirty = true;
|
|
226
|
+
historyDirty = true;
|
|
227
|
+
}
|
|
228
|
+
function commitQuiet() {
|
|
229
|
+
snapshot = null;
|
|
230
|
+
batchDirty = true;
|
|
189
231
|
}
|
|
190
232
|
function flushEvents() {
|
|
191
233
|
while (pendingEvents.length > 0) {
|
|
@@ -223,10 +265,14 @@ function createWindowManager(options = {}) {
|
|
|
223
265
|
order = next;
|
|
224
266
|
return changed;
|
|
225
267
|
}
|
|
268
|
+
function onActiveWorkspace(win) {
|
|
269
|
+
return win.workspace === workspace;
|
|
270
|
+
}
|
|
226
271
|
function topModalId() {
|
|
227
272
|
for (let i = order.length - 1; i >= 0; i -= 1) {
|
|
228
273
|
const win = windows[order[i]];
|
|
229
|
-
if (win.layer === "modal" && win.stage !== "minimized"
|
|
274
|
+
if (win.layer === "modal" && win.stage !== "minimized" && onActiveWorkspace(win))
|
|
275
|
+
return win.id;
|
|
230
276
|
}
|
|
231
277
|
return null;
|
|
232
278
|
}
|
|
@@ -234,7 +280,7 @@ function createWindowManager(options = {}) {
|
|
|
234
280
|
const modal = topModalId();
|
|
235
281
|
return order.filter((id) => {
|
|
236
282
|
const win = windows[id];
|
|
237
|
-
if (win.stage === "minimized") return false;
|
|
283
|
+
if (win.stage === "minimized" || !onActiveWorkspace(win)) return false;
|
|
238
284
|
if (modal && win.layer !== "modal") return false;
|
|
239
285
|
return true;
|
|
240
286
|
});
|
|
@@ -246,8 +292,9 @@ function createWindowManager(options = {}) {
|
|
|
246
292
|
function emitFocus(win, previous) {
|
|
247
293
|
queueEvent(() => emitter.emit("focus", { window: win, previous }));
|
|
248
294
|
}
|
|
249
|
-
function normalizeSize(size, win) {
|
|
250
|
-
return clampSize(size, win.minSize, win.maxSize);
|
|
295
|
+
function normalizeSize(size, win, drive = "width") {
|
|
296
|
+
if (win.aspectRatio === null) return clampSize(size, win.minSize, win.maxSize);
|
|
297
|
+
return applyAspect(size, win.aspectRatio, win.minSize, win.maxSize, drive);
|
|
251
298
|
}
|
|
252
299
|
function positionForOpen(init) {
|
|
253
300
|
if (init.x !== void 0 && init.y !== void 0) return { x: init.x, y: init.y };
|
|
@@ -269,11 +316,12 @@ function createWindowManager(options = {}) {
|
|
|
269
316
|
width: init.maxWidth ?? Number.POSITIVE_INFINITY,
|
|
270
317
|
height: init.maxHeight ?? Number.POSITIVE_INFINITY
|
|
271
318
|
} : null;
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
319
|
+
const aspectRatio = init.aspectRatio !== void 0 && Number.isFinite(init.aspectRatio) && init.aspectRatio > 0 ? init.aspectRatio : null;
|
|
320
|
+
const requested = {
|
|
321
|
+
width: init.width ?? defaultSize.width,
|
|
322
|
+
height: init.height ?? defaultSize.height
|
|
323
|
+
};
|
|
324
|
+
const size = aspectRatio === null ? clampSize(requested, minSize, maxSize) : applyAspect(requested, aspectRatio, minSize, maxSize, "width");
|
|
277
325
|
const position = positionForOpen(init);
|
|
278
326
|
let bounds = { ...position, ...size };
|
|
279
327
|
if (keepInViewport) bounds = clampToViewport(bounds, viewport, minVisible);
|
|
@@ -288,8 +336,10 @@ function createWindowManager(options = {}) {
|
|
|
288
336
|
stage: "normal",
|
|
289
337
|
snapZone: null,
|
|
290
338
|
layer: init.layer ?? "normal",
|
|
339
|
+
workspace: normalizeWorkspace(init.workspace, workspace),
|
|
291
340
|
minSize,
|
|
292
341
|
maxSize,
|
|
342
|
+
aspectRatio,
|
|
293
343
|
openedSeq: ++seq,
|
|
294
344
|
draggable: init.draggable ?? true,
|
|
295
345
|
resizable: init.resizable ?? true,
|
|
@@ -307,7 +357,7 @@ function createWindowManager(options = {}) {
|
|
|
307
357
|
setWindow(win);
|
|
308
358
|
raise(id);
|
|
309
359
|
let focusPayload = null;
|
|
310
|
-
if (stage !== "minimized") {
|
|
360
|
+
if (stage !== "minimized" && onActiveWorkspace(win)) {
|
|
311
361
|
const previous = focusedId;
|
|
312
362
|
const modal = topModalId();
|
|
313
363
|
if (!modal || win.layer === "modal") {
|
|
@@ -345,11 +395,12 @@ function createWindowManager(options = {}) {
|
|
|
345
395
|
function focus(id) {
|
|
346
396
|
const win = windows[id];
|
|
347
397
|
if (!win) return false;
|
|
398
|
+
if (!onActiveWorkspace(win)) setWorkspace(win.workspace);
|
|
348
399
|
const modal = topModalId();
|
|
349
400
|
if (modal && modal !== id && win.layer !== "modal") {
|
|
350
401
|
const modalWin = windows[modal];
|
|
351
402
|
if (modalWin) queueEvent(() => emitter.emit("modalblocked", { window: modalWin }));
|
|
352
|
-
|
|
403
|
+
commitQuiet();
|
|
353
404
|
return false;
|
|
354
405
|
}
|
|
355
406
|
if (win.stage === "minimized") {
|
|
@@ -384,6 +435,14 @@ function createWindowManager(options = {}) {
|
|
|
384
435
|
function fullBounds() {
|
|
385
436
|
return { x: 0, y: 0, width: viewport.width, height: viewport.height };
|
|
386
437
|
}
|
|
438
|
+
function snapBounds(win, zone) {
|
|
439
|
+
const rect = zoneBounds(zone, viewport);
|
|
440
|
+
return { x: rect.x, y: rect.y, ...normalizeSize(rect, win) };
|
|
441
|
+
}
|
|
442
|
+
function focusUnlessBlocked(id) {
|
|
443
|
+
const modal = topModalId();
|
|
444
|
+
if (modal === null || modal === id) focus(id);
|
|
445
|
+
}
|
|
387
446
|
function applyStage(id, build) {
|
|
388
447
|
const win = windows[id];
|
|
389
448
|
if (!win) return false;
|
|
@@ -410,7 +469,7 @@ function createWindowManager(options = {}) {
|
|
|
410
469
|
function maximize(id) {
|
|
411
470
|
const result = applyStage(id, (win) => {
|
|
412
471
|
if (win.stage === "maximized") return null;
|
|
413
|
-
const restoreBounds = win.stage === "normal" ? win.bounds : win.restoreBounds;
|
|
472
|
+
const restoreBounds = win.stage === "normal" ? win.bounds : win.restoreBounds ?? win.bounds;
|
|
414
473
|
return {
|
|
415
474
|
...win,
|
|
416
475
|
stage: "maximized",
|
|
@@ -431,22 +490,23 @@ function createWindowManager(options = {}) {
|
|
|
431
490
|
function restore(id) {
|
|
432
491
|
const result = applyStage(id, (win) => {
|
|
433
492
|
if (win.stage === "minimized") {
|
|
434
|
-
const
|
|
435
|
-
if (
|
|
493
|
+
const target2 = win.restoreStage ?? "normal";
|
|
494
|
+
if (target2 === "maximized") {
|
|
436
495
|
return { ...win, stage: "maximized", restoreStage: null, bounds: fullBounds() };
|
|
437
496
|
}
|
|
438
|
-
if (
|
|
497
|
+
if (target2 === "snapped" && win.snapZone) {
|
|
439
498
|
return {
|
|
440
499
|
...win,
|
|
441
500
|
stage: "snapped",
|
|
442
501
|
restoreStage: null,
|
|
443
|
-
bounds:
|
|
502
|
+
bounds: snapBounds(win, win.snapZone)
|
|
444
503
|
};
|
|
445
504
|
}
|
|
446
505
|
return { ...win, stage: "normal", restoreStage: null };
|
|
447
506
|
}
|
|
448
507
|
if (win.stage === "normal") return null;
|
|
449
|
-
const
|
|
508
|
+
const target = win.restoreBounds ?? win.bounds;
|
|
509
|
+
const bounds = { x: target.x, y: target.y, ...normalizeSize(target, win) };
|
|
450
510
|
return {
|
|
451
511
|
...win,
|
|
452
512
|
stage: "normal",
|
|
@@ -456,7 +516,7 @@ function createWindowManager(options = {}) {
|
|
|
456
516
|
bounds: keepInViewport ? clampToViewport(bounds, viewport, minVisible) : bounds
|
|
457
517
|
};
|
|
458
518
|
});
|
|
459
|
-
if (result)
|
|
519
|
+
if (result) focusUnlessBlocked(id);
|
|
460
520
|
return result;
|
|
461
521
|
}
|
|
462
522
|
function restoreTo(id, bounds) {
|
|
@@ -472,19 +532,19 @@ function createWindowManager(options = {}) {
|
|
|
472
532
|
bounds: keepInViewport ? clampToViewport(next, viewport, minVisible) : next
|
|
473
533
|
};
|
|
474
534
|
});
|
|
475
|
-
if (result)
|
|
535
|
+
if (result) focusUnlessBlocked(id);
|
|
476
536
|
return result;
|
|
477
537
|
}
|
|
478
538
|
function snap(id, zone) {
|
|
479
539
|
const result = applyStage(id, (win) => {
|
|
480
|
-
const restoreBounds = win.stage === "normal" ? win.bounds : win.restoreBounds;
|
|
540
|
+
const restoreBounds = win.stage === "normal" ? win.bounds : win.restoreBounds ?? win.bounds;
|
|
481
541
|
return {
|
|
482
542
|
...win,
|
|
483
543
|
stage: "snapped",
|
|
484
544
|
snapZone: zone,
|
|
485
545
|
restoreBounds,
|
|
486
546
|
restoreStage: null,
|
|
487
|
-
bounds:
|
|
547
|
+
bounds: snapBounds(win, zone)
|
|
488
548
|
};
|
|
489
549
|
});
|
|
490
550
|
if (result) focus(id);
|
|
@@ -511,7 +571,8 @@ function createWindowManager(options = {}) {
|
|
|
511
571
|
const win = windows[id];
|
|
512
572
|
if (!win || win.stage === "maximized" || win.stage === "minimized") return false;
|
|
513
573
|
const merged = { ...win.bounds, ...patch };
|
|
514
|
-
const
|
|
574
|
+
const drive = Math.abs(merged.height - win.bounds.height) > Math.abs(merged.width - win.bounds.width) ? "height" : "width";
|
|
575
|
+
const size = normalizeSize(merged, win, drive);
|
|
515
576
|
let bounds = { x: merged.x, y: merged.y, ...size };
|
|
516
577
|
if (keepInViewport) bounds = clampToViewport(bounds, viewport, minVisible);
|
|
517
578
|
const becameNormal = win.stage === "snapped";
|
|
@@ -532,6 +593,7 @@ function createWindowManager(options = {}) {
|
|
|
532
593
|
layer: patch.layer ?? win.layer,
|
|
533
594
|
minSize: patch.minSize ?? win.minSize,
|
|
534
595
|
maxSize: patch.maxSize === void 0 ? win.maxSize : patch.maxSize,
|
|
596
|
+
aspectRatio: patch.aspectRatio === void 0 ? win.aspectRatio : patch.aspectRatio !== null && Number.isFinite(patch.aspectRatio) && patch.aspectRatio > 0 ? patch.aspectRatio : null,
|
|
535
597
|
draggable: patch.draggable ?? win.draggable,
|
|
536
598
|
resizable: patch.resizable ?? win.resizable,
|
|
537
599
|
closable: patch.closable ?? win.closable,
|
|
@@ -562,16 +624,75 @@ function createWindowManager(options = {}) {
|
|
|
562
624
|
if (next.width === viewport.width && next.height === viewport.height) return;
|
|
563
625
|
viewport = { ...next };
|
|
564
626
|
reflowViewport();
|
|
565
|
-
|
|
566
|
-
|
|
627
|
+
commitQuiet();
|
|
628
|
+
}
|
|
629
|
+
function setWorkspace(next) {
|
|
630
|
+
const target = normalizeWorkspace(next, workspace);
|
|
631
|
+
if (target === workspace) return false;
|
|
632
|
+
const previous = workspace;
|
|
633
|
+
workspace = target;
|
|
634
|
+
const previousFocus = focusedId;
|
|
635
|
+
focusTop();
|
|
636
|
+
queueEvent(() => emitter.emit("workspace", { workspace: target, previous }));
|
|
637
|
+
if (focusedId && focusedId !== previousFocus) {
|
|
638
|
+
emitFocus(windows[focusedId], previousFocus);
|
|
639
|
+
}
|
|
640
|
+
commit();
|
|
641
|
+
return true;
|
|
642
|
+
}
|
|
643
|
+
function moveToWorkspace(id, next) {
|
|
644
|
+
const win = windows[id];
|
|
645
|
+
if (!win) return false;
|
|
646
|
+
const target = normalizeWorkspace(next, win.workspace);
|
|
647
|
+
if (target === win.workspace) return false;
|
|
648
|
+
const updated = { ...win, workspace: target };
|
|
649
|
+
setWindow(updated);
|
|
650
|
+
if (!onActiveWorkspace(updated) && focusedId === id) {
|
|
651
|
+
focusTop();
|
|
652
|
+
if (focusedId) emitFocus(windows[focusedId], id);
|
|
653
|
+
}
|
|
654
|
+
queueEvent(() => emitter.emit("update", { window: updated }));
|
|
655
|
+
commit();
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
function sendToBack(id) {
|
|
659
|
+
const win = windows[id];
|
|
660
|
+
if (!win) return false;
|
|
661
|
+
const without = order.filter((entry) => entry !== id);
|
|
662
|
+
const rank = LAYER_RANK[win.layer];
|
|
663
|
+
let insertAt = 0;
|
|
664
|
+
for (const entry of without) {
|
|
665
|
+
if (layerRankOf(entry) < rank) insertAt += 1;
|
|
666
|
+
else break;
|
|
667
|
+
}
|
|
668
|
+
const next = [...without.slice(0, insertAt), id, ...without.slice(insertAt)];
|
|
669
|
+
if (next.every((entry, i) => entry === order[i])) return false;
|
|
670
|
+
order = next;
|
|
671
|
+
queueEvent(() => emitter.emit("order", { order }));
|
|
672
|
+
if (focusedId === id) {
|
|
673
|
+
focusTop();
|
|
674
|
+
if (focusedId) emitFocus(windows[focusedId], id);
|
|
675
|
+
}
|
|
676
|
+
commit();
|
|
677
|
+
return true;
|
|
678
|
+
}
|
|
679
|
+
function center(id) {
|
|
680
|
+
const win = windows[id];
|
|
681
|
+
if (win?.stage !== "normal") return false;
|
|
682
|
+
return move(
|
|
683
|
+
id,
|
|
684
|
+
Math.round((viewport.width - win.bounds.width) / 2),
|
|
685
|
+
Math.round((viewport.height - win.bounds.height) / 2)
|
|
686
|
+
);
|
|
567
687
|
}
|
|
568
688
|
function minimized() {
|
|
569
|
-
return Object.values(windows).filter((win) => win.stage === "minimized").sort((a, b) => a.openedSeq - b.openedSeq);
|
|
689
|
+
return Object.values(windows).filter((win) => win.stage === "minimized" && onActiveWorkspace(win)).sort((a, b) => a.openedSeq - b.openedSeq);
|
|
570
690
|
}
|
|
571
691
|
function captureEntry() {
|
|
572
|
-
return { windows, order, focusedId };
|
|
692
|
+
return { windows, order, focusedId, workspace };
|
|
573
693
|
}
|
|
574
694
|
function recordHistory() {
|
|
695
|
+
if (!historyDirty) return;
|
|
575
696
|
if (skipNextHistory) {
|
|
576
697
|
skipNextHistory = false;
|
|
577
698
|
return;
|
|
@@ -596,6 +717,7 @@ function createWindowManager(options = {}) {
|
|
|
596
717
|
if (batchDirty) {
|
|
597
718
|
batchDirty = false;
|
|
598
719
|
recordHistory();
|
|
720
|
+
historyDirty = false;
|
|
599
721
|
snapshot = null;
|
|
600
722
|
flushEvents();
|
|
601
723
|
emitter.emit("change", { state: getState() });
|
|
@@ -612,7 +734,7 @@ function createWindowManager(options = {}) {
|
|
|
612
734
|
setWindow(updated);
|
|
613
735
|
queueEvent(() => emitter.emit("resize", { window: updated }));
|
|
614
736
|
} else if (win.stage === "snapped" && win.snapZone) {
|
|
615
|
-
const updated = { ...win, bounds:
|
|
737
|
+
const updated = { ...win, bounds: snapBounds(win, win.snapZone) };
|
|
616
738
|
setWindow(updated);
|
|
617
739
|
queueEvent(() => emitter.emit("resize", { window: updated }));
|
|
618
740
|
} else if (win.stage === "normal" && keepInViewport) {
|
|
@@ -629,6 +751,11 @@ function createWindowManager(options = {}) {
|
|
|
629
751
|
windows = entry.windows;
|
|
630
752
|
order = [...entry.order];
|
|
631
753
|
focusedId = entry.focusedId;
|
|
754
|
+
const previousWorkspace = workspace;
|
|
755
|
+
workspace = entry.workspace;
|
|
756
|
+
if (workspace !== previousWorkspace) {
|
|
757
|
+
queueEvent(() => emitter.emit("workspace", { workspace, previous: previousWorkspace }));
|
|
758
|
+
}
|
|
632
759
|
reflowViewport();
|
|
633
760
|
commit();
|
|
634
761
|
}
|
|
@@ -655,6 +782,13 @@ function createWindowManager(options = {}) {
|
|
|
655
782
|
function endInteraction() {
|
|
656
783
|
if (interactionDepth > 0) interactionDepth -= 1;
|
|
657
784
|
}
|
|
785
|
+
function abortInteraction() {
|
|
786
|
+
if (interactionDepth > 0 && interactionRecorded) {
|
|
787
|
+
past.pop();
|
|
788
|
+
interactionRecorded = false;
|
|
789
|
+
}
|
|
790
|
+
endInteraction();
|
|
791
|
+
}
|
|
658
792
|
function clearHistory() {
|
|
659
793
|
past.length = 0;
|
|
660
794
|
future = [];
|
|
@@ -685,7 +819,10 @@ function createWindowManager(options = {}) {
|
|
|
685
819
|
return [...layouts.keys()];
|
|
686
820
|
}
|
|
687
821
|
function arrange(mode) {
|
|
688
|
-
const ids = order.filter((id) =>
|
|
822
|
+
const ids = order.filter((id) => {
|
|
823
|
+
const win = windows[id];
|
|
824
|
+
return win.stage !== "minimized" && onActiveWorkspace(win);
|
|
825
|
+
});
|
|
689
826
|
if (ids.length === 0) return;
|
|
690
827
|
if (mode === "cascade") {
|
|
691
828
|
ids.forEach((id, index) => {
|
|
@@ -700,6 +837,7 @@ function createWindowManager(options = {}) {
|
|
|
700
837
|
});
|
|
701
838
|
return;
|
|
702
839
|
}
|
|
840
|
+
if (viewport.width <= 0 || viewport.height <= 0) return;
|
|
703
841
|
const cols = Math.ceil(Math.sqrt(ids.length));
|
|
704
842
|
const rows = Math.ceil(ids.length / cols);
|
|
705
843
|
const cellWidth = Math.floor(viewport.width / cols);
|
|
@@ -714,7 +852,9 @@ function createWindowManager(options = {}) {
|
|
|
714
852
|
});
|
|
715
853
|
}
|
|
716
854
|
function minimizeAll() {
|
|
717
|
-
for (const id of [...order])
|
|
855
|
+
for (const id of [...order]) {
|
|
856
|
+
if (onActiveWorkspace(windows[id])) minimize(id);
|
|
857
|
+
}
|
|
718
858
|
}
|
|
719
859
|
function restoreAll() {
|
|
720
860
|
for (const win of minimized()) restore(win.id);
|
|
@@ -734,7 +874,8 @@ function createWindowManager(options = {}) {
|
|
|
734
874
|
meta: { ...win.meta }
|
|
735
875
|
})),
|
|
736
876
|
order: [...order],
|
|
737
|
-
focusedId
|
|
877
|
+
focusedId,
|
|
878
|
+
workspace
|
|
738
879
|
};
|
|
739
880
|
}
|
|
740
881
|
function isBounds(value) {
|
|
@@ -788,8 +929,10 @@ function createWindowManager(options = {}) {
|
|
|
788
929
|
stage: raw.stage,
|
|
789
930
|
snapZone: ZONES.includes(raw.snapZone) ? raw.snapZone : null,
|
|
790
931
|
layer: raw.layer,
|
|
932
|
+
workspace: normalizeWorkspace(raw.workspace, 0),
|
|
791
933
|
minSize: isSize(raw.minSize) ? { ...raw.minSize } : { width: 160, height: 100 },
|
|
792
934
|
maxSize: readMaxSize(raw.maxSize),
|
|
935
|
+
aspectRatio: typeof raw.aspectRatio === "number" && Number.isFinite(raw.aspectRatio) && raw.aspectRatio > 0 ? raw.aspectRatio : null,
|
|
793
936
|
openedSeq: typeof raw.openedSeq === "number" ? raw.openedSeq : ++maxSeq,
|
|
794
937
|
draggable: raw.draggable !== false,
|
|
795
938
|
resizable: raw.resizable !== false,
|
|
@@ -818,8 +961,16 @@ function createWindowManager(options = {}) {
|
|
|
818
961
|
order = nextOrder;
|
|
819
962
|
order = sortByLayer(order);
|
|
820
963
|
seq = maxSeq;
|
|
964
|
+
const previousWorkspace = workspace;
|
|
965
|
+
workspace = normalizeWorkspace(data.workspace, 0);
|
|
966
|
+
if (workspace !== previousWorkspace) {
|
|
967
|
+
queueEvent(() => emitter.emit("workspace", { workspace, previous: previousWorkspace }));
|
|
968
|
+
}
|
|
821
969
|
focusedId = data.focusedId && windows[data.focusedId] ? data.focusedId : null;
|
|
822
|
-
|
|
970
|
+
const focusedCandidate = focusedId ? windows[focusedId] : void 0;
|
|
971
|
+
if (focusedCandidate && (focusedCandidate.stage === "minimized" || !onActiveWorkspace(focusedCandidate))) {
|
|
972
|
+
focusedId = null;
|
|
973
|
+
}
|
|
823
974
|
if (!focusedId) focusTop();
|
|
824
975
|
const modal = topModalId();
|
|
825
976
|
if (modal) {
|
|
@@ -838,6 +989,7 @@ function createWindowManager(options = {}) {
|
|
|
838
989
|
queueEvent(() => emitter.emit("open", { window: opened }));
|
|
839
990
|
}
|
|
840
991
|
}
|
|
992
|
+
if (viewport.width > 0 && viewport.height > 0) reflowViewport();
|
|
841
993
|
queueEvent(() => emitter.emit("order", { order }));
|
|
842
994
|
clearHistory();
|
|
843
995
|
skipNextHistory = true;
|
|
@@ -862,12 +1014,17 @@ function createWindowManager(options = {}) {
|
|
|
862
1014
|
snap: (id, zone) => transact(() => snap(id, zone)),
|
|
863
1015
|
move: (id, x, y) => transact(() => move(id, x, y)),
|
|
864
1016
|
moveBy: (id, dx, dy) => transact(() => moveBy(id, dx, dy)),
|
|
1017
|
+
center: (id) => transact(() => center(id)),
|
|
1018
|
+
sendToBack: (id) => transact(() => sendToBack(id)),
|
|
865
1019
|
resize: (id, patch) => transact(() => resize(id, patch)),
|
|
866
1020
|
update: (id, patch) => transact(() => update(id, patch)),
|
|
867
1021
|
get: (id) => windows[id],
|
|
868
1022
|
getState,
|
|
869
1023
|
minimized,
|
|
870
1024
|
setViewport: (next) => transact(() => setViewport(next)),
|
|
1025
|
+
workspace: () => workspace,
|
|
1026
|
+
setWorkspace: (next) => transact(() => setWorkspace(next)),
|
|
1027
|
+
moveToWorkspace: (id, next) => transact(() => moveToWorkspace(id, next)),
|
|
871
1028
|
batch: (run) => transact(run),
|
|
872
1029
|
serialize,
|
|
873
1030
|
hydrate: (data) => transact(() => hydrate(data)),
|
|
@@ -877,6 +1034,8 @@ function createWindowManager(options = {}) {
|
|
|
877
1034
|
canRedo: () => future.length > 0,
|
|
878
1035
|
beginInteraction,
|
|
879
1036
|
endInteraction,
|
|
1037
|
+
abortInteraction,
|
|
1038
|
+
clearHistory,
|
|
880
1039
|
saveLayout,
|
|
881
1040
|
loadLayout: (name) => transact(() => loadLayout(name)),
|
|
882
1041
|
getLayout,
|
|
@@ -961,7 +1120,8 @@ var defaultMessages = {
|
|
|
961
1120
|
restored: (title) => `${title} restored`,
|
|
962
1121
|
maximized: (title) => `${title} maximized`,
|
|
963
1122
|
snapped: (title, zone) => `${title} snapped to ${zone.replace("-", " ")}`,
|
|
964
|
-
focused: (title) => `${title} focused
|
|
1123
|
+
focused: (title) => `${title} focused`,
|
|
1124
|
+
workspace: (index) => `workspace ${index + 1}`
|
|
965
1125
|
};
|
|
966
1126
|
function createAnnouncer(wm, container, messages = {}) {
|
|
967
1127
|
const dict = { ...defaultMessages, ...messages };
|
|
@@ -972,7 +1132,10 @@ function createAnnouncer(wm, container, messages = {}) {
|
|
|
972
1132
|
element.style.cssText = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0";
|
|
973
1133
|
container.append(element);
|
|
974
1134
|
let clearTimer;
|
|
1135
|
+
let lastMessage = "";
|
|
1136
|
+
let detailed = false;
|
|
975
1137
|
function announce(message) {
|
|
1138
|
+
lastMessage = message;
|
|
976
1139
|
element.textContent = message;
|
|
977
1140
|
if (clearTimer !== void 0) clearTimeout(clearTimer);
|
|
978
1141
|
clearTimer = setTimeout(() => {
|
|
@@ -988,6 +1151,19 @@ function createAnnouncer(wm, container, messages = {}) {
|
|
|
988
1151
|
else if (win.stage === "snapped" && win.snapZone)
|
|
989
1152
|
announce(dict.snapped(win.title, win.snapZone));
|
|
990
1153
|
else if (win.stage === "normal" && previous !== "normal") announce(dict.restored(win.title));
|
|
1154
|
+
else return;
|
|
1155
|
+
detailed = true;
|
|
1156
|
+
}),
|
|
1157
|
+
wm.on("focus", ({ window: win, previous }) => {
|
|
1158
|
+
if (detailed || previous === null || lastMessage === dict.opened(win.title)) return;
|
|
1159
|
+
announce(dict.focused(win.title));
|
|
1160
|
+
}),
|
|
1161
|
+
wm.on("workspace", ({ workspace }) => {
|
|
1162
|
+
announce(dict.workspace(workspace));
|
|
1163
|
+
detailed = true;
|
|
1164
|
+
}),
|
|
1165
|
+
wm.subscribe(() => {
|
|
1166
|
+
detailed = false;
|
|
991
1167
|
})
|
|
992
1168
|
];
|
|
993
1169
|
return {
|
|
@@ -1076,7 +1252,9 @@ function createDragStarter(ctx) {
|
|
|
1076
1252
|
for (const otherId of state.order) {
|
|
1077
1253
|
if (otherId === id) continue;
|
|
1078
1254
|
const other = state.windows[otherId];
|
|
1079
|
-
if (other && other.stage !== "minimized"
|
|
1255
|
+
if (other && other.stage !== "minimized" && other.workspace === state.workspace) {
|
|
1256
|
+
targets.push(other.bounds);
|
|
1257
|
+
}
|
|
1080
1258
|
}
|
|
1081
1259
|
const magnet = magnetize(
|
|
1082
1260
|
{ x: nextX, y: nextY, width: current.bounds.width, height: current.bounds.height },
|
|
@@ -1110,7 +1288,7 @@ function createDragStarter(ctx) {
|
|
|
1110
1288
|
const movePoint = ctx.toLocal(moveEvent);
|
|
1111
1289
|
if (!session.moved) {
|
|
1112
1290
|
const travelled = Math.abs(movePoint.x - (session.startBounds.x + session.grabDX)) + Math.abs(movePoint.y - (session.startBounds.y + session.grabDY));
|
|
1113
|
-
if (travelled < 3
|
|
1291
|
+
if (travelled < 3) return;
|
|
1114
1292
|
session.moved = true;
|
|
1115
1293
|
if (el) {
|
|
1116
1294
|
el.dataset.wmDragging = "";
|
|
@@ -1138,42 +1316,43 @@ function createDragStarter(ctx) {
|
|
|
1138
1316
|
}
|
|
1139
1317
|
function finish(cancelled) {
|
|
1140
1318
|
if (ctx.currentDrag() !== session) return;
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
}
|
|
1156
|
-
ctx.hidePreview();
|
|
1157
|
-
if (cancelled) {
|
|
1158
|
-
if (session.moved && session.restored) {
|
|
1159
|
-
if (session.startStage === "maximized") {
|
|
1160
|
-
wm.restoreTo(id, session.startRestoreBounds ?? session.startBounds);
|
|
1161
|
-
wm.maximize(id);
|
|
1162
|
-
} else if (session.startStage === "snapped" && session.startZone) {
|
|
1163
|
-
wm.restoreTo(id, session.startRestoreBounds ?? session.startBounds);
|
|
1164
|
-
wm.snap(id, session.startZone);
|
|
1165
|
-
} else {
|
|
1166
|
-
wm.move(id, session.startBounds.x, session.startBounds.y);
|
|
1319
|
+
try {
|
|
1320
|
+
if (session.raf !== 0) view.cancelAnimationFrame(session.raf);
|
|
1321
|
+
if (session.hasPending && !cancelled) flush();
|
|
1322
|
+
if (cancelled) {
|
|
1323
|
+
if (session.moved && session.restored) {
|
|
1324
|
+
if (session.startStage === "maximized") {
|
|
1325
|
+
wm.restoreTo(id, session.startRestoreBounds ?? session.startBounds);
|
|
1326
|
+
wm.maximize(id);
|
|
1327
|
+
} else if (session.startStage === "snapped" && session.startZone) {
|
|
1328
|
+
wm.restoreTo(id, session.startRestoreBounds ?? session.startBounds);
|
|
1329
|
+
wm.snap(id, session.startZone);
|
|
1330
|
+
} else {
|
|
1331
|
+
wm.move(id, session.startBounds.x, session.startBounds.y);
|
|
1332
|
+
}
|
|
1167
1333
|
}
|
|
1334
|
+
} else if (session.moved && session.zone) {
|
|
1335
|
+
if (session.zone === "maximize") wm.maximize(id);
|
|
1336
|
+
else wm.snap(id, session.zone);
|
|
1168
1337
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1338
|
+
} finally {
|
|
1339
|
+
ctx.releaseDrag(session);
|
|
1340
|
+
releaseRect();
|
|
1341
|
+
handle.removeEventListener("pointermove", onMove);
|
|
1342
|
+
handle.removeEventListener("pointerup", onUp);
|
|
1343
|
+
handle.removeEventListener("pointercancel", onCancel);
|
|
1344
|
+
doc.removeEventListener("keydown", onKeydown, true);
|
|
1345
|
+
if (handle.hasPointerCapture(session.pointerId)) {
|
|
1346
|
+
handle.releasePointerCapture(session.pointerId);
|
|
1347
|
+
}
|
|
1348
|
+
if (el) {
|
|
1349
|
+
delete el.dataset.wmDragging;
|
|
1350
|
+
el.style.willChange = "";
|
|
1351
|
+
}
|
|
1352
|
+
ctx.hidePreview();
|
|
1353
|
+
if (cancelled) wm.abortInteraction();
|
|
1354
|
+
else wm.endInteraction();
|
|
1175
1355
|
}
|
|
1176
|
-
wm.endInteraction();
|
|
1177
1356
|
}
|
|
1178
1357
|
session.finish = finish;
|
|
1179
1358
|
wm.beginInteraction();
|
|
@@ -1206,41 +1385,42 @@ function createResizeStarter(ctx) {
|
|
|
1206
1385
|
event.preventDefault();
|
|
1207
1386
|
event.stopPropagation();
|
|
1208
1387
|
const handleEl = event.currentTarget;
|
|
1388
|
+
const session = { id, finish: (cancelled) => finish(cancelled) };
|
|
1389
|
+
ctx.claimDrag(session);
|
|
1209
1390
|
wm.beginInteraction();
|
|
1210
1391
|
const releaseRect = ctx.trackRect();
|
|
1211
1392
|
const startPoint = ctx.toLocal(event);
|
|
1212
1393
|
const start = win.bounds;
|
|
1394
|
+
const startStage = win.stage;
|
|
1395
|
+
const startZone = win.snapZone;
|
|
1396
|
+
const startRestore = win.restoreBounds;
|
|
1213
1397
|
const minSize = win.minSize;
|
|
1214
1398
|
const maxSize = win.maxSize;
|
|
1399
|
+
const aspect = win.aspectRatio;
|
|
1400
|
+
const drive = direction === "n" || direction === "s" ? "height" : "width";
|
|
1215
1401
|
let raf = 0;
|
|
1216
1402
|
let pendingX = 0;
|
|
1217
1403
|
let pendingY = 0;
|
|
1218
1404
|
let hasPending = false;
|
|
1219
1405
|
const el = ctx.windowElement(id);
|
|
1220
1406
|
if (el) el.dataset.wmResizing = direction;
|
|
1221
|
-
function clampWidth(width) {
|
|
1222
|
-
return clamp(width, minSize.width, maxSize ? maxSize.width : Number.POSITIVE_INFINITY);
|
|
1223
|
-
}
|
|
1224
|
-
function clampHeight(height) {
|
|
1225
|
-
return clamp(height, minSize.height, maxSize ? maxSize.height : Number.POSITIVE_INFINITY);
|
|
1226
|
-
}
|
|
1227
1407
|
function flush() {
|
|
1228
1408
|
if (!hasPending) return;
|
|
1229
1409
|
hasPending = false;
|
|
1230
1410
|
raf = 0;
|
|
1231
1411
|
const dx = pendingX - startPoint.x;
|
|
1232
1412
|
const dy = pendingY - startPoint.y;
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
}
|
|
1413
|
+
const top = direction.includes("n") ? Math.max(0, start.y + dy) : start.y;
|
|
1414
|
+
const raw = {
|
|
1415
|
+
width: direction.includes("e") ? start.width + dx : direction.includes("w") ? start.width - dx : start.width,
|
|
1416
|
+
height: direction.includes("s") ? start.height + dy : direction.includes("n") ? start.y + start.height - top : start.height
|
|
1417
|
+
};
|
|
1418
|
+
const size = aspect === null ? clampSize(raw, minSize, maxSize) : applyAspect(raw, aspect, minSize, maxSize, drive);
|
|
1419
|
+
const next = {
|
|
1420
|
+
x: direction.includes("w") ? start.x + start.width - size.width : start.x,
|
|
1421
|
+
y: direction.includes("n") ? start.y + start.height - size.height : start.y,
|
|
1422
|
+
...size
|
|
1423
|
+
};
|
|
1244
1424
|
wm.resize(id, next);
|
|
1245
1425
|
}
|
|
1246
1426
|
function onMove(moveEvent) {
|
|
@@ -1252,19 +1432,32 @@ function createResizeStarter(ctx) {
|
|
|
1252
1432
|
if (raf === 0) raf = view.requestAnimationFrame(flush);
|
|
1253
1433
|
}
|
|
1254
1434
|
function finish(cancelled) {
|
|
1255
|
-
if (
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1435
|
+
if (ctx.currentDrag() !== session) return;
|
|
1436
|
+
try {
|
|
1437
|
+
if (raf !== 0) view.cancelAnimationFrame(raf);
|
|
1438
|
+
if (hasPending && !cancelled) flush();
|
|
1439
|
+
if (cancelled) {
|
|
1440
|
+
if (startStage === "snapped" && startZone) {
|
|
1441
|
+
wm.restoreTo(id, startRestore ?? start);
|
|
1442
|
+
wm.snap(id, startZone);
|
|
1443
|
+
} else {
|
|
1444
|
+
wm.resize(id, start);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
} finally {
|
|
1448
|
+
ctx.releaseDrag(session);
|
|
1449
|
+
releaseRect();
|
|
1450
|
+
handleEl.removeEventListener("pointermove", onMove);
|
|
1451
|
+
handleEl.removeEventListener("pointerup", onUp);
|
|
1452
|
+
handleEl.removeEventListener("pointercancel", onCancelPointer);
|
|
1453
|
+
doc.removeEventListener("keydown", onKeydown, true);
|
|
1454
|
+
if (handleEl.hasPointerCapture(event.pointerId)) {
|
|
1455
|
+
handleEl.releasePointerCapture(event.pointerId);
|
|
1456
|
+
}
|
|
1457
|
+
if (el) delete el.dataset.wmResizing;
|
|
1458
|
+
if (cancelled) wm.abortInteraction();
|
|
1459
|
+
else wm.endInteraction();
|
|
1264
1460
|
}
|
|
1265
|
-
if (el) delete el.dataset.wmResizing;
|
|
1266
|
-
if (cancelled) wm.resize(id, start);
|
|
1267
|
-
wm.endInteraction();
|
|
1268
1461
|
}
|
|
1269
1462
|
function onUp(upEvent) {
|
|
1270
1463
|
if (upEvent.pointerId !== event.pointerId) return;
|
|
@@ -1309,6 +1502,12 @@ function createResizeHandles(doc, edge, corner) {
|
|
|
1309
1502
|
}
|
|
1310
1503
|
|
|
1311
1504
|
// src/dom/controller.ts
|
|
1505
|
+
var SNAP_SHORTCUTS = {
|
|
1506
|
+
ArrowLeft: "left",
|
|
1507
|
+
ArrowRight: "right"
|
|
1508
|
+
};
|
|
1509
|
+
var LANDMARK_TAGS = /* @__PURE__ */ new Set(["header", "footer", "aside", "nav"]);
|
|
1510
|
+
var FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1312
1511
|
function attachDesktop(wm, element, options = {}) {
|
|
1313
1512
|
const doc = element.ownerDocument;
|
|
1314
1513
|
const view = windowOf(element);
|
|
@@ -1321,10 +1520,13 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1321
1520
|
const keyboardOptions = typeof options.keyboard === "object" ? options.keyboard : {};
|
|
1322
1521
|
const moveStep = keyboardOptions.moveStep ?? 16;
|
|
1323
1522
|
const cycleEnabled = keyboardOptions.cycle !== false;
|
|
1523
|
+
const snapShortcuts = keyboardOptions.snapShortcuts !== false;
|
|
1524
|
+
const historyShortcuts = keyboardOptions.historyShortcuts !== false;
|
|
1324
1525
|
const hitEdge = options.hitAreas?.edge ?? (coarsePointer ? 16 : 8);
|
|
1325
1526
|
const hitCorner = options.hitAreas?.corner ?? (coarsePointer ? 24 : 12);
|
|
1326
1527
|
const magnetThreshold = options.magnetism === false ? 0 : (typeof options.magnetism === "object" ? options.magnetism.threshold : void 0) ?? (coarsePointer ? 12 : 8);
|
|
1327
1528
|
element.dataset.wmDesktop = "";
|
|
1529
|
+
element.tabIndex = -1;
|
|
1328
1530
|
if (view.getComputedStyle(element).position === "static") {
|
|
1329
1531
|
element.style.position = "relative";
|
|
1330
1532
|
}
|
|
@@ -1361,6 +1563,10 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1361
1563
|
function hidePreview() {
|
|
1362
1564
|
if (preview) preview.style.display = "none";
|
|
1363
1565
|
}
|
|
1566
|
+
function refreshRect() {
|
|
1567
|
+
const rect = element.getBoundingClientRect();
|
|
1568
|
+
cachedRect = { left: rect.left, top: rect.top };
|
|
1569
|
+
}
|
|
1364
1570
|
const ctx = {
|
|
1365
1571
|
wm,
|
|
1366
1572
|
doc,
|
|
@@ -1371,8 +1577,9 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1371
1577
|
},
|
|
1372
1578
|
trackRect() {
|
|
1373
1579
|
if (rectUsers === 0) {
|
|
1374
|
-
|
|
1375
|
-
|
|
1580
|
+
refreshRect();
|
|
1581
|
+
view.addEventListener("scroll", refreshRect, true);
|
|
1582
|
+
view.addEventListener("resize", refreshRect);
|
|
1376
1583
|
}
|
|
1377
1584
|
rectUsers += 1;
|
|
1378
1585
|
let released = false;
|
|
@@ -1380,7 +1587,11 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1380
1587
|
if (released) return;
|
|
1381
1588
|
released = true;
|
|
1382
1589
|
rectUsers -= 1;
|
|
1383
|
-
if (rectUsers === 0)
|
|
1590
|
+
if (rectUsers === 0) {
|
|
1591
|
+
view.removeEventListener("scroll", refreshRect, true);
|
|
1592
|
+
view.removeEventListener("resize", refreshRect);
|
|
1593
|
+
cachedRect = null;
|
|
1594
|
+
}
|
|
1384
1595
|
};
|
|
1385
1596
|
},
|
|
1386
1597
|
windowElement: (id) => registry.get(id)?.element,
|
|
@@ -1408,28 +1619,34 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1408
1619
|
if (options.autoViewport !== false) {
|
|
1409
1620
|
const applyViewport = () => wm.setViewport({ width: element.clientWidth, height: element.clientHeight });
|
|
1410
1621
|
applyViewport();
|
|
1411
|
-
const observer = new ResizeObserver(applyViewport);
|
|
1622
|
+
const observer = new view.ResizeObserver(applyViewport);
|
|
1412
1623
|
observer.observe(element);
|
|
1413
1624
|
cleanup.push(() => observer.disconnect());
|
|
1414
1625
|
}
|
|
1415
|
-
function syncWindow(attached, win, zIndex) {
|
|
1626
|
+
function syncWindow(attached, win, zIndex, activeWorkspace) {
|
|
1416
1627
|
const el = attached.element;
|
|
1417
1628
|
const firstSync = attached.lastState === null;
|
|
1418
1629
|
if (firstSync) el.style.transition = "none";
|
|
1419
|
-
if (attached.lastState !== win) {
|
|
1630
|
+
if (attached.lastState !== win || attached.lastWorkspace !== activeWorkspace) {
|
|
1420
1631
|
el.style.transform = `translate3d(${win.bounds.x}px, ${win.bounds.y}px, 0)`;
|
|
1421
1632
|
el.style.width = `${win.bounds.width}px`;
|
|
1422
1633
|
el.style.height = `${win.bounds.height}px`;
|
|
1423
1634
|
el.dataset.wmStage = win.stage;
|
|
1424
1635
|
el.dataset.wmLayer = win.layer;
|
|
1425
|
-
el.
|
|
1636
|
+
el.dataset.wmWorkspace = String(win.workspace);
|
|
1637
|
+
const hide = win.stage === "minimized" || win.workspace !== activeWorkspace;
|
|
1638
|
+
if (hide && !el.hidden && el.contains(doc.activeElement)) {
|
|
1639
|
+
element.focus({ preventScroll: true });
|
|
1640
|
+
}
|
|
1641
|
+
el.hidden = hide;
|
|
1426
1642
|
el.setAttribute("aria-label", win.title);
|
|
1427
1643
|
if (win.layer === "modal") el.setAttribute("aria-modal", "true");
|
|
1428
1644
|
else el.removeAttribute("aria-modal");
|
|
1429
1645
|
for (const handle of attached.handles) {
|
|
1430
|
-
handle.style.display = win.resizable && win.stage === "normal" ? "" : "none";
|
|
1646
|
+
handle.style.display = win.resizable && (win.stage === "normal" || win.stage === "snapped") ? "" : "none";
|
|
1431
1647
|
}
|
|
1432
1648
|
attached.lastState = win;
|
|
1649
|
+
attached.lastWorkspace = activeWorkspace;
|
|
1433
1650
|
}
|
|
1434
1651
|
if (attached.lastZ !== zIndex) {
|
|
1435
1652
|
el.style.zIndex = String(zIndex + 1);
|
|
@@ -1447,7 +1664,9 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1447
1664
|
const attached = registry.get(id);
|
|
1448
1665
|
const win = state.windows[id];
|
|
1449
1666
|
if (!attached || !win) return;
|
|
1450
|
-
if (orderChanged || attached.lastState !== win
|
|
1667
|
+
if (orderChanged || attached.lastState !== win || attached.lastWorkspace !== state.workspace) {
|
|
1668
|
+
syncWindow(attached, win, index, state.workspace);
|
|
1669
|
+
}
|
|
1451
1670
|
});
|
|
1452
1671
|
if (state.focusedId !== lastFocused) {
|
|
1453
1672
|
if (lastFocused) {
|
|
@@ -1490,15 +1709,44 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1490
1709
|
if (target) flipToTarget(attached.element, target);
|
|
1491
1710
|
} else if (previous === "minimized" && win.stage !== "minimized") {
|
|
1492
1711
|
const target = options.minimizeTarget?.(win);
|
|
1493
|
-
if (target) flipFromTarget(attached.element, target);
|
|
1712
|
+
if (target) view.requestAnimationFrame(() => flipFromTarget(attached.element, target));
|
|
1494
1713
|
}
|
|
1495
1714
|
})
|
|
1496
1715
|
);
|
|
1497
|
-
if (keyboardEnabled
|
|
1716
|
+
if (keyboardEnabled) {
|
|
1498
1717
|
const onDesktopKeydown = (event) => {
|
|
1499
|
-
if (event.key === "F6") {
|
|
1718
|
+
if (cycleEnabled && event.key === "F6") {
|
|
1500
1719
|
event.preventDefault();
|
|
1501
1720
|
wm.cycleFocus(event.shiftKey ? -1 : 1);
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
if (!event.ctrlKey && !event.metaKey) return;
|
|
1724
|
+
const target = event.target;
|
|
1725
|
+
if (target?.closest(INTERACTIVE_SELECTOR)) return;
|
|
1726
|
+
if (historyShortcuts && (event.key === "z" || event.key === "Z")) {
|
|
1727
|
+
event.preventDefault();
|
|
1728
|
+
if (event.shiftKey) wm.redo();
|
|
1729
|
+
else wm.undo();
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (!snapShortcuts || !event.altKey) return;
|
|
1733
|
+
const focused = wm.getState().focusedId;
|
|
1734
|
+
const win = focused ? wm.get(focused) : void 0;
|
|
1735
|
+
if (!win) return;
|
|
1736
|
+
const zone = SNAP_SHORTCUTS[event.key];
|
|
1737
|
+
if (zone) {
|
|
1738
|
+
event.preventDefault();
|
|
1739
|
+
if (win.snappable) wm.snap(win.id, zone);
|
|
1740
|
+
} else if (event.key === "ArrowUp") {
|
|
1741
|
+
event.preventDefault();
|
|
1742
|
+
if (win.maximizable) wm.maximize(win.id);
|
|
1743
|
+
} else if (event.key === "ArrowDown") {
|
|
1744
|
+
event.preventDefault();
|
|
1745
|
+
if (win.stage === "normal") {
|
|
1746
|
+
if (win.minimizable) wm.minimize(win.id);
|
|
1747
|
+
} else {
|
|
1748
|
+
wm.restore(win.id);
|
|
1749
|
+
}
|
|
1502
1750
|
}
|
|
1503
1751
|
};
|
|
1504
1752
|
element.addEventListener("keydown", onDesktopKeydown);
|
|
@@ -1517,6 +1765,7 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1517
1765
|
handles: [],
|
|
1518
1766
|
lastState: null,
|
|
1519
1767
|
lastZ: -1,
|
|
1768
|
+
lastWorkspace: -1,
|
|
1520
1769
|
cleanup: []
|
|
1521
1770
|
};
|
|
1522
1771
|
windowElement.dataset.wmWindow = id;
|
|
@@ -1532,6 +1781,9 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1532
1781
|
}
|
|
1533
1782
|
const handle = typeof windowOptions.handle === "string" ? windowElement.querySelector(windowOptions.handle) : windowOptions.handle ?? windowElement.querySelector("[data-wm-drag]");
|
|
1534
1783
|
attached.handle = handle;
|
|
1784
|
+
if (handle && !handle.hasAttribute("role") && LANDMARK_TAGS.has(handle.localName)) {
|
|
1785
|
+
handle.setAttribute("role", "presentation");
|
|
1786
|
+
}
|
|
1535
1787
|
const onPointerDownFocus = () => {
|
|
1536
1788
|
wm.focus(id);
|
|
1537
1789
|
};
|
|
@@ -1602,19 +1854,20 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1602
1854
|
ArrowUp: [0, -1],
|
|
1603
1855
|
ArrowDown: [0, 1]
|
|
1604
1856
|
};
|
|
1857
|
+
if (event.ctrlKey || event.metaKey) return;
|
|
1605
1858
|
const vector = arrows[event.key];
|
|
1606
1859
|
if (!vector || !keyboardEnabled) return;
|
|
1607
|
-
event.preventDefault();
|
|
1608
1860
|
const step = event.altKey ? 1 : moveStep;
|
|
1609
1861
|
const [dx, dy] = vector;
|
|
1610
1862
|
if (event.shiftKey) {
|
|
1611
|
-
if (current.resizable)
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
}
|
|
1863
|
+
if (!current.resizable) return;
|
|
1864
|
+
event.preventDefault();
|
|
1865
|
+
wm.resize(id, {
|
|
1866
|
+
width: current.bounds.width + dx * step,
|
|
1867
|
+
height: current.bounds.height + dy * step
|
|
1868
|
+
});
|
|
1617
1869
|
} else if (current.draggable && current.stage === "normal") {
|
|
1870
|
+
event.preventDefault();
|
|
1618
1871
|
wm.moveBy(id, dx * step, dy * step);
|
|
1619
1872
|
}
|
|
1620
1873
|
};
|
|
@@ -1624,13 +1877,18 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1624
1877
|
if (event.key !== "Tab") return;
|
|
1625
1878
|
const current = wm.get(id);
|
|
1626
1879
|
if (current?.layer !== "modal") return;
|
|
1627
|
-
const focusables =
|
|
1628
|
-
|
|
1629
|
-
);
|
|
1880
|
+
const focusables = [
|
|
1881
|
+
...windowElement.querySelectorAll(FOCUSABLE_SELECTOR)
|
|
1882
|
+
].filter((node) => node.offsetParent !== null || node === doc.activeElement);
|
|
1630
1883
|
if (focusables.length === 0) return;
|
|
1631
1884
|
const first = focusables[0];
|
|
1632
1885
|
const last = focusables[focusables.length - 1];
|
|
1633
1886
|
if (!first || !last) return;
|
|
1887
|
+
if (!windowElement.contains(doc.activeElement)) {
|
|
1888
|
+
event.preventDefault();
|
|
1889
|
+
(event.shiftKey ? last : first).focus();
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1634
1892
|
if (event.shiftKey && doc.activeElement === first) {
|
|
1635
1893
|
event.preventDefault();
|
|
1636
1894
|
last.focus();
|
|
@@ -1659,6 +1917,9 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1659
1917
|
lastOrder = null;
|
|
1660
1918
|
lastFocused = null;
|
|
1661
1919
|
syncAll();
|
|
1920
|
+
if (wm.getState().focusedId === id && !windowElement.contains(doc.activeElement)) {
|
|
1921
|
+
windowElement.focus({ preventScroll: true });
|
|
1922
|
+
}
|
|
1662
1923
|
return detach;
|
|
1663
1924
|
}
|
|
1664
1925
|
return {
|
|
@@ -1682,29 +1943,33 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1682
1943
|
// src/dom/binder.ts
|
|
1683
1944
|
function createDesktopBinder(wm, options = {}) {
|
|
1684
1945
|
let controller = null;
|
|
1946
|
+
let stopOpen = null;
|
|
1685
1947
|
const entries = /* @__PURE__ */ new Set();
|
|
1686
1948
|
function attachEntry(entry) {
|
|
1687
1949
|
if (controller && !entry.detach && wm.get(entry.id)) {
|
|
1688
1950
|
entry.detach = controller.attachWindow(entry.id, entry.element, entry.options);
|
|
1689
1951
|
}
|
|
1690
1952
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1953
|
+
function unbindDesktop() {
|
|
1954
|
+
stopOpen?.();
|
|
1955
|
+
stopOpen = null;
|
|
1956
|
+
for (const entry of entries) entry.detach = null;
|
|
1957
|
+
controller?.destroy();
|
|
1958
|
+
controller = null;
|
|
1959
|
+
}
|
|
1696
1960
|
return {
|
|
1697
1961
|
wm,
|
|
1698
1962
|
controller: () => controller,
|
|
1699
1963
|
bindDesktop(element) {
|
|
1700
1964
|
if (controller) throw new Error("wmkit: desktop is already bound");
|
|
1701
1965
|
controller = attachDesktop(wm, element, options);
|
|
1966
|
+
stopOpen = wm.on("open", ({ window: win }) => {
|
|
1967
|
+
for (const entry of entries) {
|
|
1968
|
+
if (entry.id === win.id) attachEntry(entry);
|
|
1969
|
+
}
|
|
1970
|
+
});
|
|
1702
1971
|
for (const entry of entries) attachEntry(entry);
|
|
1703
|
-
return
|
|
1704
|
-
for (const entry of entries) entry.detach = null;
|
|
1705
|
-
controller?.destroy();
|
|
1706
|
-
controller = null;
|
|
1707
|
-
};
|
|
1972
|
+
return unbindDesktop;
|
|
1708
1973
|
},
|
|
1709
1974
|
bindWindow(id, element, windowOptions) {
|
|
1710
1975
|
const entry = { id, element, options: windowOptions, detach: null };
|
|
@@ -1715,10 +1980,15 @@ function createDesktopBinder(wm, options = {}) {
|
|
|
1715
1980
|
entry.detach = null;
|
|
1716
1981
|
entries.delete(entry);
|
|
1717
1982
|
};
|
|
1983
|
+
},
|
|
1984
|
+
destroy() {
|
|
1985
|
+
unbindDesktop();
|
|
1986
|
+
entries.clear();
|
|
1718
1987
|
}
|
|
1719
1988
|
};
|
|
1720
1989
|
}
|
|
1721
1990
|
|
|
1991
|
+
exports.applyAspect = applyAspect;
|
|
1722
1992
|
exports.attachDesktop = attachDesktop;
|
|
1723
1993
|
exports.boundsEqual = boundsEqual;
|
|
1724
1994
|
exports.clamp = clamp;
|
|
@@ -1730,9 +2000,10 @@ exports.createEmitter = createEmitter;
|
|
|
1730
2000
|
exports.createWindowManager = createWindowManager;
|
|
1731
2001
|
exports.defaultMessages = defaultMessages;
|
|
1732
2002
|
exports.detectSnapZone = detectSnapZone;
|
|
2003
|
+
exports.flipFromTarget = flipFromTarget;
|
|
1733
2004
|
exports.flipToTarget = flipToTarget;
|
|
1734
2005
|
exports.magnetize = magnetize;
|
|
1735
2006
|
exports.prefersReducedMotion = prefersReducedMotion;
|
|
1736
2007
|
exports.zoneBounds = zoneBounds;
|
|
1737
|
-
//# sourceMappingURL=chunk-
|
|
1738
|
-
//# sourceMappingURL=chunk-
|
|
2008
|
+
//# sourceMappingURL=chunk-TJ6QOOJ2.cjs.map
|
|
2009
|
+
//# sourceMappingURL=chunk-TJ6QOOJ2.cjs.map
|