«Добавить правило» → «Сайты, IP, программы (просто)»: куда (через VPN / напрямую / блок) и списки по строке в синтаксисе v2rayN. 2ip.ru — сайт с поддоменами (domain_suffix), geosite:/geoip: — существующие наборы, сайты и программы — логическое ИЛИ. Документ не меняется до «Добавить», правило встаёт сразу после sniff/hijack-dns, «Добавить и применить» — тем же путём, что «Применить». «Проверить сайт» над списком проходит правила в порядке ядра и показывает сработавшее правило и выход; условия по программе — «может перехватить». Наборы проверяет само ядро (sing-box rule-set match -f), вне GUI-потока, с номером поколения.
11 KiB
Маршрутизация: структурный редактор sing-box
Раздел «Маршрутизация» в боковом меню редактирует активный native JSON
sing-box (data/configs/sing-box/*.json). Своей модели маршрутизации у
приложения нет: формы меняют разобранный JSON на месте и записывают его обратно,
страница «JSON» показывает тот же документ.
Страницы
| Подпункт | Что редактирует |
|---|---|
| Обзор | выбор конфига и шаблона, сравнение со стоком, сброс всего конфига или одного раздела, статус |
| Правила | route.rules (сверху вниз, первое совпадение) и route.final |
| Наборы правил | route.rule_set: local / remote / inline |
| DNS | параметры dns, dns.servers, dns.rules |
| Исходящие | outbounds и endpoints; proxy — слот выбранного сервера, только просмотр |
| Система | inbounds (TUN), остальные route.*, log, experimental, прочие секции |
| JSON | весь документ текстом |
Правки живые: «Готово» закрывает подстраницу, «Отменить правки» возвращает объект
к состоянию на момент открытия. На диск пишут только «Сохранить»/«Применить»
в верхней панели; «Проверить» запускает sing-box check бандл-ядром по
редактируемому JSON в фоне (служебные подстановки запуска в эту проверку не входят).
Простое правило и «Проверить сайт»
- «Добавить правило» → «Сайты, IP, программы (просто)» (
ui/singbox/simple_rule_page.py, разбор —singbox_config/simple_rule.py): куда (через VPN / напрямую / блок) и списки по одному в строке в синтаксисе v2rayN.2ip.ru—domain_suffix(сайт и поддомены),full:/keyword:/regexp:,geosite:/geoip:— существующие наборы,geoip:private—ip_is_private, программа с путём —process_path. Сайты/IP и программы собираются вlogicalor: в ядре разные виды условий идут через И. Документ не меняется до «Добавить»; правило встаёт сразу послеsniff/hijack-dns. «Добавить и применить» идёт тем же путём, что «Применить». - «Проверить сайт» над списком (
singbox_config/route_explain.py,application/route_check.py): порядок ядра — сверху вниз до первого совпадения,sniff/resolve/route-optionsне останавливают, иначеroute.final. Запрос — браузер: TCP/443/TLS, программа неизвестна; условия вроде процесса дают «может перехватить», перебор идёт дальше. IP-условия — только в TUN или послеresolve. Наборы проверяет само ядро:sing-box rule-set match -f <format>(без-fядро не читает.srs), наборы по URL — «не узнать». Проверяется редактируемый документ, включая несохранённые правки.
Откуда берутся поля
assets/sing-box-schema/schema.json — JSON Schema, которую генерирует само
закреплённое ядро (sing-box schema) из своих Go-структур:
python3 scripts/generate_singbox_schema.py после смены пина sing-box в
scripts/core-lock.windows-x64.json. Форк не умеет описать несколько своих
типов (amnezia-диапазоны endpoints, byte-размеры, providers); генератор
ослабляет только их и пишет пути в loose-paths.txt. Тест гарантирует, что их
нет в route, dns, inbounds, outbounds, log и experimental; такие
поля endpoints редактируются как JSON.
sing-box check по исходному JSON не видит служебных подстановок запуска.
Поэтому outbound direct и DNS-сервер bootstrap-dns, на которые запуск
ссылается сам, помечены как нужные приложению, а их удаление требует
подтверждения.
xray_fluent/singbox_config/schema.py— чтение схемы: поля объекта с учётом дискриминатора (type,action),Listable-поля, ссылки на теги (x-tag-reference).xray_fluent/singbox_config/catalog.py— только подписи, закреплённые поля и сводки строк. Поле без подписи всё равно доступно под своим native-ключом.xray_fluent/ui/singbox/form.py— форма показывает заданные, обязательные и закреплённые поля; всё остальное, что знает ядро, — в «Добавить параметр». Сменаtype/actionубирает поля, которые новый вариант не принимает.
Инварианты
- Открытие страниц и объектов не меняет JSON (пустые секции создаются только при первой настоящей правке).
- Неизвестные ядру ключи сохраняются и показываются как «неизвестное поле».
- Одиночное значение
Listable-поля остаётся скаляром, пока в нём один элемент. - При ошибке JSON разделы только читают и предлагают исправить текст на странице «JSON».
- Обновление приложения сливает стоковый шаблон посекционно
(
template_sync.merge_stock_sections): нетронутые разделы следуют за стоком, изменённые остаются вашими. Принудительной синхронизации DNS больше нет. - Виджеты редактора удаляются только через
qt_lifecycle.dispose_later: скрытие сразу, разрушение на следующем такте при выключенном циклическом GC (уScrollArea/PlainTextEdit/RoundMenuqfluentwidgets естьSmoothScrollDelegate, гонка GC с деструкторами роняет процесс, на Windows — особенно). Меню «Добавить…» строятся по клику и удаляются после закрытия. - qfluentwidgets 1.11 не освобождает Python-обёртки удалённых стилизованных
виджетов (лямбды на
destroyedиqconfig.themeChanged). Глобально это не патчится: освобождённые обёртки попадают под ту же гонку GC во всём приложении. Поэтому редактор бережёт виджеты: строки списков обновляются на месте, невидимые разделы пересобираются только при показе, повторная загрузка того же текста ничего не пересобирает, схема ядра грузится при первой сборке раздела. - Смена
type/actionпересобирает форму на следующем такте: выбор приходит из всплывающего меню самого комбобокса. - Незафиксированный ввод (списки и JSON применяются с задержкой) сбрасывается в документ по потере фокуса, «Готово», переключению раздела и перед «Сохранить/Применить/Проверить».
Графика и анимация
- Строки списков несут значок-плитку (
ui/singbox/visuals.py): тон по смыслу — прокси = акцент, напрямую = зелёный (success), блок =error, DNS =info, служебные действия =warning. Цвета берутся изui/theme.pyв момент отрисовки, так что следуют теме и акценту; hex-литералов внеtheme.pyнет. - На странице раздела только заголовок и данные. Пояснения для новичков и живая
иллюстрация открываются ссылкой «Как это б#&^ь работает?» в стоковом окне
MessageBoxBaseповерх приложения (ui/singbox/guide.py: текстыGUIDES, окно удаляется черезdispose_later). Иллюстрация (ui/singbox/art.py,QPainter) рисует конфиг пользователя: «Обзор» — карта, где пакеты уходят в прокси, напрямую или в блок пропорционально числу правил (visuals.rule_outcomes); «Правила» — зонд идёт сверху вниз и останавливается на первом совпадении; «DNS» — вращающийся глобус и запрос/ответ; «Исходящие» — хаб с импульсами; «Система» — туннель TUN; «JSON» — набор текста. - Анимации работают на
motion.FrameGate: кадры (25 fps, у JSON 12 fps) идут только пока виджет виден и в Windows не выключена анимация интерфейса; иначе рисуется неподвижный кадр. Фаза берётся из реального времени. - Иконки Fluent рисуются из кэша растров по (иконка, цвет, размер, DPR):
FluentIcon.render(fill=…)разбирает SVG на каждом вызове. - Микроанимации: вспышка строки при добавлении/дублировании/перемещении,
пульсирующая точка несохранённых правок, подстраницы въезжают справа, возврат —
слева, разделы появляются снизу (
PopUpAniStackedWidgetqfluentwidgets; текущая страница переключается сразу, анимируется только положение).