Files
oikos/web/node_modules/@surdeddd/wmkit/README.ru.md
dtoro d4d99a7473
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more
phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper,
compose/web image) must move to its own repo first so doc/layout
rewrites land once on a backend-only tree.

Change:
- New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/
  (updateURL repointed to oikos-web releases), compose/, own CI (web +
  desktop jobs), own deploy script (CI-green gate, TOCTOU guard,
  version-tagged images, prune-to-3), own webhook receiver on :9798 +
  launchd unit, own compose project publishing the same 8091:80.
- Cutover executed on mac-mini in order: oikos stack's web service
  stopped+removed, oikos-web project brought up on 8091; outer Caddy
  untouched (targets the published port) — serving + Authentik flow +
  /wails 404 quirk verified post-cutover.
- Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI
  workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install
  targets, the compose web service, oikos-web from deploy.sh's fallback
  prune list; wails + go-keyring dropped from go.mod, vendor synced.
- README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now
  point at the new repo; mbse + mascot design docs carry a path note.

Risk: production SPA serving depends on the new pipeline now; rollback
is versioned-image re-up of the old web service from a pre-split
checkout (port 8091). Desktop builds installed before the split still
check dtoro/oikos releases — one manual reinstall, noted in the
oikos-web release notes.

Verification: go vet, make test (race), make generate-check, golangci
(no new findings; baseline down 400→365); post-cutover curls —
localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
2026-08-15 22:27:52 +02:00

