Прогнать Prettier: чинит format:check в CI
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# RKNboost
|
# RKNboost
|
||||||
|
|
||||||
[github.com/maeneko/rknboost](https://github.com/maeneko/rknboost)
|
очередная обертка под zapret
|
||||||
|
|
||||||
## Требования
|
## Требования
|
||||||
|
|
||||||
@@ -24,150 +24,9 @@ npm run build:win # NSIS .exe (x64; arm64 не собираем — не
|
|||||||
npm run build:linux # AppImage + .deb
|
npm run build:linux # AppImage + .deb
|
||||||
```
|
```
|
||||||
|
|
||||||
> Установщики под чужую ОС локально собрать нельзя — для этого есть CI
|
## Ссылки
|
||||||
> (`.github/workflows/ci.yml` собирает все три платформы и кладёт артефакты в Actions).
|
|
||||||
|
|
||||||
> Если после `npm install` команда `npm run dev` падает с `Error: Electron uninstall` —
|
- **[bol-van/zapret](https://github.com/bol-van/zapret)** — сам движок zapret, спасибо за столь хороший инструмент
|
||||||
> бинарник Electron не докачался; исправляется повторным запуском
|
|
||||||
> `node node_modules/electron/install.js`.
|
|
||||||
|
|
||||||
## Структура
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
main/ # Node-процесс: окна, меню, доступ к ОС
|
|
||||||
index.ts lifecycle приложения, single instance
|
|
||||||
window.ts createWindow / createPopupWindow, настройки безопасности
|
|
||||||
ipc.ts все ipcMain.handle
|
|
||||||
preload/ # мост между main и renderer (sandbox, contextBridge)
|
|
||||||
shared/ # ipc-contract.ts — типы каналов, общие для всех процессов
|
|
||||||
renderer/ # React-приложение (обычный браузерный код, без Node API)
|
|
||||||
build/ # иконки и entitlements для electron-builder
|
|
||||||
resources/ # файлы, попадающие в собранное приложение (иконка окна)
|
|
||||||
out/ # результат electron-vite build
|
|
||||||
release/ # готовые установщики
|
|
||||||
```
|
|
||||||
|
|
||||||
## Как добавить свой IPC-канал
|
|
||||||
|
|
||||||
1. Опишите канал в `src/shared/ipc-contract.ts` (аргументы и результат).
|
|
||||||
2. Зарегистрируйте обработчик в `src/main/ipc.ts` через хелпер `handle(...)` — типы проверятся.
|
|
||||||
3. Добавьте метод в объект `api` в `src/preload/index.ts`.
|
|
||||||
4. Вызывайте из React как `window.api.мойМетод()` — тип подтянется автоматически.
|
|
||||||
|
|
||||||
События из main в renderer описываются в `IpcEvents` и подписываются через `subscribe(...)`
|
|
||||||
(пример — `onThemeChanged`, возвращает функцию отписки для `useEffect`).
|
|
||||||
|
|
||||||
## Что показывают демо-карточки
|
|
||||||
|
|
||||||
| Карточка | Каналы | Что демонстрирует |
|
|
||||||
| ---------------- | ----------------------------------------- | ------------------------------------------------------------- |
|
|
||||||
| Система | `app:info` | версии Electron/Chromium/Node, платформа, путь к userData |
|
|
||||||
| IPC ping | `ping` | минимальный invoke/handle |
|
|
||||||
| Нативный диалог | `dialog:open-file` | системный выбор файла + `fs.stat` в main |
|
|
||||||
| Всплывающее окно | `dialog:message-box`, `window:open-popup` | модальный диалог ОС и второе `BrowserWindow` |
|
|
||||||
| Тема оформления | `theme:*` | `nativeTheme` как источник правды, реакция на смену темы в ОС |
|
|
||||||
|
|
||||||
Тайтлбар системный на всех платформах — окно создаётся без `frame`/`titleBarStyle`, рамку
|
|
||||||
и кнопки свернуть/развернуть/закрыть рисует сама ОС.
|
|
||||||
|
|
||||||
## Безопасность
|
|
||||||
|
|
||||||
- `sandbox: true`, `contextIsolation: true`, `nodeIntegration: false`;
|
|
||||||
- renderer не имеет доступа к `ipcRenderer` — только к методам из `window.api`;
|
|
||||||
- `setWindowOpenHandler` и `will-navigate` не дают уйти на внешний origin, ссылки открываются
|
|
||||||
в системном браузере, причём только с протоколами из `ALLOWED_EXTERNAL_PROTOCOLS`;
|
|
||||||
- в `index.html` задан CSP без внешних источников.
|
|
||||||
|
|
||||||
## Иконки
|
|
||||||
|
|
||||||
`build/icon.png` (1024×1024) — исходник, из него electron-builder делает иконки для всех платформ;
|
|
||||||
`build/icon.icns` и `build/icon.ico` лежат рядом как готовые варианты. `resources/icon.png` —
|
|
||||||
иконка окна для Linux. Замените эти файлы своими, имена оставьте прежними.
|
|
||||||
|
|
||||||
## Подпись и нотаризация
|
|
||||||
|
|
||||||
По умолчанию выключены (`notarize: false`), сборки неподписанные — их можно запускать локально,
|
|
||||||
но macOS покажет предупреждение Gatekeeper при переносе на другую машину.
|
|
||||||
|
|
||||||
Чтобы включить:
|
|
||||||
|
|
||||||
- **macOS**: переменные окружения `CSC_LINK` (base64 сертификата Developer ID) и `CSC_KEY_PASSWORD`,
|
|
||||||
плюс `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID` и `notarize: true`
|
|
||||||
в `electron-builder.yml`.
|
|
||||||
- **Windows**: `CSC_LINK` и `CSC_KEY_PASSWORD` с .pfx-сертификатом.
|
|
||||||
|
|
||||||
В CI эти значения передаются секретами GitHub — место уже отмечено комментарием в
|
|
||||||
`.github/workflows/ci.yml`.
|
|
||||||
|
|
||||||
## Обход блокировок (zapret)
|
|
||||||
|
|
||||||
Движки и данные — из двух сторонних проектов, оба MIT, оба скачиваются `npm run zapret:fetch`
|
|
||||||
(вызывается автоматически из `npm run build`) в `resources/zapret/` (в git не хранится):
|
|
||||||
|
|
||||||
- **[bol-van/zapret](https://github.com/bol-van/zapret)** — сами движки: `tpws` (macOS, SOCKS5-прокси
|
|
||||||
без root) и `winws.exe` + драйвер **WinDivert** (Windows, служба с правами администратора).
|
|
||||||
WinDivert — LGPLv3/GPLv2 на выбор; лицензии обоих проектов кладутся рядом с бинарниками
|
|
||||||
(`LICENSE.zapret.txt`, `LICENSE.WinDivert.txt`).
|
|
||||||
- **[Flowseal/zapret-discord-youtube](https://github.com/Flowseal/zapret-discord-youtube)** —
|
- **[Flowseal/zapret-discord-youtube](https://github.com/Flowseal/zapret-discord-youtube)** —
|
||||||
списки сайтов (`resources/zapret/lists/`) и fake-пакеты (`resources/zapret/fakes/`). Версия
|
списки сайтов (`resources/zapret/lists/`) и fake-пакеты (`resources/zapret/fakes/`). (`FLOWSEAL_COMMIT`), лицензия —
|
||||||
зафиксирована по sha коммита в `scripts/fetch-zapret.mjs` (`FLOWSEAL_COMMIT`), лицензия —
|
|
||||||
`LICENSE.flowseal.txt`.
|
`LICENSE.flowseal.txt`.
|
||||||
|
|
||||||
Свои сгенерированные стратегии для Windows (`src/main/zapret/strategies.win32.generated.ts`,
|
|
||||||
22 варианта) — не батники: `scripts/import-flowseal-strategies.mjs` разбирает `general*.bat`
|
|
||||||
из того же репозитория Flowseal и переносит аргументы `winws` в TypeScript. Файл коммитится;
|
|
||||||
перегенерировать и свериться через `git diff` нужно вручную при обновлении `FLOWSEAL_COMMIT`.
|
|
||||||
На macOS `tpws` не умеет ни fake-пакетов, ни UDP — там свои профили
|
|
||||||
(`src/main/zapret/strategies.ts`, раздел `DARWIN_DEFINITIONS`) поверх тех же списков.
|
|
||||||
|
|
||||||
Списки сайтов пользователь может редактировать полностью (включая встроенные) — рабочая копия
|
|
||||||
живёт в `userData/zapret-lists` и переживает обновление приложения; `tpws`/`winws` сами
|
|
||||||
перечитывают список при изменении файла, перезапуск обхода не нужен (`src/main/zapret/lists.ts`).
|
|
||||||
|
|
||||||
### Windows: служба и права
|
|
||||||
|
|
||||||
`winws` требует драйвер WinDivert и права администратора, поэтому на Windows он работает не как
|
|
||||||
обычный дочерний процесс, а как служба Windows `rknboost-zapret` (`src/main/zapret/service.win32.ts`).
|
|
||||||
Устанавливает и настраивает её само приложение — не инсталлятор, — потому что аргументы `winws`
|
|
||||||
зависят от выбранной пользователем стратегии.
|
|
||||||
|
|
||||||
- **Приложение запрашивает права администратора при каждом запуске**
|
|
||||||
(`win.requestedExecutionLevel: requireAdministrator` в `electron-builder.yml`). Поэтому все
|
|
||||||
действия со службой выполняются напрямую, без отдельных запросов UAC по ходу работы.
|
|
||||||
Если приложение почему-то запущено без прав (dev-режим — там манифеста нет), включается
|
|
||||||
запасной путь: `outer.ps1` через `Start-Process -Verb RunAs` поднимает само приложение
|
|
||||||
с флагом `--elevated-service-task`, и UAC запрашивается на него, а не на `powershell.exe` —
|
|
||||||
в окне UAC пользователь должен видеть имя приложения, а не постороннюю программу.
|
|
||||||
При установке службы обычным пользователям выдаётся право на `start`/`stop` именно этой
|
|
||||||
службы (`sc sdset`), так что и без прав включение-выключение обхода работает.
|
|
||||||
- **Автозапуск с системой** включён по умолчанию (`sc config start= auto`) — обход работает и
|
|
||||||
после перезагрузки, не дожидаясь запуска приложения. Отключается переключателем в настройках
|
|
||||||
(тоже через UAC). Выключение обхода кнопкой на главном экране действует только до следующей
|
|
||||||
перезагрузки, если автозапуск остался включён — служба поднимется вместе с системой заново.
|
|
||||||
- **arm64 не поддерживается**: у WinDivert нет подписанной сборки под arm64 (только x64 через
|
|
||||||
эмуляцию на Windows 11), поэтому `electron-builder.yml` собирает Windows-инсталлятор только
|
|
||||||
для x64.
|
|
||||||
- Установщик — **per-user** (ставится в `%LocalAppData%`, один клик, без UAC на саму установку).
|
|
||||||
|
|
||||||
> ⚠️ Известный компромисс. Каталог установки доступен на запись обычному пользователю, а exe
|
|
||||||
> при этом требует администратора — значит подмена `RKNboost.exe` даёт злоумышленнику права
|
|
||||||
> администратора с подтверждения самого пользователя. Это осознанный выбор владельца проекта
|
|
||||||
> в пользу быстрой установки без UAC. Безопасный вариант — `perMachine: true`, тогда exe лежит
|
|
||||||
> в защищённом от записи `Program Files`, но установка снова требует UAC и мастера.
|
|
||||||
|
|
||||||
Служба работает от `LocalSystem`, и её `binPath` не должен указывать в каталог, куда обычный
|
|
||||||
пользователь может писать без прав администратора — поэтому при первом включении обхода
|
|
||||||
приложение копирует `winws.exe` и WinDivert в защищённый ACL'ом
|
|
||||||
каталог в `%ProgramData%` (`protectedWinwsPath()` в `src/main/zapret/paths.ts`) и указывает
|
|
||||||
службе именно на эту копию, а не на оригинал в папке установки. При удалении приложения
|
|
||||||
деинсталлятор (`build/installer.nsh`) сам останавливает и снимает службу вместе с этой копией.
|
|
||||||
|
|
||||||
- На одну машину служба одна: её конфиг (`%ProgramData%\rknboost\zapret\strategy.cfg`) хранит
|
|
||||||
стратегию того пользователя, который последним её менял, — с несколькими пользователями на
|
|
||||||
одной машине это разделяется некорректно.
|
|
||||||
|
|
||||||
## Авто-обновления
|
|
||||||
|
|
||||||
Не подключены. Если понадобятся: `npm i electron-updater`, добавить секцию `publish`
|
|
||||||
в `electron-builder.yml` и вызвать `autoUpdater.checkForUpdatesAndNotify()` в `src/main/index.ts`.
|
|
||||||
|
|||||||
@@ -12,8 +12,9 @@
|
|||||||
## 1. Палитра
|
## 1. Палитра
|
||||||
|
|
||||||
### Базовые цвета
|
### Базовые цвета
|
||||||
|
|
||||||
| Назначение | HEX | Заметка |
|
| Назначение | HEX | Заметка |
|
||||||
|---|---|---|
|
| ------------------------ | --------------------- | --------------------------------------------------------- |
|
||||||
| Фиолетовый (акцент) | `#a96bff` | HSL 275°, 100%, 71% — светлый, для тёмной темы и акцентов |
|
| Фиолетовый (акцент) | `#a96bff` | HSL 275°, 100%, 71% — светлый, для тёмной темы и акцентов |
|
||||||
| Фиолетовый (основной) | `#6a45b8` | тон ~40 — заливка кнопок, активные элементы |
|
| Фиолетовый (основной) | `#6a45b8` | тон ~40 — заливка кнопок, активные элементы |
|
||||||
| Тёмно-фиолетовый (текст) | `#5a2da8` | текст на светло-фиолетовом контейнере |
|
| Тёмно-фиолетовый (текст) | `#5a2da8` | текст на светло-фиолетовом контейнере |
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
| Оранжевый (исходный) | `#ffb76b` | стартовый цвет, ушёл в роль `tertiary` |
|
| Оранжевый (исходный) | `#ffb76b` | стартовый цвет, ушёл в роль `tertiary` |
|
||||||
|
|
||||||
### Связь оттенков
|
### Связь оттенков
|
||||||
|
|
||||||
Крем `#ece0d1` (тон ~33°) и оранжевый `#ffb76b` (тон ~31°) — родственники по тёплому углу спектра.
|
Крем `#ece0d1` (тон ~33°) и оранжевый `#ffb76b` (тон ~31°) — родственники по тёплому углу спектра.
|
||||||
Фиолетовый — холодный акцент в противовес тёплому нейтралу. Отсюда баланс «тёплая база + прохладный акцент».
|
Фиолетовый — холодный акцент в противовес тёплому нейтралу. Отсюда баланс «тёплая база + прохладный акцент».
|
||||||
|
|
||||||
@@ -30,7 +32,7 @@
|
|||||||
## 2. Роли Material 3
|
## 2. Роли Material 3
|
||||||
|
|
||||||
| Роль MD3 | Светлая тема | Текст на ней (`on-`) |
|
| Роль MD3 | Светлая тема | Текст на ней (`on-`) |
|
||||||
|---|---|---|
|
| ------------------------------- | ------------ | -------------------- |
|
||||||
| `primary` | `#6a45b8` | `#ffffff` |
|
| `primary` | `#6a45b8` | `#ffffff` |
|
||||||
| `primary-container` | `#ebddff` | `#5a2da8` |
|
| `primary-container` | `#ebddff` | `#5a2da8` |
|
||||||
| `secondary-container` | `#ebddff` | `#5a2da8` |
|
| `secondary-container` | `#ebddff` | `#5a2da8` |
|
||||||
@@ -45,8 +47,9 @@
|
|||||||
| `error-container` | `#ffdad6` | `#410002` |
|
| `error-container` | `#ffdad6` | `#410002` |
|
||||||
|
|
||||||
### Статусные цвета (тёплый подтон)
|
### Статусные цвета (тёплый подтон)
|
||||||
|
|
||||||
| Статус | Фон чипа | Текст | Точка |
|
| Статус | Фон чипа | Текст | Точка |
|
||||||
|---|---|---|---|
|
| ----------------- | --------- | --------- | --------- |
|
||||||
| Онлайн / успех | `#c0dd97` | `#2c4d0a` | `#639922` |
|
| Онлайн / успех | `#c0dd97` | `#2c4d0a` | `#639922` |
|
||||||
| Офлайн / нейтраль | `#e4dac9` | `#7a6a55` | `#a99a85` |
|
| Офлайн / нейтраль | `#e4dac9` | `#7a6a55` | `#a99a85` |
|
||||||
| Ошибка | `#ffdad6` | `#410002` | `#ba1a1a` |
|
| Ошибка | `#ffdad6` | `#410002` | `#ba1a1a` |
|
||||||
@@ -64,8 +67,9 @@
|
|||||||
- Моноширинный шрифт для технических данных: IP-адреса, VPN-ключи, rx/tx.
|
- Моноширинный шрифт для технических данных: IP-адреса, VPN-ключи, rx/tx.
|
||||||
|
|
||||||
Шкала:
|
Шкала:
|
||||||
|
|
||||||
| Элемент | Размер | Вес |
|
| Элемент | Размер | Вес |
|
||||||
|---|---|---|
|
| -------------------------------- | ------- | --- |
|
||||||
| Заголовок раздела | 20px | 500 |
|
| Заголовок раздела | 20px | 500 |
|
||||||
| Подзаголовок / название карточки | 15–16px | 500 |
|
| Подзаголовок / название карточки | 15–16px | 500 |
|
||||||
| Основной текст | 14px | 400 |
|
| Основной текст | 14px | 400 |
|
||||||
@@ -78,7 +82,7 @@
|
|||||||
## 4. Скругления (MD3 shape scale)
|
## 4. Скругления (MD3 shape scale)
|
||||||
|
|
||||||
| Элемент | Радиус |
|
| Элемент | Радиус |
|
||||||
|---|---|
|
| ---------------------------------- | ------- |
|
||||||
| Кнопки, чипы, инпуты (pill) | `100px` |
|
| Кнопки, чипы, инпуты (pill) | `100px` |
|
||||||
| Карточки / контейнеры | `24px` |
|
| Карточки / контейнеры | `24px` |
|
||||||
| Внутренние блоки таблицы / метрики | `16px` |
|
| Внутренние блоки таблицы / метрики | `16px` |
|
||||||
@@ -92,6 +96,7 @@
|
|||||||
## 5. Состояния и взаимодействие (MD3 state layers)
|
## 5. Состояния и взаимодействие (MD3 state layers)
|
||||||
|
|
||||||
Поверх элемента — полупрозрачный слой акцентного цвета:
|
Поверх элемента — полупрозрачный слой акцентного цвета:
|
||||||
|
|
||||||
- Hover: ~8% непрозрачности.
|
- Hover: ~8% непрозрачности.
|
||||||
- Focus / pressed: ~12%.
|
- Focus / pressed: ~12%.
|
||||||
- Для фиолетовых элементов слой фиолетовый, для нейтральных — тёмный нейтрал.
|
- Для фиолетовых элементов слой фиолетовый, для нейтральных — тёмный нейтрал.
|
||||||
@@ -99,10 +104,12 @@
|
|||||||
Элевация — через тональные поверхности (светлее/темнее крем), а не через тени. Иерархия строится тональностью: рабочая зона данных светлее крема, крем — для фона и хрома.
|
Элевация — через тональные поверхности (светлее/темнее крем), а не через тени. Иерархия строится тональностью: рабочая зона данных светлее крема, крем — для фона и хрома.
|
||||||
|
|
||||||
**Опора на платформу.**
|
**Опора на платформу.**
|
||||||
|
|
||||||
- На десктопе (указатель) hover-слой работает как первичная обратная связь.
|
- На десктопе (указатель) hover-слой работает как первичная обратная связь.
|
||||||
- На тач-устройствах hover не срабатывает — обратная связь строится на нажатии и выборе, не на наведении.
|
- На тач-устройствах hover не срабатывает — обратная связь строится на нажатии и выборе, не на наведении.
|
||||||
|
|
||||||
**Инертные vs действенные элементы.**
|
**Инертные vs действенные элементы.**
|
||||||
|
|
||||||
- Навигационные строки и статичные строки данных — **инертны** (без эффекта вдавливания); активное состояние показывается заливкой-таблеткой (`primary-container`).
|
- Навигационные строки и статичные строки данных — **инертны** (без эффекта вдавливания); активное состояние показывается заливкой-таблеткой (`primary-container`).
|
||||||
- Реальные действия (кнопки, переключатель, QR, удаление) могут иметь лёгкий pressed-эффект через state layer акцентного цвета (~12%).
|
- Реальные действия (кнопки, переключатель, QR, удаление) могут иметь лёгкий pressed-эффект через state layer акцентного цвета (~12%).
|
||||||
|
|
||||||
@@ -126,7 +133,7 @@
|
|||||||
Фиолетовый акцент — общий со светлой темой по смыслу, но осветлён под тёмный фон.
|
Фиолетовый акцент — общий со светлой темой по смыслу, но осветлён под тёмный фон.
|
||||||
|
|
||||||
| Роль | Тёмная тема | Текст на ней (`on-`) |
|
| Роль | Тёмная тема | Текст на ней (`on-`) |
|
||||||
|---|---|---|
|
| ------------------------------- | ------------------------------ | -------------------- |
|
||||||
| `surface` | `#1a1611` | `#ede2d4` |
|
| `surface` | `#1a1611` | `#ede2d4` |
|
||||||
| `surface-container` | `#251f18` | `#ede2d4` |
|
| `surface-container` | `#251f18` | `#ede2d4` |
|
||||||
| `surface-container-high` | `#302820` | `#ede2d4` |
|
| `surface-container-high` | `#302820` | `#ede2d4` |
|
||||||
@@ -142,10 +149,11 @@
|
|||||||
| приглушённый текст | `#b6a994` | — |
|
| приглушённый текст | `#b6a994` | — |
|
||||||
|
|
||||||
### Статусные цвета (тёмная тема)
|
### Статусные цвета (тёмная тема)
|
||||||
|
|
||||||
Фон и текст осветлены/переинвертированы под тёмный фон, подтон тот же тёплый.
|
Фон и текст осветлены/переинвертированы под тёмный фон, подтон тот же тёплый.
|
||||||
|
|
||||||
| Статус | Фон чипа | Текст | Точка |
|
| Статус | Фон чипа | Текст | Точка |
|
||||||
|---|---|---|---|
|
| ----------------- | --------- | --------- | --------- |
|
||||||
| Онлайн / успех | `#2d4413` | `#c8e3a0` | `#8fc24f` |
|
| Онлайн / успех | `#2d4413` | `#c8e3a0` | `#8fc24f` |
|
||||||
| Офлайн / нейтраль | `#34291c` | `#d0c0a4` | `#b0a085` |
|
| Офлайн / нейтраль | `#34291c` | `#d0c0a4` | `#b0a085` |
|
||||||
| Ошибка | `#5c1414` | `#ffb4ab` | `#ff5449` |
|
| Ошибка | `#5c1414` | `#ffb4ab` | `#ff5449` |
|
||||||
@@ -161,16 +169,19 @@
|
|||||||
Эти компоненты используют одни и те же роли на обеих платформах; различается только их расположение в раскладке.
|
Эти компоненты используют одни и те же роли на обеих платформах; различается только их расположение в раскладке.
|
||||||
|
|
||||||
### Кнопки
|
### Кнопки
|
||||||
|
|
||||||
- **Главное действие** (Создать пользователя): заливка `primary` `#6a45b8`, текст белый (`on-primary`), pill.
|
- **Главное действие** (Создать пользователя): заливка `primary` `#6a45b8`, текст белый (`on-primary`), pill.
|
||||||
- **Вторичное** (Обновить): tonal — фон `secondary-container` `#ebddff`, текст `#5a2da8`, pill.
|
- **Вторичное** (Обновить): tonal — фон `secondary-container` `#ebddff`, текст `#5a2da8`, pill.
|
||||||
- **Опасное** (Выход, удаление): контур `#e6c4bc` / роль `error`, текст `#b34a3a`, без заливки, pill.
|
- **Опасное** (Выход, удаление): контур `#e6c4bc` / роль `error`, текст `#b34a3a`, без заливки, pill.
|
||||||
- Иконка слева от текста, gap 6–8px.
|
- Иконка слева от текста, gap 6–8px.
|
||||||
|
|
||||||
### Поля ввода и поиска
|
### Поля ввода и поиска
|
||||||
|
|
||||||
- Pill-инпут, фон `surface-bright` `#fdfaf6`, бордюр `outline` `#d8cdbe`, плейсхолдер приглушённым.
|
- Pill-инпут, фон `surface-bright` `#fdfaf6`, бордюр `outline` `#d8cdbe`, плейсхолдер приглушённым.
|
||||||
- Поле поиска — с иконкой поиска слева.
|
- Поле поиска — с иконкой поиска слева.
|
||||||
|
|
||||||
### Чипы статуса
|
### Чипы статуса
|
||||||
|
|
||||||
- Pill, маленькая цветная точка + подпись. Цвета — из таблицы статусов (успех / нейтраль / ошибка), текст соответствующей роли `on-`.
|
- Pill, маленькая цветная точка + подпись. Цвета — из таблицы статусов (успех / нейтраль / ошибка), текст соответствующей роли `on-`.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -178,10 +189,12 @@
|
|||||||
# Часть III. Десктоп
|
# Часть III. Десктоп
|
||||||
|
|
||||||
### Компоновка
|
### Компоновка
|
||||||
|
|
||||||
- Многоколоночная сетка.
|
- Многоколоночная сетка.
|
||||||
- Заголовок раздела — в шапке контента.
|
- Заголовок раздела — в шапке контента.
|
||||||
|
|
||||||
### Боковое меню (постоянная панель)
|
### Боковое меню (постоянная панель)
|
||||||
|
|
||||||
- Фиксированная ширина ~240px, всегда на экране.
|
- Фиксированная ширина ~240px, всегда на экране.
|
||||||
- Фон `#f0e8dd` (`surface-container`, на тон темнее основного — мягкое разделение без жёсткой границы).
|
- Фон `#f0e8dd` (`surface-container`, на тон темнее основного — мягкое разделение без жёсткой границы).
|
||||||
- Логотип сверху, навигация ниже, «Выход» прижат к низу (`margin-top: auto`).
|
- Логотип сверху, навигация ниже, «Выход» прижат к низу (`margin-top: auto`).
|
||||||
@@ -189,6 +202,7 @@
|
|||||||
- Неактивные: текст `#5a4a3a`, при наведении — лёгкая фиолетовая подсветка (state layer).
|
- Неактивные: текст `#5a4a3a`, при наведении — лёгкая фиолетовая подсветка (state layer).
|
||||||
|
|
||||||
### Таблица пользователей
|
### Таблица пользователей
|
||||||
|
|
||||||
- Контейнер `surface-bright` `#fdfaf6`, скругление 16px, бордюр `#ece2d3` (`outline-variant`).
|
- Контейнер `surface-bright` `#fdfaf6`, скругление 16px, бордюр `#ece2d3` (`outline-variant`).
|
||||||
- Шапка на тоне `#f0e8dd`, текст среднего веса `#5a4a3a`.
|
- Шапка на тоне `#f0e8dd`, текст среднего веса `#5a4a3a`.
|
||||||
- Разделители строк — `0.5px solid #ece2d3`.
|
- Разделители строк — `0.5px solid #ece2d3`.
|
||||||
@@ -201,6 +215,7 @@
|
|||||||
# Часть IV. Мобильный
|
# Часть IV. Мобильный
|
||||||
|
|
||||||
## 1. Принципы адаптации
|
## 1. Принципы адаптации
|
||||||
|
|
||||||
1. Одна колонка. Контент течёт вертикально на ширине телефона (рабочая ширина ~360px, поля по 16px).
|
1. Одна колонка. Контент течёт вертикально на ширине телефона (рабочая ширина ~360px, поля по 16px).
|
||||||
2. Никаких таблиц с горизонтальной прокруткой — табличные данные разворачиваются в карточки.
|
2. Никаких таблиц с горизонтальной прокруткой — табличные данные разворачиваются в карточки.
|
||||||
3. Боковое меню десктопа превращается в модальный navigation drawer (паттерн Material 3).
|
3. Боковое меню десктопа превращается в модальный navigation drawer (паттерн Material 3).
|
||||||
@@ -210,22 +225,27 @@
|
|||||||
## 2. Навигация и drawer
|
## 2. Навигация и drawer
|
||||||
|
|
||||||
### Модель
|
### Модель
|
||||||
|
|
||||||
Приложение имеет один основной раздел («Пользователи»). На десктопе это постоянная боковая панель ~240px; на мобильном — **модальный drawer**, скрытый по умолчанию.
|
Приложение имеет один основной раздел («Пользователи»). На десктопе это постоянная боковая панель ~240px; на мобильном — **модальный drawer**, скрытый по умолчанию.
|
||||||
|
|
||||||
### Открытие и закрытие (важно по MD3)
|
### Открытие и закрытие (важно по MD3)
|
||||||
|
|
||||||
- Открытие — **только тапом по иконке-гамбургеру** в верхнем баре.
|
- Открытие — **только тапом по иконке-гамбургеру** в верхнем баре.
|
||||||
- Закрытие — тап по затемнению (scrim) или свайп панели к её левому краю (срабатывает при сдвиге ~40px+).
|
- Закрытие — тап по затемнению (scrim) или свайп панели к её левому краю (срабатывает при сдвиге ~40px+).
|
||||||
- Открытие edge-swipe'ом от края экрана **не используется**: жест конфликтует с системным «назад» (predictive back) на Android.
|
- Открытие edge-swipe'ом от края экрана **не используется**: жест конфликтует с системным «назад» (predictive back) на Android.
|
||||||
|
|
||||||
### Поведение
|
### Поведение
|
||||||
|
|
||||||
- Drawer выезжает слева, поверх контента, с затемнением под ним.
|
- Drawer выезжает слева, поверх контента, с затемнением под ним.
|
||||||
- Затемнение блокирует взаимодействие с экраном, пока панель открыта.
|
- Затемнение блокирует взаимодействие с экраном, пока панель открыта.
|
||||||
- Анимация — горизонтальный сдвиг панели + плавное появление scrim (быстрый, «решительный» easing, ~250–280 мс); затемнение и сдвиг анимируются вместе.
|
- Анимация — горизонтальный сдвиг панели + плавное появление scrim (быстрый, «решительный» easing, ~250–280 мс); затемнение и сдвиг анимируются вместе.
|
||||||
|
|
||||||
### Содержимое панели
|
### Содержимое панели
|
||||||
|
|
||||||
Поверхность панели — `surface-container` (на тон темнее основного экрана), мягкое разделение без жёсткой границы. Скругление контейнера 24px, правые углы.
|
Поверхность панели — `surface-container` (на тон темнее основного экрана), мягкое разделение без жёсткой границы. Скругление контейнера 24px, правые углы.
|
||||||
|
|
||||||
Сверху вниз:
|
Сверху вниз:
|
||||||
|
|
||||||
1. **Шапка** — вордмарк приложения (роль заголовка) + версия (роль приглушённого текста), внутренний отступ.
|
1. **Шапка** — вордмарк приложения (роль заголовка) + версия (роль приглушённого текста), внутренний отступ.
|
||||||
2. **Тонкий разделитель** (`outline`, 0.5px).
|
2. **Тонкий разделитель** (`outline`, 0.5px).
|
||||||
3. **Навигация** — пункты-строки с ведущей иконкой 22px и подписью 15px, gap 16px.
|
3. **Навигация** — пункты-строки с ведущей иконкой 22px и подписью 15px, gap 16px.
|
||||||
@@ -235,17 +255,21 @@
|
|||||||
4. **«Выход»** — прижат к низу (`margin-top: auto`), стиль опасного действия (контур, без заливки), pill, иконка слева.
|
4. **«Выход»** — прижат к низу (`margin-top: auto`), стиль опасного действия (контур, без заливки), pill, иконка слева.
|
||||||
|
|
||||||
### Замечание про объём меню
|
### Замечание про объём меню
|
||||||
|
|
||||||
Длинный список разделов (как у Gmail-паттерна) оправдан только при 5+ назначениях. У приложения раздел один, поэтому drawer минимальный: шапка → активный пункт → «Выход» внизу. Доп. пункты добавляются только под реальные экраны.
|
Длинный список разделов (как у Gmail-паттерна) оправдан только при 5+ назначениях. У приложения раздел один, поэтому drawer минимальный: шапка → активный пункт → «Выход» внизу. Доп. пункты добавляются только под реальные экраны.
|
||||||
|
|
||||||
## 3. Верхний бар (top app bar)
|
## 3. Верхний бар (top app bar)
|
||||||
|
|
||||||
- Слева — иконка-гамбургер (кнопка-таргет ≥ 42px, круглая зона нажатия).
|
- Слева — иконка-гамбургер (кнопка-таргет ≥ 42px, круглая зона нажатия).
|
||||||
- Рядом — заголовок раздела (роль заголовка, 20px / 500).
|
- Рядом — заголовок раздела (роль заголовка, 20px / 500).
|
||||||
- Бар без заливки и без тени; высота поверхности задаётся фоном экрана (`surface`).
|
- Бар без заливки и без тени; высота поверхности задаётся фоном экрана (`surface`).
|
||||||
|
|
||||||
## 4. Экран «Пользователи»
|
## 4. Экран «Пользователи»
|
||||||
|
|
||||||
Порядок блоков сверху вниз: верхний бар → карточка сервера → «Добавить сервер» → форма создания → счётчик → список пользователей.
|
Порядок блоков сверху вниз: верхний бар → карточка сервера → «Добавить сервер» → форма создания → счётчик → список пользователей.
|
||||||
|
|
||||||
### 4.1. Карточка VPN-сервера
|
### 4.1. Карточка VPN-сервера
|
||||||
|
|
||||||
- Поверхность — `surface-bright`, скругление 24px.
|
- Поверхность — `surface-bright`, скругление 24px.
|
||||||
- **Активная обводка** — рамка 2px роли `primary` (приглушённый светло-фиолетовый вариант): сигнал, что сервер выбран/активен.
|
- **Активная обводка** — рамка 2px роли `primary` (приглушённый светло-фиолетовый вариант): сигнал, что сервер выбран/активен.
|
||||||
- Слева — компактный **переключатель** (pill-трек, маленький круглый ползунок). Состояние «вкл» отражается цветом трека из роли статуса «успех» и положением ползунка.
|
- Слева — компактный **переключатель** (pill-трек, маленький круглый ползунок). Состояние «вкл» отражается цветом трека из роли статуса «успех» и положением ползунка.
|
||||||
@@ -253,20 +277,25 @@
|
|||||||
- Справа — нейтральный чип счётчика пиров (роль статуса «нейтраль», pill).
|
- Справа — нейтральный чип счётчика пиров (роль статуса «нейтраль», pill).
|
||||||
|
|
||||||
### 4.2. «Добавить сервер»
|
### 4.2. «Добавить сервер»
|
||||||
|
|
||||||
- Полноширинная кнопка-«призрак»: пунктирный контур (`outline`), без заливки, иконка `+` слева, приглушённый текст. Pill (100px).
|
- Полноширинная кнопка-«призрак»: пунктирный контур (`outline`), без заливки, иконка `+` слева, приглушённый текст. Pill (100px).
|
||||||
|
|
||||||
### 4.3. Форма создания пользователя
|
### 4.3. Форма создания пользователя
|
||||||
|
|
||||||
- Поле ввода: pill-инпут на `surface-bright`, бордюр `outline`, плейсхолдер приглушённым. Полная ширина.
|
- Поле ввода: pill-инпут на `surface-bright`, бордюр `outline`, плейсхолдер приглушённым. Полная ширина.
|
||||||
- Под ним пара кнопок в ряд (50/50):
|
- Под ним пара кнопок в ряд (50/50):
|
||||||
- **Создать** — главное действие: заливка `primary`, текст `on-primary`, иконка `+`, pill.
|
- **Создать** — главное действие: заливка `primary`, текст `on-primary`, иконка `+`, pill.
|
||||||
- **Обновить** — вторичное: tonal (`secondary-container` / `on-secondary-container`), иконка обновления, pill.
|
- **Обновить** — вторичное: tonal (`secondary-container` / `on-secondary-container`), иконка обновления, pill.
|
||||||
|
|
||||||
### 4.4. Список пользователей
|
### 4.4. Список пользователей
|
||||||
|
|
||||||
- Над списком — счётчик («N пользователей») приглушённым текстом 12px.
|
- Над списком — счётчик («N пользователей») приглушённым текстом 12px.
|
||||||
- Десктопная таблица (`#`, Статус, Имя, IP, rx, tx, действия) на мобильном **разворачивается в карточки** — по одной на пользователя.
|
- Десктопная таблица (`#`, Статус, Имя, IP, rx, tx, действия) на мобильном **разворачивается в карточки** — по одной на пользователя.
|
||||||
|
|
||||||
### 4.5. Карточка пользователя
|
### 4.5. Карточка пользователя
|
||||||
|
|
||||||
Поверхность `surface-bright`, бордюр `outline-variant`, скругление 24px. Внутри:
|
Поверхность `surface-bright`, бордюр `outline-variant`, скругление 24px. Внутри:
|
||||||
|
|
||||||
1. **Верхняя строка** — слева порядковый `#N` (приглушённый) + имя (16px / 500); справа чип статуса «онлайн» (роль статуса «успех»: точка + подпись, pill).
|
1. **Верхняя строка** — слева порядковый `#N` (приглушённый) + имя (16px / 500); справа чип статуса «онлайн» (роль статуса «успех»: точка + подпись, pill).
|
||||||
2. **Строка IP** — иконка-глобус + адрес моноширинным.
|
2. **Строка IP** — иконка-глобус + адрес моноширинным.
|
||||||
3. **Два блока метрик** (rx / tx) в ряд 50/50: внутренние блоки на `surface-container`, скругление 16px. Сверху подпись со стрелкой (↓ rx / ↑ tx) приглушённым 12px, под ней значение моноширинным 18px / 500.
|
3. **Два блока метрик** (rx / tx) в ряд 50/50: внутренние блоки на `surface-container`, скругление 16px. Сверху подпись со стрелкой (↓ rx / ↑ tx) приглушённым 12px, под ней значение моноширинным 18px / 500.
|
||||||
@@ -275,7 +304,7 @@
|
|||||||
## 5. Сводка мобильных компонентов (через роли и токены)
|
## 5. Сводка мобильных компонентов (через роли и токены)
|
||||||
|
|
||||||
| Компонент | Поверхность / контур | Форма | Текст |
|
| Компонент | Поверхность / контур | Форма | Текст |
|
||||||
|---|---|---|---|
|
| --------------------- | -------------------------------------- | ---------- | --------------------------- |
|
||||||
| Карточка сервера | `surface-bright` + рамка `primary` 2px | 24px | подзаголовок + mono |
|
| Карточка сервера | `surface-bright` + рамка `primary` 2px | 24px | подзаголовок + mono |
|
||||||
| Карточка пользователя | `surface-bright` + `outline-variant` | 24px | подзаголовок + mono |
|
| Карточка пользователя | `surface-bright` + `outline-variant` | 24px | подзаголовок + mono |
|
||||||
| Блок метрики (rx/tx) | `surface-container` | 16px | подпись + mono |
|
| Блок метрики (rx/tx) | `surface-container` | 16px | подпись + mono |
|
||||||
@@ -292,7 +321,7 @@
|
|||||||
# Отличия десктоп / мобильный (кратко)
|
# Отличия десктоп / мобильный (кратко)
|
||||||
|
|
||||||
| Аспект | Десктоп | Мобильный |
|
| Аспект | Десктоп | Мобильный |
|
||||||
|---|---|---|
|
| -------------------- | -------------------------------- | ---------------------------------- |
|
||||||
| Навигация | постоянная боковая панель ~240px | модальный drawer (гамбургер) |
|
| Навигация | постоянная боковая панель ~240px | модальный drawer (гамбургер) |
|
||||||
| Список пользователей | таблица с колонками | карточки, по одной на пользователя |
|
| Список пользователей | таблица с колонками | карточки, по одной на пользователя |
|
||||||
| rx / tx | ячейки таблицы | два блока-метрики внутри карточки |
|
| rx / tx | ячейки таблицы | два блока-метрики внутри карточки |
|
||||||
|
|||||||
Reference in New Issue
Block a user