@surdeddd/wmkit 0.9.1 → 0.10.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.
package/README.md CHANGED
@@ -14,6 +14,7 @@
14
14
 
15
15
  - 🪟 **Full window lifecycle** — open, close, focus, minimize, maximize, restore, drag, 8-direction resize
16
16
  - 🧠 **Headless core** — a serializable state machine plus a DOM controller; bring your own markup or use the glass theme
17
+ - 🧱 **Your window, your markup** — build the whole frame from a template string, wire its buttons by attribute, and swap the skin on a live window without losing what it was showing
17
18
  - ⚛️ **Official adapters** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, all thin sugar over one core
18
19
  - 🗃️ **Tab groups** — drop one titlebar onto another and the windows share a frame, dockview style; the tab order is yours to reorder
19
20
  - ⊞ **Snap zones** — halves, quarters, thirds and drag-to-top maximize with a live preview
@@ -26,7 +27,7 @@
26
27
  - 💾 **Persistence** — one call to serialize the desktop, one call to restore it, with versioned migrations
27
28
  - 🎨 **Sixteen themes** — glass, light, retro, terminal, paper, neon, aqua, frost, candy, carbon, brutalist, blueprint, amber, noir, forest, synth — or bring your own CSS
28
29
  - 🖼️ **Popout** *(experimental)* — send a window into Document Picture-in-Picture
29
- - 📦 **Zero dependencies**, strict TypeScript, ESM + CJS, ~10.7 kB brotli core
30
+ - 📦 **Zero dependencies**, strict TypeScript, ESM + CJS, under 15 kB brotli core
30
31
 
31
32
  ## Install
32
33
 
@@ -328,7 +329,7 @@ Moves a window's content into a [Document Picture-in-Picture](https://developer.
328
329
 
329
330
  ### `pinch(options?)`, `swipe(options?)`, `touchGestures(options?)` — `@surdeddd/wmkit/gestures`
330
331
 
331
- Two-finger gestures, opt-in so the core never pays for them: pinch a window to resize it around the point between your fingers, swipe two fingers sideways to change workspace. 1.46 kB brotlied.
332
+ Two-finger gestures, opt-in so the core never pays for them: pinch a window to resize it around the point between your fingers, swipe two fingers sideways to change workspace. 1.47 kB brotlied.
332
333
 
333
334
  ```js
334
335
  import { attachDesktop } from '@surdeddd/wmkit'
@@ -341,7 +342,7 @@ A gesture is just a function that takes a `GestureContext` and returns its teard
341
342
 
342
343
  ### `skin(spec)`, `defaultSkin`, `barebones` — `@surdeddd/wmkit/chrome`
343
344
 
344
- Builds the window itself from a markup string, so a consumer can ship their own chrome and their own buttons without writing a single listener. 802 B brotlied.
345
+ Builds the window itself from a markup string, so a consumer can ship their own chrome and their own buttons without writing a single listener. 1.02 kB brotlied.
345
346
 
346
347
  ```js
347
348
  import { attachDesktop } from '@surdeddd/wmkit'
@@ -394,9 +395,17 @@ Rows are patched in place rather than re-rendered, so keyboard focus inside the
394
395
  }
