@surdeddd/wmkit 0.6.1 → 0.7.1

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 (41) hide show
  1. package/README.md +1 -1
  2. package/README.ru.md +1 -1
  3. package/dist/angular.cjs +3 -3
  4. package/dist/angular.d.cts +2 -2
  5. package/dist/angular.d.ts +2 -2
  6. package/dist/angular.js +1 -1
  7. package/dist/{binder-CkmOoo1U.d.ts → binder-DvQFmO3K.d.ts} +1 -1
  8. package/dist/{binder-CQzQJdVj.d.cts → binder-_L3Iww0u.d.cts} +1 -1
  9. package/dist/{chunk-DYZQY3BE.cjs → chunk-2VRCYMKH.cjs} +70 -24
  10. package/dist/chunk-2VRCYMKH.cjs.map +1 -0
  11. package/dist/{chunk-3HLKVOSI.js → chunk-WOCAQEM6.js} +70 -24
  12. package/dist/chunk-WOCAQEM6.js.map +1 -0
  13. package/dist/index.cjs +18 -18
  14. package/dist/index.d.cts +4 -4
  15. package/dist/index.d.ts +4 -4
  16. package/dist/index.js +1 -1
  17. package/dist/persist.d.cts +1 -1
  18. package/dist/persist.d.ts +1 -1
  19. package/dist/popout.d.cts +1 -1
  20. package/dist/popout.d.ts +1 -1
  21. package/dist/react.cjs +3 -3
  22. package/dist/react.d.cts +2 -2
  23. package/dist/react.d.ts +2 -2
  24. package/dist/react.js +1 -1
  25. package/dist/solid.cjs +3 -3
  26. package/dist/solid.d.cts +2 -2
  27. package/dist/solid.d.ts +2 -2
  28. package/dist/solid.js +1 -1
  29. package/dist/svelte.cjs +3 -3
  30. package/dist/svelte.d.cts +2 -2
  31. package/dist/svelte.d.ts +2 -2
  32. package/dist/svelte.js +1 -1
  33. package/dist/{types-LP7Inhxx.d.cts → types-jG2lHGcA.d.cts} +2 -0
  34. package/dist/{types-LP7Inhxx.d.ts → types-jG2lHGcA.d.ts} +2 -0
  35. package/dist/vue.cjs +3 -3
  36. package/dist/vue.d.cts +2 -2
  37. package/dist/vue.d.ts +2 -2
  38. package/dist/vue.js +1 -1
  39. package/package.json +1 -1
  40. package/dist/chunk-3HLKVOSI.js.map +0 -1
  41. package/dist/chunk-DYZQY3BE.cjs.map +0 -1
package/README.md CHANGED
@@ -15,7 +15,7 @@
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
17
  - ⚛️ **Official adapters** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, all thin sugar over one core
18
- - 🗃️ **Tab groups** — drop one titlebar onto another and the windows share a frame, dockview style
18
+ - 🗃️ **Tab groups** — drop one titlebar onto another and the windows share a frame, dockview style; the tab order is yours to reorder
19
19
  - ⊞ **Snap zones** — halves, quarters, thirds and drag-to-top maximize with a live preview
20
20
  - 🧲 **Magnetism** — window edges align to neighbours and the viewport while dragging
21
21
  - ↩️ **Undo/redo** — every mutation is one step; a whole drag collapses into a single history entry
package/README.ru.md CHANGED
@@ -11,7 +11,7 @@
11
11
  - 🪟 **Полный жизненный цикл окна** — открытие, закрытие, фокус, сворачивание, разворачивание, восстановление, drag, ресайз в 8 направлениях
12
12
  - 🧠 **Headless-ядро** — сериализуемая стейт-машина плюс DOM-контроллер; своя разметка или готовая стеклянная тема
