@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
package/README.md CHANGED
@@ -24,7 +24,7 @@
24
24
  - ⌨️ **Accessible** — keyboard move/resize/snap, F6 window cycling, focus-trapped modals, `aria-live` announcements
25
25
  - ⚡ **Fast** — `transform`-only positioning, rAF-batched pointer input, structural sharing; 50 windows drag at 60fps
26
26
  - 💾 **Persistence** — one call to serialize the desktop, one call to restore it, with versioned migrations
27
- - 🎨 **Three themes** — dark glass, light glass and Win98 retro, or bring your own CSS
27
+ - 🎨 **Sixteen themes** — glass, light, retro, terminal, paper, neon, aqua, frost, candy, carbon, brutalist, blueprint, amber, noir, forest, synth — or bring your own CSS
28
28
  - 🖼️ **Popout** *(experimental)* — send a window into Document Picture-in-Picture
29
29
  - 📦 **Zero dependencies**, strict TypeScript, ESM + CJS, ~10.7 kB brotli core
30
30
 
@@ -68,7 +68,7 @@ The desktop element becomes the coordinate space. Your markup stays yours — wm
68
68
  | Attribute | Meaning |
69
69
  | --- | --- |
70
70
  | `data-wm-drag` | drag handle (usually the titlebar); double-click toggles maximize |
71
- | `data-wm-title` | window title node, linked via `aria-labelledby` |
71
+ | `data-wm-title` | window title node; its text follows the state, and it is linked via `aria-labelledby` when it shares a root with the window |
72
72
  | `data-wm-close` / `data-wm-minimize` / `data-wm-maximize` | control buttons, wired by delegation |
73
73
  | `data-wm-content` | scrollable content area (styled by themes) |
74
74
 
