@surdeddd/wmkit 0.1.1 → 0.2.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.
Files changed (54) hide show
  1. package/README.md +3 -7
  2. package/README.ru.md +3 -7
  3. package/dist/{controller-BfUF1k04.d.cts → binder-BY-saDUB.d.cts} +9 -3
  4. package/dist/{controller-1uedRvQw.d.ts → binder-D9UEE1SM.d.ts} +9 -3
  5. package/dist/{chunk-N5KNWRKU.js → chunk-2TVOEDRG.js} +298 -211
  6. package/dist/chunk-2TVOEDRG.js.map +1 -0
  7. package/dist/{chunk-TXINVEK3.cjs → chunk-KQCVZ2W7.cjs} +298 -211
  8. package/dist/chunk-KQCVZ2W7.cjs.map +1 -0
  9. package/dist/index.cjs +15 -15
  10. package/dist/index.d.cts +7 -4
  11. package/dist/index.d.ts +7 -4
  12. package/dist/index.js +1 -1
  13. package/dist/persist.cjs +1 -1
  14. package/dist/persist.cjs.map +1 -1
  15. package/dist/persist.d.cts +1 -1
  16. package/dist/persist.d.ts +1 -1
  17. package/dist/persist.js +1 -1
  18. package/dist/persist.js.map +1 -1
  19. package/dist/popout.cjs +1 -1
  20. package/dist/popout.cjs.map +1 -1
  21. package/dist/popout.d.cts +1 -1
  22. package/dist/popout.d.ts +1 -1
  23. package/dist/popout.js +1 -1
  24. package/dist/popout.js.map +1 -1
  25. package/dist/react.cjs +3 -3
  26. package/dist/react.cjs.map +1 -1
  27. package/dist/react.d.cts +2 -2
  28. package/dist/react.d.ts +2 -2
  29. package/dist/react.js +1 -1
  30. package/dist/react.js.map +1 -1
  31. package/dist/solid.cjs +3 -3
  32. package/dist/solid.cjs.map +1 -1
  33. package/dist/solid.d.cts +2 -2
  34. package/dist/solid.d.ts +2 -2
  35. package/dist/solid.js +1 -1
  36. package/dist/solid.js.map +1 -1
  37. package/dist/svelte.cjs +4 -4
  38. package/dist/svelte.cjs.map +1 -1
  39. package/dist/svelte.d.cts +2 -2
  40. package/dist/svelte.d.ts +2 -2
  41. package/dist/svelte.js +2 -2
  42. package/dist/svelte.js.map +1 -1
  43. package/dist/themes/glass.css +0 -1
  44. package/dist/{types-BYx7gCuT.d.cts → types-Bkrq2djr.d.cts} +1 -1
  45. package/dist/{types-BYx7gCuT.d.ts → types-Bkrq2djr.d.ts} +1 -1
  46. package/dist/vue.cjs +3 -3
  47. package/dist/vue.cjs.map +1 -1
  48. package/dist/vue.d.cts +2 -2
  49. package/dist/vue.d.ts +2 -2
  50. package/dist/vue.js +1 -1
  51. package/dist/vue.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/chunk-N5KNWRKU.js.map +0 -1
  54. package/dist/chunk-TXINVEK3.cjs.map +0 -1
@@ -776,175 +776,25 @@ function createAnnouncer(wm, container, messages = {}) {
776
776
  };
777
777
  }
778
778
 
779
- // src/dom/controller.ts
780
- var RESIZE_DIRECTIONS = ["n", "e", "s", "w", "ne", "nw", "se", "sw"];
781
- var RESIZE_CURSORS = {
782
- n: "ns-resize",
783
- s: "ns-resize",
784
- e: "ew-resize",
785
- w: "ew-resize",
786
- ne: "nesw-resize",
787
- sw: "nesw-resize",
788
- nw: "nwse-resize",
789
- se: "nwse-resize"
790
- };
779
+ // src/dom/shared.ts
791
780
  var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize]";
792
781
  function windowOf(element) {
793
782
  const view = element.ownerDocument.defaultView;
794
783
  if (!view) throw new Error("wmkit: desktop element is not attached to a document");
795
784
  return view;
796
785
  }
