@surdeddd/wmkit 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/README.ru.md +1 -0
- package/dist/angular.cjs +3 -3
- package/dist/angular.d.cts +2 -2
- package/dist/angular.d.ts +2 -2
- package/dist/angular.js +1 -1
- package/dist/{binder-B2A2IZGh.d.ts → binder-Ca3GOfZX.d.ts} +5 -1
- package/dist/{binder-Dl07SXIM.d.cts → binder-EBroZTRw.d.cts} +5 -1
- package/dist/{chunk-GGDF2EWV.js → chunk-L43XBN44.js} +235 -14
- package/dist/chunk-L43XBN44.js.map +1 -0
- package/dist/{chunk-GLBVT4AQ.cjs → chunk-RXAMUBUJ.cjs} +235 -14
- package/dist/chunk-RXAMUBUJ.cjs.map +1 -0
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/persist.d.cts +1 -1
- package/dist/persist.d.ts +1 -1
- package/dist/popout.d.cts +1 -1
- package/dist/popout.d.ts +1 -1
- package/dist/react.cjs +3 -3
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/solid.cjs +3 -3
- package/dist/solid.d.cts +2 -2
- package/dist/solid.d.ts +2 -2
- package/dist/solid.js +1 -1
- package/dist/svelte.cjs +3 -3
- package/dist/svelte.d.cts +2 -2
- package/dist/svelte.d.ts +2 -2
- package/dist/svelte.js +1 -1
- package/dist/{types-t6pyVYKN.d.cts → types-LP7Inhxx.d.cts} +18 -0
- package/dist/{types-t6pyVYKN.d.ts → types-LP7Inhxx.d.ts} +18 -0
- package/dist/vue.cjs +3 -3
- package/dist/vue.d.cts +2 -2
- package/dist/vue.d.ts +2 -2
- package/dist/vue.js +1 -1
- package/package.json +7 -7
- package/dist/chunk-GGDF2EWV.js.map +0 -1
- package/dist/chunk-GLBVT4AQ.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- 🪟 **Full window lifecycle** — open, close, focus, minimize, maximize, restore, drag, 8-direction resize
|
|
16
16
|
- 🧠 **Headless core** — a serializable state machine plus a DOM controller; bring your own markup or use the glass theme
|
|
17
17
|
- ⚛️ **Official adapters** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, all thin sugar over one core
|
|
18
|
+
- 🗃️ **Tab groups** — drop one titlebar onto another and the windows share a frame, dockview style
|
|
18
19
|
- ⊞ **Snap zones** — halves, quarters, thirds and drag-to-top maximize with a live preview
|
|
19
20
|
- 🧲 **Magnetism** — window edges align to neighbours and the viewport while dragging
|
|
20
21
|
- ↩️ **Undo/redo** — every mutation is one step; a whole drag collapses into a single history entry
|
package/README.ru.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
- 🪟 **Полный жизненный цикл окна** — открытие, закрытие, фокус, сворачивание, разворачивание, восстановление, drag, ресайз в 8 направлениях
|
|
12
12
|
- 🧠 **Headless-ядро** — сериализуемая стейт-машина плюс DOM-контроллер; своя разметка или готовая стеклянная тема
|
|
13
13
|
- ⚛️ **Родные адаптеры** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, тонкий сахар над одним ядром
|
|
14
|
+
- 🗃️ **Вкладочные группы** — бросьте заголовок на заголовок, и окна начнут делить одну рамку
|
|
14
15
|
- ⊞ **Snap-зоны** — половины, четверти, трети и максимизация от верхнего края с живым превью
|
|
15
16
|
- 🧲 **Магнетизм** — края окна прилипают к соседям и вьюпорту при перетаскивании
|
|
16
17
|
- ↩️ **Undo/redo** — каждая мутация = один шаг; целый drag схлопывается в одну запись истории
|
package/dist/angular.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkRXAMUBUJ_cjs = require('./chunk-RXAMUBUJ.cjs');
|
|
4
4
|
var core = require('@angular/core');
|
|
5
5
|
|
|
6
6
|
function onDestroy(dispose) {
|
|
@@ -13,7 +13,7 @@ function onDestroy(dispose) {
|
|
|
13
13
|
destroyRef?.onDestroy(dispose);
|
|
14
14
|
}
|
|
15
15
|
function useWindowManager(options) {
|
|
16
|
-
const wm =
|
|
16
|
+
const wm = chunkRXAMUBUJ_cjs.createWindowManager(options);
|
|
17
17
|
onDestroy(() => wm.destroy());
|
|
18
18
|
return wm;
|
|
19
19
|
}
|
|
@@ -28,7 +28,7 @@ function useWmWindow(wm, id) {
|
|
|
28
28
|
return core.computed(() => state().windows[id]);
|
|
29
29
|
}
|
|
30
30
|
function createDesktop(wm, options) {
|
|
31
|
-
const binder =
|
|
31
|
+
const binder = chunkRXAMUBUJ_cjs.createDesktopBinder(wm, options);
|
|
32
32
|
onDestroy(() => binder.destroy());
|
|
33
33
|
return {
|
|
34
34
|
binder,
|
package/dist/angular.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Signal } from '@angular/core';
|
|
2
|
-
import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-
|
|
3
|
-
import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-
|
|
2
|
+
import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-LP7Inhxx.cjs';
|
|
3
|
+
import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-EBroZTRw.cjs';
|
|
4
4
|
|
|
5
5
|
declare function useWindowManager(options?: ManagerOptions): WindowManager;
|
|
6
6
|
declare function useWmState(wm: WindowManager): Signal<ManagerState>;
|
package/dist/angular.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Signal } from '@angular/core';
|
|
2
|
-
import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-
|
|
3
|
-
import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-
|
|
2
|
+
import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-LP7Inhxx.js';
|
|
3
|
+
import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-Ca3GOfZX.js';
|
|
4
4
|
|
|
5
5
|
declare function useWindowManager(options?: ManagerOptions): WindowManager;
|
|
6
6
|
declare function useWmState(wm: WindowManager): Signal<ManagerState>;
|
package/dist/angular.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-
|
|
1
|
+
import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-LP7Inhxx.js';
|
|
2
2
|
|
|
3
3
|
declare function clamp(value: number, min: number, max: number): number;
|
|
4
4
|
declare function clampSize(size: Size, min: Size, max: Size | null): Size;
|
|
@@ -54,7 +54,11 @@ interface HitAreaOptions {
|
|
|
54
54
|
interface MagnetismOptions {
|
|
55
55
|
threshold?: number;
|
|
56
56
|
}
|
|
57
|
+
interface GroupingOptions {
|
|
58
|
+
dwell?: number;
|
|
59
|
+
}
|
|
57
60
|
interface DesktopOptions {
|
|
61
|
+
grouping?: boolean | GroupingOptions;
|
|
58
62
|
snap?: boolean | DesktopSnapOptions;
|
|
59
63
|
keyboard?: boolean | DesktopKeyboardOptions;
|
|
60
64
|
announce?: boolean | Partial<AnnouncerMessages>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-
|
|
1
|
+
import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-LP7Inhxx.cjs';
|
|
2
2
|
|
|
3
3
|
declare function clamp(value: number, min: number, max: number): number;
|
|
4
4
|
declare function clampSize(size: Size, min: Size, max: Size | null): Size;
|
|
@@ -54,7 +54,11 @@ interface HitAreaOptions {
|
|
|
54
54
|
interface MagnetismOptions {
|
|
55
55
|
threshold?: number;
|
|
56
56
|
}
|
|
57
|
+
interface GroupingOptions {
|
|
58
|
+
dwell?: number;
|
|
59
|
+
}
|
|
57
60
|
interface DesktopOptions {
|
|
61
|
+
grouping?: boolean | GroupingOptions;
|
|
58
62
|
snap?: boolean | DesktopSnapOptions;
|
|
59
63
|
keyboard?: boolean | DesktopKeyboardOptions;
|
|
60
64
|
announce?: boolean | Partial<AnnouncerMessages>;
|
|
@@ -197,6 +197,8 @@ function createWindowManager(options = {}) {
|
|
|
197
197
|
let focusedId = null;
|
|
198
198
|
let viewport = options.viewport ?? { width: 0, height: 0 };
|
|
199
199
|
let workspace = normalizeWorkspace(options.workspace, 0);
|
|
200
|
+
let activeTabs = {};
|
|
201
|
+
let groupCounter = 0;
|
|
200
202
|
let seq = 0;
|
|
201
203
|
let idCounter = 0;
|
|
202
204
|
let cascadeIndex = 0;
|
|
@@ -215,7 +217,7 @@ function createWindowManager(options = {}) {
|
|
|
215
217
|
const layouts = /* @__PURE__ */ new Map();
|
|
216
218
|
function getState() {
|
|
217
219
|
if (!snapshot) {
|
|
218
|
-
snapshot = { windows, order, focusedId, viewport, workspace };
|
|
220
|
+
snapshot = { windows, order, focusedId, viewport, workspace, groups: buildGroups() };
|
|
219
221
|
}
|
|
220
222
|
return snapshot;
|
|
221
223
|
}
|
|
@@ -267,11 +269,54 @@ function createWindowManager(options = {}) {
|
|
|
267
269
|
function onActiveWorkspace(win) {
|
|
268
270
|
return win.workspace === workspace;
|
|
269
271
|
}
|
|
272
|
+
function membersOf(groupId) {
|
|
273
|
+
return order.filter((entry) => windows[entry].groupId === groupId);
|
|
274
|
+
}
|
|
275
|
+
function isVisibleTab(win) {
|
|
276
|
+
return win.groupId === null || activeTabs[win.groupId] === win.id;
|
|
277
|
+
}
|
|
278
|
+
function buildGroups() {
|
|
279
|
+
const result = {};
|
|
280
|
+
for (const [groupId, activeId] of Object.entries(activeTabs)) {
|
|
281
|
+
result[groupId] = { id: groupId, activeId, members: membersOf(groupId) };
|
|
282
|
+
}
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
function syncGroup(source) {
|
|
286
|
+
if (source.groupId === null) return;
|
|
287
|
+
for (const id of membersOf(source.groupId)) {
|
|
288
|
+
if (id === source.id) continue;
|
|
289
|
+
const member = windows[id];
|
|
290
|
+
if (boundsEqual(member.bounds, source.bounds) && member.stage === source.stage && member.snapZone === source.snapZone && member.layer === source.layer && member.workspace === source.workspace) {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
setWindow({
|
|
294
|
+
...member,
|
|
295
|
+
bounds: source.bounds,
|
|
296
|
+
stage: source.stage,
|
|
297
|
+
snapZone: source.snapZone,
|
|
298
|
+
restoreBounds: source.restoreBounds,
|
|
299
|
+
restoreStage: source.restoreStage,
|
|
300
|
+
layer: source.layer,
|
|
301
|
+
workspace: source.workspace
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function dissolveIfOrphaned(groupId) {
|
|
306
|
+
const members = membersOf(groupId);
|
|
307
|
+
if (members.length >= 2) return;
|
|
308
|
+
for (const id of members) {
|
|
309
|
+
setWindow({ ...windows[id], groupId: null });
|
|
310
|
+
}
|
|
311
|
+
const { [groupId]: _dropped, ...rest } = activeTabs;
|
|
312
|
+
activeTabs = rest;
|
|
313
|
+
}
|
|
270
314
|
function topModalId() {
|
|
271
315
|
for (let i = order.length - 1; i >= 0; i -= 1) {
|
|
272
316
|
const win = windows[order[i]];
|
|
273
|
-
if (win.layer === "modal" && win.stage !== "minimized" && onActiveWorkspace(win))
|
|
317
|
+
if (win.layer === "modal" && win.stage !== "minimized" && onActiveWorkspace(win) && isVisibleTab(win)) {
|
|
274
318
|
return win.id;
|
|
319
|
+
}
|
|
275
320
|
}
|
|
276
321
|
return null;
|
|
277
322
|
}
|
|
@@ -279,7 +324,7 @@ function createWindowManager(options = {}) {
|
|
|
279
324
|
const modal = topModalId();
|
|
280
325
|
return order.filter((id) => {
|
|
281
326
|
const win = windows[id];
|
|
282
|
-
if (win.stage === "minimized" || !onActiveWorkspace(win)) return false;
|
|
327
|
+
if (win.stage === "minimized" || !onActiveWorkspace(win) || !isVisibleTab(win)) return false;
|
|
283
328
|
if (modal && win.layer !== "modal") return false;
|
|
284
329
|
return true;
|
|
285
330
|
});
|
|
@@ -342,6 +387,7 @@ function createWindowManager(options = {}) {
|
|
|
342
387
|
snapZone: null,
|
|
343
388
|
layer: init.layer ?? "normal",
|
|
344
389
|
workspace: normalizeWorkspace(init.workspace, workspace),
|
|
390
|
+
groupId: null,
|
|
345
391
|
minSize,
|
|
346
392
|
maxSize,
|
|
347
393
|
aspectRatio,
|
|
@@ -385,6 +431,13 @@ function createWindowManager(options = {}) {
|
|
|
385
431
|
const win = windows[id];
|
|
386
432
|
if (!win) return false;
|
|
387
433
|
removeWindow(id);
|
|
434
|
+
if (win.groupId !== null) {
|
|
435
|
+
const remaining = membersOf(win.groupId);
|
|
436
|
+
if (activeTabs[win.groupId] === id && remaining[0]) {
|
|
437
|
+
activeTabs = { ...activeTabs, [win.groupId]: remaining[0] };
|
|
438
|
+
}
|
|
439
|
+
dissolveIfOrphaned(win.groupId);
|
|
440
|
+
}
|
|
388
441
|
queueEvent(() => emitter.emit("close", { window: win }));
|
|
389
442
|
if (focusedId === id) {
|
|
390
443
|
focusTop();
|
|
@@ -401,6 +454,7 @@ function createWindowManager(options = {}) {
|
|
|
401
454
|
const win = windows[id];
|
|
402
455
|
if (!win) return false;
|
|
403
456
|
if (!onActiveWorkspace(win)) setWorkspace(win.workspace);
|
|
457
|
+
if (!isVisibleTab(win)) activateTab(id);
|
|
404
458
|
const modal = topModalId();
|
|
405
459
|
if (modal && modal !== id && win.layer !== "modal") {
|
|
406
460
|
const modalWin = windows[modal];
|
|
@@ -460,6 +514,7 @@ function createWindowManager(options = {}) {
|
|
|
460
514
|
const next = build(win);
|
|
461
515
|
if (!next) return false;
|
|
462
516
|
setWindow(next);
|
|
517
|
+
syncGroup(next);
|
|
463
518
|
queueEvent(() => emitter.emit("stage", { window: next, previous: win.stage }));
|
|
464
519
|
commit();
|
|
465
520
|
return true;
|
|
@@ -569,6 +624,7 @@ function createWindowManager(options = {}) {
|
|
|
569
624
|
if (boundsEqual(bounds, win.bounds)) return true;
|
|
570
625
|
const next = { ...win, bounds };
|
|
571
626
|
setWindow(next);
|
|
627
|
+
syncGroup(next);
|
|
572
628
|
queueEvent(() => emitter.emit("move", { window: next }));
|
|
573
629
|
commit();
|
|
574
630
|
return true;
|
|
@@ -590,6 +646,7 @@ function createWindowManager(options = {}) {
|
|
|
590
646
|
if (boundsEqual(bounds, win.bounds) && !becameNormal) return true;
|
|
591
647
|
const next = becameNormal ? { ...win, stage: "normal", snapZone: null, restoreBounds: null, bounds } : { ...win, bounds };
|
|
592
648
|
setWindow(next);
|
|
649
|
+
syncGroup(next);
|
|
593
650
|
if (becameNormal) queueEvent(() => emitter.emit("stage", { window: next, previous: "snapped" }));
|
|
594
651
|
queueEvent(() => emitter.emit("resize", { window: next }));
|
|
595
652
|
commit();
|
|
@@ -617,6 +674,7 @@ function createWindowManager(options = {}) {
|
|
|
617
674
|
const resized = size.width !== next.bounds.width || size.height !== next.bounds.height;
|
|
618
675
|
const finalWin = resized ? { ...next, bounds: { ...next.bounds, ...size } } : next;
|
|
619
676
|
setWindow(finalWin);
|
|
677
|
+
syncGroup(finalWin);
|
|
620
678
|
if (patch.layer && patch.layer !== win.layer) {
|
|
621
679
|
order = sortByLayer(order);
|
|
622
680
|
queueEvent(() => emitter.emit("order", { order }));
|
|
@@ -658,6 +716,7 @@ function createWindowManager(options = {}) {
|
|
|
658
716
|
if (target === win.workspace) return false;
|
|
659
717
|
const updated = { ...win, workspace: target };
|
|
660
718
|
setWindow(updated);
|
|
719
|
+
syncGroup(updated);
|
|
661
720
|
if (!onActiveWorkspace(updated) && focusedId === id) {
|
|
662
721
|
focusTop();
|
|
663
722
|
if (focusedId) emitFocus(windows[focusedId], id);
|
|
@@ -670,6 +729,77 @@ function createWindowManager(options = {}) {
|
|
|
670
729
|
commit();
|
|
671
730
|
return true;
|
|
672
731
|
}
|
|
732
|
+
function emitGroup(groupId, previous) {
|
|
733
|
+
const members = membersOf(groupId);
|
|
734
|
+
const activeId = activeTabs[groupId];
|
|
735
|
+
queueEvent(() => emitter.emit("group", { groupId, members, activeId, previous }));
|
|
736
|
+
}
|
|
737
|
+
function group(ids) {
|
|
738
|
+
const seeds = ids.filter((id, index) => windows[id] && ids.indexOf(id) === index);
|
|
739
|
+
const host = seeds[0] ? windows[seeds[0]] : null;
|
|
740
|
+
if (!host) return null;
|
|
741
|
+
const expanded = [];
|
|
742
|
+
for (const id of seeds) {
|
|
743
|
+
const win = windows[id];
|
|
744
|
+
const siblings = win.groupId === null ? [id] : membersOf(win.groupId);
|
|
745
|
+
for (const entry of siblings) if (!expanded.includes(entry)) expanded.push(entry);
|
|
746
|
+
}
|
|
747
|
+
if (expanded.length < 2) return null;
|
|
748
|
+
const groupId = `${idPrefix}-group-${++groupCounter}`;
|
|
749
|
+
const retired = /* @__PURE__ */ new Set();
|
|
750
|
+
for (const id of expanded) {
|
|
751
|
+
const win = windows[id];
|
|
752
|
+
if (win.groupId !== null) retired.add(win.groupId);
|
|
753
|
+
setWindow({
|
|
754
|
+
...win,
|
|
755
|
+
groupId,
|
|
756
|
+
bounds: host.bounds,
|
|
757
|
+
stage: host.stage,
|
|
758
|
+
snapZone: host.snapZone,
|
|
759
|
+
restoreBounds: host.restoreBounds,
|
|
760
|
+
restoreStage: host.restoreStage,
|
|
761
|
+
layer: host.layer,
|
|
762
|
+
workspace: host.workspace
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
const next = { ...activeTabs, [groupId]: host.id };
|
|
766
|
+
for (const old of retired) delete next[old];
|
|
767
|
+
activeTabs = next;
|
|
768
|
+
raise(host.id);
|
|
769
|
+
emitGroup(groupId, null);
|
|
770
|
+
queueEvent(() => emitter.emit("order", { order }));
|
|
771
|
+
commit();
|
|
772
|
+
if (onActiveWorkspace(host) && host.stage !== "minimized") focus(host.id);
|
|
773
|
+
return groupId;
|
|
774
|
+
}
|
|
775
|
+
function ungroup(id) {
|
|
776
|
+
const win = windows[id];
|
|
777
|
+
if (!win?.groupId) return false;
|
|
778
|
+
const groupId = win.groupId;
|
|
779
|
+
const wasActive = activeTabs[groupId] === id;
|
|
780
|
+
setWindow({ ...win, groupId: null });
|
|
781
|
+
const remaining = membersOf(groupId);
|
|
782
|
+
if (wasActive) activeTabs = { ...activeTabs, [groupId]: remaining[0] };
|
|
783
|
+
dissolveIfOrphaned(groupId);
|
|
784
|
+
if (membersOf(groupId).length >= 2) emitGroup(groupId, wasActive ? id : null);
|
|
785
|
+
commit();
|
|
786
|
+
focus(id);
|
|
787
|
+
return true;
|
|
788
|
+
}
|
|
789
|
+
function activateTab(id) {
|
|
790
|
+
const win = windows[id];
|
|
791
|
+
if (!win?.groupId) return false;
|
|
792
|
+
const groupId = win.groupId;
|
|
793
|
+
const previous = activeTabs[groupId];
|
|
794
|
+
if (previous === id) return false;
|
|
795
|
+
activeTabs = { ...activeTabs, [groupId]: id };
|
|
796
|
+
emitGroup(groupId, previous);
|
|
797
|
+
commit();
|
|
798
|
+
return true;
|
|
799
|
+
}
|
|
800
|
+
function groupMembers(groupId) {
|
|
801
|
+
return membersOf(groupId).map((id) => windows[id]);
|
|
802
|
+
}
|
|
673
803
|
function sendToBack(id) {
|
|
674
804
|
const win = windows[id];
|
|
675
805
|
if (!win) return false;
|
|
@@ -701,10 +831,10 @@ function createWindowManager(options = {}) {
|
|
|
701
831
|
);
|
|
702
832
|
}
|
|
703
833
|
function minimized() {
|
|
704
|
-
return Object.values(windows).filter((win) => win.stage === "minimized" && onActiveWorkspace(win)).sort((a, b) => a.openedSeq - b.openedSeq);
|
|
834
|
+
return Object.values(windows).filter((win) => win.stage === "minimized" && onActiveWorkspace(win) && isVisibleTab(win)).sort((a, b) => a.openedSeq - b.openedSeq);
|
|
705
835
|
}
|
|
706
836
|
function captureEntry() {
|
|
707
|
-
return { windows, order, focusedId, workspace };
|
|
837
|
+
return { windows, order, focusedId, workspace, activeTabs };
|
|
708
838
|
}
|
|
709
839
|
function recordHistory() {
|
|
710
840
|
if (!historyDirty) return;
|
|
@@ -766,6 +896,7 @@ function createWindowManager(options = {}) {
|
|
|
766
896
|
windows = entry.windows;
|
|
767
897
|
order = [...entry.order];
|
|
768
898
|
focusedId = entry.focusedId;
|
|
899
|
+
activeTabs = entry.activeTabs;
|
|
769
900
|
const previousWorkspace = workspace;
|
|
770
901
|
workspace = entry.workspace;
|
|
771
902
|
if (workspace !== previousWorkspace) {
|
|
@@ -896,7 +1027,8 @@ function createWindowManager(options = {}) {
|
|
|
896
1027
|
})),
|
|
897
1028
|
order: [...order],
|
|
898
1029
|
focusedId,
|
|
899
|
-
workspace
|
|
1030
|
+
workspace,
|
|
1031
|
+
activeTabs: { ...activeTabs }
|
|
900
1032
|
};
|
|
901
1033
|
}
|
|
902
1034
|
function isBounds(value) {
|
|
@@ -951,6 +1083,7 @@ function createWindowManager(options = {}) {
|
|
|
951
1083
|
snapZone: ZONES.includes(raw.snapZone) ? raw.snapZone : null,
|
|
952
1084
|
layer: raw.layer,
|
|
953
1085
|
workspace: normalizeWorkspace(raw.workspace, 0),
|
|
1086
|
+
groupId: typeof raw.groupId === "string" && raw.groupId.length > 0 ? raw.groupId : null,
|
|
954
1087
|
minSize: isSize(raw.minSize) ? { ...raw.minSize } : { width: 160, height: 100 },
|
|
955
1088
|
maxSize: readMaxSize(raw.maxSize),
|
|
956
1089
|
aspectRatio: typeof raw.aspectRatio === "number" && Number.isFinite(raw.aspectRatio) && raw.aspectRatio > 0 ? raw.aspectRatio : null,
|
|
@@ -992,7 +1125,18 @@ function createWindowManager(options = {}) {
|
|
|
992
1125
|
if (focusedCandidate && (focusedCandidate.stage === "minimized" || !onActiveWorkspace(focusedCandidate))) {
|
|
993
1126
|
focusedId = null;
|
|
994
1127
|
}
|
|
995
|
-
|
|
1128
|
+
const restoredTabs = {};
|
|
1129
|
+
const rawTabs = data.activeTabs;
|
|
1130
|
+
for (const id of order) {
|
|
1131
|
+
const groupId = windows[id].groupId;
|
|
1132
|
+
if (groupId === null || restoredTabs[groupId]) continue;
|
|
1133
|
+
const wanted = typeof rawTabs === "object" && rawTabs !== null ? rawTabs[groupId] : void 0;
|
|
1134
|
+
const members = membersOf(groupId);
|
|
1135
|
+
restoredTabs[groupId] = typeof wanted === "string" && members.includes(wanted) ? wanted : members[0];
|
|
1136
|
+
}
|
|
1137
|
+
activeTabs = restoredTabs;
|
|
1138
|
+
for (const groupId of Object.keys(restoredTabs)) dissolveIfOrphaned(groupId);
|
|
1139
|
+
if (!focusedId || !isVisibleTab(windows[focusedId])) focusTop();
|
|
996
1140
|
const modal = topModalId();
|
|
997
1141
|
if (modal) {
|
|
998
1142
|
const focusedWin = focusedId ? windows[focusedId] : null;
|
|
@@ -1046,6 +1190,10 @@ function createWindowManager(options = {}) {
|
|
|
1046
1190
|
workspace: () => workspace,
|
|
1047
1191
|
setWorkspace: (next) => transact(() => setWorkspace(next)),
|
|
1048
1192
|
moveToWorkspace: (id, next) => transact(() => moveToWorkspace(id, next)),
|
|
1193
|
+
group: (ids) => transact(() => group(ids)),
|
|
1194
|
+
ungroup: (id) => transact(() => ungroup(id)),
|
|
1195
|
+
activateTab: (id) => transact(() => activateTab(id)),
|
|
1196
|
+
groupMembers,
|
|
1049
1197
|
batch: (run) => transact(run),
|
|
1050
1198
|
serialize,
|
|
1051
1199
|
hydrate: (data) => transact(() => hydrate(data)),
|
|
@@ -1235,6 +1383,11 @@ function createDragStarter(ctx) {
|
|
|
1235
1383
|
restored: win.stage === "normal",
|
|
1236
1384
|
moved: false,
|
|
1237
1385
|
zone: null,
|
|
1386
|
+
groupTarget: null,
|
|
1387
|
+
hoverId: null,
|
|
1388
|
+
hoverTimer: 0,
|
|
1389
|
+
clientX: event.clientX,
|
|
1390
|
+
clientY: event.clientY,
|
|
1238
1391
|
raf: 0,
|
|
1239
1392
|
pendingX: 0,
|
|
1240
1393
|
pendingY: 0,
|
|
@@ -1243,6 +1396,18 @@ function createDragStarter(ctx) {
|
|
|
1243
1396
|
}
|
|
1244
1397
|
};
|
|
1245
1398
|
const el = ctx.windowElement(id);
|
|
1399
|
+
function armGroup(target2) {
|
|
1400
|
+
session.groupTarget = target2;
|
|
1401
|
+
ctx.markGroupTarget(target2);
|
|
1402
|
+
}
|
|
1403
|
+
function clearHover() {
|
|
1404
|
+
if (session.hoverTimer !== 0) {
|
|
1405
|
+
view.clearTimeout(session.hoverTimer);
|
|
1406
|
+
session.hoverTimer = 0;
|
|
1407
|
+
}
|
|
1408
|
+
session.groupTarget = null;
|
|
1409
|
+
ctx.markGroupTarget(null);
|
|
1410
|
+
}
|
|
1246
1411
|
function flush() {
|
|
1247
1412
|
if (!session.hasPending || ctx.currentDrag() !== session) return;
|
|
1248
1413
|
session.hasPending = false;
|
|
@@ -1290,6 +1455,20 @@ function createDragStarter(ctx) {
|
|
|
1290
1455
|
nextY = magnet.y;
|
|
1291
1456
|
}
|
|
1292
1457
|
wm.move(id, nextX, nextY);
|
|
1458
|
+
const candidate = ctx.groupTarget(session.clientX, session.clientY, id);
|
|
1459
|
+
if (candidate !== session.hoverId) {
|
|
1460
|
+
session.hoverId = candidate;
|
|
1461
|
+
clearHover();
|
|
1462
|
+
if (candidate) {
|
|
1463
|
+
if (ctx.groupDwell <= 0) armGroup(candidate);
|
|
1464
|
+
else session.hoverTimer = view.setTimeout(() => armGroup(candidate), ctx.groupDwell);
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
if (session.groupTarget) {
|
|
1468
|
+
session.zone = null;
|
|
1469
|
+
ctx.hidePreview();
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1293
1472
|
if (ctx.snapEnabled && current.snappable) {
|
|
1294
1473
|
const viewport = wm.getState().viewport;
|
|
1295
1474
|
const rawZone = detectSnapZone(session.pendingX, session.pendingY, viewport, ctx.snapDetect);
|
|
@@ -1322,6 +1501,8 @@ function createDragStarter(ctx) {
|
|
|
1322
1501
|
}
|
|
1323
1502
|
session.pendingX = movePoint.x;
|
|
1324
1503
|
session.pendingY = movePoint.y;
|
|
1504
|
+
session.clientX = moveEvent.clientX;
|
|
1505
|
+
session.clientY = moveEvent.clientY;
|
|
1325
1506
|
session.hasPending = true;
|
|
1326
1507
|
if (session.raf === 0) session.raf = view.requestAnimationFrame(flush);
|
|
1327
1508
|
}
|
|
@@ -1356,6 +1537,8 @@ function createDragStarter(ctx) {
|
|
|
1356
1537
|
wm.move(id, session.startBounds.x, session.startBounds.y);
|
|
1357
1538
|
}
|
|
1358
1539
|
}
|
|
1540
|
+
} else if (session.moved && session.groupTarget) {
|
|
1541
|
+
if (wm.group([session.groupTarget, id])) wm.activateTab(id);
|
|
1359
1542
|
} else if (session.moved && session.zone) {
|
|
1360
1543
|
if (session.zone === "maximize") wm.maximize(id);
|
|
1361
1544
|
else wm.snap(id, session.zone);
|
|
@@ -1375,6 +1558,8 @@ function createDragStarter(ctx) {
|
|
|
1375
1558
|
el.style.willChange = "";
|
|
1376
1559
|
}
|
|
1377
1560
|
ctx.hidePreview();
|
|
1561
|
+
if (session.hoverTimer !== 0) view.clearTimeout(session.hoverTimer);
|
|
1562
|
+
ctx.markGroupTarget(null);
|
|
1378
1563
|
if (cancelled) wm.abortInteraction();
|
|
1379
1564
|
else wm.endInteraction();
|
|
1380
1565
|
}
|
|
@@ -1544,6 +1729,8 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1544
1729
|
const doc = element.ownerDocument;
|
|
1545
1730
|
const view = windowOf(element);
|
|
1546
1731
|
const coarsePointer = typeof view.matchMedia === "function" && view.matchMedia("(pointer: coarse)").matches;
|
|
1732
|
+
const groupingEnabled = options.grouping !== false;
|
|
1733
|
+
const groupDwell = (typeof options.grouping === "object" ? options.grouping.dwell : void 0) ?? 420;
|
|
1547
1734
|
const snapEnabled = options.snap !== false;
|
|
1548
1735
|
const snapOptions = typeof options.snap === "object" ? options.snap : {};
|
|
1549
1736
|
const snapPreviewEnabled = snapOptions.preview !== false;
|
|
@@ -1567,6 +1754,7 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1567
1754
|
let lastOrder = null;
|
|
1568
1755
|
let lastFocused = null;
|
|
1569
1756
|
let drag = null;
|
|
1757
|
+
let groupTargetId = null;
|
|
1570
1758
|
let cachedRect = null;
|
|
1571
1759
|
let rectUsers = 0;
|
|
1572
1760
|
let announcer = null;
|
|
@@ -1638,6 +1826,29 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1638
1826
|
hitEdge,
|
|
1639
1827
|
hitCorner,
|
|
1640
1828
|
magnetThreshold,
|
|
1829
|
+
groupDwell,
|
|
1830
|
+
groupTarget(clientX, clientY, selfId) {
|
|
1831
|
+
if (!groupingEnabled || typeof doc.elementsFromPoint !== "function") return null;
|
|
1832
|
+
for (const node of doc.elementsFromPoint(clientX, clientY)) {
|
|
1833
|
+
const handle = node.closest?.("[data-wm-drag]");
|
|
1834
|
+
const host = handle?.closest("[data-wm-window]");
|
|
1835
|
+
const id = host?.dataset.wmWindow;
|
|
1836
|
+
if (id && id !== selfId && registry.has(id)) return id;
|
|
1837
|
+
}
|
|
1838
|
+
return null;
|
|
1839
|
+
},
|
|
1840
|
+
markGroupTarget(id) {
|
|
1841
|
+
if (groupTargetId === id) return;
|
|
1842
|
+
if (groupTargetId) {
|
|
1843
|
+
const previous = registry.get(groupTargetId);
|
|
1844
|
+
if (previous) delete previous.element.dataset.wmTabTarget;
|
|
1845
|
+
}
|
|
1846
|
+
groupTargetId = id;
|
|
1847
|
+
if (id) {
|
|
1848
|
+
const next = registry.get(id);
|
|
1849
|
+
if (next) next.element.dataset.wmTabTarget = "";
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1641
1852
|
currentDrag: () => drag,
|
|
1642
1853
|
claimDrag(session) {
|
|
1643
1854
|
drag = session;
|
|
@@ -1655,18 +1866,25 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1655
1866
|
observer.observe(element);
|
|
1656
1867
|
cleanup.push(() => observer.disconnect());
|
|
1657
1868
|
}
|
|
1658
|
-
function syncWindow(attached, win, zIndex, activeWorkspace) {
|
|
1869
|
+
function syncWindow(attached, win, zIndex, activeWorkspace, activeTab) {
|
|
1659
1870
|
const el = attached.element;
|
|
1660
1871
|
const firstSync = attached.lastState === null;
|
|
1661
1872
|
if (firstSync) el.style.transition = "none";
|
|
1662
|
-
if (attached.lastState !== win || attached.lastWorkspace !== activeWorkspace) {
|
|
1873
|
+
if (attached.lastState !== win || attached.lastWorkspace !== activeWorkspace || attached.lastTab !== activeTab) {
|
|
1663
1874
|
el.style.transform = `translate3d(${win.bounds.x}px, ${win.bounds.y}px, 0)`;
|
|
1664
1875
|
el.style.width = `${win.bounds.width}px`;
|
|
1665
1876
|
el.style.height = `${win.bounds.height}px`;
|
|
1666
1877
|
el.dataset.wmStage = win.stage;
|
|
1667
1878
|
el.dataset.wmLayer = win.layer;
|
|
1668
1879
|
el.dataset.wmWorkspace = String(win.workspace);
|
|
1669
|
-
|
|
1880
|
+
if (win.groupId === null) {
|
|
1881
|
+
delete el.dataset.wmGroup;
|
|
1882
|
+
delete el.dataset.wmTab;
|
|
1883
|
+
} else {
|
|
1884
|
+
el.dataset.wmGroup = win.groupId;
|
|
1885
|
+
el.dataset.wmTab = activeTab ? "active" : "inactive";
|
|
1886
|
+
}
|
|
1887
|
+
const hide = win.stage === "minimized" || win.workspace !== activeWorkspace || !activeTab;
|
|
1670
1888
|
if (hide && !el.hidden && el.contains(doc.activeElement)) {
|
|
1671
1889
|
element.focus({ preventScroll: true });
|
|
1672
1890
|
}
|
|
@@ -1684,6 +1902,7 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1684
1902
|
}
|
|
1685
1903
|
attached.lastState = win;
|
|
1686
1904
|
attached.lastWorkspace = activeWorkspace;
|
|
1905
|
+
attached.lastTab = activeTab;
|
|
1687
1906
|
}
|
|
1688
1907
|
if (attached.lastZ !== zIndex) {
|
|
1689
1908
|
el.style.zIndex = String(zIndex + 1);
|
|
@@ -1701,8 +1920,9 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1701
1920
|
const attached = registry.get(id);
|
|
1702
1921
|
const win = state.windows[id];
|
|
1703
1922
|
if (!attached || !win) return;
|
|
1704
|
-
|
|
1705
|
-
|
|
1923
|
+
const activeTab = win.groupId === null || state.groups[win.groupId]?.activeId === win.id;
|
|
1924
|
+
if (orderChanged || attached.lastState !== win || attached.lastWorkspace !== state.workspace || attached.lastTab !== activeTab) {
|
|
1925
|
+
syncWindow(attached, win, index, state.workspace, activeTab);
|
|
1706
1926
|
}
|
|
1707
1927
|
});
|
|
1708
1928
|
if (state.focusedId !== lastFocused) {
|
|
@@ -1805,6 +2025,7 @@ function attachDesktop(wm, element, options = {}) {
|
|
|
1805
2025
|
lastState: null,
|
|
1806
2026
|
lastZ: -1,
|
|
1807
2027
|
lastWorkspace: -1,
|
|
2028
|
+
lastTab: true,
|
|
1808
2029
|
cleanup: []
|
|
1809
2030
|
};
|
|
1810
2031
|
windowElement.dataset.wmWindow = id;
|
|
@@ -2028,5 +2249,5 @@ function createDesktopBinder(wm, options = {}) {
|
|
|
2028
2249
|
}
|
|
2029
2250
|
|
|
2030
2251
|
export { applyAspect, attachDesktop, boundsEqual, clamp, clampSize, clampToViewport, createAnnouncer, createDesktopBinder, createEmitter, createWindowManager, defaultMessages, detectSnapZone, flipFromTarget, flipToTarget, magnetize, prefersReducedMotion, zoneBounds };
|
|
2031
|
-
//# sourceMappingURL=chunk-
|
|
2032
|
-
//# sourceMappingURL=chunk-
|
|
2252
|
+
//# sourceMappingURL=chunk-L43XBN44.js.map
|
|
2253
|
+
//# sourceMappingURL=chunk-L43XBN44.js.map
|