@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.
@@ -195,6 +195,12 @@ function createWindowManager(options = {}) {
195
195
  const idPrefix = options.idPrefix ?? "wm";
196
196
  const historyLimit = options.historyLimit ?? 50;
197
197
  let windows = {};
198
+ let windowsShared = false;
199
+ let modalCount = 0;
200
+ let cachedGroups = null;
201
+ let cachedOrder = null;
202
+ let cachedTabs = null;
203
+ let groupsDirty = false;
198
204
  let order = [];
199
205
  let focusedId = null;
200
206
  let viewport = options.viewport ?? { width: 0, height: 0 };
@@ -219,6 +225,7 @@ function createWindowManager(options = {}) {
219
225
  const layouts = /* @__PURE__ */ new Map();
220
226
  function getState() {
221
227
  if (!snapshot) {
228
+ windowsShared = true;
222
229
  snapshot = { windows, order, focusedId, viewport, workspace, groups: buildGroups() };
223
230
  }
224
231
  return snapshot;
@@ -240,14 +247,36 @@ function createWindowManager(options = {}) {
240
247
  function queueEvent(emit) {
241
248
  pendingEvents.push(emit);
242
249
  }
250
+ function ownWindows() {
251
+ if (windowsShared) {
252
+ windows = { ...windows };
253
+ windowsShared = false;
254
+ }
255
+ return windows;
256
+ }
243
257
  function setWindow(next) {
244
- windows = { ...windows, [next.id]: next };
258
+ const map = ownWindows();
259
+ const previous = map[next.id];
260
+ const wasModal = previous?.layer === "modal";
261
+ if (wasModal !== (next.layer === "modal")) modalCount += wasModal ? -1 : 1;
262
+ if (previous?.groupId !== next.groupId) groupsDirty = true;
263
+ map[next.id] = next;
245
264
  }
246
265
  function removeWindow(id) {
247
- const { [id]: _removed, ...rest } = windows;
248
- windows = rest;
266
+ const map = ownWindows();
267
+ const previous = map[id];
268
+ if (previous?.layer === "modal") modalCount -= 1;
269
+ if (previous?.groupId != null) groupsDirty = true;
270
+ delete map[id];
249
271
  order = order.filter((entry) => entry !== id);
250
272
  }
273
+ function countModals() {
274
+ let total = 0;
275
+ for (const id of order) {
276
+ if (windows[id].layer === "modal") total += 1;
277
+ }
278
+ return total;
279
+ }
251
280
  function layerRankOf(id) {
252
281
  return LAYER_RANK[windows[id].layer];
253
282
  }
@@ -284,31 +313,57 @@ function createWindowManager(options = {}) {
284
313
  return win.groupId === null || activeTabs[win.groupId] === win.id;
285
314
  }
286
315
  function buildGroups() {
316
+ if (cachedGroups && !groupsDirty && cachedOrder === order && cachedTabs === activeTabs) {
317
+ return cachedGroups;
318
+ }
287
319
  const result = /* @__PURE__ */ Object.create(null);
288
320
  for (const [groupId, activeId] of Object.entries(activeTabs)) {
289
321
  result[groupId] = { id: groupId, activeId, members: membersOf(groupId) };
290
322
  }
323
+ cachedGroups = result;
324
+ cachedOrder = order;
325
+ cachedTabs = activeTabs;
326
+ groupsDirty = false;
291
327
  return result;
292
328
  }
329
+ function sharedSize(ids, size) {
330
+ let shared = size;
331
+ for (const id of ids) {
332
+ const fit = normalizeSize(shared, windows[id]);
333
+ shared = {
334
+ width: Math.max(shared.width, fit.width),
335
+ height: Math.max(shared.height, fit.height)
336
+ };
337
+ }
338
+ return shared;
339
+ }
293
340
  function syncGroup(source) {
294
- if (source.groupId === null) return;
295
- for (const id of membersOf(source.groupId)) {
296
- if (id === source.id) continue;
341
+ if (source.groupId === null) return source;
342
+ const members = membersOf(source.groupId);
343
+ const fitted = sharedSize(members, source.bounds);
344
+ let effective = source;
345
+ if (fitted.width !== source.bounds.width || fitted.height !== source.bounds.height) {
346
+ effective = { ...source, bounds: { ...source.bounds, ...fitted } };
347
+ setWindow(effective);
348
+ }
349
+ for (const id of members) {
350
+ if (id === effective.id) continue;
297
351
  const member = windows[id];
298
- if (boundsEqual(member.bounds, source.bounds) && member.stage === source.stage && member.snapZone === source.snapZone && member.layer === source.layer && member.workspace === source.workspace) {
352
+ if (boundsEqual(member.bounds, effective.bounds) && member.stage === effective.stage && member.snapZone === effective.snapZone && member.layer === effective.layer && member.workspace === effective.workspace) {
299
353
  continue;
300
354
  }
301
355
  setWindow({
302
356
  ...member,
303
- bounds: source.bounds,
304
- stage: source.stage,
305
- snapZone: source.snapZone,
306
- restoreBounds: source.restoreBounds,
307
- restoreStage: source.restoreStage,
308
- layer: source.layer,
309
- workspace: source.workspace
357
+ bounds: effective.bounds,
358
+ stage: effective.stage,
359
+ snapZone: effective.snapZone,
360
+ restoreBounds: effective.restoreBounds,
361
+ restoreStage: effective.restoreStage,
362
+ layer: effective.layer,
363
+ workspace: effective.workspace
310
364
  });
311
365
  }
366
+ return effective;
312
367
  }
313
368
  function dissolveIfOrphaned(groupId) {
314
369
  const members = membersOf(groupId);
@@ -320,6 +375,7 @@ function createWindowManager(options = {}) {
320
375
  activeTabs = rest;
321
376
  }
322
377
  function topModalId() {
378
+ if (modalCount === 0) return null;
323
379
  for (let i = order.length - 1; i >= 0; i -= 1) {
324
380
  const win = windows[order[i]];
325
381
  if (win.layer === "modal" && win.stage !== "minimized" && onActiveWorkspace(win) && isVisibleTab(win)) {
@@ -338,8 +394,13 @@ function createWindowManager(options = {}) {
338
394
  });
339
395
  }
340
396
  function focusTop() {
341
- const targets = focusTargets();
342
- focusedId = targets.length > 0 ? targets[targets.length - 1] : null;
397
+ for (let i = order.length - 1; i >= 0; i -= 1) {
398
+ const win = windows[order[i]];
399
+ if (win.stage === "minimized" || !onActiveWorkspace(win) || !isVisibleTab(win)) continue;
400
+ focusedId = win.id;
401
+ return;
402
+ }
403
+ focusedId = null;
343
404
  }
344
405
  function reconcileFocus() {
345
406
  if (focusedId === null) return;
@@ -530,8 +591,8 @@ function createWindowManager(options = {}) {
530
591
  const next = build(win);
531
592
  if (!next) return false;
532
593
  setWindow(next);
533
- syncGroup(next);
534
- queueEvent(() => emitter.emit("stage", { window: next, previous: win.stage }));
594
+ const synced = syncGroup(next);
595
+ queueEvent(() => emitter.emit("stage", { window: synced, previous: win.stage }));
535
596
  if (next.groupId !== null) reconcileFocus();
536
597
  commit();
537
598
  return true;
@@ -663,9 +724,11 @@ function createWindowManager(options = {}) {
663
724
  if (boundsEqual(bounds, win.bounds) && !becameNormal) return true;
664
725
  const next = becameNormal ? { ...win, stage: "normal", snapZone: null, restoreBounds: null, bounds } : { ...win, bounds };
665
726
  setWindow(next);
666
- syncGroup(next);
667
- if (becameNormal) queueEvent(() => emitter.emit("stage", { window: next, previous: "snapped" }));
668
- queueEvent(() => emitter.emit("resize", { window: next }));
727
+ const synced = syncGroup(next);
728
+ if (becameNormal) {
729
+ queueEvent(() => emitter.emit("stage", { window: synced, previous: "snapped" }));
730
+ }
731
+ queueEvent(() => emitter.emit("resize", { window: synced }));
669
732
  commit();
670
733
  return true;
671
734
  }
@@ -691,18 +754,18 @@ function createWindowManager(options = {}) {
691
754
  const resized = size.width !== next.bounds.width || size.height !== next.bounds.height;
692
755
  const finalWin = resized ? { ...next, bounds: { ...next.bounds, ...size } } : next;
693
756
  setWindow(finalWin);
694
- syncGroup(finalWin);
757
+ const synced = syncGroup(finalWin);
695
758
  if (patch.layer && patch.layer !== win.layer) {
696
759
  order = sortByLayer(order);
697
760
  queueEvent(() => emitter.emit("order", { order }));
698
761
  if (patch.layer === "modal" && topModalId() === id && focusedId !== id) {
699
762
  const previous = focusedId;
700
763
  focusedId = id;
701
- emitFocus(finalWin, previous);
764
+ emitFocus(synced, previous);
702
765
  }
703
766
  }
704
- queueEvent(() => emitter.emit("update", { window: finalWin }));
705
- if (resized) queueEvent(() => emitter.emit("resize", { window: finalWin }));
767
+ queueEvent(() => emitter.emit("update", { window: synced }));
768
+ if (resized) queueEvent(() => emitter.emit("resize", { window: synced }));
706
769
  commit();
707
770
  return true;
708
771
  }
@@ -797,9 +860,10 @@ function createWindowManager(options = {}) {
797
860
  emitGroup(groupId, null);
798
861
  queueEvent(() => emitter.emit("order", { order }));
799
862
  const hostVisible = onActiveWorkspace(host) && host.stage !== "minimized";
800
- if (!hostVisible) reconcileFocus();
801
863
  commit();
802
864
  if (hostVisible) focus(host.id);
865
+ reconcileFocus();
866
+ commit();
803
867
  return groupId;
804
868
  }
805
869
  function ungroup(id) {
@@ -869,6 +933,7 @@ function createWindowManager(options = {}) {
869
933
  return Object.values(windows).filter((win) => win.stage === "minimized" && onActiveWorkspace(win) && isVisibleTab(win)).sort((a, b) => a.openedSeq - b.openedSeq);
870
934
  }
871
935
  function captureEntry() {
936
+ windowsShared = true;
872
937
  return { windows, order, focusedId, workspace, activeTabs };
873
938
  }
874
939
  function recordHistory() {
@@ -913,13 +978,13 @@ function createWindowManager(options = {}) {
913
978
  if (win.stage === "maximized") {
914
979
  const updated = { ...win, bounds: fullBounds() };
915
980
  setWindow(updated);
916
- syncGroup(updated);
917
- queueEvent(() => emitter.emit("resize", { window: updated }));
981
+ const synced = syncGroup(updated);
982
+ queueEvent(() => emitter.emit("resize", { window: synced }));
918
983
  } else if (win.stage === "snapped" && win.snapZone) {
919
984
  const updated = { ...win, bounds: snapBounds(win, win.snapZone) };
920
985
  setWindow(updated);
921
- syncGroup(updated);
922
- queueEvent(() => emitter.emit("resize", { window: updated }));
986
+ const synced = syncGroup(updated);
987
+ queueEvent(() => emitter.emit("resize", { window: synced }));
923
988
  } else if (win.stage === "normal" && keepInViewport) {
924
989
  const bounds = clampToViewport(win.bounds, viewport, minVisible);
925
990
  if (!boundsEqual(bounds, win.bounds)) {
@@ -934,6 +999,8 @@ function createWindowManager(options = {}) {
934
999
  function applyEntry(entry) {
935
1000
  windows = entry.windows;
936
1001
  order = [...entry.order];
1002
+ modalCount = countModals();
1003
+ groupsDirty = true;
937
1004
  focusedId = entry.focusedId;
938
1005
  activeTabs = entry.activeTabs;
939
1006
  const previousWorkspace = workspace;
@@ -1152,8 +1219,11 @@ function createWindowManager(options = {}) {
1152
1219
  if (!seen.has(id)) nextOrder.push(id);
1153
1220
  }
1154
1221
  windows = nextWindows;
1222
+ windowsShared = false;
1155
1223
  order = nextOrder;
1156
1224
  order = sortByLayer(order);
1225
+ modalCount = countModals();
1226
+ groupsDirty = true;
1157
1227
  seq = maxSeq;
1158
1228
  const previousWorkspace = workspace;
1159
1229
  workspace = normalizeWorkspace(data.workspace, 0);
@@ -1262,6 +1332,7 @@ function createWindowManager(options = {}) {
1262
1332
 
1263
1333
  // src/dom/animate.ts
1264
1334
  function prefersReducedMotion(win) {
1335
+ if (typeof win.matchMedia !== "function") return false;
1265
1336
  return win.matchMedia("(prefers-reduced-motion: reduce)").matches;
1266
1337
  }
1267
1338
  function flipFromTarget(source, target, options = {}) {
@@ -1390,14 +1461,6 @@ function createAnnouncer(wm, container, messages = {}) {
1390
1461
  };
1391
1462
  }
1392
1463
 
1393
- // src/dom/shared.ts
1394
- var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize]";
1395
- function windowOf(element) {
1396
- const view = element.ownerDocument.defaultView;
1397
- if (!view) throw new Error("wmkit: desktop element is not attached to a document");
1398
- return view;
1399
- }
1400
-
1401
1464
  // src/dom/drag.ts
1402
1465
  function createDragStarter(ctx) {
1403
1466
  const { wm, doc, view } = ctx;
@@ -1405,7 +1468,7 @@ function createDragStarter(ctx) {
1405
1468
  const win = wm.get(id);
1406
1469
  if (!win?.draggable || event.button !== 0 || ctx.currentDrag()) return;
1407
1470
  const target = event.target;
1408
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
1471
+ if (target?.closest(ctx.interactiveSelector)) return;
1409
1472
  event.preventDefault();
1410
1473
  const releaseRect = ctx.trackRect();
1411
1474
  const point = ctx.toLocal(event);
@@ -1760,6 +1823,99 @@ function createResizeHandles(doc, edge, corner) {
1760
1823
  });
1761
1824
  }
1762
1825
 
1826
+ // src/dom/shared.ts
1827
+ var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize]";
1828
+ function windowOf(element) {
1829
+ const view = element.ownerDocument.defaultView;
1830
+ if (!view) throw new Error("wmkit: desktop element is not attached to a document");
1831
+ return view;
1832
+ }
1833
+
1834
+ // src/dom/stacking.ts
1835
+ var UNASSIGNED = Number.NEGATIVE_INFINITY;
1836
+ var tails = new Int32Array(0);
1837
+ var parents = new Int32Array(0);
1838
+ var kept = new Uint8Array(0);
1839
+ function reserve(length) {
1840
+ if (tails.length >= length) return;
1841
+ const size = Math.max(length, 64);
1842
+ tails = new Int32Array(size);
1843
+ parents = new Int32Array(size);
1844
+ kept = new Uint8Array(size);
1845
+ }
1846
+ function markLongestIncreasing(target) {
1847
+ const length = target.length;
1848
+ reserve(length);
1849
+ kept.fill(0, 0, length);
1850
+ let size = 0;
1851
+ for (let i = 0; i < length; i += 1) {
1852
+ const value = target.zAt(i);
1853
+ parents[i] = -1;
1854
+ if (!Number.isFinite(value)) continue;
1855
+ let low = 0;
1856
+ let high = size;
1857
+ while (low < high) {
1858
+ const mid = low + high >> 1;
1859
+ if (target.zAt(tails[mid]) < value) low = mid + 1;
1860
+ else high = mid;
1861
+ }
1862
+ if (low > 0) parents[i] = tails[low - 1];
1863
+ tails[low] = i;
1864
+ if (low === size) size += 1;
1865
+ }
1866
+ let cursor = size > 0 ? tails[size - 1] : -1;
1867
+ let count = 0;
1868
+ while (cursor !== -1) {
1869
+ kept[cursor] = 1;
1870
+ count += 1;
1871
+ cursor = parents[cursor];
1872
+ }
1873
+ return count;
1874
+ }
1875
+ function between(low, high, base, gap) {
1876
+ if (!Number.isFinite(high)) return Number.isFinite(low) ? low + gap : base + gap;
1877
+ if (!Number.isFinite(low)) {
1878
+ const candidate2 = high - gap > base ? high - gap : Math.floor((base + high) / 2);
1879
+ return candidate2 > base && candidate2 < high ? candidate2 : null;
1880
+ }
1881
+ const candidate = Math.floor((low + high) / 2);
1882
+ return candidate > low && candidate < high ? candidate : null;
1883
+ }
1884
+ function renormalize(target, base, gap) {
1885
+ let writes = 0;
1886
+ for (let i = 0; i < target.length; i += 1) {
1887
+ const z = base + (i + 1) * gap;
1888
+ if (target.zAt(i) === z) continue;
1889
+ target.assign(i, z);
1890
+ writes += 1;
1891
+ }
1892
+ return writes;
1893
+ }
1894
+ function restack(target, options = {}) {
1895
+ const base = options.base ?? 0;
1896
+ const gap = options.gap ?? 32;
1897
+ const length = target.length;
1898
+ if (length === 0) return 0;
1899
+ const keepCount = markLongestIncreasing(target);
1900
+ if (length - keepCount > length / 3) return renormalize(target, base, gap);
1901
+ let writes = 0;
1902
+ for (let i = 0; i < length; i += 1) {
1903
+ if (kept[i] === 1) continue;
1904
+ const low = i > 0 ? target.zAt(i - 1) : UNASSIGNED;
1905
+ let high = Number.POSITIVE_INFINITY;
1906
+ for (let j = i + 1; j < length; j += 1) {
1907
+ if (kept[j] !== 1) continue;
1908
+ high = target.zAt(j);
1909
+ break;
1910
+ }
1911
+ const z = between(low, high, base, gap);
1912
+ if (z === null) return renormalize(target, base, gap);
1913
+ target.assign(i, z);
1914
+ writes += 1;
1915
+ }
1916
+ return writes;
1917
+ }
1918
+
1763
1919
  // src/dom/controller.ts
1764
1920
  var SNAP_SHORTCUTS = {
1765
1921
  ArrowLeft: "left",
@@ -1786,11 +1942,17 @@ function attachDesktop(wm, element, options = {}) {
1786
1942
  const hitEdge = options.hitAreas?.edge ?? (coarsePointer ? 16 : 8);
1787
1943
  const hitCorner = options.hitAreas?.corner ?? (coarsePointer ? 24 : 12);
1788
1944
  const magnetThreshold = options.magnetism === false ? 0 : (typeof options.magnetism === "object" ? options.magnetism.threshold : void 0) ?? (coarsePointer ? 12 : 8);
1945
+ const zIndexBase = options.stacking?.base ?? 0;
1946
+ const zIndexGap = options.stacking?.gap ?? 32;
1947
+ const interactiveSelector = options.interactiveSelector ?? INTERACTIVE_SELECTOR;
1948
+ const animationEnabled = options.animation !== false;
1949
+ const animationOptions = typeof options.animation === "object" ? options.animation : {};
1789
1950
  element.dataset.wmDesktop = "";
1790
1951
  element.tabIndex = -1;
1791
1952
  if (view.getComputedStyle(element).position === "static") {
1792
1953
  element.style.position = "relative";
1793
1954
  }
1955
+ if (options.stacking?.isolate !== false) element.style.isolation = "isolate";
1794
1956
  const registry = /* @__PURE__ */ new Map();
1795
1957
  const cleanup = [];
1796
1958
  let lastOrder = null;
@@ -1868,6 +2030,7 @@ function attachDesktop(wm, element, options = {}) {
1868
2030
  topEdge,
1869
2031
  hitEdge,
1870
2032
  hitCorner,
2033
+ interactiveSelector,
1871
2034
  magnetThreshold,
1872
2035
  groupDwell,
1873
2036
  groupTarget(clientX, clientY, selfId) {
@@ -1904,7 +2067,7 @@ function attachDesktop(wm, element, options = {}) {
1904
2067
  observer.observe(element);
1905
2068
  cleanup.push(() => observer.disconnect());
1906
2069
  }
1907
- function syncWindow(attached, win, zIndex, activeWorkspace, activeTab) {
2070
+ function syncWindow(attached, win, activeWorkspace, activeTab) {
1908
2071
  const el = attached.element;
1909
2072
  const firstSync = attached.lastState === null;
1910
2073
  if (firstSync) el.style.transition = "none";
@@ -1942,27 +2105,44 @@ function attachDesktop(wm, element, options = {}) {
1942
2105
  attached.lastWorkspace = activeWorkspace;
1943
2106
  attached.lastTab = activeTab;
1944
2107
  }
1945
- if (attached.lastZ !== zIndex) {
1946
- el.style.zIndex = String(zIndex + 1);
1947
- attached.lastZ = zIndex;
1948
- }
1949
2108
  if (firstSync) {
1950
2109
  void el.offsetWidth;
1951
2110
  el.style.transition = "";
1952
2111
  }
1953
2112
  }
2113
+ const stackRows = [];
2114
+ const stackTarget = {
2115
+ length: 0,
2116
+ zAt: (index) => stackRows[index].lastZ,
2117
+ assign(index, z) {
2118
+ const attached = stackRows[index];
2119
+ attached.lastZ = z;
2120
+ attached.element.style.zIndex = String(z);
2121
+ }
2122
+ };
2123
+ function applyStacking(order) {
2124
+ let length = 0;
2125
+ for (const id of order) {
2126
+ const attached = registry.get(id);
2127
+ if (attached) stackRows[length++] = attached;
2128
+ }
2129
+ stackTarget.length = length;
2130
+ restack(stackTarget, { base: zIndexBase, gap: zIndexGap });
2131
+ stackRows.length = length;
2132
+ }
1954
2133
  function syncAll() {
1955
2134
  const state = wm.getState();
1956
2135
  const orderChanged = state.order !== lastOrder;
1957
- state.order.forEach((id, index) => {
2136
+ for (const id of state.order) {
1958
2137
  const attached = registry.get(id);
1959
2138
  const win = state.windows[id];
1960
- if (!attached || !win) return;
2139
+ if (!attached || !win) continue;
1961
2140
  const activeTab = win.groupId === null || state.groups[win.groupId]?.activeId === win.id;
1962
- if (orderChanged || attached.lastState !== win || attached.lastWorkspace !== state.workspace || attached.lastTab !== activeTab) {
1963
- syncWindow(attached, win, index, state.workspace, activeTab);
2141
+ if (attached.lastState !== win || attached.lastWorkspace !== state.workspace || attached.lastTab !== activeTab) {
2142
+ syncWindow(attached, win, state.workspace, activeTab);
1964
2143
  }
1965
- });
2144
+ }
2145
+ if (orderChanged) applyStacking(state.order);
1966
2146
  if (state.focusedId !== lastFocused) {
1967
2147
  if (lastFocused) {
1968
2148
  const prev = registry.get(lastFocused);
@@ -2000,12 +2180,17 @@ function attachDesktop(wm, element, options = {}) {
2000
2180
  wm.on("stage", ({ window: win, previous }) => {
2001
2181
  const attached = registry.get(win.id);
2002
2182
  if (!attached) return;
2183
+ if (!animationEnabled) return;
2003
2184
  if (win.stage === "minimized" && previous !== "minimized") {
2004
2185
  const target = options.minimizeTarget?.(win);
2005
- if (target) flipToTarget(attached.element, target);
2186
+ if (target) flipToTarget(attached.element, target, animationOptions);
2006
2187
  } else if (previous === "minimized" && win.stage !== "minimized") {
2007
2188
  const target = options.minimizeTarget?.(win);
2008
- if (target) view.requestAnimationFrame(() => flipFromTarget(attached.element, target));
2189
+ if (target) {
2190
+ view.requestAnimationFrame(
2191
+ () => flipFromTarget(attached.element, target, animationOptions)
2192
+ );
2193
+ }
2009
2194
  }
2010
2195
  })
2011
2196
  );
@@ -2019,7 +2204,7 @@ function attachDesktop(wm, element, options = {}) {
2019
2204
  if (!event.ctrlKey && !event.metaKey) return;
2020
2205
  if (drag) return;
2021
2206
  const target = event.target;
2022
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
2207
+ if (target?.closest(interactiveSelector)) return;
2023
2208
  if (historyShortcuts && (event.key === "z" || event.key === "Z")) {
2024
2209
  event.preventDefault();
2025
2210
  if (event.shiftKey) wm.redo();
@@ -2061,7 +2246,7 @@ function attachDesktop(wm, element, options = {}) {
2061
2246
  handle: null,
2062
2247
  handles: [],
2063
2248
  lastState: null,
2064
- lastZ: -1,
2249
+ lastZ: UNASSIGNED,
2065
2250
  lastWorkspace: -1,
2066
2251
  lastTab: true,
2067
2252
  cleanup: []
@@ -2095,7 +2280,7 @@ function attachDesktop(wm, element, options = {}) {
2095
2280
  const current = wm.get(id);
2096
2281
  if (!current) return;
2097
2282
  if (target.closest("[data-wm-close]")) {
2098
- if (current.closable) wm.close(id);
2283
+ if (current.closable && options.beforeClose?.(current) !== false) wm.close(id);
2099
2284
  } else if (target.closest("[data-wm-minimize]")) {
2100
2285
  if (current.minimizable) wm.minimize(id);
2101
2286
  } else if (target.closest("[data-wm-maximize]")) {
@@ -2111,7 +2296,7 @@ function attachDesktop(wm, element, options = {}) {
2111
2296
  attached.cleanup.push(() => handle.removeEventListener("pointerdown", onHandleDown));
2112
2297
  const onDoubleClick = (event) => {
2113
2298
  const target = event.target;
2114
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
2299
+ if (target?.closest(interactiveSelector)) return;
2115
2300
  const current = wm.get(id);
2116
2301
  if (current?.maximizable) wm.toggleMaximize(id);
2117
2302
  };
@@ -2143,7 +2328,7 @@ function attachDesktop(wm, element, options = {}) {
2143
2328
  }
2144
2329
  const onWindowKeydown = (event) => {
2145
2330
  const target = event.target;
2146
- if (target?.closest(INTERACTIVE_SELECTOR)) return;
2331
+ if (target?.closest(interactiveSelector)) return;
2147
2332
  const current = wm.get(id);
2148
2333
  if (!current) return;
2149
2334
  const arrows = {
@@ -2303,5 +2488,5 @@ exports.flipToTarget = flipToTarget;
2303
2488
  exports.magnetize = magnetize;
2304
2489
  exports.prefersReducedMotion = prefersReducedMotion;
2305
2490
  exports.zoneBounds = zoneBounds;
2306
- //# sourceMappingURL=chunk-C2UHNBSA.cjs.map
2307
- //# sourceMappingURL=chunk-C2UHNBSA.cjs.map
2491
+ //# sourceMappingURL=chunk-DYZQY3BE.cjs.map
2492
+ //# sourceMappingURL=chunk-DYZQY3BE.cjs.map