@surdeddd/wmkit 0.5.1 → 0.6.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/dist/angular.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkC2UHNBSA_cjs = require('./chunk-C2UHNBSA.cjs');
3
+ var chunkDYZQY3BE_cjs = require('./chunk-DYZQY3BE.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 = chunkC2UHNBSA_cjs.createWindowManager(options);
16
+ const wm = chunkDYZQY3BE_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 = chunkC2UHNBSA_cjs.createDesktopBinder(wm, options);
31
+ const binder = chunkDYZQY3BE_cjs.createDesktopBinder(wm, options);
32
32
  onDestroy(() => binder.destroy());
33
33
  return {
34
34
  binder,
@@ -1,6 +1,6 @@
1
1
  import { Signal } from '@angular/core';
2
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';
3
+ import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-CQzQJdVj.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
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';
3
+ import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-CkmOoo1U.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 { createWindowManager, createDesktopBinder } from './chunk-2WQHHYYR.js';
1
+ import { createWindowManager, createDesktopBinder } from './chunk-3HLKVOSI.js';
2
2
  import { signal, computed, inject, DestroyRef } from '@angular/core';
3
3
 
4
4
  function onDestroy(dispose) {
@@ -57,6 +57,11 @@ interface MagnetismOptions {
57
57
  interface GroupingOptions {
58
58
  dwell?: number;
59
59
  }
60
+ interface StackingOptions {
61
+ base?: number;
62
+ gap?: number;
63
+ isolate?: boolean;
64
+ }
60
65
  interface DesktopOptions {
61
66
  grouping?: boolean | GroupingOptions;
62
67
  snap?: boolean | DesktopSnapOptions;
@@ -65,9 +70,17 @@ interface DesktopOptions {
65
70
  autoViewport?: boolean;
66
71
  hitAreas?: HitAreaOptions;
67
72
  magnetism?: boolean | MagnetismOptions;
73
+ stacking?: StackingOptions;
74
+ animation?: boolean | AnimationOptions;
75
+ interactiveSelector?: string;
76
+ beforeClose?: (window: WindowState) => boolean | void;
68
77
  minimizeTarget?: (window: WindowState) => Element | null;
69
78
  onTitlebarContextMenu?: (window: WindowState, event: MouseEvent) => void;
70
79
  }
80
+ interface AnimationOptions {
81
+ duration?: number;
82
+ easing?: string;
83
+ }
71
84
  interface WindowAttachOptions {
72
85
  handle?: HTMLElement | string;
73
86
  resizeHandles?: boolean;
@@ -57,6 +57,11 @@ interface MagnetismOptions {
57
57
  interface GroupingOptions {
58
58
  dwell?: number;
59
59
  }
60
+ interface StackingOptions {
61
+ base?: number;
62
+ gap?: number;
63
+ isolate?: boolean;
64
+ }
60
65
  interface DesktopOptions {
61
66
  grouping?: boolean | GroupingOptions;
62
67
  snap?: boolean | DesktopSnapOptions;
@@ -65,9 +70,17 @@ interface DesktopOptions {
65
70
  autoViewport?: boolean;
66
71
  hitAreas?: HitAreaOptions;
67
72
  magnetism?: boolean | MagnetismOptions;
73
+ stacking?: StackingOptions;
74
+ animation?: boolean | AnimationOptions;
75
+ interactiveSelector?: string;
76
+ beforeClose?: (window: WindowState) => boolean | void;
68
77
  minimizeTarget?: (window: WindowState) => Element | null;
69
78
  onTitlebarContextMenu?: (window: WindowState, event: MouseEvent) => void;
70
79
  }
80
+ interface AnimationOptions {
81
+ duration?: number;
82
+ easing?: string;
83
+ }
71
84
  interface WindowAttachOptions {
72
85
  handle?: HTMLElement | string;
73
86
  resizeHandles?: boolean;
@@ -193,6 +193,12 @@ function createWindowManager(options = {}) {
193
193
  const idPrefix = options.idPrefix ?? "wm";
194
194
  const historyLimit = options.historyLimit ?? 50;
195
195
  let windows = {};
196
+ let windowsShared = false;
197
+ let modalCount = 0;
198
+ let cachedGroups = null;
199
+ let cachedOrder = null;
200
+ let cachedTabs = null;
201
+ let groupsDirty = false;
196
202
  let order = [];
197
203
  let focusedId = null;
198
204
  let viewport = options.viewport ?? { width: 0, height: 0 };
@@ -217,6 +223,7 @@ function createWindowManager(options = {}) {
217
223
  const layouts = /* @__PURE__ */ new Map();
218
224
  function getState() {
219
225
  if (!snapshot) {
226
+ windowsShared = true;
220
227
  snapshot = { windows, order, focusedId, viewport, workspace, groups: buildGroups() };
221
228
  }
222
229
  return snapshot;
@@ -238,14 +245,36 @@ function createWindowManager(options = {}) {
238
245
  function queueEvent(emit) {
239
246
  pendingEvents.push(emit);
240
247
  }
248
+ function ownWindows() {
249
+ if (windowsShared) {
250
+ windows = { ...windows };
251
+ windowsShared = false;
252
+ }
253
+ return windows;
254
+ }
241
255
  function setWindow(next) {
242
- windows = { ...windows, [next.id]: next };
256
+ const map = ownWindows();
257
+ const previous = map[next.id];
258
+ const wasModal = previous?.layer === "modal";
259
+ if (wasModal !== (next.layer === "modal")) modalCount += wasModal ? -1 : 1;
260
+ if (previous?.groupId !== next.groupId) groupsDirty = true;
261
+ map[next.id] = next;
243
262
  }
244
263
  function removeWindow(id) {
245
- const { [id]: _removed, ...rest } = windows;
246
- windows = rest;
264
+ const map = ownWindows();
265
+ const previous = map[id];
266
+ if (previous?.layer === "modal") modalCount -= 1;
267
+ if (previous?.groupId != null) groupsDirty = true;
268
+ delete map[id];
247
269
  order = order.filter((entry) => entry !== id);
248
270
  }
271
+ function countModals() {
272
+ let total = 0;
273
+ for (const id of order) {
274
+ if (windows[id].layer === "modal") total += 1;
275
+ }
276
+ return total;
277
+ }
249
278
  function layerRankOf(id) {
250
279
  return LAYER_RANK[windows[id].layer];
251
280
  }
@@ -282,31 +311,57 @@ function createWindowManager(options = {}) {
282
311
  return win.groupId === null || activeTabs[win.groupId] === win.id;
283
312
  }
284
313
  function buildGroups() {
314
+ if (cachedGroups && !groupsDirty && cachedOrder === order && cachedTabs === activeTabs) {
315
+ return cachedGroups;
316
+ }
285
317
  const result = /* @__PURE__ */ Object.create(null);
286
318
  for (const [groupId, activeId] of Object.entries(activeTabs)) {
287
319
  result[groupId] = { id: groupId, activeId, members: membersOf(groupId) };
288
320
  }
321
+ cachedGroups = result;
322
+ cachedOrder = order;
323
+ cachedTabs = activeTabs;
324
+ groupsDirty = false;
289
325
  return result;
290
326
  }
327
+ function sharedSize(ids, size) {
328
+ let shared = size;
329
+ for (const id of ids) {
330
+ const fit = normalizeSize(shared, windows[id]);
331
+ shared = {
332
+ width: Math.max(shared.width, fit.width),
333
+ height: Math.max(shared.height, fit.height)
334
+ };
335
+ }
336
+ return shared;
337
+ }
291
338
  function syncGroup(source) {
292
- if (source.groupId === null) return;
293
- for (const id of membersOf(source.groupId)) {
294
- if (id === source.id) continue;
339
+ if (source.groupId === null) return source;
340
+ const members = membersOf(source.groupId);
341
+ const fitted = sharedSize(members, source.bounds);
342
+ let effective = source;
343
+ if (fitted.width !== source.bounds.width || fitted.height !== source.bounds.height) {
344
+ effective = { ...source, bounds: { ...source.bounds, ...fitted } };
345
+ setWindow(effective);
346
+ }
347
+ for (const id of members) {
348
+ if (id === effective.id) continue;
295
349
  const member = windows[id];
296
- if (boundsEqual(member.bounds, source.bounds) && member.stage === source.stage && member.snapZone === source.snapZone && member.layer === source.layer && member.workspace === source.workspace) {
350
+ if (boundsEqual(member.bounds, effective.bounds) && member.stage === effective.stage && member.snapZone === effective.snapZone && member.layer === effective.layer && member.workspace === effective.workspace) {
297
351
  continue;
298
352
  }
299
353
  setWindow({
300
354
  ...member,
301
- bounds: source.bounds,
302
- stage: source.stage,
303
- snapZone: source.snapZone,
304
- restoreBounds: source.restoreBounds,
305
- restoreStage: source.restoreStage,
306
- layer: source.layer,
307
- workspace: source.workspace
355
+ bounds: effective.bounds,
356
+ stage: effective.stage,
357
+ snapZone: effective.snapZone,
358
+ restoreBounds: effective.restoreBounds,
359
+ restoreStage: effective.restoreStage,
360
+ layer: effective.layer,
361
+ workspace: effective.workspace
308
362
  });
309
363
  }
364
+ return effective;
310
365
  }
311
366
  function dissolveIfOrphaned(groupId) {
312
367
  const members = membersOf(groupId);
@@ -318,6 +373,7 @@ function createWindowManager(options = {}) {
318
373
  activeTabs = rest;
319
374
  }
320
375
  function topModalId() {
376
+ if (modalCount === 0) return null;
321
377
  for (let i = order.length - 1; i >= 0; i -= 1) {
322
378
  const win = windows[order[i]];
323
379
  if (win.layer === "modal" && win.stage !== "minimized" && onActiveWorkspace(win) && isVisibleTab(win)) {
@@ -336,8 +392,13 @@ function createWindowManager(options = {}) {
336
392
  });
337
393
  }
338
394
  function focusTop() {
339
- const targets = focusTargets();
340
- focusedId = targets.length > 0 ? targets[targets.length - 1] : null;
395
+ for (let i = order.length - 1; i >= 0; i -= 1) {
396
+ const win = windows[order[i]];
397
+ if (win.stage === "minimized" || !onActiveWorkspace(win) || !isVisibleTab(win)) continue;
398
+ focusedId = win.id;
399
+ return;
400
+ }
401
+ focusedId = null;
341
402
  }
342
403
  function reconcileFocus() {
343
404
  if (focusedId === null) return;
@@ -528,8 +589,8 @@ function createWindowManager(options = {}) {
528
589
  const next = build(win);
529
590
  if (!next) return false;
530
591
  setWindow(next);
531
- syncGroup(next);
532
- queueEvent(() => emitter.emit("stage", { window: next, previous: win.stage }));
592
+ const synced = syncGroup(next);
593
+ queueEvent(() => emitter.emit("stage", { window: synced, previous: win.stage }));
533
594
  if (next.groupId !== null) reconcileFocus();
534
595
  commit();
535
596
  return true;
@@ -661,9 +722,11 @@ function createWindowManager(options = {}) {
661
722
  if (boundsEqual(bounds, win.bounds) && !becameNormal) return true;
662
723
  const next = becameNormal ? { ...win, stage: "normal", snapZone: null, restoreBounds: null, bounds } : { ...win, bounds };
663
724
  setWindow(next);
664
- syncGroup(next);
665
- if (becameNormal) queueEvent(() => emitter.emit("stage", { window: next, previous: "snapped" }));
666
- queueEvent(() => emitter.emit("resize", { window: next }));
725
+ const synced = syncGroup(next);
726
+ if (becameNormal) {
727
+ queueEvent(() => emitter.emit("stage", { window: synced, previous: "snapped" }));
728
+ }
729
+ queueEvent(() => emitter.emit("resize", { window: synced }));
667
730
  commit();
668
731
  return true;
669
732
  }
@@ -689,18 +752,18 @@ function createWindowManager(options = {}) {
689
752
  const resized = size.width !== next.bounds.width || size.height !== next.bounds.height;
690
753
  const finalWin = resized ? { ...next, bounds: { ...next.bounds, ...size } } : next;
691
754
  setWindow(finalWin);
692
- syncGroup(finalWin);
755
+ const synced = syncGroup(finalWin);
693
756
  if (patch.layer && patch.layer !== win.layer) {
694
757
  order = sortByLayer(order);
695
758
  queueEvent(() => emitter.emit("order", { order }));
696
759
  if (patch.layer === "modal" && topModalId() === id && focusedId !== id) {
697
760
  const previous = focusedId;
698
761
  focusedId = id;
699
- emitFocus(finalWin, previous);
762
+ emitFocus(synced, previous);
700
763
  }
701
764
  }
702
- queueEvent(() => emitter.emit("update", { window: finalWin }));
703
- if (resized) queueEvent(() => emitter.emit("resize", { window: finalWin }));
765
+ queueEvent(() => emitter.emit("update", { window: synced }));
766
+ if (resized) queueEvent(() => emitter.emit("resize", { window: synced }));
704
767
  commit();
705
768
  return true;
706
769
  }
@@ -795,9 +858,10 @@ function createWindowManager(options = {}) {
795
858
  emitGroup(groupId, null);
796
859
  queueEvent(() => emitter.emit("order", { order }));
797
860
  const hostVisible = onActiveWorkspace(host) && host.stage !== "minimized";
798
- if (!hostVisible) reconcileFocus();
799
861
  commit();
800
862
  if (hostVisible) focus(host.id);
863
+ reconcileFocus();
864
+ commit();
801
865
  return groupId;
802
866
  }
803
867
  function ungroup(id) {
@@ -867,6 +931,7 @@ function createWindowManager(options = {}) {
867
931
  return Object.values(windows).filter((win) => win.stage === "minimized" && onActiveWorkspace(win) && isVisibleTab(win)).sort((a, b) => a.openedSeq - b.openedSeq);
868
932
  }
869
933
  function captureEntry() {
934
+ windowsShared = true;
870
935
  return { windows, order, focusedId, workspace, activeTabs };
871
936
  }
872
937
  function recordHistory() {
@@ -911,13 +976,13 @@ function createWindowManager(options = {}) {
911
976
  if (win.stage === "maximized") {
912
977
  const updated = { ...win, bounds: fullBounds() };
913
978
  setWindow(updated);
914
- syncGroup(updated);
915
- queueEvent(() => emitter.emit("resize", { window: updated }));
979
+ const synced = syncGroup(updated);
980
+ queueEvent(() => emitter.emit("resize", { window: synced }));
916
981
  } else if (win.stage === "snapped" && win.snapZone) {
917
982
  const updated = { ...win, bounds: snapBounds(win, win.snapZone) };
918
983
  setWindow(updated);
919
- syncGroup(updated);
920
- queueEvent(() => emitter.emit("resize", { window: updated }));
984
+ const synced = syncGroup(updated);
985
+ queueEvent(() => emitter.emit("resize", { window: synced }));
921
986
  } else if (win.stage === "normal" && keepInViewport) {
922
987
  const bounds = clampToViewport(win.bounds, viewport, minVisible);
923
988
  if (!boundsEqual(bounds, win.bounds)) {
@@ -932,6 +997,8 @@ function createWindowManager(options = {}) {
932
997
  function applyEntry(entry) {
933
998
  windows = entry.windows;
934
999
  order = [...entry.order];
1000
+ modalCount = countModals();
1001
+ groupsDirty = true;
935
1002
  focusedId = entry.focusedId;
936
1003
  activeTabs = entry.activeTabs;
937
1004
  const previousWorkspace = workspace;
@@ -1150,8 +1217,11 @@ function createWindowManager(options = {}) {
1150
1217
  if (!seen.has(id)) nextOrder.push(id);
1151
1218
  }
1152
1219
  windows = nextWindows;
1220
+ windowsShared = false;
1153
1221
  order = nextOrder;
1154
1222
  order = sortByLayer(order);
1223
+ modalCount = countModals();
1224
+ groupsDirty = true;
1155
1225
  seq = maxSeq;
1156
1226
  const previousWorkspace = workspace;
1157
1227
  workspace = normalizeWorkspace(data.workspace, 0);
@@ -1260,6 +1330,7 @@ function createWindowManager(options = {}) {
1260
1330
 
1261
1331
  // src/dom/animate.ts
1262
1332
  function prefersReducedMotion(win) {
1333
+ if (typeof win.matchMedia !== "function") return false;
1263
1334
  return win.matchMedia("(prefers-reduced-motion: reduce)").matches;
1264
1335
  }
1265
1336
  function flipFromTarget(source, target, options = {}) {
@@ -1388,14 +1459,6 @@ function createAnnouncer(wm, container, messages = {}) {
1388
1459
  };
1389
1460
  }
1390
1461
 
1391
- // src/dom/shared.ts
1392
- var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize]";
1393
- function windowOf(element) {
1394
- const view = element.ownerDocument.defaultView;
1395
- if (!view) throw new Error("wmkit: desktop element is not attached to a document");
1396
- return view;
1397
- }
1398
-
1399
1462
  // src/dom/drag.ts
1400
1463
  function createDragStarter(ctx) {
1401
1464
  const { wm, doc, view } = ctx;
@@ -1403,7 +1466,7 @@ function createDragStarter(ctx) {
1403
1466
  const win = wm.get(id);
1404
1467
  if (!win?.draggable || event.button !== 0 || ctx.currentDrag()) return;
1405
1468
  const target = event.target;
1406
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
1469
+ if (target?.closest(ctx.interactiveSelector)) return;
1407
1470
  event.preventDefault();
1408
1471
  const releaseRect = ctx.trackRect();
1409
1472
  const point = ctx.toLocal(event);
@@ -1758,6 +1821,99 @@ function createResizeHandles(doc, edge, corner) {
1758
1821
  });
1759
1822
  }
1760
1823
 
1824
+ // src/dom/shared.ts
1825
+ var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize]";
1826
+ function windowOf(element) {
1827
+ const view = element.ownerDocument.defaultView;
1828
+ if (!view) throw new Error("wmkit: desktop element is not attached to a document");
1829
+ return view;
1830
+ }
1831
+
1832
+ // src/dom/stacking.ts
1833
+ var UNASSIGNED = Number.NEGATIVE_INFINITY;
1834
+ var tails = new Int32Array(0);
1835
+ var parents = new Int32Array(0);
1836
+ var kept = new Uint8Array(0);
1837
+ function reserve(length) {
1838
+ if (tails.length >= length) return;
1839
+ const size = Math.max(length, 64);
1840
+ tails = new Int32Array(size);
1841
+ parents = new Int32Array(size);
1842
+ kept = new Uint8Array(size);
1843
+ }
1844
+ function markLongestIncreasing(target) {
1845
+ const length = target.length;
1846
+ reserve(length);
1847
+ kept.fill(0, 0, length);
1848
+ let size = 0;
1849
+ for (let i = 0; i < length; i += 1) {
1850
+ const value = target.zAt(i);
1851
+ parents[i] = -1;
1852
+ if (!Number.isFinite(value)) continue;
1853
+ let low = 0;
1854
+ let high = size;
1855
+ while (low < high) {
1856
+ const mid = low + high >> 1;
1857
+ if (target.zAt(tails[mid]) < value) low = mid + 1;
1858
+ else high = mid;
1859
+ }
1860
+ if (low > 0) parents[i] = tails[low - 1];
1861
+ tails[low] = i;
1862
+ if (low === size) size += 1;
1863
+ }
1864
+ let cursor = size > 0 ? tails[size - 1] : -1;
1865
+ let count = 0;
1866
+ while (cursor !== -1) {
1867
+ kept[cursor] = 1;
1868
+ count += 1;
1869
+ cursor = parents[cursor];
1870
+ }
1871
+ return count;
1872
+ }
1873
+ function between(low, high, base, gap) {
1874
+ if (!Number.isFinite(high)) return Number.isFinite(low) ? low + gap : base + gap;
1875
+ if (!Number.isFinite(low)) {
1876
+ const candidate2 = high - gap > base ? high - gap : Math.floor((base + high) / 2);
1877
+ return candidate2 > base && candidate2 < high ? candidate2 : null;
1878
+ }
1879
+ const candidate = Math.floor((low + high) / 2);
1880
+ return candidate > low && candidate < high ? candidate : null;
1881
+ }
1882
+ function renormalize(target, base, gap) {
1883
+ let writes = 0;
1884
+ for (let i = 0; i < target.length; i += 1) {
1885
+ const z = base + (i + 1) * gap;
1886
+ if (target.zAt(i) === z) continue;
1887
+ target.assign(i, z);
1888
+ writes += 1;
1889
+ }
1890
+ return writes;
1891
+ }
1892
+ function restack(target, options = {}) {
1893
+ const base = options.base ?? 0;
1894
+ const gap = options.gap ?? 32;
1895
+ const length = target.length;
1896
+ if (length === 0) return 0;
1897
+ const keepCount = markLongestIncreasing(target);
1898
+ if (length - keepCount > length / 3) return renormalize(target, base, gap);
1899
+ let writes = 0;
1900
+ for (let i = 0; i < length; i += 1) {
1901
+ if (kept[i] === 1) continue;
1902
+ const low = i > 0 ? target.zAt(i - 1) : UNASSIGNED;
1903
+ let high = Number.POSITIVE_INFINITY;
1904
+ for (let j = i + 1; j < length; j += 1) {
1905
+ if (kept[j] !== 1) continue;
1906
+ high = target.zAt(j);
1907
+ break;
1908
+ }
1909
+ const z = between(low, high, base, gap);
1910
+ if (z === null) return renormalize(target, base, gap);
1911
+ target.assign(i, z);
1912
+ writes += 1;
1913
+ }
1914
+ return writes;
1915
+ }
1916
+
1761
1917
  // src/dom/controller.ts
1762
1918
  var SNAP_SHORTCUTS = {
1763
1919
  ArrowLeft: "left",
@@ -1784,11 +1940,17 @@ function attachDesktop(wm, element, options = {}) {
1784
1940
  const hitEdge = options.hitAreas?.edge ?? (coarsePointer ? 16 : 8);
1785
1941
  const hitCorner = options.hitAreas?.corner ?? (coarsePointer ? 24 : 12);
1786
1942
  const magnetThreshold = options.magnetism === false ? 0 : (typeof options.magnetism === "object" ? options.magnetism.threshold : void 0) ?? (coarsePointer ? 12 : 8);
1943
+ const zIndexBase = options.stacking?.base ?? 0;
1944
+ const zIndexGap = options.stacking?.gap ?? 32;
1945
+ const interactiveSelector = options.interactiveSelector ?? INTERACTIVE_SELECTOR;
1946
+ const animationEnabled = options.animation !== false;
1947
+ const animationOptions = typeof options.animation === "object" ? options.animation : {};
1787
1948
  element.dataset.wmDesktop = "";
1788
1949
  element.tabIndex = -1;
1789
1950
  if (view.getComputedStyle(element).position === "static") {
1790
1951
  element.style.position = "relative";
1791
1952
  }
1953
+ if (options.stacking?.isolate !== false) element.style.isolation = "isolate";
1792
1954
  const registry = /* @__PURE__ */ new Map();
1793
1955
  const cleanup = [];
1794
1956
  let lastOrder = null;
@@ -1866,6 +2028,7 @@ function attachDesktop(wm, element, options = {}) {
1866
2028
  topEdge,
1867
2029
  hitEdge,
1868
2030
  hitCorner,
2031
+ interactiveSelector,
1869
2032
  magnetThreshold,
1870
2033
  groupDwell,
1871
2034
  groupTarget(clientX, clientY, selfId) {
@@ -1902,7 +2065,7 @@ function attachDesktop(wm, element, options = {}) {
1902
2065
  observer.observe(element);
1903
2066
  cleanup.push(() => observer.disconnect());
1904
2067
  }
1905
- function syncWindow(attached, win, zIndex, activeWorkspace, activeTab) {
2068
+ function syncWindow(attached, win, activeWorkspace, activeTab) {
1906
2069
  const el = attached.element;
1907
2070
  const firstSync = attached.lastState === null;
1908
2071
  if (firstSync) el.style.transition = "none";
@@ -1940,27 +2103,44 @@ function attachDesktop(wm, element, options = {}) {
1940
2103
  attached.lastWorkspace = activeWorkspace;
1941
2104
  attached.lastTab = activeTab;
1942
2105
  }
1943
- if (attached.lastZ !== zIndex) {
1944
- el.style.zIndex = String(zIndex + 1);
1945
- attached.lastZ = zIndex;
1946
- }
1947
2106
  if (firstSync) {
1948
2107
  void el.offsetWidth;
1949
2108
  el.style.transition = "";
1950
2109
  }
1951
2110
  }
2111
+ const stackRows = [];
2112
+ const stackTarget = {
2113
+ length: 0,
2114
+ zAt: (index) => stackRows[index].lastZ,
2115
+ assign(index, z) {
2116
+ const attached = stackRows[index];
2117
+ attached.lastZ = z;
2118
+ attached.element.style.zIndex = String(z);
2119
+ }
2120
+ };
2121
+ function applyStacking(order) {
2122
+ let length = 0;
2123
+ for (const id of order) {
2124
+ const attached = registry.get(id);
2125
+ if (attached) stackRows[length++] = attached;
2126
+ }
2127
+ stackTarget.length = length;
2128
+ restack(stackTarget, { base: zIndexBase, gap: zIndexGap });
2129
+ stackRows.length = length;
2130
+ }
1952
2131
  function syncAll() {
1953
2132
  const state = wm.getState();
1954
2133
  const orderChanged = state.order !== lastOrder;
1955
- state.order.forEach((id, index) => {
2134
+ for (const id of state.order) {
1956
2135
  const attached = registry.get(id);
1957
2136
  const win = state.windows[id];
1958
- if (!attached || !win) return;
2137
+ if (!attached || !win) continue;
1959
2138
  const activeTab = win.groupId === null || state.groups[win.groupId]?.activeId === win.id;
1960
- if (orderChanged || attached.lastState !== win || attached.lastWorkspace !== state.workspace || attached.lastTab !== activeTab) {
1961
- syncWindow(attached, win, index, state.workspace, activeTab);
2139
+ if (attached.lastState !== win || attached.lastWorkspace !== state.workspace || attached.lastTab !== activeTab) {
2140
+ syncWindow(attached, win, state.workspace, activeTab);
1962
2141
  }
1963
- });
2142
+ }
2143
+ if (orderChanged) applyStacking(state.order);
1964
2144
  if (state.focusedId !== lastFocused) {
1965
2145
  if (lastFocused) {
1966
2146
  const prev = registry.get(lastFocused);
@@ -1998,12 +2178,17 @@ function attachDesktop(wm, element, options = {}) {
1998
2178
  wm.on("stage", ({ window: win, previous }) => {
1999
2179
  const attached = registry.get(win.id);
2000
2180
  if (!attached) return;
2181
+ if (!animationEnabled) return;
2001
2182
  if (win.stage === "minimized" && previous !== "minimized") {
2002
2183
  const target = options.minimizeTarget?.(win);
2003
- if (target) flipToTarget(attached.element, target);
2184
+ if (target) flipToTarget(attached.element, target, animationOptions);
2004
2185
  } else if (previous === "minimized" && win.stage !== "minimized") {
2005
2186
  const target = options.minimizeTarget?.(win);
2006
- if (target) view.requestAnimationFrame(() => flipFromTarget(attached.element, target));
2187
+ if (target) {
2188
+ view.requestAnimationFrame(
2189
+ () => flipFromTarget(attached.element, target, animationOptions)
2190
+ );
2191
+ }
2007
2192
  }
2008
2193
  })
2009
2194
  );
@@ -2017,7 +2202,7 @@ function attachDesktop(wm, element, options = {}) {
2017
2202
  if (!event.ctrlKey && !event.metaKey) return;
2018
2203
  if (drag) return;
2019
2204
  const target = event.target;
2020
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
2205
+ if (target?.closest(interactiveSelector)) return;
2021
2206
  if (historyShortcuts && (event.key === "z" || event.key === "Z")) {
2022
2207
  event.preventDefault();
2023
2208
  if (event.shiftKey) wm.redo();
@@ -2059,7 +2244,7 @@ function attachDesktop(wm, element, options = {}) {
2059
2244
  handle: null,
2060
2245
  handles: [],
2061
2246
  lastState: null,
2062
- lastZ: -1,
2247
+ lastZ: UNASSIGNED,
2063
2248
  lastWorkspace: -1,
2064
2249
  lastTab: true,
2065
2250
  cleanup: []
@@ -2093,7 +2278,7 @@ function attachDesktop(wm, element, options = {}) {
2093
2278
  const current = wm.get(id);
2094
2279
  if (!current) return;
2095
2280
  if (target.closest("[data-wm-close]")) {
2096
- if (current.closable) wm.close(id);
2281
+ if (current.closable && options.beforeClose?.(current) !== false) wm.close(id);
2097
2282
  } else if (target.closest("[data-wm-minimize]")) {
2098
2283
  if (current.minimizable) wm.minimize(id);
2099
2284
  } else if (target.closest("[data-wm-maximize]")) {
@@ -2109,7 +2294,7 @@ function attachDesktop(wm, element, options = {}) {
2109
2294
  attached.cleanup.push(() => handle.removeEventListener("pointerdown", onHandleDown));
2110
2295
  const onDoubleClick = (event) => {
2111
2296
  const target = event.target;
2112
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
2297
+ if (target?.closest(interactiveSelector)) return;
2113
2298
  const current = wm.get(id);
2114
2299
  if (current?.maximizable) wm.toggleMaximize(id);
2115
2300
  };
@@ -2141,7 +2326,7 @@ function attachDesktop(wm, element, options = {}) {
2141
2326
  }
2142
2327
  const onWindowKeydown = (event) => {
2143
2328
  const target = event.target;
2144
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
2329
+ if (target?.closest(interactiveSelector)) return;
2145
2330
  const current = wm.get(id);
2146
2331
  if (!current) return;
2147
2332
  const arrows = {
@@ -2285,5 +2470,5 @@ function createDesktopBinder(wm, options = {}) {
2285
2470
  }
2286
2471
 
2287
2472
  export { applyAspect, attachDesktop, boundsEqual, clamp, clampSize, clampToViewport, createAnnouncer, createDesktopBinder, createEmitter, createWindowManager, defaultMessages, detectSnapZone, flipFromTarget, flipToTarget, magnetize, prefersReducedMotion, zoneBounds };
2288
- //# sourceMappingURL=chunk-2WQHHYYR.js.map
2289
- //# sourceMappingURL=chunk-2WQHHYYR.js.map
2473
+ //# sourceMappingURL=chunk-3HLKVOSI.js.map
2474
+ //# sourceMappingURL=chunk-3HLKVOSI.js.map