@@ -326,6 +326,61 @@ persist(wm, {
326
326
 
327
327
  Moves a window's content into a [Document Picture-in-Picture](https://developer.mozilla.org/docs/Web/API/Document_Picture-in-Picture_API) always-on-top OS window, keeping the same JS context and state. Feature-detect with `isPopoutSupported()`.
328
328
 
329
+ ### `pinch(options?)`, `swipe(options?)`, `touchGestures(options?)` — `@surdeddd/wmkit/gestures`
330
+
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
+
333
+ ```js
334
+ import { attachDesktop } from '@surdeddd/wmkit'
335
+ import { touchGestures } from '@surdeddd/wmkit/gestures'
336
+
337
+ attachDesktop(wm, root, { gestures: [touchGestures({ swipe: { workspaces: 4 } })] })
338
+ ```
339
+
340
+ A gesture is just a function that takes a `GestureContext` and returns its teardown, so your own gestures cooperate with the built-in drag and resize through the same `claim`/`busy` pair — see [docs/api.md](docs/api.md).
341
+
342
+ ### `skin(spec)`, `defaultSkin`, `barebones` — `@surdeddd/wmkit/chrome`
343
+
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
+
346
+ ```js
347
+ import { attachDesktop } from '@surdeddd/wmkit'
348
+ import { skin } from '@surdeddd/wmkit/chrome'
349
+
350
+ const mine = skin({
351
+ name: 'mine',
352
+ template:
353
+ '<section><header data-wm-drag><span data-wm-title>{{title}}</span>' +
354
+ '<button data-wm-close aria-label="Close {{title}}"></button></header>' +
355
+ '<div data-wm-content></div></section>',
356
+ })
357
+
358
+ const desktop = attachDesktop(wm, root, { skins: { mine } })
359
+ desktop.mountWindow('notes', 'mine').content.append(myApp)
360
+ ```
361
+
362
+ `data-wm-close`, `data-wm-minimize`, `data-wm-maximize`, `data-wm-restore`, `data-wm-center`, `data-wm-send-back`, `data-wm-snap="left"` and `data-wm-to-workspace="2"` are read straight off your markup. Every `{{placeholder}}` is HTML-escaped. `shadow: true` seals the chrome inside an open shadow root without losing grouping, focus trapping or the accessible name — see [docs/recipes.md](docs/recipes.md).
363
+
364
+ ### `createDevtools(wm, options?)` — `@surdeddd/wmkit/devtools`
365
+
366
+ An opt-in panel that shows the live window table, an event log and the manager controls, so you can see what the state machine is doing without a debugger. It follows the manager, never the DOM, so it works with any adapter or none.
367
+
368
+ ```js
369
+ import { createDevtools, devtoolsMessagesRu } from '@surdeddd/wmkit/devtools'
370
+
371
+ const panel = createDevtools(wm, { logLimit: 100 })
372
+ panel.destroy()
373
+ ```
374
+
375
+ | Option | Default | Meaning |
376
+ | --- | --- | --- |
377
+ | `container` | `document.body` | where the panel mounts |
378
+ | `messages` | English catalog | pass `devtoolsMessagesRu` for Russian, or your own strings |
379
+ | `logLimit` | `50` | ring buffer size for the event log |
380
+ | `events` | open, close, focus, stage, workspace, group, modalblocked | which manager events to log |
381
+
382
+ Rows are patched in place rather than re-rendered, so keyboard focus inside the panel survives while windows move. It injects one scoped stylesheet, shared between panels and removed with the last one.
383
+
329
384
  ## Theming
330
385
 
331
386
  `@surdeddd/wmkit/themes/glass.css` styles the `data-wm-*` attributes and exposes CSS variables:
@@ -339,7 +394,9 @@ Moves a window's content into a [Document Picture-in-Picture](https://developer.
339
394
  }
340
395
  ```
341
396
 
342
- Two more ready-made themes ship alongside: `themes/light.css` (light glass) and `themes/retro.css` (pixel-perfect Win98 nostalgia). All three style the same `data-wm-*` attributes, so switching is a one-line import swap.
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
+
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).
343
400
 
344
401
  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.
345
402
 
package/README.ru.md CHANGED
@@ -20,7 +20,7 @@
20
20
  - ⌨️ **Доступность** — move/resize/snap с клавиатуры, цикл окон по F6, focus-trap в модалках, `aria-live`-анонсы
21
21
  - ⚡ **Производительность** — позиционирование только через `transform`, rAF-батчинг ввода, structural sharing; 50 окон таскаются на 60fps
22
22
  - 💾 **Персист** — один вызов сериализует рабочий стол, один — восстанавливает, с версионированием и миграциями
23
- - 🎨 **Три темы**тёмное стекло, светлое стекло и Win98-ретро, либо полностью свой CSS
23
+ - 🎨 **Шестнадцать тем**glass, light, retro, terminal, paper, neon, aqua, frost, candy, carbon, brutalist, blueprint, amber, noir, forest, synth — либо полностью свой CSS
24
24
  - 🖼️ **Popout** *(experimental)* — вынос окна в Document Picture-in-Picture
25
25
  - 📦 **Ноль зависимостей**, строгий TypeScript, ESM + CJS, ~10.7 kB brotli
26
26
 
@@ -64,7 +64,7 @@ desktop.attachWindow(win.id, el, { removeOnClose: true })
64
64
  | Атрибут | Смысл |
65
65
  | --- | --- |
66
66
  | `data-wm-drag` | ручка перетаскивания (обычно тайтлбар); двойной клик — toggle maximize |
67
- | `data-wm-title` | узел заголовка, связывается через `aria-labelledby` |
67
+ | `data-wm-title` | узел заголовка: его текст следует за состоянием, а связь через `aria-labelledby` ставится, когда узел лежит в том же корне, что и окно |
68
68
  | `data-wm-close` / `data-wm-minimize` / `data-wm-maximize` | кнопки управления, работают через делегирование |
69
69
  | `data-wm-content` | скроллируемая область контента |
70
70
 
@@ -262,11 +262,68 @@ if (isPopoutSupported()) await popout(wm, 'docs', contentElement)
262
262
 
263
263
  Окно уезжает в настоящее always-on-top окно ОС (Document Picture-in-Picture) с тем же JS-контекстом и состоянием.
264
264
 
265
+ ### `pinch(options?)`, `swipe(options?)`, `touchGestures(options?)` — `@surdeddd/wmkit/gestures`
266
+
267
+ Жесты двумя пальцами, подключаются по желанию — ядро за них не платит: пинч растягивает окно вокруг точки между пальцами, свайп двумя пальцами вбок переключает рабочий стол. 1.46 кБ в brotli.
268
+
269
+ ```js
270
+ import { attachDesktop } from '@surdeddd/wmkit'
271
+ import { touchGestures } from '@surdeddd/wmkit/gestures'
272
+
273
+ attachDesktop(wm, root, { gestures: [touchGestures({ swipe: { workspaces: 4 } })] })
274
+ ```
275
+
276
+ Жест — это просто функция: принимает `GestureContext`, возвращает свою отписку. Свои жесты уживаются со встроенными драгом и ресайзом через ту же пару `claim`/`busy` — подробности в [docs/api.md](docs/api.md).
277
+
278
+ ### `skin(spec)`, `defaultSkin`, `barebones` — `@surdeddd/wmkit/chrome`
279
+
280
+ Собирает само окно из строки разметки: можно принести свою обвязку и свои кнопки, не написав ни одного обработчика. 802 Б в brotli.
281
+
282
+ ```js
283
+ import { attachDesktop } from '@surdeddd/wmkit'
284
+ import { skin } from '@surdeddd/wmkit/chrome'
285
+
286
+ const mine = skin({
287
+ name: 'mine',
288
+ template:
289
+ '<section><header data-wm-drag><span data-wm-title>{{title}}</span>' +
290
+ '<button data-wm-close aria-label="Закрыть {{title}}"></button></header>' +
291
+ '<div data-wm-content></div></section>',
292
+ })
293
+
294
+ const desktop = attachDesktop(wm, root, { skins: { mine } })
295
+ desktop.mountWindow('notes', 'mine').content.append(myApp)
296
+ ```
297
+
298
+ `data-wm-close`, `data-wm-minimize`, `data-wm-maximize`, `data-wm-restore`, `data-wm-center`, `data-wm-send-back`, `data-wm-snap="left"` и `data-wm-to-workspace="2"` читаются прямо из вашей разметки. Каждая подстановка `{{...}}` экранируется. `shadow: true` запечатывает обвязку в открытом теневом корне, не теряя ни группировку, ни ловушку фокуса, ни доступное имя — подробности в [docs/recipes.md](docs/recipes.md).
299
+
300
+ ### `createDevtools(wm, options?)` — `@surdeddd/wmkit/devtools`
301
+
302
+ Панель, которую подключают по желанию: живая таблица окон, журнал событий и управление менеджером — чтобы видеть, что делает машина состояний, без отладчика. Она следит за менеджером, а не за DOM, поэтому работает с любым адаптером и без него.
303
+
304
+ ```js
305
+ import { createDevtools, devtoolsMessagesRu } from '@surdeddd/wmkit/devtools'
306
+
307
+ const panel = createDevtools(wm, { messages: devtoolsMessagesRu })
308
+ panel.destroy()
309
+ ```
310
+
311
+ | Опция | По умолчанию | Значение |
312
+ | --- | --- | --- |
313
+ | `container` | `document.body` | куда монтируется панель |
314
+ | `messages` | английский каталог | `devtoolsMessagesRu` для русского или свои строки |
315
+ | `logLimit` | `50` | размер кольцевого буфера журнала |
316
+ | `events` | open, close, focus, stage, workspace, group, modalblocked | какие события менеджера писать |
317
+
318
+ Строки обновляются точечно, а не перерисовываются целиком, поэтому фокус клавиатуры внутри панели переживает перемещение окон. Панель добавляет один изолированный стиль — общий для всех панелей и удаляемый вместе с последней.
319
+
265
320
  ## Темизация
266
321
 
267
322
  Подключите `@surdeddd/wmkit/themes/glass.css` и переопределяйте CSS-переменные (`--wm-radius`, `--wm-bg`, `--wm-accent`, …) — или не подключайте ничего и стилизуйте `data-wm-stage`, `data-wm-focused`, `data-wm-dragging`, `[hidden]` сами.
268
323
 
269
- Ещё две готовые темы: `themes/light.css` (светлое стекло) и `themes/retro.css` (Win98-ностальгия). Все три стилизуют одни и те же `data-wm-*` атрибуты — переключение = замена одного импорта.
324
+ Ещё пятнадцать готовых тем: `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
+ Тема одевает все окна, вариант — одно. Задайте `meta.variant` у окна, и десктоп отзеркалит его в `data-wm-variant` для вашего CSS — подробности в [docs/theming.md](docs/theming.md).
270
327
 
271
328
  ## SSR
272
329
 
package/dist/angular.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkNJAI5R4J_cjs = require('./chunk-NJAI5R4J.cjs');
3
+ var chunkZWUVWRUS_cjs = require('./chunk-ZWUVWRUS.cjs');
4
+ require('./chunk-BDHFXEJN.cjs');
4
5
  var core = require('@angular/core');
5
6
 
6
7
  function onDestroy(dispose) {
@@ -13,7 +14,7 @@ function onDestroy(dispose) {
13
14
  destroyRef?.onDestroy(dispose);
14
15
  }
15
16
  function useWindowManager(options) {
16
- const wm = chunkNJAI5R4J_cjs.createWindowManager(options);
17
+ const wm = chunkZWUVWRUS_cjs.createWindowManager(options);
17
18
  onDestroy(() => wm.destroy());
18
19
  return wm;
19
20
  }
@@ -28,7 +29,7 @@ function useWmWindow(wm, id) {
28
29
  return core.computed(() => state().windows[id]);
29
30
  }
30
31
  function createDesktop(wm, options) {
31
- const binder = chunkNJAI5R4J_cjs.createDesktopBinder(wm, options);
32
+ const binder = chunkZWUVWRUS_cjs.createDesktopBinder(wm, options);
32
33
  onDestroy(() => binder.destroy());
33
34
  return {
34
35
  binder,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/angular.ts"],"names":["inject","DestroyRef","createWindowManager","signal","computed","createDesktopBinder"],"mappings":";;;;;AAMA,SAAS,UAAU,OAAA,EAA2B;AAC5C,EAAA,IAAI,UAAA,GAAgC,IAAA;AACpC,EAAA,IAAI;AACF,IAAA,UAAA,GAAaA,YAAOC,eAAU,CAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACN,IAAA,UAAA,GAAa,IAAA;AAAA,EACf;AACA,EAAA,UAAA,EAAY,UAAU,OAAO,CAAA;AAC/B;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,MAAM,EAAA,GAAKC,sCAAoB,OAAO,CAAA;AACtC,EAAA,SAAA,CAAU,MAAM,EAAA,CAAG,OAAA,EAAS,CAAA;AAC5B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,WAAW,EAAA,EAAyC;AAClE,EAAA,MAAM,KAAA,GAAQC,WAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAG,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AACnD,EAAA,SAAA,CAAU,IAAI,CAAA;AACd,EAAA,OAAO,MAAM,UAAA,EAAW;AAC1B;AAEO,SAAS,WAAA,CAAY,IAAmB,EAAA,EAA6C;AAC1F,EAAA,MAAM,KAAA,GAAQ,WAAW,EAAE,CAAA;AAC3B,EAAA,OAAOC,cAAS,MAAM,KAAA,EAAM,CAAE,OAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C;AAQO,SAAS,aAAA,CAAc,IAAmB,OAAA,EAA0C;AACzF,EAAA,MAAM,MAAA,GAASC,qCAAA,CAAoB,EAAA,EAAI,OAAO,CAAA;AAC9C,EAAA,SAAA,CAAU,MAAM,MAAA,CAAO,OAAA,EAAS,CAAA;AAChC,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAQ,OAAA,EAAS;AACf,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AACzC,MAAA,SAAA,CAAU,MAAM,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,CAAO,IAAI,aAAA,EAAe;AACxB,MAAA,OAAO,CAAC,OAAA,KAAY;AAClB,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,EAAA,EAAI,SAAS,aAAa,CAAA;AAC3D,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACF","file":"angular.cjs","sourcesContent":["import { computed, DestroyRef, inject, type Signal, signal } from '@angular/core'\nimport { createWindowManager } from '../core/manager'\nimport type { ManagerOptions, ManagerState, WindowManager, WindowState } from '../core/types'\nimport { createDesktopBinder, type DesktopBinder } from '../dom/binder'\nimport type { DesktopOptions, WindowAttachOptions } from '../dom/shared'\n\nfunction onDestroy(dispose: () => void): void {\n let destroyRef: DestroyRef | null = null\n try {\n destroyRef = inject(DestroyRef)\n } catch {\n destroyRef = null\n }\n destroyRef?.onDestroy(dispose)\n}\n\nexport function useWindowManager(options?: ManagerOptions): WindowManager {\n const wm = createWindowManager(options)\n onDestroy(() => wm.destroy())\n return wm\n}\n\nexport function useWmState(wm: WindowManager): Signal<ManagerState> {\n const state = signal(wm.getState())\n const stop = wm.subscribe((next) => state.set(next))\n onDestroy(stop)\n return state.asReadonly()\n}\n\nexport function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined> {\n const state = useWmState(wm)\n return computed(() => state().windows[id])\n}\n\nexport interface AngularDesktop {\n binder: DesktopBinder\n desktop(element: HTMLElement): void\n window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void\n}\n\nexport function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop {\n const binder = createDesktopBinder(wm, options)\n onDestroy(() => binder.destroy())\n return {\n binder,\n desktop(element) {\n const unbind = binder.bindDesktop(element)\n onDestroy(unbind)\n },\n window(id, windowOptions) {\n return (element) => {\n const unbind = binder.bindWindow(id, element, windowOptions)\n onDestroy(unbind)\n }\n },\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/adapters/angular.ts"],"names":["inject","DestroyRef","createWindowManager","signal","computed","createDesktopBinder"],"mappings":";;;;;;AAMA,SAAS,UAAU,OAAA,EAA2B;AAC5C,EAAA,IAAI,UAAA,GAAgC,IAAA;AACpC,EAAA,IAAI;AACF,IAAA,UAAA,GAAaA,YAAOC,eAAU,CAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACN,IAAA,UAAA,GAAa,IAAA;AAAA,EACf;AACA,EAAA,UAAA,EAAY,UAAU,OAAO,CAAA;AAC/B;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,MAAM,EAAA,GAAKC,sCAAoB,OAAO,CAAA;AACtC,EAAA,SAAA,CAAU,MAAM,EAAA,CAAG,OAAA,EAAS,CAAA;AAC5B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,WAAW,EAAA,EAAyC;AAClE,EAAA,MAAM,KAAA,GAAQC,WAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAG,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AACnD,EAAA,SAAA,CAAU,IAAI,CAAA;AACd,EAAA,OAAO,MAAM,UAAA,EAAW;AAC1B;AAEO,SAAS,WAAA,CAAY,IAAmB,EAAA,EAA6C;AAC1F,EAAA,MAAM,KAAA,GAAQ,WAAW,EAAE,CAAA;AAC3B,EAAA,OAAOC,cAAS,MAAM,KAAA,EAAM,CAAE,OAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C;AAQO,SAAS,aAAA,CAAc,IAAmB,OAAA,EAA0C;AACzF,EAAA,MAAM,MAAA,GAASC,qCAAA,CAAoB,EAAA,EAAI,OAAO,CAAA;AAC9C,EAAA,SAAA,CAAU,MAAM,MAAA,CAAO,OAAA,EAAS,CAAA;AAChC,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAQ,OAAA,EAAS;AACf,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AACzC,MAAA,SAAA,CAAU,MAAM,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,CAAO,IAAI,aAAA,EAAe;AACxB,MAAA,OAAO,CAAC,OAAA,KAAY;AAClB,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,EAAA,EAAI,SAAS,aAAa,CAAA;AAC3D,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACF","file":"angular.cjs","sourcesContent":["import { computed, DestroyRef, inject, type Signal, signal } from '@angular/core'\nimport { createWindowManager } from '../core/manager'\nimport type { ManagerOptions, ManagerState, WindowManager, WindowState } from '../core/types'\nimport { createDesktopBinder, type DesktopBinder } from '../dom/binder'\nimport type { DesktopOptions, WindowAttachOptions } from '../dom/shared'\n\nfunction onDestroy(dispose: () => void): void {\n let destroyRef: DestroyRef | null = null\n try {\n destroyRef = inject(DestroyRef)\n } catch {\n destroyRef = null\n }\n destroyRef?.onDestroy(dispose)\n}\n\nexport function useWindowManager(options?: ManagerOptions): WindowManager {\n const wm = createWindowManager(options)\n onDestroy(() => wm.destroy())\n return wm\n}\n\nexport function useWmState(wm: WindowManager): Signal<ManagerState> {\n const state = signal(wm.getState())\n const stop = wm.subscribe((next) => state.set(next))\n onDestroy(stop)\n return state.asReadonly()\n}\n\nexport function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined> {\n const state = useWmState(wm)\n return computed(() => state().windows[id])\n}\n\nexport interface AngularDesktop {\n binder: DesktopBinder\n desktop(element: HTMLElement): void\n window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void\n}\n\nexport function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop {\n const binder = createDesktopBinder(wm, options)\n onDestroy(() => binder.destroy())\n return {\n binder,\n desktop(element) {\n const unbind = binder.bindDesktop(element)\n onDestroy(unbind)\n },\n window(id, windowOptions) {\n return (element) => {\n const unbind = binder.bindWindow(id, element, windowOptions)\n onDestroy(unbind)\n }\n },\n }\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { Signal } from '@angular/core';
2
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';
3
+ import { D as DesktopBinder } from './binder-DGoj0xG1.cjs';
4
+ import { W as WindowAttachOptions, D as DesktopOptions } from './shared-DdGFz3oy.cjs';
4
5
 
5
6
  declare function useWindowManager(options?: ManagerOptions): WindowManager;
6
7
  declare function useWmState(wm: WindowManager): Signal<ManagerState>;
package/dist/angular.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Signal } from '@angular/core';
2
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';
3
+ import { D as DesktopBinder } from './binder-Lo13iTVB.js';
4
+ import { W as WindowAttachOptions, D as DesktopOptions } from './shared-BoHXlPCu.js';
4
5
 
5
6
  declare function useWindowManager(options?: ManagerOptions): WindowManager;
6
7
  declare function useWmState(wm: WindowManager): Signal<ManagerState>;
package/dist/angular.js CHANGED
@@ -1,4 +1,5 @@
1
- import { createWindowManager, createDesktopBinder } from './chunk-FOWW4LXS.js';
1
+ import { createWindowManager, createDesktopBinder } from './chunk-TUGQX3CX.js';
2
+ import './chunk-5XVIC5LH.js';
2
3
  import { signal, computed, inject, DestroyRef } from '@angular/core';
3
4
 
4
5
  function onDestroy(dispose) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/angular.ts"],"names":[],"mappings":";;;AAMA,SAAS,UAAU,OAAA,EAA2B;AAC5C,EAAA,IAAI,UAAA,GAAgC,IAAA;AACpC,EAAA,IAAI;AACF,IAAA,UAAA,GAAa,OAAO,UAAU,CAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACN,IAAA,UAAA,GAAa,IAAA;AAAA,EACf;AACA,EAAA,UAAA,EAAY,UAAU,OAAO,CAAA;AAC/B;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,MAAM,EAAA,GAAK,oBAAoB,OAAO,CAAA;AACtC,EAAA,SAAA,CAAU,MAAM,EAAA,CAAG,OAAA,EAAS,CAAA;AAC5B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,WAAW,EAAA,EAAyC;AAClE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAG,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AACnD,EAAA,SAAA,CAAU,IAAI,CAAA;AACd,EAAA,OAAO,MAAM,UAAA,EAAW;AAC1B;AAEO,SAAS,WAAA,CAAY,IAAmB,EAAA,EAA6C;AAC1F,EAAA,MAAM,KAAA,GAAQ,WAAW,EAAE,CAAA;AAC3B,EAAA,OAAO,SAAS,MAAM,KAAA,EAAM,CAAE,OAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C;AAQO,SAAS,aAAA,CAAc,IAAmB,OAAA,EAA0C;AACzF,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,EAAA,EAAI,OAAO,CAAA;AAC9C,EAAA,SAAA,CAAU,MAAM,MAAA,CAAO,OAAA,EAAS,CAAA;AAChC,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAQ,OAAA,EAAS;AACf,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AACzC,MAAA,SAAA,CAAU,MAAM,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,CAAO,IAAI,aAAA,EAAe;AACxB,MAAA,OAAO,CAAC,OAAA,KAAY;AAClB,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,EAAA,EAAI,SAAS,aAAa,CAAA;AAC3D,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACF","file":"angular.js","sourcesContent":["import { computed, DestroyRef, inject, type Signal, signal } from '@angular/core'\nimport { createWindowManager } from '../core/manager'\nimport type { ManagerOptions, ManagerState, WindowManager, WindowState } from '../core/types'\nimport { createDesktopBinder, type DesktopBinder } from '../dom/binder'\nimport type { DesktopOptions, WindowAttachOptions } from '../dom/shared'\n\nfunction onDestroy(dispose: () => void): void {\n let destroyRef: DestroyRef | null = null\n try {\n destroyRef = inject(DestroyRef)\n } catch {\n destroyRef = null\n }\n destroyRef?.onDestroy(dispose)\n}\n\nexport function useWindowManager(options?: ManagerOptions): WindowManager {\n const wm = createWindowManager(options)\n onDestroy(() => wm.destroy())\n return wm\n}\n\nexport function useWmState(wm: WindowManager): Signal<ManagerState> {\n const state = signal(wm.getState())\n const stop = wm.subscribe((next) => state.set(next))\n onDestroy(stop)\n return state.asReadonly()\n}\n\nexport function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined> {\n const state = useWmState(wm)\n return computed(() => state().windows[id])\n}\n\nexport interface AngularDesktop {\n binder: DesktopBinder\n desktop(element: HTMLElement): void\n window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void\n}\n\nexport function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop {\n const binder = createDesktopBinder(wm, options)\n onDestroy(() => binder.destroy())\n return {\n binder,\n desktop(element) {\n const unbind = binder.bindDesktop(element)\n onDestroy(unbind)\n },\n window(id, windowOptions) {\n return (element) => {\n const unbind = binder.bindWindow(id, element, windowOptions)\n onDestroy(unbind)\n }\n },\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/adapters/angular.ts"],"names":[],"mappings":";;;;AAMA,SAAS,UAAU,OAAA,EAA2B;AAC5C,EAAA,IAAI,UAAA,GAAgC,IAAA;AACpC,EAAA,IAAI;AACF,IAAA,UAAA,GAAa,OAAO,UAAU,CAAA;AAAA,EAChC,CAAA,CAAA,MAAQ;AACN,IAAA,UAAA,GAAa,IAAA;AAAA,EACf;AACA,EAAA,UAAA,EAAY,UAAU,OAAO,CAAA;AAC/B;AAEO,SAAS,iBAAiB,OAAA,EAAyC;AACxE,EAAA,MAAM,EAAA,GAAK,oBAAoB,OAAO,CAAA;AACtC,EAAA,SAAA,CAAU,MAAM,EAAA,CAAG,OAAA,EAAS,CAAA;AAC5B,EAAA,OAAO,EAAA;AACT;AAEO,SAAS,WAAW,EAAA,EAAyC;AAClE,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,GAAG,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,GAAA,CAAI,IAAI,CAAC,CAAA;AACnD,EAAA,SAAA,CAAU,IAAI,CAAA;AACd,EAAA,OAAO,MAAM,UAAA,EAAW;AAC1B;AAEO,SAAS,WAAA,CAAY,IAAmB,EAAA,EAA6C;AAC1F,EAAA,MAAM,KAAA,GAAQ,WAAW,EAAE,CAAA;AAC3B,EAAA,OAAO,SAAS,MAAM,KAAA,EAAM,CAAE,OAAA,CAAQ,EAAE,CAAC,CAAA;AAC3C;AAQO,SAAS,aAAA,CAAc,IAAmB,OAAA,EAA0C;AACzF,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,EAAA,EAAI,OAAO,CAAA;AAC9C,EAAA,SAAA,CAAU,MAAM,MAAA,CAAO,OAAA,EAAS,CAAA;AAChC,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAQ,OAAA,EAAS;AACf,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,WAAA,CAAY,OAAO,CAAA;AACzC,MAAA,SAAA,CAAU,MAAM,CAAA;AAAA,IAClB,CAAA;AAAA,IACA,MAAA,CAAO,IAAI,aAAA,EAAe;AACxB,MAAA,OAAO,CAAC,OAAA,KAAY;AAClB,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,UAAA,CAAW,EAAA,EAAI,SAAS,aAAa,CAAA;AAC3D,QAAA,SAAA,CAAU,MAAM,CAAA;AAAA,MAClB,CAAA;AAAA,IACF;AAAA,GACF;AACF","file":"angular.js","sourcesContent":["import { computed, DestroyRef, inject, type Signal, signal } from '@angular/core'\nimport { createWindowManager } from '../core/manager'\nimport type { ManagerOptions, ManagerState, WindowManager, WindowState } from '../core/types'\nimport { createDesktopBinder, type DesktopBinder } from '../dom/binder'\nimport type { DesktopOptions, WindowAttachOptions } from '../dom/shared'\n\nfunction onDestroy(dispose: () => void): void {\n let destroyRef: DestroyRef | null = null\n try {\n destroyRef = inject(DestroyRef)\n } catch {\n destroyRef = null\n }\n destroyRef?.onDestroy(dispose)\n}\n\nexport function useWindowManager(options?: ManagerOptions): WindowManager {\n const wm = createWindowManager(options)\n onDestroy(() => wm.destroy())\n return wm\n}\n\nexport function useWmState(wm: WindowManager): Signal<ManagerState> {\n const state = signal(wm.getState())\n const stop = wm.subscribe((next) => state.set(next))\n onDestroy(stop)\n return state.asReadonly()\n}\n\nexport function useWmWindow(wm: WindowManager, id: string): Signal<WindowState | undefined> {\n const state = useWmState(wm)\n return computed(() => state().windows[id])\n}\n\nexport interface AngularDesktop {\n binder: DesktopBinder\n desktop(element: HTMLElement): void\n window(id: string, options?: WindowAttachOptions): (element: HTMLElement) => void\n}\n\nexport function createDesktop(wm: WindowManager, options?: DesktopOptions): AngularDesktop {\n const binder = createDesktopBinder(wm, options)\n onDestroy(() => binder.destroy())\n return {\n binder,\n desktop(element) {\n const unbind = binder.bindDesktop(element)\n onDestroy(unbind)\n },\n window(id, windowOptions) {\n return (element) => {\n const unbind = binder.bindWindow(id, element, windowOptions)\n onDestroy(unbind)\n }\n },\n }\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { W as WindowManager } from './types-jG2lHGcA.cjs';
2
+ import { a as DesktopController, W as WindowAttachOptions, D as DesktopOptions } from './shared-DdGFz3oy.cjs';
3
+
4
+ interface DesktopBinder {
5
+ wm: WindowManager;
6
+ controller(): DesktopController | null;
7
+ bindDesktop(element: HTMLElement): () => void;
8
+ bindWindow(id: string, element: HTMLElement, options?: WindowAttachOptions): () => void;
9
+ destroy(): void;
10
+ }
11
+ declare function createDesktopBinder(wm: WindowManager, options?: DesktopOptions): DesktopBinder;
12
+
13
+ export { type DesktopBinder as D, createDesktopBinder as c };
@@ -0,0 +1,13 @@
1
+ import { W as WindowManager } from './types-jG2lHGcA.js';
2
+ import { a as DesktopController, W as WindowAttachOptions, D as DesktopOptions } from './shared-BoHXlPCu.js';
3
+
4
+ interface DesktopBinder {
5
+ wm: WindowManager;
6
+ controller(): DesktopController | null;
7
+ bindDesktop(element: HTMLElement): () => void;
8
+ bindWindow(id: string, element: HTMLElement, options?: WindowAttachOptions): () => void;
9
+ destroy(): void;
10
+ }
11
+ declare function createDesktopBinder(wm: WindowManager, options?: DesktopOptions): DesktopBinder;
12
+
13
+ export { type DesktopBinder as D, createDesktopBinder as c };
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ // src/plugins/chrome/messages.ts
4
+ var chromeMessages = {
5
+ minimize: "Minimize",
6
+ maximize: "Maximize",
7
+ close: "Close"
8
+ };
9
+ var chromeMessagesRu = {
10
+ minimize: "\u0421\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
11
+ maximize: "\u0420\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
12
+ close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
13
+ };
14
+
15
+ // src/plugins/chrome/template.ts
16
+ var ESCAPES = {
17
+ "&": "&amp;",
18
+ "<": "&lt;",
19
+ ">": "&gt;",
20
+ '"': "&quot;",
21
+ "'": "&#39;"
22
+ };
23
+ function escapeHtml(value) {
24
+ return value.replace(/[&<>"']/g, (char) => ESCAPES[char]);
25
+ }
26
+ function compileTemplate(template) {
27
+ const parts = template.split(/\{\{\s*([a-z]+)\s*\}\}/i);
28
+ return (values) => parts.map((part, index) => index % 2 === 0 ? part : escapeHtml(values[part] ?? "")).join("");
29
+ }
30
+
31
+ // src/plugins/chrome/index.ts
32
+ var SLOT = "[data-wm-content]";
33
+ var SLOT_PATTERN = /data-wm-content\b/g;
34
+ var BAD_SLOT_COUNT = "wmkit: a skin template needs exactly one [data-wm-content]";
35
+ var BAD_STRUCTURE = "wmkit: a skin template needs a single root element that holds its [data-wm-content]";
36
+ var UNSCOPED_STYLES = "wmkit: a skin with styles needs a name to scope them to";
37
+ function valuesOf(win) {
38
+ const variant = win.meta.variant;
39
+ return {
40
+ id: win.id,
41
+ title: win.title,
42
+ stage: win.stage,
43
+ layer: win.layer,
44
+ workspace: String(win.workspace),
45
+ variant: typeof variant === "string" ? variant : ""
46
+ };
47
+ }
48
+ function injectStyles(doc, name, styles) {
49
+ if (doc.head.querySelector(`style[data-wm-skin-styles="${name}"]`)) return;
50
+ const tag = doc.createElement("style");
51
+ tag.dataset.wmSkinStyles = name;
52
+ tag.textContent = styles;
53
+ doc.head.append(tag);
54
+ }
55
+ function skin(spec) {
56
+ if ((spec.template.match(SLOT_PATTERN) ?? []).length !== 1) throw new Error(BAD_SLOT_COUNT);
57
+ if (spec.styles !== void 0 && spec.name === void 0) throw new Error(UNSCOPED_STYLES);
58
+ const render = compileTemplate(spec.template);
59
+ const sheets = /* @__PURE__ */ new WeakMap();
60
+ function adopt(root, doc, css) {
61
+ if (typeof CSSStyleSheet.prototype.replaceSync !== "function") {
62
+ const tag = doc.createElement("style");
63
+ tag.textContent = css;
64
+ root.prepend(tag);
65
+ return;
66
+ }
67
+ let sheet = sheets.get(doc);
68
+ if (!sheet) {
69
+ sheet = new CSSStyleSheet();
70
+ sheet.replaceSync(css);
71
+ sheets.set(doc, sheet);
72
+ }
73
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
74
+ }
75
+ return ({ doc, window: win }) => {
76
+ const holder = doc.createElement("template");
77
+ holder.innerHTML = render(valuesOf(win));
78
+ const built = holder.content.firstElementChild;
79
+ const slot = built === null ? null : built.matches(SLOT) ? built : built.querySelector(SLOT);
80
+ if (built === null || slot === null) throw new Error(BAD_STRUCTURE);
81
+ if (spec.shadow !== true) {
82
+ if (spec.name !== void 0) built.dataset.wmSkin = spec.name;
83
+ if (spec.styles !== void 0) injectStyles(doc, spec.name, spec.styles);
84
+ return { element: built, content: slot };
85
+ }
86
+ const element = doc.createElement("div");
87
+ if (spec.name !== void 0) element.dataset.wmSkin = spec.name;
88
+ const root = element.attachShadow({ mode: "open" });
89
+ const projection = doc.createElement("slot");
90
+ if (slot === built) root.append(projection);
91
+ else {
92
+ slot.replaceWith(projection);
93
+ root.append(...built.childNodes);
94
+ }
95
+ if (spec.styles !== void 0) adopt(root, doc, spec.styles);
96
+ const content = doc.createElement("div");
97
+ content.dataset.wmContent = "";
98
+ element.append(content);
99
+ return { element, content };
100
+ };
101
+ }
102
+ function windowChrome(messages = chromeMessages) {
103
+ return skin({
104
+ name: "wmkit",
105
+ template: `<section><header data-wm-drag><span data-wm-title>{{title}}</span><span data-wm-controls><button type="button" data-wm-minimize aria-label="${messages.minimize}"></button><button type="button" data-wm-maximize aria-label="${messages.maximize}"></button><button type="button" data-wm-close aria-label="${messages.close}"></button></span></header><div data-wm-content></div></section>`
106
+ });
107
+ }
108
+ var defaultSkin = windowChrome();
109
+ var barebones = skin({
110
+ name: "barebones",
111
+ template: "<section><header data-wm-drag><span data-wm-title>{{title}}</span></header><div data-wm-content></div></section>"
112
+ });
113
+
114
+ exports.barebones = barebones;
115
+ exports.chromeMessages = chromeMessages;
116
+ exports.chromeMessagesRu = chromeMessagesRu;
117
+ exports.compileTemplate = compileTemplate;
118
+ exports.defaultSkin = defaultSkin;
119
+ exports.skin = skin;
120
+ exports.windowChrome = windowChrome;
121
+ //# sourceMappingURL=chrome.cjs.map
122
+ //# sourceMappingURL=chrome.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugins/chrome/messages.ts","../src/plugins/chrome/template.ts","../src/plugins/chrome/index.ts"],"names":[],"mappings":"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","file":"chrome.cjs","sourcesContent":["export interface ChromeMessages {\n minimize: string\n maximize: string\n close: string\n}\n\nexport const chromeMessages: ChromeMessages = {\n minimize: 'Minimize',\n maximize: 'Maximize',\n close: 'Close',\n}\n\nexport const chromeMessagesRu: ChromeMessages = {\n minimize: 'Свернуть',\n maximize: 'Развернуть',\n close: 'Закрыть',\n}\n","const ESCAPES: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (char) => ESCAPES[char] as string)\n}\n\nexport function compileTemplate(template: string): (values: Record<string, string>) => string {\n const parts = template.split(/\\{\\{\\s*([a-z]+)\\s*\\}\\}/i)\n return (values) =>\n parts.map((part, index) => (index % 2 === 0 ? part : escapeHtml(values[part] ?? ''))).join('')\n}\n","import type { WindowState } from '../../core/types'\nimport type { SkinMount, WindowSkin } from '../../dom/shared'\nimport { type ChromeMessages, chromeMessages } from './messages'\nimport { compileTemplate } from './template'\n\nexport type { ChromeMessages } from './messages'\nexport { chromeMessages, chromeMessagesRu } from './messages'\nexport { compileTemplate } from './template'\n\nexport interface SkinSpec {\n template: string\n styles?: string\n shadow?: boolean\n name?: string\n}\n\nconst SLOT = '[data-wm-content]'\nconst SLOT_PATTERN = /data-wm-content\\b/g\n\nconst BAD_SLOT_COUNT = 'wmkit: a skin template needs exactly one [data-wm-content]'\nconst BAD_STRUCTURE =\n 'wmkit: a skin template needs a single root element that holds its [data-wm-content]'\nconst UNSCOPED_STYLES = 'wmkit: a skin with styles needs a name to scope them to'\n\nfunction valuesOf(win: WindowState): Record<string, string> {\n const variant = win.meta.variant\n return {\n id: win.id,\n title: win.title,\n stage: win.stage,\n layer: win.layer,\n workspace: String(win.workspace),\n variant: typeof variant === 'string' ? variant : '',\n }\n}\n\nfunction injectStyles(doc: Document, name: string, styles: string): void {\n if (doc.head.querySelector(`style[data-wm-skin-styles=\"${name}\"]`)) return\n const tag = doc.createElement('style')\n tag.dataset.wmSkinStyles = name\n tag.textContent = styles\n doc.head.append(tag)\n}\n\nexport function skin(spec: SkinSpec): WindowSkin {\n if ((spec.template.match(SLOT_PATTERN) ?? []).length !== 1) throw new Error(BAD_SLOT_COUNT)\n if (spec.styles !== undefined && spec.name === undefined) throw new Error(UNSCOPED_STYLES)\n const render = compileTemplate(spec.template)\n const sheets = new WeakMap<Document, CSSStyleSheet>()\n\n function adopt(root: ShadowRoot, doc: Document, css: string): void {\n if (typeof CSSStyleSheet.prototype.replaceSync !== 'function') {\n const tag = doc.createElement('style')\n tag.textContent = css\n root.prepend(tag)\n return\n }\n let sheet = sheets.get(doc)\n if (!sheet) {\n sheet = new CSSStyleSheet()\n sheet.replaceSync(css)\n sheets.set(doc, sheet)\n }\n root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet]\n }\n\n return ({ doc, window: win }): SkinMount => {\n const holder = doc.createElement('template')\n holder.innerHTML = render(valuesOf(win))\n const built = holder.content.firstElementChild as HTMLElement | null\n const slot =\n built === null ? null : built.matches(SLOT) ? built : built.querySelector<HTMLElement>(SLOT)\n if (built === null || slot === null) throw new Error(BAD_STRUCTURE)\n\n if (spec.shadow !== true) {\n if (spec.name !== undefined) built.dataset.wmSkin = spec.name\n if (spec.styles !== undefined) injectStyles(doc, spec.name as string, spec.styles)\n return { element: built, content: slot }\n }\n\n const element = doc.createElement('div')\n if (spec.name !== undefined) element.dataset.wmSkin = spec.name\n const root = element.attachShadow({ mode: 'open' })\n const projection = doc.createElement('slot')\n if (slot === built) root.append(projection)\n else {\n slot.replaceWith(projection)\n root.append(...built.childNodes)\n }\n if (spec.styles !== undefined) adopt(root, doc, spec.styles)\n const content = doc.createElement('div')\n content.dataset.wmContent = ''\n element.append(content)\n return { element, content }\n }\n}\n\nexport function windowChrome(messages: ChromeMessages = chromeMessages): WindowSkin {\n return skin({\n name: 'wmkit',\n template:\n '<section>' +\n '<header data-wm-drag>' +\n '<span data-wm-title>{{title}}</span>' +\n '<span data-wm-controls>' +\n `<button type=\"button\" data-wm-minimize aria-label=\"${messages.minimize}\"></button>` +\n `<button type=\"button\" data-wm-maximize aria-label=\"${messages.maximize}\"></button>` +\n `<button type=\"button\" data-wm-close aria-label=\"${messages.close}\"></button>` +\n '</span>' +\n '</header>' +\n '<div data-wm-content></div>' +\n '</section>',\n })\n}\n\nexport const defaultSkin: WindowSkin = windowChrome()\n\nexport const barebones: WindowSkin = skin({\n name: 'barebones',\n template:\n '<section>' +\n '<header data-wm-drag><span data-wm-title>{{title}}</span></header>' +\n '<div data-wm-content></div>' +\n '</section>',\n})\n"]}
@@ -0,0 +1,25 @@
1
+ import { l as WindowSkin } from './shared-DdGFz3oy.cjs';
2
+ import './types-jG2lHGcA.cjs';
3
+
4
+ interface ChromeMessages {
5
+ minimize: string;
6
+ maximize: string;
7
+ close: string;
8
+ }
9
+ declare const chromeMessages: ChromeMessages;
10
+ declare const chromeMessagesRu: ChromeMessages;
11
+
12
+ declare function compileTemplate(template: string): (values: Record<string, string>) => string;
13
+
14
+ interface SkinSpec {
15
+ template: string;
16
+ styles?: string;
17
+ shadow?: boolean;
18
+ name?: string;
19
+ }
20
+ declare function skin(spec: SkinSpec): WindowSkin;
21
+ declare function windowChrome(messages?: ChromeMessages): WindowSkin;
22
+ declare const defaultSkin: WindowSkin;
23
+ declare const barebones: WindowSkin;
24
+
25
+ export { type ChromeMessages, type SkinSpec, barebones, chromeMessages, chromeMessagesRu, compileTemplate, defaultSkin, skin, windowChrome };
@@ -0,0 +1,25 @@
1
+ import { l as WindowSkin } from './shared-BoHXlPCu.js';
2
+ import './types-jG2lHGcA.js';
3
+
4
+ interface ChromeMessages {
5
+ minimize: string;
6
+ maximize: string;
7
+ close: string;
8
+ }
9
+ declare const chromeMessages: ChromeMessages;
10
+ declare const chromeMessagesRu: ChromeMessages;
11
+
12
+ declare function compileTemplate(template: string): (values: Record<string, string>) => string;
13
+
14
+ interface SkinSpec {
15
+ template: string;
16
+ styles?: string;
17
+ shadow?: boolean;
18
+ name?: string;
19
+ }
20
+ declare function skin(spec: SkinSpec): WindowSkin;
21
+ declare function windowChrome(messages?: ChromeMessages): WindowSkin;
22
+ declare const defaultSkin: WindowSkin;
23
+ declare const barebones: WindowSkin;
24
+
25
+ export { type ChromeMessages, type SkinSpec, barebones, chromeMessages, chromeMessagesRu, compileTemplate, defaultSkin, skin, windowChrome };
package/dist/chrome.js ADDED
@@ -0,0 +1,114 @@
1
+ // src/plugins/chrome/messages.ts
2
+ var chromeMessages = {
3
+ minimize: "Minimize",
4
+ maximize: "Maximize",
5
+ close: "Close"
6
+ };
7
+ var chromeMessagesRu = {
8
+ minimize: "\u0421\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
9
+ maximize: "\u0420\u0430\u0437\u0432\u0435\u0440\u043D\u0443\u0442\u044C",
10
+ close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C"
11
+ };
12
+
13
+ // src/plugins/chrome/template.ts
14
+ var ESCAPES = {
15
+ "&": "&amp;",
16
+ "<": "&lt;",
17
+ ">": "&gt;",
18
+ '"': "&quot;",
19
+ "'": "&#39;"
20
+ };
21
+ function escapeHtml(value) {
22
+ return value.replace(/[&<>"']/g, (char) => ESCAPES[char]);
23
+ }
24
+ function compileTemplate(template) {
25
+ const parts = template.split(/\{\{\s*([a-z]+)\s*\}\}/i);
26
+ return (values) => parts.map((part, index) => index % 2 === 0 ? part : escapeHtml(values[part] ?? "")).join("");
27
+ }
28
+
29
+ // src/plugins/chrome/index.ts
30
+ var SLOT = "[data-wm-content]";
31
+ var SLOT_PATTERN = /data-wm-content\b/g;
32
+ var BAD_SLOT_COUNT = "wmkit: a skin template needs exactly one [data-wm-content]";
33
+ var BAD_STRUCTURE = "wmkit: a skin template needs a single root element that holds its [data-wm-content]";
34
+ var UNSCOPED_STYLES = "wmkit: a skin with styles needs a name to scope them to";
35
+ function valuesOf(win) {
36
+ const variant = win.meta.variant;
37
+ return {
38
+ id: win.id,
39
+ title: win.title,
40
+ stage: win.stage,
41
+ layer: win.layer,
42
+ workspace: String(win.workspace),
43
+ variant: typeof variant === "string" ? variant : ""
44
+ };
45
+ }
46
+ function injectStyles(doc, name, styles) {
47
+ if (doc.head.querySelector(`style[data-wm-skin-styles="${name}"]`)) return;
48
+ const tag = doc.createElement("style");
49
+ tag.dataset.wmSkinStyles = name;
50
+ tag.textContent = styles;
51
+ doc.head.append(tag);
52
+ }
53
+ function skin(spec) {
54
+ if ((spec.template.match(SLOT_PATTERN) ?? []).length !== 1) throw new Error(BAD_SLOT_COUNT);
55
+ if (spec.styles !== void 0 && spec.name === void 0) throw new Error(UNSCOPED_STYLES);
56
+ const render = compileTemplate(spec.template);
57
+ const sheets = /* @__PURE__ */ new WeakMap();
58
+ function adopt(root, doc, css) {
59
+ if (typeof CSSStyleSheet.prototype.replaceSync !== "function") {
60
+ const tag = doc.createElement("style");
61
+ tag.textContent = css;
62
+ root.prepend(tag);
63
+ return;
64
+ }
65
+ let sheet = sheets.get(doc);
66
+ if (!sheet) {
67
+ sheet = new CSSStyleSheet();
68
+ sheet.replaceSync(css);
69
+ sheets.set(doc, sheet);
70
+ }
71
+ root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
72
+ }
73
+ return ({ doc, window: win }) => {
74
+ const holder = doc.createElement("template");
75
+ holder.innerHTML = render(valuesOf(win));
76
+ const built = holder.content.firstElementChild;
77
+ const slot = built === null ? null : built.matches(SLOT) ? built : built.querySelector(SLOT);
78
+ if (built === null || slot === null) throw new Error(BAD_STRUCTURE);
79
+ if (spec.shadow !== true) {
80
+ if (spec.name !== void 0) built.dataset.wmSkin = spec.name;
81
+ if (spec.styles !== void 0) injectStyles(doc, spec.name, spec.styles);
82
+ return { element: built, content: slot };
83
+ }
84
+ const element = doc.createElement("div");
85
+ if (spec.name !== void 0) element.dataset.wmSkin = spec.name;
86
+ const root = element.attachShadow({ mode: "open" });
87
+ const projection = doc.createElement("slot");
88
+ if (slot === built) root.append(projection);
89
+ else {
90
+ slot.replaceWith(projection);
91
+ root.append(...built.childNodes);
92
+ }
93
+ if (spec.styles !== void 0) adopt(root, doc, spec.styles);
94
+ const content = doc.createElement("div");
95
+ content.dataset.wmContent = "";
96
+ element.append(content);
97
+ return { element, content };
98
+ };
99
+ }
100
+ function windowChrome(messages = chromeMessages) {
101
+ return skin({
102
+ name: "wmkit",
103
+ template: `<section><header data-wm-drag><span data-wm-title>{{title}}</span><span data-wm-controls><button type="button" data-wm-minimize aria-label="${messages.minimize}"></button><button type="button" data-wm-maximize aria-label="${messages.maximize}"></button><button type="button" data-wm-close aria-label="${messages.close}"></button></span></header><div data-wm-content></div></section>`
104
+ });
105
+ }
106
+ var defaultSkin = windowChrome();
107
+ var barebones = skin({
108
+ name: "barebones",
109
+ template: "<section><header data-wm-drag><span data-wm-title>{{title}}</span></header><div data-wm-content></div></section>"
110
+ });
111
+
112
+ export { barebones, chromeMessages, chromeMessagesRu, compileTemplate, defaultSkin, skin, windowChrome };
113
+ //# sourceMappingURL=chrome.js.map
114
+ //# sourceMappingURL=chrome.js.map