# Выбор технологического стека и анализ фреймворков

Документ отвечает на вопросы: на чём делать браузерный видеозвонок, который
встраивается в чужие приложения, полностью кастомизируется, работает в обычных
браузерах и Electron/WebView-обёртках, адаптивен и не тормозит. Ниже — сравнение
вариантов, обоснование текущего выбора и путь развития (React/Vue-обёртки).

## 1. Требования, из которых вытекает выбор

| Требование | Следствие для стека |
|---|---|
| Встраивается в **чужую** страницу (React/Vue/Angular/varies) | никакой глобальной зависимости от фреймворка хоста; изоляция стилей; один `<script>` |
| Полная кастомизация дизайна (темы, токены, лого) | CSS-переменные + дизайн-токены, без сборки SCSS владельцем темы |
| Несколько инстансов/iframe на странице, Electron | Shadow DOM/Web Components, отсутствие singleton-состояния в модулях |
| Работает из коробки в Electron/WebView2/Tauri/WKWebView | стандартные Web API, secure context, без «магии» сборщика |
| Адаптив: ПК, ноут, телефон | CSS Grid/flex + container queries, без тяжёлых UI-kit'ов |
| Низкое потребление памяти/CPU | минимум JS, ленивые медиа-модули, виртуализация только по необходимости |
| Долгий срок жизни, найм | знакомые паттерны, типизация, изоляция пакетов |

Ключевой конфликт: **удобство DX фреймворка** vs **размер/встраиваемость/CPU**.
Для чужого сайта лишние 45–90 КБ gzip — уже заметно; камера/кодирование и так
нагружают CPU, поэтому UI-слой должен быть максимально дешёвым.

## 2. Сравнение вариантов (актуально на 2026)

Оценки bundle size — порядок величины для небольшого виджета (gzip), по
публичным бенчмаркам (krausest/js-framework-benchmark, bundlephobia):

| Вариант | Runtime+UI (gzip) | Память/CPU | Встраиваемость | Темизация | Electron | Вывод |
|---|---|---|---|---|---|---|
| **Vanilla TS + Web Components** (выбран) | **~15–25 КБ** UI + zod-часть | минимум: нет VDOM/diff, точечные DOM-апдейты | идеальная: Shadow DOM, без глобального runtime, один бандл | CSS-переменные из коробки | работает без настройки | ✅ ядро |
| React 19 + ReactDOM | ~45–60 КБ | выше: VDOM, память под элементы; для тяжёлого медиа — ок при мемоизации | хорошо, но два React на странице/host-версии → изоляция через shadow root | отлично (tokens/CSS vars) | стандартно | для host-обёрток/примеров |
| Preact | ~12–15 КБ | близко к vanilla | хорошо | отлично | да | разумный компромисс для UI-слоя |
| Vue 3.5 | ~30–40 КБ | среднее: Proxy-реактивность, меньше аллокаций | хорошо (custom element + shadow) | отлично | да | альтернатива |
| Svelte 5 (runes) | ~15–20 КБ | низкое: компиляция в DOM-операции | отлично (custom element) | отлично | да | сильная альтернатива |
| Solid | ~12–18 КБ | очень низкое (fine-grained) | хорошо | отлично | да | сильная альтернатива |
| Lit | ~18–25 КБ | низкое, Web Components-first | идеальная | CSS vars | да | близко к нашему выбору |
| Qwik | ~25–40 КБ | низкое после гидрации, но SSR-ориентирован | сложнее для embed | хорошо | да | не для этого кейса |
| Angular | ~80–130 КБ | высокое, тяжёлый runtime | плохо для embed | хорошо | да | не подходит |

Дополнительно: WebRTC API (`RTCPeerConnection`, `MediaStreamTrackProcessor`,
`setCodecPreferences`, Document PiP) — стандартные, от фреймворка не зависят.
Медиа-часть (кодирование/декодирование, blur) всегда дороже UI, поэтому
оптимизация начинается с **медиа-слоя**, а не с фреймворка.

## 3. Решение и обоснование

**Ядро виджета — Vanilla TypeScript + Web Components/Shadow DOM, без React.**

Почему:
1. **Встраиваемость.** Компания может встроить виджет в React, Vue, Angular,
   серверный шаблон, WordPress, Electron — один `<script>` и `mount()`.
   Нет зависимости от версии фреймворка хоста, нет конфликта React-рутов.
2. **Стили.** Shadow DOM + CSS-переменные `--agil-*` дают полную изоляцию и
   одновременно прозрачную кастомизацию: компания переопределяет токены, не
   ломая чужие стили.
3. **Память/CPU.** Нет VDOM-диффов и виртуального дерева; для видео-плиток
   достаточно прямых DOM-обновлений. Меньше GC-пауз во время звонка.
4. **Electron/WebView.** Web Components и стандартные API одинаково работают в
   Chromium-обёртках; нет SSR-зависимостей и глобального state-runtime.
5. **Размер.** UI-слой ~20 КБ gzip вместо 50–90 КБ у фреймворков; полный бандл
   SDK определяется в основном протоколом/zod и медиа-кодом (см.
   `docs/performance.md`).