797
- function attachDesktop(wm, element, options = {}) {
798
- const doc = element.ownerDocument;
799
- const view = windowOf(element);
800
- const snapEnabled = options.snap !== false;
801
- const snapOptions = typeof options.snap === "object" ? options.snap : {};
802
- const snapPreviewEnabled = snapOptions.preview !== false;
803
- const topEdge = snapOptions.topEdge ?? "maximize";
804
- const keyboardEnabled = options.keyboard !== false;
805
- const keyboardOptions = typeof options.keyboard === "object" ? options.keyboard : {};
806
- const moveStep = keyboardOptions.moveStep ?? 16;
807
- const cycleEnabled = keyboardOptions.cycle !== false;
808
- element.dataset.wmDesktop = "";
809
- if (view.getComputedStyle(element).position === "static") {
810
- element.style.position = "relative";
811
- }
812
- const registry = /* @__PURE__ */ new Map();
813
- const cleanup = [];
814
- let lastOrder = null;
815
- let lastFocused = null;
816
- let drag = null;
817
- let announcer = null;
818
- if (options.announce !== false) {
819
- announcer = createAnnouncer(
820
- wm,
821
- element,
822
- typeof options.announce === "object" ? options.announce : {}
823
- );
824
- cleanup.push(() => announcer?.destroy());
825
- }
826
- let preview = null;
827
- function showPreview(bounds) {
828
- if (!snapPreviewEnabled) return;
829
- if (!preview) {
830
- preview = doc.createElement("div");
831
- preview.dataset.wmSnapPreview = "";
832
- preview.style.cssText = "position:absolute;left:0;top:0;pointer-events:none;display:none;z-index:2147483646";
833
- element.append(preview);
834
- }
835
- preview.style.display = "block";
836
- preview.style.transform = `translate3d(${bounds.x}px, ${bounds.y}px, 0)`;
837
- preview.style.width = `${bounds.width}px`;
838
- preview.style.height = `${bounds.height}px`;
839
- }
840
- function hidePreview() {
841
- if (preview) preview.style.display = "none";
842
- }
843
- if (options.autoViewport !== false) {
844
- const applyViewport = () => wm.setViewport({ width: element.clientWidth, height: element.clientHeight });
845
- applyViewport();
846
- const observer = new ResizeObserver(applyViewport);
847
- observer.observe(element);
848
- cleanup.push(() => observer.disconnect());
849
- }
850
- function syncWindow(attached, win, zIndex) {
851
- const el = attached.element;
852
- const firstSync = attached.lastState === null;
853
- if (firstSync) el.style.transition = "none";
854
- if (attached.lastState !== win) {
855
- el.style.transform = `translate3d(${win.bounds.x}px, ${win.bounds.y}px, 0)`;
856
- el.style.width = `${win.bounds.width}px`;
857
- el.style.height = `${win.bounds.height}px`;
858
- el.dataset.wmStage = win.stage;
859
- el.dataset.wmLayer = win.layer;
860
- el.hidden = win.stage === "minimized";
861
- el.setAttribute("aria-label", win.title);
862
- if (win.layer === "modal") el.setAttribute("aria-modal", "true");
863
- else el.removeAttribute("aria-modal");
864
- for (const handle of attached.handles) {
865
- handle.style.display = win.resizable && win.stage === "normal" ? "" : "none";
866
- }
867
- attached.lastState = win;
868
- }
869
- el.style.zIndex = String(zIndex + 1);
870
- if (firstSync) {
871
- void el.offsetWidth;
872
- el.style.transition = "";
873
- }
874
- }
875
- function syncAll() {
876
- const state = wm.getState();
877
- const orderChanged = state.order !== lastOrder;
878
- state.order.forEach((id, index) => {
879
- const attached = registry.get(id);
880
- const win = state.windows[id];
881
- if (!attached || !win) return;
882
- if (orderChanged || attached.lastState !== win) syncWindow(attached, win, index);
883
- });
884
- if (state.focusedId !== lastFocused) {
885
- if (lastFocused) {
886
- const prev = registry.get(lastFocused);
887
- if (prev) delete prev.element.dataset.wmFocused;
888
- }
889
- if (state.focusedId) {
890
- const next = registry.get(state.focusedId);
891
- if (next) next.element.dataset.wmFocused = "";
892
- }
893
- lastFocused = state.focusedId;
894
- }
895
- lastOrder = state.order;
896
- }
897
- cleanup.push(wm.subscribe(syncAll));
898
- cleanup.push(
899
- wm.on("focus", ({ window: win }) => {
900
- const attached = registry.get(win.id);
901
- if (!attached) return;
902
- if (!attached.element.contains(doc.activeElement)) {
903
- attached.element.focus({ preventScroll: true });
904
- }
905
- })
906
- );
907
- cleanup.push(
908
- wm.on("modalblocked", ({ window: win }) => {
909
- const attached = registry.get(win.id);
910
- if (!attached) return;
911
- delete attached.element.dataset.wmFlash;
912
- void attached.element.offsetWidth;
913
- attached.element.dataset.wmFlash = "";
914
- })
915
- );
916
- cleanup.push(
917
- wm.on("stage", ({ window: win, previous }) => {
918
- if (win.stage !== "minimized" || previous === "minimized") return;
919
- const attached = registry.get(win.id);
920
- const target = options.minimizeTarget?.(win);
921
- if (attached && target) flipToTarget(attached.element, target);
922
- })
923
- );
924
- if (keyboardEnabled && cycleEnabled) {
925
- const onDesktopKeydown = (event) => {
926
- if (event.key === "F6") {
927
- event.preventDefault();
928
- wm.cycleFocus(event.shiftKey ? -1 : 1);
929
- }
930
- };
931
- element.addEventListener("keydown", onDesktopKeydown);
932
- cleanup.push(() => element.removeEventListener("keydown", onDesktopKeydown));
933
- }
934
- function desktopPoint(event) {
935
- const rect = element.getBoundingClientRect();
936
- return { x: event.clientX - rect.left, y: event.clientY - rect.top };
937
- }
938
- function endDrag(cancelled) {
939
- if (drag) drag.finish(cancelled);
940
- }
941
- function startDrag(id, handle, event) {
786
+
787
+ // src/dom/drag.ts
788
+ function createDragStarter(ctx) {
789
+ const { wm, doc, view } = ctx;
790
+ return function startDrag(id, handle, event) {
942
791
  const win = wm.get(id);
943
- if (!win?.draggable || event.button !== 0 || drag) return;
792
+ if (!win?.draggable || event.button !== 0 || ctx.currentDrag()) return;
944
793
  const target = event.target;
945
794
  if (target?.closest(INTERACTIVE_SELECTOR)) return;
946
795
  event.preventDefault();
947
- const point = desktopPoint(event);
796
+ const releaseRect = ctx.trackRect();
797
+ const point = ctx.toLocal(event);
948
798
  const startBounds = win.bounds;
949
799
  const session = {
950
800
  id,
@@ -966,10 +816,9 @@ function attachDesktop(wm, element, options = {}) {
966
816
  finish: () => {
967
817
  }
968
818
  };
969
- const attached = registry.get(id);
970
- const el = attached?.element;
819
+ const el = ctx.windowElement(id);
971
820
  function flush() {
972
- if (!session.hasPending || drag !== session) return;
821
+ if (!session.hasPending || ctx.currentDrag() !== session) return;
973
822
  session.hasPending = false;
974
823
  session.raf = 0;
975
824
  const current = wm.get(id);
@@ -993,32 +842,35 @@ function attachDesktop(wm, element, options = {}) {
993
842
  return;
994
843
  }
995
844
  wm.move(id, session.pendingX - session.grabDX, session.pendingY - session.grabDY);
996
- if (snapEnabled && current.snappable) {
845
+ if (ctx.snapEnabled && current.snappable) {
997
846
  const viewport = wm.getState().viewport;
998
- const rawZone = detectSnapZone(session.pendingX, session.pendingY, viewport, snapOptions);
847
+ const rawZone = detectSnapZone(session.pendingX, session.pendingY, viewport, ctx.snapDetect);
999
848
  let zone = rawZone;
1000
849
  if (rawZone === "top") {
1001
- if (topEdge === "maximize") zone = current.maximizable ? "maximize" : null;
1002
- else if (topEdge === "none") zone = null;
850
+ if (ctx.topEdge === "maximize") zone = current.maximizable ? "maximize" : null;
851
+ else if (ctx.topEdge === "none") zone = null;
1003
852
  }
1004
853
  session.zone = zone;
1005
854
  if (zone) {
1006
- showPreview(
855
+ ctx.showPreview(
1007
856
  zone === "maximize" ? { x: 0, y: 0, width: viewport.width, height: viewport.height } : zoneBounds(zone, viewport)
1008
857
  );
1009
858
  } else {
1010
- hidePreview();
859
+ ctx.hidePreview();
1011
860
  }
1012
861
  }
1013
862
  }
1014
863
  function onMove(moveEvent) {
1015
864
  if (moveEvent.pointerId !== session.pointerId) return;
1016
- const movePoint = desktopPoint(moveEvent);
865
+ const movePoint = ctx.toLocal(moveEvent);
1017
866
  if (!session.moved) {
1018
867
  const travelled = Math.abs(movePoint.x - (session.startBounds.x + session.grabDX)) + Math.abs(movePoint.y - (session.startBounds.y + session.grabDY));
1019
868
  if (travelled < 3 && session.restored) return;
1020
869
  session.moved = true;
1021
- if (el) el.dataset.wmDragging = "";
870
+ if (el) {
871
+ el.dataset.wmDragging = "";
872
+ el.style.willChange = "transform";
873
+ }
1022
874
  }
1023
875
  session.pendingX = movePoint.x;
1024
876
  session.pendingY = movePoint.y;
@@ -1040,10 +892,11 @@ function attachDesktop(wm, element, options = {}) {
1040
892
  }
1041
893
  }
1042
894
  function finish(cancelled) {
1043
- if (drag !== session) return;
1044
- if (session.raf !== 0) view?.cancelAnimationFrame(session.raf);
895
+ if (ctx.currentDrag() !== session) return;
896
+ if (session.raf !== 0) view.cancelAnimationFrame(session.raf);
1045
897
  if (session.hasPending && !cancelled) flush();
1046
- drag = null;
898
+ ctx.releaseDrag(session);
899
+ releaseRect();
1047
900
  handle.removeEventListener("pointermove", onMove);
1048
901
  handle.removeEventListener("pointerup", onUp);
1049
902
  handle.removeEventListener("pointercancel", onCancel);
@@ -1051,8 +904,11 @@ function attachDesktop(wm, element, options = {}) {
1051
904
  if (handle.hasPointerCapture(session.pointerId)) {
1052
905
  handle.releasePointerCapture(session.pointerId);
1053
906
  }
1054
- if (el) delete el.dataset.wmDragging;
1055
- hidePreview();
907
+ if (el) {
908
+ delete el.dataset.wmDragging;
909
+ el.style.willChange = "";
910
+ }
911
+ ctx.hidePreview();
1056
912
  if (cancelled) {
1057
913
  if (session.moved && session.restored) {
1058
914
  if (session.startStage === "maximized") {
@@ -1073,20 +929,37 @@ function attachDesktop(wm, element, options = {}) {
1073
929
  }
1074
930
  }
1075
931
  session.finish = finish;
1076
- drag = session;
932
+ ctx.claimDrag(session);
1077
933
  handle.setPointerCapture(event.pointerId);
1078
934
  handle.addEventListener("pointermove", onMove);
1079
935
  handle.addEventListener("pointerup", onUp);
1080
936
  handle.addEventListener("pointercancel", onCancel);
1081
937
  doc.addEventListener("keydown", onKeydown, true);
1082
- }
1083
- function startResize(id, direction, event) {
938
+ };
939
+ }
940
+
941
+ // src/dom/resize.ts
942
+ var RESIZE_DIRECTIONS = ["n", "e", "s", "w", "ne", "nw", "se", "sw"];
943
+ var RESIZE_CURSORS = {
944
+ n: "ns-resize",
945
+ s: "ns-resize",
946
+ e: "ew-resize",
947
+ w: "ew-resize",
948
+ ne: "nesw-resize",
949
+ sw: "nesw-resize",
950
+ nw: "nwse-resize",
951
+ se: "nwse-resize"
952
+ };
953
+ function createResizeStarter(ctx) {
954
+ const { wm, doc, view } = ctx;
955
+ return function startResize(id, direction, event) {
1084
956
  const win = wm.get(id);
1085
- if (!win?.resizable || event.button !== 0 || drag) return;
957
+ if (!win?.resizable || event.button !== 0 || ctx.currentDrag()) return;
1086
958
  event.preventDefault();
1087
959
  event.stopPropagation();
1088
960
  const handleEl = event.currentTarget;
1089
- const startPoint = desktopPoint(event);
961
+ const releaseRect = ctx.trackRect();
962
+ const startPoint = ctx.toLocal(event);
1090
963
  const start = win.bounds;
1091
964
  const minSize = win.minSize;
1092
965
  const maxSize = win.maxSize;
@@ -1094,8 +967,8 @@ function attachDesktop(wm, element, options = {}) {
1094
967
  let pendingX = 0;
1095
968
  let pendingY = 0;
1096
969
  let hasPending = false;
1097
- const attached = registry.get(id);
1098
- if (attached) attached.element.dataset.wmResizing = direction;
970
+ const el = ctx.windowElement(id);
971
+ if (el) el.dataset.wmResizing = direction;
1099
972
  function clampWidth(width) {
1100
973
  return clamp(width, minSize.width, maxSize ? maxSize.width : Number.POSITIVE_INFINITY);
1101
974
  }
@@ -1123,15 +996,16 @@ function attachDesktop(wm, element, options = {}) {
1123
996
  }
1124
997
  function onMove(moveEvent) {
1125
998
  if (moveEvent.pointerId !== event.pointerId) return;
1126
- const movePoint = desktopPoint(moveEvent);
999
+ const movePoint = ctx.toLocal(moveEvent);
1127
1000
  pendingX = movePoint.x;
1128
1001
  pendingY = movePoint.y;
1129
1002
  hasPending = true;
1130
- if (raf === 0) raf = view?.requestAnimationFrame(flush) ?? 0;
1003
+ if (raf === 0) raf = view.requestAnimationFrame(flush);
1131
1004
  }
1132
1005
  function finish(cancelled) {
1133
- if (raf !== 0) view?.cancelAnimationFrame(raf);
1006
+ if (raf !== 0) view.cancelAnimationFrame(raf);
1134
1007
  if (hasPending && !cancelled) flush();
1008
+ releaseRect();
1135
1009
  handleEl.removeEventListener("pointermove", onMove);
1136
1010
  handleEl.removeEventListener("pointerup", onUp);
1137
1011
  handleEl.removeEventListener("pointercancel", onCancelPointer);
@@ -1139,7 +1013,7 @@ function attachDesktop(wm, element, options = {}) {
1139
1013
  if (handleEl.hasPointerCapture(event.pointerId)) {
1140
1014
  handleEl.releasePointerCapture(event.pointerId);
1141
1015
  }
1142
- if (attached) delete attached.element.dataset.wmResizing;
1016
+ if (el) delete el.dataset.wmResizing;
1143
1017
  if (cancelled) wm.resize(id, start);
1144
1018
  }
1145
1019
  function onUp(upEvent) {
@@ -1161,6 +1035,220 @@ function attachDesktop(wm, element, options = {}) {
1161
1035
  handleEl.addEventListener("pointerup", onUp);
1162
1036
  handleEl.addEventListener("pointercancel", onCancelPointer);
1163
1037
  doc.addEventListener("keydown", onKeydown, true);
1038
+ };
1039
+ }
1040
+ function createResizeHandles(doc, edge, corner) {
1041
+ const base = "position:absolute;touch-action:none;user-select:none;-webkit-user-select:none;";
1042
+ const styles = {
1043
+ n: `top:${-edge / 2}px;left:${corner}px;right:${corner}px;height:${edge}px`,
1044
+ s: `bottom:${-edge / 2}px;left:${corner}px;right:${corner}px;height:${edge}px`,
1045
+ e: `right:${-edge / 2}px;top:${corner}px;bottom:${corner}px;width:${edge}px`,
1046
+ w: `left:${-edge / 2}px;top:${corner}px;bottom:${corner}px;width:${edge}px`,
1047
+ ne: `top:${-edge / 2}px;right:${-edge / 2}px;width:${corner}px;height:${corner}px`,
1048
+ nw: `top:${-edge / 2}px;left:${-edge / 2}px;width:${corner}px;height:${corner}px`,
1049
+ se: `bottom:${-edge / 2}px;right:${-edge / 2}px;width:${corner}px;height:${corner}px`,
1050
+ sw: `bottom:${-edge / 2}px;left:${-edge / 2}px;width:${corner}px;height:${corner}px`
1051
+ };
1052
+ return RESIZE_DIRECTIONS.map((direction) => {
1053
+ const element = doc.createElement("div");
1054
+ element.dataset.wmResize = direction;
1055
+ element.setAttribute("aria-hidden", "true");
1056
+ element.style.cssText = `${base}${styles[direction]};cursor:${RESIZE_CURSORS[direction]}`;
1057
+ return { element, direction };
1058
+ });
1059
+ }
1060
+
1061
+ // src/dom/controller.ts
1062
+ function attachDesktop(wm, element, options = {}) {
1063
+ const doc = element.ownerDocument;
1064
+ const view = windowOf(element);
1065
+ const coarsePointer = typeof view.matchMedia === "function" && view.matchMedia("(pointer: coarse)").matches;
1066
+ const snapEnabled = options.snap !== false;
1067
+ const snapOptions = typeof options.snap === "object" ? options.snap : {};
1068
+ const snapPreviewEnabled = snapOptions.preview !== false;
1069
+ const topEdge = snapOptions.topEdge ?? "maximize";
1070
+ const keyboardEnabled = options.keyboard !== false;
1071
+ const keyboardOptions = typeof options.keyboard === "object" ? options.keyboard : {};
1072
+ const moveStep = keyboardOptions.moveStep ?? 16;
1073
+ const cycleEnabled = keyboardOptions.cycle !== false;
1074
+ const hitEdge = options.hitAreas?.edge ?? (coarsePointer ? 16 : 8);
1075
+ const hitCorner = options.hitAreas?.corner ?? (coarsePointer ? 24 : 12);
1076
+ element.dataset.wmDesktop = "";
1077
+ if (view.getComputedStyle(element).position === "static") {
1078
+ element.style.position = "relative";
1079
+ }
1080
+ const registry = /* @__PURE__ */ new Map();
1081
+ const cleanup = [];
1082
+ let lastOrder = null;
1083
+ let lastFocused = null;
1084
+ let drag = null;
1085
+ let cachedRect = null;
1086
+ let rectUsers = 0;
1087
+ let announcer = null;
1088
+ if (options.announce !== false) {
1089
+ announcer = createAnnouncer(
1090
+ wm,
1091
+ element,
1092
+ typeof options.announce === "object" ? options.announce : {}
1093
+ );
1094
+ cleanup.push(() => announcer?.destroy());
1095
+ }
1096
+ let preview = null;
1097
+ function showPreview(bounds) {
1098
+ if (!snapPreviewEnabled) return;
1099
+ if (!preview) {
1100
+ preview = doc.createElement("div");
1101
+ preview.dataset.wmSnapPreview = "";
1102
+ preview.style.cssText = "position:absolute;left:0;top:0;pointer-events:none;display:none;z-index:2147483646";
1103
+ element.append(preview);
1104
+ }
1105
+ preview.style.display = "block";
1106
+ preview.style.transform = `translate3d(${bounds.x}px, ${bounds.y}px, 0)`;
1107
+ preview.style.width = `${bounds.width}px`;
1108
+ preview.style.height = `${bounds.height}px`;
1109
+ }
1110
+ function hidePreview() {
1111
+ if (preview) preview.style.display = "none";
1112
+ }
1113
+ const ctx = {
1114
+ wm,
1115
+ doc,
1116
+ view,
1117
+ toLocal(event) {
1118
+ const rect = cachedRect ?? element.getBoundingClientRect();
1119
+ return { x: event.clientX - rect.left, y: event.clientY - rect.top };
1120
+ },
1121
+ trackRect() {
1122
+ if (rectUsers === 0) {
1123
+ const rect = element.getBoundingClientRect();
1124
+ cachedRect = { left: rect.left, top: rect.top };
1125
+ }
1126
+ rectUsers += 1;
1127
+ let released = false;
1128
+ return () => {
1129
+ if (released) return;
1130
+ released = true;
1131
+ rectUsers -= 1;
1132
+ if (rectUsers === 0) cachedRect = null;
1133
+ };
1134
+ },
1135
+ windowElement: (id) => registry.get(id)?.element,
1136
+ showPreview,
1137
+ hidePreview,
1138
+ snapEnabled,
1139
+ snapDetect: {
1140
+ threshold: snapOptions.threshold ?? (coarsePointer ? 20 : 12),
1141
+ cornerSize: snapOptions.cornerSize ?? (coarsePointer ? 96 : 64)
1142
+ },
1143
+ topEdge,
1144
+ hitEdge,
1145
+ hitCorner,
1146
+ currentDrag: () => drag,
1147
+ claimDrag(session) {
1148
+ drag = session;
1149
+ },
1150
+ releaseDrag(session) {
1151
+ if (drag === session) drag = null;
1152
+ }
1153
+ };
1154
+ const startDrag = createDragStarter(ctx);
1155
+ const startResize = createResizeStarter(ctx);
1156
+ if (options.autoViewport !== false) {
1157
+ const applyViewport = () => wm.setViewport({ width: element.clientWidth, height: element.clientHeight });
1158
+ applyViewport();
1159
+ const observer = new ResizeObserver(applyViewport);
1160
+ observer.observe(element);
1161
+ cleanup.push(() => observer.disconnect());
1162
+ }
1163
+ function syncWindow(attached, win, zIndex) {
1164
+ const el = attached.element;
1165
+ const firstSync = attached.lastState === null;
1166
+ if (firstSync) el.style.transition = "none";
1167
+ if (attached.lastState !== win) {
1168
+ el.style.transform = `translate3d(${win.bounds.x}px, ${win.bounds.y}px, 0)`;
1169
+ el.style.width = `${win.bounds.width}px`;
1170
+ el.style.height = `${win.bounds.height}px`;
1171
+ el.dataset.wmStage = win.stage;
1172
+ el.dataset.wmLayer = win.layer;
1173
+ el.hidden = win.stage === "minimized";
1174
+ el.setAttribute("aria-label", win.title);
1175
+ if (win.layer === "modal") el.setAttribute("aria-modal", "true");
1176
+ else el.removeAttribute("aria-modal");
1177
+ for (const handle of attached.handles) {
1178
+ handle.style.display = win.resizable && win.stage === "normal" ? "" : "none";
1179
+ }
1180
+ attached.lastState = win;
1181
+ }
1182
+ if (attached.lastZ !== zIndex) {
1183
+ el.style.zIndex = String(zIndex + 1);
1184
+ attached.lastZ = zIndex;
1185
+ }
1186
+ if (firstSync) {
1187
+ void el.offsetWidth;
1188
+ el.style.transition = "";
1189
+ }
1190
+ }
1191
+ function syncAll() {
1192
+ const state = wm.getState();
1193
+ const orderChanged = state.order !== lastOrder;
1194
+ state.order.forEach((id, index) => {
1195
+ const attached = registry.get(id);
1196
+ const win = state.windows[id];
1197
+ if (!attached || !win) return;
1198
+ if (orderChanged || attached.lastState !== win) syncWindow(attached, win, index);
1199
+ });
1200
+ if (state.focusedId !== lastFocused) {
1201
+ if (lastFocused) {
1202
+ const prev = registry.get(lastFocused);
1203
+ if (prev) delete prev.element.dataset.wmFocused;
1204
+ }
1205
+ if (state.focusedId) {
1206
+ const next = registry.get(state.focusedId);
1207
+ if (next) next.element.dataset.wmFocused = "";
1208
+ }
1209
+ lastFocused = state.focusedId;
1210
+ }
1211
+ lastOrder = state.order;
1212
+ }
1213
+ cleanup.push(wm.subscribe(syncAll));
1214
+ cleanup.push(
1215
+ wm.on("focus", ({ window: win }) => {
1216
+ const attached = registry.get(win.id);
1217
+ if (!attached) return;
1218
+ if (!attached.element.contains(doc.activeElement)) {
1219
+ attached.element.focus({ preventScroll: true });
1220
+ }
1221
+ })
1222
+ );
1223
+ cleanup.push(
1224
+ wm.on("modalblocked", ({ window: win }) => {
1225
+ const attached = registry.get(win.id);
1226
+ if (!attached) return;
1227
+ delete attached.element.dataset.wmFlash;
1228
+ void attached.element.offsetWidth;
1229
+ attached.element.dataset.wmFlash = "";
1230
+ })
1231
+ );
1232
+ cleanup.push(
1233
+ wm.on("stage", ({ window: win, previous }) => {
1234
+ if (win.stage !== "minimized" || previous === "minimized") return;
1235
+ const attached = registry.get(win.id);
1236
+ const target = options.minimizeTarget?.(win);
1237
+ if (attached && target) flipToTarget(attached.element, target);
1238
+ })
1239
+ );
1240
+ if (keyboardEnabled && cycleEnabled) {
1241
+ const onDesktopKeydown = (event) => {
1242
+ if (event.key === "F6") {
1243
+ event.preventDefault();
1244
+ wm.cycleFocus(event.shiftKey ? -1 : 1);
1245
+ }
1246
+ };
1247
+ element.addEventListener("keydown", onDesktopKeydown);
1248
+ cleanup.push(() => element.removeEventListener("keydown", onDesktopKeydown));
1249
+ }
1250
+ function endDrag(cancelled) {
1251
+ if (drag) drag.finish(cancelled);
1164
1252
  }
1165
1253
  function attachWindow(id, windowElement, windowOptions = {}) {
1166
1254
  const win = wm.get(id);
@@ -1171,6 +1259,7 @@ function attachDesktop(wm, element, options = {}) {
1171
1259
  handle: null,
1172
1260
  handles: [],
1173
1261
  lastState: null,
1262
+ lastZ: -1,
1174
1263
  cleanup: []
1175
1264
  };
1176
1265
  windowElement.dataset.wmWindow = id;
@@ -1223,24 +1312,11 @@ function attachDesktop(wm, element, options = {}) {
1223
1312
  attached.cleanup.push(() => handle.removeEventListener("dblclick", onDoubleClick));
1224
1313
  }
1225
1314
  if (windowOptions.resizeHandles !== false) {
1226
- for (const direction of RESIZE_DIRECTIONS) {
1227
- const resizeHandle = doc.createElement("div");
1228
- resizeHandle.dataset.wmResize = direction;
1229
- resizeHandle.setAttribute("aria-hidden", "true");
1230
- const base = "position:absolute;touch-action:none;user-select:none;-webkit-user-select:none;";
1231
- const size = 8;
1232
- const corner = 12;
1233
- const styles = {
1234
- n: `top:${-size / 2}px;left:${corner}px;right:${corner}px;height:${size}px`,
1235
- s: `bottom:${-size / 2}px;left:${corner}px;right:${corner}px;height:${size}px`,
1236
- e: `right:${-size / 2}px;top:${corner}px;bottom:${corner}px;width:${size}px`,
1237
- w: `left:${-size / 2}px;top:${corner}px;bottom:${corner}px;width:${size}px`,
1238
- ne: `top:${-size / 2}px;right:${-size / 2}px;width:${corner}px;height:${corner}px`,
1239
- nw: `top:${-size / 2}px;left:${-size / 2}px;width:${corner}px;height:${corner}px`,
1240
- se: `bottom:${-size / 2}px;right:${-size / 2}px;width:${corner}px;height:${corner}px`,
1241
- sw: `bottom:${-size / 2}px;left:${-size / 2}px;width:${corner}px;height:${corner}px`
1242
- };
1243
- resizeHandle.style.cssText = `${base}${styles[direction]};cursor:${RESIZE_CURSORS[direction]}`;
1315
+ for (const { element: resizeHandle, direction } of createResizeHandles(
1316
+ doc,
1317
+ hitEdge,
1318
+ hitCorner
1319
+ )) {
1244
1320
  const onResizeDown = (event) => startResize(id, direction, event);
1245
1321
  resizeHandle.addEventListener("pointerdown", onResizeDown);
1246
1322
  attached.cleanup.push(() => resizeHandle.removeEventListener("pointerdown", onResizeDown));
@@ -1298,16 +1374,25 @@ function attachDesktop(wm, element, options = {}) {
1298
1374
  };
1299
1375
  windowElement.addEventListener("keydown", onModalTrap);
1300
1376
  attached.cleanup.push(() => windowElement.removeEventListener("keydown", onModalTrap));
1301
- registry.set(id, attached);
1302
- lastOrder = null;
1303
- lastFocused = null;
1304
- syncAll();
1305
- return () => {
1377
+ const detach = () => {
1306
1378
  if (drag?.id === id) endDrag(true);
1307
1379
  for (const dispose of attached.cleanup) dispose();
1308
1380
  for (const resizeHandle of attached.handles) resizeHandle.remove();
1309
1381
  registry.delete(id);
1310
1382
  };
1383
+ if (windowOptions.removeOnClose) {
1384
+ const stopOnClose = wm.on("close", ({ window: closed }) => {
1385
+ if (closed.id !== id) return;
1386
+ detach();
1387
+ windowElement.remove();
1388
+ });
1389
+ attached.cleanup.push(stopOnClose);
1390
+ }
1391
+ registry.set(id, attached);
1392
+ lastOrder = null;
1393
+ lastFocused = null;
1394
+ syncAll();
1395
+ return detach;
1311
1396
  }
1312
1397
  return {
1313
1398
  element,
@@ -1326,6 +1411,8 @@ function attachDesktop(wm, element, options = {}) {
1326
1411
  }
1327
1412
  };
1328
1413
  }
1414
+
1415
+ // src/dom/binder.ts
1329
1416
  function createDesktopBinder(wm, options = {}) {
1330
1417
  let controller = null;
1331
1418
  const entries = /* @__PURE__ */ new Set();
@@ -1379,5 +1466,5 @@ exports.detectSnapZone = detectSnapZone;
1379
1466
  exports.flipToTarget = flipToTarget;
1380
1467
  exports.prefersReducedMotion = prefersReducedMotion;
1381
1468
  exports.zoneBounds = zoneBounds;
1382
- //# sourceMappingURL=chunk-TXINVEK3.cjs.map
1383
- //# sourceMappingURL=chunk-TXINVEK3.cjs.map
1469
+ //# sourceMappingURL=chunk-KQCVZ2W7.cjs.map
1470
+ //# sourceMappingURL=chunk-KQCVZ2W7.cjs.map