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