@surdeddd/wmkit 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Headless window manager for the web.** Draggable, resizable, snappable windows with a taskbar model, keyboard accessibility and state persistence — for vanilla JS and every major framework.
4
4
 
5
- [Русская версия](./README.ru.md) · [Live demo](https://wmkit.vercel.app) · [Mirror (Pages)](https://surdeddd.github.io/wmkit/) · [GitHub](https://github.com/Surdeddd/wmkit)
5
+ [Русская версия](./README.ru.md) · [Live demo](https://wmkit.vercel.app) · [Mirror (Pages)](https://surdeddd.github.io/wmkit/) · [Docs](./docs/README.md) · [GitHub](https://github.com/Surdeddd/wmkit)
6
6
 
7
7
  [![CI](https://github.com/Surdeddd/wmkit/actions/workflows/ci.yml/badge.svg)](https://github.com/Surdeddd/wmkit/actions/workflows/ci.yml)
8
8
  [![npm](https://img.shields.io/npm/v/@surdeddd/wmkit)](https://www.npmjs.com/package/@surdeddd/wmkit)
@@ -346,6 +346,18 @@ Skip the import entirely and the library stays headless: state attributes (`data
346
346
 
347
347
  The core never touches `window`/`document` — create managers and even `hydrate()` state on the server, then call `attachDesktop` after mount. `persist` no-ops without usable storage.
348
348
 
349
+ ## Documentation
350
+
351
+ This README is the tour. The reference lives in [`docs/`](./docs/README.md):
352
+
353
+ | Page | What is in it |
354
+ | --- | --- |
355
+ | [API reference](./docs/api.md) | every export, option, method, event and type |
356
+ | [Adapters](./docs/adapters.md) | complete React, Vue, Svelte, Solid and Angular integrations |
357
+ | [Theming](./docs/theming.md) | `data-wm-*` contract, CSS variables, writing a theme from scratch |
358
+ | [Recipes](./docs/recipes.md) | taskbar, modals, persistence, workspaces, SSR, testing, performance |
359
+ | [Browser support](./docs/browser-support.md) | baselines per entry point and what degrades where |
360
+
349
361
  ## Comparison
350
362
 
351
363
  | | wmkit | WinBox | jsPanel4 | Dockview | Zag floating-panel |
@@ -364,7 +376,7 @@ The core never touches `window`/`document` — create managers and even `hydrate
364
376
 
365
377
  ## Quality
366
378
 
367
- - 216 unit tests, **100%** line/branch/function/statement coverage on the core state machine and persistence
379
+ - 250 unit tests: **100%** line/branch/function/statement coverage on the core state machine and persistence, with enforced floors on the DOM layer and the adapters
368
380
  - 190+ Playwright scenarios on Chromium, WebKit and mobile emulation: drag, 8-way resize, snap, magnetism, workspaces, undo after drag, keyboard, touch, persistence across reloads, 50-window stress, modal traps, axe accessibility scans, visual regression screenshots
369
381
  - performance benchmarks run in CI on every push (`vitest bench`): 1 000 windows open in ~150 ms, a move among 50 windows costs ~1.2 µs, a full 100-step undo/redo sweep ~52 µs
370
382
  - `publint` + `@arethetypeswrong/cli` validate the published package, `size-limit` guards bundle budgets
package/README.ru.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Headless оконный менеджер для веба.** Перетаскиваемые окна с ресайзом, снэпом, таскбаром, клавиатурной доступностью и персистом состояния — для vanilla JS и всех основных фреймворков.
4
4
 
5
- [English version](./README.md) · [Живое демо](https://wmkit.vercel.app) · [Зеркало (Pages)](https://surdeddd.github.io/wmkit/) · [GitHub](https://github.com/Surdeddd/wmkit)
5
+ [English version](./README.md) · [Живое демо](https://wmkit.vercel.app) · [Зеркало (Pages)](https://surdeddd.github.io/wmkit/) · [Документация](./docs/README.md) · [GitHub](https://github.com/Surdeddd/wmkit)
6
6
 
7
7
  [![wmkit — живой демо-десктоп](https://raw.githubusercontent.com/Surdeddd/wmkit/main/.github/assets/hero.png)](https://surdeddd.github.io/wmkit/)
8
8
 
@@ -73,7 +73,140 @@ desktop.attachWindow(win.id, el, { removeOnClose: true })
73
73
 
74
74
  ## Адаптеры
75
75
 
76
- Примеры для React, Vue, Svelte, Solid и Angular в [английском README](./README.md#react) и на [лендинге](https://surdeddd.github.io/wmkit/) (табы «Фреймворки»). Принцип один: контент окна живёт в дереве вашего фреймворка, никакого innerHTML.
76
+ Каждый адаптер тонкая обёртка над тем же ядром и тем же DOM-контроллером, около 60 строк. Они нужны только чтобы привязаться и отвязаться в нужный момент жизненного цикла компонента. Контент окна всегда живёт в дереве вашего фреймворка: никакого innerHTML, порталов и переноса узлов.
77
+
78
+ ### React
79
+
80
+ ```tsx
81
+ import { useWindowManager, useDesktop, useWmState, useWmWindowRef } from '@surdeddd/wmkit/react'
82
+
83
+ function Desktop() {
84
+ const wm = useWindowManager()
85
+ const { ref, binder } = useDesktop(wm)
86
+ const state = useWmState(wm)
87
+
88
+ return (
89
+ <div ref={ref} style={{ position: 'relative', height: '100vh' }}>
90
+ <button type="button" onClick={() => wm.open({ title: 'Окно' })}>открыть</button>
91
+ {state.order.map((id) => (
92
+ <Win key={id} binder={binder} win={state.windows[id]} />
93
+ ))}
94
+ </div>
95
+ )
96
+ }
97
+
98
+ function Win({ binder, win }) {
99
+ const ref = useWmWindowRef(binder, win.id, { removeOnClose: true })
100
+ return (
101
+ <section ref={ref}>
102
+ <header data-wm-drag><span data-wm-title>{win.title}</span></header>
103
+ <div data-wm-content>ваше дерево компонентов</div>
104
+ </section>
105
+ )
106
+ }
107
+ ```
108
+
109
+ ### Vue
110
+
111
+ ```vue
112
+ <script setup lang="ts">
113
+ import { ref } from 'vue'
114
+ import { useWindowManager, useDesktop, useWmWindowEl } from '@surdeddd/wmkit/vue'
115
+
116
+ const host = ref<HTMLElement>()
117
+ const panel = ref<HTMLElement>()
118
+ const wm = useWindowManager()
119
+ const binder = useDesktop(wm, host)
120
+ useWmWindowEl(binder, 'notes', panel)
121
+ wm.open({ id: 'notes', title: 'Заметки' })
122
+ </script>
123
+
124
+ <template>
125
+ <div ref="host" style="position: relative; height: 100vh">
126
+ <section ref="panel">
127
+ <header data-wm-drag><span data-wm-title>Заметки</span></header>
128
+ <div data-wm-content>композаблы</div>
129
+ </section>
130
+ </div>
131
+ </template>
132
+ ```
133
+
134
+ ### Svelte
135
+
136
+ ```svelte
137
+ <script lang="ts">
138
+ import { createManager, createDesktop, wmWindowStore } from '@surdeddd/wmkit/svelte'
139
+
140
+ const wm = createManager()
141
+ const dk = createDesktop(wm)
142
+ wm.open({ id: 'notes', title: 'Заметки' })
143
+ const notes = wmWindowStore(wm, 'notes')
144
+ </script>
145
+
146
+ <div use:dk.desktop style="position: relative; height: 100vh">
147
+ <section use:dk.window={{ id: 'notes' }}>
148
+ <header data-wm-drag><span data-wm-title>{$notes?.title}</span></header>
149
+ <div data-wm-content>сторы и экшены</div>
150
+ </section>
151
+ </div>
152
+ ```
153
+
154
+ ### Solid
155
+
156
+ ```tsx
157
+ import { useWindowManager, createDesktop, useWmState } from '@surdeddd/wmkit/solid'
158
+
159
+ function Desktop() {
160
+ const wm = useWindowManager()
161
+ const dk = createDesktop(wm)
162
+ const state = useWmState(wm)
163
+ wm.open({ id: 'notes', title: 'Заметки' })
164
+
165
+ return (
166
+ <div ref={dk.desktop} style={{ position: 'relative', height: '100vh' }}>
167
+ <section ref={dk.window('notes')}>
168
+ <header data-wm-drag><span data-wm-title>Заметки</span></header>
169
+ <div data-wm-content>{state().order.length} окон</div>
170
+ </section>
171
+ </div>
172
+ )
173
+ }
174
+ ```
175
+
176
+ ### Angular
177
+
178
+ ```ts
179
+ import { Component, ElementRef, afterNextRender, viewChild } from '@angular/core'
180
+ import { useWindowManager, createDesktop, useWmState } from '@surdeddd/wmkit/angular'
181
+
182
+ @Component({
183
+ selector: 'app-desktop',
184
+ template: `
185
+ <div #host style="position: relative; height: 100vh">
186
+ <section #panel>
187
+ <header data-wm-drag><span data-wm-title>Заметки</span></header>
188
+ <div data-wm-content>{{ state().order.length }} окон</div>
189
+ </section>
190
+ </div>`,
191
+ })
192
+ export class DesktopComponent {
193
+ private host = viewChild.required<ElementRef>('host')
194
+ private panel = viewChild.required<ElementRef>('panel')
195
+ wm = useWindowManager()
196
+ state = useWmState(this.wm)
197
+
198
+ constructor() {
199
+ const dk = createDesktop(this.wm)
200
+ afterNextRender(() => {
201
+ dk.desktop(this.host().nativeElement)
202
+ this.wm.open({ id: 'notes', title: 'Заметки' })
203
+ dk.window('notes')(this.panel().nativeElement)
204
+ })
205
+ }
206
+ }
207
+ ```
208
+
209
+ Полные примеры с таскбаром, модалками и сигнатурами всех хуков — в [docs/adapters.md](./docs/adapters.md).
77
210
 
78
211
  ## API ядра — кратко
79
212
 
@@ -140,7 +273,7 @@ if (isPopoutSupported()) await popout(wm, 'docs', contentElement)
140
273
 
141
274
  ## Качество
142
275
 
143
- - 216 юнит-тестов, **100%** покрытие стейт-машины и persist по строкам/веткам/функциям
276
+ - 250 юнит-тестов: **100%** покрытие стейт-машины и persist по строкам/веткам/функциям, плюс обязательные пороги на DOM-слой и адаптеры
144
277
  - 190+ Playwright-сценариев на Chromium, WebKit и мобильной эмуляции: drag, ресайз во все стороны, снэп, магнетизм, рабочие столы, undo после drag, клавиатура, touch, персист через перезагрузку, стресс на 50 окон, модальные ловушки, axe-аудиты доступности, визуальная регрессия по скриншотам
145
278
  - перф-бенчмарки в CI на каждый push (`vitest bench`): 1 000 окон открываются за ~150 мс, move среди 50 окон ~1.2 мкс, полный undo/redo-проход на 100 шагов ~52 мкс
146
279
  - `publint` + `@arethetypeswrong/cli` проверяют валидность пакета, `size-limit` следит за бюджетами
package/dist/angular.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk3KZZOLHW_cjs = require('./chunk-3KZZOLHW.cjs');
3
+ var chunkGLBVT4AQ_cjs = require('./chunk-GLBVT4AQ.cjs');
4
4
  var core = require('@angular/core');
5
5
 
6
6
  function onDestroy(dispose) {
@@ -13,7 +13,7 @@ function onDestroy(dispose) {
13
13
  destroyRef?.onDestroy(dispose);
14
14
  }
15
15
  function useWindowManager(options) {
16
- const wm = chunk3KZZOLHW_cjs.createWindowManager(options);
16
+ const wm = chunkGLBVT4AQ_cjs.createWindowManager(options);
17
17
  onDestroy(() => wm.destroy());
18
18
  return wm;
19
19
  }
@@ -28,7 +28,7 @@ function useWmWindow(wm, id) {
28
28
  return core.computed(() => state().windows[id]);
29
29
  }
30
30
  function createDesktop(wm, options) {
31
- const binder = chunk3KZZOLHW_cjs.createDesktopBinder(wm, options);
31
+ const binder = chunkGLBVT4AQ_cjs.createDesktopBinder(wm, options);
32
32
  onDestroy(() => binder.destroy());
33
33
  return {
34
34
  binder,
package/dist/angular.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createWindowManager, createDesktopBinder } from './chunk-FPZDAKNP.js';
1
+ import { createWindowManager, createDesktopBinder } from './chunk-GGDF2EWV.js';
2
2
  import { signal, computed, inject, DestroyRef } from '@angular/core';
3
3
 
4
4
  function onDestroy(dispose) {
@@ -209,6 +209,7 @@ function createWindowManager(options = {}) {
209
209
  let future = [];
210
210
  let interactionDepth = 0;
211
211
  let interactionRecorded = false;
212
+ let interactionMark = null;
212
213
  let skipNextHistory = false;
213
214
  let pendingHistory = null;
214
215
  const layouts = /* @__PURE__ */ new Map();
@@ -319,7 +320,13 @@ function createWindowManager(options = {}) {
319
320
  width: init.width ?? defaultSize.width,
320
321
  height: init.height ?? defaultSize.height
321
322
  };
322
- const size = aspectRatio === null ? clampSize(requested, minSize, maxSize) : applyAspect(requested, aspectRatio, minSize, maxSize, "width");
323
+ const size = aspectRatio === null ? clampSize(requested, minSize, maxSize) : applyAspect(
324
+ requested,
325
+ aspectRatio,
326
+ minSize,
327
+ maxSize,
328
+ init.width === void 0 && init.height !== void 0 ? "height" : "width"
329
+ );
323
330
  const position = positionForOpen(init);
324
331
  let bounds = { ...position, ...size };
325
332
  if (keepInViewport) bounds = clampToViewport(bounds, viewport, minVisible);
@@ -435,7 +442,13 @@ function createWindowManager(options = {}) {
435
442
  }
436
443
  function snapBounds(win, zone) {
437
444
  const rect = zoneBounds(zone, viewport);
438
- return { x: rect.x, y: rect.y, ...normalizeSize(rect, win) };
445
+ const byWidth = normalizeSize(rect, win);
446
+ const size = byWidth.height > rect.height ? normalizeSize(rect, win, "height") : byWidth;
447
+ return {
448
+ x: Math.max(0, Math.min(rect.x, viewport.width - size.width)),
449
+ y: Math.max(0, Math.min(rect.y, viewport.height - size.height)),
450
+ ...size
451
+ };
439
452
  }
440
453
  function focusUnlessBlocked(id) {
441
454
  const modal = topModalId();
@@ -600,7 +613,7 @@ function createWindowManager(options = {}) {
600
613
  snappable: patch.snappable ?? win.snappable,
601
614
  meta: patch.meta ? { ...win.meta, ...patch.meta } : win.meta
602
615
  };
603
- const size = normalizeSize(next.bounds, next);
616
+ const size = next.stage === "maximized" ? next.bounds : normalizeSize(next.bounds, next);
604
617
  const resized = size.width !== next.bounds.width || size.height !== next.bounds.height;
605
618
  const finalWin = resized ? { ...next, bounds: { ...next.bounds, ...size } } : next;
606
619
  setWindow(finalWin);
@@ -648,6 +661,10 @@ function createWindowManager(options = {}) {
648
661
  if (!onActiveWorkspace(updated) && focusedId === id) {
649
662
  focusTop();
650
663
  if (focusedId) emitFocus(windows[focusedId], id);
664
+ } else if (onActiveWorkspace(updated) && topModalId() === id && focusedId !== id) {
665
+ const previous = focusedId;
666
+ focusedId = id;
667
+ emitFocus(updated, previous);
651
668
  }
652
669
  queueEvent(() => emitter.emit("update", { window: updated }));
653
670
  commit();
@@ -775,14 +792,20 @@ function createWindowManager(options = {}) {
775
792
  }
776
793
  function beginInteraction() {
777
794
  interactionDepth += 1;
778
- if (interactionDepth === 1) interactionRecorded = false;
795
+ if (interactionDepth === 1) {
796
+ interactionRecorded = false;
797
+ interactionMark = { past: [...past], future };
798
+ }
779
799
  }
780
800
  function endInteraction() {
781
801
  if (interactionDepth > 0) interactionDepth -= 1;
802
+ if (interactionDepth === 0) interactionMark = null;
782
803
  }
783
804
  function abortInteraction() {
784
- if (interactionDepth > 0 && interactionRecorded) {
785
- past.pop();
805
+ if (interactionDepth > 0 && interactionMark) {
806
+ past.length = 0;
807
+ past.push(...interactionMark.past);
808
+ future = interactionMark.future;
786
809
  interactionRecorded = false;
787
810
  }
788
811
  endInteraction();
@@ -1130,10 +1153,8 @@ function createAnnouncer(wm, container, messages = {}) {
1130
1153
  element.style.cssText = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0";
1131
1154
  container.append(element);
1132
1155
  let clearTimer;
1133
- let lastMessage = "";
1134
1156
  let detailed = false;
1135
1157
  function announce(message) {
1136
- lastMessage = message;
1137
1158
  element.textContent = message;
1138
1159
  if (clearTimer !== void 0) clearTimeout(clearTimer);
1139
1160
  clearTimer = setTimeout(() => {
@@ -1141,8 +1162,14 @@ function createAnnouncer(wm, container, messages = {}) {
1141
1162
  }, 2e3);
1142
1163
  }
1143
1164
  const unsubscribers = [
1144
- wm.on("open", ({ window: win }) => announce(dict.opened(win.title))),
1145
- wm.on("close", ({ window: win }) => announce(dict.closed(win.title))),
1165
+ wm.on("open", ({ window: win }) => {
1166
+ announce(dict.opened(win.title));
1167
+ detailed = true;
1168
+ }),
1169
+ wm.on("close", ({ window: win }) => {
1170
+ announce(dict.closed(win.title));
1171
+ detailed = true;
1172
+ }),
1146
1173
  wm.on("stage", ({ window: win, previous }) => {
1147
1174
  if (win.stage === "minimized") announce(dict.minimized(win.title));
1148
1175
  else if (win.stage === "maximized") announce(dict.maximized(win.title));
@@ -1153,7 +1180,7 @@ function createAnnouncer(wm, container, messages = {}) {
1153
1180
  detailed = true;
1154
1181
  }),
1155
1182
  wm.on("focus", ({ window: win, previous }) => {
1156
- if (detailed || previous === null || lastMessage === dict.opened(win.title)) return;
1183
+ if (detailed || previous === null) return;
1157
1184
  announce(dict.focused(win.title));
1158
1185
  }),
1159
1186
  wm.on("workspace", ({ workspace }) => {
@@ -1400,6 +1427,7 @@ function createResizeStarter(ctx) {
1400
1427
  let pendingX = 0;
1401
1428
  let pendingY = 0;
1402
1429
  let hasPending = false;
1430
+ let applied = false;
1403
1431
  const el = ctx.windowElement(id);
1404
1432
  if (el) el.dataset.wmResizing = direction;
1405
1433
  function flush() {
@@ -1408,18 +1436,24 @@ function createResizeStarter(ctx) {
1408
1436
  raf = 0;
1409
1437
  const dx = pendingX - startPoint.x;
1410
1438
  const dy = pendingY - startPoint.y;
1411
- const top = direction.includes("n") ? Math.max(0, start.y + dy) : start.y;
1439
+ const floor = Math.min(0, start.y);
1440
+ const top = direction.includes("n") ? Math.max(floor, start.y + dy) : start.y;
1412
1441
  const raw = {
1413
1442
  width: direction.includes("e") ? start.width + dx : direction.includes("w") ? start.width - dx : start.width,
1414
1443
  height: direction.includes("s") ? start.height + dy : direction.includes("n") ? start.y + start.height - top : start.height
1415
1444
  };
1416
- const size = aspect === null ? clampSize(raw, minSize, maxSize) : applyAspect(raw, aspect, minSize, maxSize, drive);
1445
+ let size = aspect === null ? clampSize(raw, minSize, maxSize) : applyAspect(raw, aspect, minSize, maxSize, drive);
1446
+ const available = start.y + start.height - floor;
1447
+ if (direction.includes("n") && size.height > available) {
1448
+ size = applyAspect({ ...size, height: available }, aspect ?? 0, minSize, maxSize, "height");
1449
+ }
1417
1450
  const next = {
1418
1451
  x: direction.includes("w") ? start.x + start.width - size.width : start.x,
1419
1452
  y: direction.includes("n") ? start.y + start.height - size.height : start.y,
1420
1453
  ...size
1421
1454
  };
1422
1455
  wm.resize(id, next);
1456
+ applied = true;
1423
1457
  }
1424
1458
  function onMove(moveEvent) {
1425
1459
  if (moveEvent.pointerId !== event.pointerId) return;
@@ -1434,7 +1468,7 @@ function createResizeStarter(ctx) {
1434
1468
  try {
1435
1469
  if (raf !== 0) view.cancelAnimationFrame(raf);
1436
1470
  if (hasPending && !cancelled) flush();
1437
- if (cancelled) {
1471
+ if (cancelled && applied) {
1438
1472
  if (startStage === "snapped" && startZone) {
1439
1473
  wm.restoreTo(id, startRestore ?? start);
1440
1474
  wm.snap(id, startZone);
@@ -1504,6 +1538,7 @@ var SNAP_SHORTCUTS = {
1504
1538
  ArrowLeft: "left",
1505
1539
  ArrowRight: "right"
1506
1540
  };
1541
+ var LANDMARK_TAGS = /* @__PURE__ */ new Set(["header", "footer", "aside", "nav"]);
1507
1542
  var FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
1508
1543
  function attachDesktop(wm, element, options = {}) {
1509
1544
  const doc = element.ownerDocument;
@@ -1635,6 +1670,11 @@ function attachDesktop(wm, element, options = {}) {
1635
1670
  if (hide && !el.hidden && el.contains(doc.activeElement)) {
1636
1671
  element.focus({ preventScroll: true });
1637
1672
  }
1673
+ if (!hide && el.hidden && wm.getState().focusedId === win.id) {
1674
+ queueMicrotask(() => {
1675
+ if (!el.hidden && !el.contains(doc.activeElement)) el.focus({ preventScroll: true });
1676
+ });
1677
+ }
1638
1678
  el.hidden = hide;
1639
1679
  el.setAttribute("aria-label", win.title);
1640
1680
  if (win.layer === "modal") el.setAttribute("aria-modal", "true");
@@ -1683,6 +1723,7 @@ function attachDesktop(wm, element, options = {}) {
1683
1723
  wm.on("focus", ({ window: win }) => {
1684
1724
  const attached = registry.get(win.id);
1685
1725
  if (!attached) return;
1726
+ if (attached.element.hidden) syncAll();
1686
1727
  if (!attached.element.contains(doc.activeElement)) {
1687
1728
  attached.element.focus({ preventScroll: true });
1688
1729
  }
@@ -1718,6 +1759,7 @@ function attachDesktop(wm, element, options = {}) {
1718
1759
  return;
1719
1760
  }
1720
1761
  if (!event.ctrlKey && !event.metaKey) return;
1762
+ if (drag) return;
1721
1763
  const target = event.target;
1722
1764
  if (target?.closest(INTERACTIVE_SELECTOR)) return;
1723
1765
  if (historyShortcuts && (event.key === "z" || event.key === "Z")) {
@@ -1778,6 +1820,9 @@ function attachDesktop(wm, element, options = {}) {
1778
1820
  }
1779
1821
  const handle = typeof windowOptions.handle === "string" ? windowElement.querySelector(windowOptions.handle) : windowOptions.handle ?? windowElement.querySelector("[data-wm-drag]");
1780
1822
  attached.handle = handle;
1823
+ if (handle && !handle.hasAttribute("role") && LANDMARK_TAGS.has(handle.localName)) {
1824
+ handle.setAttribute("role", "presentation");
1825
+ }
1781
1826
  const onPointerDownFocus = () => {
1782
1827
  wm.focus(id);
1783
1828
  };
@@ -1983,5 +2028,5 @@ function createDesktopBinder(wm, options = {}) {
1983
2028
  }
1984
2029
 
1985
2030
  export { applyAspect, attachDesktop, boundsEqual, clamp, clampSize, clampToViewport, createAnnouncer, createDesktopBinder, createEmitter, createWindowManager, defaultMessages, detectSnapZone, flipFromTarget, flipToTarget, magnetize, prefersReducedMotion, zoneBounds };
1986
- //# sourceMappingURL=chunk-FPZDAKNP.js.map
1987
- //# sourceMappingURL=chunk-FPZDAKNP.js.map
2031
+ //# sourceMappingURL=chunk-GGDF2EWV.js.map
2032
+ //# sourceMappingURL=chunk-GGDF2EWV.js.map