@surdeddd/wmkit 0.7.2 → 0.9.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 (91) hide show
  1. package/README.md +60 -3
  2. package/README.ru.md +60 -3
  3. package/dist/angular.cjs +4 -3
  4. package/dist/angular.cjs.map +1 -1
  5. package/dist/angular.d.cts +2 -1
  6. package/dist/angular.d.ts +2 -1
  7. package/dist/angular.js +2 -1
  8. package/dist/angular.js.map +1 -1
  9. package/dist/binder-DGoj0xG1.d.cts +13 -0
  10. package/dist/binder-Lo13iTVB.d.ts +13 -0
  11. package/dist/chrome.cjs +122 -0
  12. package/dist/chrome.cjs.map +1 -0
  13. package/dist/chrome.d.cts +25 -0
  14. package/dist/chrome.d.ts +25 -0
  15. package/dist/chrome.js +114 -0
  16. package/dist/chrome.js.map +1 -0
  17. package/dist/chunk-5XVIC5LH.js +140 -0
  18. package/dist/chunk-5XVIC5LH.js.map +1 -0
  19. package/dist/chunk-BDHFXEJN.cjs +149 -0
  20. package/dist/chunk-BDHFXEJN.cjs.map +1 -0
  21. package/dist/{chunk-NJAI5R4J.cjs → chunk-TUGQX3CX.js} +205 -175
  22. package/dist/chunk-TUGQX3CX.js.map +1 -0
  23. package/dist/{chunk-FOWW4LXS.js → chunk-ZWUVWRUS.cjs} +236 -178
  24. package/dist/chunk-ZWUVWRUS.cjs.map +1 -0
  25. package/dist/devtools.cjs +330 -0
  26. package/dist/devtools.cjs.map +1 -0
  27. package/dist/devtools.d.cts +43 -0
  28. package/dist/devtools.d.ts +43 -0
  29. package/dist/devtools.js +326 -0
  30. package/dist/devtools.js.map +1 -0
  31. package/dist/gestures.cjs +219 -0
  32. package/dist/gestures.cjs.map +1 -0
  33. package/dist/gestures.d.cts +19 -0
  34. package/dist/gestures.d.ts +19 -0
  35. package/dist/gestures.js +215 -0
  36. package/dist/gestures.js.map +1 -0
  37. package/dist/index.cjs +35 -34
  38. package/dist/index.d.cts +3 -2
  39. package/dist/index.d.ts +3 -2
  40. package/dist/index.js +2 -1
  41. package/dist/react.cjs +4 -3
  42. package/dist/react.cjs.map +1 -1
  43. package/dist/react.d.cts +2 -1
  44. package/dist/react.d.ts +2 -1
  45. package/dist/react.js +2 -1
  46. package/dist/react.js.map +1 -1
  47. package/dist/{binder-DvQFmO3K.d.ts → shared-BoHXlPCu.d.ts} +55 -8
  48. package/dist/{binder-_L3Iww0u.d.cts → shared-DdGFz3oy.d.cts} +55 -8
  49. package/dist/solid.cjs +4 -3
  50. package/dist/solid.cjs.map +1 -1
  51. package/dist/solid.d.cts +2 -1
  52. package/dist/solid.d.ts +2 -1
  53. package/dist/solid.js +2 -1
  54. package/dist/solid.js.map +1 -1
  55. package/dist/svelte.cjs +4 -3
  56. package/dist/svelte.cjs.map +1 -1
  57. package/dist/svelte.d.cts +2 -1
  58. package/dist/svelte.d.ts +2 -1
  59. package/dist/svelte.js +2 -1
  60. package/dist/svelte.js.map +1 -1
  61. package/dist/themes/amber.css +316 -0
  62. package/dist/themes/aqua.css +285 -0
  63. package/dist/themes/blueprint.css +303 -0
  64. package/dist/themes/brutalist.css +307 -0
  65. package/dist/themes/candy.css +277 -0
  66. package/dist/themes/carbon.css +297 -0
  67. package/dist/themes/forest.css +278 -0
  68. package/dist/themes/frost.css +279 -0
  69. package/dist/themes/glass.css +2 -1
  70. package/dist/themes/light.css +2 -1
  71. package/dist/themes/neon.css +289 -0
  72. package/dist/themes/noir.css +282 -0
  73. package/dist/themes/paper.css +258 -0
  74. package/dist/themes/retro.css +8 -0
  75. package/dist/themes/synth.css +283 -0
  76. package/dist/themes/terminal.css +273 -0
  77. package/dist/themes.cjs +8747 -0
  78. package/dist/themes.cjs.map +1 -0
  79. package/dist/themes.d.cts +12 -0
  80. package/dist/themes.d.ts +12 -0
  81. package/dist/themes.js +8742 -0
  82. package/dist/themes.js.map +1 -0
  83. package/dist/vue.cjs +4 -3
  84. package/dist/vue.cjs.map +1 -1
  85. package/dist/vue.d.cts +2 -1
  86. package/dist/vue.d.ts +2 -1
  87. package/dist/vue.js +2 -1
  88. package/dist/vue.js.map +1 -1
  89. package/package.json +80 -7
  90. package/dist/chunk-FOWW4LXS.js.map +0 -1
  91. package/dist/chunk-NJAI5R4J.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ import { clampToViewport, boundsEqual, clampSize, applyAspect, clamp, zoneBounds, magnetize, detectSnapZone } from './chunk-5XVIC5LH.js';