**Где фреймворк всё же уместен:** хост-примеры и обёртки для DX — отдельные
тонкие пакеты `@agil/calls-react` / `@agil/calls-vue` (roadmap): React-компонент
`<AgilCall/>` внутри 15–40 строк, который вызывает наш `mount()` и **не тащит**
фреймворк в бандл виджета. Так заказчик получает «родной» DX, не увеличивая
вес встраиваемого решения.

## 4. Почему не «взять фреймворк и не сделать SPA»

- Jitsi/Telemost-подобный продукт — это **встраиваемый виджет**, а не сайт:
  SSR/роутинг/SPA-фреймворк не нужны, но добавляют вес и точку отказа.
- Темизация у нас — **данные (JSON-токены)**, а не SCSS/компиляция: UI-слой
  обязан уметь применить тему в рантайме без пересборки (это требование
  редактора/MCP). Vanilla + CSS vars делают это тривиально.
- Встраивание в чужие дизайн-системы требует **отсутствия глобальных стилей**,
  каскада и z-index-войн. Shadow DOM — единственный предсказуемый способ.

## 5. Работа в Electron и браузерных обёртках

Виджет — обычная веб-страница: работает в `BrowserWindow` (Electron),
WebView2 (Windows), Tauri (WebView2/WKWebView), CEF. Требования и нюансы:

| Обёртка | Secure context | Камера/мик | Экран | Замечания |
|---|---|---|---|---|
| Electron (Chromium) | `file://` считается trustworthy в Chromium, но лучше `app://`/`http://localhost` | через `session.setPermissionRequestHandler` | `setDisplayMediaRequestHandler` (нужен обработчик!) | наш пример — `examples/electron` |
| WebView2 | да (https/localhost) | разрешения WebView2 | `ScreenCaptureStarting`/`getDisplayMedia` | требуется автоплей-политика |
| Tauri (WKWebView/WebView2) | https обязателен | WKWebView требует `https`; WebView2 — ок | ограничения WKWebView | для iOS — нативное зеркало (roadmap) |
| CEF | да | разрешения CEF | через `OnRequestMediaAccessPermission` | проверять на целевой версии |

Практика для Electron: грузить UI с `http://localhost`/custom `app://`
(а не `file://`), включить `contextIsolation: true`, не отключать `webSecurity`,
обработать `setPermissionRequestHandler` (media, display-capture),
`setDisplayMediaRequestHandler` (выбор окна/экрана), настроить
`autoplayPolicy: 'no-user-gesture-required'` при необходимости, CSP без
`unsafe-eval`. Пример и инструкция: `docs/electron.md`, `examples/electron/`.

## 6. Адаптив и мобильный браузер

- Layout: CSS Grid/`container-type: inline-size` для плиток; спотлайт/сетка;
  мобильный режим — controls в нижнюю панель, side-panel на весь экран,
  safe-area (`env(safe-area-inset-*)`), тап-таргеты ≥44px.
- Видео: не более 2–4 потоков на телефоне (mesh ≤8 только для десктопа),
  дефолт 360p/15fps на мобильном, отключение simulcast при слабом канале,
  пауза рендера скрытых плиток (`IntersectionObserver` + `video.pause()`).
- Память: один `AudioContext`, ревок `ObjectURL`, остановка треков при leave,
  выгрузка MediaPipe после отключения blur, отписка от `stats` при невидимой
  вкладке (`document.visibilitychange`), лимит истории чата на клиенте.
- CPU: `requestVideoFrameCallback` вместо лишних таймеров, Canvas-запись только
  по требованию, пиксель-рейт ограничен, `degradationPreference` и адаптивные
  слои simulcast.

## 7. План развития стека

1. **Сейчас (MVP):** Vanilla TS + Web Components, Vite 6, один бандл виджета,
   SDK IIFE/ESM, Shadow DOM изоляция.
2. **Волна 2:** `@agil/calls-react`, `@agil/calls-vue` (тонкие обёртки, ≤1 КБ +
   peer-фреймворк хоста), Storybook примеров кастомизации, шаблоны тем.
3. **Волна 3:** мобильные: React Native (LiveKit RN SDK + наш UI-слой) либо
   WebView-обёртка с нативным CallKit/соединением (зависит от требований iOS).
4. **Оптимизация:** вынести zod-валидацию из браузерного бандла (сервер уже
   валидирует конфиг; браузеру нужны только типы + пресеты) — минус ~35–50 КБ
   gzip; медиа-модули (blur/запись/STT) — динамические `import()` уже сейчас.
5. **Не делать:** SPA-фреймворк внутри виджета, SSR, глобальный store в
   `window` — всё это противоречит встраиваемости.

## 8. Итог

Для встраиваемого видеозвонка с тотальной кастомизацией **Vanilla TS +
Web Components — лучший выбор по встраиваемости, памяти и размеру**. React/Vue
имеют смысл только как тонкие обёртки для DX хоста и как примеры интеграции, а
не как runtime виджета. Метрики производительности и бюджеты —
`docs/performance.md`; Electron-интеграция — `docs/electron.md`.