13
13
  - ⚛️ **Родные адаптеры** — `@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, тонкий сахар над одним ядром
14
- - 🗃️ **Вкладочные группы** — бросьте заголовок на заголовок, и окна начнут делить одну рамку
14
+ - 🗃️ **Вкладочные группы** — бросьте заголовок на заголовок, и окна начнут делить одну рамку; порядок вкладок можно менять
15
15
  - ⊞ **Snap-зоны** — половины, четверти, трети и максимизация от верхнего края с живым превью
16
16
  - 🧲 **Магнетизм** — края окна прилипают к соседям и вьюпорту при перетаскивании
17
17
  - ↩️ **Undo/redo** — каждая мутация = один шаг; целый drag схлопывается в одну запись истории
package/dist/angular.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkDYZQY3BE_cjs = require('./chunk-DYZQY3BE.cjs');
3
+ var chunk2VRCYMKH_cjs = require('./chunk-2VRCYMKH.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 = chunkDYZQY3BE_cjs.createWindowManager(options);
16
+ const wm = chunk2VRCYMKH_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 = chunkDYZQY3BE_cjs.createDesktopBinder(wm, options);
31
+ const binder = chunk2VRCYMKH_cjs.createDesktopBinder(wm, options);
32
32
  onDestroy(() => binder.destroy());
33
33
  return {
34
34
  binder,
@@ -1,6 +1,6 @@
1
1
  import { Signal } from '@angular/core';
2
- import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-LP7Inhxx.cjs';
3
- import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-CQzQJdVj.cjs';
2
+ import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-jG2lHGcA.cjs';
3
+ import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-_L3Iww0u.cjs';
4
4
 
5
5
  declare function useWindowManager(options?: ManagerOptions): WindowManager;
6
6
  declare function useWmState(wm: WindowManager): Signal<ManagerState>;
package/dist/angular.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Signal } from '@angular/core';
2
- import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-LP7Inhxx.js';
3
- import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-CkmOoo1U.js';
2
+ import { W as WindowManager, M as ManagerOptions, a as ManagerState, b as WindowState } from './types-jG2lHGcA.js';
3
+ import { D as DesktopBinder, W as WindowAttachOptions, a as DesktopOptions } from './binder-DvQFmO3K.js';
4
4
 
5
5
  declare function useWindowManager(options?: ManagerOptions): WindowManager;
6
6
  declare function useWmState(wm: WindowManager): Signal<ManagerState>;
package/dist/angular.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createWindowManager, createDesktopBinder } from './chunk-3HLKVOSI.js';
1
+ import { createWindowManager, createDesktopBinder } from './chunk-WOCAQEM6.js';
2
2
  import { signal, computed, inject, DestroyRef } from '@angular/core';
3
3
 
4
4
  function onDestroy(dispose) {
@@ -1,4 +1,4 @@
1
- import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-LP7Inhxx.js';
1
+ import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-jG2lHGcA.js';
2
2
 
3
3
  declare function clamp(value: number, min: number, max: number): number;
4
4
  declare function clampSize(size: Size, min: Size, max: Size | null): Size;
@@ -1,4 +1,4 @@
1
- import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-LP7Inhxx.cjs';
1
+ import { f as Size, B as Bounds, g as SnapZone, W as WindowManager, b as WindowState } from './types-jG2lHGcA.cjs';
2
2
 
3
3
  declare function clamp(value: number, min: number, max: number): number;
4
4
  declare function clampSize(size: Size, min: Size, max: Size | null): Size;
@@ -285,8 +285,7 @@ function createWindowManager(options = {}) {
285
285
  }
286
286
  function blockOf(id) {
287
287
  const win = windows[id];
288
- if (win.groupId === null) return [id];
289
- return [...membersOf(win.groupId).filter((entry) => entry !== id), id];
288
+ return win.groupId === null ? [id] : membersOf(win.groupId);
290
289
  }
291
290
  function raise(id) {
292
291
  const win = windows[id];
@@ -895,6 +894,37 @@ function createWindowManager(options = {}) {
895
894
  commit();
896
895
  return true;
897
896
  }
897
+ function moveTab(id, index) {
898
+ const win = windows[id];
899
+ if (!win?.groupId || !Number.isFinite(index)) return false;
900
+ const members = membersOf(win.groupId);
901
+ const from = members.indexOf(id);
902
+ const target = Math.max(0, Math.min(members.length - 1, Math.trunc(index)));
903
+ if (from === -1 || from === target) return false;
904
+ const reordered = [...members];
905
+ reordered.splice(from, 1);
906
+ reordered.splice(target, 0, id);
907
+ const next = [...order];
908
+ let cursor = 0;
909
+ for (let i = 0; i < next.length; i += 1) {
910
+ if (!members.includes(next[i])) continue;
911
+ next[i] = reordered[cursor++];
912
+ }
913
+ order = next;
914
+ emitGroup(win.groupId, null);
915
+ queueEvent(() => emitter.emit("order", { order }));
916
+ commit();
917
+ return true;
918
+ }
919
+ function cycleTab(direction = 1) {
920
+ const win = focusedId ? windows[focusedId] : void 0;
921
+ if (!win?.groupId) return null;
922
+ const members = membersOf(win.groupId);
923
+ const current = members.indexOf(activeTabs[win.groupId]);
924
+ const index = (current + direction + members.length) % members.length;
925
+ const next = members[index];
926
+ return activateTab(next) ? next : null;
927
+ }
898
928
  function groupMembers(groupId) {
899
929
  return membersOf(groupId).map((id) => windows[id]);
900
930
  }
@@ -997,6 +1027,7 @@ function createWindowManager(options = {}) {
997
1027
  }
998
1028
  }
999
1029
  function applyEntry(entry) {
1030
+ const previousWindows = windows;
1000
1031
  windows = entry.windows;
1001
1032
  order = [...entry.order];
1002
1033
  modalCount = countModals();
@@ -1008,6 +1039,16 @@ function createWindowManager(options = {}) {
1008
1039
  if (workspace !== previousWorkspace) {
1009
1040
  queueEvent(() => emitter.emit("workspace", { workspace, previous: previousWorkspace }));
1010
1041
  }
1042
+ for (const id of Object.keys(previousWindows)) {
1043
+ if (windows[id]) continue;
1044
+ const closed = previousWindows[id];
1045
+ queueEvent(() => emitter.emit("close", { window: closed }));
1046
+ }
1047
+ for (const id of order) {
1048
+ if (previousWindows[id]) continue;
1049
+ const opened = windows[id];
1050
+ queueEvent(() => emitter.emit("open", { window: opened }));
1051
+ }
1011
1052
  reflowViewport();
1012
1053
  commit();
1013
1054
  }
@@ -1303,6 +1344,8 @@ function createWindowManager(options = {}) {
1303
1344
  group: (ids) => transact(() => group(ids)),
1304
1345
  ungroup: (id) => transact(() => ungroup(id)),
1305
1346
  activateTab: (id) => transact(() => activateTab(id)),
1347
+ moveTab: (id, index) => transact(() => moveTab(id, index)),
1348
+ cycleTab: (direction) => transact(() => cycleTab(direction)),
1306
1349
  groupMembers,
1307
1350
  batch: (run) => transact(run),
1308
1351
  serialize,
@@ -1956,7 +1999,7 @@ function attachDesktop(wm, element, options = {}) {
1956
1999
  const registry = /* @__PURE__ */ new Map();
1957
2000
  const cleanup = [];
1958
2001
  let lastOrder = null;
1959
- let lastFocused = null;
2002
+ let focusedEl = null;
1960
2003
  let drag = null;
1961
2004
  let groupTargetId = null;
1962
2005
  let groupTargetEl = null;
@@ -2143,16 +2186,11 @@ function attachDesktop(wm, element, options = {}) {
2143
2186
  }
2144
2187
  }
2145
2188
  if (orderChanged) applyStacking(state.order);
2146
- if (state.focusedId !== lastFocused) {
2147
- if (lastFocused) {
2148
- const prev = registry.get(lastFocused);
2149
- if (prev) delete prev.element.dataset.wmFocused;
2150
- }
2151
- if (state.focusedId) {
2152
- const next = registry.get(state.focusedId);
2153
- if (next) next.element.dataset.wmFocused = "";
2154
- }
2155
- lastFocused = state.focusedId;
2189
+ const wanted = state.focusedId ? registry.get(state.focusedId)?.element ?? null : null;
2190
+ if (wanted !== focusedEl) {
2191
+ if (focusedEl) delete focusedEl.dataset.wmFocused;
2192
+ focusedEl = wanted;
2193
+ if (focusedEl) focusedEl.dataset.wmFocused = "";
2156
2194
  }
2157
2195
  lastOrder = state.order;
2158
2196
  }
@@ -2382,23 +2420,31 @@ function attachDesktop(wm, element, options = {}) {
2382
2420
  };
2383
2421
  windowElement.addEventListener("keydown", onModalTrap);
2384
2422
  attached.cleanup.push(() => windowElement.removeEventListener("keydown", onModalTrap));
2423
+ let detached = false;
2385
2424
  const detach = () => {
2425
+ if (detached) return;
2426
+ detached = true;
2386
2427
  if (drag?.id === id) endDrag(true);
2387
2428
  for (const dispose of attached.cleanup) dispose();
2388
2429
  for (const resizeHandle of attached.handles) resizeHandle.remove();
2389
2430
  registry.delete(id);
2431
+ if (registry.get(id)?.element !== windowElement) delete windowElement.dataset.wmWindow;
2432
+ delete windowElement.dataset.wmFocused;
2433
+ delete windowElement.dataset.wmTabTarget;
2434
+ if (focusedEl === windowElement) focusedEl = null;
2435
+ if (groupTargetEl === windowElement) {
2436
+ groupTargetId = null;
2437
+ groupTargetEl = null;
2438
+ }
2390
2439
  };
2391
- if (windowOptions.removeOnClose) {
2392
- const stopOnClose = wm.on("close", ({ window: closed }) => {
2393
- if (closed.id !== id) return;
2394
- detach();
2395
- windowElement.remove();
2396
- });
2397
- attached.cleanup.push(stopOnClose);
2398
- }
2440
+ const stopOnClose = wm.on("close", ({ window: closed }) => {
2441
+ if (closed.id !== id) return;
2442
+ detach();
2443
+ if (windowOptions.removeOnClose) windowElement.remove();
2444
+ });
2445
+ attached.cleanup.push(stopOnClose);
2399
2446
  registry.set(id, attached);
2400
2447
  lastOrder = null;
2401
- lastFocused = null;
2402
2448
  syncAll();
2403
2449
  if (wm.getState().focusedId === id && !windowElement.contains(doc.activeElement)) {
2404
2450
  windowElement.focus({ preventScroll: true });
@@ -2488,5 +2534,5 @@ exports.flipToTarget = flipToTarget;
2488
2534
  exports.magnetize = magnetize;
2489
2535
  exports.prefersReducedMotion = prefersReducedMotion;
2490
2536
  exports.zoneBounds = zoneBounds;
2491
- //# sourceMappingURL=chunk-DYZQY3BE.cjs.map
2492
- //# sourceMappingURL=chunk-DYZQY3BE.cjs.map
2537
+ //# sourceMappingURL=chunk-2VRCYMKH.cjs.map
2538
+ //# sourceMappingURL=chunk-2VRCYMKH.cjs.map