Кнопка в карточке ноды открывает модалку: выбор маршрута (по умолчанию /
IP-адрес / интерфейс) с подсказками из данных ноды, ожидание, затем отчёт
картинкой либо тем же отчётом в JSON. Есть копирование JSON, скачивание
SVG, повтор проверки и полноэкранный режим.
Кнопка показывается только для узлов 3.3.0+ (versions.node) и только для
подключённых — иначе админ упирался бы в ошибку панели.
Отчёт вставляется как <img src="data:...">, а не сырым SVG в разметку: он
несёт встроенный моноширинный шрифт и <style>, на которых держится
выравнивание колонок, и санитайзер их выбрасывает. В <img> SVG рисуется в
изолированном контексте без скриптов и внешних запросов — и безопаснее, и
вернее по виду.
Зум и панорамирование — react-zoom-pan-pinch с той же конфигурацией, что в
самой панели Remnawave. Своя реализация обязательна: index.html задаёт
user-scalable=no, поэтому браузерного пинча нет ни в мобильном вебе, ни в
Mini App. Колесо намеренно не масштабирует, иначе прокрутка над отчётом
превращается в зум.
В отличие от панели стартовый масштаб считается от читаемой ширины: на
телефоне «вписано по ширине» превращает отчёт в серую рябь. На 390px это
2.42x с панорамированием, на десктопе — 1x. При смене ширины (полный
экран, поворот) масштаб пересчитывается.
Опрос задачи с потолком в три минуты — иначе забытая открытой модалка
опрашивала бы панель бесконечно. Пока проверка идёт, модалка не
закрывается: задача уже поставлена, терять её результат молча хуже.
Заодно в AdminRemnawave.tsx осели автоправки biome (import type,
Math.pow -> **) — файл всё равно правился, они механические.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tsc в vite-проекте ничего не эмитит (noEmit) — только тип-гейт, который
уже стоит в CI и в самом docker-воркфлоу (build-and-push: needs: lint).
build:docker = vite build; npm run build с tsc сохранён для людей и CI.
~10s с каждой сборки образа. Спасибо @kewldan.
- telegram-webview-guards.grit: исключение сузил с подстроки "platform"
до якорного "src/platform/" — прежний eslint-override действовал ровно на
src/platform/**, а bare-substring щадил бы любой файл с "platform" в имени
(проверено: src/platform всё ещё exempt, гвард срабатывает в обычных файлах)
- biome.json: вернул исключения *.min.js/*.min.css из .prettierignore
- lint-staged: вернул форматирование *.css в pre-commit (biome format --write) —
иначе CI-формат-чек ловил бы css, который хук не трогал
- lint.yml: метка шага "Run ESLint" → "Run linter" (выполняет biome)
Один инструмент вместо девяти dev-зависимостей. Замеры на этом репо
(483 файла, медиана из 3 прогонов, один и тот же компьютер):
- eslint . ~20.4s -> biome lint . ~2s
- prettier --check . ~8.8s -> biome format . ~0.2s
- суммарно линт+формат ~29s -> biome check . ~2-3s (~10x)
Паритет с прежними правилами:
- форматирование бинарно совместимо с Prettier-конфигом (biome format
--write тронул 9 файлов из ~480 — микроразличия);
- запрет window.confirm/alert/open/prompt и navigator.clipboard
(Telegram WebView) перенесён GritQL-плагином
biome-plugins/telegram-webview-guards.grit c теми же сообщениями
и исключениями для src/platform/** и clipboard.ts; плагин сразу
нашёл window.open в Wheel.tsx под старым eslint-disable —
переведён на biome-ignore;
- react-hooks-правила: useExhaustiveDependencies/useHookAtTopLevel;
- новые для проекта правила (a11y и пр.) не включались или понижены
до warning, чтобы миграция не смешивалась с чисткой кода — включать
можно отдельными PR.
Ограничения (задокументированы): сортировка tailwind-классов
(prettier-plugin-tailwindcss) в Biome пока nursery — не включена;
CSS исключён из Biome (парсер спотыкается о Tailwind-синтаксис
globals.css) — файл один и правится редко.
CI не меняется: имена npm-скриптов lint/format:check сохранены.
lint-staged переведён на biome check --write.
Нативный <input type="date"> выглядел чужеродно (браузерный светлый календарь).
Новый общий компонент DateField: react-day-picker внутри Radix Popover, тёмная
тема через CSS-переменные .rdp-dark, навигация — наши иконки из барреля,
локализация месяцев/дней по языку кабинета (date-fns). Принимает/отдаёт
'YYYY-MM-DD' — drop-in замена нативного инпута. Подключён в селекторе периода
статистики (свой период). Остальные места — следующими шагами.
Replace the cabinet's hand-written heroicons-style SVG icon components with
the Remnawave panel's own icon family — Phosphor via react-icons/pi (Duotone).
All icons now live in a single central barrel (src/components/icons:
index + extended-icons + editor-icons, 144 icons); every feature file imports
from it instead of redefining inline SVGs (~440 icon defs removed).
- Add react-icons dependency
- Desktop nav (AppShell) now uses the central Phosphor icons, removing a
stroke-vs-duotone inconsistency the partial migration introduced
- Icons with custom props (SortIcon's direction, expandable chevrons) kept as
thin Phosphor wrappers; the RemnawaveIcon brand logo and animated SVGs
(checkmarks, spinners) are intentionally left as-is
- Restore the original per-call-site icon sizes wherever the centralized
default differed (211 call sites)
This package was not imported anywhere in the codebase but pulled in
@excalidraw/excalidraw and old @radix-ui packages that caused peer
dependency warnings on every npm install (React 19 vs React 16-18).
Tiptap is used directly via @tiptap/* packages.
Node fill color now encodes subscription status (paid/trial active/expired),
while border color indicates referral role (partner/top/active referrer).
Added BFS-based radial initial positioning to spread referral branches outward.
Updated legend with two sections and adjusted FA2 gravity for less clustering.