395
396
  ```
396
397
 
397
- Fifteen more ready-made themes ship alongside — `light`, `retro`, `terminal`, `paper`, `neon`, `aqua`, `frost`, `candy`, `carbon`, `brutalist`, `blueprint`, `amber`, `noir`, `forest` and `synth`. They all style the same `data-wm-*` attributes, so switching is a one-line import swap, and every one of them ships 24px pointer targets plus `prefers-reduced-motion` and `forced-colors` blocks.
398
+ Fifteen more ready-made themes ship alongside — `light`, `retro`, `terminal`, `paper`, `neon`, `aqua`, `frost`, `candy`, `carbon`, `brutalist`, `blueprint`, `amber`, `noir`, `forest` and `synth`. They all style the same `data-wm-*` attributes, so switching is a one-line import swap, and every one of them ships 24px pointer targets plus `prefers-reduced-motion` and `forced-colors` blocks. Every one is also held to WCAG AA contrast by a browser test that measures the rendered pixels of the demo under all sixteen.
398
399
 
399
- A theme dresses every window; a variant dresses one. Set `meta.variant` on a window and the desktop mirrors it to `data-wm-variant` for your CSS to pick up — see [docs/theming.md](docs/theming.md).
400
+ Three layers, from the widest to the narrowest:
401
+
402
+ | layer | reaches | changes |
403
+ | --- | --- | --- |
404
+ | theme | every window | the tokens |
405
+ | variant | one window | the tokens, through `data-wm-variant` |
406
+ | skin | one window | the markup itself |
407
+
408
+ A theme and a variant repaint what is there; a skin replaces it. Set `meta.variant` for the first two and `meta.skin` for the third — see [docs/theming.md](docs/theming.md).
400
409
 
401
410
  Skip the import entirely and the library stays headless: state attributes (`data-wm-stage`, `data-wm-focused`, `data-wm-dragging`, `data-wm-flash`, `[hidden]`) are yours to style.
402
411
 
@@ -413,7 +422,7 @@ This README is the tour. The reference lives in [`docs/`](./docs/README.md):
413
422
  | [API reference](./docs/api.md) | every export, option, method, event and type |
414
423
  | [Adapters](./docs/adapters.md) | complete React, Vue, Svelte, Solid and Angular integrations |
415
424
  | [Theming](./docs/theming.md) | `data-wm-*` contract, CSS variables, writing a theme from scratch |
416
- | [Recipes](./docs/recipes.md) | taskbar, modals, persistence, workspaces, SSR, testing, performance |
425
+ | [Recipes](./docs/recipes.md) | building a window from scratch, taskbar, modals, persistence, workspaces, SSR, testing, performance |
417
426
  | [Browser support](./docs/browser-support.md) | baselines per entry point and what degrades where |
418
427
 
419
428
  ## Comparison
@@ -434,8 +443,8 @@ This README is the tour. The reference lives in [`docs/`](./docs/README.md):
434
443
 
435
444
  ## Quality
436
445
 
437
- - 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
438
- - 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
446
+ - 565 unit tests: **100%** line/branch/function/statement coverage on the core state machine, the chrome plugin and persistence, with enforced floors on the DOM layer and the adapters
447
+ - 550+ Playwright scenarios on Chromium, Firefox, 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
439
448
  - 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
440
449
  - `publint` + `@arethetypeswrong/cli` validate the published package, `size-limit` guards bundle budgets
441
450
 
package/README.ru.md CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  - 🪟 **Полный жизненный цикл окна** — открытие, закрытие, фокус, сворачивание, разворачивание, восстановление, drag, ресайз в 8 направлениях
12
12
  - 🧠 **Headless-ядро** — сериализуемая стейт-машина плюс DOM-контроллер; своя разметка или готовая стеклянная тема
13
+ - 🧱 **Своё окно, своя разметка** — соберите весь корпус из строки-шаблона, повесьте кнопки атрибутами и меняйте скин на живом окне, не теряя того, что оно показывало
13
14
  - ⚛️ **Родные адаптеры** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, тонкий сахар над одним ядром
14
15
  - 🗃️ **Вкладочные группы** — бросьте заголовок на заголовок, и окна начнут делить одну рамку; порядок вкладок можно менять
15
16
  - ⊞ **Snap-зоны** — половины, четверти, трети и максимизация от верхнего края с живым превью
@@ -22,7 +23,7 @@
22
23
  - 💾 **Персист** — один вызов сериализует рабочий стол, один — восстанавливает, с версионированием и миграциями
23
24
  - 🎨 **Шестнадцать тем** — glass, light, retro, terminal, paper, neon, aqua, frost, candy, carbon, brutalist, blueprint, amber, noir, forest, synth — либо полностью свой CSS
24
25
  - 🖼️ **Popout** *(experimental)* — вынос окна в Document Picture-in-Picture
25
- - 📦 **Ноль зависимостей**, строгий TypeScript, ESM + CJS, ~10.7 kB brotli
26
+ - 📦 **Ноль зависимостей**, строгий TypeScript, ESM + CJS, ядро меньше 15 кБ в brotli
26
27
 
27
28
  ## Установка
28
29
 
@@ -277,7 +278,7 @@ attachDesktop(wm, root, { gestures: [touchGestures({ swipe: { workspaces: 4 } })
277
278
 
278
279
  ### `skin(spec)`, `defaultSkin`, `barebones` — `@surdeddd/wmkit/chrome`
279
280
 
280
- Собирает само окно из строки разметки: можно принести свою обвязку и свои кнопки, не написав ни одного обработчика. 802 Б в brotli.
281
+ Собирает само окно из строки разметки: можно принести свою обвязку и свои кнопки, не написав ни одного обработчика. 1.02 кБ в brotli.
281
282
 
282
283
  ```js