140 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wmkit
**Headless оконный менеджер для веба.** Перетаскиваемые окна с ресайзом, снэпом, таскбаром, клавиатурной доступностью и персистом состояния — для vanilla JS и всех основных фреймворков.
[English version](./README.md) · [Живое демо](https://wmkit.vercel.app) · [Зеркало (Pages)](https://surdeddd.github.io/wmkit/) · [GitHub](https://github.com/Surdeddd/wmkit)
[![wmkit — живой демо-десктоп](https://raw.githubusercontent.com/Surdeddd/wmkit/main/.github/assets/hero.png)](https://surdeddd.github.io/wmkit/)
<p align="center"><em>Все окна выше настоящие — <a href="https://surdeddd.github.io/wmkit/">откройте демо</a> и потаскайте.</em></p>
- 🪟 **Полный жизненный цикл окна** — открытие, закрытие, фокус, сворачивание, разворачивание, восстановление, drag, ресайз в 8 направлениях
- 🧠 **Headless-ядро** — сериализуемая стейт-машина плюс DOM-контроллер; своя разметка или готовая стеклянная тема
- ⚛️ **Родные адаптеры**`@surdeddd/wmkit/react`, `/vue`, `/svelte`, `/solid`, `/angular`, тонкий сахар над одним ядром
-**Snap-зоны** — половины, четверти и максимизация от верхнего края с живым превью
- 🧲 **Магнетизм** — края окна прилипают к соседям и вьюпорту при перетаскивании
- ↩️ **Undo/redo** — каждая мутация = один шаг; целый drag схлопывается в одну запись истории
- 🗂️ **Именованные layout'ы и arrange** — снапшоты рабочего стола, `cascade`/`tile` одним вызовом
- ⌨️ **Доступность** — move/resize с клавиатуры, цикл окон по F6, focus-trap в модалках, `aria-live`-анонсы
-**Производительность** — позиционирование только через `transform`, rAF-батчинг ввода, structural sharing; 50 окон таскаются на 60fps
- 💾 **Персист** — один вызов сериализует рабочий стол, один — восстанавливает
- 🎨 **Три темы** — тёмное стекло, светлое стекло и Win98-ретро, либо полностью свой CSS
- 🖼️ **Popout** *(experimental)* — вынос окна в Document Picture-in-Picture
- 📦 **Ноль зависимостей**, строгий TypeScript, ESM + CJS, ~9.3 kB brotli
## Установка
```bash
npm install @surdeddd/wmkit
# или
pnpm add @surdeddd/wmkit
```
## Быстрый старт (vanilla)
```js
import { createWindowManager, attachDesktop } from '@surdeddd/wmkit'
import '@surdeddd/wmkit/themes/glass.css'
const wm = createWindowManager()
const desktop = attachDesktop(wm, document.querySelector('#desktop'))
const win = wm.open({ title: 'Привет', width: 420, height: 280 })
const el = document.createElement('section')
el.innerHTML = `
<header data-wm-drag>
<span data-wm-title>Привет</span>
<span data-wm-controls>
<button data-wm-minimize aria-label="Свернуть"></button>
<button data-wm-maximize aria-label="Развернуть"></button>
<button data-wm-close aria-label="Закрыть"></button>
</span>
</header>
<div data-wm-content>Что угодно.</div>
`
document.querySelector('#desktop').append(el)
desktop.attachWindow(win.id, el, { removeOnClose: true })
```
Элемент рабочего стола становится системой координат. Разметка остаётся вашей — wmkit вешает поведение на `data-wm-*` атрибуты:
| Атрибут | Смысл |
| --- | --- |
| `data-wm-drag` | ручка перетаскивания (обычно тайтлбар); двойной клик — toggle maximize |
| `data-wm-title` | узел заголовка, связывается через `aria-labelledby` |
| `data-wm-close` / `data-wm-minimize` / `data-wm-maximize` | кнопки управления, работают через делегирование |
| `data-wm-content` | скроллируемая область контента |
`removeOnClose` сам отвязывает контроллер и удаляет элемент при закрытии окна. На тач-устройствах хит-зоны ресайза и порог снэпа автоматически крупнее (`pointer: coarse`); настраиваются через `attachDesktop(wm, el, { hitAreas: { edge, corner } })`.
Контроллер добавляет ресайз-хендлы (`[data-wm-resize]`), превью снэпа (`[data-wm-snap-preview]`) и скрытый live-регион для скринридеров.
## Адаптеры
Примеры для React, Vue, Svelte, Solid и Angular — в [английском README](./README.md#react) и на [лендинге](https://surdeddd.github.io/wmkit/) (табы «Фреймворки»). Принцип один: контент окна живёт в дереве вашего фреймворка, никакого innerHTML.
## API ядра — кратко
```ts
const wm = createWindowManager({ keepInViewport: true, defaultSize: { width: 480, height: 320 } })
wm.open({ id: 'docs', title: 'Документы', layer: 'floating' })
wm.snap('docs', 'left') // 'right' | 'top-left' | 'bottom-right' | …
wm.minimize('docs') // restore вернёт предыдущий stage, включая maximized/snapped
wm.update('docs', { title: 'Новый заголовок', meta: { pinned: true } })
const json = wm.serialize() // JSON-безопасный снапшот
wm.hydrate(json)
wm.on('stage', ({ window, previous }) => console.log(previous, '→', window.stage))
wm.batch(() => { /* много операций — одно уведомление */ })
wm.undo(); wm.redo() // история изменений, drag = одна запись (historyLimit, default 50)
wm.saveLayout('работа'); wm.loadLayout('работа') // именованные снапшоты рабочего стола
wm.arrange('tile') // или 'cascade'; плюс minimizeAll() / restoreAll()
```
Слои: `normal` < `floating` (always-on-top) < `modal`. Модалка блокирует фокус нижних окон (попытка — событие `modalblocked` и flash-анимация), Tab заперт внутри.
Клавиатура по умолчанию: стрелки двигают сфокусированное окно (16 px), `Alt` — шаг 1 px, `Shift+стрелки` — ресайз, `F6`/`Shift+F6` — цикл по окнам, `Escape` отменяет активный drag/resize.
Магнетизм включён из коробки (порог 8 px, на тач-устройствах 12 px): `attachDesktop(wm, el, { magnetism: { threshold: 16 } })` или `magnetism: false`. Свой контекст-меню тайтлбара — через `onTitlebarContextMenu(win, event)` (правый клик и long-press на таче).
### Персист
```js
import { persist } from '@surdeddd/wmkit/persist'
persist(wm, { key: 'my-desktop' }) // авто-восстановление + debounce-сохранение
```
### Popout (experimental)
```js
import { popout, isPopoutSupported } from '@surdeddd/wmkit/popout'
if (isPopoutSupported()) await popout(wm, 'docs', contentElement)
```
Окно уезжает в настоящее always-on-top окно ОС (Document Picture-in-Picture) с тем же JS-контекстом и состоянием.
## Темизация
Подключите `@surdeddd/wmkit/themes/glass.css` и переопределяйте CSS-переменные (`--wm-radius`, `--wm-bg`, `--wm-accent`, …) — или не подключайте ничего и стилизуйте `data-wm-stage`, `data-wm-focused`, `data-wm-dragging`, `[hidden]` сами.
Ещё две готовые темы: `themes/light.css` (светлое стекло) и `themes/retro.css` (Win98-ностальгия). Все три стилизуют одни и те же `data-wm-*` атрибуты — переключение = замена одного импорта.
## SSR
Ядро не трогает `window`/`document`: менеджер можно создавать и гидрейтить на сервере, `attachDesktop` вызывается после маунта. `persist` тихо выключается без доступного storage.
## Качество
- 172 юнит-теста, **100%** покрытие стейт-машины и persist по строкам/веткам/функциям
- 178+ Playwright-сценариев на Chromium, WebKit и мобильной эмуляции: drag, ресайз во все стороны, снэп, магнетизм, undo после drag, клавиатура, touch, персист через перезагрузку, стресс на 50 окон, модальные ловушки, axe-аудиты доступности, визуальная регрессия по скриншотам
- перф-бенчмарки в CI на каждый push (`vitest bench`): 1 000 окон открываются за ~150 мс, move среди 50 окон ~1.2 мкс, полный undo/redo-проход на 100 шагов ~52 мкс
- `publint` + `@arethetypeswrong/cli` проверяют валидность пакета, `size-limit` следит за бюджетами
## Лицензия
[MIT](./LICENSE) © Максим Кравцов