2
2
 
3
3
  // src/core/emitter.ts
4
4
  function createEmitter() {
@@ -28,143 +28,6 @@ function createEmitter() {
28
28
  };
29
29
  }
30
30
 
31
- // src/core/geometry.ts
32
- function clamp(value, min, max) {
33
- return Math.min(Math.max(value, min), max);
34
- }
35
- function clampSize(size, min, max) {
36
- return {
37
- width: clamp(size.width, min.width, max ? max.width : Number.POSITIVE_INFINITY),
38
- height: clamp(size.height, min.height, max ? max.height : Number.POSITIVE_INFINITY)
39
- };
40
- }
41
- function clampToViewport(bounds, viewport, minVisible) {
42
- if (viewport.width <= 0 || viewport.height <= 0) return bounds;
43
- const visibleX = Math.min(minVisible, bounds.width);
44
- const visibleY = Math.min(minVisible, bounds.height);
45
- const x = clamp(bounds.x, visibleX - bounds.width, Math.max(0, viewport.width - visibleX));
46
- const y = clamp(bounds.y, 0, Math.max(0, viewport.height - visibleY));
47
- return x === bounds.x && y === bounds.y ? bounds : { ...bounds, x, y };
48
- }
49
- function boundsEqual(a, b) {
50
- return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
51
- }
52
- function applyAspect(size, ratio, min, max, drive) {
53
- if (!Number.isFinite(ratio) || ratio <= 0) return clampSize(size, min, max);
54
- const maxWidth = max ? max.width : Number.POSITIVE_INFINITY;
55
- const maxHeight = max ? max.height : Number.POSITIVE_INFINITY;
56
- if (drive === "height") {
57
- const height = clamp(
58
- size.height,
59
- Math.max(min.height, min.width / ratio),
60
- Math.min(maxHeight, maxWidth / ratio)
61
- );
62
- return { width: height * ratio, height };
63
- }
64
- const width = clamp(
65
- size.width,
66
- Math.max(min.width, min.height * ratio),
67
- Math.min(maxWidth, maxHeight * ratio)
68
- );
69
- return { width, height: width / ratio };
70
- }
71
- function zoneBounds(zone, viewport) {
72
- const w = viewport.width;
73
- const h = viewport.height;
74
- const halfW = Math.round(w / 2);
75
- const halfH = Math.round(h / 2);
76
- const oneThird = Math.round(w / 3);
77
- const twoThirds = Math.round(w * 2 / 3);
78
- switch (zone) {
79
- case "left-third":
80
- return { x: 0, y: 0, width: oneThird, height: h };
81
- case "center-third":
82
- return { x: oneThird, y: 0, width: twoThirds - oneThird, height: h };
83
- case "right-third":
84
- return { x: twoThirds, y: 0, width: w - twoThirds, height: h };
85
- case "left":
86
- return { x: 0, y: 0, width: halfW, height: h };
87
- case "right":
88
- return { x: halfW, y: 0, width: w - halfW, height: h };
89
- case "top":
90
- return { x: 0, y: 0, width: w, height: halfH };
91
- case "bottom":
92
- return { x: 0, y: halfH, width: w, height: h - halfH };
93
- case "top-left":
94
- return { x: 0, y: 0, width: halfW, height: halfH };
95
- case "top-right":
96
- return { x: halfW, y: 0, width: w - halfW, height: halfH };
97
- case "bottom-left":
98
- return { x: 0, y: halfH, width: halfW, height: h - halfH };
99
- case "bottom-right":
100
- return { x: halfW, y: halfH, width: w - halfW, height: h - halfH };
101
- }
102
- }
103
- function detectSnapZone(x, y, viewport, options = {}) {
104
- const threshold = options.threshold ?? 12;
105
- const cornerSize = options.cornerSize ?? 64;
106
- const w = viewport.width;
107
- const h = viewport.height;
108
- if (w <= 0 || h <= 0) return null;
109
- const nearLeft = x <= threshold;
110
- const nearRight = x >= w - threshold;
111
- const nearTop = y <= threshold;
112
- const nearBottom = y >= h - threshold;
113
- if (nearLeft || nearRight) {
114
- const side = nearLeft ? "left" : "right";
115
- if (y <= cornerSize) return `top-${side}`;
116
- if (y >= h - cornerSize) return `bottom-${side}`;
117
- return side;
118
- }
119
- if (nearTop) {
120
- if (x <= cornerSize) return "top-left";
121
- if (x >= w - cornerSize) return "top-right";
122
- return "top";
123
- }
124
- if (nearBottom) {
125
- if (x <= cornerSize) return "bottom-left";
126
- if (x >= w - cornerSize) return "bottom-right";
127
- return "bottom";
128
- }
129
- return null;
130
- }
131
- function nearestEdge(low, high, targetLow, targetHigh) {
132
- return [targetLow - low, targetHigh - low, targetLow - high, targetHigh - high];
133
- }
134
- function magnetize(bounds, targets, threshold) {
135
- const result = { x: bounds.x, y: bounds.y, snappedX: false, snappedY: false };
136
- if (threshold <= 0 || targets.length === 0) return result;
137
- let bestX = threshold + 1;
138
- let bestY = threshold + 1;
139
- for (const target of targets) {
140
- for (const delta of nearestEdge(
141
- bounds.x,
142
- bounds.x + bounds.width,
143
- target.x,
144
- target.x + target.width
145
- )) {
146
- if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestX)) bestX = delta;
147
- }
148
- for (const delta of nearestEdge(
149
- bounds.y,
150
- bounds.y + bounds.height,
151
- target.y,
152
- target.y + target.height
153
- )) {
154
- if (Math.abs(delta) <= threshold && Math.abs(delta) < Math.abs(bestY)) bestY = delta;
155
- }
156
- }
157
- if (Math.abs(bestX) <= threshold) {
158
- result.x = bounds.x + bestX;
159
- result.snappedX = true;
160
- }
161
- if (Math.abs(bestY) <= threshold) {
162
- result.y = bounds.y + bestY;
163
- result.snappedY = true;
164
- }
165
- return result;
166
- }
167
-
168
31
  // src/core/manager.ts
