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