283
284
  import { attachDesktop } from '@surdeddd/wmkit'
@@ -323,7 +324,17 @@ panel.destroy()
323
324
 
324
325
  Ещё пятнадцать готовых тем: `light`, `retro`, `terminal`, `paper`, `neon`, `aqua`, `frost`, `candy`, `carbon`, `brutalist`, `blueprint`, `amber`, `noir`, `forest`, `synth`. Все стилизуют одни и те же `data-wm-*` атрибуты — переключение = замена одного импорта, и у каждой есть 24px тач-цели плюс блоки `prefers-reduced-motion` и `forced-colors`.
325
326
 
326
- Тема одевает все окна, вариант одно. Задайте `meta.variant` у окна, и десктоп отзеркалит его в `data-wm-variant` для вашего CSS подробности в [docs/theming.md](docs/theming.md).
327
+ Каждая тема к тому же держит контраст WCAG AA: браузерный тест меряет отрендеренные пиксели демо под всеми шестнадцатью и падает ниже 4.5:1.
328
+
329
+ Три слоя, от самого широкого к самому узкому:
330
+
331
+ | слой | достаёт до | меняет |
332
+ | --- | --- | --- |
333
+ | тема | всех окон | токены |
334
+ | вариант | одного окна | токены, через `data-wm-variant` |
335
+ | скин | одного окна | саму разметку |
336
+
337
+ Тема и вариант перекрашивают то, что есть; скин это заменяет. Для первых двух — `meta.variant`, для третьего — `meta.skin`, подробности в [docs/theming.md](docs/theming.md).
327
338
 
328
339
  ## SSR
329
340
 
@@ -331,8 +342,8 @@ panel.destroy()
331
342
 
332
343
  ## Качество
333
344
 
334
- - 250 юнит-тестов: **100%** покрытие стейт-машины и persist по строкам/веткам/функциям, плюс обязательные пороги на DOM-слой и адаптеры
335
- - 190+ Playwright-сценариев на Chromium, WebKit и мобильной эмуляции: drag, ресайз во все стороны, снэп, магнетизм, рабочие столы, undo после drag, клавиатура, touch, персист через перезагрузку, стресс на 50 окон, модальные ловушки, axe-аудиты доступности, визуальная регрессия по скриншотам
345
+ - 565 юнит-тестов: **100%** покрытие стейт-машины, chrome-плагина и persist по строкам/веткам/функциям, плюс обязательные пороги на DOM-слой и адаптеры
346
+ - 550+ Playwright-сценариев на Chromium, Firefox, WebKit и мобильной эмуляции: drag, ресайз во все стороны, снэп, магнетизм, рабочие столы, undo после drag, клавиатура, touch, персист через перезагрузку, стресс на 50 окон, модальные ловушки, axe-аудиты доступности, визуальная регрессия по скриншотам
336
347
  - перф-бенчмарки в CI на каждый push (`vitest bench`): 1 000 окон открываются за ~150 мс, move среди 50 окон ~1.2 мкс, полный undo/redo-проход на 100 шагов ~52 мкс
337
348
  - `publint` + `@arethetypeswrong/cli` проверяют валидность пакета, `size-limit` следит за бюджетами
338
349
 
package/dist/angular.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkZWUVWRUS_cjs = require('./chunk-ZWUVWRUS.cjs');
3
+ var chunkVHS2YX3A_cjs = require('./chunk-VHS2YX3A.cjs');
4
4
  require('./chunk-BDHFXEJN.cjs');
5
5
  var core = require('@angular/core');
6
6
 
@@ -14,7 +14,7 @@ function onDestroy(dispose) {
14
14
  destroyRef?.onDestroy(dispose);
15
15
  }