169
32
  var LAYER_RANK = { normal: 0, floating: 1, modal: 2 };
170
33
  var STAGES = ["normal", "minimized", "maximized", "snapped"];
@@ -1504,6 +1367,22 @@ function createAnnouncer(wm, container, messages = {}) {
1504
1367
  };
1505
1368
  }
1506
1369
 
1370
+ // src/dom/actions.ts
1371
+ function createActions(wm, id) {
1372
+ return {
1373
+ focus: () => wm.focus(id),
1374
+ close: () => wm.close(id),
1375
+ minimize: () => wm.minimize(id),
1376
+ maximize: () => wm.maximize(id),
1377
+ toggleMaximize: () => wm.toggleMaximize(id),
1378
+ restore: () => wm.restore(id),
1379
+ center: () => wm.center(id),
1380
+ sendToBack: () => wm.sendToBack(id),
1381
+ snap: (zone) => wm.snap(id, zone),
1382
+ moveToWorkspace: (workspace) => wm.moveToWorkspace(id, workspace)
1383
+ };
1384
+ }
1385
+
1507
1386
  // src/dom/drag.ts
1508
1387
  function createDragStarter(ctx) {
1509
1388
  const { wm, doc, view } = ctx;
@@ -1538,8 +1417,7 @@ function createDragStarter(ctx) {
1538
1417
  pendingX: 0,
1539
1418
  pendingY: 0,
1540
1419
  hasPending: false,
1541
- finish: () => {
1542
- }
1420
+ finish: (cancelled) => finish(cancelled)
1543
1421
  };
1544
1422
  const el = ctx.windowElement(id);
1545
1423
  function armGroup(target2) {
@@ -1712,7 +1590,6 @@ function createDragStarter(ctx) {
1712
1590
  else wm.endInteraction();
1713
1591
  }
1714
1592
  }
1715
- session.finish = finish;
1716
1593
  wm.beginInteraction();
1717
1594
  ctx.claimDrag(session);
1718
1595
  handle.setPointerCapture(event.pointerId);
@@ -1867,7 +1744,7 @@ function createResizeHandles(doc, edge, corner) {
1867
1744
  }
1868
1745
 
1869
1746
  // src/dom/shared.ts
1870
- var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize]";
1747
+ var INTERACTIVE_SELECTOR = "button, input, select, textarea, a[href], [contenteditable], [data-wm-close], [data-wm-minimize], [data-wm-maximize], [data-wm-snap], [data-wm-restore], [data-wm-center], [data-wm-send-back], [data-wm-to-workspace]";
1871
1748
  function windowOf(element) {
1872
1749
  const view = element.ownerDocument.defaultView;
1873
1750
  if (!view) throw new Error("wmkit: desktop element is not attached to a document");
@@ -1964,8 +1841,53 @@ var SNAP_SHORTCUTS = {
1964
1841
  ArrowLeft: "left",
1965
1842
  ArrowRight: "right"
1966
1843
  };
1844
+ var SNAP_ZONES = /* @__PURE__ */ new Set([
1845
+ "left",
1846
+ "right",
1847
+ "top",
1848
+ "bottom",
1849
+ "top-left",
1850
+ "top-right",
1851
+ "bottom-left",
1852
+ "bottom-right",
1853
+ "left-third",
1854
+ "center-third",
1855
+ "right-third"
1856
+ ]);
1967
1857
  var LANDMARK_TAGS = /* @__PURE__ */ new Set(["header", "footer", "aside", "nav"]);
1968
1858
  var FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
1859
+ function focusablesOf(el) {
1860
+ const root = el.shadowRoot;
1861
+ if (!root) return [...el.querySelectorAll(FOCUSABLE_SELECTOR)];
1862
+ const found = [];
1863
+ for (const node of root.querySelectorAll(`${FOCUSABLE_SELECTOR}, slot`)) {
1864
+ if (!(node instanceof HTMLSlotElement)) {
1865
+ found.push(node);
1866
+ continue;
1867
+ }
1868
+ for (const assigned of node.assignedElements()) {
1869
+ if (assigned.matches(FOCUSABLE_SELECTOR)) found.push(assigned);
1870
+ found.push(...assigned.querySelectorAll(FOCUSABLE_SELECTOR));
1871
+ }
1872
+ }
1873
+ return found;
1874
+ }
1875
+ function dragHandleAt(node, clientX, clientY) {
1876
+ const handle = node.closest?.("[data-wm-drag]");
1877
+ if (handle) return handle;
1878
+ const inner = node.shadowRoot?.elementFromPoint?.(clientX, clientY) ?? null;
1879
+ return inner === null || inner === node ? null : dragHandleAt(inner, clientX, clientY);
1880
+ }
1881
+ function windowElementOf(node) {
1882
+ const found = node.closest("[data-wm-window]");
1883
+ if (found) return found;
1884
+ const host = node.getRootNode().host;
1885
+ return host ? windowElementOf(host) : null;
1886
+ }
1887
+ function defaultVariant(win) {
1888
+ const variant = win.meta.variant;
1889
+ return typeof variant === "string" && variant !== "" ? variant : null;
1890
+ }
1969
1891
  function attachDesktop(wm, element, options = {}) {
1970
1892
  const doc = element.ownerDocument;
1971
1893
  const view = windowOf(element);
@@ -1990,6 +1912,7 @@ function attachDesktop(wm, element, options = {}) {
1990
1912
  const interactiveSelector = options.interactiveSelector ?? INTERACTIVE_SELECTOR;
1991
1913
  const animationEnabled = options.animation !== false;
1992
1914
  const animationOptions = typeof options.animation === "object" ? options.animation : {};
1915
+ const windowVariant = options.windowVariant ?? defaultVariant;
1993
1916
  element.dataset.wmDesktop = "";
1994
1917
  element.tabIndex = -1;
1995
1918
  if (view.getComputedStyle(element).position === "static") {
@@ -2079,8 +2002,8 @@ function attachDesktop(wm, element, options = {}) {
2079
2002
  groupTarget(clientX, clientY, selfId) {
2080
2003
  if (!groupingEnabled || typeof doc.elementsFromPoint !== "function") return null;
2081
2004
  for (const node of doc.elementsFromPoint(clientX, clientY)) {
2082
- const handle = node.closest?.("[data-wm-drag]");
2083
- const host = handle?.closest("[data-wm-window]");
2005
+ const handle = dragHandleAt(node, clientX, clientY);
2006
+ const host = handle && windowElementOf(handle);
2084
2007
  const id = host?.dataset.wmWindow;
2085
2008
  if (id && id !== selfId && registry.get(id)?.element === host) return id;
2086
2009
  }
@@ -2103,6 +2026,21 @@ function attachDesktop(wm, element, options = {}) {
2103
2026
  };
2104
2027
  const startDrag = createDragStarter(ctx);
2105
2028
  const startResize = createResizeStarter(ctx);
2029
+ if (options.gestures) {
2030
+ const gestureContext = {
2031
+ wm,
2032
+ doc,
2033
+ view,
2034
+ desktop: element,
2035
+ toLocal: (event) => ctx.toLocal(event),
2036
+ trackRect: () => ctx.trackRect(),
2037
+ windowElement: (id) => ctx.windowElement(id),
2038
+ busy: () => drag !== null,
2039
+ claim: (gesture) => ctx.claimDrag(gesture),
2040
+ release: (gesture) => ctx.releaseDrag(gesture)
2041
+ };
2042
+ for (const gesture of options.gestures) cleanup.push(gesture(gestureContext));
2043
+ }
2106
2044
  if (options.autoViewport !== false) {
2107
2045
  const applyViewport = () => wm.setViewport({ width: element.clientWidth, height: element.clientHeight });
2108
2046
  applyViewport();
@@ -2121,6 +2059,9 @@ function attachDesktop(wm, element, options = {}) {
2121
2059
  el.dataset.wmStage = win.stage;
2122
2060
  el.dataset.wmLayer = win.layer;
2123
2061
  el.dataset.wmWorkspace = String(win.workspace);
2062
+ const variant = windowVariant(win);
2063
+ if (variant === null) delete el.dataset.wmVariant;
2064
+ else el.dataset.wmVariant = variant;
2124
2065
  if (win.groupId === null) {
2125
2066
  delete el.dataset.wmGroup;
2126
2067
  delete el.dataset.wmTab;
@@ -2139,6 +2080,9 @@ function attachDesktop(wm, element, options = {}) {
2139
2080
  }
2140
2081
  el.hidden = hide;
2141
2082
  el.setAttribute("aria-label", win.title);
2083
+ if (attached.title && attached.title.textContent !== win.title) {
2084
+ attached.title.textContent = win.title;
2085
+ }
2142
2086
  if (win.layer === "modal") el.setAttribute("aria-modal", "true");
2143
2087
  else el.removeAttribute("aria-modal");
2144
2088
  for (const handle of attached.handles) {
@@ -2176,15 +2120,30 @@ function attachDesktop(wm, element, options = {}) {
2176
2120
  function syncAll() {
2177
2121
  const state = wm.getState();
2178
2122
  const orderChanged = state.order !== lastOrder;
2123
+ let restyle = null;
2179
2124
  for (const id of state.order) {
2180
2125
  const attached = registry.get(id);
2181
2126
  const win = state.windows[id];
2182
2127
  if (!attached || !win) continue;
2128
+ const wanted2 = typeof win.meta.skin === "string" ? win.meta.skin : null;
2129
+ if (attached.skin !== null && wanted2 !== null && wanted2 !== attached.skin) {
2130
+ restyle = restyle ?? [];
2131
+ restyle.push([id, wanted2]);
2132
+ continue;
2133
+ }
2183
2134
  const activeTab = win.groupId === null || state.groups[win.groupId]?.activeId === win.id;
2184
2135
  if (attached.lastState !== win || attached.lastWorkspace !== state.workspace || attached.lastTab !== activeTab) {
2185
2136
  syncWindow(attached, win, state.workspace, activeTab);
2186
2137
  }
2187
2138
  }
2139
+ if (restyle !== null) {
2140
+ for (const [id, wanted2] of restyle) {
2141
+ const attached = registry.get(id);
2142
+ if (attached && attached.skin !== wanted2) buildSkin(id, wanted2, attached.mountOptions);
2143
+ }
2144
+ syncAll();
2145
+ return;
2146
+ }
2188
2147
  if (orderChanged) applyStacking(state.order);
2189
2148
  const wanted = state.focusedId ? registry.get(state.focusedId)?.element ?? null : null;
2190
2149
  if (wanted !== focusedEl) {
@@ -2282,6 +2241,11 @@ function attachDesktop(wm, element, options = {}) {
2282
2241
  const attached = {
2283
2242
  element: windowElement,
2284
2243
  handle: null,
2244
+ title: null,
2245
+ skin: null,
2246
+ mount: null,
2247
+ mountOptions: windowOptions,
2248
+ release: null,
2285
2249
  handles: [],
2286
2250
  lastState: null,
2287
2251
  lastZ: UNASSIGNED,
@@ -2295,10 +2259,11 @@ function attachDesktop(wm, element, options = {}) {
2295
2259
  windowElement.style.position = "absolute";
2296
2260
  windowElement.style.left = "0";
2297
2261
  windowElement.style.top = "0";
2298
- const titleEl = windowElement.querySelector("[data-wm-title]");
2299
- if (titleEl) {
2300
- if (!titleEl.id) titleEl.id = `wmkit-title-${id}`;
2301
- windowElement.setAttribute("aria-labelledby", titleEl.id);
2262
+ const lightTitle = windowElement.querySelector("[data-wm-title]");
2263
+ attached.title = lightTitle ?? windowElement.shadowRoot?.querySelector("[data-wm-title]") ?? null;
2264
+ if (lightTitle) {
2265
+ if (!lightTitle.id) lightTitle.id = `wmkit-title-${id}`;
2266
+ windowElement.setAttribute("aria-labelledby", lightTitle.id);
2302
2267
  }
2303
2268
  const handle = typeof windowOptions.handle === "string" ? windowElement.querySelector(windowOptions.handle) : windowOptions.handle ?? windowElement.querySelector("[data-wm-drag]");
2304
2269
  attached.handle = handle;
@@ -2319,10 +2284,38 @@ function attachDesktop(wm, element, options = {}) {
2319
2284
  if (!current) return;
2320
2285
  if (target.closest("[data-wm-close]")) {
2321
2286
  if (current.closable && options.beforeClose?.(current) !== false) wm.close(id);
2322
- } else if (target.closest("[data-wm-minimize]")) {
2287
+ return;
2288
+ }
2289
+ if (target.closest("[data-wm-minimize]")) {
2323
2290
  if (current.minimizable) wm.minimize(id);
2324
- } else if (target.closest("[data-wm-maximize]")) {
2291
+ return;
2292
+ }
2293
+ if (target.closest("[data-wm-maximize]")) {
2325
2294
  if (current.maximizable) wm.toggleMaximize(id);
2295
+ return;
2296
+ }
2297
+ const act = createActions(wm, id);
2298
+ const snapTarget = target.closest("[data-wm-snap]");
2299
+ if (snapTarget) {
2300
+ const zone = snapTarget.dataset.wmSnap ?? "";
2301
+ if (current.snappable && SNAP_ZONES.has(zone)) act.snap(zone);
2302
+ return;
2303
+ }
2304
+ if (target.closest("[data-wm-restore]")) {
2305
+ act.restore();
2306
+ return;
2307
+ }
2308
+ if (target.closest("[data-wm-center]")) {
2309
+ act.center();
2310
+ return;
2311
+ }
2312
+ if (target.closest("[data-wm-send-back]")) {
2313
+ act.sendToBack();
2314
+ return;
2315
+ }
2316
+ const workspaceTarget = target.closest("[data-wm-to-workspace]");
2317
+ if (workspaceTarget) {
2318
+ act.moveToWorkspace(Number.parseInt(workspaceTarget.dataset.wmToWorkspace ?? "", 10));
2326
2319
  }
2327
2320
  };
2328
2321
  windowElement.addEventListener("click", onClick);
@@ -2360,7 +2353,7 @@ function attachDesktop(wm, element, options = {}) {
2360
2353
  const onResizeDown = (event) => startResize(id, direction, event);
2361
2354
  resizeHandle.addEventListener("pointerdown", onResizeDown);
2362
2355
  attached.cleanup.push(() => resizeHandle.removeEventListener("pointerdown", onResizeDown));
2363
- windowElement.append(resizeHandle);
2356
+ (windowElement.shadowRoot ?? windowElement).append(resizeHandle);
2364
2357
  attached.handles.push(resizeHandle);
2365
2358
  }
2366
2359
  }
@@ -2398,9 +2391,10 @@ function attachDesktop(wm, element, options = {}) {
2398
2391
  if (event.key !== "Tab") return;
2399
2392
  const current = wm.get(id);
2400
2393
  if (current?.layer !== "modal") return;
2401
- const focusables = [
2402
- ...windowElement.querySelectorAll(FOCUSABLE_SELECTOR)
2403
- ].filter((node) => node.offsetParent !== null || node === doc.activeElement);
2394
+ const active = windowElement.shadowRoot?.activeElement ?? doc.activeElement;
2395
+ const focusables = focusablesOf(windowElement).filter(
2396
+ (node) => node.offsetParent !== null || node === active
2397
+ );
2404
2398
  if (focusables.length === 0) return;
2405
2399
  const first = focusables[0];
2406
2400
  const last = focusables[focusables.length - 1];
@@ -2410,10 +2404,10 @@ function attachDesktop(wm, element, options = {}) {
2410
2404
  (event.shiftKey ? last : first).focus();
2411
2405
  return;
2412
2406
  }
2413
- if (event.shiftKey && doc.activeElement === first) {
2407
+ if (event.shiftKey && active === first) {
2414
2408
  event.preventDefault();
2415
2409
  last.focus();
2416
- } else if (!event.shiftKey && doc.activeElement === last) {
2410
+ } else if (!event.shiftKey && active === last) {
2417
2411
  event.preventDefault();
2418
2412
  first.focus();
2419
2413
  }
@@ -2451,10 +2445,62 @@ function attachDesktop(wm, element, options = {}) {
2451
2445
  }
2452
2446
  return detach;
2453
2447
  }
2448
+ function resolveSkin(skin) {
2449
+ if (typeof skin !== "string") return skin;
2450
+ const found = options.skins?.[skin];
2451
+ if (!found) throw new Error(`wmkit: unknown skin "${skin}"`);
2452
+ return found;
2453
+ }
2454
+ function buildSkin(id, skin, windowOptions) {
2455
+ const win = wm.get(id);
2456
+ if (!win) throw new Error(`wmkit: cannot mount unknown window "${id}"`);
2457
+ const standing = registry.get(id);
2458
+ const previous = standing?.mount ?? null;
2459
+ const carried = previous === null ? [] : [...previous.content.childNodes];
2460
+ if (previous !== null) {
2461
+ registry.delete(id);
2462
+ standing?.release?.();
2463
+ previous.element.remove();
2464
+ }
2465
+ const mount = resolveSkin(skin)({ doc, id, window: win, actions: createActions(wm, id) });
2466
+ element.append(mount.element);
2467
+ const release = attachWindow(id, mount.element, windowOptions);
2468
+ const attached = registry.get(id);
2469
+ attached.skin = typeof skin === "string" ? skin : null;
2470
+ attached.mount = mount;
2471
+ attached.mountOptions = windowOptions;
2472
+ attached.release = release;
2473
+ mount.content.append(...carried);
2474
+ previous?.destroy?.();
2475
+ return mount;
2476
+ }
2477
+ function mountWindow(id, skin, windowOptions = {}) {
2478
+ let last = buildSkin(id, skin, windowOptions);
2479
+ const live = () => {
2480
+ last = registry.get(id)?.mount ?? last;
2481
+ return last;
2482
+ };
2483
+ return {
2484
+ get element() {
2485
+ return live().element;
2486
+ },
2487
+ get content() {
2488
+ return live().content;
2489
+ },
2490
+ detach() {
2491
+ const mount = live();
2492
+ registry.get(id)?.release?.();
2493
+ mount.element.remove();
2494
+ mount.destroy?.();
2495
+ }
2496
+ };
2497
+ }
2454
2498
  return {
2455
2499
  element,
2456
2500
  wm,
2457
2501
  attachWindow,
2502
+ mountWindow,
2503
+ actions: (id) => createActions(wm, id),
2458
2504
  destroy() {
2459
2505
  endDrag(true);
2460
2506
  for (const [, attached] of registry) {
@@ -2524,22 +2570,6 @@ function createDesktopBinder(wm, options = {}) {
2524
2570
  };
2525
2571
  }
2526
2572
 
2527
- exports.applyAspect = applyAspect;
2528
- exports.attachDesktop = attachDesktop;
2529
- exports.boundsEqual = boundsEqual;
2530
- exports.clamp = clamp;
2531
- exports.clampSize = clampSize;
2532
- exports.clampToViewport = clampToViewport;
2533
- exports.createAnnouncer = createAnnouncer;
2534
- exports.createDesktopBinder = createDesktopBinder;
2535
- exports.createEmitter = createEmitter;
2536
- exports.createWindowManager = createWindowManager;
2537
- exports.defaultMessages = defaultMessages;
2538
- exports.detectSnapZone = detectSnapZone;
2539
- exports.flipFromTarget = flipFromTarget;
2540
- exports.flipToTarget = flipToTarget;
2541
- exports.magnetize = magnetize;
2542
- exports.prefersReducedMotion = prefersReducedMotion;
2543
- exports.zoneBounds = zoneBounds;
2544
- //# sourceMappingURL=chunk-NJAI5R4J.cjs.map
2545
- //# sourceMappingURL=chunk-NJAI5R4J.cjs.map
2573
+ export { attachDesktop, createAnnouncer, createDesktopBinder, createEmitter, createWindowManager, defaultMessages, flipFromTarget, flipToTarget, prefersReducedMotion };
2574
+ //# sourceMappingURL=chunk-TUGQX3CX.js.map
2575
+ //# sourceMappingURL=chunk-TUGQX3CX.js.map