16
16
  function useWindowManager(options) {
17
- const wm = chunkZWUVWRUS_cjs.createWindowManager(options);
17
+ const wm = chunkVHS2YX3A_cjs.createWindowManager(options);
18
18
  onDestroy(() => wm.destroy());
19
19
  return wm;
20
20
  }
@@ -29,7 +29,7 @@ function useWmWindow(wm, id) {
29
29
  return core.computed(() => state().windows[id]);
30
30
  }
31
31
  function createDesktop(wm, options) {
32
- const binder = chunkZWUVWRUS_cjs.createDesktopBinder(wm, options);
32
+ const binder = chunkVHS2YX3A_cjs.createDesktopBinder(wm, options);
33
33
  onDestroy(() => binder.destroy());
34
34
  return {
35
35
  binder,
package/dist/angular.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createWindowManager, createDesktopBinder } from './chunk-TUGQX3CX.js';
1
+ import { createWindowManager, createDesktopBinder } from './chunk-V7OTLDVK.js';
2
2
  import './chunk-5XVIC5LH.js';
3
3
  import { signal, computed, inject, DestroyRef } from '@angular/core';
4
4
 
@@ -1856,6 +1856,14 @@ var SNAP_ZONES = /* @__PURE__ */ new Set([
1856
1856
  ]);
1857
1857
  var LANDMARK_TAGS = /* @__PURE__ */ new Set(["header", "footer", "aside", "nav"]);
1858
1858
  var FOCUSABLE_SELECTOR = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
1859
+ function firedOn(event) {
1860
+ const path = event.composedPath();
1861
+ const first = path.length > 0 ? path[0] : event.target;
1862
+ return first instanceof Element ? first : null;
1863
+ }
1864
+ function findInWindow(el, selector) {
1865
+ return el.querySelector(selector) ?? el.shadowRoot?.querySelector(selector) ?? null;
1866
+ }
1859
1867
  function focusablesOf(el) {
1860
1868
  const root = el.shadowRoot;
1861
1869
  if (!root) return [...el.querySelectorAll(FOCUSABLE_SELECTOR)];
@@ -1875,8 +1883,15 @@ function focusablesOf(el) {
1875
1883
  function dragHandleAt(node, clientX, clientY) {
1876
1884
  const handle = node.closest?.("[data-wm-drag]");
1877
1885
  if (handle) return handle;
1878
- const inner = node.shadowRoot?.elementFromPoint?.(clientX, clientY) ?? null;
1879
- return inner === null || inner === node ? null : dragHandleAt(inner, clientX, clientY);
1886
+ const root = node.shadowRoot;
1887
+ if (!root) return null;
1888
+ for (const candidate of root.querySelectorAll("[data-wm-drag]")) {
1889
+ const box = candidate.getBoundingClientRect();
1890
+ if (clientX >= box.left && clientX <= box.right && clientY >= box.top && clientY <= box.bottom) {
1891
+ return candidate;
1892
+ }
1893
+ }
1894
+ return null;
1880
1895
  }
1881
1896
  function windowElementOf(node) {
1882
1897
  const found = node.closest("[data-wm-window]");
@@ -2117,7 +2132,24 @@ function attachDesktop(wm, element, options = {}) {
2117
2132
  restack(stackTarget, { base: zIndexBase, gap: zIndexGap });
2118
2133
  stackRows.length = length;
2119
2134
  }
2135
+ let syncing = false;
2136
+ let syncAgain = false;
2120
2137
  function syncAll() {
2138
+ if (syncing) {
2139
+ syncAgain = true;
2140
+ return;
2141
+ }
2142
+ syncing = true;
2143
+ try {
2144
+ do {
2145
+ syncAgain = false;
2146
+ syncPass();
2147
+ } while (syncAgain);
2148
+ } finally {
2149
+ syncing = false;
2150
+ }
2151
+ }
2152
+ function syncPass() {
2121
2153
  const state = wm.getState();
2122
2154
  const orderChanged = state.order !== lastOrder;
2123
2155
  let restyle = null;
@@ -2141,7 +2173,7 @@ function attachDesktop(wm, element, options = {}) {
2141
2173
  const attached = registry.get(id);
2142
2174
  if (attached && attached.skin !== wanted2) buildSkin(id, wanted2, attached.mountOptions);
2143
2175
  }
2144
- syncAll();
2176
+ syncAgain = true;
2145
2177
  return;
2146
2178
  }
2147
2179
  if (orderChanged) applyStacking(state.order);
@@ -2200,7 +2232,7 @@ function attachDesktop(wm, element, options = {}) {
2200
2232
  }
2201
2233
  if (!event.ctrlKey && !event.metaKey) return;
2202
2234
  if (drag) return;
2203
- const target = event.target;
2235
+ const target = firedOn(event);
2204
2236
  if (target?.closest(interactiveSelector)) return;
2205
2237
  if (historyShortcuts && (event.key === "z" || event.key === "Z")) {
2206
2238
  event.preventDefault();
@@ -2231,6 +2263,7 @@ function attachDesktop(wm, element, options = {}) {
2231
2263
  element.addEventListener("keydown", onDesktopKeydown);
2232
2264
  cleanup.push(() => element.removeEventListener("keydown", onDesktopKeydown));
2233
2265
  }
2266
+ let rebuilding = false;
2234
2267
  function endDrag(cancelled) {
2235
2268
  if (drag) drag.finish(cancelled);
2236
2269
  }
@@ -2260,12 +2293,12 @@ function attachDesktop(wm, element, options = {}) {
2260
2293
  windowElement.style.left = "0";
2261
2294
  windowElement.style.top = "0";
2262
2295
  const lightTitle = windowElement.querySelector("[data-wm-title]");
2263
- attached.title = lightTitle ?? windowElement.shadowRoot?.querySelector("[data-wm-title]") ?? null;
2296
+ attached.title = lightTitle ?? findInWindow(windowElement, "[data-wm-title]");
2264
2297
  if (lightTitle) {
2265
2298
  if (!lightTitle.id) lightTitle.id = `wmkit-title-${id}`;
2266
2299
  windowElement.setAttribute("aria-labelledby", lightTitle.id);
2267
2300
  }
2268
- const handle = typeof windowOptions.handle === "string" ? windowElement.querySelector(windowOptions.handle) : windowOptions.handle ?? windowElement.querySelector("[data-wm-drag]");
2301
+ const handle = typeof windowOptions.handle === "string" ? findInWindow(windowElement, windowOptions.handle) : windowOptions.handle ?? findInWindow(windowElement, "[data-wm-drag]");
2269
2302
  attached.handle = handle;
2270
2303
  if (handle && !handle.hasAttribute("role") && LANDMARK_TAGS.has(handle.localName)) {
2271
2304
  handle.setAttribute("role", "presentation");
@@ -2278,7 +2311,7 @@ function attachDesktop(wm, element, options = {}) {
2278
2311
  () => windowElement.removeEventListener("pointerdown", onPointerDownFocus, true)
2279
2312
  );
2280
2313
  const onClick = (event) => {
2281
- const target = event.target;
2314
+ const target = firedOn(event);
2282
2315
  if (!target) return;
2283
2316
  const current = wm.get(id);
2284
2317
  if (!current) return;
@@ -2326,7 +2359,7 @@ function attachDesktop(wm, element, options = {}) {
2326
2359
  handle.addEventListener("pointerdown", onHandleDown);
2327
2360
  attached.cleanup.push(() => handle.removeEventListener("pointerdown", onHandleDown));
2328
2361
  const onDoubleClick = (event) => {
2329
- const target = event.target;
2362
+ const target = firedOn(event);
2330
2363
  if (target?.closest(interactiveSelector)) return;
2331
2364
  const current = wm.get(id);
2332
2365
  if (current?.maximizable) wm.toggleMaximize(id);
@@ -2358,7 +2391,7 @@ function attachDesktop(wm, element, options = {}) {
2358
2391
  }
2359
2392
  }
2360
2393
  const onWindowKeydown = (event) => {
2361
- const target = event.target;
2394
+ const target = firedOn(event);
2362
2395
  if (target?.closest(interactiveSelector)) return;
2363
2396
  const current = wm.get(id);
2364
2397
  if (!current) return;
@@ -2418,7 +2451,7 @@ function attachDesktop(wm, element, options = {}) {
2418
2451
  const detach = () => {
2419
2452
  if (detached) return;
2420
2453
  detached = true;
2421
- if (drag?.id === id) endDrag(true);
2454
+ if (drag?.id === id) endDrag(!rebuilding);
2422
2455
  for (const dispose of attached.cleanup) dispose();
2423
2456
  for (const resizeHandle of attached.handles) resizeHandle.remove();
2424
2457
  registry.delete(id);
@@ -2454,15 +2487,21 @@ function attachDesktop(wm, element, options = {}) {
2454
2487
  function buildSkin(id, skin, windowOptions) {
2455
2488
  const win = wm.get(id);
2456
2489
  if (!win) throw new Error(`wmkit: cannot mount unknown window "${id}"`);
2490
+ const build = resolveSkin(skin);
2457
2491
  const standing = registry.get(id);
2458
2492
  const previous = standing?.mount ?? null;
2459
- const carried = previous === null ? [] : [...previous.content.childNodes];
2493
+ const carried = previous === null || previous.content.ownerDocument !== doc ? [] : [...previous.content.childNodes];
2460
2494
  if (previous !== null) {
2461
2495
  registry.delete(id);
2462
- standing?.release?.();
2496
+ rebuilding = true;
2497
+ try {
2498
+ standing?.release?.();
2499
+ } finally {
2500
+ rebuilding = false;
2501
+ }
2463
2502
  previous.element.remove();
2464
2503
  }
2465
- const mount = resolveSkin(skin)({ doc, id, window: win, actions: createActions(wm, id) });
2504
+ const mount = build({ doc, id, window: win, actions: createActions(wm, id) });
2466
2505
  element.append(mount.element);
2467
2506
  const release = attachWindow(id, mount.element, windowOptions);
2468
2507
  const attached = registry.get(id);
@@ -2474,10 +2513,14 @@ function attachDesktop(wm, element, options = {}) {
2474
2513
  previous?.destroy?.();
2475
2514
  return mount;
2476
2515
  }
2477
- function mountWindow(id, skin, windowOptions = {}) {
2516
+ function mountWindow(id, skin, options2 = {}) {
2517
+ const windowOptions = { removeOnClose: true, ...options2 };
2478
2518
  let last = buildSkin(id, skin, windowOptions);
2519
+ let spent = false;
2479
2520
  const live = () => {
2480
- last = registry.get(id)?.mount ?? last;
2521
+ if (spent) return last;
2522
+ const now = registry.get(id)?.mount;
2523
+ if (now) last = now;
2481
2524
  return last;
2482
2525
  };
2483
2526
  return {
@@ -2488,8 +2531,10 @@ function attachDesktop(wm, element, options = {}) {
2488
2531
  return live().content;
2489
2532
  },
2490
2533
  detach() {
2534
+ if (spent) return;
2491
2535
  const mount = live();
2492
- registry.get(id)?.release?.();
2536
+ spent = true;
2537
+ if (registry.get(id)?.mount === mount) registry.get(id)?.release?.();
2493
2538
  mount.element.remove();
2494
2539
  mount.destroy?.();
2495
2540
  }
@@ -2506,6 +2551,10 @@ function attachDesktop(wm, element, options = {}) {
2506
2551
  for (const [, attached] of registry) {
2507
2552
  for (const dispose of attached.cleanup) dispose();
2508
2553
  for (const resizeHandle of attached.handles) resizeHandle.remove();
2554
+ if (attached.mount) {
2555
+ attached.mount.element.remove();
2556
+ attached.mount.destroy?.();
2557
+ }
2509
2558
  }
2510
2559
  registry.clear();
2511
2560
  for (const dispose of cleanup) dispose();
@@ -2571,5 +2620,5 @@ function createDesktopBinder(wm, options = {}) {
2571
2620
  }
2572
2621
 
2573
2622
  export { attachDesktop, createAnnouncer, createDesktopBinder, createEmitter, createWindowManager, defaultMessages, flipFromTarget, flipToTarget, prefersReducedMotion };
2574
- //# sourceMappingURL=chunk-TUGQX3CX.js.map
2575
- //# sourceMappingURL=chunk-TUGQX3CX.js.map
2623
+ //# sourceMappingURL=chunk-V7OTLDVK.js.map
2624
+ //# sourceMappingURL=chunk-V7OTLDVK.js.map