Compare commits

..
Author SHA1 Message Date
Denozordec 042407f03e fix(ci): подключить тесты к базе в сети задания
Docker images / frontend-image (push) Successful in 2m15s
Docker images / publish-release (push) Successful in 10s
Docker images / updater-image (push) Successful in 41s
Docker images / backend-image (push) Successful in 1m41s
Docker images / backend-test (push) Successful in 5m11s
Docker images / notify-webhook (push) Successful in 2s
Docker images / prepare-release (push) Successful in 12s
2026-10-15 22:14:14 +07:00
Denozordec b2cf71fcc7 fix(db): индексировать историю для очистки планировщика
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Skipped
Docker images / frontend-image (push) Successful in 2m39s
Docker images / backend-test (push) Failing after 2m49s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 42s
Docker images / prepare-release (push) Successful in 14s
2026-10-15 22:04:38 +07:00
Denozordec fda7f353ad fix(map): сохранить узлы при датах опроса из базы данных 2026-10-15 21:51:43 +07:00
Denozordec 72bf648ca8 fix(ci): исправить условие уведомления для новой версии сборщика
Docker images / publish-release (push) Successful in 11s
Docker images / backend-image (push) Successful in 3m2s
Docker images / frontend-image (push) Successful in 3m7s
Docker images / backend-test (push) Successful in 3m58s
Docker images / updater-image (push) Successful in 1m16s
Docker images / notify-webhook (push) Successful in 3s
Docker images / prepare-release (push) Successful in 13s
2026-10-15 20:54:29 +07:00
Denozordec 8613749de1 chore(release)!: подготовить вторую основную версию
docker.yml (push) Invalid workflow file
2026-10-15 20:49:58 +07:00
Denozordec 6210f34740 fix(tests): завершить лабораторную приёмку и обновить фикстуру удаления 2026-10-15 20:30:07 +07:00
Denozordec 481688e72a fix(tests): согласовать полную регрессию с фокусом выбранного сервера 2026-10-15 20:19:07 +07:00
Denozordec c88d31fa45 chore(rest): подтвердить устойчивость повторных параллельных чтений 2026-10-15 14:39:29 +07:00
Denozordec 3d51bba86d fix(ui): сделать сетевые показатели и настройки читаемыми в обеих темах 2026-10-15 14:37:30 +07:00
Denozordec 3c9096804b fix(ui): исправить фокус списка серверов и контраст общих элементов 2026-10-15 14:15:56 +07:00
Denozordec 9b0d7bb6e8 chore(certificates): подтвердить реальное продление и полный откат 2026-10-15 13:47:24 +07:00
Denozordec 8c5cfa0706 fix(certificates): заменять сертификат уникальным поколением 2026-10-15 01:32:21 +07:00
Denozordec c683e1b2c1 fix(routeros): подтверждать импорт сертификата и ограничить REST-сессии 2026-10-15 00:28:23 +07:00
Denozordec 9178ff2ccb fix(certificates): продлевать по подтверждённому сроку и проверить реальный импорт 2026-10-15 00:05:03 +07:00
Denozordec 0e86628d58 fix(certificates): учитывать подтверждённый часовой пояс устройства 2026-10-14 21:50:11 +07:00
Denozordec e78bc2d7cb chore(test): зафиксировать выпуск сертификата и препятствие продлению 2026-10-14 21:43:57 +07:00
Denozordec 37a9c828d3 chore(test): подготовить проверку реального продления сертификата 2026-10-14 21:31:17 +07:00
Denozordec 3d23ae4be4 fix(certificates): подтверждать результат продления и исключить выключенные серверы 2026-10-14 21:01:22 +07:00
Denozordec 0563dd7cbd fix(ui): раскрыть последствия выпуска и исправить доступность мастеров 2026-10-14 20:43:16 +07:00
Denozordec 0374b57a30 chore(test): проверить реальный выпуск сертификата и восстановление стенда 2026-10-14 19:45:24 +07:00
Denozordec dc7b22940a fix(containers): исключить опрос выключенных серверов и ложные пустые данные 2026-10-14 19:25:14 +07:00
Denozordec 74151176d7 fix(containers): подтверждать остановку и удаление контейнера 2026-10-14 19:03:58 +07:00
Denozordec b3d11276dd fix(containers): учитывать фактические состояния контейнеров 2026-10-14 18:35:07 +07:00
Denozordec 0cc8fe5572 chore(test): подготовить пакет из указанного источника 2026-10-14 15:36:44 +07:00
Denozordec 61e6671685 ci(test): включить проверку загрузки баз в общий профиль 2026-10-14 15:33:30 +07:00
Denozordec 4f6de4b08e chore(test): подготовить пакет и процедуру проверки контейнеров 2026-10-14 15:19:16 +07:00
Denozordec 044002e7eb chore(test): уточнить предпосылки внешних задач и проверить релизы 2026-10-14 15:14:51 +07:00
Denozordec 48cc65ec2b fix(geoip): проверить локальный кеш и ограничить загрузку баз 2026-10-14 15:04:20 +07:00
Denozordec 3c3d152c2d chore(test): проверить большую историю со статусами файлов 2026-10-14 14:48:58 +07:00
Denozordec 026a358ca6 chore(test): проверить хранение последних копий и частичное удаление 2026-10-14 14:46:25 +07:00
Denozordec 4b028be241 chore(test): проверить ограниченное восстановление из хранилища через интерфейс 2026-10-14 14:41:26 +07:00
Denozordec 0216415953 chore(test): проверить REST за десятиминутным окном сессий 2026-10-14 14:31:21 +07:00
Denozordec 6617b776bb fix(backups): показать отсутствие файлов и защитить удаление записей 2026-10-14 14:30:33 +07:00
Denozordec e452e528e1 chore(test): сопоставить REST клиенты и проверить сессии RouterOS 2026-10-14 11:35:59 +07:00
Denozordec ab48973eb9 fix(deploy): сохранять локальные резервные копии при пересоздании контейнера 2026-10-14 11:21:48 +07:00
Denozordec 659e658f18 fix(backups): отклонять восстановление старых частичных экспортов 2026-10-14 11:16:29 +07:00
Denozordec faf452d149 fix(backups): защитить подключение S3 и согласовать операции каталога 2026-10-14 11:05:38 +07:00
Denozordec 7cef0cc133 chore(test): проверить сохранение S3 через реальный интерфейс 2026-10-14 10:46:18 +07:00
Denozordec 41a76f1163 fix(backups): читать экспорт RouterOS порциями без потери данных 2026-10-14 10:38:49 +07:00
Denozordec c3fb66dcbf fix(backups): получать нативный экспорт и проверить восстановление из S3 2026-10-14 10:31:14 +07:00
Denozordec 03b0896369 chore(test): проверить Selectel S3 и операции резервных копий 2026-10-14 10:01:35 +07:00
Denozordec 2277a76196 chore(test): подготовить безопасную проверку тестового S3 2026-10-14 09:27:09 +07:00
Denozordec cdb528276b fix(test): сохранять снимки GRE и VXLAN в результатах проверки 2026-10-14 09:22:32 +07:00
Denozordec af07c59ffa chore(test): подтвердить Android IPsec и очистить стенд 2026-10-14 09:06:25 +07:00
Denozordec baf81c0c7d chore(test): подготовить профиль Android для проверки IPsec 2026-10-14 08:56:06 +07:00
Denozordec 0d08c21bb9 chore(test): сопоставить диагностику REST двух тестовых стендов 2026-10-14 08:37:20 +07:00
Denozordec cbb4f9f0fc chore(test): сохранить короткое наблюдение REST тестовых CHR 2026-10-14 03:09:09 +07:00
Denozordec c21deba804 chore(docs): согласовать текущий план и остаток внешней приёмки 2026-10-14 03:00:59 +07:00
Denozordec 19ad294ded fix(netflow): запускать TypeScript worker с собственным загрузчиком 2026-10-14 02:57:24 +07:00
Denozordec 7a38bb36aa fix(gre): отклонять повреждённые и повторяющиеся native идентификаторы 2026-10-14 02:09:13 +07:00
Denozordec 51a579d370 fix(gre): не подменять неизвестные флаги состоянием туннеля 2026-10-14 01:39:35 +07:00
Denozordec 9c4a0992d7 fix(alerts): учитывать ошибки реального источника GRE 2026-10-14 01:16:19 +07:00
Denozordec 8b60ab040a fix(ui): повысить контраст заполненных таблиц BGP и OSPF 2026-10-14 01:05:45 +07:00
Denozordec d91cc77cda fix(api): проверять достоверность WAN и не опрашивать выключенные серверы 2026-10-14 00:47:29 +07:00
Denozordec 4f084222ad fix(api): сохранять ошибки наблюдения BGP для оповещений 2026-10-14 00:40:41 +07:00
Denozordec 5ac5a25dfc fix(api): показывать ошибки опроса старых OSPF и BFD API 2026-10-14 00:31:50 +07:00
Denozordec eaf724d6cc fix(ui): исправить контраст метки IPsec в таблице GRE 2026-10-14 00:19:54 +07:00
Denozordec 0c5d7d3207 fix(network): выбрать IPv6-команду в экспорте GRE 2026-10-13 22:01:46 +07:00
Denozordec 1be4a490b5 fix(network): согласовать GRE PSK с FastPath и экспортом 2026-10-13 21:42:21 +07:00
Denozordec 7d86af7e8b fix(auth): закрыть обход JWT через закодированные URL 2026-10-13 21:23:51 +07:00
Denozordec 727e35f985 fix(network): исправить keepalive GRE и каталог IPsec 2026-10-13 21:15:49 +07:00
Denozordec 3f3f06f147 fix(network): скрыть GRE PSK и сохранить признак IPsec 2026-10-13 20:45:27 +07:00
Denozordec a404da768d fix(network): защитить ключи WireGuard и режим просмотра операций 2026-10-13 20:12:33 +07:00
Denozordec c8f9c83f45 fix(ui): учитывать права и состояние устройства при откате истории 2026-10-13 19:45:39 +07:00
Denozordec 5ce20e8892 chore(tests): проверить клавиатуру и фокус живых панелей 2026-10-13 19:22:54 +07:00
Denozordec b9000b16e4 fix(ui): улучшить контраст трафика и предупреждений целей 2026-10-13 16:50:12 +07:00
Denozordec 6cc0094591 chore(tests): проверить живую навигацию разделов под двумя ролями 2026-10-13 16:37:55 +07:00
Denozordec 7a58f472b3 chore(tests): подтвердить маскирование заполненных секретов интеграций 2026-10-13 16:32:58 +07:00
Denozordec 3c30c5c7cb chore(tests): проверить секреты в ответах административного API 2026-10-13 16:28:10 +07:00
Denozordec bf39b3de18 chore(tests): подтвердить цельную доставку очереди Telegram 2026-10-13 16:24:54 +07:00
Denozordec cbe44e8072 fix(traffic): останавливать сбор при потере соединения блокировки 2026-10-13 16:20:46 +07:00
Denozordec 46de87e213 fix(auth): восстанавливать раздел и фильтры после входа Portal 2026-10-13 16:09:27 +07:00
Denozordec 199910eceb chore(tests): проверить срок JWT и границы выхода Portal 2026-10-13 15:59:20 +07:00
Denozordec f8d2b21cf6 chore(tests): проверить доставку Telegram и сохранить границы повтора 2026-10-13 15:53:06 +07:00
Denozordec 608f743003 fix(auth): скрыть пароли от читателя и согласовать Portal 2026-10-13 15:35:31 +07:00
Denozordec a15917d08b chore(tests): подключить тестовый Portal и подтвердить Telegram 2026-10-13 15:14:45 +07:00
Denozordec efdfc6f224 chore(tests): подтвердить общую регрессию интерфейса и backend 2026-10-13 14:40:20 +07:00
Denozordec b525e70655 fix(traffic): согласовать опрос между процессами backend 2026-10-13 14:35:00 +07:00
Denozordec a68bd61e58 fix(traffic): объединять чтения истории общего сервера 2026-10-13 14:15:44 +07:00
Denozordec 7b88416415 fix(traffic): считать общий пик по совпадающим интервалам 2026-10-13 13:58:56 +07:00
Denozordec dbc3b2c947 chore(docs): обновить дату и состояние матрицы приёмки 2026-10-13 02:17:31 +07:00
Denozordec a9f19e1f4b chore(docs): сохранить пример несовпадающих пиков привязок 2026-10-13 02:16:15 +07:00
Denozordec 7c667f8036 fix(traffic): учитывать пик только подтверждённых измерений 2026-10-13 02:14:03 +07:00
Denozordec 504f62fc0b fix(traffic): прекращать опрос изменённого источника 2026-10-13 02:08:26 +07:00
Denozordec 5d19b6a258 fix(traffic): учитывать состав каждой волны измерений 2026-10-13 01:52:50 +07:00
Denozordec a8b80d9914 chore(tests): проверить архивы с идентичностью счётчиков 2026-10-13 01:31:02 +07:00
Denozordec b57ae316ba fix(traffic): подтверждать идентичность соседних счётчиков 2026-10-13 01:19:22 +07:00
Denozordec 493c30bebc fix(traffic): ограничивать доверие скорости после пропусков 2026-10-13 00:54:42 +07:00
Denozordec 9603048bd8 chore(docs): зафиксировать границу доверия скорости после пропусков 2026-10-12 23:02:31 +07:00
Denozordec a40b26c3c5 fix(traffic): сохранять историю отключённых интерфейсов 2026-10-12 23:00:23 +07:00
Denozordec c69d99ba80 fix(traffic): показывать причину неполного опроса peers 2026-10-12 22:54:35 +07:00
Denozordec d8f8d3c7a4 fix(traffic): принимать измерения peers только полным срезом 2026-10-12 22:41:39 +07:00
Denozordec b4d0733cfa fix(traffic): подтверждать свежесть статусов привязанных интерфейсов 2026-10-12 22:35:17 +07:00
Denozordec 2b3d583e3b fix(traffic): сохранять точность объёмов и обозначать бинарные единицы 2026-10-12 21:59:50 +07:00
Denozordec 2358ecef60 fix(traffic): проверять live-срез и прекращать опрос выключенных устройств 2026-10-12 21:35:55 +07:00
Denozordec 1275dd7b9b fix(traffic): сохранять историю и показывать достоверную свежесть 2026-10-12 20:23:07 +07:00
Denozordec ebe62ae2ef fix(uptime): проверить RTT через SQL и обновить тестовый стенд 2026-10-12 18:21:50 +07:00
Denozordec ca2b634743 fix(uptime): точно разбирать составные длительности RTT 2026-10-12 18:17:10 +07:00
Denozordec 737e04a6a9 fix(traffic): проверить сбор трафика через HTTP и PostgreSQL 2026-10-12 18:13:48 +07:00
Denozordec 4527e5537c fix(traffic): исключить ложные нули из суммы трафика 2026-10-12 18:06:05 +07:00
Denozordec 8fb46b1552 fix(uptime): отличать ошибки опроса от измеренных потерь Ping 2026-10-12 17:55:16 +07:00
Denozordec ca23284486 fix(alerts): исключить неподтверждённые проблемы из журнала мониторинга 2026-10-12 16:45:26 +07:00
Denozordec d3f9a120e6 fix(alerts): объяснить отключённые и неоднозначные цели оповещений 2026-10-12 15:47:16 +07:00
Denozordec db7105119d fix(alerts): учитывать свежесть и настройки источников оповещений 2026-10-12 15:12:15 +07:00
Denozordec 8ffb8b5f00 fix(alerts): исключить неоднозначные и отключённые устройства из сигналов 2026-10-12 14:14:45 +07:00
Denozordec 65d4a17882 chore(ux): зафиксировать аудит свежести и контекста источников оповещений 2026-10-12 12:13:23 +07:00
Denozordec 13618e7efe fix(alerts): не подтверждать события устаревшими GRE BGP снимками 2026-10-12 12:03:11 +07:00
Denozordec 7ad66f178b chore(tests): проверить backend на PostgreSQL и убрать ожидание idle пула 2026-10-12 11:48:44 +07:00
Denozordec fdddc52019 fix(collectors): сохранять частичный результат и актуальные GRE BGP сигналы 2026-10-12 11:09:17 +07:00
Denozordec 9fbe27c1b0 fix(bgp): сохранять неизвестные показатели без вымышленных чисел 2026-10-12 10:38:05 +07:00
Denozordec c557a8c8cb fix(alerts): исключить переходы между неоднозначными BGP peers 2026-10-12 10:27:41 +07:00
Denozordec 22b0e8ae8e chore(ux): подтвердить четырёхбайтовые ASN в IPv6 BGP 2026-10-12 10:19:49 +07:00
Denozordec fef7bc5520 fix(bgp): сохранять неизвестное состояние и достоверную историю оповещений 2026-10-12 10:14:39 +07:00
Denozordec 8cb0fbd195 chore(ux): подтвердить снятие IPv6 BGP маршрутов при потере BFD 2026-10-12 10:01:24 +07:00
Denozordec cf7f4edf78 chore(ux): подтвердить потерю и восстановление BFD для OSPFv3 2026-10-12 09:54:08 +07:00
Denozordec e1617f1472 fix(ospf): сопоставлять IPv6 адреса и исключать неоднозначный BFD 2026-10-12 09:48:06 +07:00
Denozordec 1b63e3ec43 chore(ux): зафиксировать общую регрессию интерфейса и контекста 2026-10-12 09:38:52 +07:00
Denozordec d3d8afaa0c fix(api): сохранять большие идентификаторы серверов без округления 2026-10-11 23:39:02 +07:00
Denozordec 90e6c7be25 chore(ux): подтвердить сессии и маршруты BGP IPv6 2026-10-11 23:16:48 +07:00
Denozordec f6b9c6941d fix(ospf): показывать IPv6 маршруты и фактический шлюз 2026-10-11 23:05:19 +07:00
Denozordec 12ead0b6c2 fix(ospf): показать конфликт состояний OSPF и BFD 2026-10-11 22:42:14 +07:00
Denozordec 4d3cb659c1 fix(wireguard): поддержать IPv6 и исключить выключенные серверы 2026-10-11 22:08:10 +07:00
Denozordec f3cbc32720 fix(gre): сохранять и восстанавливать статические IPv6 адреса 2026-10-11 21:37:12 +07:00
Denozordec 655ea9727f fix(ui): сделать названия колонок потоков понятнее 2026-10-11 18:41:06 +07:00
Denozordec 555bd31596 fix(netflow): проверять месяц и точные календарные границы 2026-10-11 18:27:41 +07:00
Denozordec 0c14da190b fix(netflow): подтверждать выбранный срез перед запросами 2026-10-11 18:16:29 +07:00
Denozordec 64df472202 fix(netflow): проверять ответы аналитики и выбранный месяц 2026-10-11 17:58:18 +07:00
Denozordec 979dc3cfb0 fix(ui): уточнить подписи ICMPv6 и портов IPv6 2026-10-11 17:31:06 +07:00
Denozordec d569932b2a chore(ux): подтвердить настоящий IPv6 IPFIX и восстановление стенда 2026-10-11 17:11:32 +07:00
Denozordec 11813179a7 fix(netflow): сохранить IPv6 в смешанных шаблонах IPFIX 2026-10-11 16:44:00 +07:00
Denozordec c718654bc7 fix(netflow): сохранить неизвестные координаты RIPE без ложных нулей 2026-10-11 16:28:54 +07:00
Denozordec af66faaae4 fix(netflow): исправить классификацию IPv6 и кеш префиксов RIPE 2026-10-11 16:21:43 +07:00
Denozordec 59938118bd fix(traffic): дождаться каталога перед запросами выбранного сервера 2026-10-11 13:10:37 +07:00
Denozordec b619ef208e fix(netflow): исключить специальные IPv4 из внешнего обогащения 2026-10-11 12:59:57 +07:00
Denozordec f3156b44cf chore(test): завершить общую регрессию и сверить production outputs 2026-10-11 12:28:59 +07:00
Denozordec c242b611e2 chore(test): подтвердить цепочку промежуточного CA и повторный IKE 2026-10-11 12:08:39 +07:00
Denozordec c331558e31 chore(test): проверить внешний отзыв сертификата при установленном IKE 2026-10-11 12:02:59 +07:00
Denozordec 8af5c7a080 chore(test): подтвердить отказы IKE с повреждённым и истёкшим CRL 2026-10-11 11:55:44 +07:00
Denozordec 7c0cd50334 chore(test): подтвердить отказ IKE без внешнего CRL и восстановление 2026-10-11 11:51:26 +07:00
Denozordec 07499cd340 chore(test): проверить внешнюю загрузку CRL и отказ HTTP 2026-10-11 11:40:16 +07:00
Denozordec bd023b8aaa fix(terminal): показывать последние записи журнала RouterOS 2026-10-11 11:26:59 +07:00
Denozordec 1e4d3b4678 chore(test): подтвердить изоляцию диагностических буферов RouterOS 2026-10-11 11:15:11 +07:00
Denozordec 3b908a9e4e chore(test): подтвердить ECDSA IPsec и итоговую очистку стенда 2026-10-11 03:31:34 +07:00
Denozordec 63ee8a4549 chore(test): подтвердить сроки сертификатов в отдельных клиентах 2026-10-11 03:27:06 +07:00
Denozordec e17ddfdc20 chore(test): сохранить отказ положительного контроля сертификатов 2026-10-11 03:20:22 +07:00
Denozordec b190412af1 chore(test): зафиксировать тайм-ауты сертификатов с неверными сроками 2026-10-11 03:06:45 +07:00
Denozordec 7867914cbc fix(ipsec): проверять CRL-цепочку до удаления identity 2026-10-11 02:56:09 +07:00
Denozordec a31a65fbe6 chore(test): подтвердить отзыв сертификата при включённом CRL 2026-10-11 02:46:41 +07:00
Denozordec aed1963eef chore(test): зафиксировать поведение IPsec при отключённом CRL 2026-10-11 02:36:47 +07:00
Denozordec 07396d9385 fix(ipsec): пояснить границу онлайн-статуса IKE 2026-10-11 02:35:57 +07:00
Denozordec cad01853e1 chore(test): подтвердить границы аутентификации сертификатов IPsec 2026-10-11 02:24:53 +07:00
Denozordec 67644c50cb chore(test): проверить смену ключей и восстановление IPsec 2026-10-11 02:23:23 +07:00
Denozordec d4f5746325 fix(ipsec): учитывать уникальный DNS SAN в активных сессиях 2026-10-11 02:18:09 +07:00
Denozordec b4e56d1998 chore(test): проверить кодирование DN и FQDN в реальном IPsec 2026-10-11 02:11:34 +07:00
Denozordec d379045d20 chore(test): подтвердить две IKE-сессии одного сертификата 2026-10-10 23:14:37 +07:00
Denozordec 08fcebd549 fix(ipsec): экспортировать сертификат только по прямой привязке 2026-10-10 22:29:51 +07:00
Denozordec 9da5e55367 fix(tests): ждать окончания анимации кнопок резервных копий 2026-10-10 22:00:04 +07:00
Denozordec 057b530364 fix(ipsec): удалять сертификат только по явной привязке 2026-10-10 21:26:48 +07:00
Denozordec d52c48e5ed fix(ipsec): сохранять общий сертификат при удалении identity 2026-10-10 21:11:04 +07:00
Denozordec 58f5c48a97 fix(ipsec): показывать все привязки общего сертификата 2026-10-10 20:59:24 +07:00
Denozordec 60b2e099f0 fix(ipsec): учитывать несколько сессий без ложного офлайн 2026-10-10 20:42:58 +07:00
Denozordec cb6ec09312 fix(ipsec): показывать неопределённый статус при общих Remote ID 2026-10-10 10:33:54 +07:00
Denozordec 7815e00a1c fix(ipsec): исключить чужие сессии при сопоставлении CN 2026-10-10 09:56:21 +07:00
Denozordec 2aa93ad365 fix(ipsec): исправить статус клиента с полным DN сертификата 2026-10-10 09:42:57 +07:00
Denozordec 1d61b50591 chore(test): подтвердить IPv6 в GRE и WireGuard 2026-10-10 09:06:40 +07:00
Denozordec 2a804876b0 chore(test): проверить IPv6 внутри VXLAN на стенде 2026-10-10 09:02:31 +07:00
Denozordec e7aaec926d chore(test): подтвердить мост и обучение MAC в VXLAN 2026-10-10 08:59:20 +07:00
Denozordec a58756c421 chore(docs): зафиксировать готовность внешних интеграций 2026-10-10 03:49:57 +07:00
Denozordec 78b0c07b88 chore(docs): сохранить итоговую сверку стенда и плана 2026-10-10 03:45:04 +07:00
Denozordec beb72ceea1 chore(test): подтвердить таймер BGP без BFD 2026-10-10 03:34:04 +07:00
Denozordec 2d4e8baa13 chore(test): сверить потери Ping на GRE и public пути 2026-10-10 03:25:04 +07:00
Denozordec cd8613fab6 fix(api): отклонять некорректное подтверждение PATCH 2026-10-10 03:15:59 +07:00
Denozordec 89bde27f19 fix(ui): улучшить контраст и доступность прокрутки 2026-10-10 02:50:53 +07:00
Denozordec 94bdb2a8a1 chore(test): сохранить общую регрессию и её границы 2026-10-10 02:47:22 +07:00
Denozordec 7f0c2aad0f fix(api): завершать запрос при обрыве ответа RouterOS 2026-10-10 02:06:56 +07:00
Denozordec 5944e5e751 fix(uptime): объяснить неизвестный результат теста скорости 2026-10-10 01:41:15 +07:00
Denozordec dbe1e7c917 chore(test): проверить обрыв ответа сетевого теста 2026-10-10 01:32:21 +07:00
Denozordec 64bf0b4f6e chore(test): проверить время истории speed в разных поясах 2026-10-10 01:27:05 +07:00
Denozordec 890a5cba09 chore(test): сверить исходники и образ тестового стенда 2026-10-10 01:18:34 +07:00
Denozordec 9d6cd000ef chore(docs): свести оставшуюся внешнюю приёмку 2026-10-10 01:12:59 +07:00
Denozordec e23038d409 chore(test): подтвердить резервный путь BGP на стенде 2026-10-10 01:11:06 +07:00
Denozordec 40f730b5be chore(docs): сохранить итоговый контроль окна UX/UI 2026-10-09 22:45:42 +07:00
Denozordec f319eb947a chore(test): подтвердить обмен и отзыв собственного BGP-префикса 2026-10-09 22:40:30 +07:00
Denozordec c9bf5abe55 fix(ui): сохранить целый CIDR в таблице маршрутов OSPF 2026-10-09 22:26:08 +07:00
Denozordec ee704a86f2 chore(test): подтвердить резервный путь OSPF и сохранить наблюдения потерь 2026-10-09 21:59:46 +07:00
Denozordec 8b7bff165a fix(terminal): запретить выключенные устройства и исправить средний RTT 2026-10-09 21:35:25 +07:00
Denozordec 5b3133ce73 fix(routes): подтвердить применение и восстановление рекурсивных маршрутов 2026-10-09 21:08:10 +07:00
Denozordec 36c6a21ca5 fix(ospf): подтвердить права и результат оптимизации на RouterOS 2026-10-09 20:40:24 +07:00
Denozordec 3bfd4ae6cf chore(test): разделить наблюдения BFD и удаления маршрута OSPF 2026-10-09 20:12:31 +07:00
Denozordec 2e1586e4d4 chore(test): подтвердить BFD и восстановление OSPF на стенде 2026-10-09 18:00:45 +07:00
Denozordec c35272273b chore(test): подтвердить OSPF через GRE на реальном стенде 2026-10-09 17:41:08 +07:00
Denozordec 8795969871 fix(routing): исключить опрос выключенных устройств BGP и OSPF 2026-10-09 17:22:41 +07:00
Denozordec 89de7e134b fix(ipsec): обеспечить NAT и изоляцию политик для передачи данных 2026-10-09 17:10:33 +07:00
Denozordec 43b00e0740 fix(ipsec): различать частичный и неподтверждённый результат операций 2026-10-09 16:06:33 +07:00
Denozordec a83e4734df fix(ipsec): восстановить mode-config с проверкой зависимостей 2026-10-09 15:49:50 +07:00
Denozordec 5d9ef9dcf3 fix(ipsec): подтвердить сертификатный клиент и сообщать сбой истории 2026-10-09 15:32:10 +07:00
Denozordec b9a8615b8b fix(ipsec): показывать подключённых клиентов и адрес mode-config 2026-10-09 15:14:41 +07:00
Denozordec f80d9b7637 fix(ipsec): подтвердить достоверность операций и приёмку раздела 2026-10-09 12:46:22 +07:00
Denozordec e2d81f9657 fix(vxlan): подтвердить достоверность опроса и экспорта раздела 2026-10-09 10:38:24 +07:00
Denozordec 6b068ac39a chore(test): проверить VXLAN на двух реальных CHR 2026-10-09 09:44:54 +07:00
Denozordec 0067c2eeff chore(test): проверить отказ записи ревизии на стенде 2026-10-09 09:36:06 +07:00
Denozordec 8eaf78b686 fix(wireguard): учитывать неподтверждённую ревизию импорта 2026-10-09 09:21:02 +07:00
Denozordec 91ee424b42 chore(test): подтвердить компенсацию импорта на двух CHR 2026-10-09 09:15:48 +07:00
Denozordec e01454b720 fix(wireguard): очищать подтверждённые объекты при отказе импорта 2026-10-09 09:04:05 +07:00
Denozordec f9980d7d7e chore(test): подтвердить безопасный импорт conf на стенде 2026-10-09 08:53:59 +07:00
Denozordec 479f2adc78 fix(wireguard): проверять конфигурацию до импорта 2026-10-09 08:39:36 +07:00
Denozordec 7ce43adc07 chore(test): подтвердить импорт WireGuard на двух CHR 2026-10-09 08:31:09 +07:00
Denozordec d13c773856 chore(test): подтвердить экспорт шаблона WireGuard на стенде 2026-10-09 08:19:26 +07:00
Denozordec c16fc0c5b5 fix(ci): вынести межслойный тест из frontend Docker контекста 2026-10-08 23:28:37 +07:00
Denozordec c82e8fa12b fix(wireguard): исправить endpoint и поля клиентского шаблона 2026-10-08 23:21:45 +07:00
Denozordec 206255b856 chore(test): проверить экспорт WireGuard без приватных ключей 2026-10-08 23:12:43 +07:00
Denozordec a1183f2ec9 chore(test): подтвердить мобильные панели WireGuard на стенде 2026-10-08 23:03:00 +07:00
Denozordec bf43263e43 fix(wireguard): расширить мобильные панели и сохранить видимость кнопок 2026-10-08 22:50:50 +07:00
Denozordec ae0162db53 chore(test): подтвердить полный сценарий WireGuard через интерфейс 2026-10-08 22:45:16 +07:00
Denozordec 6a46fb1e40 fix(wireguard): сохранять меню действий при перерисовке 2026-10-08 22:32:57 +07:00
Denozordec 4c4ba02b44 chore(test): развернуть и подтвердить полное удаление WireGuard 2026-10-08 22:12:33 +07:00
Denozordec 3722fad329 fix(wireguard): удалять связанные пиры и IP вместе с интерфейсом 2026-10-08 21:59:53 +07:00
Denozordec fe0ca3fa46 chore(test): проверить реальную связность WireGuard и интерфейс 2026-10-08 21:48:30 +07:00
Denozordec 02cdfd869a chore(test): развернуть и проверить мобильную форму GRE 2026-10-08 21:36:36 +07:00
Denozordec fd521f6ef8 fix(gre): расширить поля внутренних IP на телефоне 2026-10-08 21:24:44 +07:00
Denozordec ed80aef195 chore(test): развернуть и проверить устойчивость меню GRE 2026-10-08 21:16:37 +07:00
Denozordec 731803bade fix(gre): сохранять меню действий при обновлении таблицы 2026-10-08 16:27:17 +07:00
Denozordec 26e8225815 chore(test): развернуть и проверить сохранение GRE peer IP 2026-10-08 16:18:56 +07:00
Denozordec bd3f996f55 fix(gre): сохранять удалённый внутренний IP туннеля 2026-10-08 15:50:11 +07:00
Denozordec 56f29a60d1 chore(test): проверить реальную GRE пару и очистку туннелей 2026-10-08 15:36:55 +07:00
Denozordec 37bc356ff7 chore(test): зафиксировать сетевую конфигурацию тестовых CHR 2026-10-08 15:14:51 +07:00
Denozordec 2ead73c30a chore(test): свести результаты и ограничения приёмки очереди 2026-10-08 15:07:20 +07:00
Denozordec edc459382b chore(test): проверить доступность очереди на production frontend 2026-10-08 15:04:27 +07:00
Denozordec a4ae10d7f5 chore(test): проверить изоляцию повтора при смене источника 2026-10-08 14:56:39 +07:00
Denozordec bb7b940bfc chore(test): подтвердить права очереди в браузере с JWT 2026-10-08 14:47:49 +07:00
Denozordec 1d6f2d598c chore(test): развернуть и проверить пояснение повтора на стенде 2026-10-08 14:41:40 +07:00
Denozordec b55588f29b fix(ui): объяснить неподтверждённый результат повтора сообщения 2026-10-08 14:30:54 +07:00
Denozordec e80cfeb4a9 chore(test): проверить браузерный повтор при потере подтверждения 2026-10-08 14:28:14 +07:00
Denozordec ca8eafa1ff chore(test): подтвердить права очереди на настоящих JWT 2026-10-08 14:22:03 +07:00
Denozordec 22a4e2f36e chore(test): проверить очередь в браузере на реальной базе 2026-10-08 14:18:28 +07:00
Denozordec 693cce2535 chore(test): проверить задержку и исчерпание попыток отправки 2026-10-08 14:12:40 +07:00
Denozordec ffe44dc938 chore(test): проверить удержание отправок с неизвестным исходом 2026-10-08 14:09:17 +07:00
Denozordec 507c7010ad chore(test): подтвердить обработку ошибок сохранения отправки 2026-10-08 14:06:23 +07:00
Denozordec d44511da8e chore(test): подтвердить конкурентный захват очереди на PostgreSQL 2026-10-08 14:00:45 +07:00
Denozordec 7c4b21be19 chore(test): подтвердить откат некорректного receipt очереди 2026-10-08 10:58:39 +07:00
Denozordec 87fc1be657 chore(test): проверить конкурентное разрешение unknown в PostgreSQL 2026-10-08 10:55:55 +07:00
Denozordec 06d73c50cc chore(test): проверить конкурентную дедупликацию очереди в PostgreSQL 2026-10-08 10:53:03 +07:00
Denozordec 49af17178e chore(test): подтвердить UDP-тест и историю в реальном интерфейсе 2026-10-08 10:49:26 +07:00
Denozordec 800c6f69ae chore(test): проверить receive и both после восстановления CHR2 2026-10-08 10:46:12 +07:00
Denozordec e1457fbc77 chore(test): подтвердить восстановленный доступ CHR2 и дашборд 2026-10-08 10:43:06 +07:00
Denozordec 8a1c36f358 chore(test): сопоставить CLI и REST при отказе доступа CHR2 2026-10-08 10:29:34 +07:00
Denozordec 34096cbfd5 chore(test): сохранить диагностику направленного bandwidth-тайм-аута 2026-10-08 10:19:25 +07:00
Denozordec a178becdcd chore(test): подтвердить новую форму speed и обновить frontend на стенде 2026-10-08 10:09:35 +07:00
Denozordec f194e7269b fix(ui): уточнить интерфейсы speed и исправить CLI-подсказку 2026-10-08 10:00:42 +07:00
Denozordec 47f747586c chore(test): подтвердить исправление proxy timeout на production-стенде 2026-10-08 09:46:25 +07:00
Denozordec aa7dbdf608 fix(web): дождаться ответа backend при длительной speed-команде 2026-10-08 09:38:17 +07:00
Denozordec fc1a60d413 chore(test): проверить отказ bandwidth-аутентификации на реальном CHR 2026-10-08 09:30:52 +07:00
Denozordec c0a32e8ed2 chore(test): подтвердить смену speed-контекста вторым клиентом в браузере 2026-10-08 09:24:45 +07:00
Denozordec 01061b4406 chore(test): проверить конфликты speed и блокировки PostgreSQL на стенде 2026-10-08 09:19:43 +07:00
Denozordec 9ccfc7f2b6 chore(test): подтвердить новые образы и неопределённый исход speed на стенде 2026-10-08 09:10:50 +07:00
Denozordec 8782054078 fix(ui): показывать неопределённый исход speed и сверять историю 2026-10-08 08:57:54 +07:00
Denozordec 96fa910804 fix(ui): сохранить snapshot speed при неизвестном исходе записи 2026-10-08 08:48:58 +07:00
Denozordec f408afba20 fix(api): сохранить неизвестный исход записи speed без ложной ошибки 2026-10-08 00:21:31 +07:00
Denozordec c337fddac9 chore(test): проверить права и восстановление базы в браузере 2026-10-08 00:03:13 +07:00
Denozordec 1adb82c9a2 chore(test): проверить остановку записей и обновить backend стенда 2026-10-07 23:50:18 +07:00
Denozordec 4c12c55c9d fix(api): исключить выключенные серверы из мониторинга 2026-10-07 23:37:06 +07:00
Denozordec f3e205950f fix(api): восстановить разделы PostgreSQL в одной транзакции 2026-10-07 23:28:16 +07:00
Denozordec c24ad7810c chore(test): выявить ошибку восстановления разделов PostgreSQL 2026-10-07 23:20:12 +07:00
Denozordec f1aac49dfc fix(api): сбросить кэши после восстановления базы 2026-10-07 23:12:32 +07:00
Denozordec 2943032462 fix(api): дождаться фоновых записей перед операцией с БД 2026-10-07 23:07:10 +07:00
Denozordec 8682f2a7a5 fix(api): дождаться запросов перед операцией с базой 2026-10-07 23:00:39 +07:00
Denozordec 9264b6636a chore(test): проверить настоящий UDP collector и завершение записи 2026-10-07 22:55:33 +07:00
Denozordec f8da3e08a0 fix(api): подтвердить остановку NetFlow перед операцией с БД 2026-10-07 22:48:19 +07:00
Denozordec 1b89d56ea3 fix(api): дождаться заданий планировщика перед операцией с БД 2026-10-07 22:41:50 +07:00
Denozordec 6720ad3539 chore(test): проверить архив PostgreSQL, права и конфликт операций 2026-10-07 22:36:24 +07:00
Denozordec 66bed28382 chore(test): сохранить реальные тесты скорости на паре CHR 2026-10-07 22:24:59 +07:00
Denozordec 476dbec204 chore(test): зафиксировать подключение второго CHR 2026-10-07 22:10:24 +07:00
Denozordec 83593756ce chore(test): зафиксировать подключение тестового CHR 2026-10-07 21:59:35 +07:00
Denozordec 65d04baf2f chore(deploy): зафиксировать проверенное развёртывание тестового стенда 2026-10-07 21:39:59 +07:00
Denozordec 673e5ba444 fix(api): нормализовать дату сохранённого снимка Internet path 2026-10-07 21:26:23 +07:00
Denozordec 4d88b97d49 chore(deploy): подготовить отдельный тестовый Docker-стенд 2026-10-07 21:04:28 +07:00
Denozordec 6255f44975 chore(ux): сохранить итоговую проверку контекста и точку внешней приёмки 2026-10-07 18:42:00 +07:00
Denozordec 683148d620 chore(ux): сохранить общую регрессию после исправлений скорости 2026-10-07 18:32:17 +07:00
Denozordec 9680a6422b fix(ui): отличать неизвестный RTT от отсутствия ответов Ping 2026-10-07 17:58:11 +07:00
Denozordec 30cc99c68e fix(ui): использовать серверное время и не подменять отсутствующий Ping 2026-10-07 17:52:33 +07:00
Denozordec 7f18fcb6db fix(ui): сбрасывать снимок при изменении параметров speed-пробы 2026-10-07 17:41:14 +07:00
Denozordec 090206c893 fix(api): сохранять результат только в пробу с теми же параметрами 2026-10-07 17:33:48 +07:00
Denozordec b59944b573 fix(api): проверять параметры запуска теста скорости до операции 2026-10-07 17:30:52 +07:00
Denozordec b0e114b56d fix(docs): указать даты выполнения в заголовках этапов 2026-10-07 17:10:51 +07:00
Denozordec a56f555c25 fix(docs): восстановить недостающие записи журнала UX/UI 2026-10-07 17:09:26 +07:00
Denozordec 65c63e2860 fix(ui): убрать неиспользуемый код прежней таблицы мониторинга 2026-10-07 15:57:53 +07:00
Denozordec e4f951175c fix(uptime): сбросить снимок при смене пары и сохранить историю 2026-10-07 15:52:58 +07:00
Denozordec 8627459bfa chore(docs): зафиксировать общую проверку 221 контекста 2026-10-07 15:25:46 +07:00
Denozordec a3fcba0d43 fix(uptime): защитить пару устройств и атомарно сохранить результат 2026-10-07 15:12:41 +07:00
Denozordec 08ea416989 chore(docs): зафиксировать общую регрессию 1176 сценариев 2026-10-07 15:03:54 +07:00
Denozordec 5fedf7ae68 chore(docs): подготовить протокол внешней приёмки UX/UI 2026-10-07 14:37:13 +07:00
Denozordec 579e816bbb fix(ui): показать измерения рядом с закреплённым WAN на телефоне 2026-10-07 14:28:07 +07:00
Denozordec 5919af7f9b fix(ospf): сопоставить направления измерений и модели 2026-10-07 14:19:13 +07:00
Denozordec fb32f87d33 fix(uptime): проверять завершение теста перед записью успеха 2026-10-07 14:17:36 +07:00
Denozordec 9d5959601f fix(uptime): сохранить неизвестную скорость и проверить подтверждение теста 2026-10-07 14:15:56 +07:00
Denozordec 1904c86657 fix(ui): пояснить область скоростных снимков WAN 2026-10-07 13:59:20 +07:00
Denozordec 4ba7f82ccf fix(optimizer): правильно сопоставить направления DL и UL 2026-10-07 13:44:36 +07:00
Denozordec 7d331aff41 chore(ux): зафиксировать аудит скоростных измерений 2026-10-07 13:26:05 +07:00
Denozordec 4fee9e371f fix(optimizer): убрать лишние поиски и расчёты маршрутов 2026-10-07 13:16:12 +07:00
Denozordec 057f0d352a chore(ux): измерить генерацию всех комбинаций маршрутов 2026-10-07 12:48:22 +07:00
Denozordec 53e36f2a03 fix(ui): сделать сводки площадок краткими и достоверными 2026-10-07 12:42:20 +07:00
Denozordec 52d8a05480 fix(optimizer): ускорить точный выбор проб из большого пула 2026-10-07 12:24:07 +07:00
Denozordec c6dfa5267b chore(ux): измерить большой пул проб оптимизатора 2026-10-04 23:47:35 +07:00
Denozordec 8e3b7a65ce fix(ui): сократить сводку большого списка WAN 2026-10-04 23:43:57 +07:00
Denozordec 0355343f9f chore(ux): разделить исторические отчёты контекстных проверок 2026-10-04 23:39:18 +07:00
Denozordec ba27d2ade0 chore(ux): сохранить полную контекстную регрессию 2026-10-04 23:36:52 +07:00
Denozordec b1f8326304 fix(ui): возвращать фокус терминала после включения поля 2026-10-04 23:19:14 +07:00
Denozordec 55df77296a chore(ux): сохранить общую регрессию и сбой фокуса терминала 2026-10-04 23:12:35 +07:00
Denozordec 991f2b3871 fix(ui): учитывать весь каталог Community в оптимизаторе 2026-10-04 22:15:00 +07:00
Denozordec 53603f17c1 chore(ux): зафиксировать скрытый срез Community 2026-10-04 22:09:40 +07:00
Denozordec 833225d169 fix(optimizer): отсечь заведомо худшие назначения проб 2026-10-04 22:08:32 +07:00
Denozordec 1ad6b4a6b6 fix(ui): ограничить отображение матрицы WAN и Jump Host 2026-10-04 21:58:57 +07:00
Denozordec b3da4216e6 chore(ux): измерить матрицу WAN и расчёт назначения проб 2026-10-04 21:52:05 +07:00
Denozordec 28e2d38ee6 chore(ui): уточнить риски матрицы и расчёта оптимизатора 2026-10-04 18:15:22 +07:00
Denozordec a16bfae805 fix(ui): ограничить полные маршруты с сохранением индексов 2026-10-04 18:13:45 +07:00
Denozordec 82f620ac0b chore(ui): зафиксировать профиль полной таблицы маршрутов 2026-10-04 18:05:56 +07:00
Denozordec 059fd93520 fix(ui): ограничить карточки оптимизатора с сохранением URL 2026-10-04 18:03:59 +07:00
Denozordec 7265709b1c chore(ui): зафиксировать профиль тысячи карточек оптимизатора 2026-10-04 17:53:24 +07:00
Denozordec be54666661 fix(ui): ограничить preview OSPF без изменения применения 2026-10-04 17:50:45 +07:00
Denozordec 9f74cfebfe fix(ui): объединить параллельные связи OSPF в читаемый граф 2026-10-04 17:37:42 +07:00
Denozordec 086f118204 fix(ui): ограничить списки деталей OSPF без потери данных 2026-10-04 17:32:01 +07:00
Denozordec 8d86c5cbb0 fix(ui): ограничить граф OSPF с сохранением полного контекста 2026-10-04 17:25:55 +07:00
Denozordec 1a82c1b41f fix(ui): ограничить таблицу соседей OSPF и сохранить URL узла 2026-10-04 17:09:16 +07:00
Denozordec 2554c3572a chore(ui): зафиксировать профиль большой топологии OSPF 2026-10-04 16:58:53 +07:00
Denozordec ab56225a1a fix(ui): сохранить контекст и фокус форм Firewall без эффектов 2026-10-04 16:56:15 +07:00
Denozordec 21dc646553 fix(ui): ограничить трассировку и защитить повторную симуляцию 2026-10-04 16:45:39 +07:00
Denozordec 9231597d88 fix(ui): ограничить редактор сценария без потери правил 2026-10-04 16:38:19 +07:00
Denozordec 2e648dcf1c fix(ui): блокировать операции Firewall после ошибки чтения 2026-10-04 16:29:31 +07:00
Denozordec 1e9889f892 fix(ui): ограничить таблицу Firewall и сохранить порядок правил 2026-10-04 16:23:29 +07:00
Denozordec 4a5ae8baa1 fix(ui): ограничить список Filters с сохранением порядка 2026-10-04 16:12:22 +07:00
Denozordec 06850169ba fix(ui): ограничить форму маршрута без потери конечных точек 2026-10-04 16:01:41 +07:00
Denozordec 9124d451a5 fix(ui): ограничить группы и детали рекурсивных маршрутов 2026-10-04 15:52:07 +07:00
Denozordec f515818254 fix(ui): подтверждать копирование community после записи 2026-10-04 15:45:56 +07:00
Denozordec adfb2fc4c3 fix(ui): ограничить отображение каталога Communities 2026-10-04 15:41:21 +07:00
Denozordec 9d4d9f308f fix(ui): пояснить выборку сессий и исправить перенос вкладок 2026-10-04 13:16:17 +07:00
Denozordec 9bbecfbafe chore(ui): уточнить карту производительности оставшихся таблиц 2026-10-04 13:06:18 +07:00
Denozordec 97936aa81e fix(ui): ограничить отображение каталога GRE-туннелей 2026-10-04 13:03:27 +07:00
Denozordec dcc8550482 fix(ui): ограничить отображение каталога VXLAN-туннелей 2026-10-04 12:57:34 +07:00
Denozordec c7aec1500c fix(ui): защищать чтение WireGuard при смене источника 2026-10-04 12:48:24 +07:00
Denozordec e51964b4d2 fix(ui): восстанавливать фильтры WireGuard из ссылки 2026-10-04 12:30:43 +07:00
Denozordec c6702efcc4 fix(ui): показывать сведения о рукопожатии без ложного статуса 2026-10-04 12:21:53 +07:00
Denozordec 837298a171 fix(ui): ограничить раскрытие большого списка пиров WireGuard 2026-10-04 12:16:34 +07:00
Denozordec 747f544462 fix(ui): ограничить отображение каталога пиров WireGuard 2026-10-04 12:10:38 +07:00
Denozordec a6be18c1d6 fix(ui): ограничить отображение каталога интерфейсов WireGuard 2026-10-04 12:05:08 +07:00
Denozordec 7693b32372 fix(ui): различать нулевые и неизвестные счётчики WireGuard 2026-10-04 11:58:10 +07:00
Denozordec 53c1bfbd3b fix(api): сохранять каталог пинг-проб атомарно 2026-10-04 11:53:48 +07:00
Denozordec 9c22fa8a68 chore(ux): зафиксировать общую регрессию последних изменений 2026-10-04 11:28:54 +07:00
Denozordec 21859be580 chore(ux): зафиксировать повтор контекстной регрессии 2026-10-04 11:00:27 +07:00
Denozordec 5b2a2917c1 fix(ui): ограничить отображение каталога BFD-сессий 2026-10-04 10:51:59 +07:00
Denozordec 586da55c24 fix(ui): ограничить отображение каталога OSPF-маршрутов 2026-10-04 10:48:11 +07:00
Denozordec 908974302e fix(api): сохранять каталог speed-проб атомарно 2026-10-04 10:41:03 +07:00
Denozordec d4f2961f76 fix(api): проверить каталог speed-проб и изолировать источник 2026-10-04 10:31:02 +07:00
Denozordec f30319ac55 chore(test): сохранить проверку контекста пользователей и бэкапов 2026-10-04 05:22:24 +07:00
Denozordec a558412a27 fix(api): сохранить неизвестные значения истории скорости 2026-10-04 05:19:34 +07:00
Denozordec e5ff69e243 fix(ui): сохранить историю скорости при частичном сбое 2026-10-04 05:09:20 +07:00
Denozordec 611e542ea7 fix(ui): ограничить каталог бэкапов и вернуть фокус действий 2026-10-04 05:01:29 +07:00
Denozordec 5d9a6f3757 fix(ui): ограничить каталог пользователей и добавить поиск 2026-10-04 04:48:06 +07:00
Denozordec e0f83bdf42 fix(ui): сохранить имя строки при прокрутке таблицы 2026-10-04 04:38:23 +07:00
Denozordec 5fbfc752fb fix(ui): ограничить каталог сертификатов и сохранить детали 2026-10-04 04:29:30 +07:00
Denozordec 5488d86b12 chore(test): проверить отказ большого каталога проб 2026-10-04 04:22:25 +07:00
Denozordec e5cf05759e fix(ui): ограничить строки истории тестов скорости 2026-10-04 04:19:56 +07:00
Denozordec 488f2fe482 fix(ui): открыть крайние колонки сводной статистики 2026-10-04 04:11:53 +07:00
Denozordec 8d6d82082c chore(test): сохранить общую регрессию смены контекста 2026-10-04 04:04:12 +07:00
Denozordec 91c1194550 fix(test): актуализировать данные общей UX регрессии 2026-10-04 03:55:30 +07:00
Denozordec c4110fa323 fix(ui): сохранять ручную страницу выбранной сессии BGP 2026-10-04 03:27:41 +07:00
Denozordec 1cce712833 fix(ui): ограничить боковой каталог устройств 2026-10-04 03:22:39 +07:00
Denozordec 2bdcc1621f fix(ui): очищать недоступные интерфейсы при загрузке 2026-10-04 03:06:47 +07:00
Denozordec ab334764cf fix(ui): раскрывать предупреждения ресурсов порциями 2026-10-04 03:00:32 +07:00
Denozordec 2762f073a0 fix(ui): ограничить строки ресурсов мониторинга 2026-10-04 02:54:33 +07:00
Denozordec 76a5e81fd4 fix(ui): ограничить строки таблицы проб скорости 2026-10-04 02:44:52 +07:00
Denozordec 057281c246 fix(test): проверить большой каталог проб скорости 2026-10-04 00:56:57 +07:00
Denozordec ef457b471a fix(ui): сохранять страницу компактной таблицы при опросе 2026-10-04 00:53:28 +07:00
Denozordec 160b39186a fix(api): проверять обязательные поля снимка RouterOS 2026-10-04 00:50:42 +07:00
Denozordec b658f7127f fix(test): обновить данные сквозных проверок дашборда 2026-10-04 00:45:30 +07:00
Denozordec 39789765cb fix(ui): ограничить каталог BGP и сохранить страницу при обновлении 2026-10-04 00:38:44 +07:00
Denozordec 604b2b0b51 fix(ui): раскрывать большую очередь проблем порциями 2026-10-04 00:33:25 +07:00
Denozordec 5e5cf70980 fix(ui): ограничить отрисовку больших компактных таблиц 2026-10-04 00:30:09 +07:00
Denozordec 12c376b4c5 fix(ui): ограничить отрисовку большого каталога серверов 2026-10-04 00:24:33 +07:00
Denozordec c3a9c00aed fix(ui): показывать подтверждённые метрики участков пути 2026-10-04 00:18:21 +07:00
Denozordec d4ae1c3127 fix(api): подтверждать полный сбор пути без скрытых ошибок 2026-10-04 00:11:17 +07:00
Denozordec 5fdd6ef58c fix(ui): сохранять подтверждённый снимок пути при ошибке API 2026-10-04 00:07:21 +07:00
Denozordec 55c9349677 fix(ui): учитывать ошибки устройств в сводке туннелей 2026-10-03 23:51:16 +07:00
Denozordec 9d3129372b fix(uptime): показывать реальные наблюдения и пропуски RTT на дашборде 2026-10-03 23:42:45 +07:00
Denozordec 8f65d1620d fix(ui): проверять каталоги дашборда и сохранять неизвестный статус серверов 2026-10-03 23:20:37 +07:00
Denozordec 9e0f695684 fix(traffic): отличать измеренный ноль от отсутствия наблюдений 2026-10-03 23:10:39 +07:00
Denozordec a28f871fd4 fix(ui): проверять данные трафика без подстановки нулей в серии 2026-10-03 23:00:02 +07:00
Denozordec f0eaa94e02 fix(ui): сохранить неизвестные данные и ошибки BGP на дашборде 2026-10-03 22:55:15 +07:00
Denozordec c343ce62c8 fix(ui): исправить доступность оболочки и проверку состояния сервера 2026-10-03 22:49:52 +07:00
Denozordec 1a9966ec95 fix(ui): завершить локальную приёмку администрирования 2026-10-03 22:32:53 +07:00
Denozordec 271291a910 fix(probes): проверять расписание и сохранять подтверждённые данные 2026-10-03 22:12:08 +07:00
Denozordec 1d27127b52 fix(releases): проверять манифест и достоверность сведений о сборке 2026-10-03 21:59:55 +07:00
Denozordec d5edfe5ef6 fix(ui): повысить приоритет правил и исправить мобильные строки 2026-10-03 21:49:05 +07:00
Denozordec c7e1f75e2e fix(alerts): обозначить недоступность событий префиксов 2026-10-03 19:55:31 +07:00
Denozordec f3ec6b31ce fix(alerts): сделать выбор объектов доступным с клавиатуры 2026-10-03 19:50:31 +07:00
Denozordec 1deccf862c fix(ui): не подменять неполные сетевые счётчики нулями 2026-10-03 19:44:13 +07:00
Denozordec 587dd94f82 fix(ui): проверять права каждого источника дашборда 2026-10-03 19:38:30 +07:00
Denozordec 1999e8160e fix(ui): проверять права и сохранять достоверность общей сводки 2026-10-03 19:33:10 +07:00
Denozordec b109532f33 fix(alerts): подтвердить риск ручного повтора сообщения 2026-10-03 19:21:19 +07:00
Denozordec 7d0608bbce fix(alerts): защитить ручной повтор очереди проверкой версии 2026-10-03 19:14:27 +07:00
Denozordec 89c31289bd fix(alerts): показать подтверждённое состояние очереди сообщений 2026-10-03 19:09:42 +07:00
Denozordec b1ad9515fa fix(alerts): удерживать неизвестные отправки без автоматического повтора 2026-10-03 18:56:08 +07:00
Denozordec 46be201d10 fix(alerts): различать отказ и неизвестный результат Telegram 2026-10-03 18:48:22 +07:00
Denozordec 382e318b97 fix(alerts): отклонять запись устаревшего снимка правил 2026-10-03 18:41:48 +07:00
Denozordec c5c1c2812d fix(alerts): читать согласованный снимок и реальные цели проб 2026-10-03 18:27:22 +07:00
Denozordec dc5594ca7c fix(alerts): защитить тест правила общим замком Telegram 2026-10-03 18:20:29 +07:00
Denozordec 9c1d14635a fix(alerts): подтверждать запись правил и сохранять формы при сбое 2026-10-03 18:13:57 +07:00
Denozordec 875819c577 fix(alerts): сохранить черновик Telegram и подтвердить операции 2026-10-03 18:00:01 +07:00
Denozordec a5c8e614bd fix(test): учесть общий опрос в проверке прав каталогов 2026-10-03 17:51:49 +07:00
Denozordec 526f305aa6 fix(alerts): разделить загрузку каталогов целей и правил 2026-10-03 17:50:23 +07:00
Denozordec a8a3eb59fb fix(alerts): проверять каталоги объектов и сохранять частичные сбои 2026-10-03 17:34:33 +07:00
Denozordec 3710fc6aab fix(alerts): отличать неизвестные данные от пустого списка правил 2026-10-03 17:30:46 +07:00
Denozordec 9bc3a5db77 fix(alerts): подтверждать конфигурацию внутри транзакции сохранения 2026-10-03 17:22:41 +07:00
Denozordec 6093442bcf fix(alerts): проверять правила и группы до замены конфигурации 2026-10-03 17:16:21 +07:00
Denozordec 3556a378f2 fix(alerts): проверять доступ и использовать единый источник тестового сообщения 2026-10-03 17:11:39 +07:00
Denozordec 98319c9d50 fix(alerts): сохранять параметры выборочно и проверять результат отправки 2026-10-03 17:06:44 +07:00
Denozordec 0bcac655ea fix(alerts): проверять ответы и подтверждать сохранённые параметры 2026-10-03 17:00:53 +07:00
Denozordec 596fea4814 fix(settings): проверить контекст и завершить локальную приёмку базы 2026-10-03 16:55:41 +07:00
Denozordec 57293479d1 fix(settings): защищать восстановление базы от повторов и смены контекста 2026-10-03 16:52:02 +07:00
Denozordec 92b65a30ce fix(settings): сохранять результат операций базы при вторичных сбоях 2026-10-03 16:43:52 +07:00
Denozordec 738c98b9d3 chore(docs): закрепить актуальную точку продолжения UX/UI плана 2026-10-03 16:39:06 +07:00
Denozordec af5dbb0519 fix(settings): восстанавливать базу транзакционно и сохранять результат операции 2026-10-03 14:45:51 +07:00
Denozordec ff8b04bddf fix(settings): проверять резервные копии и исключить секреты из журнала 2026-10-03 14:36:12 +07:00
Denozordec e5842b415b fix(settings): проверять доступность текущего источника без гонок 2026-10-03 14:30:41 +07:00
Denozordec b6f1b72500 fix(settings): убрать фиктивные операции и подключить реальные параметры 2026-10-03 14:21:17 +07:00
Denozordec 2b6d3ab2e0 fix(evolution): проверить серверный каталог и завершить локальную приёмку 2026-10-03 14:12:06 +07:00
Denozordec d272ef57f0 fix(evolution): исключить моковые данные из живого каталога 2026-10-03 14:08:44 +07:00
Denozordec 118cd31d65 fix(evolution): сохранять черновики и подтверждать операции формы 2026-10-03 13:59:24 +07:00
Denozordec 7e3cb754b0 fix(evolution): проверять каталог и сохранять неизвестные счётчики 2026-10-03 13:51:07 +07:00
Denozordec 9db1471b2c fix(evolution): разделить состояния настроек и каталога и защитить контекст 2026-10-03 13:42:00 +07:00
Denozordec 554dba55ec fix(evolution): подтверждать сохранение ключа и ответ проверки API 2026-10-03 13:16:09 +07:00
Denozordec 2b62e0e956 fix(evolution): проверять настройки и подтверждение сохранённых полей 2026-10-03 13:11:45 +07:00
Denozordec 2215007c91 fix(netflow): завершить локальную приёмку и объединить сценарий сброса 2026-10-03 13:07:39 +07:00
Denozordec d9e09281e8 fix(netflow): генерировать файлы хоста из согласованного снимка настроек 2026-10-03 13:02:34 +07:00
Denozordec b80fa042d5 fix(netflow): согласовать проверку сетевых параметров формы и сервера 2026-10-03 12:59:05 +07:00
Denozordec 32b7812169 fix(netflow): отклонять конкурентный сброс до удаления данных 2026-10-03 12:54:27 +07:00
Denozordec a575795ff5 fix(netflow): проверить смену источника и адаптивность формы 2026-10-03 12:51:40 +07:00
Denozordec fcd8de2a96 fix(netflow): сохранять черновики и показывать подтверждённые результаты 2026-10-03 12:48:56 +07:00
Denozordec c9b0f9266e fix(netflow): выполнять сброс данных одной транзакцией и сохранять результат 2026-10-03 12:37:40 +07:00
Denozordec e2aae3897d fix(netflow): сохранять отдельные поля и защищать созданную пару ключей 2026-10-03 12:33:31 +07:00
Denozordec 8b017bab3f fix(netflow): защитить приватный экспорт и разделить результаты сохранения 2026-10-03 12:31:51 +07:00
Denozordec 4af4fdc544 fix(netflow): проверять ответы настроек и подтверждения операций 2026-10-03 12:29:05 +07:00
Denozordec 52cb93bf24 fix(geoip): проверить смену источника и читаемость на телефоне 2026-10-03 12:19:54 +07:00
Denozordec 57e7dcf990 fix(geoip): сохранять черновик и показывать частичный результат обновления 2026-10-03 12:13:25 +07:00
Denozordec f0d9f488e6 fix(geoip): ограничить изменение баз правами записи трафика 2026-10-03 12:05:48 +07:00
Denozordec 65aeedd3b0 fix(scheduler): завершить локальную приёмку контекста и адаптивности 2026-10-03 12:03:38 +07:00
Denozordec 5df337f6d7 fix(scheduler): подтверждать ручные операции и учитывать частичные ошибки 2026-10-03 11:56:08 +07:00
Denozordec cb377fb60c fix(collectors): сохранять только изменённые настройки с учётом прав 2026-10-03 11:50:23 +07:00
Denozordec 117aa9a21a fix(collectors): проверять параметры и подтверждения сохранения 2026-10-03 11:43:35 +07:00
Denozordec d453f2aec3 fix(collectors): разделить загрузку настроек и сохранить черновики 2026-10-03 11:41:00 +07:00
Denozordec dd387cf379 fix(evobgp): требовать подтверждение проверки соединения 2026-10-01 22:17:42 +07:00
Denozordec 0d2affaf6b fix(evobgp): проверять каталог и сохранять навигацию при ошибках API 2026-10-01 22:11:10 +07:00
Denozordec a0263c22a5 fix(collectors): проверять реальные ответы настроек без подстановки defaults 2026-10-01 21:59:40 +07:00
Denozordec 5d5ea680aa fix(scheduler): показывать подтверждённый статус независимо от настроек uptime 2026-10-01 21:55:03 +07:00
Denozordec 1f75bc5cea fix(scheduler): различать неизвестную историю и подтверждённые запуски 2026-10-01 21:47:10 +07:00
Denozordec 933a4eaf57 fix(scheduler): исключить поздние ответы после смены фильтра и источника 2026-10-01 21:39:04 +07:00
Denozordec 1b51d2af01 fix(geoip): отклонять неполные ответы и проверять подтверждения операций 2026-10-01 21:31:36 +07:00
Denozordec 638c17caef fix(scheduler): проверять подтверждения и контекст API планировщика 2026-10-01 21:24:37 +07:00
Denozordec 92b7399ddb fix(scheduler): читать снимки JSONB и показывать недоступные результаты 2026-10-01 21:19:36 +07:00
Denozordec 63b2f75012 fix(scheduler): отделить результат запуска от сбоев журналирования 2026-10-01 21:09:29 +07:00
Denozordec 49b8a1a19c chore(ui): завершить локальную приёмку резервных копий и этапа 11А 2026-10-01 20:59:42 +07:00
Denozordec 5ce080baf3 fix(backups): сохранить достоверность плановых операций при сбоях 2026-10-01 20:54:18 +07:00
Denozordec 98b4c39a00 fix(backups): показывать частичный результат внутри формы 2026-10-01 20:39:35 +07:00
Denozordec ad457b90a9 fix(backups): показывать ошибки диалогов на узких экранах 2026-10-01 16:48:20 +07:00
Denozordec b74084b67c fix(backups): адаптировать историю и формы к узким экранам 2026-10-01 16:45:41 +07:00
Denozordec 11e357cc94 fix(backups): восстанавливать вкладку и фильтры из ссылки 2026-10-01 16:37:34 +07:00
Denozordec 6f152c8dfe fix(backups): сохранять неизвестный результат восстановления 2026-10-01 16:32:06 +07:00
Denozordec 39570656c9 fix(backups): не повторять импорт после потери ответа 2026-10-01 16:22:06 +07:00
Denozordec 99ff5aebaf fix(backups): сверять сервер и файл подтверждения импорта 2026-10-01 16:18:37 +07:00
Denozordec af86d2c3cd fix(backups): сохранять запись при неполной очистке файлов 2026-10-01 16:14:00 +07:00
Denozordec 4c5604bf87 fix(backups): подтверждать операции в текущем контексте 2026-10-01 16:11:26 +07:00
Denozordec 714af7e872 fix(backups): продолжать наблюдение без повторного создания 2026-10-01 15:58:47 +07:00
Denozordec 4aee2a34f1 fix(backups): сохранить идентичность фоновой задачи 2026-10-01 15:44:02 +07:00
Denozordec 30bd479a6f fix(backups): подтверждать настройки и последнюю проверку S3 2026-10-01 15:37:00 +07:00
Denozordec 8e01666e0e fix(backups): сохранять расписание и хранилище независимо 2026-10-01 15:25:48 +07:00
Denozordec 5ce21e9a47 fix(backups): отделить результат операций от ошибок журнала 2026-10-01 15:11:32 +07:00
Denozordec 3dcc7c9ea6 fix(backups): разделить загрузку каталогов и права действий 2026-10-01 15:06:52 +07:00
Denozordec b460997cbf fix(test): указать хранилище в фикстуре резервных копий 2026-10-01 14:56:21 +07:00
Denozordec 4ae2954f97 fix(backups): проверять ответы и прогресс резервного копирования 2026-10-01 14:55:03 +07:00
Denozordec 46fe38321a fix(certificates): сохранить достоверный результат выпуска 2026-10-01 14:50:44 +07:00
Denozordec 16fa6eb5d8 fix(ui): проверить доступность и адаптивность сертификатов 2026-10-01 14:42:11 +07:00
Denozordec f5a71728ac fix(ui): восстанавливать задачу выпуска без повторной операции 2026-10-01 14:29:56 +07:00
Denozordec 97526bf06f fix(api): исключить повторное создание задачи выпуска 2026-10-01 14:16:10 +07:00
Denozordec 299f1da90b fix(api): проверять цель и отменять наблюдение выпуска 2026-10-01 14:10:33 +07:00
Denozordec 1d50ec8d17 fix(api): согласовать задачу выпуска с контрактом сертификатов 2026-10-01 14:07:03 +07:00
Denozordec 594e60a1f4 fix(ui): сохранять черновики и результат настроек ACME 2026-10-01 14:03:30 +07:00
Denozordec 1c407b77de fix(ui): сохранять подтверждённые настройки автообновления 2026-10-01 13:56:42 +07:00
Denozordec df89e88f4e fix(api): проверять ответы настроек и выпуска сертификатов 2026-10-01 13:48:50 +07:00
Denozordec adb6589816 fix(ui): сохранять контекст и источник сертификатов 2026-10-01 13:46:12 +07:00
Denozordec b184922f61 fix(ui): сохранять достоверные снимки сертификатов 2026-10-01 13:40:32 +07:00
Denozordec 17f03e7af1 fix(ui): показывать неизвестные поля сертификатов явно 2026-10-01 13:32:41 +07:00
Denozordec bb74eae64c fix(ui): убрать недостоверную шкалу срока сертификата 2026-10-01 13:28:19 +07:00
Denozordec f99612f6aa fix(api): изолировать ошибки создания клиента устройства 2026-10-01 11:26:22 +07:00
Denozordec c7b4981dff fix(api): сохранить последние снимки сертификатов 2026-10-01 11:23:45 +07:00
Denozordec 26869632a0 fix(api): передавать наблюдаемые состояния сертификатов 2026-10-01 11:18:32 +07:00
Denozordec 71cc6fa3a7 fix(ui): сохранить контекст и фокус контейнеров 2026-10-01 11:13:29 +07:00
Denozordec 3ceb8c038c fix(ui): показывать достоверный снимок контейнера 2026-10-01 11:02:31 +07:00
Denozordec c419a46955 fix(ui): подтверждать команды контейнеров ответом API 2026-10-01 10:53:07 +07:00
Denozordec 860add8edf fix(ui): сохранить достоверные состояния контейнеров 2026-10-01 10:47:11 +07:00
Denozordec 644ddb64b3 fix(api): показать частичные отказы каталога контейнеров 2026-10-01 10:39:37 +07:00
Denozordec 1e0fc4cd2d fix(ui): завершить доступность формы пользователей 2026-10-01 10:35:34 +07:00
Denozordec 9320c11d58 fix(ui): различать снимок историю и неизвестный каталог пользователей 2026-10-01 10:18:42 +07:00
Denozordec c37498512f fix(api): показывать источник и частичные сбои каталога интерфейсов 2026-10-01 10:10:41 +07:00
Denozordec ecd72d9750 fix(ui): сохранять контекст каталога и выбирать клиента IPsec 2026-10-01 10:01:09 +07:00
Denozordec 4896d8ef6f fix(ui): продолжать частичное сохранение пользователя без дублей 2026-10-01 09:53:23 +07:00
Denozordec 02ca48ef9c fix(ui): подтверждать удаление пользователя и сохранять ошибку 2026-10-01 09:39:39 +07:00
Denozordec 2d5fb3598f fix(ui): показывать подтверждённые каталоги пользователей 2026-10-01 09:34:51 +07:00
Denozordec 0a332ce341 chore(docs): сохранить карту реализации администрирования 2026-10-01 09:33:40 +07:00
Denozordec 517e0bca67 chore(test): зафиксировать локальную приёмку маршрутизации и диагностики 2026-10-01 09:33:02 +07:00
Denozordec 3c41de136e fix(ui): сохранять явный выбор туннеля диагностики 2026-10-01 09:21:05 +07:00
Denozordec d28d7a2cdb fix(ui): улучшить доступность и мобильный вывод диагностики 2026-10-01 09:17:25 +07:00
Denozordec 440e10c646 fix(api): прекращать диагностику при отмене HTTP 2026-10-01 09:00:19 +07:00
Denozordec 9d2101d700 fix(api): сохранять неизвестные показатели диагностики 2026-10-01 08:54:31 +07:00
Denozordec 33185fddae fix(api): проверять параметры и подтверждать результат MTU 2026-10-01 08:49:54 +07:00
Denozordec 68ccc49135 fix(api): отделить отказ диагностики от успешного вывода 2026-10-01 08:42:06 +07:00
Denozordec b60b7ac8e8 fix(ui): проверять источник IPv4 для диагностики 2026-10-01 08:36:31 +07:00
Denozordec d0c715e93c fix(ui): сохранять устройство диагностики в URL 2026-10-01 08:31:50 +07:00
Denozordec 1b91ba4e1d fix(ui): показывать реальный источник и ошибки DNS 2026-10-01 04:24:28 +07:00
Denozordec 155dfc5716 fix(ui): подтверждать GRE перед тестом пропускной способности 2026-10-01 04:20:19 +07:00
Denozordec b856215c03 fix(ui): согласовать права диагностики с API 2026-10-01 04:14:52 +07:00
Denozordec cba3db1508 fix(ui): сохранять каталог устройств диагностики при отказе 2026-10-01 04:11:43 +07:00
Denozordec 6fa8e5bfd1 fix(ui): различать результат и отмену диагностики 2026-10-01 04:07:08 +07:00
Denozordec 8bda85129f fix(ui): проверить терминал и мобильный выбор устройства 2026-10-01 03:59:47 +07:00
Denozordec ea4095d1f1 fix(api): отличать отказ терминала от пустого вывода 2026-10-01 03:43:35 +07:00
Denozordec a2eae189be fix(ui): подтверждать выполнение команд терминала 2026-10-01 03:36:14 +07:00
Denozordec bc20e5b9f4 fix(ui): завершить доступное управление картой сети 2026-10-01 03:26:31 +07:00
Denozordec bc83b8dbbf fix(ui): вписывать топологию в область карты 2026-10-01 03:18:02 +07:00
Denozordec 89e48c2424 fix(ui): адаптировать панели и действия карты сети 2026-10-01 03:06:28 +07:00
Denozordec 8bc4f22af7 fix(ui): сохранять контекст карты сети в ссылке 2026-10-01 02:26:03 +07:00
Denozordec 0cdb43a29d fix(ui): обновлять детали и пояснять модель карты сети 2026-10-01 02:15:52 +07:00
Denozordec 6cb27b2aab chore(test): проверить смену источника карты сети 2026-10-01 02:00:21 +07:00
Denozordec 1bce4e1ea2 fix(ui): показывать ошибки и свежесть NetFlow карты 2026-10-01 01:56:39 +07:00
Denozordec e47e2b4029 fix(ui): убрать выдуманные измерения из карты сети 2026-10-01 01:46:29 +07:00
Denozordec 329d6b82f7 fix(ui): показывать неизвестный статус карты отдельно 2026-10-01 01:39:53 +07:00
Denozordec 1215cbe8ae fix(ui): проверять целостность снимка карты сети 2026-10-01 01:33:50 +07:00
Denozordec 66fde3fc62 fix(ui): завершить локальную приёмку оптимизатора 2026-10-01 01:14:18 +07:00
Denozordec 5a23dfcfe6 fix(ui): согласовать настройки и контекст Community 2026-10-01 01:04:02 +07:00
Denozordec 6e6558bde9 fix(ui): исключить подстановку моковых WAN в live расчёт 2026-10-01 00:58:59 +07:00
Denozordec 338335ddc0 fix(api): проверять однозначность Router ID оптимизатора 2026-10-01 00:53:40 +07:00
Denozordec 7077844655 fix(api): передавать происхождение метрик оптимизатора OSPF 2026-10-01 00:50:17 +07:00
Denozordec 255f272fab fix(ui): отделить измерения цепочек от модели 2026-10-01 00:41:54 +07:00
Denozordec 9404bd5136 fix(ui): пояснить расчётные метрики preview OSPF 2026-09-30 23:40:56 +07:00
Denozordec d68c11e58a chore(test): проверить смену источника оптимизатора 2026-09-30 23:37:17 +07:00
Denozordec c82434c435 fix(ui): сохранять устройство оптимизатора в адресе 2026-09-30 23:34:05 +07:00
Denozordec 2cdfa2cb13 fix(ui): не подменять неизвестный индекс маршрута нулём 2026-09-30 23:27:14 +07:00
Denozordec fb70d80220 fix(ui): завершить адаптивность оптимизатора маршрутов 2026-09-30 23:23:58 +07:00
Denozordec 0ee7f02e87 fix(api): проверять исходные шаблоны и план оптимизации OSPF 2026-09-30 23:03:48 +07:00
Denozordec c88e1400bf fix(ui): подтверждать применение OSPF и согласовать права 2026-09-30 22:56:31 +07:00
Denozordec bf2815b5a4 fix(ui): проверять контекст расчёта OSPF оптимизатора 2026-09-30 22:47:04 +07:00
Denozordec f93dfe34c2 fix(ui): сохранять согласованные данные оптимизатора 2026-09-30 22:38:11 +07:00
Denozordec 59f0f82176 fix(ui): обозначить расчёт и симуляцию оптимизатора 2026-09-30 22:29:46 +07:00
Denozordec e58604c3c0 fix(ui): завершить адаптивность рекурсивных маршрутов 2026-09-30 22:19:21 +07:00
Denozordec 54ef752a5b fix(api): сохранять достоверные параметры рекурсивных маршрутов 2026-09-30 22:08:22 +07:00
Denozordec 50fad3ac80 fix(ui): сохранять контекст и блокировку рекурсивных маршрутов 2026-09-30 21:43:41 +07:00
Denozordec 52493debc2 fix(api): валидировать применение и восстановление маршрутов 2026-09-30 21:15:23 +07:00
Denozordec f5fc9282e2 fix(ui): подтверждать операции рекурсивных маршрутов 2026-09-30 21:08:01 +07:00
Denozordec 79a8d95e7d fix(ui): завершить адаптивность и приёмку OSPF 2026-09-30 20:43:10 +07:00
Denozordec 259c5c3014 fix(ui): связывать OSPF узлы по подтверждённым ID 2026-09-30 20:19:37 +07:00
Denozordec 7a68ae8184 fix(ui): сохранять поиск и выбранный узел OSPF 2026-09-30 20:09:00 +07:00
Denozordec 1d522527b4 fix(ui): восстанавливать контекст OSPF из URL 2026-09-30 19:58:43 +07:00
Denozordec af05ad5cda fix(api): показывать подтверждённые метрики OSPF 2026-09-30 19:49:47 +07:00
Denozordec e3791f3496 fix(api): сохранять неизвестные показатели BFD и OSPF 2026-09-30 19:42:07 +07:00
Denozordec 47dcd89b3c fix(api): закрепить неизвестное состояние BFD в строгом каталоге 2026-09-30 18:13:51 +07:00
Denozordec b1080b0a05 fix(ui): учитывать область в стоимости соседа OSPF 2026-09-30 18:09:10 +07:00
Denozordec 2199950681 fix(ui): проверять диапазон AS в каталоге BGP 2026-09-30 18:06:18 +07:00
Denozordec 748a3c00ad fix(ui): не подставлять выдуманный cost соседа OSPF 2026-09-30 18:04:14 +07:00
Denozordec 3dc25628af fix(ui): сохранять переходные и неизвестные состояния OSPF 2026-09-30 18:02:11 +07:00
Denozordec c18c148a23 fix(ui): разделить время опроса и подтверждения OSPF 2026-09-30 17:50:47 +07:00
Denozordec baa43b7ad4 fix(ui): защищать оптимизацию OSPF и подтверждение результата 2026-09-30 17:46:56 +07:00
Denozordec 4254904c3a fix(ui): сохранять снимки OSPF при отказе устройств 2026-09-30 17:37:23 +07:00
Denozordec a89cefcbf2 fix(api): показывать ошибки строгого OSPF-каталога 2026-09-30 17:27:44 +07:00
Denozordec 72a63b4a4f fix(ui): улучшить мобильную сводку и детали BGP 2026-09-30 17:23:18 +07:00
Denozordec aa678b9d5d fix(ui): восстанавливать поиск и фильтры BGP из URL 2026-09-30 17:15:37 +07:00
Denozordec 5c307a5b6c fix(bgp): сохранять неизвестные AS и тип сессии 2026-09-30 17:09:32 +07:00
Denozordec 2e17cbe926 fix(ui): сохранять контекст и свежесть опроса BGP 2026-09-30 17:05:37 +07:00
Denozordec 8fdc4b917b fix(bgp): отображать только подтверждённые показатели сессий 2026-09-30 16:56:28 +07:00
Denozordec 7046808429 fix(ui): сохранять BGP-сессии при частичных отказах 2026-09-30 16:48:46 +07:00
Denozordec 85e701960e fix(api): показывать ошибки опроса в каталоге BGP 2026-09-30 16:41:45 +07:00
Denozordec f9cfb4e532 chore(docs): сохранить карту маршрутизации и диагностики 2026-09-30 16:38:14 +07:00
Denozordec 919d0c4714 fix(api): проверить контекст GRE и завершить локальную приёмку этапа 9 2026-09-30 16:29:59 +07:00
Denozordec a29ee31052 fix(ui): улучшить мобильные экраны и сводку GRE и VXLAN 2026-09-30 16:27:10 +07:00
Denozordec fc3d588cec fix(ui): убрать неподтверждённые данные и действия GRE-пулов 2026-09-30 16:10:21 +07:00
Denozordec 337d14abdf fix(gre): экспортировать поддерживаемые параметры и очищать PSK 2026-09-30 16:06:32 +07:00
Denozordec a21252fb02 fix(ui): показывать свежесть GRE и закреплять экспорт 2026-09-30 16:00:48 +07:00
Denozordec ceb2113209 fix(ui): изолировать операции GRE и готовность устройств 2026-09-30 15:56:51 +07:00
Denozordec fb009344e1 fix(ui): сохранять подтверждённые данные GRE при отказах 2026-09-30 15:49:26 +07:00
Denozordec 6ea3cdad0e fix(ui): учитывать неизвестный статус устройств VPN 2026-09-30 15:41:45 +07:00
Denozordec 6f00596e33 fix(api): сообщать об отказах общего GRE-каталога 2026-09-30 15:31:09 +07:00
Denozordec d112935c73 fix(api): отклонять несогласованные объекты IPsec и VXLAN 2026-09-30 15:25:29 +07:00
Denozordec 0e0f98e0f4 fix(vxlan): корректно экспортировать параметры RouterOS 2026-09-30 15:21:55 +07:00
Denozordec d200e87e3d fix(ui): показывать достоверное состояние и экспорт VXLAN 2026-09-30 15:15:13 +07:00
Denozordec 6fc342cd2c fix(ui): проверить клавиатуру и адаптивность IPsec 2026-09-30 15:06:31 +07:00
Denozordec ce43b2d762 fix(ui): показывать свежесть опроса и изменений IPsec 2026-09-30 14:58:55 +07:00
Denozordec 921aba3054 fix(ui): изолировать контекст и операции IPsec 2026-09-30 14:55:20 +07:00
Denozordec f0071a495c fix(ui): учитывать готовность устройства в действиях IPsec 2026-09-30 14:48:21 +07:00
Denozordec b8923d075d fix(ui): сохранять данные IPsec при сбоях опроса 2026-09-30 14:43:22 +07:00
Denozordec 8fab2f7fd2 chore(docs): зафиксировать общую регрессию Filters 2026-09-30 12:44:31 +07:00
Denozordec 29a4ea2e5b fix(ui): улучшить клавиатурный ввод Community в Filters 2026-09-30 12:39:45 +07:00
Denozordec 4ae5f2edcd fix(ui): показывать свежесть данных Filters 2026-09-30 12:25:42 +07:00
Denozordec 0ab5a23eb3 fix(ui): сохранять недоступные шлюзы Filters 2026-09-30 12:21:00 +07:00
Denozordec 860e88ed3e fix(ui): читать актуальную цель копирования Filters 2026-09-30 12:10:20 +07:00
Denozordec 365ab2173c fix(ui): изолировать источники данных Filters 2026-09-30 12:05:28 +07:00
Denozordec 24cc66d8d2 fix(ui): отклонять устаревшее чтение Filters 2026-09-30 11:57:22 +07:00
Denozordec 1c7ffa07a8 fix(ui): проверять данные загрузки Filters 2026-09-30 11:50:36 +07:00
Denozordec 1fba16e1f2 fix(ui): изолировать операции восстановления Filters 2026-09-30 11:42:57 +07:00
Denozordec 7554f1804b fix(ui): подтверждать перемещение правил Filters 2026-09-30 11:35:36 +07:00
Denozordec c4011cfaff fix(ui): подтверждать удаление правил Filters 2026-09-30 11:29:54 +07:00
Denozordec 8412ba9bbe fix(ui): подтверждать копирование правил Filters 2026-09-30 11:24:48 +07:00
Denozordec 1cf7297d64 fix(ui): подтверждать сохранение правил Filters 2026-09-30 11:17:32 +07:00
Denozordec 4b01f20558 fix(ui): показывать свежесть срезов Firewall 2026-09-30 11:04:57 +07:00
Denozordec c2159508d5 fix(ui): изолировать операции Firewall по источнику 2026-09-30 10:56:49 +07:00
Denozordec 5e6057842f fix(ui): сохранять недоступные срезы Firewall 2026-09-30 10:46:36 +07:00
Denozordec 59440555cd fix(ui): сохранять данные Firewall при сбоях загрузки 2026-09-30 10:33:54 +07:00
Denozordec 92ba474ca9 fix(ui): исключить конфликты восстановления Firewall 2026-09-30 10:24:35 +07:00
Denozordec 14f8b1ee9a fix(ui): подтверждать перемещение правил Firewall 2026-09-30 10:18:34 +07:00
Denozordec b47c5645d5 fix(ui): подтверждать сохранение форм Firewall 2026-09-30 10:13:11 +07:00
Denozordec 14a9e6d26c fix(ui): подтверждать удаление объектов Firewall 2026-09-30 10:07:07 +07:00
Denozordec 0c460d4767 fix(ui): подтверждать переключение адресов Firewall 2026-09-30 09:59:36 +07:00
Denozordec 131ecb8668 fix(ui): подтверждать переключение правил Firewall 2026-09-30 09:52:29 +07:00
Denozordec 14be83a869 fix(ui): проверять данные VXLAN и гонки загрузки 2026-09-30 09:47:51 +07:00
Denozordec e163a9ed9d fix(ui): показывать частичные сбои опроса VXLAN 2026-09-30 09:42:43 +07:00
Denozordec 4dc2ee2129 fix(ui): различать ошибку и пустые данные VXLAN 2026-09-30 09:37:31 +07:00
Denozordec 3d518ff902 fix(ui): подтверждать сохранение формы GRE 2026-09-30 09:34:12 +07:00
Denozordec 66b439cc2e fix(ui): подтверждать переключение туннелей GRE 2026-09-30 00:42:28 +07:00
Denozordec 780d5e9237 fix(ui): подтверждать удаление туннеля GRE 2026-09-30 00:39:39 +07:00
Denozordec f5b64ea154 fix(ui): подтверждать сохранение peer IPsec 2026-09-29 19:31:55 +07:00
Denozordec 611f31875e fix(ui): подтверждать удаление объектов IPsec 2026-09-29 19:28:26 +07:00
Denozordec 74e7cd257d fix(ui): сохранять контекст экспорта сертификатов IPsec 2026-09-29 19:23:34 +07:00
Denozordec 86ecf764ff fix(ui): подтверждать сохранение пользователей IPsec 2026-09-29 19:15:15 +07:00
Denozordec f9413d5100 fix(ui): подтверждать инициализацию IPsec и сохранять форму 2026-09-29 19:09:22 +07:00
Denozordec 4a8d696e4e fix(ui): проверять ревизии и сохранять ошибки истории 2026-09-29 19:04:00 +07:00
Denozordec 46d62c98b8 fix(ui): сохранять ошибку загрузки истории IPsec 2026-09-29 18:55:46 +07:00
Denozordec 3dd4ad34ee fix(ui): проверять подтверждение восстановления сетевых настроек 2026-09-29 13:58:26 +07:00
Denozordec d0f5649725 fix(ui): различать ошибки и пустую историю WireGuard 2026-09-29 13:42:00 +07:00
Denozordec 1d1de12c58 fix(ui): сохранять подтверждение восстановления при ошибке 2026-09-29 13:37:32 +07:00
Denozordec 221d6db10c fix(ui): проверять подтверждение восстановления WireGuard 2026-09-29 08:43:31 +07:00
Denozordec 425a77a1ca fix(ui): сохранять точный выбор пира при экспорте WireGuard 2026-09-29 08:40:36 +07:00
Denozordec 4aa4726c6b fix(ui): защитить экспорт WireGuard от устаревших ответов 2026-09-29 08:37:53 +07:00
Denozordec d3ae2598fb fix(ui): подтверждать переключение интерфейсов WireGuard 2026-09-29 08:33:10 +07:00
Denozordec 8fff3d7fff fix(ui): подтверждать создание интерфейса и пира WireGuard 2026-09-29 08:30:07 +07:00
Denozordec ec6ec52965 fix(ui): защитить форму импорта WireGuard во время запроса 2026-09-29 08:22:58 +07:00
Denozordec efc6da1249 fix(ui): проверять подтверждение импорта WireGuard 2026-09-28 23:58:59 +07:00
Denozordec f980619046 fix(ui): подтверждать удаление WireGuard и блокировать повторы 2026-09-28 23:52:43 +07:00
Denozordec 165f40acd7 fix(ui): завершить локальную приёмку трафика и статистики 2026-09-28 23:45:48 +07:00
Denozordec 48b7a061c1 fix(ui): обозначить выбранные состояния и поиск трафика 2026-09-28 17:02:16 +07:00
Denozordec 460cf67313 fix(ui): выбирать неактивные серверы для месячного отчёта 2026-09-28 16:59:40 +07:00
Denozordec 7dd377d7c3 fix(ui): выбирать календарный месяц отчёта потоков 2026-09-28 16:54:17 +07:00
Denozordec 7a01e8d545 test(ui): проверить ожидание первого сообщения SSE 2026-09-28 16:49:56 +07:00
Denozordec 88a8b5f02f fix(ui): отображать одиночные измерения на графике трафика 2026-09-28 16:47:17 +07:00
Denozordec ed491c313f fix(ui): распознавать CRLF и комментарии в потоке SSE 2026-09-28 11:38:11 +07:00
Denozordec f50a29f116 fix(ui): снимать статус live при отсутствии обновлений 2026-09-28 11:35:29 +07:00
Denozordec 5734340db2 fix(ui): исключить подмену выбранного среза в сводке трафика 2026-09-28 11:31:24 +07:00
Denozordec c6d146d38f fix(ui): показывать временные отметки трафика серверов 2026-09-28 11:27:20 +07:00
Denozordec 65241c94e9 fix(ui): показывать время получения аналитики потоков 2026-09-28 11:22:20 +07:00
Denozordec 7318c5286b test(ui): проверить поздний фрагмент открытого потока 2026-09-28 11:12:06 +07:00
Denozordec 53bca894bb chore(docs): сохранить общий план реализации UX/UI 2026-09-24 21:13:05 +07:00
Denozordec c3ce626b47 test(ui): проверить разрывы основного графика трафика 2026-09-24 21:10:42 +07:00
Denozordec ee3dcdacf2 test(ui): проверить трафик после смены источника данных 2026-09-24 21:09:39 +07:00
Denozordec 2c2a3ef078 test(ui): проверить отмену потока при смене сервера 2026-09-24 21:07:44 +07:00
Denozordec c563583ccb fix(ui): привязать фильтр интерфейса к серверу потоков 2026-09-24 16:04:08 +07:00
Denozordec 1d98a2a66e fix(ui): сохранять пропуски в мини-графиках измерений 2026-09-24 15:58:23 +07:00
Denozordec f3568d836b fix(ui): корректно отображать историю в карточках трафика 2026-09-24 15:54:53 +07:00
Denozordec ccbc1aa029 fix(ui): пояснять отсутствие отдельного направления трафика 2026-09-24 15:51:32 +07:00
Denozordec ec8e28d15d fix(ui): отличать пустую историю трафика от нулевой 2026-09-24 10:06:46 +07:00
Denozordec 7c6f937cec fix(ui): отличать неизвестные flow-метрики от нулевых 2026-09-24 10:02:50 +07:00
Denozordec a03909e504 fix(ui): отклонять повреждённые данные live-потока 2026-09-24 09:58:32 +07:00
Denozordec 094c812c37 fix(ui): адаптировать длинные данные и пустые списки трафика 2026-09-24 09:53:10 +07:00
Denozordec cb16c03e01 test(ui): проверить период трафика на телефоне 2026-09-24 01:40:34 +07:00
Denozordec 3dc85a22e9 fix(ui): адаптировать панели трафика для телефона 2026-09-24 01:39:32 +07:00
Denozordec 8ac40ba78a fix(ui): показывать подтверждённые данные месячного отчёта 2026-09-24 01:36:36 +07:00
Denozordec 24313340d5 fix(ui): привязать flow-аналитику к контексту запроса 2026-09-24 01:31:43 +07:00
Denozordec 94dd453b33 fix(ui): защитить списки трафика от устаревших ответов 2026-09-24 01:25:20 +07:00
Denozordec 480aa82f78 fix(ui): привязать детали трафика к выбранному контексту 2026-09-24 01:18:13 +07:00
Denozordec 9eb6a3fad1 fix(ui): показывать остановку потока live-трафика 2026-09-24 01:13:11 +07:00
Denozordec e05b3a38c8 fix(ui): сохранять и проверять контекст отчёта статистики 2026-09-23 20:30:11 +07:00
Denozordec 94ad73212b fix(ui): проверять период статистики до запроса 2026-09-23 20:26:23 +07:00
Denozordec 167e19cd91 fix(ui): отделить ошибки статистики от пустых данных 2026-09-23 20:22:18 +07:00
Denozordec 3898881d8a test(ui): завершить локальную приёмку серверов и мониторинга 2026-09-23 20:18:35 +07:00
Denozordec ddb47a0ee4 test(ui): проверить смену режима во время опроса 2026-09-23 20:12:42 +07:00
Denozordec 27f8323624 test(ui): проверить опрос сервера из очереди проблем 2026-09-23 14:50:16 +07:00
Denozordec ca9474ac83 fix(ui): адаптировать формы серверов и прокрутку ресурсов 2026-09-23 14:48:13 +07:00
Denozordec 9cd15528c9 fix(ui): исправить адаптивность форм и вкладок мониторинга 2026-09-23 14:38:52 +07:00
Denozordec a9ed459481 fix(ui): убрать неработающий экспорт серверов 2026-09-23 14:28:39 +07:00
Denozordec 0734679fd6 fix(ui): сохранять фильтры ресурсов в URL 2026-09-23 09:28:03 +07:00
Denozordec fd7a08d5dc fix(ui): сохранять вкладку и фильтры Ping в URL 2026-09-23 09:24:18 +07:00
Denozordec f7e0e20673 fix(ui): сериализовать ручной опрос и ограничить ожидание Speed-test 2026-09-23 09:17:20 +07:00
Denozordec cc40a5e33d fix(ui): блокировать изменение проб во время Speed-test 2026-09-21 21:14:13 +07:00
Denozordec ce14f5de3e fix(ui): подтверждать удаление проб и изменение звезды 2026-09-21 21:08:06 +07:00
Denozordec 4f235058be fix(ui): подтверждать переключение проб через API 2026-09-21 21:03:14 +07:00
Denozordec 2f5b617e8c fix(ui): сохранять формы проб после подтверждения API 2026-09-21 16:24:07 +07:00
Denozordec bb9278fe58 fix(ui): учитывать права на действия мониторинга 2026-09-21 16:16:16 +07:00
Denozordec 61950638bc fix(ui): учитывать права на операции с серверами 2026-09-21 15:00:13 +07:00
Denozordec e9e5030047 fix(ui): адаптировать строки Ping-проб для телефона 2026-09-21 09:58:43 +07:00
Denozordec dfb993e1a3 fix(ui): сохранять фильтры серверов в адресе страницы 2026-09-21 09:50:47 +07:00
Denozordec b64e6834a9 chore(docs): подготовить этап серверов и мониторинга 2026-09-20 22:11:27 +07:00
Denozordec d25afa4b9e fix(ui): завершить переходы к объектам дашборда через VXLAN 2026-09-20 22:08:00 +07:00
Denozordec af84f0135d fix(ui): открывать интерфейс WireGuard с контекстом сервера 2026-09-20 21:59:57 +07:00
Denozordec 67a152b7c8 fix(ui): открывать GRE-туннель с контекстом сервера 2026-09-20 17:09:33 +07:00
Denozordec 6c8e66693b fix(ui): открывать выбранную проверку из очереди проблем 2026-09-20 17:00:30 +07:00
Denozordec 668c7c6823 fix(ui): открывать выбранный сервер из очереди проблем 2026-09-20 16:51:28 +07:00
Denozordec 4ec32765df fix(ui): поднять очередь проблем и уточнить сводку дашборда 2026-09-20 12:04:45 +07:00
Denozordec fe9df167ec fix(ui): объединить навигацию и сделать поиск доступным 2026-09-20 12:04:21 +07:00
Denozordec 9b3196363c fix(ui): адаптировать шапку страницы к узким экранам 2026-09-20 11:49:44 +07:00
Denozordec 6a726b13b4 fix(ui): сохранить данные дашборда при сбоях и гонках запросов 2026-09-20 00:58:58 +07:00
Denozordec 0d418e2e39 chore(ui): завершить миграцию Filters и удалить старый API 2026-09-20 00:47:33 +07:00
Denozordec 4fd844f977 fix(ui): подключить фильтры статистики с проверкой срезов 2026-09-14 22:27:12 +07:00
Denozordec 52af82af42 fix(ui): подготовить проверку срезов статистики для FilterQuery 2026-09-14 22:12:15 +07:00
Denozordec fca42aaa81 fix(ui): перенести фильтры BGP и бэкапов на FilterQuery 2026-09-14 22:09:48 +07:00
Denozordec a475b5c6df chore(ui): удалить старую основу Data Grid и TanStack v8 2026-09-14 22:05:50 +07:00
Denozordec bdba83b2b6 fix(ui): перенести таблицы оптимизатора маршрутов на ReUI v9 2026-09-14 22:01:11 +07:00
Denozordec 1ea07f65b2 fix(ui): перенести таблицу рекурсивных маршрутов на ReUI v9 2026-09-14 13:54:05 +07:00
Denozordec ddf1851e92 fix(ui): перенести таблицы сборщика и проб на ReUI v9 2026-09-14 13:49:54 +07:00
Denozordec f81b35308b fix(ui): перенести таблицы Firewall и проверить порядок правил 2026-09-14 13:39:47 +07:00
Denozordec 45530cdb99 fix(ui): перенести таблицу сетевых фильтров на ReUI v9 2026-09-14 08:43:08 +07:00
Denozordec d47f7ab162 fix(ui): перенести таблицы администрирования и сортировать бэкапы по байтам 2026-09-14 08:38:09 +07:00
Denozordec 583b23ded1 fix(ui): перенести таблицы IPsec и GRE на ReUI v9 2026-09-14 08:31:16 +07:00
Denozordec 8b9b584513 fix(ui): перенести таблицу VXLAN на ReUI v9 2026-09-13 21:30:52 +07:00
Denozordec e7d45d194d fix(ui): перенести таблицы WireGuard на ReUI v9 2026-09-13 21:17:48 +07:00
Denozordec d3db82fd1c fix(ui): перенести таблицы BGP и OSPF на ReUI v9 2026-09-13 21:10:06 +07:00
Denozordec e66762ca2e fix(ui): перенести основные таблицы на ReUI v9 2026-09-13 21:01:23 +07:00
Denozordec 3d3c45e4d2 fix(ui): завершить основу таблиц и фильтров серверов 2026-09-13 14:46:17 +07:00
Denozordec f9a4a07996 fix(ui): исправить зависимости мемоизации таблицы 2026-09-13 09:56:02 +07:00
Denozordec e7755a4d16 chore(ui): сохранить основу поэтапной миграции таблиц 2026-09-13 09:46:41 +07:00
Denozordec c30f817926 fix(servers): исправить сохранение и опрос серверов 2026-09-13 09:32:37 +07:00
Denozordec 88326d9851 chore(ui): зафиксировать исходную базу улучшений интерфейса 2026-09-13 01:48:05 +07:00
DenozordecandCursor f8e7437210 fix(ui): убрать горизонтальный скролл в шторке сертификата
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 3m9s
Docker images / backend-image (push) Successful in 2m46s
Docker images / backend-test (push) Successful in 1m56s
Docker images / updater-image (push) Successful in 49s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 5s
Co-authored-by: Cursor <[email protected]>
2026-09-13 01:22:36 +07:00
DenozordecandCursor e0364d45df test(ipsec): закрепить отсутствие proposal-ключей в .sswan
Co-authored-by: Cursor <[email protected]>
2026-09-13 01:22:36 +07:00
DenozordecandCursor bbff586da2 fix(ipsec): убрать невалидный ike-proposal из .sswan-профиля
Co-authored-by: Cursor <[email protected]>
2026-09-13 01:22:36 +07:00
DenozordecandCursor fbd1be8952 test(ipsec): покрыть формы ret и print в декодере файла RouterOS
Docker images / publish-release (push) Successful in 9s
Docker images / frontend-image (push) Successful in 3m4s
Docker images / backend-image (push) Successful in 2m25s
Docker images / backend-test (push) Successful in 2m28s
Docker images / updater-image (push) Successful in 51s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Co-authored-by: Cursor <[email protected]>
2026-09-13 01:01:42 +07:00
DenozordecandCursor 0bf5d6065c fix(ipsec): читать содержимое .p12 из поля ret ответа /file/get
Co-authored-by: Cursor <[email protected]>
2026-09-13 01:01:31 +07:00
DenozordecandCursor 67c3a4fd73 test(ipsec): покрыть декодер содержимого файла RouterOS
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 3m14s
Docker images / backend-image (push) Successful in 3m19s
Docker images / backend-test (push) Successful in 1m53s
Docker images / updater-image (push) Successful in 49s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 6s
Co-authored-by: Cursor <[email protected]>
2026-09-13 00:44:53 +07:00
DenozordecandCursor 9beb24273b fix(ipsec): скачивать .p12 через /file/get вместо несуществующего REST-пути
Co-authored-by: Cursor <[email protected]>
2026-09-13 00:44:44 +07:00
DenozordecandCursor 5cc6128086 fix(ipsec): уточнить подсказку про политику sensitive у группы пользователя
Docker images / publish-release (push) Successful in 7s
Docker images / frontend-image (push) Successful in 3m13s
Docker images / updater-image (push) Successful in 44s
Docker images / backend-image (push) Successful in 2m44s
Docker images / backend-test (push) Successful in 2m13s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Co-authored-by: Cursor <[email protected]>
2026-09-13 00:15:33 +07:00
DenozordecandCursor 569b6be2b4 fix(ipsec): не блокироваться на пароле .p12 и подсказать политику sensitive
Docker images / publish-release (push) Successful in 14s
Docker images / frontend-image (push) Successful in 2m54s
Docker images / updater-image (push) Successful in 45s
Docker images / backend-image (push) Successful in 2m43s
Docker images / backend-test (push) Successful in 2m18s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
Co-authored-by: Cursor <[email protected]>
2026-09-13 00:15:05 +07:00
DenozordecandCursor 752256f12e fix(ipsec): требовать пароль .p12 не короче 8 символов
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 2m19s
Docker images / backend-image (push) Successful in 2m40s
Docker images / backend-test (push) Successful in 1m51s
Docker images / updater-image (push) Successful in 39s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 7s
Co-authored-by: Cursor <[email protected]>
2026-09-13 00:10:10 +07:00
DenozordecandCursor a3502b9755 fix(ipsec): убрать неподдерживаемый параметр при подписи сертификата
Docker images / publish-release (push) Successful in 9s
Docker images / frontend-image (push) Successful in 2m17s
Docker images / backend-image (push) Successful in 2m20s
Docker images / backend-test (push) Successful in 1m37s
Docker images / updater-image (push) Successful in 38s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 6s
Co-authored-by: Cursor <[email protected]>
2026-09-12 22:21:19 +07:00
DenozordecandCursor 0e1524c600 fix(ipsec): клиенты через общий IKEv2-peer и отдельный клиентский сертификат
Docker images / frontend-image (push) Successful in 2m54s
Docker images / publish-release (push) Successful in 10s
Docker images / updater-image (push) Successful in 40s
Docker images / backend-image (push) Successful in 2m11s
Docker images / backend-test (push) Successful in 1m51s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 7s
Co-authored-by: Cursor <[email protected]>
2026-09-12 22:06:35 +07:00
DenozordecandCursor 11ae7593f4 fix(ipsec): убрать неподдерживаемый параметр при добавлении сертификата
Co-authored-by: Cursor <[email protected]>
2026-09-12 22:06:27 +07:00
Denozordec 3eb75ea0b8 feat(ipsec): add certificate export functionality by name
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 2m57s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 2m48s
Docker images / backend-test (push) Successful in 2m41s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 12s
- Implemented a new backend route for exporting existing client certificates in .p12 format by name.
- Enhanced the frontend to support exporting certificates directly from the IPsec server grid.
- Updated the IPsec page to manage certificate states and handle exports effectively.
- Introduced new utility functions for certificate handling and improved data structures to accommodate the changes.
- Added tests to ensure the reliability of the new certificate export feature.
2026-09-12 21:39:14 +07:00
Denozordec 7365d8d8fb feat(ipsec): enhance IPsec management with peer and certificate handling
Docker images / publish-release (push) Successful in 15s
Docker images / frontend-image (push) Successful in 2m56s
Docker images / backend-image (push) Successful in 2m35s
Docker images / backend-test (push) Successful in 2m39s
Docker images / updater-image (push) Successful in 44s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
- Added support for managing IPsec peers, including editing and deleting functionality.
- Introduced a new UI component for displaying peers and their details within the IPsec server grid.
- Updated the IPsec user creation form to allow binding identities to specific peers.
- Enhanced backend routes and services to handle peer patching and deletion requests.
- Improved data structures to accommodate multiple peers and certificates for each server.
- Added tests to ensure proper functionality of new peer management features.
2026-09-12 21:04:49 +07:00
Denozordec 7755d77340 feat(ipsec): управление IKEv2/IPsec VPN и клиентами из одного окна
Docker images / publish-release (push) Successful in 8s
Docker images / frontend-image (push) Successful in 4m19s
Docker images / backend-image (push) Successful in 2m40s
Docker images / backend-test (push) Successful in 2m32s
Docker images / updater-image (push) Successful in 50s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 15s
- мастер инициализации сервера: CA и серверный сертификаты, peer/profile/proposal, пул, mode-config, policy-template, managed NAT masquerade
- клиенты по сертификату (RSA) и PSK: статический IP или из пула, онлайн-статус по active-peers
- скачивание .p12 и strongSwan .sswan с инструкцией, перекачка с новой passphrase
- история изменений (config_revisions, секция ipsec) и restore только managed-объектов
- привязка IPsec-клиентов к пользователям приложения по Common Name
- страница /ipsec с KPI и вкладками Клиенты/Сервер/CLI, сайдбар, command palette
2026-09-12 20:20:56 +07:00
Denozordec 564aae21f0 fix(statistics): выровнять трафик-данные с картой сети
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 3m28s
Docker images / updater-image (push) Successful in 50s
Docker images / backend-image (push) Successful in 3m5s
Docker images / backend-test (push) Successful in 2m40s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
- Окно периода клампится к текущему моменту: «Сегодня»/«7 д» больше не делят байты на ещё не наступившие часы суток (avgBps/bps были занижены).
- Пресет «24 ч» шлёт точные ISO-границы «сейчас−24ч … сейчас» вместо дат «вчера…сегодня» (~48 ч); подпись диапазона и подсветка пресета сверяются по длительности окна.
- Таксономия сервисов как на карте: DNS/SSH/BGP/WireGuard/GRE и пустые метки сворачиваются в «Прочее» (normalizeFactService) в разбивке сервисов и pivot country×service.
- Тесты: кламп окна, ISO-24ч, будущее «to», невалидный диапазон, нормализация меток.
2026-09-12 13:22:06 +07:00
Denozordec 0fdd2fc1e1 feat(traffic-flow): раскрытие сервисов страны на карте сети
Docker images / frontend-image (push) Successful in 3m14s
Docker images / publish-release (push) Successful in 10s
Docker images / updater-image (push) Successful in 43s
Docker images / backend-image (push) Successful in 2m44s
Docker images / backend-test (push) Successful in 2m11s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
- Контракт: countryServiceGroups (группа сервисов страны) в flowMapHopsDto, тип FlowMapCountryServiceGroup.
- Бэкенд: вложенная агрегация brand×ISO в том же проходе hops; рёбра страна→сервис, пути с реальным EN; cap 8 / min 4, доля от байтов страны.
- Хелпер mapCountryServiceNodeId: id вида cc:us|svc:google, сервисы разных стран не смешиваются.
- Карта сети: клик по стране сдвигает колонку стран и раскрывает столбец её сервисов; остальные страны приглушены; панели страны и вложенного сервиса.
- Тесты: группы US/US+NL, рёбра от страны, cap/min вложенного слоя; mock countryServiceGroups для демо-режима.
2026-09-12 12:47:30 +07:00
DenozordecandCursor f15a7348db feat(traffic-flow): enhance country handling in flow analytics
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 3m26s
Docker images / backend-image (push) Successful in 2m23s
Docker images / backend-test (push) Successful in 2m19s
Docker images / updater-image (push) Successful in 47s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
- Introduced country-specific handling in traffic flow analytics, including new mappings for country nodes and edges.
- Added mock data for countries and their respective service paths to improve visualization in the Network Map.
- Updated the `countryName` function to utilize `Intl.DisplayNames` for better localization of country names.
- Enhanced tests to validate country handling and ensure accurate representation in flow analytics.

Co-authored-by: Cursor <[email protected]>
2026-09-12 11:38:37 +07:00
DenozordecandCursor f3c846201c feat(traffic-flow): enhance Instagram service handling and classification
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 3m20s
Docker images / backend-image (push) Successful in 2m25s
Docker images / backend-test (push) Successful in 2m34s
Docker images / updater-image (push) Successful in 43s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
- Added support for Instagram in traffic flow resolution and classification, including new test cases to validate its behavior.
- Introduced `INSTAGRAM` brand handling in the traffic flow logic, ensuring accurate service identification for Instagram-related traffic.
- Updated existing tests to cover various scenarios involving Instagram, including IP resolution and service categorization.
- Enhanced the service brand icon component to include an Instagram icon for better visual representation.

Co-authored-by: Cursor <[email protected]>
2026-09-12 11:20:16 +07:00
DenozordecandCursor ee9804f1bb feat(traffic-flow): enhance flow analytics and IP handling
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 3m20s
Docker images / backend-image (push) Successful in 2m40s
Docker images / backend-test (push) Successful in 2m29s
Docker images / updater-image (push) Successful in 51s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
- Introduced `canonicalIp` function to standardize IP address formats across the application, improving consistency in flow processing.
- Updated traffic flow analytics to utilize new endpoint resolution logic, enhancing accuracy in traffic classification.
- Enhanced tests for traffic flow analytics and IP handling, ensuring comprehensive coverage for new functionalities.
- Improved traffic flow destination resolution with additional test cases for various internet services, including Google and Fastly.

Co-authored-by: Cursor <[email protected]>
2026-09-12 01:51:23 +07:00
DenozordecandCursor 4ce6169d14 feat(traffic-flow): enhance traffic classification and map display
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 2m42s
Docker images / updater-image (push) Successful in 43s
Docker images / backend-image (push) Successful in 2m30s
Docker images / backend-test (push) Successful in 2m16s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 12s
- Added a new state variable `mapAsnLoaded` to track ASN loading status in the NetworkMapPage.
- Updated the page to conditionally display a message when ASN data is not loaded, improving user feedback.
- Enhanced traffic classification tests to include additional scenarios for Google and YouTube services, ensuring comprehensive coverage.
- Introduced a new function `pickMapInternetDest` to improve destination selection logic in traffic flow processing.
- Updated the traffic flow destination classification to handle new conditions and improve accuracy.

Co-authored-by: Cursor <[email protected]>
2026-09-12 01:07:26 +07:00
DenozordecandCursor 60a0970d73 feat(traffic-flow): enhance traffic flow analytics and overlay configuration
Docker images / publish-release (push) Successful in 12s
Docker images / frontend-image (push) Successful in 3m22s
Docker images / backend-image (push) Successful in 2m55s
Docker images / backend-test (push) Successful in 2m42s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 12s
- Updated the traffic flow analytics to include new state variables for named bytes, total bytes, and window seconds, improving data granularity.
- Modified the NetworkMapPage to display classified traffic data, including a new section for showing classified and total bytes.
- Enhanced the applyFlowOverlay function to support a new option for disabling GRE fast path, allowing for more flexible traffic flow configurations.
- Added a toggle in the FlowOverlaySheet component to enable or disable the GRE fast path setting, improving user control over traffic processing.
- Updated tests to validate the new functionalities and ensure accurate traffic flow analytics.

Co-authored-by: Cursor <[email protected]>
2026-09-12 00:03:13 +07:00
DenozordecandCursor fc29dcede7 feat(traffic-flow): add NAT fields to flow processing and analytics
Docker images / publish-release (push) Successful in 9s
Docker images / frontend-image (push) Successful in 3m30s
Docker images / backend-image (push) Successful in 2m51s
Docker images / backend-test (push) Successful in 2m22s
Docker images / updater-image (push) Successful in 45s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
- Introduced new fields for NAT source and destination IPs, as well as their respective ports, in the flow data model.
- Updated database schema and migration scripts to accommodate the new NAT fields in the `flow_buckets` table.
- Enhanced flow analytics and processing functions to utilize the new NAT fields, improving accuracy in traffic flow analysis.
- Added tests to validate the handling of NAT data in various scenarios, ensuring robustness in flow processing.

Co-authored-by: Cursor <[email protected]>
2026-09-11 23:01:48 +07:00
DenozordecandCursor 5f774ce26e feat(gre): enhance GRE tunnel status handling and IPsec configuration
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 3m1s
Docker images / backend-image (push) Successful in 2m31s
Docker images / backend-test (push) Successful in 2m18s
Docker images / updater-image (push) Successful in 40s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
- Introduced a new `greStatusMeta` function to streamline status retrieval and default handling.
- Updated GRE tunnel command generation to improve IPsec configuration handling, including optional fields for encryption and authentication algorithms.
- Enhanced the GRE tunnels data grid to display IPsec settings more clearly, including handling cases where certain parameters may be undefined.
- Refactored status display logic in the GRE page to utilize the new status meta function for consistency.

Co-authored-by: Cursor <[email protected]>
2026-09-11 15:39:32 +07:00
DenozordecandCursor 25b82997b6 feat(config-revisions): implement configuration history for Firewall, GRE, and WireGuard
Docker images / frontend-image (push) Successful in 2m51s
Docker images / publish-release (push) Successful in 12s
Docker images / updater-image (push) Successful in 44s
Docker images / backend-image (push) Successful in 2m49s
Docker images / backend-test (push) Successful in 2m20s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 12s
- Added configuration history management for Firewall, GRE, and WireGuard pages, enabling users to view and restore previous configurations.
- Introduced new components for displaying configuration history and integrated them into the respective pages.
- Enhanced API routes to support fetching and restoring configuration revisions, ensuring data consistency across the application.
- Updated state management to handle loading and restoring states effectively, improving user experience during data operations.
- Enhanced tests to cover new functionalities and ensure reliability.

Co-authored-by: Cursor <[email protected]>
2026-09-11 14:03:00 +07:00
DenozordecandCursor b4a3c3a925 feat(filters, recursive-routes): enhance configuration history and live data handling
- Introduced configuration history management in Filters and Recursive Routes pages, allowing users to view and restore previous configurations.
- Updated state management to handle live data loading and error states more effectively, improving user experience during data fetching.
- Added new components for displaying configuration history and integrated them into existing pages.
- Enhanced API interactions to support fetching and applying configuration revisions, ensuring data consistency across the application.
- Updated tests to cover new functionalities and ensure reliability.

Co-authored-by: Cursor <[email protected]>
2026-09-11 12:15:36 +07:00
DenozordecandCursor 9c0ee7940e feat(bgp, vxlan, ospf): enhance server data handling and introduce new routes
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 2m50s
Docker images / backend-image (push) Successful in 2m40s
Docker images / backend-test (push) Successful in 1m40s
Docker images / updater-image (push) Successful in 42s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 7s
- Added support for fetching and displaying server data in BGP, VXLAN, and OSPF pages, improving the overall user experience.
- Introduced new backend routes for OSPF and VXLAN, allowing for better data management and retrieval.
- Implemented mapping functions for backend server data to frontend types, ensuring consistency across components.
- Enhanced the sidebar to display counts for BGP sessions, VXLAN tunnels, and containers, providing users with quick insights into their network status.
- Updated tests to cover new functionalities and ensure reliability.

Co-authored-by: Cursor <[email protected]>
2026-09-11 11:18:04 +07:00
DenozordecandCursor 5750590b68 feat(traffic-flow): add rebuild facts endpoint and enhance traffic flow analytics
Docker images / publish-release (push) Successful in 9s
Docker images / frontend-image (push) Successful in 3m15s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 2m50s
Docker images / backend-test (push) Successful in 2m6s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
- Introduced a new endpoint `/traffic/flow/rebuild-facts` to rebuild flow facts from buckets, improving data accuracy and management.
- Updated traffic flow analytics to utilize the new `resolveInternetDest` function for better destination resolution.
- Enhanced tests for traffic flow IP handling and added new utility functions for managing internet destinations.

Co-authored-by: Cursor <[email protected]>
2026-09-11 10:32:51 +07:00
DenozordecandCursor 3c42c114f5 refactor(statistics): improve component structure and enhance state management
Docker images / publish-release (push) Successful in 8s
Docker images / frontend-image (push) Successful in 4m11s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 2m18s
Docker images / backend-test (push) Successful in 2m17s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
- Introduced Suspense for lazy loading in StatisticsPage to optimize rendering.
- Refactored StatisticsPage to separate inner logic into StatisticsPageInner for better readability.
- Updated PeriodSelector to utilize useCallback for handling state changes, improving performance and clarity.

Co-authored-by: Cursor <[email protected]>
2026-09-11 09:38:31 +07:00
DenozordecandCursor 5aef419582 fix(statistics): считать уникальный payload без дублей hops
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 3m13s
Docker images / updater-image (push) Successful in 46s
Docker images / backend-image (push) Successful in 2m52s
Docker images / backend-test (push) Successful in 2m10s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Co-authored-by: Cursor <[email protected]>
2026-09-11 01:27:40 +07:00
DenozordecandCursor 0c0dfa1df7 fix(statistics): не двоить overlay и транзит в кубе
Docker images / frontend-image (push) Successful in 3m25s
Docker images / publish-release (push) Successful in 14s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 3m9s
Docker images / backend-test (push) Successful in 2m18s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Co-authored-by: Cursor <[email protected]>
2026-09-11 00:46:25 +07:00
DenozordecandCursor c162a41bc0 fix(network-map): подписать unbound-пути парой серверов
Docker images / publish-release (push) Successful in 12s
Docker images / frontend-image (push) Successful in 3m29s
Docker images / backend-image (push) Successful in 2m41s
Docker images / backend-test (push) Successful in 2m22s
Docker images / updater-image (push) Successful in 52s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 7s
Co-authored-by: Cursor <[email protected]>
2026-09-10 22:19:05 +07:00
DenozordecandCursor 97e43b2335 feat(statistics): enhance interface handling and data aggregation
Docker images / publish-release (push) Successful in 15s
Docker images / frontend-image (push) Successful in 3m24s
Docker images / backend-image (push) Successful in 2m39s
Docker images / backend-test (push) Successful in 2m29s
Docker images / updater-image (push) Successful in 44s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
Updated the statistics aggregation service to improve interface resolution and data handling. Introduced new functions for managing interface aliases and collapsing server interface rows, ensuring accurate data representation. Enhanced test coverage for interface resolution and added checks for new functionality.

- Implemented `factIfaceAliases` and `collapseServerIfaceRows` for better interface data management.
- Updated `resolveIfaceName` to handle additional cases for interface indexing.
- Enhanced tests for interface resolution and aggregation logic.

Co-authored-by: Cursor <[email protected]>
2026-09-10 21:49:50 +07:00
DenozordecandCursor db21e1217c feat(network-map): enhance service selection logic and UI updates
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 2m52s
Docker images / backend-image (push) Successful in 2m20s
Docker images / backend-test (push) Successful in 2m19s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
Refactor the service selection mechanism to utilize a live reference for selected services, ensuring accurate display of service details. Update UI components to reflect changes in service selection, including dynamic updates for service metrics and paths. Additionally, introduce new utility functions for deduplicating flow rows across exporters in the backend, improving data handling in traffic flow analysis.

- Updated service selection logic in NetworkMapPage component.
- Enhanced UI to display live service data.
- Added deduplication functions in traffic-flow-dedup module for improved data integrity.

Co-authored-by: Cursor <[email protected]>
2026-09-10 19:54:16 +07:00
DenozordecandCursor 5bb9066be8 feat(statistics): добавить BI-разрез и сводную матрицу
Docker images / publish-release (push) Successful in 14s
Docker images / frontend-image (push) Successful in 3m30s
Docker images / updater-image (push) Successful in 52s
Docker images / backend-image (push) Successful in 3m8s
Docker images / backend-test (push) Successful in 2m23s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
Сопоставить клиентов с ifIndex как на карте трафика, чтобы KPI пользователей не обнулялся. На экране — разрез остальных измерений и сводная матрица.

Co-authored-by: Cursor <[email protected]>
2026-09-10 16:07:25 +07:00
DenozordecandCursor b1fd259f10 fix(statistics): убрать параметры хранения с родительских партиций
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 3m26s
Docker images / backend-image (push) Successful in 2m32s
Docker images / backend-test (push) Successful in 2m25s
Docker images / updater-image (push) Successful in 45s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
PostgreSQL 42809: FILLFACTOR и autovacuum нельзя задавать на partitioned parent — только на листьях.

Co-authored-by: Cursor <[email protected]>
2026-09-10 13:36:35 +07:00
DenozordecandCursor 3687bb8fa2 feat(statistics): добавить раздел статистики трафика
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 4m47s
Docker images / updater-image (push) Successful in 48s
Docker images / backend-image (push) Successful in 2m53s
Docker images / backend-test (push) Successful in 2m19s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
Куб IPFIX час+день с AND-слайсами и экраном отчётности /statistics, живой /traffic не меняем.

Co-authored-by: Cursor <[email protected]>
2026-09-10 11:57:47 +07:00
Denozordec a80caf5676 feat(netflow): локальные GeoLite2-базы для стран и ASN потоков
Docker images / publish-release (push) Successful in 15s
Docker images / frontend-image (push) Successful in 4m31s
Docker images / backend-image (push) Successful in 3m1s
Docker images / backend-test (push) Successful in 2m13s
Docker images / updater-image (push) Successful in 48s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 13s
Lookup страны/ASN при ingest теперь идёт сначала по локальным mmdb
MaxMind GeoLite2 (Country + ASN, зеркало P3TERX, без ключей), с
мгновенным синхронным ответом для IPv4/IPv6; RIPEstat остаётся
fallback до первой загрузки баз и при промахе.

- backend/src/services/traffic-flow-geoip.ts: ридеры maxmind, фасад
  resolveFlowIp (geoip-first → RIPE-кэш), статус ридеров
- geoip-update-collector.ts: conditional GET по ETag, валидация пробоем
  8.8.8.8 (US / AS15169), атомарная подмена с .prev-откатом
- джоба планировщика geoip_update (по умолчанию раз в 7 дней),
  настройки geoip_settings + миграция 0004, API /api/geoip
  (GET/PUT/update), контракты @mmapp/contracts/geoip
- engine/analytics/map-hops переведены на resolveFlowIp
- секция «GeoIP-базы» в настройках NetFlow, метки джобы на странице
  сбора данных, тесты test:geoip
2026-09-09 23:33:31 +07:00
DenozordecandCursor d39e3454aa feat(network-map): показывать ASN-бренды по доле среди сервисов
Docker images / publish-release (push) Successful in 15s
Docker images / frontend-image (push) Successful in 3m15s
Docker images / backend-image (push) Successful in 2m44s
Docker images / backend-test (push) Successful in 2m5s
Docker images / updater-image (push) Successful in 48s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
Долю считать среди именованных брендов, а не от всего окна. На карте всегда оставлять топ-8 и добавить иконки новых брендов.

Co-authored-by: Cursor <[email protected]>
2026-09-09 16:53:06 +07:00
DenozordecandCursor a8f2055c77 feat(netflow): расширить каталог ASN популярных сервисов
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 3m14s
Docker images / backend-image (push) Successful in 4m2s
Docker images / backend-test (push) Successful in 2m5s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
В аналитике потока видны сети со своим AS (Apple, Steam, VK, Epic и другие), а не только Cloudflare/AWS. Holder и порты Steam не перебивают Cloudflare CIDR.

Co-authored-by: Cursor <[email protected]>
2026-09-09 13:18:58 +07:00
DenozordecandCursor cdeb97d841 feat(certificates): добавить отключение автообновления на странице сертификатов
Docker images / publish-release (push) Successful in 15s
Docker images / frontend-image (push) Successful in 3m19s
Docker images / backend-image (push) Successful in 2m45s
Docker images / backend-test (push) Successful in 2m7s
Docker images / updater-image (push) Successful in 49s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
Co-authored-by: Cursor <[email protected]>
2026-09-08 16:21:50 +07:00
DenozordecandCursor 36c5305db7 feat(dashboard): собрать ops-дашборд на ReUI Frame с живыми данными
Docker images / publish-release (push) Successful in 14s
Docker images / frontend-image (push) Successful in 3m14s
Docker images / updater-image (push) Successful in 47s
Docker images / backend-image (push) Successful in 2m44s
Docker images / backend-test (push) Successful in 2m13s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
Co-authored-by: Cursor <[email protected]>
2026-09-08 15:13:27 +07:00
Denozordec 5f2b4e2d40 feat(backups): replace button with checkbox for server selection in backup settings
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 3m10s
Docker images / updater-image (push) Successful in 42s
Docker images / backend-image (push) Successful in 2m29s
Docker images / backend-test (push) Successful in 2m4s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
Updated the BackupCreateSheet and BackupsSettings components to use checkboxes for selecting servers instead of buttons. This improves user interaction by allowing for easier selection and toggling of server states. The Checkbox component is now integrated for better accessibility and functionality.
2026-09-08 14:47:44 +07:00
DenozordecandCursor cff26813b9 feat(backups): добавить S3-хранилище и обновить экран бэкапов
Docker images / publish-release (push) Successful in 16s
Docker images / frontend-image (push) Successful in 4m37s
Docker images / backend-image (push) Successful in 3m6s
Docker images / backend-test (push) Successful in 2m18s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
Сохранять снимки в S3-compatible бакет, скачивать и удалять их вместе с локальными файлами.
Привести /backups к DNA /servers: Frame, KPI, фильтры и реальное восстановление.

Co-authored-by: Cursor <[email protected]>
2026-09-08 14:06:27 +07:00
DenozordecandCursor 39c8ec4a02 fix(netflow): починить live-аналитику и снизить запись в PostgreSQL
Docker images / publish-release (push) Successful in 10s
Docker images / frontend-image (push) Successful in 3m20s
Docker images / updater-image (push) Successful in 49s
Docker images / backend-image (push) Successful in 2m44s
Docker images / backend-test (push) Successful in 2m9s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
Коллектор снова отдаёт назначения в живом потоке. Ошибки записи видны в статусе, лишние перезаписи минутных агрегатов убраны.

Co-authored-by: Cursor <[email protected]>
2026-09-08 12:46:37 +07:00
DenozordecandCursor c6c859a495 perf(db): сжать схему PostgreSQL 18 и повторно загрузить SQLite
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 3m23s
Docker images / backend-image (push) Successful in 2m47s
Docker images / backend-test (push) Successful in 2m2s
Docker images / updater-image (push) Successful in 44s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
При первом рестарте wipe всех таблиц и импорт из SQLite в компактную схему. Retention через DROP PARTITION, lz4 и AIO worker.

Co-authored-by: Cursor <[email protected]>
2026-09-08 10:54:36 +07:00
DenozordecandCursor bf5cfff12c ci(backend): убрать конфликт контейнера PostgreSQL в тестах
Docker images / publish-release (push) Successful in 13s
Docker images / frontend-image (push) Successful in 3m16s
Docker images / backend-image (push) Successful in 3m0s
Docker images / backend-test (push) Successful in 2m4s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 9s
На общем Docker-раннере имя mm-pg и порт 5432 сталкивались с прошлым запуском. Контейнер и порт теперь уникальны, старый mm-pg снимается.

Co-authored-by: Cursor <[email protected]>
2026-09-08 09:34:29 +07:00
DenozordecandCursor 30a8ec4420 fix(db): исправить импорт jsonb из sqlite
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Skipped
Docker images / frontend-image (push) Successful in 3m14s
Docker images / updater-image (push) Successful in 47s
Docker images / backend-test (push) Failing after 12s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
JSON-колонки передаются строкой с приведением ::jsonb, иначе node-pg кодирует массивы как PostgreSQL array и ETL падает. Повтор импорта идёт, пока нет маркера. Том PG18 монтируется в /var/lib/postgresql.

Co-authored-by: Cursor <[email protected]>
2026-09-08 09:28:03 +07:00
DenozordecandCursor ec43591a99 feat(db): перевести хранилище с SQLite на PostgreSQL
Docker images / publish-release (push) Successful in 11s
Docker images / frontend-image (push) Successful in 4m28s
Docker images / backend-image (push) Successful in 2m49s
Docker images / backend-test (push) Successful in 4m9s
Docker images / updater-image (push) Successful in 58s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 12s
При старте backend накатывает схему PostgreSQL 18 и, если база пустая, один раз импортирует mikrotik.db с тома. Повторный старт не копирует данные. Бэкап в UI идёт через pg_dump.

Co-authored-by: Cursor <[email protected]>
2026-09-08 01:36:48 +07:00
DenozordecandCursor 0e5fb065e2 fix(backend): уменьшить лишние записи SQLite
Docker images / frontend-image (push) Successful in 3m14s
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 2m8s
Docker images / updater-image (push) Successful in 47s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 10s
Убрать WAL TRUNCATE с hot path NetFlow, не писать неизменённые UPDATE
и служебные INSERT, кэшировать карты topology/analytics.

Co-authored-by: Cursor <[email protected]>
2026-09-07 20:22:20 +07:00
DenozordecandCursor 5188b2aff2 fix(network-map): обрезать пунктир до сервиса и показать пути клиентов
Docker images / publish-release (push) Successful in 13s
Docker images / backend-image (push) Successful in 1m58s
Docker images / frontend-image (push) Successful in 3m17s
Docker images / updater-image (push) Successful in 49s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Линия от обода EN до рамки сервиса; таблица клиент, узел и сервис с подсветкой на карте.

Co-authored-by: Cursor <[email protected]>
2026-09-07 17:39:18 +07:00
DenozordecandCursor cd1fd2c9d3 fix(network-map): рисовать пунктир и скорость до сервиса
Docker images / publish-release (push) Successful in 9s
Docker images / backend-image (push) Successful in 1m41s
Docker images / frontend-image (push) Successful in 2m55s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 43s
Docker images / prepare-release (push) Successful in 8s
Якорь hop на EN даже без nextHop; линия ARN\to Google с NetflowRateBadge.

Co-authored-by: Cursor <[email protected]>
2026-09-07 17:02:28 +07:00
DenozordecandCursor 77425cca32 fix(network-map): якорить сервисы на выходную ноду
Docker images / publish-release (push) Successful in 16s
Docker images / backend-image (push) Successful in 1m46s
Docker images / frontend-image (push) Successful in 2m56s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 45s
Docker images / prepare-release (push) Successful in 8s
Пунктир от EN, а не от JH; перетаскивание узлов сервисов; иконка Google без foreignObject.

Co-authored-by: Cursor <[email protected]>
2026-09-07 16:10:39 +07:00
DenozordecandCursor 29d245cde3 feat(traffic): enhance flow analytics and brand classification
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 2m12s
Docker images / frontend-image (push) Successful in 4m3s
Docker images / updater-image (push) Successful in 48s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
- Introduced `pickInternetPeer` function to improve IP classification by selecting the appropriate public IP from source and destination.
- Updated `buildFlowAnalytics` and related functions to utilize the new peer selection logic, enhancing accuracy in flow analytics.
- Added tests for new classifications and ensured existing tests cover new scenarios for Google and Cloudflare.
- Refactored traffic flow brand mappings to include additional CIDR ranges for Google and Cloudflare.

Co-authored-by: Cursor <[email protected]>
2026-09-07 15:35:52 +07:00
DenozordecandCursor 7a491a325d fix(network-map): убрать тяжёлую классификацию из poll карты
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 1m53s
Docker images / frontend-image (push) Successful in 3m11s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 49s
Docker images / prepare-release (push) Successful in 9s
Не сканировать RIPE и каталог по каждой строке окна; сервисы по уникальным dst и кэшу ASN. Порог доли на карте настраиваемый и отключаемый.

Co-authored-by: Cursor <[email protected]>
2026-09-07 14:17:04 +07:00
DenozordecandCursor db64621122 feat(network-map): показать конечные сервисы на карте сети
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 2m0s
Docker images / frontend-image (push) Successful in 3m15s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 45s
Docker images / prepare-release (push) Successful in 9s
NetFlow ≥ 5% окна, узлы с логотипом бренда справа от EN, скорость потока на рёбрах к сервисам.

Co-authored-by: Cursor <[email protected]>
2026-09-07 13:30:45 +07:00
DenozordecandCursor 6332d83a12 feat(traffic): показать поток NetFlow на карте сети
Docker images / publish-release (push) Successful in 11s
Docker images / backend-image (push) Successful in 2m14s
Docker images / frontend-image (push) Successful in 3m14s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 46s
Docker images / prepare-release (push) Successful in 10s
Скорость между узлами считается как в Трафике (5 мин, без overlay/mesh), отдельно от ёмкости WAN и BT.

Co-authored-by: Cursor <[email protected]>
2026-09-07 12:37:47 +07:00
DenozordecandCursor 3834c40aa8 fix(traffic): починить сборку tsc для частичных IPFIX-фикстур
Docker images / publish-release (push) Successful in 11s
Docker images / backend-image (push) Successful in 1m50s
Docker images / frontend-image (push) Successful in 3m15s
Docker images / updater-image (push) Successful in 47s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 6s
Тесты не должны попадать в Docker tsc; ingest принимает ParsedFlowInput и нормализует поля.

Co-authored-by: Cursor <[email protected]>
2026-09-07 11:45:22 +07:00
DenozordecandCursor 90c8c393e5 feat(traffic): разделить плоскости IPFIX и показать путь JH→EN
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Failing after 1m31s
Docker images / frontend-image (push) Successful in 2m36s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 42s
Docker images / prepare-release (push) Successful in 8s
Считать payload отдельно от overlay GRE/ESP и mesh; вкладка Пути и KPI Wire из счётчиков iface.

Co-authored-by: Cursor <[email protected]>
2026-09-07 11:39:14 +07:00
DenozordecandCursor cb799da13a fix(netflow): изолировать коллектор IPFIX и срезать раздувание базы
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 2m23s
Docker images / frontend-image (push) Successful in 3m16s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 57s
Docker images / prepare-release (push) Successful in 8s
Co-authored-by: Cursor <[email protected]>
2026-09-07 09:50:24 +07:00
DenozordecandCursor e0ddb17539 feat(traffic): классифицировать потоки по брендам и разгрузить ingest
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m48s
Docker images / frontend-image (push) Successful in 2m53s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 41s
Docker images / prepare-release (push) Successful in 12s
Починить ISO-страну Cloudflare, не держать holder как сервис, клик по срезу открывает Сессии. Не вызывать REST /interface с UDP, flush SQLite пачкой — uptime не должен ловить timeout 10s.

Co-authored-by: Cursor <[email protected]>
2026-09-07 03:01:14 +07:00
DenozordecandCursor 2820683cba feat(traffic): добавить карту и срезы IPFIX по странам и сессиям
Docker images / frontend-image (push) Successful in 3m23s
Docker images / publish-release (push) Successful in 14s
Docker images / backend-image (push) Successful in 2m6s
Docker images / updater-image (push) Successful in 49s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
Имена ifIndex пишутся по обоим ключам REST, дедуп 5-tuple по max байт, RIPEstat только из prefix-кэша, карта откуда-куда в Frame.

Co-authored-by: Cursor <[email protected]>
2026-09-07 02:13:21 +07:00
DenozordecandCursor 37167f78e3 feat(traffic): показать аналитику IPFIX по серверам и интерфейсам
Docker images / publish-release (push) Successful in 11s
Docker images / backend-image (push) Successful in 2m10s
Docker images / frontend-image (push) Successful in 2m53s
Docker images / updater-image (push) Successful in 48s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 12s
Резолвить ifIndex в имена RouterOS, дать вкладке Потоки ту же оболочку сервер/клиент/iface, что у обычного трафика, и обновлять срезы live без перезагрузки.

Co-authored-by: Cursor <[email protected]>
2026-09-07 01:21:12 +07:00
DenozordecandCursor cf68b59b3f fix(traffic): сбрасывать src-address Traffic Flow в авто
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 2m2s
Docker images / frontend-image (push) Successful in 3m11s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 47s
Docker images / prepare-release (push) Successful in 11s
Target с адресом wg-flow помечался invalid, IPFIX не уходил на коллектор.

Co-authored-by: Cursor <[email protected]>
2026-09-07 00:37:12 +07:00
DenozordecandCursor 5e512407e5 fix(traffic): записывать потоки при подмене UDP-источника в Docker
Docker images / publish-release (push) Successful in 11s
Docker images / backend-image (push) Successful in 2m11s
Docker images / frontend-image (push) Successful in 2m57s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 11s
Пакеты IPFIX доходили, но разговоры отбрасывались, если Docker подменял адрес jump-host на 172.x.

Co-authored-by: Cursor <[email protected]>
2026-09-07 00:02:50 +07:00
DenozordecandCursor 13889005f8 refactor(traffic): update traffic flow host file generation and configuration scripts
Docker images / frontend-image (push) Successful in 3m2s
Docker images / publish-release (push) Successful in 9s
Docker images / backend-image (push) Successful in 1m39s
Docker images / updater-image (push) Successful in 43s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 6s
- Renamed and refactored functions for better clarity, including `buildHostComposeSnippet` to `buildHostComposeOverride` and `buildHostNftSnippet` to `buildHostLinuxInstallSh`.
- Introduced a new script for Linux installation of WireGuard, enhancing the setup process for Docker hosts.
- Updated the `generateNativeConf` function to conditionally include the listen port in the configuration.
- Adjusted the `FlowOverlaySheet` component to default to the new Linux tab and improved the user interface for selecting jump-hosts.

Co-authored-by: Cursor <[email protected]>
2026-09-06 23:20:12 +07:00
DenozordecandCursor f0dc5acfd3 fix(traffic): сделать JH сервером WG и патчить traffic-flow по id
Docker images / publish-release (push) Successful in 9s
Docker images / backend-image (push) Successful in 1m43s
Docker images / frontend-image (push) Successful in 2m52s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 45s
Docker images / prepare-release (push) Successful in 11s
RouterOS не принимает PATCH без .id у singleton. Пир на jump-host без endpoint: клиент — хост MM с Endpoint JH:13232.

Co-authored-by: Cursor <[email protected]>
2026-09-06 22:58:31 +07:00
DenozordecandCursor 63bed28251 fix(ci): импортировать TrafficFlowHostFile в клиенте overlay
Docker images / publish-release (push) Successful in 16s
Docker images / backend-image (push) Successful in 56s
Docker images / frontend-image (push) Successful in 3m6s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 49s
Docker images / prepare-release (push) Successful in 11s
Co-authored-by: Cursor <[email protected]>
2026-09-06 22:24:44 +07:00
DenozordecandCursor 95dcd3df58 fix(traffic): спрашивать endpoint MM в sheet подключения JH
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Successful in 1m31s
Docker images / frontend-image (push) Failing after 2m38s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 46s
Docker images / prepare-release (push) Successful in 6s
Co-authored-by: Cursor <[email protected]>
2026-09-06 22:16:41 +07:00
DenozordecandCursor 1e9312acbd feat(traffic): добавить приём Traffic Flow с jump-host
Docker images / frontend-image (push) Successful in 3m56s
Docker images / publish-release (push) Successful in 12s
Docker images / backend-image (push) Successful in 2m1s
Docker images / updater-image (push) Successful in 46s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Чтобы видеть «кто с кем», а не только объём порта: IPFIX внутри WG на хосте Docker MM, REST-счётчики не трогаем.

Co-authored-by: Cursor <[email protected]>
2026-09-06 21:54:30 +07:00
Denozordec 5884bd8873 feat(traffic, users): enhance interface and peer management features
Docker images / publish-release (push) Successful in 10s
Docker images / backend-image (push) Successful in 1m37s
Docker images / frontend-image (push) Successful in 2m44s
Docker images / updater-image (push) Successful in 40s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 7s
Added support for managing peer information in interface bindings, including new fields for peerPublicKey and peerName in the BoundIfaceTraffic interface. Updated the database schema to include these fields in user_interface_bindings and traffic_samples tables. Enhanced the UI components to display peer details alongside interface names, improving user experience and clarity in the traffic management system. Updated relevant functions and services to handle peer-specific logic, ensuring robust integration across the application.
2026-09-06 20:45:37 +07:00
Denozordec fc161506e7 feat(users): enhance interface type mapping and parsing logic
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m30s
Docker images / frontend-image (push) Successful in 2m11s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 40s
Docker images / prepare-release (push) Successful in 6s
Updated the mapRosInterfaceType function to support additional interface types, including "gre-tunnel" and improved handling for various input formats. Enhanced the parseRawInterfaces function to accommodate new interface types in the test cases, ensuring accurate parsing and type assignment. Added corresponding test cases to validate the new functionality and ensure robustness in interface type handling.
2026-09-06 19:35:01 +07:00
Denozordec b3e50a1f5f feat(users): implement user management features and database schema
Docker images / publish-release (push) Successful in 11s
Docker images / backend-image (push) Successful in 1m57s
Docker images / frontend-image (push) Successful in 4m8s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 44s
Docker images / prepare-release (push) Successful in 10s
Added user management functionality, including the creation of app_users and user_interface_bindings tables in the database. Implemented API routes for user data retrieval and permissions handling. Enhanced the traffic monitoring system to include user traffic statistics and interface bindings. Updated relevant components and services to support the new user features, improving overall application functionality and user experience.
2026-09-06 19:20:05 +07:00
Denozordec fe32c9313a feat(ui): integrate KpiStatGrid for enhanced statistics display
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m43s
Docker images / frontend-image (push) Successful in 2m58s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 39s
Docker images / prepare-release (push) Successful in 9s
Replaced existing KPI display implementations across multiple pages with the new KpiStatGrid component for a more consistent and visually appealing presentation of statistics. Updated the Backups, BGP, Certificates, Communities, Containers, Dashboard, and Data Collection pages to utilize the KpiStatGrid, improving the overall user experience and maintainability of the codebase. Additionally, added new dependencies in package.json for required libraries.
2026-09-06 17:58:05 +07:00
Denozordec 6123660346 feat(terminal, wireguard): enhance UI components and functionality
Docker images / publish-release (push) Successful in 10s
Docker images / backend-image (push) Successful in 1m36s
Docker images / frontend-image (push) Successful in 2m45s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 43s
Docker images / prepare-release (push) Successful in 7s
Updated the TerminalPage to include a new ServerTileRail for server selection and added QuickCmds for quick command execution. Enhanced the WireGuardPage with new tabs for interface and peer management, improved server selection handling, and added support for displaying peer details. Refactored data grids to support compact server views and improved empty state handling. Introduced new hooks for managing server states and improved user experience across components.
2026-09-06 01:23:40 +07:00
Denozordec b680f882cc feat(ui): enhance CodeExportSheet and WgExportSheet components
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m40s
Docker images / frontend-image (push) Successful in 2m47s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 38s
Docker images / prepare-release (push) Successful in 9s
Updated CodeExportSheet to improve UI elements, including the addition of a Badge component for live status indication and refined layout adjustments. Modified WgExportSheet to simplify format labels and incorporate live content fetching capabilities. Adjusted styles for better visual consistency and user experience.
2026-09-06 00:23:51 +07:00
DenozordecandCursor 5e0c16e808 fix(ui): выровнять confirms и токены под ReUI PRO
Docker images / publish-release (push) Successful in 10s
Docker images / backend-image (push) Successful in 1m31s
Docker images / frontend-image (push) Successful in 2m11s
Docker images / updater-image (push) Successful in 40s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Добавить ui-design-contract, AlertDialog вместо hand-roll, CodeExportSheet для preview фильтров, ReUI Badge и gap вместо space-y.

Co-authored-by: Cursor <[email protected]>
2026-09-06 00:12:57 +07:00
DenozordecandCursor 66509b26bd fix(ui): выровнять экспорт WireGuard под ReUI Frame
Убрать дубль копирования, вынести CodeExportSheet и семантические токены.
Открывать вкладку Peer при экспорте пира. Мигрировать VXLAN/Firewall/Containers/GRE.

Co-authored-by: Cursor <[email protected]>
2026-09-06 00:12:50 +07:00
Denozordec 15ad53af1f feat(wireguard): implement WireGuard interface management and permissions
Docker images / frontend-image (push) Successful in 2m56s
Docker images / publish-release (push) Successful in 10s
Docker images / backend-image (push) Successful in 1m39s
Docker images / updater-image (push) Successful in 53s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 8s
Added comprehensive support for managing WireGuard interfaces, including CRUD operations and peer management. Updated permissions to include access control for WireGuard routes. Enhanced the UI components to display and interact with WireGuard configurations, improving user experience and functionality. Introduced new tests for WireGuard-related functionalities to ensure reliability.
2026-09-05 02:10:55 +07:00
Denozordec 883842636b refactor(api): streamline API requests with requestJson and requestBlob functions
Docker images / publish-release (push) Successful in 7s
Docker images / backend-image (push) Successful in 1m24s
Docker images / frontend-image (push) Successful in 2m6s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 43s
Docker images / prepare-release (push) Successful in 6s
Replaced direct fetch calls with requestJson and requestBlob utility functions across multiple components for improved consistency and error handling. This change enhances the maintainability of the codebase by centralizing API request logic and ensuring uniform handling of authentication and response parsing.
2026-09-05 01:42:44 +07:00
Denozordec b9f430de16 refactor(acme-cloudflare): enhance upsertARecord and syncCertificateDomainRecords functions
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m32s
Docker images / frontend-image (push) Successful in 2m21s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 41s
Docker images / prepare-release (push) Successful in 8s
Updated the upsertARecord function to return status messages ("updated", "created", "skipped_cname") instead of void, improving clarity on record handling. Modified syncCertificateDomainRecords to collect and return skipped CNAME records, enhancing error handling and feedback during DNS operations.
2026-09-05 01:29:58 +07:00
Denozordec 25e040a5dd fix(settings): improve error handling and success notifications in EvoBGP settings
Docker images / frontend-image (push) Successful in 2m26s
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m26s
Docker images / updater-image (push) Successful in 41s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 7s
Enhanced the error handling in the settings page by introducing a dedicated error message function. Added success and error toast notifications for better user feedback during settings save operations. Updated API key normalization to ensure consistent handling across the application.
2026-09-05 00:07:58 +07:00
Denozordec f2df990746 chore(docker): enhance Dockerfile for backend to manage nested dependencies
Docker images / publish-release (push) Successful in 9s
Docker images / backend-image (push) Successful in 1m23s
Docker images / frontend-image (push) Successful in 2m12s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 42s
Docker images / prepare-release (push) Successful in 5s
Updated the Dockerfile to create a directory for nested workspace dependencies and adjusted the copy commands to ensure proper handling of node_modules during the build process.
2026-09-04 23:48:03 +07:00
Denozordec 77bc174e43 chore(deps): update package-lock.json and backend dependencies, adjust Dockerfile for leaner backend image
Docker images / publish-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 1m46s
Docker images / frontend-image (push) Successful in 2m35s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 38s
Docker images / prepare-release (push) Successful in 7s
Removed unnecessary frontend dependencies from backend Docker image and updated package-lock.json to include new dev dependencies. Adjusted backend Dockerfile to streamline the build process and ensure proper workspace configuration.
2026-09-04 23:37:23 +07:00
DenozordecandCursor 0e9349e508 feat(auth): интегрировать SSO auth-portal
Docker images / frontend-image (push) Successful in 2m23s
Docker images / publish-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 2m52s
Docker images / updater-image (push) Successful in 38s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 5s
JWT на backend, handoff/callback на UI, RBAC mm:*, AUTH_* в compose.

Co-authored-by: Cursor <[email protected]>
2026-09-04 20:38:28 +07:00
DenozordecandCursor 0208aa4d7c chore(deploy): добавить Traefik-стеки и общий compose с CDNManager
Docker images / frontend-image (push) Successful in 2m1s
Docker images / publish-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 3m23s
Docker images / updater-image (push) Successful in 38s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 5s
Единый стек CDN+MM, вариант рядом с CDN Traefik и CLI-скрипт запуска.

Co-authored-by: Cursor <[email protected]>
2026-09-04 19:07:49 +07:00
Denozordec 42a2e18047 docs: обновить раздел о прод-развёртывании Docker и изменить ссылки на репозиторий
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Failing after 1m30s
Docker images / frontend-image (push) Successful in 3m45s
Docker images / updater-image (push) Successful in 50s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 23s
2026-09-04 18:54:26 +07:00
DenozordecandCursor 9df3971f6c chore(reui): обновить ReUI agent skill до актуальной версии
Docker images / publish-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 5m58s
Docker images / frontend-image (push) Successful in 2m55s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 53s
Docker images / prepare-release (push) Successful in 4s
Co-authored-by: Cursor <[email protected]>
2026-08-19 20:03:55 +07:00
DenozordecandCursor 7ed269cf2c ci: переключить registry на git.shx.one
Docker images / publish-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 4m25s
Docker images / frontend-image (push) Successful in 2m10s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 45s
Docker images / prepare-release (push) Successful in 6s
Обновить Gitea Actions, compose, updater и ссылки на образы.

Co-authored-by: Cursor <[email protected]>
2026-08-17 16:43:40 +07:00
Denozordec 7f81c304ae fix(config): добавить ключ лицензии REUI в настройки локальной конфигурации
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Failing after 2m40s
Docker images / frontend-image (push) Successful in 1m49s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 42s
Docker images / prepare-release (push) Successful in 5s
2026-08-17 15:42:10 +07:00
DenozordecandCursor 5f29cfaf0f fix(ui): выровнять chrome Frame и убрать Card-оболочки
Docker images / publish-release (push) Skipped
Docker images / backend-image (push) Failing after 2m36s
Docker images / frontend-image (push) Successful in 2m22s
Docker images / updater-image (push) Successful in 55s
Docker images / notify-webhook (push) Skipped
Docker images / prepare-release (push) Successful in 4s
Подключить App Switcher, NavUser, OpsPanel и AlertDialog вместо Card-shell.

Co-authored-by: Cursor <[email protected]>
2026-08-17 15:36:56 +07:00
Denozordec d2de8d3188 fix(ui): улучшить функциональность панели данных и заменить устаревшие компоненты
Docker images / backend-image (push) Successful in 2m1s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 8s
Docker images / prepare-release (push) Successful in 7s
Docker images / frontend-image (push) Successful in 2m7s
Docker images / updater-image (push) Successful in 41s
2026-06-30 22:36:00 +07:00
Denozordec d3a2d38b37 fix(ui): заменить таблицы на карточки данных и улучшить функциональность поиска
Docker images / backend-image (push) Successful in 1m37s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m50s
Docker images / updater-image (push) Successful in 44s
2026-06-30 22:22:51 +07:00
Denozordec a1a9124f3d fix(config): добавить новые зависимости и обновить конфигурацию компонентов
Docker images / backend-image (push) Successful in 2m37s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 8s
Docker images / prepare-release (push) Successful in 5s
Docker images / frontend-image (push) Successful in 2m22s
Docker images / updater-image (push) Successful in 38s
2026-06-30 21:30:40 +07:00
Denozordec 009011a917 chore(docs): обновить инструкции по локальному запуску проекта
Docker images / backend-image (push) Successful in 3m42s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 8s
Docker images / frontend-image (push) Successful in 2m40s
Docker images / prepare-release (push) Successful in 7s
Docker images / updater-image (push) Successful in 53s
2026-06-30 20:07:20 +07:00
Denozordec 399871f4f9 fix(config): улучшить обработку URL для бэкенда и увеличить таймаут запросов
Docker images / backend-image (push) Successful in 1m36s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 8s
Docker images / frontend-image (push) Successful in 1m39s
Docker images / prepare-release (push) Successful in 6s
Docker images / updater-image (push) Successful in 50s
2026-05-12 23:19:51 +07:00
Denozordec 158fc36294 fix(api): увеличить лимит размера тела запроса и улучшить обработку URL для API
Docker images / backend-image (push) Successful in 1m36s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m40s
Docker images / prepare-release (push) Successful in 6s
Docker images / updater-image (push) Successful in 38s
2026-05-12 23:11:23 +07:00
Denozordec 7dc4836c71 fix(dashboard): улучшить логику загрузки данных на странице панели управления
Docker images / backend-image (push) Successful in 1m43s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m47s
Docker images / updater-image (push) Successful in 54s
2026-05-12 22:47:30 +07:00
Denozordec efc3812e12 fix(updater): обновить URL для проверки состояния сервиса 2026-05-12 22:46:16 +07:00
Denozordec 8d5fd84962 fix(data-collection): добавить возможность отключения переключателей и улучшить обработку настроек
Docker images / backend-image (push) Successful in 1m31s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m41s
Docker images / updater-image (push) Successful in 37s
2026-05-12 22:37:55 +07:00
Denozordec f69e65b014 fix(docker): добавить переменную окружения для внутреннего URL бэкенда
Docker images / backend-image (push) Successful in 1m34s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m38s
Docker images / updater-image (push) Successful in 37s
2026-05-12 22:21:57 +07:00
Denozordec 49d14a00af fix(data-collection): улучшить обработку ошибок и загрузку данных с API
Docker images / backend-image (push) Successful in 1m32s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
Docker images / frontend-image (push) Successful in 1m41s
Docker images / prepare-release (push) Successful in 5s
Docker images / updater-image (push) Successful in 37s
2026-05-12 22:11:49 +07:00
Denozordec 9ab2418a5f feat(backup): добавить таблицу для резервных копий и функции для работы с ними
Docker images / backend-image (push) Successful in 1m35s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m42s
Docker images / updater-image (push) Successful in 37s
2026-05-12 21:56:13 +07:00
Denozordec 63aa9d424b chore(backups): удалить устаревшие резервные копии Gateway
Docker images / backend-image (push) Successful in 1m32s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 7s
Docker images / frontend-image (push) Successful in 1m38s
Docker images / updater-image (push) Successful in 37s
2026-05-12 21:55:41 +07:00
Denozordec 43ab17b253 feat(scheduler): добавить расписание бэкапов и автообновление сертификатов
Docker images / backend-image (push) Successful in 2m13s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 8s
Docker images / frontend-image (push) Successful in 2m11s
Docker images / prepare-release (push) Successful in 6s
Docker images / updater-image (push) Successful in 50s
2026-05-12 21:46:11 +07:00
Denozordec 4b8cf83ee9 fix(backend): улучшить управление блокировками и очистку устаревших директорий
Docker images / backend-image (push) Successful in 1m34s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
Docker images / prepare-release (push) Successful in 5s
Docker images / frontend-image (push) Successful in 1m41s
Docker images / updater-image (push) Successful in 45s
2026-05-12 21:28:08 +07:00
Denozordec cdaf3deb03 chore(release): обновить заметки о релизе и манифест для версии 1.3.4
Docker images / backend-image (push) Successful in 2m30s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 2m7s
Docker images / updater-image (push) Successful in 38s
2026-05-12 20:51:21 +07:00
Denozordec 3ce4179f3c chore(package): обновить зависимости и добавить поддержку swc для Windows 2026-05-12 20:48:45 +07:00
Denozordec 1a47905beb fix(backend): улучшить обработку сертификатов и добавить поддержку цепочек сертификатов 2026-05-12 20:43:34 +07:00
Denozordec b51d979a1c fix(backend): улучшить обработку хранилищ доверия для сертификатов 2026-05-12 20:40:56 +07:00
Denozordec bc01da1708 fix(backend): добавить функции для работы с IP-адресами и DNS-записями 2026-05-12 20:35:07 +07:00
Denozordec 500a543045 fix(backend): улучшить уникальность идентификаторов сертификатов 2026-05-12 20:29:57 +07:00
Denozordec 60024facab fix(backend): улучшить обработку сертификатов и добавить поддержку PUT запросов 2026-05-12 20:29:06 +07:00
Denozordec 53ea0e74a3 fix(ui): добавить новые компоненты для управления сертификатами 2026-05-12 20:11:22 +07:00
Denozordec 6c8ca17590 fix(ui): улучшить отображение таблицы сертификатов и добавить классы для сетки 2026-05-12 20:04:44 +07:00
Denozordec d62d212c86 chore(backend): добавить поддержку ACME и управление сертификатами 2026-05-12 20:00:25 +07:00
Denozordec bfceb07a23 fix(ui): убрать проверку статуса бэкенда из компонентов
Docker images / backend-image (push) Successful in 1m32s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 5s
Docker images / frontend-image (push) Successful in 1m39s
Docker images / updater-image (push) Successful in 37s
2026-05-12 18:58:38 +07:00
Denozordec e7a8ad0910 chore(rules): добавить локальный hook для проверки сообщений коммитов
Docker images / backend-image (push) Successful in 2m36s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 5s
Docker images / frontend-image (push) Successful in 2m25s
Docker images / updater-image (push) Successful in 37s
2026-05-12 18:18:30 +07:00
Denozordec 0435608ef9 chore: обновить правила коммитов и улучшить форматирование релизных заметок
Docker images / backend-image (push) Successful in 1m33s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m39s
Docker images / prepare-release (push) Successful in 5s
Docker images / updater-image (push) Successful in 37s
- Уточнены правила формирования сообщений коммитов, включая использование русского языка и уточнение типов изменений (fix/chore/feat).
- Добавлена функция для форматирования заголовков коммитов в компоненте модалки релизов, улучшая читаемость.
- Обновлены ссылки на правила коммитов в документации для большей ясности.
2026-05-12 18:06:29 +07:00
Denozordec c2a8a08618 feat: обновить название проекта и улучшить документацию
Docker images / backend-image (push) Successful in 1m32s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 5s
Docker images / frontend-image (push) Successful in 1m37s
Docker images / updater-image (push) Successful in 38s
- Изменено название проекта с "RouterLists" на "MikrotikManager" во всех соответствующих файлах и компонентах.
- Обновлены ссылки и комментарии в документации, чтобы отразить новое название и улучшить ясность.
- Внесены изменения в правила коммитов, чтобы обеспечить использование русского языка в сообщениях коммитов.
2026-05-12 17:53:53 +07:00
Denozordec 35876fcd6f feat: update Dockerfile and release management scripts
Docker images / backend-image (push) Successful in 1m45s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 1m37s
Docker images / prepare-release (push) Successful in 6s
Docker images / updater-image (push) Successful in 37s
- Replaced the public directory creation step with a direct copy in the Dockerfile for improved build efficiency.
- Enhanced the compute-release script to retrieve all tags and manage release manifests more effectively, including better handling of published timestamps and commit details.
- Updated the ReleasesPage component to conditionally format the published date based on the build type, improving user clarity on release status.
2026-05-12 17:39:50 +07:00
Denozordec 7207d1b749 feat: update release notes and manifest for v1.1.0
Docker images / backend-image (push) Successful in 2m34s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 6s
Docker images / prepare-release (push) Successful in 6s
Docker images / frontend-image (push) Successful in 2m8s
Docker images / updater-image (push) Successful in 37s
- Added release notes for version 1.1.0, highlighting enhancements and refactoring.
- Updated release manifest with new published timestamp and commit details for the latest release.
- Modified compute-release script to allow dynamic output directory configuration, improving flexibility in release management.
2026-05-12 17:30:25 +07:00
Denozordec 4b92a6eb81 feat: release v1.1.0 with enhancements and refactoring
Docker images / backend-image (push) Has been skipped
Docker images / publish-release (push) Has been skipped
Docker images / notify-webhook (push) Has been skipped
Docker images / prepare-release (push) Failing after 7s
Docker images / frontend-image (push) Has been skipped
Docker images / updater-image (push) Has been skipped
- Updated application version to 1.1.0, reflecting new features and improvements.
- Enhanced release management by allowing Docker images to be built even when no new commits are present since the last tag.
- Implemented new features including database backup and restore functionality, improved network configuration handling, and enhanced UI feedback with toast notifications.
- Refactored code to replace custom API fetch logic with a utility function for better maintainability.
- Streamlined Docker workflows and improved build efficiency through various configuration updates.
2026-05-12 17:28:13 +07:00
Denozordec 9ccc8459d7 feat: update application versioning and enhance release management
Docker images / backend-image (push) Has been skipped
Docker images / publish-release (push) Has been skipped
Docker images / notify-webhook (push) Has been skipped
Docker images / prepare-release (push) Successful in 38s
Docker images / frontend-image (push) Has been skipped
Docker images / updater-image (push) Has been skipped
- Bumped application version to 1.0.0 across all relevant package files, ensuring consistency in versioning.
- Introduced new environment variables for application version and release URL in Dockerfiles, improving deployment transparency.
- Enhanced the health check endpoint to return the current application version, providing better visibility for monitoring.
- Updated CI/CD workflows to include steps for preparing and publishing releases, streamlining the release process.
- Added a new "Releases" section in the application sidebar for easier access to version information.
2026-05-12 17:19:20 +07:00
Denozordec f282585b52 feat: enhance network configuration handling in updater script
Docker images / backend-image (push) Successful in 39s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 40s
Docker images / updater-image (push) Successful in 45s
- Improved network mode handling by introducing a variable for network names, allowing for better management of custom networks.
- Added support for network aliases, enabling the specification of multiple aliases for a given network in the snapshot.
- Enhanced health check logging to include the last HTTP status received, providing better visibility into health check failures.
2026-05-12 16:42:00 +07:00
Denozordec 40e2040fb0 feat: refactor theme management and remove next-themes dependency
Docker images / backend-image (push) Successful in 2m35s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 2m9s
Docker images / updater-image (push) Successful in 38s
- Replaced the next-themes package with a custom ThemeProvider component for enhanced theme management.
- Implemented local storage handling for theme persistence and system theme detection.
- Updated the Toaster component to utilize the new theme context.
- Removed unused next-themes references from package.json and package-lock.json.
2026-05-12 16:07:40 +07:00
Denozordec bb07ba0f69 feat: implement database backup and restore functionality in settings page
Docker images / backend-image (push) Successful in 1m39s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 1m51s
Docker images / updater-image (push) Successful in 36s
- Added UI components for downloading and restoring the application database, enhancing data management capabilities.
- Integrated new API routes for system database operations in the backend.
- Implemented state management for backup and restore processes, including loading indicators and user notifications.
2026-05-12 15:59:27 +07:00
Denozordec 1fefed2aa0 chore: enhance data source configuration and mock data handling
Docker images / backend-image (push) Successful in 39s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 1m38s
Docker images / updater-image (push) Successful in 35s
- Added support for mock data mode in the frontend by introducing a new environment variable in the Dockerfile and workflow configuration.
- Updated the settings page to conditionally display options based on the availability of mock data, improving user experience.
- Refactored data source logic to include a check for mock data availability, ensuring proper mode handling in the application.
2026-05-12 15:38:27 +07:00
Denozordec 4489dfb2d6 chore: enhance frontend configuration for backend integration
Docker images / backend-image (push) Successful in 39s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 1m36s
Docker images / updater-image (push) Successful in 37s
- Added environment variables for backend URLs in the Dockerfile to facilitate dynamic configuration.
- Updated Next.js configuration to include rewrites for health and API endpoints, improving backend connectivity.
- Modified settings page to handle backend URL locking and normalization, enhancing user experience and data integrity.
2026-05-12 15:28:20 +07:00
Denozordec 4b240d70d0 chore: add server and server_snapshots tables to database schema
Docker images / backend-image (push) Successful in 1m32s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 41s
Docker images / updater-image (push) Successful in 36s
- Introduced new tables for managing server configurations and snapshots, enhancing data organization and retrieval.
- The servers table includes fields for server details, while the server_snapshots table captures performance metrics and status updates.
2026-05-12 15:17:28 +07:00
Denozordec 63252ae85e chore: enhance image reference resolution in updater script
Docker images / backend-image (push) Successful in 38s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 40s
Docker images / updater-image (push) Successful in 45s
- Added functionality to resolve image references with digests, improving the rollback process in the updater script.
- Updated health check URLs in targets.json.example to use service names instead of localhost, ensuring proper connectivity in containerized environments.
2026-05-12 15:12:23 +07:00
Denozordec a359874a42 chore: enhance Dockerfile for backend to rebuild better-sqlite3
Docker images / backend-image (push) Successful in 2m32s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 42s
Docker images / updater-image (push) Successful in 36s
- Updated the backend Dockerfile to include a rebuild step for better-sqlite3 after npm ci, improving compatibility and performance.
- Removed redundant package installation commands to streamline the Docker build process.
2026-05-12 15:08:11 +07:00
Denozordec 4ec247f2c0 chore: update Docker Compose configuration for data volume management
Docker images / backend-image (push) Successful in 40s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 40s
Docker images / updater-image (push) Successful in 35s
- Changed backend data volume mapping to use a specific host path for data storage.
- Updated updater service volume mappings to point to designated host paths for state and configuration files, streamlining the deployment process.
2026-05-12 14:51:10 +07:00
Denozordec 2697a35c0a chore: streamline Docker workflow and enhance build efficiency
Docker images / backend-image (push) Successful in 40s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 39s
Docker images / updater-image (push) Successful in 36s
- Updated Dockerfiles for frontend and backend to include the --ignore-scripts flag in npm ci commands, further optimizing build times.
- Added a step in the Docker workflow to create the public directory in the staging environment, ensuring proper setup for deployments.
- Enhanced the configuration for dynamic image naming in the Docker workflow, improving consistency across builds.
2026-05-12 14:46:52 +07:00
Denozordec 7295545cde chore: add updater image configuration to Docker workflow
Docker images / backend-image (push) Successful in 39s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 40s
Docker images / updater-image (push) Successful in 49s
- Introduced a new job for building and pushing an updater Docker image, dynamically setting its name based on the repository structure.
- Updated existing image name configurations for frontend and backend to strip version numbers, ensuring cleaner image tags.
- Enhanced the Docker workflow with steps for checking out the repository, configuring Buildx, and logging into the Gitea registry.
2026-05-12 14:26:59 +07:00
Denozordec a5b5a2a3d3 chore: enhance Dockerfiles and package.json for improved build process
Docker images / backend-image (push) Successful in 3m5s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Successful in 2m13s
- Added ENV npm_config_ignore_scripts=true to frontend and backend Dockerfiles to skip scripts during npm install, optimizing build times.
- Introduced a postinstall script in package.json to ensure the build process for @mmapp/contracts runs after installation.
2026-05-12 14:04:01 +07:00
Denozordec 01596864dc chore: update Dockerfiles to ignore scripts during npm install
Docker images / backend-image (push) Failing after 1m33s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Failing after 55s
- Modified Dockerfile for frontend and backend to include the --ignore-scripts flag in npm ci commands, improving build efficiency.
- Added a step in the Docker workflow to create the public directory in the staging environment.
2026-05-12 14:00:05 +07:00
Denozordec 02119a3acd chore: configure dynamic image naming for frontend and backend in Docker workflow
Docker images / backend-image (push) Failing after 1m31s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Failing after 30s
- Added steps to dynamically set the image names for both frontend and backend based on the repository owner and name.
- Updated Docker build tags to use the newly configured image names instead of hardcoded values.
2026-05-12 13:56:56 +07:00
Denozordec c36994c36b chore: update configuration and dependencies
Docker images / backend-image (push) Failing after 2m56s
Docker images / notify-webhook (push) Has been skipped
Docker images / frontend-image (push) Failing after 32s
- Added .ci/docker/ to .gitignore to exclude Docker CI files.
- Set Next.js output configuration to "standalone" for optimized deployment.
- Updated package-lock.json to include new optional dependency for Windows SWC binaries.
2026-05-12 12:11:43 +07:00
DenozordecandCursor dfbaa859ad chore: isolate frontend and backend via npm workspaces and @mmapp/contracts
Co-authored-by: Cursor <[email protected]>
2026-05-12 10:59:05 +07:00
Denozordec b9a75b6831 feat: implement internet path functionality with backend support
Added internet path settings and snapshot management to the application. This includes new database tables for internet path settings and snapshots, API routes for fetching and managing internet path data, and integration into the dashboard and data collection pages. Enhanced the scheduler to support internet path jobs, ensuring regular data collection and updates. Updated relevant types and interfaces to accommodate the new functionality.
2026-05-08 00:40:41 +07:00
Denozordec 11ad94f67d feat: integrate sonner for toast notifications and enhance UI feedback
Added the sonner library for toast notifications across various components, improving user feedback for actions such as saving settings, syncing rules, and handling errors. Updated the layout to include a Toaster component for consistent notification display. Refactored alert messages in the backups, gre, and filters pages to utilize the new notification system, enhancing overall user experience.
2026-05-07 20:49:35 +07:00
Denozordec 84ecd4f061 feat: enhance backups page with live data loading and backup job management
Implemented live data fetching for servers and backups on the backups page, replacing static initial data. Added functionality for manual backup creation and job status tracking, including error handling and UI updates. Updated the network map layout to improve node prioritization and visual representation of server roles.

Also, registered new backups API routes in the backend for improved data handling.
2026-05-07 14:24:58 +07:00
Denozordec 6d8379501c refactor: replace custom API fetch logic with requestJson utility across multiple pages
Updated the API fetching mechanism in various components to utilize the new requestJson function for improved consistency and error handling. This change affects the alerts, dashboard, data collection, filters, gre, network map, probes, recursive routes, route optimizer, servers, settings, traffic, and uptime pages.
2026-05-07 13:35:41 +07:00
DenozordecandCursor 5f31bb47fb chore: synchronize pending app/backend updates and repository hygiene
Includes current frontend and backend work in progress and removes generated artifacts from tracking to keep the repository clean for дальнейшая разработка.

Co-authored-by: Cursor <[email protected]>
2026-05-07 12:29:04 +07:00
Denozordec bdb9b72fac Init 2 2026-05-03 11:16:07 +07:00
Denozordec ce00c4c671 Update .gitignore to include additional build and log files 2026-05-02 15:48:45 +07:00
Denozordec f3f831653f Init commit 2026-05-02 01:17:08 +07:00
1567 changed files with 10039 additions and 146265 deletions

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
---
name: reui
description: Use the ReUI registry from your AI agent - find, install, and correctly use ReUI components (the 22 free building blocks like data-grid, kanban, filters), their free examples, premium blocks, and Motion Icons. Applies in any project using ReUI, the @reui registry, REUI_LICENSE_KEY, or any shadcn project where the user asks for premium blocks, data grids, kanban boards, dashboards, or full pages. Pairs with the free ReUI MCP server for live, scored registry search and inline component APIs.
user-invocable: false
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
---
> **ReUI skill version `fe6742e29c`.** If the ReUI MCP's `get_agent_skill` reports a newer `version`, re-run the ReUI installer (see `get_agent_skill` -> `install.recommended`) to update this skill. Cloud/tools-only agents have no local file and always read the latest - they can ignore this.
# ReUI for Agents
ReUI is a shadcn-compatible registry. It ships four things you **reuse** - never redesign:
- **components** - the 22 ReUI building blocks with real APIs: `data-grid`, `kanban`, `filters`, `date-selector`, `tree`, `stepper`, ... (free)
- **examples** - free `c-*` single-pattern use-cases of a component (`c-kanban-1`); install one and read it to see exact composition
- **blocks** - premium full-page sections that compose components (`data-grid-2`, `pricing-page-1`); Pro or Ultimate license at install
- **icons** - Motion Icons in 4 styles, static + hover-animated variants; Ultimate license at install
The skill is free and this MCP is free to use; it just needs a ReUI account. On first use your agent opens a browser "Sign in with ReUI" prompt (a free account is created if you don't have one). Free covers components and examples with a daily request allowance; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the limit (see [rules/registry.md](./rules/registry.md)). The same account and skill work in every agent and service the MCP connects to - this skill is agent-agnostic.
Skill + MCP are a team: this skill is the workflow (how to find, install, read the API, and adapt by reuse); the MCP is the live data and the hands (search, get_component, install commands). Your job: find the right item, install it with the shadcn CLI, read its real API, and **adapt by reuse** - wire real data and theme it; do not hand-roll or restyle what ReUI already provides. This skill **layers on the shadcn skill**: follow that for generic rules (spacing, `cn()`, semantic colors, forms); follow this for everything ReUI-specific.
## The core loop (MCP-native)
1. **Find** - call the ReUI MCP `search` tool with the user's intent. It returns a ranked, scored list across components/examples/blocks/icons, each with an `install` command, `previewUrl`, `docsUrl`, and `componentsUsed`. Pass hints (`type`, `component`, `category`, `features`, `free`) when you can infer them.
2. **Install** - run the returned command non-interactively (`npx shadcn@latest add @reui/<name> --yes`). The CLI resolves deps, aliases, and the base/style from `components.json`. See [cli.md](./rules/cli.md).
3. **Read the API (on your base)** - first note your base from `components.json` -> `style` (`base-nova` -> Base UI, `radix-nova` -> Radix UI). For each component an item uses, call `get_component(name)` and read its **inline `api`** (no web fetch); then `get_examples(name)` to install a worked example and copy its composition - the installed files are already in your base. Whenever you work with a component's API, also **share its `docsUrl`** (the primitive's API documentation page) with the user so they have the full reference. See [components.md](./rules/components.md).
4. **Adapt (reuse-first)** - swap demo data for real data, fix icon imports, align tokens. Do not redesign. See [adapting.md](./rules/adapting.md).
**Always show the preview.** Every item a tool returns carries a `previewUrl` (a live preview page). Whenever you list, recommend, or present ReUI items to the user - blocks, components, examples, or icons, whether from `search`, `search_icons`, `list_components`, `compose_page`, or any getter - include each item's `previewUrl` so they can SEE it before installing. Blocks and examples open an individual live preview; icons and components link to their live category/component page. Never present an item without its preview link.
If the ReUI MCP is not configured, fall back to `npx shadcn@latest search @reui -q "..."` then `add` - but the MCP gives scored matches + inline APIs; prefer it.
## Commands
Run ReUI as explicit slash commands (via the ReUI MCP) **or** just ask in plain language - both run the same workflow.
| Command | Invoke | Does |
| ----------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| **build** | `/mcp__reui__build <what>` | Compose a page/section/feature from ReUI: plan → install → read API → adapt → craft → audit. |
| **add** | `/mcp__reui__add <item>` | Find & install one component/example/block/icon and wire it in. |
| **fix** | `/mcp__reui__fix [target]` | Diagnose & fix ReUI usage: wrong/undocumented props, base/radix mismatch, missing states, a11y/scroll. |
| **improve** | `/mcp__reui__improve [target]` | Refine + extend existing ReUI UI to a production-exceptional bar (hierarchy, density, states, responsive, motion). |
Invocation differs slightly per agent (`/mcp__reui__build` in Claude Code/Cursor/Windsurf, `/mcp.reui.build` in VS Code). No command surface? Just describe what you want - this skill drives the identical loop.
## When to reach for ReUI vs plain shadcn
| Need | Reach for |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| A full page or section (dashboard, billing, auth, pricing, settings) | `compose_page` first (plans sections + best blocks), then ReUI **blocks** |
| A data table with sorting/filtering/pagination/virtualization | the **data-grid** component (never hand-roll a `<table>`) |
| A drag-and-drop board | the **kanban** component |
| Advanced column filtering, date range, tree, stepper, ... | the matching ReUI **component** |
| A single generic control already in shadcn (Button, Dialog, Select) | plain **shadcn** |
## Detailed references
- [rules/registry.md](./rules/registry.md) - the four types, the @reui registry, base/radix, free vs premium + license
- [rules/workflow.md](./rules/workflow.md) - the find -> install -> read-API -> adapt loop (most important)
- [rules/components.md](./rules/components.md) - the 22 components, the data-grid contract, base vs radix
- [rules/adapting.md](./rules/adapting.md) - reuse-first: preserve the design (no over-customizing), reuse examples + a block's own elements, real data, don't invent APIs
- [rules/craft.md](./rules/craft.md) - make it exceptional: point of view, hierarchy, density, states, responsive, motion, the bar
- [rules/quality.md](./rules/quality.md) - security, accessibility, and scroll gates (the done gate)
- [rules/styling.md](./rules/styling.md) - ReUI extended tokens, theme adaptation, density
- [rules/icons.md](./rules/icons.md) - portable icons, swapping imports, Motion Icons (static + animated)
- [tools.md](./tools.md) - the ReUI MCP: golden path, the 19 tools, token rules, result shapes, errors
+43
View File
@@ -0,0 +1,43 @@
# Adapting installed ReUI code (reuse-first, no AI slop)
ReUI items ship production-quality. Your job is to **adapt by reuse** - wire real data and fit the app - not to redesign or hand-roll. The output should look like ReUI built it for this product.
## Preserve the design - don't over-customize
The design IS the product. A ReUI block/component encodes senior-designer decisions: spacing, hierarchy, density, color treatment, and component choices. The fastest way to turn a premium block back into generic AI slop is to "improve" its look - so don't.
- Change **data, copy, and props**; keep the **structure and styling** it ships with. Make the **smallest** change that wires the real data. If your diff touches `className` / JSX structure more than data / props, you are over-customizing - stop and reuse.
- Don't swap ReUI components for hand-rolled ones, restructure the layout, re-skin spacing / radius / colors, or add decorative chrome. Let the installed components carry the default spacing, radius, sizing, icon rhythm, density, and state styling; add custom Tailwind only when a component genuinely lacks a contract you need.
- Want a different look? `search` for a block whose design already fits and reuse that - don't restyle this one into a new design.
## Reuse the parts: examples and the block's own elements
- **Examples are building parts.** A free `c-*` example is a correct, single-pattern composition you can reuse. Before composing from scratch, `get_examples(component)`, install the closest one, and reuse its wiring - assemble UI from examples instead of hand-rolling what an example already shows.
- **Reuse a block's own elements.** Need more rows, cards, items, or sections than ship by default? Repeat the block's **existing** element by mapping real data through the same markup - never invent parallel markup that drifts from its design. Need a variant (empty / loading / expanded)? Derive it from an element the block already has.
## Don't invent (read, don't guess)
- Never write a prop, variant value, import path, or `@reui/...` name you didn't read in a component's inline `api`, an installed example, or a `search` result. If you didn't see it, treat it as nonexistent - call `get_component` / `get_examples` / `search` first, or run the MCP `validate_usage` tool to check planned names + props against the docs before writing code.
- If a getter returns `found: false` or `search` returns nothing, say so and fall back (plain shadcn, or ask) - never fabricate an install command or an API.
## What to change vs leave alone
- **Change:** the item's own data, copy, props, and layout to fit the app.
- **Leave alone:** installed component files, hooks, and the shared theme - do not edit vendored ReUI internals; change behavior through props and the documented API.
- Blocks are **portable React** - no `next/link`, `next/image`, or other framework-runtime imports inside them. Keep them portable.
## Demo data -> real data
- Replace every placeholder with the user's real data. Model it as **typed data structures** and **map over arrays** - never duplicate JSX per row/card. Keep small block-specific formatters next to the data.
- Wire the real source (columns, fields, fetch). For `data-grid`, implement the server fetch contract if the user needs server-side data.
- **Type from the component API, derive during render.** Type domain state through the component's own types - e.g. map status to `BadgeProps["variant"]` via a typed `Record<Status, …>` - instead of stringly-typed values. Compute view state during render; don't mirror derived data into `useState`/`useEffect`.
- **Adapt on the right base.** Use the API for the project's base (Base UI vs Radix - see [components.md](./components.md)); the installed files are already base-correct, so reuse their shape rather than translating from memory.
## Believable content (no AI tells)
- Use realistic labels, counts, timestamps, and statuses that map to a real workflow.
- No decorative buttons, fake tabs, meaningless toggles, equal-weight card walls, empty gradients, ornamental icons, or generic SaaS filler. Every element should do something.
## Operational surfaces (settings / profile / admin)
Pick ONE archetype and keep the family consistent: a vertical rail (3-6 sections), horizontal tabs (5-8), or a frame/stack. Prefer `frame` for tool-like surfaces, a card for profile-like ones. Don't mix archetypes in one surface.
+60
View File
@@ -0,0 +1,60 @@
# CLI: registry setup, license, non-interactive install
## Registry setup (one-time, per project)
Free items (the 22 components and all `c-*` examples) need only the plain string registry in `components.json`:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium items (blocks; Motion Icons and templates) require a ReUI license at install:
1. Add the key to `.env.local`:
```bash
REUI_LICENSE_KEY=your-license-key
```
2. Switch `components.json` to the authenticated object form:
```json
{
"registries": {
"@reui": {
"url": "https://reui.io/r/{style}/{name}.json",
"headers": { "Authorization": "Bearer ${REUI_LICENSE_KEY}" }
}
}
}
```
The shadcn CLI expands `${REUI_LICENSE_KEY}` from `.env.local` inside `components.json`, but an MCP client config never expands variables, so a ReUI MCP server config must carry the raw token instead (for example `reui_pat_your_token_here`).
The MCP `get_project_context` tool returns the right config. Full guide: https://reui.io/docs/registry?ref=skill
## Installing
Use the project's package runner (check `packageManager`):
```bash
npx shadcn@latest add @reui/<name> --yes # npm
pnpm dlx shadcn@latest add @reui/<name> --yes # pnpm
bunx --bun shadcn@latest add @reui/<name> --yes # bun
```
`--yes` skips confirmation prompts. The CLI auto-detects the package manager from the lockfile (there is no `--package-manager` flag). It also resolves the correct base+style variant from `components.json`, so do not pass a style.
## Handling prompts and conflicts
- **Always pass `--yes`** so the CLI does not block on confirmation prompts.
- **Do NOT pass `--overwrite` by default.** If the CLI reports an existing file, read the output and resolve deliberately: install under a different name, adjust the path, or ask the user. Only use `--overwrite` when the user explicitly wants to replace a file.
- **Preview first when touching an existing project**: `npx shadcn@latest add @reui/<name> --dry-run` shows what would change; `--diff <file>` shows a specific file's diff. Use these before overwriting.
- Run from the **project root** so `components.json` and `.env.local` are found.
## Free vs premium boundary
- Public, no key: `c-*` examples and the 22 components (`@reui/data-grid`, `@reui/badge`, ...) that those examples depend on.
- Key required at install: blocks (`@reui/<category>-N`) need a Pro or Ultimate license; Motion Icons (`@reui/icons/...`) and templates need Ultimate.
If an install 401/403s, the license key is missing, invalid, or the plan does not cover that resource (blocks: Pro or higher; icons and templates: Ultimate). Point the user to https://reui.io/account?ref=skill (their key) or https://reui.io/pricing?ref=skill (upgrade).
+428
View File
@@ -0,0 +1,428 @@
# ReUI components
The 22 ReUI building blocks: `alert`, `autocomplete`, `badge`, `cascader`, `code-block`, `data-grid`, `date-selector`, `event-calendar`, `filters`, `frame`, `gantt`, `icon-stack`, `icon-tile`, `kanban`, `number-field`, `phone-input`, `rating`, `scrollspy`, `sortable`, `stepper`, `timeline`, `tree`. Examples and blocks are composed from these.
**Rule one: never guess a component's API. Read it first.** Call **`get_component(name)`** for its inline `api` (props + usage, no web fetch), and **share the result's `docsUrl`** (the component's API documentation page) with the user whenever you work with that component's API, so they have the full reference (the `/llms.txt` index is a further fallback). Then call **`get_examples(name)`** to install a worked example and copy real composition. The contracts below are first-try orientation (required props, composition shape, the one gotcha); the inline `api` is the full reference. No single block fits? Compose: search the components you need, read each `get_component`, install a `get_examples` example per component, and adapt.
## data-grid (the flagship - read its API every time)
`data-grid` wraps TanStack Table v9. It is NOT a styled `<table>` and does NOT take `data`/`columns` props directly. The contract:
- Build a TanStack table instance with `useTable({ features: dataGridFeatures, ... })` (columns, data). `dataGridFeatures` is exported by the primitive and already bundles sorting, filtering, pagination, row selection, expanding, pinning, resizing and faceting, so there are no per-table row models to wire.
- Pass that instance to `<DataGrid table={table} recordCount={total}>`.
- Compose the body with `DataGridTable` inside `DataGrid`, and enable features through `tableLayout` (e.g. `{ headerSticky: true, columnsResizable: true }`), not ad-hoc classes.
- Server-side data uses the documented fetch shape (`recordCount` is the total for pagination).
```tsx
const table = useTable({
features: dataGridFeatures,
data,
columns,
})
<DataGrid table={table} recordCount={data.length}>
<DataGridTable />
</DataGrid>
```
Common mistakes:
- **Incorrect:** `<DataGrid data={rows} columns={cols} />` - these props do not exist. **Correct:** build a `useTable({ features: dataGridFeatures, ... })` instance and pass `table={table}` + `recordCount`.
- **Incorrect:** a raw `<table>` / hand-rolled pagination. **Correct:** use `data-grid`; read its API for sticky header, pagination, virtualization, row selection.
- **Incorrect:** styling rows/cells with arbitrary classes. **Correct:** drive layout via `tableLayout` and the primitive's `DataGridColumnMeta` (e.g. `cellClassName`, `headerTitle`), set through the bundle's `columnMeta` slot.
## event-calendar
**Required:** events via `events`/`onEventsChange` (controlled) or `defaultEvents` (uncontrolled), plus a height on the root.
**Shape:**
```tsx
<EventCalendar defaultEvents={events} defaultView="month" className="h-[560px]">
<EventCalendarNav />
<EventCalendarContent />
</EventCalendar>
```
**Gotcha:** headless-first: `EventCalendarContent` renders the active view (month/week/day/days/agenda; a resource view activates when `resources` is passed) - there is no per-view JSX to compose. Events are `{ id, title, start, end (exclusive), allDay?, color?, recurrence?, resourceId? }`. Mutations flow through `onEventUpdate`/`canDropEvent` (return `false` to reject); the root needs an explicit height because it is a min-h-0 flex column.
## gantt
**Required:** `resources` (the left tree) plus bars via `events`/`defaultEvents` attached by `resourceId`.
**Shape:**
```tsx
<Gantt defaultEvents={bars} resources={tasks} defaultScale="month" className="h-[480px]">
<GanttNav />
<GanttView />
</Gantt>
```
**Gotcha:** bars move along the time axis only (never across rows) and are all-day spans with exclusive `end`; `progress` is 0-100. Scales are `day | week | month | quarter | year`. Zoom control, infinite scroll, summary rollups, and row checkboxes are ON by default - turn off what you do not need. Same `onEventUpdate`/`canDropEvent` commit pipeline as `event-calendar`; the root needs an explicit height.
## kanban
**Required:** `value` (`Record<string, T[]>`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Kanban value={cols} onValueChange={setCols} getItemValue={(i) => i.id}>
<KanbanBoard>
{Object.entries(cols).map(([id, items]) => (
<KanbanColumn key={id} value={id}>
<KanbanColumnHandle><h3>{id}</h3></KanbanColumnHandle>
<KanbanColumnContent value={id}>
{items.map((i) => (
<KanbanItem key={i.id} value={i.id}>
<KanbanItemHandle>{i.title}</KanbanItemHandle>
</KanbanItem>
))}
</KanbanColumnContent>
</KanbanColumn>
))}
</KanbanBoard>
<KanbanOverlay><div className="bg-muted size-full rounded-md" /></KanbanOverlay>
</Kanban>
```
**Gotcha:** state is `Record<columnId, T[]>`. Each `KanbanColumnContent value` must match its parent `KanbanColumn value`. Omit `KanbanOverlay` and the drag preview silently breaks.
## sortable
**Required:** `value` (`T[]`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Sortable value={items} onValueChange={setItems} getItemValue={(i) => i.id}>
{items.map((i) => (
<SortableItem key={i.id} value={i.id}>
<SortableItemHandle><GripVertical /></SortableItemHandle>
{i.label}
</SortableItem>
))}
</Sortable>
```
**Gotcha:** a flat 1D reorder list (not columns - that is `kanban`). `getItemValue` must return a stable, unique string. Pass `layout="grid"` or `layout="nested"` for non-list layouts.
## filters
**Required:** `fields` (`FilterField[]`). The value is ONE `FilterQuery` tree - `query` + `onQueryChange`, or uncontrolled `defaultQuery`.
**Shape:**
```tsx
const fields: FilterField[] = [
{ id: "title", label: "Title", type: "text" },
{
id: "status",
label: "Status",
type: "select",
options: [
{ value: "active", label: "Active" },
{ value: "archived", label: "Archived" },
],
},
]
const [query, setQuery] = useState<FilterQuery>(() => createFilterQuery())
<Filters fields={fields} query={query} onQueryChange={setQuery} />
```
**Gotcha:** the state is a TREE, not a list of chips. `FilterQuery` is a group of rules joined by `and`/`or` and a group may hold another group, so `(A and B) or C` is expressible; a rule is `{ id, type: "rule", path: ["status"], operator, value }` and `path` is the whole nested attribute path, root first. The pre-rewrite API is GONE: there is no `filters`/`onChange` prop, no `FilterFieldConfig` (fields are `FilterField`, nested through their own `fields`, keyed `id` not `key`), and no `createFilter()` - it minted ids inside a pure function and broke hydration, so ids now come from `createFilterIdFactory(seed)` seeded off `useId`, and `createFilterQuery()` / `createFilterRule()` take one. Read the query back with `flattenFilterConditions` (`{ path, field, operator, values, negated }` per rule, incomplete rules skipped) and walk the tree yourself when the parentheses carry meaning - the primitive compiles nothing, no SQL, no query string.
`variant` picks the chrome over that one query: `"basic"`, the default, is the flat chip row for a toolbar over a table; `"advanced"` is the condition builder, hung off a trigger or rendered in place with `advancedMode="inline"`. Both read and write the same tree, so a saved view built in one opens in the other. Other props worth knowing before you hand-roll them: `size` is two rungs, `"sm" | "default"`, resolved per style (there is no `lg`); `reorderable` turns on drag and Alt+Arrow row moves in the builder; `onBeforeQueryChange` is the ONE veto point for every write (return `false` to refuse, it cannot rewrite); `editors` registers custom value editors a field selects by `editor` name; `labels` / `operatorLabels` own every rendered string; `pathCollapse` + `maxPathSegments` shorten deep attribute paths; `renderChip` / `renderValue` / `renderEmpty` replace rendered parts. On a field, `loadOptions` supplies async options with paging and `resolveValues` renders a chip restored from a saved view whose option was never loaded. Pairs naturally with `data-grid`.
## cascader
**Required:** `items` (a tree of `{ value, label, children? }`), plus the panel parts inside `CascaderContent`.
**Shape:**
```tsx
<Cascader items={items} value={value} onValueChange={setValue}>
<CascaderTrigger render={<Button variant="outline" />}>
<CascaderValue placeholder="Select an attribute" />
</CascaderTrigger>
<CascaderContent className="w-80">
<CascaderPanel>
<CascaderNav>
<CascaderBreadcrumb />
<CascaderInput />
</CascaderNav>
<CascaderEmpty />
<CascaderList maxHeight={288}>
<CascaderItems />
</CascaderList>
<CascaderStatus />
</CascaderPanel>
</CascaderContent>
</Cascader>
```
**Gotcha:** pressing a branch NAVIGATES, it does not select - only leaves are selectable until you pass `selectable="any"` or a predicate, and once a branch is selectable its chevron becomes the only way to open it. `CascaderInput` must stay inside `CascaderContent` (Base UI refills the query from the selection when the input sits outside the popup). Always include `CascaderStatus`: it is the live region announcing level changes, which the visual breadcrumb does not provide to screen readers. Accepts a flat adjacency list via `getParent` as well as nested `children`. `searchScope="deep"` searches every level and annotates results with their path; `multiple` gives checkbox rows; `inline` + a bare `CascaderPanel` embeds it with no popover.
The shape above is `mode="drill"`, the default. `mode="tree"` keeps the same parts (drop `CascaderBreadcrumb`, pass `showBack={false}`, drive expansion with `expanded`/`onExpandedChange`); `mode="columns"` REPLACES `CascaderList` + `CascaderItems` with a single `CascaderColumns`, and has no breadcrumb. Other props worth knowing before you hand-roll them: `cascade` (multi-select only, parent/child selection with indeterminate branches - pair it with `selectable="any"`, since a leaf-only tree can never cascade), `indicator={false}` to drop the single-select check and its gutter (visual only, no-op with `multiple`), `virtualize`/`virtualizeThreshold` plus `CascaderVirtualItems` for long levels, and `getChildren` for async levels with cursor paging, retry on failure and optional `prefetch`. `CascaderFooter` pins commands below the list (`actions` is the quick path) and `CascaderSubmenu` opens one as a side-anchored flyout with the full menu keyboard model. To head a run of rows use `CascaderGroup` wrapping a `CascaderLabel` - a bare label inside a listbox names nothing and is dropped from the accessibility tree - and `CascaderSeparator` for the rule between runs. Every rendered string comes from `labels`, and the panel is RTL-correct under a `DirectionProvider` or `dir="rtl"`.
## date-selector
**Required:** none, but wire `onChange` to capture the value.
**Shape:**
```tsx
const [value, setValue] = useState<DateSelectorValue | undefined>()
<DateSelector value={value} onChange={setValue} label="Due date" />
```
**Gotcha:** the value is a structured `DateSelectorValue` (period / operator / start+end dates), NOT a `Date` - never pass a raw `Date`. Use `allowRange={false}` to lock single-date picking. Read `get_component("date-selector")` for the value shape.
## tree
**Required:** `tree` (a `@headless-tree/core` instance you construct)
**Shape:**
```tsx
<Tree tree={tree}>
{tree.getItems().map((item) => (
<TreeItem key={item.getId()} item={item}>
<TreeItemLabel />
</TreeItem>
))}
</Tree>
```
**Gotcha:** `Tree` is a styled shell - it takes a headless-tree instance via `tree`, NOT `data`/`items` props. Build the instance with `@headless-tree/react`. External API: https://headless-tree.lukasbach.com/
## stepper
**Required:** `StepperItem step` (number), `StepperContent value` (number)
**Shape:**
```tsx
<Stepper defaultValue={1}>
<StepperNav>
<StepperItem step={1}>
<StepperTrigger><StepperIndicator>1</StepperIndicator></StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger><StepperIndicator>2</StepperIndicator></StepperTrigger>
</StepperItem>
</StepperNav>
<StepperPanel>
<StepperContent value={1}>Step 1 content</StepperContent>
<StepperContent value={2}>Step 2 content</StepperContent>
</StepperPanel>
</Stepper>
```
**Gotcha:** steps are 1-indexed. Without `StepperPanel` + `StepperContent` you render the nav trail but no body. Put `StepperSeparator` in every `StepperItem` except the last.
## timeline
**Required:** `TimelineItem step` (number)
**Shape:**
```tsx
<Timeline>
<TimelineItem step={1}>
<TimelineHeader>
<TimelineDate>March 2024</TimelineDate>
<TimelineTitle>Project initialized</TimelineTitle>
</TimelineHeader>
<TimelineIndicator />
<TimelineSeparator />
<TimelineContent>Repo and architecture set up.</TimelineContent>
</TimelineItem>
</Timeline>
```
**Gotcha:** each item needs a unique `step`. `orientation` is `"vertical"` (default) or `"horizontal"`. This is a static event display, not interactive like `stepper`.
## autocomplete
**Required:** `items` (array; each item has at least `value`)
**Shape:**
```tsx
<Autocomplete items={items}>
<AutocompleteInput placeholder="Search..." />
<AutocompleteContent>
<AutocompleteEmpty>No results found.</AutocompleteEmpty>
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.value} value={item}>{item.label}</AutocompleteItem>
)}
</AutocompleteList>
</AutocompleteContent>
</Autocomplete>
```
**Gotcha:** `AutocompleteList` takes a render-prop `(item) => ReactNode`, NOT a mapped array of children. External API: https://base-ui.com/react/components/autocomplete
## phone-input
**Required:** none, but wire `onChange`.
**Shape:**
```tsx
<PhoneInput placeholder="Enter phone number" defaultCountry="US" value={value} onChange={setValue} />
```
**Gotcha:** `value`/`onChange` use an E.164 string (e.g. `"+14155551234"`), not a display-formatted string; `onChange` can fire `undefined`. `defaultCountry` is a 2-letter ISO code. Wraps `react-phone-number-input`.
## number-field
**Required:** wrap the controls in `NumberFieldGroup`.
**Shape:**
```tsx
<NumberField defaultValue={0}>
<NumberFieldScrubArea label="Quantity" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
```
**Gotcha:** import from `@/components/ui/number-field`. The accessible label goes on `NumberFieldScrubArea`, not `NumberField`. External API: https://base-ui.com/react/components/number-field
## rating
**Required:** `rating` (number)
**Shape:**
```tsx
<Rating rating={4.5} showValue editable onRatingChange={setRating} />
```
**Gotcha:** supports decimals (partial stars). Pass `editable` + `onRatingChange` for interactive input; omit both for a read-only display.
## scrollspy
**Required:** `targetRef` (the scroll container ref)
**Shape:**
```tsx
<Scrollspy targetRef={containerRef}>
<a href="#s1" data-scrollspy-anchor="s1">Section 1</a>
<a href="#s2" data-scrollspy-anchor="s2">Section 2</a>
</Scrollspy>
<div ref={containerRef}>
<div id="s1">...</div>
<div id="s2">...</div>
</div>
```
**Gotcha:** each link's `data-scrollspy-anchor` must match a section `id`. `targetRef` is the scrollable container (defaults to the window).
## frame
**Required:** `Frame` > `FramePanel`
**Shape:**
```tsx
<Frame>
<FramePanel>
<FrameHeader>
<FrameTitle>Title</FrameTitle>
<FrameDescription>Description</FrameDescription>
</FrameHeader>
<div className="p-5">Content</div>
<FrameFooter>Footer</FrameFooter>
</FramePanel>
</Frame>
```
**Gotcha:** a structured card shell for tool-like surfaces. `stacked` connects multiple panels with shared borders; `dense` removes panel padding; radius via the `--frame-radius` CSS variable.
## icon-stack
**Required:** one child icon
**Shape:**
```tsx
<IconStack aria-hidden="true">
<InboxIcon className="size-4" />
</IconStack>
```
**Gotcha:** isometric layered artwork for empty states and illustrations; style the inner icon via its own `className`. Mark purely decorative stacks `aria-hidden="true"` and keep the real label in surrounding copy.
## icon-tile
**Required:** one child icon
**Shape:**
```tsx
<IconTile variant="elevated" size="lg">
<PackageIcon />
</IconTile>
```
**Gotcha:** the square container an icon sits in, so every list row, feature card and empty state shares one affordance. `variant`: `outline` (default) | `elevated` (muted fill, raised ring) | `soft` (tinted nested, tone from currentColor) | `solid` (filled tone, contrasting glyph) | `frame` (double container). `soft` and `solid` retint from one text color class (they default to `text-primary`). `size`: `xs | sm | default | lg | xl` (24/32/40/48/64px tile, glyph scales 12/14/16/20/24px). `radius`: `default | full`. Do not set a `size-*` class on the child icon unless you mean to override the tile's glyph size; recolor with `className` on the tile, not the icon.
## code-block
**Required:** `code` + `language`, or pre-highlighted `lines`.
**Shape:**
```tsx
<CodeBlock code={code} language="tsx" />
<CodeBlock code={code} language="tsx" showLineNumbers maxLines={20}>
<CodeBlockHeader>
<CodeBlockTitle>use-totals.ts</CodeBlockTitle>
<CodeBlockLanguage />
<CodeBlockCopyButton className="ml-auto" />
</CodeBlockHeader>
<CodeBlockExpandButton />
</CodeBlock>
```
**Gotcha:** the one-liner is already a complete block; every child (header, title, language label, copy button, wrap toggle, expand button, line actions) is optional chrome, at any depth. Shiki is the only npm dependency and loads lazily, one chunk per language, on first highlight; `lines` (from `highlightCode` in `code-block-highlight`, which is server-safe and has no `"use client"`) or `highlight={false}` loads nothing at all. `maxLines` caps the height AND marks the block collapsible, which is what makes `CodeBlockExpandButton` appear. Use `variant="ghost"` when the block sits inside a surface that already has a border.
## alert
**Required:** `Alert` > `AlertTitle`
**Shape:**
```tsx
<Alert variant="success">
<ShieldCheckIcon />
<AlertTitle>Security update</AlertTitle>
<AlertDescription>Enable two-factor authentication.</AlertDescription>
<AlertAction><Button size="xs">Update</Button></AlertAction>
</Alert>
```
**Gotcha:** shadcn-compatible API. `variant`: `default | destructive | info | success | warning | invert`. The non-default variants use ReUI extended color tokens (`--success`/`--info`/`--warning`/`--invert`), which the install adds. Defer generic alert rules to the shadcn skill.
## badge
**Required:** none (text child).
**Shape:**
```tsx
<Badge variant="success-light" size="sm">Success</Badge>
<Badge variant="outline" radius="full">Pill</Badge>
```
**Gotcha:** shadcn-compatible. Rich `variant` set (solid, `-outline`, `-light` per color), `size` `xs..xl`, `radius` `default | full`. Like `alert`, the color variants rely on ReUI extended tokens. Prefer `Badge` variants over raw color classes for statuses.
## base vs radix - write for the project's base
ReUI ships every component in two builds: `base` (Base UI) and `radix` (Radix UI). The install command and name are identical, and the CLI installs the build matching the project. But you must write/adapt code against the **right base**, because their APIs differ.
**Detect the base first.** Read `components.json` -> `style` and take the segment before the first `-`:
- `"style": "base-nova"` -> **Base UI**
- `"style": "radix-nova"` -> **Radix UI**
**Then use that base's API.** The deltas mirror shadcn's base-vs-radix split:
- Slot/composition: Base UI `render={<… />}` vs Radix `asChild`.
- `Select`: Base UI takes `items`; Radix uses `<SelectItem>` children.
- `ToggleGroup`: Base UI `multiple` boolean vs Radix `type="single" | "multiple"`.
The safest path is to **read the installed files and `c-*` examples** - they're already in your base, so reuse their wiring instead of guessing. When `get_component`'s inline `api` or an example shows the other base's shape, translate it to your base (or `validate_usage` to confirm). Defer the generic base/radix mechanics to the shadcn skill.
+45
View File
@@ -0,0 +1,45 @@
# Craft: make ReUI UI exceptional, not generic
ReUI items ship senior-designer quality. Your adaptation has to hold that bar, so the result reads like a real product surface a team would keep - not a wireframe an AI generated. Use these alongside the reuse rules in [adapting.md](./adapting.md).
## Have a point of view
Pick an emotional register before you compose - calm, operational, premium, editorial, dense, energetic - and let layout, spacing, surface treatment, and icon behavior all reinforce it. One or two memorable decisions and restraint everywhere else beats ten generic ones. UI with no point of view reads as generated.
## Brutally clear hierarchy
One focal point per card or panel: the dominant metric or task first, its label second, supporting detail third. The first thing the eye lands on should be the right thing; secondary text must read as secondary. Borders, separators, and surfaces do real work to create 2-3 information bands - don't flatten everything to equal weight.
## Spacing rhythm and deliberate density
Gaps are a signal, not a default. Keep them intentional and consistent within a family (`gap-1`/`gap-2` for tight operational rows, larger gaps for section breaks), and smaller within a group than between groups. Match the surrounding ReUI density; don't pad an operational surface like a marketing page, and don't drift density mid-section. The composition should still feel authored in grayscale.
## Cover the real states (the usual miss)
A surface isn't done at the happy path. Compose, and wire:
- **Empty** - a purposeful empty state (short message + the primary action), never a blank panel.
- **Loading** - a **skeleton** that matches the real layout, not a centered spinner.
- **Error** - an inline, recoverable error with a retry, announced via `role="status"`/`aria-live`.
Derive these from an element the block already has (don't invent parallel markup), or `get_examples` for a state-specific example.
## Responsive by default
Mobile-first, not mobile-afterthought. In constrained rows/cards/sidebars, put `min-w-0` on the shrinking container and `truncate` long single-line labels; protect the primary label's width and let secondary content compress. Reflow layouts (multi-column -> single column) rather than just shrinking them. Desktop and mobile should both look designed.
## Motion, subtly
Motion should clarify, not decorate. Use ReUI Motion Icons on primary actions for a subtle hover cue; keep transitions short (~200-300ms) with calm easing; prefer a skeleton pulse over a spinner. No bouncing, no gratuitous entrance animations on every element.
## Real, activated content
Use believable, typed data (realistic labels, counts, timestamps, statuses that map to a real workflow) - never lorem or abstract filler. Every visible control does something: no decorative buttons, fake tabs, meaningless toggles, or stats with no job. It must still hold with long names, empty values, and crowded data.
## Avoid the AI tells
These instantly read as generated - don't ship them: equal-weight card walls, empty gradients, repetitive padding everywhere, generic enterprise copy, ornamental icons, and number tiles that don't earn their place.
## The bar
Before you finish, ask: **would a product team keep this instead of replacing it? Does it still feel strong after swapping in real content?** If not, reuse the shipped ReUI design harder - don't restyle it into something new - then run the [quality.md](./quality.md) gates.
+39
View File
@@ -0,0 +1,39 @@
# Icons (ReUI delta over shadcn)
Follow the shadcn icon rules (use the project's configured `iconLibrary`, `data-icon` on icons inside `Button`, no sizing classes on icons inside components, pass icons as component objects not string keys). ReUI adds the following.
## Portable icons (library-agnostic)
ReUI components, examples, and blocks are authored to be icon-library-agnostic. When `iconLibrary` is set in `components.json`, the shadcn CLI installs each item's icons in **your** library automatically - you swap nothing. If an installed item's icons don't match your project (for example `iconLibrary` isn't set, so they came in from the item's demo library), change the **import source and component name** to your library, keeping the same icon-name semantics:
- `lucide` -> `lucide-react`
- `tabler` -> `@tabler/icons-react`
- `phosphor` -> `@phosphor-icons/react`
- `remix` -> `@remixicon/react`
- `hugeicons` -> `@hugeicons/react`
Don't assume `lucide-react`; read `iconLibrary` from `components.json`.
## Keep icons purposeful
Icons support the hierarchy, they don't replace it: keep them small, matched to the surrounding density, and decorative ones `aria-hidden="true"` (an icon-only control still needs an accessible label on the control). Don't add ornamental icons that do no job.
## Motion Icons (the `@reui/icons/...` set)
ReUI ships its own icon set in 4 styles (outline, solid, duotone, filled), each icon in two variants:
```bash
npx shadcn@latest add @reui/icons/default/<style>/<name> --yes # static
npx shadcn@latest add @reui/icons/animated/<style>/<name> --yes # hover-animated (motion/react)
```
Finding them via the MCP is free; installing requires an Ultimate license (`REUI_LICENSE_KEY`, see [cli.md](./cli.md)). Reach for a Motion Icon on a primary action when a subtle hover cue helps; keep motion restrained.
Finding icons:
- Several icons (the common case): **`search_icons(concepts[])`** - up to 24 concepts in one call, the best icons per concept with install commands. Pass `animated: true` to get only icons with a hover-animated Motion variant.
- One icon: `search` with `type: "icon"`.
- Icon results and `get_icon` carry `animated: true` and `installAnimated` when an animated variant exists - use those install strings, do not construct paths by hand.
- Every icon result carries a `previewUrl` (its live icon-category page) - **share it with the user** so they can SEE the icon before installing.
The `icon-stack` component composes multiple icons into a stacked display.
+22
View File
@@ -0,0 +1,22 @@
# Quality gates (security, accessibility, scroll)
These are the **done gate**, not a nice-to-have: before you call any ReUI work finished, call the MCP `get_audit_checklist` tool and pass every item below (plus the craft bar in [craft.md](./craft.md)). Then typecheck and lint.
## Security
- Never `dangerouslySetInnerHTML`. Render data as text/components.
- External links (`target="_blank"`) must always pair `rel="noopener noreferrer"`.
- No real PII, secrets, or tokens in demo or committed code. Remote media only from sources the project already allows.
## Accessibility
- Implicit list/card items that navigate get real anchors with a standard hover affordance.
- Icon-only or numeric buttons need an `aria-label`; decorative icons get `aria-hidden`.
- Every non-submit button is `type="button"`.
- Keyboard + focus: everything interactive is reachable in a sensible Tab order with a visible focus ring; layers (dialogs/sheets/menus) trap focus and close on `Escape`. ReUI components ship standard keyboard behavior - read each component's inline `api` rather than re-implementing it.
- Announce async UI: loading and error messages use `role="status"` / `aria-live` so they're not silent to screen readers.
## Scroll mechanics
- Make scroll regions with a parent-owned height: a `min-h-0` + flex chain down to the scroll container. Never guess a `max-h`.
- The scroll container owns `overflow-auto`; ancestors stay `min-h-0` so the height resolves.
+35
View File
@@ -0,0 +1,35 @@
# ReUI registry structure
ReUI is a shadcn-compatible registry with four entity types. **Examples and blocks are built FROM components** - reuse them, don't rebuild.
- **component** - one of the 22 ReUI building blocks with a real API (`data-grid`, `kanban`, `filters`, `date-selector`, `tree`, ...). Install directly (`@reui/data-grid`) or let it come in as a dependency of an example/block. Free. Read its API with `get_component(name)`.
- **example** - a free `c-*` single-pattern use-case of a component (`c-kanban-1`, `c-data-grid-3`). Install one and read it to copy real composition. Find a component's examples with `get_examples(name)`.
- **block** - a premium, full-page section that composes several components (`data-grid-2`, `pricing-page-1`). Pro or Ultimate license at install. Adapts to your active theme via semantic tokens.
- **icon** - Motion Icons in 4 styles (outline, solid, duotone, filled), static (`@reui/icons/default/<style>/<name>`) and hover-animated (`@reui/icons/animated/<style>/<name>`). Ultimate license at install. See [icons.md](./icons.md).
## The @reui registry
Install everything through the shadcn CLI: `npx shadcn@latest add @reui/<name> --yes`. The CLI reads the `@reui` registry from the project's `components.json`. Free items need only the plain string form:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium installs need the authenticated form + `REUI_LICENSE_KEY` in `.env.local` - see [cli.md](./cli.md). The MCP `get_project_context` tool returns the right config.
## Know your base: base or radix
ReUI ships every item in two builds - `base` (Base UI) and `radix` (Radix UI) - with mirrored names. The CLI installs the build matching your project automatically, but **you must write code against the right base's API**. Detect it from `components.json` -> `style`: the segment before the first `-` is the base (`base-nova` -> Base UI, `radix-nova` -> Radix UI). The installed files and `c-*` examples are already in your base - read them and adapt on that base. See [components.md](./components.md) for the API deltas.
**So the MCP's own `docsUrl` and `previewUrl` match your base**, send your `style` to the MCP: add `?style=<your components.json style>` to the ReUI MCP server URL (or set an `X-Reui-Style` header) in your MCP client config - set once, applies to every call. The MCP then resolves docs/preview links to YOUR library (`/docs/components/radix/...`, `/preview/radix/...` for a radix project) instead of the default base; `get_project_context` echoes back the style it currently sees so you can confirm it. Install commands are unaffected (the CLI already installs the right variant). If you notice the MCP returning `base` links for a `radix` project, tell the user to add `?style=` to the server URL.
Blocks adapt to your active theme through semantic tokens and CSS variables - change the theme and every block follows.
## Free vs premium
- **Free, no key:** the 22 components, all `c-*` examples, the ReUI MCP, and this skill.
- **Premium, license required at install:** blocks (Pro or Ultimate), Motion Icons and templates (Ultimate). Set `REUI_LICENSE_KEY` (see [cli.md](./cli.md)).
## Component API index
The canonical index of every component's API docs is **https://reui.io/llms.txt** (returned as `componentsApiUrl` in MCP results). Prefer the inline `api` from `get_component`; use the index/docs as the fallback.
+26
View File
@@ -0,0 +1,26 @@
# Styling (ReUI delta over shadcn)
Follow the shadcn skill for the generic rules (semantic colors not raw values, `gap-*` not `space-y-*`, `size-*`, `cn()`, no manual `dark:` overrides, no overlay `z-index`). This file is only the ReUI-specific additions.
## ReUI extended semantic tokens
ReUI adds semantic tokens beyond shadcn's base set. Use these instead of raw colors for status and emphasis:
- `--success` / `--success-foreground`
- `--info` / `--info-foreground`
- `--warning` / `--warning-foreground`
- `--destructive-foreground` (paired with shadcn's `--destructive`)
- `--invert` / `--invert-foreground` (inverted surfaces)
Use them as Tailwind utilities (`bg-success text-success-foreground`, `text-warning`, ...). They are defined in the project's global CSS and registered with Tailwind (`@theme inline` on v4). If a token is missing in the project, add it to the global CSS file (never a new file) following the same `name` / `name-foreground` convention, exactly as the shadcn customization rules describe.
**Incorrect:** `<span className="text-green-600">Active</span>`
**Correct:** `<Badge variant="success">Active</Badge>` or `<span className="text-success">Active</span>`
## Blocks follow your theme
When you install a block it adapts to your active theme through the semantic tokens above and the project's CSS variables. Don't hardcode style-specific values into installed block code and don't fork it to "restyle" - change the theme via the CSS variables / a preset and every block follows. Want a different look? `search` for a block whose design already fits instead of re-skinning one.
## Density and typography rhythm
ReUI operational UI usually feels dense, not airy. Keep the gap between a title and its supporting description tight by default (`gap-0.5`, `space-y-1`, or `space-y-px`), and smaller than the gap between sections. Match the surrounding ReUI density when you add rows or fields; do not pad operational surfaces like a marketing page.
+54
View File
@@ -0,0 +1,54 @@
# Workflow: find -> install -> read API -> adapt
The core ReUI loop. The MCP tells you what to install and gives you the API; the shadcn CLI installs it; you turn the installed files into correct, themed, data-wired code by **reuse**, not redesign.
## 1. Find (ReUI MCP `search` / `compose_page`)
**Full multi-section page ask?** Call `compose_page(intent, sections?)` FIRST, before searching block-by-block. It returns ordered sections, each with the best block for the intent (top pick + alternates); sections listed in `unavailableSections` have no real inventory - compose those from components, do not force a bad block.
For everything else, call `search` with the user's intent. Pass structured hints whenever you can infer them - you are an LLM, so do the parsing the server cannot:
- `type`: `"component"` (one of the 22 building blocks), `"example"` (a c-\* use-case), `"block"` (a full page/section), `"icon"`.
- `component`: the ReUI component the request implies (`"data-grid"`, `"kanban"`, ...).
- `category`, `features` (e.g. `["sortable","pagination"]`), `free`.
Example: "build a users management page with filters" -> `search({ query: "users management page with filters", type: "block", component: "data-grid", features: ["filters"] })`.
Each result has `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `termCoverage`, and `whyMatch`. `score` is relative to the top hit (the top is ~100 by construction), not an absolute quality - compare results to each other, and show the user the top options if several score closely; do not silently guess. A low `termCoverage` means a weak match even with a high score - rephrase or widen.
**Always show the preview link.** Whenever you list or recommend items - from `search`, `search_icons`, `list_components`, `compose_page`, or a getter - include each item's `previewUrl` (a live preview page) so the user can SEE it before you install. Blocks and examples link to an individual live preview; icons and components to their live category/component page. This applies to every listing, not only a single pick.
## 2. Install (shadcn CLI)
Run the result's `install` command from the project root, non-interactively:
```bash
npx shadcn@latest add @reui/<name> --yes
```
The CLI reads `components.json`, installs the correct base+style variant, resolves `registryDependencies` (a block pulls in its components), installs npm deps, and rewrites aliases. Do not pass the base/style. See [cli.md](./cli.md).
## 3. Read the API (do not guess props)
Before writing code against any component an item uses:
1. The item's `componentDigests` already give a 1-line contract per component - often enough to wire it. For the full API, call **`get_component(names)`** with ALL of `componentsUsed` in ONE call (it accepts an array) and read each inline `api` - no web fetch. **Share the component's `docsUrl`** (its API documentation page) with the user whenever you work with that component's API, so they have the full reference; the `/llms.txt` index is a further fallback.
2. Call **`get_examples(name)`** for the free `c-*` examples of that component; install one and **read the added files** to copy the exact composition. This is the fastest correct path - the example shows real wiring you adapt, not invent.
3. About to write a prop you did not see in an `api` or installed file? Run **`validate_usage`** BEFORE writing the code - per-prop documented / notDocumented verdicts plus did-you-mean suggestions. notDocumented means read the API, not push on.
## 4. Adapt (reuse-first) - do not skip
Installing files is not the end, and redesigning them defeats the point. First note the project's **base** so you write the right API - read `components.json` -> `style` and take the segment before the first `-` (`base-nova` -> Base UI, `radix-nova` -> Radix UI), see [components.md](./components.md). After `add`:
1. **Read the added files**; keep the composition intact. For a block, verify the components are wired correctly (for `data-grid`: a `useTable({ features: dataGridFeatures, ... })` instance passed as `table`, `recordCount` set - see [components.md](./components.md)).
2. **Replace demo data with the user's real data** via typed structures (see [adapting.md](./adapting.md)).
3. **Fix icon imports** to the project's icon library (see [icons.md](./icons.md)).
4. **Align styling** to semantic tokens and the active theme - no raw colors (see [styling.md](./styling.md)).
5. **Validate before finalizing**: if your adaptation introduced components or props you did not read in an `api` or example, run `validate_usage` on them.
6. **Hit the craft bar** - clear hierarchy, deliberate density, the empty / loading / error states, subtle motion, and mobile-first responsiveness (see [craft.md](./craft.md)). Generic-looking output means you under-reused the design, not that it needs restyling.
7. **Pass the quality gates** (security, a11y, scroll) - call the MCP `get_audit_checklist` tool and clear every item (see [quality.md](./quality.md)).
8. **Typecheck / lint**.
## If no single block fits
Compose from components (`compose_page` tells you which sections have no block inventory via `unavailableSections`). `search` the components you need, read each `get_component` API, install a worked `get_examples` example for each, and assemble by adapting those examples. A block in the same category is a useful reference - install it and read its files to see how ReUI composes those components, then adapt.
+58
View File
@@ -0,0 +1,58 @@
# ReUI MCP: full reference
The ReUI MCP (`https://mcp.reui.io`, Streamable HTTP) is free to use but needs a ReUI account: on first use the agent signs in with ReUI (a free account is created if the user has none), so every request is tied to an account. Free covers components and examples; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the daily request limit. It does **discovery + guidance** (search, inline APIs, page planning, validation) and never serves source; the shadcn CLI does **installation**, and the license key lives there (the `@reui` entry in `components.json`, backed by `.env.local`). Goal: from the user's intent to correct, themed, data-wired ReUI code in the **fewest tokens and calls**, with **no guessing**.
## Golden path (token-optimal - follow this order)
Most tasks need 2-4 MCP calls and ZERO web fetches:
1. **`search(query, ...hints)`** -> pick the top 1-3 results. Each result already carries `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `whyMatch`. The payload is complete - do not call another tool just to "confirm" a result.
2. **`get_component([...componentsUsed])`** in ONE batched call (one name or an array of up to 20) -> read each inline `api`. This **replaces** fetching docs pages. Often skippable: search responses carry `componentDigests`, a compact API contract per referenced component.
3. **`get_examples(component)`** -> install ONE returned `c-*` example, read the added files, copy the composition.
4. **`get_install_command(item)`** only to validate a name you are unsure of (results already include `install`). Run the install with the shadcn CLI (`--yes`).
5. **`get_audit_checklist()`** before declaring done.
If you already know the exact item name, skip `search`. Everything else is situational.
## The 5 task-specific tools (when to reach for each)
- **`compose_page`** - BEFORE building any full page (dashboard, settings, billing, landing). Pass the intent (and optionally the sections you want); it returns ordered sections, each with the best premium block for the intent (top pick + alternates). Sections with no real inventory are listed honestly in `unavailableSections` - compose those from components instead of forcing a bad block.
- **`search_icons`** - whenever you need icons, especially several. Batch up to 24 concepts in one call; each concept returns its best icons with install commands. Pass `animated: true` to get only icons that have a hover-animated Motion variant.
- **`validate_usage`** - BEFORE writing code with component names or props you have not read in an inline `api` or an installed example. It checks planned names + props against the indexed API docs and registry item names; returns did-you-mean suggestions and per-prop documented / notDocumented verdicts. Deterministic, no inference - a notDocumented prop means stop and read the API, not push on.
- **`whats_new`** - when your registry knowledge might be stale (a name 404s, the user mentions an item you don't know). Returns items added/removed per build, newest first.
- **`report_issue`** - when an installed item is actually broken (bad source, wrong dependency, broken preview). Goes straight to the ReUI team; rate-limited 5/hour. Not for usage questions.
## All 19 tools
`search`, `get_block`, `get_example`, `get_icon`, `list_block_groups`, `list_block_categories`, `list_example_categories`, `list_icon_categories`, `list_components`, `get_component`, `get_examples`, `search_icons`, `compose_page`, `validate_usage`, `whats_new`, `report_issue`, `get_install_command`, `get_project_context`, `get_audit_checklist`. The MCP serves the full parameter schemas; do not guess parameters beyond them.
## Token + speed rules
- **Batch `get_component`** - ONE call with the whole `componentsUsed` array, never N calls. Skip it entirely when `componentDigests` already answers the question.
- **Read source by installing** - the MCP serves no source. To read or analyze an item's real code, install it with the shadcn CLI and open the local files. Learn an API from the inline `api` / `componentDigests`, never by reading raw source.
- **Infer `search` hints yourself** (`type`, `component`, `category`, `features`, `free`) - hints shrink the result set and the tokens. Keep `limit` low; one right result beats ten.
- Run independent calls (and the shadcn install) concurrently in one turn - serial tool calls are the main source of slowness.
- Don't repeat a search for the same intent; don't call `list_*` to "see everything" - `search` is the entry point, `list_*` is only for browsing a taxonomy the user explicitly wants to explore.
- Prefer `get_component`'s inline `api` over `docsUrl` / `/llms.txt`. Fetch a web page only as a last resort.
## Result shapes (so you don't re-fetch)
- `score` is 0-100 RELATIVE to the top hit (the top is ~100 by construction), not absolute - compare results to each other.
- `termCoverage` (0-1) is the share of the query the item matched - low means a weak match even if the score looks high; rephrase or widen the search.
- Each result carries `whyMatch`, `install`, docs/preview URLs, and a `free` flag; premium items carry `requiredPlan` (`"pro"` for blocks, `"ultimate"` for icons).
- `componentDigests` is a top-level map: a compact API contract per referenced component - often enough to wire an item without a `get_component` call.
- Icon results and `get_icon` include `animated: true` and `installAnimated` when a hover-animated Motion variant exists (animated: `@reui/icons/animated/<style>/<name>`; static: `@reui/icons/default/<style>/<name>`).
## Error playbook
- **401** - the MCP requires a signed-in ReUI account. The client prompts "Sign in with ReUI" (OAuth) on first use; a free account is created if needed. For headless/CI, pass a personal token (`reui_pat_...`, created at https://reui.io/account/mcp?ref=skill) as `Authorization: Bearer`.
- **403 / locked result** - a valid account but the plan does not cover the item: premium blocks need Pro, Motion Icons need Ultimate. Point to https://reui.io/pricing?ref=skill (upgrade). Free accounts still get all components + examples.
- **429** - rate limited (120 requests/min per IP); back off, honor `Retry-After`.
- **not found** (`found: false`) - use the returned `suggestions`, or `search`; check `whats_new` if you suspect a stale name. Never run a fabricated install command.
## Fallbacks
- No ReUI MCP: `npx shadcn@latest search @reui -q "..."` then `add` (generic, no scoring / inline API).
- The shadcn project's own MCP also works over the `@reui` registry: https://ui.shadcn.com/docs/mcp.
Per-agent MCP setup: https://reui.io/docs/mcp?ref=skill
+1
View File
@@ -0,0 +1 @@
{"version":"0.0.1","configurations":[{"name":"MikrotikManager","runtimeExecutable":"npm","runtimeArgs":["run","dev"],"port":3000,"autoPort":true}]}
+70
View File
@@ -0,0 +1,70 @@
{
"permissions": {
"allow": [
"Bash(npm install *)",
"mcp__Claude_Preview__preview_start",
"Bash(npx tsc *)",
"Bash(npm run *)",
"Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:3000/dashboard)",
"Bash(curl -s -o /dev/null -w '%{http_code}' http://localhost:3000__TRACKED_VAR__)",
"Bash(curl -s -o /dev/null -w '%{http_code}' http://localhost:3001__TRACKED_VAR__)",
"PowerShell(netstat -ano | Select-String \":300\" | Where-Object { $_ -match \"LISTENING\" })",
"Bash(curl -s -o /dev/null -w '%{http_code}' http://localhost:__TRACKED_VAR__/dashboard)",
"Bash(curl -s -o /dev/null -w '%{http_code}' http://localhost:3333__TRACKED_VAR__)",
"Bash(curl -s -o /dev/null -w '%{http_code}' http://localhost:59959__TRACKED_VAR__)",
"WebFetch(domain:git.shx.one)",
"Bash(curl -s \"https://git.shx.one/denozord/router-lists-ui/raw/branch/v5/frontend/src/RouteOptimizerPage.jsx\")",
"Bash(curl -s \"https://git.shx.one/denozord/router-lists-ui/raw/branch/v5/frontend/src/OspfToolsPage.jsx\")",
"Bash(node -e ' *)",
"Bash(powershell -Command \"Get-Item 'C:\\\\Users\\\\shats\\\\.claude\\\\projects\\\\C--Users-shats-Dev-MikrotikManager-3\\\\b1dbd554-4665-40c0-bb5b-19d63bb494a0.jsonl'\")",
"Bash(node -e \"const { createRequire } = require\\('module'\\); const r = createRequire\\(__filename\\); const lucide = r\\('lucide-react'\\); ['SlackIcon','WebhookIcon'].forEach\\(n => console.log\\(n, !!lucide[n]\\)\\)\")",
"mcp__Claude_in_Chrome__tabs_context_mcp",
"Bash(npx eslint *)",
"Bash(grep -E \"^C:\\\\\\\\\\\\\\\\| [0-9]+:[0-9]+ error\")",
"Bash(grep -E \"\\(\\\\\\\\\\\\\\\\.*\\\\.tsx$\\)|\\(error\\)\")",
"Bash(python -c \"import sys,json; data=json.load\\(sys.stdin\\); [print\\(m['ruleId'], m['line'], m['message'][:60]\\) for f in data for m in f['messages'] if m['severity']==2]\")",
"Bash(python -c \"import sys,json; data=json.load\\(sys.stdin\\); [print\\(m['ruleId'], m['line'], m['message'][:80]\\) for f in data for m in f['messages'] if m['severity']==2]\")",
"Bash(npx next *)",
"Bash(dir \"C:\\\\Users\\\\shats\\\\Dev\\\\MikrotikManager-3\\\\app\\\\\\(main\\)\")",
"Bash(Get-ChildItem \"C:\\\\Users\\\\shats\\\\Dev\\\\MikrotikManager-3\\\\app\\\\\\(main\\)\" -Directory)",
"Bash(Select-Object Name)",
"Bash(Sort-Object Name)",
"Bash(awk 'NR>=508 && NR<=730' \"app/\\(main\\)/servers/page.tsx\")",
"Bash(grep -n \"Fragment\\\\|<>\\\\s*$\" \"app/\\(main\\)/filters/page.tsx\")",
"Bash(grep -n \"\\\\.map\\\\|Fragment\\\\|<>\\\\s*$\" \"app/\\(main\\)/ospf/page.tsx\")",
"Bash(grep -v \"//.*console\\\\|ErrorRecord\\\\|console\\\\.error.*err\\\\|any>\\\\|: any\\\\|as any\\\\|any\\\\[\\\\]\\\\|Record<.*any\\\\|Promise<any\")",
"Bash(curl -s http://localhost:8000/api/servers)",
"Bash(curl -s -X POST http://localhost:8000/api/servers/1/poll)",
"Bash(curl -s http://localhost:8000/api/servers/1)",
"Bash(curl -s http://localhost:8000/api/servers/1/snapshots)",
"Bash(curl -s -X DELETE http://localhost:8000/api/servers/1 -w \"\\\\nHTTP %{http_code}\")",
"Bash(curl -s http://localhost:8000/api/servers/999)",
"Bash(curl -s -X POST http://localhost:8000/api/servers -H \"Content-Type: application/json\" -d \"{}\")",
"Bash(curl -s -X POST http://localhost:8000/api/servers/test-connection -H 'Content-Type: application/json' -d '{\"host\":\"192.168.0.254\",\"port\":80,\"useSsl\":false,\"verifySsl\":false,\"apiPath\":\"/rest\",\"username\":\"admin\",\"password\":\"\"}')",
"Bash(curl -s -X POST http://localhost:8000/api/servers -H 'Content-Type: application/json' -d '{\"host\":\"192.168.0.254\",\"port\":80,\"useSsl\":false,\"verifySsl\":false,\"username\":\"admin\",\"password\":\"test123\",\"name\":\"RB5009 Home\",\"type\":\"home-router\",\"site\":\"MSK\",\"country\":\"RU\",\"asn\":\"\",\"comment\":\"Тестовый роутер\",\"enabled\":true}')",
"Bash(python3 -m json.tool)",
"Bash(curl -s -X POST http://localhost:8000/api/servers -H 'Content-Type: application/json' -d '{\"host\":\"192.168.0.254\",\"port\":80,\"useSsl\":false,\"verifySsl\":false,\"username\":\"admin\",\"password\":\"test123\",\"name\":\"RB5009 Home\",\"type\":\"home-router\",\"site\":\"MSK\",\"country\":\"RU\"}')",
"Bash(node -e \"const d=require\\('fs'\\).readFileSync\\('/dev/stdin','utf8'\\); const a=JSON.parse\\(d\\); a.forEach\\(s=>console.log\\(s.id, s.host, s.name\\)\\)\")",
"Bash(curl -s http://localhost:8000/api/bgp/sessions)",
"Bash(curl -s \"http://localhost:8000/api/bgp/sessions/raw/3\")",
"Bash(node -e \"0\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/routing/ospf/neighbor\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/routing/ospf/instance\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/routing/ospf/area\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/routing/ospf/interface-template\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/ip/route?.type=ospf\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/ip/route\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://192.168.0.254/rest/routing/ospf/neighbor\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://192.168.0.254/rest/routing/ospf/instance\")",
"Bash(curl -s http://localhost:8000/api/ospf/all)",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://192.168.0.254/rest/routing/bfd/session\")",
"Bash(curl -su \"admin:Ifwrbqifwrbq1995\" \"http://vpsville.msk.rt.shx.su/rest/routing/bfd/session\")",
"Bash(node -e \"const j=JSON.parse\\(require\\('fs'\\).readFileSync\\(0,'utf8'\\)\\); console.log\\('bfd:', JSON.stringify\\(j.bfdSessions, null, 2\\)\\)\")",
"Bash(xargs grep *)",
"Bash(npx shadcn@latest add popover --yes)"
]
},
"env": {
"REUI_LICENSE_KEY": "REUI-EAF4-EA40-6C5C-5D86"
}
}
+67
View File
@@ -0,0 +1,67 @@
---
name: reui
description: Use the ReUI registry from your AI agent - find, install, and correctly use ReUI components (the 22 free building blocks like data-grid, kanban, filters), their free examples, premium blocks, and Motion Icons. Applies in any project using ReUI, the @reui registry, REUI_LICENSE_KEY, or any shadcn project where the user asks for premium blocks, data grids, kanban boards, dashboards, or full pages. Pairs with the free ReUI MCP server for live, scored registry search and inline component APIs.
user-invocable: false
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
---
> **ReUI skill version `fe6742e29c`.** If the ReUI MCP's `get_agent_skill` reports a newer `version`, re-run the ReUI installer (see `get_agent_skill` -> `install.recommended`) to update this skill. Cloud/tools-only agents have no local file and always read the latest - they can ignore this.
# ReUI for Agents
ReUI is a shadcn-compatible registry. It ships four things you **reuse** - never redesign:
- **components** - the 22 ReUI building blocks with real APIs: `data-grid`, `kanban`, `filters`, `date-selector`, `tree`, `stepper`, ... (free)
- **examples** - free `c-*` single-pattern use-cases of a component (`c-kanban-1`); install one and read it to see exact composition
- **blocks** - premium full-page sections that compose components (`data-grid-2`, `pricing-page-1`); Pro or Ultimate license at install
- **icons** - Motion Icons in 4 styles, static + hover-animated variants; Ultimate license at install
The skill is free and this MCP is free to use; it just needs a ReUI account. On first use your agent opens a browser "Sign in with ReUI" prompt (a free account is created if you don't have one). Free covers components and examples with a daily request allowance; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the limit (see [rules/registry.md](./rules/registry.md)). The same account and skill work in every agent and service the MCP connects to - this skill is agent-agnostic.
Skill + MCP are a team: this skill is the workflow (how to find, install, read the API, and adapt by reuse); the MCP is the live data and the hands (search, get_component, install commands). Your job: find the right item, install it with the shadcn CLI, read its real API, and **adapt by reuse** - wire real data and theme it; do not hand-roll or restyle what ReUI already provides. This skill **layers on the shadcn skill**: follow that for generic rules (spacing, `cn()`, semantic colors, forms); follow this for everything ReUI-specific.
## The core loop (MCP-native)
1. **Find** - call the ReUI MCP `search` tool with the user's intent. It returns a ranked, scored list across components/examples/blocks/icons, each with an `install` command, `previewUrl`, `docsUrl`, and `componentsUsed`. Pass hints (`type`, `component`, `category`, `features`, `free`) when you can infer them.
2. **Install** - run the returned command non-interactively (`npx shadcn@latest add @reui/<name> --yes`). The CLI resolves deps, aliases, and the base/style from `components.json`. See [cli.md](./rules/cli.md).
3. **Read the API (on your base)** - first note your base from `components.json` -> `style` (`base-nova` -> Base UI, `radix-nova` -> Radix UI). For each component an item uses, call `get_component(name)` and read its **inline `api`** (no web fetch); then `get_examples(name)` to install a worked example and copy its composition - the installed files are already in your base. Whenever you work with a component's API, also **share its `docsUrl`** (the primitive's API documentation page) with the user so they have the full reference. See [components.md](./rules/components.md).
4. **Adapt (reuse-first)** - swap demo data for real data, fix icon imports, align tokens. Do not redesign. See [adapting.md](./rules/adapting.md).
**Always show the preview.** Every item a tool returns carries a `previewUrl` (a live preview page). Whenever you list, recommend, or present ReUI items to the user - blocks, components, examples, or icons, whether from `search`, `search_icons`, `list_components`, `compose_page`, or any getter - include each item's `previewUrl` so they can SEE it before installing. Blocks and examples open an individual live preview; icons and components link to their live category/component page. Never present an item without its preview link.
If the ReUI MCP is not configured, fall back to `npx shadcn@latest search @reui -q "..."` then `add` - but the MCP gives scored matches + inline APIs; prefer it.
## Commands
Run ReUI as explicit slash commands (via the ReUI MCP) **or** just ask in plain language - both run the same workflow.
| Command | Invoke | Does |
| ----------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| **build** | `/mcp__reui__build <what>` | Compose a page/section/feature from ReUI: plan → install → read API → adapt → craft → audit. |
| **add** | `/mcp__reui__add <item>` | Find & install one component/example/block/icon and wire it in. |
| **fix** | `/mcp__reui__fix [target]` | Diagnose & fix ReUI usage: wrong/undocumented props, base/radix mismatch, missing states, a11y/scroll. |
| **improve** | `/mcp__reui__improve [target]` | Refine + extend existing ReUI UI to a production-exceptional bar (hierarchy, density, states, responsive, motion). |
Invocation differs slightly per agent (`/mcp__reui__build` in Claude Code/Cursor/Windsurf, `/mcp.reui.build` in VS Code). No command surface? Just describe what you want - this skill drives the identical loop.
## When to reach for ReUI vs plain shadcn
| Need | Reach for |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| A full page or section (dashboard, billing, auth, pricing, settings) | `compose_page` first (plans sections + best blocks), then ReUI **blocks** |
| A data table with sorting/filtering/pagination/virtualization | the **data-grid** component (never hand-roll a `<table>`) |
| A drag-and-drop board | the **kanban** component |
| Advanced column filtering, date range, tree, stepper, ... | the matching ReUI **component** |
| A single generic control already in shadcn (Button, Dialog, Select) | plain **shadcn** |
## Detailed references
- [rules/registry.md](./rules/registry.md) - the four types, the @reui registry, base/radix, free vs premium + license
- [rules/workflow.md](./rules/workflow.md) - the find -> install -> read-API -> adapt loop (most important)
- [rules/components.md](./rules/components.md) - the 22 components, the data-grid contract, base vs radix
- [rules/adapting.md](./rules/adapting.md) - reuse-first: preserve the design (no over-customizing), reuse examples + a block's own elements, real data, don't invent APIs
- [rules/craft.md](./rules/craft.md) - make it exceptional: point of view, hierarchy, density, states, responsive, motion, the bar
- [rules/quality.md](./rules/quality.md) - security, accessibility, and scroll gates (the done gate)
- [rules/styling.md](./rules/styling.md) - ReUI extended tokens, theme adaptation, density
- [rules/icons.md](./rules/icons.md) - portable icons, swapping imports, Motion Icons (static + animated)
- [tools.md](./tools.md) - the ReUI MCP: golden path, the 19 tools, token rules, result shapes, errors
+43
View File
@@ -0,0 +1,43 @@
# Adapting installed ReUI code (reuse-first, no AI slop)
ReUI items ship production-quality. Your job is to **adapt by reuse** - wire real data and fit the app - not to redesign or hand-roll. The output should look like ReUI built it for this product.
## Preserve the design - don't over-customize
The design IS the product. A ReUI block/component encodes senior-designer decisions: spacing, hierarchy, density, color treatment, and component choices. The fastest way to turn a premium block back into generic AI slop is to "improve" its look - so don't.
- Change **data, copy, and props**; keep the **structure and styling** it ships with. Make the **smallest** change that wires the real data. If your diff touches `className` / JSX structure more than data / props, you are over-customizing - stop and reuse.
- Don't swap ReUI components for hand-rolled ones, restructure the layout, re-skin spacing / radius / colors, or add decorative chrome. Let the installed components carry the default spacing, radius, sizing, icon rhythm, density, and state styling; add custom Tailwind only when a component genuinely lacks a contract you need.
- Want a different look? `search` for a block whose design already fits and reuse that - don't restyle this one into a new design.
## Reuse the parts: examples and the block's own elements
- **Examples are building parts.** A free `c-*` example is a correct, single-pattern composition you can reuse. Before composing from scratch, `get_examples(component)`, install the closest one, and reuse its wiring - assemble UI from examples instead of hand-rolling what an example already shows.
- **Reuse a block's own elements.** Need more rows, cards, items, or sections than ship by default? Repeat the block's **existing** element by mapping real data through the same markup - never invent parallel markup that drifts from its design. Need a variant (empty / loading / expanded)? Derive it from an element the block already has.
## Don't invent (read, don't guess)
- Never write a prop, variant value, import path, or `@reui/...` name you didn't read in a component's inline `api`, an installed example, or a `search` result. If you didn't see it, treat it as nonexistent - call `get_component` / `get_examples` / `search` first, or run the MCP `validate_usage` tool to check planned names + props against the docs before writing code.
- If a getter returns `found: false` or `search` returns nothing, say so and fall back (plain shadcn, or ask) - never fabricate an install command or an API.
## What to change vs leave alone
- **Change:** the item's own data, copy, props, and layout to fit the app.
- **Leave alone:** installed component files, hooks, and the shared theme - do not edit vendored ReUI internals; change behavior through props and the documented API.
- Blocks are **portable React** - no `next/link`, `next/image`, or other framework-runtime imports inside them. Keep them portable.
## Demo data -> real data
- Replace every placeholder with the user's real data. Model it as **typed data structures** and **map over arrays** - never duplicate JSX per row/card. Keep small block-specific formatters next to the data.
- Wire the real source (columns, fields, fetch). For `data-grid`, implement the server fetch contract if the user needs server-side data.
- **Type from the component API, derive during render.** Type domain state through the component's own types - e.g. map status to `BadgeProps["variant"]` via a typed `Record<Status, …>` - instead of stringly-typed values. Compute view state during render; don't mirror derived data into `useState`/`useEffect`.
- **Adapt on the right base.** Use the API for the project's base (Base UI vs Radix - see [components.md](./components.md)); the installed files are already base-correct, so reuse their shape rather than translating from memory.
## Believable content (no AI tells)
- Use realistic labels, counts, timestamps, and statuses that map to a real workflow.
- No decorative buttons, fake tabs, meaningless toggles, equal-weight card walls, empty gradients, ornamental icons, or generic SaaS filler. Every element should do something.
## Operational surfaces (settings / profile / admin)
Pick ONE archetype and keep the family consistent: a vertical rail (3-6 sections), horizontal tabs (5-8), or a frame/stack. Prefer `frame` for tool-like surfaces, a card for profile-like ones. Don't mix archetypes in one surface.
+60
View File
@@ -0,0 +1,60 @@
# CLI: registry setup, license, non-interactive install
## Registry setup (one-time, per project)
Free items (the 22 components and all `c-*` examples) need only the plain string registry in `components.json`:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium items (blocks; Motion Icons and templates) require a ReUI license at install:
1. Add the key to `.env.local`:
```bash
REUI_LICENSE_KEY=your-license-key
```
2. Switch `components.json` to the authenticated object form:
```json
{
"registries": {
"@reui": {
"url": "https://reui.io/r/{style}/{name}.json",
"headers": { "Authorization": "Bearer ${REUI_LICENSE_KEY}" }
}
}
}
```
The shadcn CLI expands `${REUI_LICENSE_KEY}` from `.env.local` inside `components.json`, but an MCP client config never expands variables, so a ReUI MCP server config must carry the raw token instead (for example `reui_pat_your_token_here`).
The MCP `get_project_context` tool returns the right config. Full guide: https://reui.io/docs/registry?ref=skill
## Installing
Use the project's package runner (check `packageManager`):
```bash
npx shadcn@latest add @reui/<name> --yes # npm
pnpm dlx shadcn@latest add @reui/<name> --yes # pnpm
bunx --bun shadcn@latest add @reui/<name> --yes # bun
```
`--yes` skips confirmation prompts. The CLI auto-detects the package manager from the lockfile (there is no `--package-manager` flag). It also resolves the correct base+style variant from `components.json`, so do not pass a style.
## Handling prompts and conflicts
- **Always pass `--yes`** so the CLI does not block on confirmation prompts.
- **Do NOT pass `--overwrite` by default.** If the CLI reports an existing file, read the output and resolve deliberately: install under a different name, adjust the path, or ask the user. Only use `--overwrite` when the user explicitly wants to replace a file.
- **Preview first when touching an existing project**: `npx shadcn@latest add @reui/<name> --dry-run` shows what would change; `--diff <file>` shows a specific file's diff. Use these before overwriting.
- Run from the **project root** so `components.json` and `.env.local` are found.
## Free vs premium boundary
- Public, no key: `c-*` examples and the 22 components (`@reui/data-grid`, `@reui/badge`, ...) that those examples depend on.
- Key required at install: blocks (`@reui/<category>-N`) need a Pro or Ultimate license; Motion Icons (`@reui/icons/...`) and templates need Ultimate.
If an install 401/403s, the license key is missing, invalid, or the plan does not cover that resource (blocks: Pro or higher; icons and templates: Ultimate). Point the user to https://reui.io/account?ref=skill (their key) or https://reui.io/pricing?ref=skill (upgrade).
+428
View File
@@ -0,0 +1,428 @@
# ReUI components
The 22 ReUI building blocks: `alert`, `autocomplete`, `badge`, `cascader`, `code-block`, `data-grid`, `date-selector`, `event-calendar`, `filters`, `frame`, `gantt`, `icon-stack`, `icon-tile`, `kanban`, `number-field`, `phone-input`, `rating`, `scrollspy`, `sortable`, `stepper`, `timeline`, `tree`. Examples and blocks are composed from these.
**Rule one: never guess a component's API. Read it first.** Call **`get_component(name)`** for its inline `api` (props + usage, no web fetch), and **share the result's `docsUrl`** (the component's API documentation page) with the user whenever you work with that component's API, so they have the full reference (the `/llms.txt` index is a further fallback). Then call **`get_examples(name)`** to install a worked example and copy real composition. The contracts below are first-try orientation (required props, composition shape, the one gotcha); the inline `api` is the full reference. No single block fits? Compose: search the components you need, read each `get_component`, install a `get_examples` example per component, and adapt.
## data-grid (the flagship - read its API every time)
`data-grid` wraps TanStack Table v9. It is NOT a styled `<table>` and does NOT take `data`/`columns` props directly. The contract:
- Build a TanStack table instance with `useTable({ features: dataGridFeatures, ... })` (columns, data). `dataGridFeatures` is exported by the primitive and already bundles sorting, filtering, pagination, row selection, expanding, pinning, resizing and faceting, so there are no per-table row models to wire.
- Pass that instance to `<DataGrid table={table} recordCount={total}>`.
- Compose the body with `DataGridTable` inside `DataGrid`, and enable features through `tableLayout` (e.g. `{ headerSticky: true, columnsResizable: true }`), not ad-hoc classes.
- Server-side data uses the documented fetch shape (`recordCount` is the total for pagination).
```tsx
const table = useTable({
features: dataGridFeatures,
data,
columns,
})
<DataGrid table={table} recordCount={data.length}>
<DataGridTable />
</DataGrid>
```
Common mistakes:
- **Incorrect:** `<DataGrid data={rows} columns={cols} />` - these props do not exist. **Correct:** build a `useTable({ features: dataGridFeatures, ... })` instance and pass `table={table}` + `recordCount`.
- **Incorrect:** a raw `<table>` / hand-rolled pagination. **Correct:** use `data-grid`; read its API for sticky header, pagination, virtualization, row selection.
- **Incorrect:** styling rows/cells with arbitrary classes. **Correct:** drive layout via `tableLayout` and the primitive's `DataGridColumnMeta` (e.g. `cellClassName`, `headerTitle`), set through the bundle's `columnMeta` slot.
## event-calendar
**Required:** events via `events`/`onEventsChange` (controlled) or `defaultEvents` (uncontrolled), plus a height on the root.
**Shape:**
```tsx
<EventCalendar defaultEvents={events} defaultView="month" className="h-[560px]">
<EventCalendarNav />
<EventCalendarContent />
</EventCalendar>
```
**Gotcha:** headless-first: `EventCalendarContent` renders the active view (month/week/day/days/agenda; a resource view activates when `resources` is passed) - there is no per-view JSX to compose. Events are `{ id, title, start, end (exclusive), allDay?, color?, recurrence?, resourceId? }`. Mutations flow through `onEventUpdate`/`canDropEvent` (return `false` to reject); the root needs an explicit height because it is a min-h-0 flex column.
## gantt
**Required:** `resources` (the left tree) plus bars via `events`/`defaultEvents` attached by `resourceId`.
**Shape:**
```tsx
<Gantt defaultEvents={bars} resources={tasks} defaultScale="month" className="h-[480px]">
<GanttNav />
<GanttView />
</Gantt>
```
**Gotcha:** bars move along the time axis only (never across rows) and are all-day spans with exclusive `end`; `progress` is 0-100. Scales are `day | week | month | quarter | year`. Zoom control, infinite scroll, summary rollups, and row checkboxes are ON by default - turn off what you do not need. Same `onEventUpdate`/`canDropEvent` commit pipeline as `event-calendar`; the root needs an explicit height.
## kanban
**Required:** `value` (`Record<string, T[]>`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Kanban value={cols} onValueChange={setCols} getItemValue={(i) => i.id}>
<KanbanBoard>
{Object.entries(cols).map(([id, items]) => (
<KanbanColumn key={id} value={id}>
<KanbanColumnHandle><h3>{id}</h3></KanbanColumnHandle>
<KanbanColumnContent value={id}>
{items.map((i) => (
<KanbanItem key={i.id} value={i.id}>
<KanbanItemHandle>{i.title}</KanbanItemHandle>
</KanbanItem>
))}
</KanbanColumnContent>
</KanbanColumn>
))}
</KanbanBoard>
<KanbanOverlay><div className="bg-muted size-full rounded-md" /></KanbanOverlay>
</Kanban>
```
**Gotcha:** state is `Record<columnId, T[]>`. Each `KanbanColumnContent value` must match its parent `KanbanColumn value`. Omit `KanbanOverlay` and the drag preview silently breaks.
## sortable
**Required:** `value` (`T[]`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Sortable value={items} onValueChange={setItems} getItemValue={(i) => i.id}>
{items.map((i) => (
<SortableItem key={i.id} value={i.id}>
<SortableItemHandle><GripVertical /></SortableItemHandle>
{i.label}
</SortableItem>
))}
</Sortable>
```
**Gotcha:** a flat 1D reorder list (not columns - that is `kanban`). `getItemValue` must return a stable, unique string. Pass `layout="grid"` or `layout="nested"` for non-list layouts.
## filters
**Required:** `fields` (`FilterField[]`). The value is ONE `FilterQuery` tree - `query` + `onQueryChange`, or uncontrolled `defaultQuery`.
**Shape:**
```tsx
const fields: FilterField[] = [
{ id: "title", label: "Title", type: "text" },
{
id: "status",
label: "Status",
type: "select",
options: [
{ value: "active", label: "Active" },
{ value: "archived", label: "Archived" },
],
},
]
const [query, setQuery] = useState<FilterQuery>(() => createFilterQuery())
<Filters fields={fields} query={query} onQueryChange={setQuery} />
```
**Gotcha:** the state is a TREE, not a list of chips. `FilterQuery` is a group of rules joined by `and`/`or` and a group may hold another group, so `(A and B) or C` is expressible; a rule is `{ id, type: "rule", path: ["status"], operator, value }` and `path` is the whole nested attribute path, root first. The pre-rewrite API is GONE: there is no `filters`/`onChange` prop, no `FilterFieldConfig` (fields are `FilterField`, nested through their own `fields`, keyed `id` not `key`), and no `createFilter()` - it minted ids inside a pure function and broke hydration, so ids now come from `createFilterIdFactory(seed)` seeded off `useId`, and `createFilterQuery()` / `createFilterRule()` take one. Read the query back with `flattenFilterConditions` (`{ path, field, operator, values, negated }` per rule, incomplete rules skipped) and walk the tree yourself when the parentheses carry meaning - the primitive compiles nothing, no SQL, no query string.
`variant` picks the chrome over that one query: `"basic"`, the default, is the flat chip row for a toolbar over a table; `"advanced"` is the condition builder, hung off a trigger or rendered in place with `advancedMode="inline"`. Both read and write the same tree, so a saved view built in one opens in the other. Other props worth knowing before you hand-roll them: `size` is two rungs, `"sm" | "default"`, resolved per style (there is no `lg`); `reorderable` turns on drag and Alt+Arrow row moves in the builder; `onBeforeQueryChange` is the ONE veto point for every write (return `false` to refuse, it cannot rewrite); `editors` registers custom value editors a field selects by `editor` name; `labels` / `operatorLabels` own every rendered string; `pathCollapse` + `maxPathSegments` shorten deep attribute paths; `renderChip` / `renderValue` / `renderEmpty` replace rendered parts. On a field, `loadOptions` supplies async options with paging and `resolveValues` renders a chip restored from a saved view whose option was never loaded. Pairs naturally with `data-grid`.
## cascader
**Required:** `items` (a tree of `{ value, label, children? }`), plus the panel parts inside `CascaderContent`.
**Shape:**
```tsx
<Cascader items={items} value={value} onValueChange={setValue}>
<CascaderTrigger render={<Button variant="outline" />}>
<CascaderValue placeholder="Select an attribute" />
</CascaderTrigger>
<CascaderContent className="w-80">
<CascaderPanel>
<CascaderNav>
<CascaderBreadcrumb />
<CascaderInput />
</CascaderNav>
<CascaderEmpty />
<CascaderList maxHeight={288}>
<CascaderItems />
</CascaderList>
<CascaderStatus />
</CascaderPanel>
</CascaderContent>
</Cascader>
```
**Gotcha:** pressing a branch NAVIGATES, it does not select - only leaves are selectable until you pass `selectable="any"` or a predicate, and once a branch is selectable its chevron becomes the only way to open it. `CascaderInput` must stay inside `CascaderContent` (Base UI refills the query from the selection when the input sits outside the popup). Always include `CascaderStatus`: it is the live region announcing level changes, which the visual breadcrumb does not provide to screen readers. Accepts a flat adjacency list via `getParent` as well as nested `children`. `searchScope="deep"` searches every level and annotates results with their path; `multiple` gives checkbox rows; `inline` + a bare `CascaderPanel` embeds it with no popover.
The shape above is `mode="drill"`, the default. `mode="tree"` keeps the same parts (drop `CascaderBreadcrumb`, pass `showBack={false}`, drive expansion with `expanded`/`onExpandedChange`); `mode="columns"` REPLACES `CascaderList` + `CascaderItems` with a single `CascaderColumns`, and has no breadcrumb. Other props worth knowing before you hand-roll them: `cascade` (multi-select only, parent/child selection with indeterminate branches - pair it with `selectable="any"`, since a leaf-only tree can never cascade), `indicator={false}` to drop the single-select check and its gutter (visual only, no-op with `multiple`), `virtualize`/`virtualizeThreshold` plus `CascaderVirtualItems` for long levels, and `getChildren` for async levels with cursor paging, retry on failure and optional `prefetch`. `CascaderFooter` pins commands below the list (`actions` is the quick path) and `CascaderSubmenu` opens one as a side-anchored flyout with the full menu keyboard model. To head a run of rows use `CascaderGroup` wrapping a `CascaderLabel` - a bare label inside a listbox names nothing and is dropped from the accessibility tree - and `CascaderSeparator` for the rule between runs. Every rendered string comes from `labels`, and the panel is RTL-correct under a `DirectionProvider` or `dir="rtl"`.
## date-selector
**Required:** none, but wire `onChange` to capture the value.
**Shape:**
```tsx
const [value, setValue] = useState<DateSelectorValue | undefined>()
<DateSelector value={value} onChange={setValue} label="Due date" />
```
**Gotcha:** the value is a structured `DateSelectorValue` (period / operator / start+end dates), NOT a `Date` - never pass a raw `Date`. Use `allowRange={false}` to lock single-date picking. Read `get_component("date-selector")` for the value shape.
## tree
**Required:** `tree` (a `@headless-tree/core` instance you construct)
**Shape:**
```tsx
<Tree tree={tree}>
{tree.getItems().map((item) => (
<TreeItem key={item.getId()} item={item}>
<TreeItemLabel />
</TreeItem>
))}
</Tree>
```
**Gotcha:** `Tree` is a styled shell - it takes a headless-tree instance via `tree`, NOT `data`/`items` props. Build the instance with `@headless-tree/react`. External API: https://headless-tree.lukasbach.com/
## stepper
**Required:** `StepperItem step` (number), `StepperContent value` (number)
**Shape:**
```tsx
<Stepper defaultValue={1}>
<StepperNav>
<StepperItem step={1}>
<StepperTrigger><StepperIndicator>1</StepperIndicator></StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger><StepperIndicator>2</StepperIndicator></StepperTrigger>
</StepperItem>
</StepperNav>
<StepperPanel>
<StepperContent value={1}>Step 1 content</StepperContent>
<StepperContent value={2}>Step 2 content</StepperContent>
</StepperPanel>
</Stepper>
```
**Gotcha:** steps are 1-indexed. Without `StepperPanel` + `StepperContent` you render the nav trail but no body. Put `StepperSeparator` in every `StepperItem` except the last.
## timeline
**Required:** `TimelineItem step` (number)
**Shape:**
```tsx
<Timeline>
<TimelineItem step={1}>
<TimelineHeader>
<TimelineDate>March 2024</TimelineDate>
<TimelineTitle>Project initialized</TimelineTitle>
</TimelineHeader>
<TimelineIndicator />
<TimelineSeparator />
<TimelineContent>Repo and architecture set up.</TimelineContent>
</TimelineItem>
</Timeline>
```
**Gotcha:** each item needs a unique `step`. `orientation` is `"vertical"` (default) or `"horizontal"`. This is a static event display, not interactive like `stepper`.
## autocomplete
**Required:** `items` (array; each item has at least `value`)
**Shape:**
```tsx
<Autocomplete items={items}>
<AutocompleteInput placeholder="Search..." />
<AutocompleteContent>
<AutocompleteEmpty>No results found.</AutocompleteEmpty>
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.value} value={item}>{item.label}</AutocompleteItem>
)}
</AutocompleteList>
</AutocompleteContent>
</Autocomplete>
```
**Gotcha:** `AutocompleteList` takes a render-prop `(item) => ReactNode`, NOT a mapped array of children. External API: https://base-ui.com/react/components/autocomplete
## phone-input
**Required:** none, but wire `onChange`.
**Shape:**
```tsx
<PhoneInput placeholder="Enter phone number" defaultCountry="US" value={value} onChange={setValue} />
```
**Gotcha:** `value`/`onChange` use an E.164 string (e.g. `"+14155551234"`), not a display-formatted string; `onChange` can fire `undefined`. `defaultCountry` is a 2-letter ISO code. Wraps `react-phone-number-input`.
## number-field
**Required:** wrap the controls in `NumberFieldGroup`.
**Shape:**
```tsx
<NumberField defaultValue={0}>
<NumberFieldScrubArea label="Quantity" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
```
**Gotcha:** import from `@/components/ui/number-field`. The accessible label goes on `NumberFieldScrubArea`, not `NumberField`. External API: https://base-ui.com/react/components/number-field
## rating
**Required:** `rating` (number)
**Shape:**
```tsx
<Rating rating={4.5} showValue editable onRatingChange={setRating} />
```
**Gotcha:** supports decimals (partial stars). Pass `editable` + `onRatingChange` for interactive input; omit both for a read-only display.
## scrollspy
**Required:** `targetRef` (the scroll container ref)
**Shape:**
```tsx
<Scrollspy targetRef={containerRef}>
<a href="#s1" data-scrollspy-anchor="s1">Section 1</a>
<a href="#s2" data-scrollspy-anchor="s2">Section 2</a>
</Scrollspy>
<div ref={containerRef}>
<div id="s1">...</div>
<div id="s2">...</div>
</div>
```
**Gotcha:** each link's `data-scrollspy-anchor` must match a section `id`. `targetRef` is the scrollable container (defaults to the window).
## frame
**Required:** `Frame` > `FramePanel`
**Shape:**
```tsx
<Frame>
<FramePanel>
<FrameHeader>
<FrameTitle>Title</FrameTitle>
<FrameDescription>Description</FrameDescription>
</FrameHeader>
<div className="p-5">Content</div>
<FrameFooter>Footer</FrameFooter>
</FramePanel>
</Frame>
```
**Gotcha:** a structured card shell for tool-like surfaces. `stacked` connects multiple panels with shared borders; `dense` removes panel padding; radius via the `--frame-radius` CSS variable.
## icon-stack
**Required:** one child icon
**Shape:**
```tsx
<IconStack aria-hidden="true">
<InboxIcon className="size-4" />
</IconStack>
```
**Gotcha:** isometric layered artwork for empty states and illustrations; style the inner icon via its own `className`. Mark purely decorative stacks `aria-hidden="true"` and keep the real label in surrounding copy.
## icon-tile
**Required:** one child icon
**Shape:**
```tsx
<IconTile variant="elevated" size="lg">
<PackageIcon />
</IconTile>
```
**Gotcha:** the square container an icon sits in, so every list row, feature card and empty state shares one affordance. `variant`: `outline` (default) | `elevated` (muted fill, raised ring) | `soft` (tinted nested, tone from currentColor) | `solid` (filled tone, contrasting glyph) | `frame` (double container). `soft` and `solid` retint from one text color class (they default to `text-primary`). `size`: `xs | sm | default | lg | xl` (24/32/40/48/64px tile, glyph scales 12/14/16/20/24px). `radius`: `default | full`. Do not set a `size-*` class on the child icon unless you mean to override the tile's glyph size; recolor with `className` on the tile, not the icon.
## code-block
**Required:** `code` + `language`, or pre-highlighted `lines`.
**Shape:**
```tsx
<CodeBlock code={code} language="tsx" />
<CodeBlock code={code} language="tsx" showLineNumbers maxLines={20}>
<CodeBlockHeader>
<CodeBlockTitle>use-totals.ts</CodeBlockTitle>
<CodeBlockLanguage />
<CodeBlockCopyButton className="ml-auto" />
</CodeBlockHeader>
<CodeBlockExpandButton />
</CodeBlock>
```
**Gotcha:** the one-liner is already a complete block; every child (header, title, language label, copy button, wrap toggle, expand button, line actions) is optional chrome, at any depth. Shiki is the only npm dependency and loads lazily, one chunk per language, on first highlight; `lines` (from `highlightCode` in `code-block-highlight`, which is server-safe and has no `"use client"`) or `highlight={false}` loads nothing at all. `maxLines` caps the height AND marks the block collapsible, which is what makes `CodeBlockExpandButton` appear. Use `variant="ghost"` when the block sits inside a surface that already has a border.
## alert
**Required:** `Alert` > `AlertTitle`
**Shape:**
```tsx
<Alert variant="success">
<ShieldCheckIcon />
<AlertTitle>Security update</AlertTitle>
<AlertDescription>Enable two-factor authentication.</AlertDescription>
<AlertAction><Button size="xs">Update</Button></AlertAction>
</Alert>
```
**Gotcha:** shadcn-compatible API. `variant`: `default | destructive | info | success | warning | invert`. The non-default variants use ReUI extended color tokens (`--success`/`--info`/`--warning`/`--invert`), which the install adds. Defer generic alert rules to the shadcn skill.
## badge
**Required:** none (text child).
**Shape:**
```tsx
<Badge variant="success-light" size="sm">Success</Badge>
<Badge variant="outline" radius="full">Pill</Badge>
```
**Gotcha:** shadcn-compatible. Rich `variant` set (solid, `-outline`, `-light` per color), `size` `xs..xl`, `radius` `default | full`. Like `alert`, the color variants rely on ReUI extended tokens. Prefer `Badge` variants over raw color classes for statuses.
## base vs radix - write for the project's base
ReUI ships every component in two builds: `base` (Base UI) and `radix` (Radix UI). The install command and name are identical, and the CLI installs the build matching the project. But you must write/adapt code against the **right base**, because their APIs differ.
**Detect the base first.** Read `components.json` -> `style` and take the segment before the first `-`:
- `"style": "base-nova"` -> **Base UI**
- `"style": "radix-nova"` -> **Radix UI**
**Then use that base's API.** The deltas mirror shadcn's base-vs-radix split:
- Slot/composition: Base UI `render={<… />}` vs Radix `asChild`.
- `Select`: Base UI takes `items`; Radix uses `<SelectItem>` children.
- `ToggleGroup`: Base UI `multiple` boolean vs Radix `type="single" | "multiple"`.
The safest path is to **read the installed files and `c-*` examples** - they're already in your base, so reuse their wiring instead of guessing. When `get_component`'s inline `api` or an example shows the other base's shape, translate it to your base (or `validate_usage` to confirm). Defer the generic base/radix mechanics to the shadcn skill.
+45
View File
@@ -0,0 +1,45 @@
# Craft: make ReUI UI exceptional, not generic
ReUI items ship senior-designer quality. Your adaptation has to hold that bar, so the result reads like a real product surface a team would keep - not a wireframe an AI generated. Use these alongside the reuse rules in [adapting.md](./adapting.md).
## Have a point of view
Pick an emotional register before you compose - calm, operational, premium, editorial, dense, energetic - and let layout, spacing, surface treatment, and icon behavior all reinforce it. One or two memorable decisions and restraint everywhere else beats ten generic ones. UI with no point of view reads as generated.
## Brutally clear hierarchy
One focal point per card or panel: the dominant metric or task first, its label second, supporting detail third. The first thing the eye lands on should be the right thing; secondary text must read as secondary. Borders, separators, and surfaces do real work to create 2-3 information bands - don't flatten everything to equal weight.
## Spacing rhythm and deliberate density
Gaps are a signal, not a default. Keep them intentional and consistent within a family (`gap-1`/`gap-2` for tight operational rows, larger gaps for section breaks), and smaller within a group than between groups. Match the surrounding ReUI density; don't pad an operational surface like a marketing page, and don't drift density mid-section. The composition should still feel authored in grayscale.
## Cover the real states (the usual miss)
A surface isn't done at the happy path. Compose, and wire:
- **Empty** - a purposeful empty state (short message + the primary action), never a blank panel.
- **Loading** - a **skeleton** that matches the real layout, not a centered spinner.
- **Error** - an inline, recoverable error with a retry, announced via `role="status"`/`aria-live`.
Derive these from an element the block already has (don't invent parallel markup), or `get_examples` for a state-specific example.
## Responsive by default
Mobile-first, not mobile-afterthought. In constrained rows/cards/sidebars, put `min-w-0` on the shrinking container and `truncate` long single-line labels; protect the primary label's width and let secondary content compress. Reflow layouts (multi-column -> single column) rather than just shrinking them. Desktop and mobile should both look designed.
## Motion, subtly
Motion should clarify, not decorate. Use ReUI Motion Icons on primary actions for a subtle hover cue; keep transitions short (~200-300ms) with calm easing; prefer a skeleton pulse over a spinner. No bouncing, no gratuitous entrance animations on every element.
## Real, activated content
Use believable, typed data (realistic labels, counts, timestamps, statuses that map to a real workflow) - never lorem or abstract filler. Every visible control does something: no decorative buttons, fake tabs, meaningless toggles, or stats with no job. It must still hold with long names, empty values, and crowded data.
## Avoid the AI tells
These instantly read as generated - don't ship them: equal-weight card walls, empty gradients, repetitive padding everywhere, generic enterprise copy, ornamental icons, and number tiles that don't earn their place.
## The bar
Before you finish, ask: **would a product team keep this instead of replacing it? Does it still feel strong after swapping in real content?** If not, reuse the shipped ReUI design harder - don't restyle it into something new - then run the [quality.md](./quality.md) gates.
+39
View File
@@ -0,0 +1,39 @@
# Icons (ReUI delta over shadcn)
Follow the shadcn icon rules (use the project's configured `iconLibrary`, `data-icon` on icons inside `Button`, no sizing classes on icons inside components, pass icons as component objects not string keys). ReUI adds the following.
## Portable icons (library-agnostic)
ReUI components, examples, and blocks are authored to be icon-library-agnostic. When `iconLibrary` is set in `components.json`, the shadcn CLI installs each item's icons in **your** library automatically - you swap nothing. If an installed item's icons don't match your project (for example `iconLibrary` isn't set, so they came in from the item's demo library), change the **import source and component name** to your library, keeping the same icon-name semantics:
- `lucide` -> `lucide-react`
- `tabler` -> `@tabler/icons-react`
- `phosphor` -> `@phosphor-icons/react`
- `remix` -> `@remixicon/react`
- `hugeicons` -> `@hugeicons/react`
Don't assume `lucide-react`; read `iconLibrary` from `components.json`.
## Keep icons purposeful
Icons support the hierarchy, they don't replace it: keep them small, matched to the surrounding density, and decorative ones `aria-hidden="true"` (an icon-only control still needs an accessible label on the control). Don't add ornamental icons that do no job.
## Motion Icons (the `@reui/icons/...` set)
ReUI ships its own icon set in 4 styles (outline, solid, duotone, filled), each icon in two variants:
```bash
npx shadcn@latest add @reui/icons/default/<style>/<name> --yes # static
npx shadcn@latest add @reui/icons/animated/<style>/<name> --yes # hover-animated (motion/react)
```
Finding them via the MCP is free; installing requires an Ultimate license (`REUI_LICENSE_KEY`, see [cli.md](./cli.md)). Reach for a Motion Icon on a primary action when a subtle hover cue helps; keep motion restrained.
Finding icons:
- Several icons (the common case): **`search_icons(concepts[])`** - up to 24 concepts in one call, the best icons per concept with install commands. Pass `animated: true` to get only icons with a hover-animated Motion variant.
- One icon: `search` with `type: "icon"`.
- Icon results and `get_icon` carry `animated: true` and `installAnimated` when an animated variant exists - use those install strings, do not construct paths by hand.
- Every icon result carries a `previewUrl` (its live icon-category page) - **share it with the user** so they can SEE the icon before installing.
The `icon-stack` component composes multiple icons into a stacked display.
+22
View File
@@ -0,0 +1,22 @@
# Quality gates (security, accessibility, scroll)
These are the **done gate**, not a nice-to-have: before you call any ReUI work finished, call the MCP `get_audit_checklist` tool and pass every item below (plus the craft bar in [craft.md](./craft.md)). Then typecheck and lint.
## Security
- Never `dangerouslySetInnerHTML`. Render data as text/components.
- External links (`target="_blank"`) must always pair `rel="noopener noreferrer"`.
- No real PII, secrets, or tokens in demo or committed code. Remote media only from sources the project already allows.
## Accessibility
- Implicit list/card items that navigate get real anchors with a standard hover affordance.
- Icon-only or numeric buttons need an `aria-label`; decorative icons get `aria-hidden`.
- Every non-submit button is `type="button"`.
- Keyboard + focus: everything interactive is reachable in a sensible Tab order with a visible focus ring; layers (dialogs/sheets/menus) trap focus and close on `Escape`. ReUI components ship standard keyboard behavior - read each component's inline `api` rather than re-implementing it.
- Announce async UI: loading and error messages use `role="status"` / `aria-live` so they're not silent to screen readers.
## Scroll mechanics
- Make scroll regions with a parent-owned height: a `min-h-0` + flex chain down to the scroll container. Never guess a `max-h`.
- The scroll container owns `overflow-auto`; ancestors stay `min-h-0` so the height resolves.
+35
View File
@@ -0,0 +1,35 @@
# ReUI registry structure
ReUI is a shadcn-compatible registry with four entity types. **Examples and blocks are built FROM components** - reuse them, don't rebuild.
- **component** - one of the 22 ReUI building blocks with a real API (`data-grid`, `kanban`, `filters`, `date-selector`, `tree`, ...). Install directly (`@reui/data-grid`) or let it come in as a dependency of an example/block. Free. Read its API with `get_component(name)`.
- **example** - a free `c-*` single-pattern use-case of a component (`c-kanban-1`, `c-data-grid-3`). Install one and read it to copy real composition. Find a component's examples with `get_examples(name)`.
- **block** - a premium, full-page section that composes several components (`data-grid-2`, `pricing-page-1`). Pro or Ultimate license at install. Adapts to your active theme via semantic tokens.
- **icon** - Motion Icons in 4 styles (outline, solid, duotone, filled), static (`@reui/icons/default/<style>/<name>`) and hover-animated (`@reui/icons/animated/<style>/<name>`). Ultimate license at install. See [icons.md](./icons.md).
## The @reui registry
Install everything through the shadcn CLI: `npx shadcn@latest add @reui/<name> --yes`. The CLI reads the `@reui` registry from the project's `components.json`. Free items need only the plain string form:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium installs need the authenticated form + `REUI_LICENSE_KEY` in `.env.local` - see [cli.md](./cli.md). The MCP `get_project_context` tool returns the right config.
## Know your base: base or radix
ReUI ships every item in two builds - `base` (Base UI) and `radix` (Radix UI) - with mirrored names. The CLI installs the build matching your project automatically, but **you must write code against the right base's API**. Detect it from `components.json` -> `style`: the segment before the first `-` is the base (`base-nova` -> Base UI, `radix-nova` -> Radix UI). The installed files and `c-*` examples are already in your base - read them and adapt on that base. See [components.md](./components.md) for the API deltas.
**So the MCP's own `docsUrl` and `previewUrl` match your base**, send your `style` to the MCP: add `?style=<your components.json style>` to the ReUI MCP server URL (or set an `X-Reui-Style` header) in your MCP client config - set once, applies to every call. The MCP then resolves docs/preview links to YOUR library (`/docs/components/radix/...`, `/preview/radix/...` for a radix project) instead of the default base; `get_project_context` echoes back the style it currently sees so you can confirm it. Install commands are unaffected (the CLI already installs the right variant). If you notice the MCP returning `base` links for a `radix` project, tell the user to add `?style=` to the server URL.
Blocks adapt to your active theme through semantic tokens and CSS variables - change the theme and every block follows.
## Free vs premium
- **Free, no key:** the 22 components, all `c-*` examples, the ReUI MCP, and this skill.
- **Premium, license required at install:** blocks (Pro or Ultimate), Motion Icons and templates (Ultimate). Set `REUI_LICENSE_KEY` (see [cli.md](./cli.md)).
## Component API index
The canonical index of every component's API docs is **https://reui.io/llms.txt** (returned as `componentsApiUrl` in MCP results). Prefer the inline `api` from `get_component`; use the index/docs as the fallback.
+26
View File
@@ -0,0 +1,26 @@
# Styling (ReUI delta over shadcn)
Follow the shadcn skill for the generic rules (semantic colors not raw values, `gap-*` not `space-y-*`, `size-*`, `cn()`, no manual `dark:` overrides, no overlay `z-index`). This file is only the ReUI-specific additions.
## ReUI extended semantic tokens
ReUI adds semantic tokens beyond shadcn's base set. Use these instead of raw colors for status and emphasis:
- `--success` / `--success-foreground`
- `--info` / `--info-foreground`
- `--warning` / `--warning-foreground`
- `--destructive-foreground` (paired with shadcn's `--destructive`)
- `--invert` / `--invert-foreground` (inverted surfaces)
Use them as Tailwind utilities (`bg-success text-success-foreground`, `text-warning`, ...). They are defined in the project's global CSS and registered with Tailwind (`@theme inline` on v4). If a token is missing in the project, add it to the global CSS file (never a new file) following the same `name` / `name-foreground` convention, exactly as the shadcn customization rules describe.
**Incorrect:** `<span className="text-green-600">Active</span>`
**Correct:** `<Badge variant="success">Active</Badge>` or `<span className="text-success">Active</span>`
## Blocks follow your theme
When you install a block it adapts to your active theme through the semantic tokens above and the project's CSS variables. Don't hardcode style-specific values into installed block code and don't fork it to "restyle" - change the theme via the CSS variables / a preset and every block follows. Want a different look? `search` for a block whose design already fits instead of re-skinning one.
## Density and typography rhythm
ReUI operational UI usually feels dense, not airy. Keep the gap between a title and its supporting description tight by default (`gap-0.5`, `space-y-1`, or `space-y-px`), and smaller than the gap between sections. Match the surrounding ReUI density when you add rows or fields; do not pad operational surfaces like a marketing page.
+54
View File
@@ -0,0 +1,54 @@
# Workflow: find -> install -> read API -> adapt
The core ReUI loop. The MCP tells you what to install and gives you the API; the shadcn CLI installs it; you turn the installed files into correct, themed, data-wired code by **reuse**, not redesign.
## 1. Find (ReUI MCP `search` / `compose_page`)
**Full multi-section page ask?** Call `compose_page(intent, sections?)` FIRST, before searching block-by-block. It returns ordered sections, each with the best block for the intent (top pick + alternates); sections listed in `unavailableSections` have no real inventory - compose those from components, do not force a bad block.
For everything else, call `search` with the user's intent. Pass structured hints whenever you can infer them - you are an LLM, so do the parsing the server cannot:
- `type`: `"component"` (one of the 22 building blocks), `"example"` (a c-\* use-case), `"block"` (a full page/section), `"icon"`.
- `component`: the ReUI component the request implies (`"data-grid"`, `"kanban"`, ...).
- `category`, `features` (e.g. `["sortable","pagination"]`), `free`.
Example: "build a users management page with filters" -> `search({ query: "users management page with filters", type: "block", component: "data-grid", features: ["filters"] })`.
Each result has `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `termCoverage`, and `whyMatch`. `score` is relative to the top hit (the top is ~100 by construction), not an absolute quality - compare results to each other, and show the user the top options if several score closely; do not silently guess. A low `termCoverage` means a weak match even with a high score - rephrase or widen.
**Always show the preview link.** Whenever you list or recommend items - from `search`, `search_icons`, `list_components`, `compose_page`, or a getter - include each item's `previewUrl` (a live preview page) so the user can SEE it before you install. Blocks and examples link to an individual live preview; icons and components to their live category/component page. This applies to every listing, not only a single pick.
## 2. Install (shadcn CLI)
Run the result's `install` command from the project root, non-interactively:
```bash
npx shadcn@latest add @reui/<name> --yes
```
The CLI reads `components.json`, installs the correct base+style variant, resolves `registryDependencies` (a block pulls in its components), installs npm deps, and rewrites aliases. Do not pass the base/style. See [cli.md](./cli.md).
## 3. Read the API (do not guess props)
Before writing code against any component an item uses:
1. The item's `componentDigests` already give a 1-line contract per component - often enough to wire it. For the full API, call **`get_component(names)`** with ALL of `componentsUsed` in ONE call (it accepts an array) and read each inline `api` - no web fetch. **Share the component's `docsUrl`** (its API documentation page) with the user whenever you work with that component's API, so they have the full reference; the `/llms.txt` index is a further fallback.
2. Call **`get_examples(name)`** for the free `c-*` examples of that component; install one and **read the added files** to copy the exact composition. This is the fastest correct path - the example shows real wiring you adapt, not invent.
3. About to write a prop you did not see in an `api` or installed file? Run **`validate_usage`** BEFORE writing the code - per-prop documented / notDocumented verdicts plus did-you-mean suggestions. notDocumented means read the API, not push on.
## 4. Adapt (reuse-first) - do not skip
Installing files is not the end, and redesigning them defeats the point. First note the project's **base** so you write the right API - read `components.json` -> `style` and take the segment before the first `-` (`base-nova` -> Base UI, `radix-nova` -> Radix UI), see [components.md](./components.md). After `add`:
1. **Read the added files**; keep the composition intact. For a block, verify the components are wired correctly (for `data-grid`: a `useTable({ features: dataGridFeatures, ... })` instance passed as `table`, `recordCount` set - see [components.md](./components.md)).
2. **Replace demo data with the user's real data** via typed structures (see [adapting.md](./adapting.md)).
3. **Fix icon imports** to the project's icon library (see [icons.md](./icons.md)).
4. **Align styling** to semantic tokens and the active theme - no raw colors (see [styling.md](./styling.md)).
5. **Validate before finalizing**: if your adaptation introduced components or props you did not read in an `api` or example, run `validate_usage` on them.
6. **Hit the craft bar** - clear hierarchy, deliberate density, the empty / loading / error states, subtle motion, and mobile-first responsiveness (see [craft.md](./craft.md)). Generic-looking output means you under-reused the design, not that it needs restyling.
7. **Pass the quality gates** (security, a11y, scroll) - call the MCP `get_audit_checklist` tool and clear every item (see [quality.md](./quality.md)).
8. **Typecheck / lint**.
## If no single block fits
Compose from components (`compose_page` tells you which sections have no block inventory via `unavailableSections`). `search` the components you need, read each `get_component` API, install a worked `get_examples` example for each, and assemble by adapting those examples. A block in the same category is a useful reference - install it and read its files to see how ReUI composes those components, then adapt.
+58
View File
@@ -0,0 +1,58 @@
# ReUI MCP: full reference
The ReUI MCP (`https://mcp.reui.io`, Streamable HTTP) is free to use but needs a ReUI account: on first use the agent signs in with ReUI (a free account is created if the user has none), so every request is tied to an account. Free covers components and examples; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the daily request limit. It does **discovery + guidance** (search, inline APIs, page planning, validation) and never serves source; the shadcn CLI does **installation**, and the license key lives there (the `@reui` entry in `components.json`, backed by `.env.local`). Goal: from the user's intent to correct, themed, data-wired ReUI code in the **fewest tokens and calls**, with **no guessing**.
## Golden path (token-optimal - follow this order)
Most tasks need 2-4 MCP calls and ZERO web fetches:
1. **`search(query, ...hints)`** -> pick the top 1-3 results. Each result already carries `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `whyMatch`. The payload is complete - do not call another tool just to "confirm" a result.
2. **`get_component([...componentsUsed])`** in ONE batched call (one name or an array of up to 20) -> read each inline `api`. This **replaces** fetching docs pages. Often skippable: search responses carry `componentDigests`, a compact API contract per referenced component.
3. **`get_examples(component)`** -> install ONE returned `c-*` example, read the added files, copy the composition.
4. **`get_install_command(item)`** only to validate a name you are unsure of (results already include `install`). Run the install with the shadcn CLI (`--yes`).
5. **`get_audit_checklist()`** before declaring done.
If you already know the exact item name, skip `search`. Everything else is situational.
## The 5 task-specific tools (when to reach for each)
- **`compose_page`** - BEFORE building any full page (dashboard, settings, billing, landing). Pass the intent (and optionally the sections you want); it returns ordered sections, each with the best premium block for the intent (top pick + alternates). Sections with no real inventory are listed honestly in `unavailableSections` - compose those from components instead of forcing a bad block.
- **`search_icons`** - whenever you need icons, especially several. Batch up to 24 concepts in one call; each concept returns its best icons with install commands. Pass `animated: true` to get only icons that have a hover-animated Motion variant.
- **`validate_usage`** - BEFORE writing code with component names or props you have not read in an inline `api` or an installed example. It checks planned names + props against the indexed API docs and registry item names; returns did-you-mean suggestions and per-prop documented / notDocumented verdicts. Deterministic, no inference - a notDocumented prop means stop and read the API, not push on.
- **`whats_new`** - when your registry knowledge might be stale (a name 404s, the user mentions an item you don't know). Returns items added/removed per build, newest first.
- **`report_issue`** - when an installed item is actually broken (bad source, wrong dependency, broken preview). Goes straight to the ReUI team; rate-limited 5/hour. Not for usage questions.
## All 19 tools
`search`, `get_block`, `get_example`, `get_icon`, `list_block_groups`, `list_block_categories`, `list_example_categories`, `list_icon_categories`, `list_components`, `get_component`, `get_examples`, `search_icons`, `compose_page`, `validate_usage`, `whats_new`, `report_issue`, `get_install_command`, `get_project_context`, `get_audit_checklist`. The MCP serves the full parameter schemas; do not guess parameters beyond them.
## Token + speed rules
- **Batch `get_component`** - ONE call with the whole `componentsUsed` array, never N calls. Skip it entirely when `componentDigests` already answers the question.
- **Read source by installing** - the MCP serves no source. To read or analyze an item's real code, install it with the shadcn CLI and open the local files. Learn an API from the inline `api` / `componentDigests`, never by reading raw source.
- **Infer `search` hints yourself** (`type`, `component`, `category`, `features`, `free`) - hints shrink the result set and the tokens. Keep `limit` low; one right result beats ten.
- Run independent calls (and the shadcn install) concurrently in one turn - serial tool calls are the main source of slowness.
- Don't repeat a search for the same intent; don't call `list_*` to "see everything" - `search` is the entry point, `list_*` is only for browsing a taxonomy the user explicitly wants to explore.
- Prefer `get_component`'s inline `api` over `docsUrl` / `/llms.txt`. Fetch a web page only as a last resort.
## Result shapes (so you don't re-fetch)
- `score` is 0-100 RELATIVE to the top hit (the top is ~100 by construction), not absolute - compare results to each other.
- `termCoverage` (0-1) is the share of the query the item matched - low means a weak match even if the score looks high; rephrase or widen the search.
- Each result carries `whyMatch`, `install`, docs/preview URLs, and a `free` flag; premium items carry `requiredPlan` (`"pro"` for blocks, `"ultimate"` for icons).
- `componentDigests` is a top-level map: a compact API contract per referenced component - often enough to wire an item without a `get_component` call.
- Icon results and `get_icon` include `animated: true` and `installAnimated` when a hover-animated Motion variant exists (animated: `@reui/icons/animated/<style>/<name>`; static: `@reui/icons/default/<style>/<name>`).
## Error playbook
- **401** - the MCP requires a signed-in ReUI account. The client prompts "Sign in with ReUI" (OAuth) on first use; a free account is created if needed. For headless/CI, pass a personal token (`reui_pat_...`, created at https://reui.io/account/mcp?ref=skill) as `Authorization: Bearer`.
- **403 / locked result** - a valid account but the plan does not cover the item: premium blocks need Pro, Motion Icons need Ultimate. Point to https://reui.io/pricing?ref=skill (upgrade). Free accounts still get all components + examples.
- **429** - rate limited (120 requests/min per IP); back off, honor `Retry-After`.
- **not found** (`found: false`) - use the returned `suggestions`, or `search`; check `whats_new` if you suspect a stale name. Never run a fabricated install command.
## Fallbacks
- No ReUI MCP: `npx shadcn@latest search @reui -q "..."` then `add` (generic, no scoring / inline API).
- The shadcn project's own MCP also works over the `@reui` registry: https://ui.shadcn.com/docs/mcp.
Per-agent MCP setup: https://reui.io/docs/mcp?ref=skill
+16
View File
@@ -0,0 +1,16 @@
# CodeGraph data files
# These are local to each machine and should not be committed
# Database
*.db
*.db-wal
*.db-shm
# Cache
cache/
# Logs
*.log
# Hook markers
.dirty
+3
View File
@@ -0,0 +1,3 @@
[mcp_servers.reui]
url = "https://mcp.reui.io/api/mcp?style=base-nova"
bearer_token_env_var = "REUI_LICENSE_KEY"
+21
View File
@@ -0,0 +1,21 @@
{"sessionId":"00844d","runId":"initial","hypothesisId":"H6","location":"components/debug-runtime-probe.tsx","message":"cursor-ref-count-on-mount","data":{"pathname":"/bgp","count":80},"timestamp":1777738439411}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H5","location":"components/debug-runtime-probe.tsx","message":"route-change","data":{"pathname":"/bgp","search":""},"timestamp":1777738439411}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"mock","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738439416}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"hydrated-from-storage","data":{"mode":"live","backendUrl":"http://localhost:8000"},"timestamp":1777738439428}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"live","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738439603}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H4","location":"lib/data-source.tsx","message":"check-backend-start","data":{"url":"http://localhost:8000"},"timestamp":1777738439605}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H4","location":"lib/data-source.tsx","message":"check-backend-result","data":{"url":"http://localhost:8000","ok":true,"status":200},"timestamp":1777738439621}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"mock","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738508126}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"mock","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738508134}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"hydrated-from-storage","data":{"mode":"live","backendUrl":"http://localhost:8000"},"timestamp":1777738508145}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"hydrated-from-storage","data":{"mode":"live","backendUrl":"http://localhost:8000"},"timestamp":1777738508145}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"live","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738508204}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H4","location":"lib/data-source.tsx","message":"check-backend-start","data":{"url":"http://localhost:8000"},"timestamp":1777738508205}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H4","location":"lib/data-source.tsx","message":"check-backend-result","data":{"url":"http://localhost:8000","ok":true,"status":200},"timestamp":1777738508228}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"mock","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738508553}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"mock","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738508560}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"hydrated-from-storage","data":{"mode":"live","backendUrl":"http://localhost:8000"},"timestamp":1777738508571}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"hydrated-from-storage","data":{"mode":"live","backendUrl":"http://localhost:8000"},"timestamp":1777738508572}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H7","location":"lib/data-source.tsx","message":"provider-mounted","data":{"mode":"live","backendUrl":"http://localhost:8000","storedMode":"live","storedBackend":"http://localhost:8000"},"timestamp":1777738508662}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H4","location":"lib/data-source.tsx","message":"check-backend-start","data":{"url":"http://localhost:8000"},"timestamp":1777738508663}
{"sessionId":"00844d","runId":"initial","hypothesisId":"H4","location":"lib/data-source.tsx","message":"check-backend-result","data":{"url":"http://localhost:8000","ok":true,"status":200},"timestamp":1777738508676}
+34
View File
@@ -0,0 +1,34 @@
---
description: "Generate Commit Message: русский subject, fix vs feat, Conventional Commits"
alwaysApply: true
---
# Git commit message (Generate Commit Message)
При **Generate Commit Message**, автогенерации subject и предложении `git commit`:
## Обязательно
- Subject и body — **только на русском** (после `:` в conventional commit).
- Локальный hook `.githooks/commit-msg` **отклоняет** коммиты без кириллицы в subject.
- Префикс и scope — английский: `fix(ui):`, `chore(rules):`.
## Тип: сначала не `feat`
- **`fix`** — баг, регрессия, правка вёрстки/модалки, некорректное поведение, CI/CD, мелкий дефект.
- **`chore` / `docs` / `ci` / `build`** — правила Cursor, README, workflow, зависимости, рефакторинг без новой UX-фичи.
- **`feat`** — только **новая** пользовательская возможность (страница, API, сценарий), которой не было.
Если сомневаешься между `feat` и `fix` — выбирай **`fix`** или **`chore`**, не `feat`.
## Примеры
- `fix(ui): выровнять модалку сводки релиза`
- `fix(ci): исправить сборку release manifest`
- `chore(rules): усилить правила русских commit message`
- `feat(releases): добавить страницу релизов`
## Запрещено
- `feat: update layout`, `fix: add modal`, `chore: update rules` — английский subject.
- `feat` для правки UI, правил, документации и багфиксов.
+44
View File
@@ -0,0 +1,44 @@
---
description: "Быстрый запуск локального frontend и backend"
alwaysApply: true
---
# Локальный запуск проекта
## Требования
- **Node.js 22**, npm с workspaces.
- Первый раз (или после смены зависимостей): `npm install` из корня репозитория.
- Backend: скопировать `backend/.env.example` → `backend/.env` (по умолчанию `PORT=8000`, `CORS_ORIGIN=http://localhost:3000`).
## Запуск (два процесса)
Из корня репозитория поднять **два** long-running процесса в **отдельных** терминалах:
```powershell
npm run dev
npm --prefix backend run dev
```
| Сервис | URL | Проверка |
|--------|-----|----------|
| Frontend (Next.js 16, Turbopack) | http://localhost:3000 | открыть в браузере |
| Backend (Fastify) | http://localhost:8000 | `GET /health` |
Проверка backend в PowerShell:
```powershell
Invoke-WebRequest -Uri http://localhost:8000/health -UseBasicParsing | Select-Object -ExpandProperty Content
```
Ожидаемый ответ: `{"status":"ok",...}`.
## Поведение агента
Если пользователь просит «запусти проект», «запусти фронт и бэк» или похожее:
1. Сначала проверить уже запущенные терминалы — не дублировать процессы.
2. Запустить обе команды выше как фоновые long-running процессы.
3. Дождаться готовности: frontend — `Ready`, backend — `Server listening` / успешный `/health`.
Подробности архитектуры и env — `README.md`, раздел «Запуск».
+131
View File
@@ -0,0 +1,131 @@
---
description: "Fastify 5 + Zod + Drizzle + SQLite — модульная архитектура, слои, валидация, ошибки (ответы на русском)"
globs: "**/{features,shared,lib,plugins}/**/*.ts,**/app.ts,**/server.ts"
alwaysApply: false
---
# Backend: Fastify 5 + TypeScript (ESM) + Zod + Drizzle + SQLite
**Язык:** все ответы пользователю — **только на русском**.
**Стек:** TypeScript (`"type": "module"`), **Fastify 5.x**, **Zod** + `@fastify/type-provider-zod`, **SQLite** (`better-sqlite3`) + **Drizzle ORM**, `@fastify/cors`, `pino-pretty`, `undici`, `dotenv`.
**Цель:** масштабируемый модульный backend, предсказуемый DX, строгое разделение ответственности.
---
## 1. Архитектура (критично)
- **Feature-based модули**, не монолитные файлы. Каждая фича — изолированная папка.
Структура фичи:
```text
/features/<feature-name>/
route.ts — регистрация маршрутов Fastify
controller.ts — разбор запроса/ответа, вызов сервиса
service.ts — бизнес-логика (переиспользуемая)
schema.ts — Zod-схемы (params, query, body, response)
repository.ts — только Drizzle-запросы к БД
types.ts — опционально
```
**Запреты:** не смешивать слои; **нет** бизнес-логики в `route`; **нет** SQL/Drizzle в `controller`; сервисы не должны дублировать обязанности репозитория.
---
## 2. Fastify
- Регистрация фич через **плагины** и инкапсулированные модули.
- Везде **async/await**; декорации инстанса Fastify — только при реальной необходимости.
---
## 3. Валидация (Zod обязательна)
- **Все входы** — через Zod; `@fastify/type-provider-zod` для типобезопасности.
- Явно задавать схемы: **params**, **query**, **body**, **response** (где уместно).
- Нет «сырого» входа; схемы **переиспользовать** между слоями (импорт из `schema.ts`).
---
## 4. База данных (Drizzle)
- Любой доступ к БД — **только** из `repository.ts` / слоя репозитория.
- Типизированные запросы Drizzle; **без** raw SQL, если нет веской причины.
- Запросы — простые, композируемые; логику БД не выносить наружу репозитория.
- Учитывать возможную смену БД: избегать sqlite-специфичных костылей вне репозитория/миграций.
---
## 5. Ошибки
- Единый формат ошибок API (код/сообщение для клиента).
- Обрабатывать: ошибки валидации, БД, внешних HTTP (`undici`).
- **Не** отдавать клиенту внутренние детали (стейки, SQL, секреты).
---
## 6. Логирование
- **Pino**; в dev — **pino-pretty**.
- Логировать ошибки и значимые действия; не засорять лог шумом.
---
## 7. Внешние HTTP (undici)
- Вызовы только из **service** (или выделенного клиента, вызываемого из сервиса).
- Таймауты, обработка ошибок сети/статусов, без утечки сырого ответа наружу.
---
## 8. Конфигурация окружения
- `dotenv` для локальной разработки; **валидация env** (предпочтительно через Zod-схему).
---
## 9. Организация кода
- Небольшие файлы, одна ответственность на файл.
- Композиция вместо копипаста; общее — в `/shared/` и `/lib/`.
---
## 10. Масштабируемость
- Расширяемые границы модулей, слабая связанность, явные контракты между слоями.
---
## 11. Процесс изменений (обязательно)
Для каждого изменения в ответе указать: что сделано; зачем (архитектура + практики); затронутые файлы; минимальный полный дифф без лишнего рефакторинга.
---
## 12. Формат ответа агента
1. Что изменено
2. Почему (архитектура + практики)
3. Структура файлов (если новая фича)
4. Код (несколько файлов при необходимости)
5. Замечания / улучшения
---
## 13. Антипаттерны (строго запрещено)
- Толстые route handlers
- Смешение БД и бизнес-логики в одном месте
- Пропуск валидации входа
- Глобальное мутабельное состояние
- Дублирование запросов к БД
- Хардкод секретов и конфигурации (всё через env/конфиг)
---
## Роль агента
Вести себя как **senior backend engineer**: чистый модульный код, строгое разделение слоёв, долгосрочная поддерживаемость и предсказуемый процесс разработки.
+38
View File
@@ -0,0 +1,38 @@
---
description: "[Multi-agent] Роль ANALYZER — анализ кодовой базы перед реализацией, без кода"
alwaysApply: false
---
# Агент: ANALYZER (анализатор)
Ты выполняешь **только фазу анализа** перед любыми изменениями кода.
## Язык
- Все ответы пользователю — **на русском**.
## Обязанности
- Искать по **всему** репозиторию релевантные места.
- Зафиксировать:
- похожие компоненты и дубликаты по смыслу;
- существующие UI-паттерны, **особенно в зоне `/servers`** (`app/(main)/servers` и связанные компоненты);
- общие хуки, утилиты, UI-обёртки;
- стратегии загрузки данных (RSC, route handlers, server actions, клиентский fetch — что уже принято в проекте).
- Явно отметить **риски дублирования** и конфликты с текущими абстракциями.
## Запреты
- **Нельзя** писать реализацию (патчи, готовый production-код, большие вставки «как сделать»). Допустимы короткие псевдокод-намёки уровня «расширить X», без полной реализации.
- **Нельзя** предлагать «с нуля», если в кодовой базе уже есть переиспользуемое решение — приоритет **reuse**.
## Формат вывода (строго)
1. **Релевантные файлы** (пути).
2. **Существующие паттерны** (кратко, с привязкой к файлам).
3. **Что переиспользовать** (компоненты, хуки, утилиты).
4. **Риски и конфликты** (дубли, расхождение с `/servers`, ломкие зависимости).
## Цель фазы
Дать Implementer’у однозначный вход: **что трогать, что не плодить, откуда копировать поведение**.
+30
View File
@@ -0,0 +1,30 @@
---
description: "[Multi-agent] Глобальные правила пайплайна (Next 16.2.4, Turbopack, shadcn) — подключать с ролями"
alwaysApply: false
---
# Глобальные правила (все агенты пайплайна)
**Язык:** отвечать пользователю **только на русском**.
## Стек и документация
- Next.js **16.2.4** (App Router, **Turbopack**), React, **shadcn/ui**.
- Сверять решения с **официальной** документацией Next.js и shadcn для версий проекта. Не придумывать API и недокументированные паттерны.
## Встроенные возможности Next.js (предпочтительно)
- App Router, Server Components, Server Actions, Route Handlers — вместо обходных кастомных путей, если задача покрывается фреймворком.
## Эталон UI
- **`app/(main)/servers`** — эталон **поведения** CRUD. Оболочка ops: **Frame + DataGrid**, не Card-shell. Не копировать `DataPageCard` на новые экраны.
## Качество и совместимость
- Устранять: ошибки сборки, TypeScript, гидратации, предупреждения о плохих практиках; учитывать **Turbopack** в dev.
## Связь с проектом
- Полные стандарты проекта (формат ответа, чеклисты) — в правиле **`next-shadcn-production.mdc`** (обычно уже подключено). Эти правила **дополняют** его ролью в пайплайне, не отменяют.
- Сообщения коммитов: **`commit-messages-ru.mdc`** (язык и `fix` vs `feat`), semver bump — **`release-versioning.mdc`**.
+37
View File
@@ -0,0 +1,37 @@
---
description: "[Multi-agent] Роль IMPLEMENTER — реализация по результатам анализа, /servers, shadcn"
alwaysApply: false
---
# Агент: IMPLEMENTER (реализатор)
Ты пишешь **production-ready** изменения **на основе** отчёта Analyzer (и уточнённой задачи пользователя).
## Язык
- Все ответы пользователю — **на русском**.
## Обязанности
- Внедрять фичу **минимально**, но **полностью**: все затронутые файлы (типы, UI, API, actions/handlers) согласованы.
- **Переиспользовать** существующие абстракции из отчёта анализа и кодовой базы.
- Выравнивать UI/UX с **`/servers`** как с главным эталоном (layout, отступы, сетка, типографика, композиция).
- Предпочитать **Server Components**; `'use client'` — только при необходимости интерактива (состояние, браузерные API, обработчики).
- UI: по возможности **shadcn/ui**; не вводить новый визуальный/UX-паттерн, если на `/servers` уже есть эквивалент.
- При завершении задачи предлагать subject по **`commit-messages-ru.mdc`**: правки UI/багов → `fix`, инфраструктура/правила → `chore`, новая фича → `feat`; описание **на русском**.
## Формат вывода (строго)
1. **Что изменено** (файлы и суть).
2. **Почему так** — связка Next.js + shadcn/ui + эталон **`/servers`** + принятые в репозитории паттерны.
3. **Код** — диффы или полные фрагменты по всем затронутым файлам.
4. По возможности: **Server vs Client** для новых/изменённых границ компонентов.
## Запреты
- Не оставлять «полфичи» (забытые типы, сломанные импорты, несогласованный API).
- Не копипастить большие куски без привязки к существующим компонентам, если анализ уже указал переиспользование.
## Согласование с проектом
- Если активно правило **`next-shadcn-production.mdc`**, выдерживай его структуру ответа (шаги 0–6) и требования к проверкам.
+58
View File
@@ -0,0 +1,58 @@
---
description: "[Multi-agent] Оркестратор — последовательность Analyzer→Implementer→Reviewer→UI Guardian"
alwaysApply: false
---
# Оркестратор пайплайна (multi-agent)
Используй это правило, когда нужно прогнать задачу через **все роли по очереди**. Подключай вместе с **`ma-global.mdc`** и при необходимости **`next-shadcn-production.mdc`**.
## Язык
- На каждой фазе ответы пользователю — **на русском**.
## Порядок фаз (не нарушать)
1. **Analyzer** — правило **`ma-analyzer.mdc`**: только анализ, без реализации.
2. **Implementer** — правило **`ma-implementer.mdc`**: код по результатам фазы 1.
3. **Reviewer** — правило **`ma-reviewer.mdc`**: строгое ревью результата фазы 2.
4. **UI Guardian** — правило **`ma-ui-guardian.mdc`**: только сверка с **`/servers`**.
## Как исполнять в одном чате
- Выполни **фазу 1** целиком и остановись на выходном формате Analyzer.
- Затем **фаза 2** с явной ссылкой: «реализуй согласно отчёту Analyzer выше».
- Затем **фаза 3** по артефакту фазы 2.
- Затем **фаза 4** по UI-части изменений.
Если пользователь просит «только одну фазу» — выполни только её и напомни номер следующей.
## Промпт-шаблон для пользователя (скопировать в чат)
```text
Запусти multi-agent пайплайн для задачи ниже.
Фазы строго по порядку: Analyzer → Implementer → Reviewer → UI Guardian.
Язык ответов — русский. Стек: Next.js 16.2.4 App Router + Turbopack, shadcn/ui.
Эталон UI: app/(main)/servers.
Следуй правилам ma-global и соответствующим ma-* ролям.
Задача:
<опишите фичу или баг>
```
## Файлы ролей в `.cursor/rules/`
| Роль | Файл |
|------------|-------------------------|
| Глобальные | `ma-global.mdc` |
| Analyzer | `ma-analyzer.mdc` |
| Implementer| `ma-implementer.mdc` |
| Reviewer | `ma-reviewer.mdc` |
| UI Guardian| `ma-ui-guardian.mdc` |
| Оркестратор| `ma-orchestrator.mdc` |
## Ожидаемый результат пайплайна
- Согласованность с кодовой базой и **максимальный reuse**.
- UI, выровненный под **`/servers`**.
- Отсутствие известных блокеров по Next.js, гидратации, TS и заметных anti-patterns после фазы Reviewer.
+42
View File
@@ -0,0 +1,42 @@
---
description: "[Multi-agent] Роль REVIEWER — строгая проверка Next/shadcn/a11y/гидратации/Turbopack"
alwaysApply: false
---
# Агент: REVIEWER (ревьюер)
Ты **валидируешь** предложенные или уже внесённые изменения. Режим: **строгий**.
## Язык
- Все ответы пользователю — **на русском**.
## Что проверять
- **Next.js:** App Router, границы RSC/Client, данные ближе к серверу, кэширование/revalidate где уместно, корректные route handlers и server actions.
- **shadcn/ui:** композиция, токены/классы проекта, отсутствие «левого» самописного UI там, где хватает shadcn.
- **Производительность:** лишние клиентские деревья, waterfall fetch, чрезмерные `'use client'` границы.
- **a11y:** семантика, фокус, aria, клавиатура (в разумных пределах контекста правки).
- **Гидратация:** несовпадение разметки/случайные значения на сервере и клиенте, неправильное использование браузерных API в RSC.
- **Turbopack:** не предлагать паттерны, ломающие dev-сборку Next 16 + Turbopack; учитывать типичные предупреждения toolchain.
## Обязательные находки
- Ошибочное **смешение Server/Client** (хуки в RSC, серверные модули в клиенте без обёртки и т.д.).
- **Over-fetching** и дублирующая логика запросов.
- **Дублирование** логики/UI там, где уже есть общий модуль или паттерн `/servers`.
## Формат вывода (строго)
Таблица или списки:
1. **Проблема** (кратко).
2. **Серьёзность:** блокер / высокая / средняя / низкая.
3. **Рекомендация по исправлению** (конкретные шаги или патч-уровень).
Если дефектов нет — явно напиши **«Блокирующих замечаний нет»** и перечисли **что именно** было проверено.
## Запреты
- Нельзя «мягко пропускать» очевидные проблемы ради скорости.
- Нельзя ограничиваться общими фразами — каждая **рекомендация должна быть actionable**.
+43
View File
@@ -0,0 +1,43 @@
---
description: "[Multi-agent] Роль UI GUARDIAN — сверка UI с CRUD /servers и оболочкой Frame+DataGrid"
alwaysApply: false
---
# Агент: UI GUARDIAN (хранитель UI)
Ты отвечаешь **только** за **визуальную и UX-согласованность**.
## Язык
- Все ответы пользователю — **на русском**.
## Эталон
Два слоя (не смешивать):
1. **Поведение CRUD** — каталог **`app/(main)/servers`**: фильтры, таблица, диалоги, формы, плотность, паттерны кнопок.
2. **Оболочка ops** — **ReUI Frame + DataGrid**, не shadcn `Card` как page shell. KPI — `IconTile` `variant="elevated"` `className="size-10.5"`. Preview: [frame](https://reui.io/docs/components/base/frame) · [data-grid](https://reui.io/docs/components/base/data-grid) · [stats-12](https://reui.io/preview/base/stats-12) · [icon-tile](https://reui.io/docs/components/base/icon-tile).
`DataPageCard` — ops list shell на ReUI Frame; новые экраны не копируют Card-shell.
## Обязанности
- Сравнить **каждое** спорное UI-решение: CRUD — с `/servers`; оболочка — с Frame+DataGrid.
- Требовать **переиспользование** существующих компонентов/обёрток, если они покрывают задачу.
## Правила
- **Нельзя** принимать новый Card page-shell без явной технической необходимости (назвать её).
- **Нельзя** предлагать «новый стиль» ради вариации — только выравнивание с эталоном или расширение существующих примитивов.
## Формат вывода (строго)
1. **Несоответствия** эталону (конкретно: CRUD vs Frame-shell).
2. **Обязательные правки** (что поменять).
3. **Что переиспользовать**: файлы/компоненты и паттерн.
Если расхождений нет — явно: **«Расхождений с эталоном CRUD /servers и Frame+DataGrid не выявлено»** и кратко перечисли проверенные аспекты.
## Граница ответственности
- Не дублируй полный код-ревью Implementer/Reviewer: фокус только на **консистентности** и **reuse** UI.
+84
View File
@@ -0,0 +1,84 @@
---
description: Next.js App Router + shadcn/ui — эталон /servers, поиск по репозиторию, формат ответа и проверки
alwaysApply: true
---
# Next.js 16 (App Router, Turbopack) + shadcn/ui — правила проекта
Стек ориентира: **Next.js 16.2.4** (App Router, Turbopack), **React**, **shadcn/ui**. При расхождении с документацией — верифицировать по официальным источникам для вашей версии.
**Язык:** все ответы пользователю — **только на русском**.
**Git / Generate Commit Message:** subject и body — **на русском**; `feat` только для новой UX-фичи, иначе `fix`/`chore`. См. **`commit-messages-ru.mdc`** и **`release-versioning.mdc`**.
## 1. Документация в первую очередь
- Решения сверять с **официальной** документацией Next.js и shadcn/ui (актуальные версии проекта).
- Не выдумывать API и «недокументированные» паттерны; предпочитать стабильные, описанные в доках решения.
## 2. Эталон UI/UX: `/servers` (CRUD) + Frame+DataGrid (оболочка)
- **`app/(main)/servers`** — эталон **поведения** CRUD: фильтры, сортировка, пагинация, диалоги, формы, плотность.
- **Оболочка ops-списков / dashboard / KPI:** **ReUI Frame + DataGrid**, не shadcn `Card` как page shell. Preview: [frame](https://reui.io/docs/components/base/frame) · [data-grid](https://reui.io/docs/components/base/data-grid) · [stats-12](https://reui.io/preview/base/stats-12) · [icon-tile](https://reui.io/docs/components/base/icon-tile).
- `DataPageCard` — ops list shell на ReUI Frame (`components/data-page-card.tsx`). Внутренние панели — `OpsPanel` / Frame, не shadcn Card как page shell.
- Переиспользовать паттерны `/servers` для CRUD; **не** вводить Card-grid как новый SoT.
## 3. Большой репозиторий (критично)
- **Перед изменениями** искать по проекту: похожие компоненты, существующие паттерны, общие утилиты, хуки, UI-абстракции.
- **Не дублировать:** если похожее решение уже есть — расширять или переиспользовать, а не писать с нуля.
## 4. Согласованность между файлами
При правке фичи проверять и при необходимости обновлять **все** связанное: компоненты, хуки, route handlers / server actions, типы, стили/обёртки UI — не только открытый файл.
## 5. Зависимости и влияние
Перед реализацией: от чего зависит целевой файл, где используется, какие побочные эффекты; избегать ломающих изменений без явной необходимости.
## 6. Нативные возможности фреймворка
- Next.js: App Router, Server Components, Server Actions, Route Handlers — по умолчанию предпочтительнее кастомных обходных путей.
- UI: компоненты **shadcn/ui**; не плодить самописное, если стандартное решение покрывает задачу.
## 7. Практики качества
- Рекомендации Next.js: загрузка данных, рендеринг, производительность.
- Минимизировать `'use client'` там, где достаточно серверных компонентов.
- Композиция shadcn/ui и доступность (a11y).
## 8. Валидация каждого изменения (обязательно)
В ответе явно указать:
- **Почему** это согласуется с Next.js, shadcn/ui, CRUD `/servers` и оболочкой Frame+DataGrid.
- **Server vs Client Component** и обоснование.
- Что **переиспользовано** из проекта (паттерны/компоненты).
- Какие **файлы проанализированы**.
## 9. Turbopack и консоль разработки
После правок: проверить сборку/типы (по возможности запустить проверки проекта), ошибки гидратации, предупреждения; критичное исправить; не игнорировать предупреждения о плохих практиках.
## 10. Правки кода
- Минимальные, но **полные** (все зависимые места обновлены).
- Не рефакторить несвязанную логику.
## 11. Формат ответа (обязательная структура)
1. **Шаг 0:** список проанализированных файлов.
2. **Шаг 1:** что изменено.
3. **Шаг 2:** почему (Next.js + shadcn/ui + CRUD `/servers` + Frame+DataGrid + паттерны кодовой базы).
4. **Шаг 3:** анализ влияния (что ещё затронуто).
5. **Шаг 4:** код (диффы или несколько файлов).
6. **Шаг 5:** проверка dev/консоли (ошибки, предупреждения, исправления).
7. **Шаг 6:** опциональные улучшения.
## 12. Дублирование и антипаттерны
Явно указывать, если найдено дублирование или антипаттерн; ссылаться на существующую реализацию в проекте; предлагать переиспользование или рефакторинг вместо копипаста.
## Цель
Вести себя как **senior** в production-кодовой базе: консистентность, переиспользование, CRUD как на `/servers`, оболочка Frame+DataGrid (не Card-shell), безопасные масштабируемые изменения по best practices Next.js и shadcn/ui, готовый к продакшену код без ошибок сборки/рантайма по возможности.
+49
View File
@@ -0,0 +1,49 @@
---
description: "Conventional Commits, semver релизов MikrotikManager и русские git commit message (Generate Commit Message)"
alwaysApply: true
---
# Версионирование релизов
## Generate Commit Message (критично)
Для **Generate Commit Message** в Cursor, предложения subject в чате, `git commit` и любой автогенерации сообщения коммита:
- Формат: `тип(scope): описание на русском` — префикс Conventional Commits и scope **на английском**, текст после `:` **только на русском**.
- Body и footer (если нужны) — **только на русском**.
- В описании после `:` не использовать английские слова: `add`, `update`, `implement`, `rename` и т.п.
- **`feat` не по умолчанию.** Сначала проверь `fix` / `chore` / `ci` / `docs`; `feat` — только новая пользовательская возможность.
- Примеры: `fix(ui): выровнять модалку релиза`; `chore(rules): усилить правила commit message`; `feat(releases): добавить страницу релизов`.
- Детальный чеклист типов — **`commit-messages-ru.mdc`**.
- Версию **не** править вручную в UI, `package.json` и git-тегах; bump делает CI через [`.ci/scripts/compute-release.mjs`](.ci/scripts/compute-release.mjs).
- Базовая линия: `v1.0.0`. Формат отображения: `v1.2.3`.
- **Язык коммитов:** subject и body — **только на русском**; префикс Conventional Commits (`feat`, `fix`, `chore` и т.д.) и scope — на английском.
## Patch (`1.0.x`)
`fix:`, `chore:`, `docs:`, `refactor:`, `style:`, `test:`, `build:`, `ci:`
- Один логический смысл на коммит; subject в повелительном наклонении, до ~72 символов, **на русском**.
- Scope по желанию: `fix(servers):`, `chore(ci):`.
## Minor (`1.x.0`)
`feat:` — новая пользовательская возможность.
## Major (`2.0.0` после `1.x`)
`тип!:`, `тип(scope)!:` или footer `BREAKING CHANGE:` / `BREAKING-CHANGE:` повышают major, обнуляя minor и patch. При смешанной истории major имеет приоритет над minor и patch. Версия, тег, manifest и Docker tags вычисляются CI; package.json вручную не меняется. Для согласованного перехода на вторую основную версию используется `chore(release)!:` с русским описанием.
## Примеры
- `fix(servers): исправить опрос API после таймаута` — patch
- `feat(filters): добавить синхронизацию BGP in` — minor
- `chore(ci): добавить job prepare-release` — хорошо
- `feat: add release modal` — плохо (subject не на русском)
## Антипаттерны
WIP, «update», subject на английском, смешение несвязанных изменений, маскировка фич под `fix`/`chore`, ручной bump в sidebar.
Подробности — раздел CI/CD в `README.md`.
+73
View File
@@ -0,0 +1,73 @@
---
description: Use the ReUI registry (blocks, primitives, icons) correctly
globs: ["**/*.tsx","**/*.ts"]
alwaysApply: false
---
---
name: reui
description: Use the ReUI registry from your AI agent - find, install, and correctly use ReUI components (the 22 free building blocks like data-grid, kanban, filters), their free examples, premium blocks, and Motion Icons. Applies in any project using ReUI, the @reui registry, REUI_LICENSE_KEY, or any shadcn project where the user asks for premium blocks, data grids, kanban boards, dashboards, or full pages. Pairs with the free ReUI MCP server for live, scored registry search and inline component APIs.
user-invocable: false
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
---
> **ReUI skill version `fe6742e29c`.** If the ReUI MCP's `get_agent_skill` reports a newer `version`, re-run the ReUI installer (see `get_agent_skill` -> `install.recommended`) to update this skill. Cloud/tools-only agents have no local file and always read the latest - they can ignore this.
# ReUI for Agents
ReUI is a shadcn-compatible registry. It ships four things you **reuse** - never redesign:
- **components** - the 22 ReUI building blocks with real APIs: `data-grid`, `kanban`, `filters`, `date-selector`, `tree`, `stepper`, ... (free)
- **examples** - free `c-*` single-pattern use-cases of a component (`c-kanban-1`); install one and read it to see exact composition
- **blocks** - premium full-page sections that compose components (`data-grid-2`, `pricing-page-1`); Pro or Ultimate license at install
- **icons** - Motion Icons in 4 styles, static + hover-animated variants; Ultimate license at install
The skill is free and this MCP is free to use; it just needs a ReUI account. On first use your agent opens a browser "Sign in with ReUI" prompt (a free account is created if you don't have one). Free covers components and examples with a daily request allowance; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the limit (see [rules/registry.md](./rules/registry.md)). The same account and skill work in every agent and service the MCP connects to - this skill is agent-agnostic.
Skill + MCP are a team: this skill is the workflow (how to find, install, read the API, and adapt by reuse); the MCP is the live data and the hands (search, get_component, install commands). Your job: find the right item, install it with the shadcn CLI, read its real API, and **adapt by reuse** - wire real data and theme it; do not hand-roll or restyle what ReUI already provides. This skill **layers on the shadcn skill**: follow that for generic rules (spacing, `cn()`, semantic colors, forms); follow this for everything ReUI-specific.
## The core loop (MCP-native)
1. **Find** - call the ReUI MCP `search` tool with the user's intent. It returns a ranked, scored list across components/examples/blocks/icons, each with an `install` command, `previewUrl`, `docsUrl`, and `componentsUsed`. Pass hints (`type`, `component`, `category`, `features`, `free`) when you can infer them.
2. **Install** - run the returned command non-interactively (`npx shadcn@latest add @reui/<name> --yes`). The CLI resolves deps, aliases, and the base/style from `components.json`. See [cli.md](./rules/cli.md).
3. **Read the API (on your base)** - first note your base from `components.json` -> `style` (`base-nova` -> Base UI, `radix-nova` -> Radix UI). For each component an item uses, call `get_component(name)` and read its **inline `api`** (no web fetch); then `get_examples(name)` to install a worked example and copy its composition - the installed files are already in your base. Whenever you work with a component's API, also **share its `docsUrl`** (the primitive's API documentation page) with the user so they have the full reference. See [components.md](./rules/components.md).
4. **Adapt (reuse-first)** - swap demo data for real data, fix icon imports, align tokens. Do not redesign. See [adapting.md](./rules/adapting.md).
**Always show the preview.** Every item a tool returns carries a `previewUrl` (a live preview page). Whenever you list, recommend, or present ReUI items to the user - blocks, components, examples, or icons, whether from `search`, `search_icons`, `list_components`, `compose_page`, or any getter - include each item's `previewUrl` so they can SEE it before installing. Blocks and examples open an individual live preview; icons and components link to their live category/component page. Never present an item without its preview link.
If the ReUI MCP is not configured, fall back to `npx shadcn@latest search @reui -q "..."` then `add` - but the MCP gives scored matches + inline APIs; prefer it.
## Commands
Run ReUI as explicit slash commands (via the ReUI MCP) **or** just ask in plain language - both run the same workflow.
| Command | Invoke | Does |
| ----------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| **build** | `/mcp__reui__build <what>` | Compose a page/section/feature from ReUI: plan → install → read API → adapt → craft → audit. |
| **add** | `/mcp__reui__add <item>` | Find & install one component/example/block/icon and wire it in. |
| **fix** | `/mcp__reui__fix [target]` | Diagnose & fix ReUI usage: wrong/undocumented props, base/radix mismatch, missing states, a11y/scroll. |
| **improve** | `/mcp__reui__improve [target]` | Refine + extend existing ReUI UI to a production-exceptional bar (hierarchy, density, states, responsive, motion). |
Invocation differs slightly per agent (`/mcp__reui__build` in Claude Code/Cursor/Windsurf, `/mcp.reui.build` in VS Code). No command surface? Just describe what you want - this skill drives the identical loop.
## When to reach for ReUI vs plain shadcn
| Need | Reach for |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| A full page or section (dashboard, billing, auth, pricing, settings) | `compose_page` first (plans sections + best blocks), then ReUI **blocks** |
| A data table with sorting/filtering/pagination/virtualization | the **data-grid** component (never hand-roll a `<table>`) |
| A drag-and-drop board | the **kanban** component |
| Advanced column filtering, date range, tree, stepper, ... | the matching ReUI **component** |
| A single generic control already in shadcn (Button, Dialog, Select) | plain **shadcn** |
## Detailed references
- [rules/registry.md](./rules/registry.md) - the four types, the @reui registry, base/radix, free vs premium + license
- [rules/workflow.md](./rules/workflow.md) - the find -> install -> read-API -> adapt loop (most important)
- [rules/components.md](./rules/components.md) - the 22 components, the data-grid contract, base vs radix
- [rules/adapting.md](./rules/adapting.md) - reuse-first: preserve the design (no over-customizing), reuse examples + a block's own elements, real data, don't invent APIs
- [rules/craft.md](./rules/craft.md) - make it exceptional: point of view, hierarchy, density, states, responsive, motion, the bar
- [rules/quality.md](./rules/quality.md) - security, accessibility, and scroll gates (the done gate)
- [rules/styling.md](./rules/styling.md) - ReUI extended tokens, theme adaptation, density
- [rules/icons.md](./rules/icons.md) - portable icons, swapping imports, Motion Icons (static + animated)
- [tools.md](./tools.md) - the ReUI MCP: golden path, the 19 tools, token rules, result shapes, errors
+67
View File
@@ -0,0 +1,67 @@
---
name: reui
description: Use the ReUI registry from your AI agent - find, install, and correctly use ReUI components (the 22 free building blocks like data-grid, kanban, filters), their free examples, premium blocks, and Motion Icons. Applies in any project using ReUI, the @reui registry, REUI_LICENSE_KEY, or any shadcn project where the user asks for premium blocks, data grids, kanban boards, dashboards, or full pages. Pairs with the free ReUI MCP server for live, scored registry search and inline component APIs.
user-invocable: false
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
---
> **ReUI skill version `fe6742e29c`.** If the ReUI MCP's `get_agent_skill` reports a newer `version`, re-run the ReUI installer (see `get_agent_skill` -> `install.recommended`) to update this skill. Cloud/tools-only agents have no local file and always read the latest - they can ignore this.
# ReUI for Agents
ReUI is a shadcn-compatible registry. It ships four things you **reuse** - never redesign:
- **components** - the 22 ReUI building blocks with real APIs: `data-grid`, `kanban`, `filters`, `date-selector`, `tree`, `stepper`, ... (free)
- **examples** - free `c-*` single-pattern use-cases of a component (`c-kanban-1`); install one and read it to see exact composition
- **blocks** - premium full-page sections that compose components (`data-grid-2`, `pricing-page-1`); Pro or Ultimate license at install
- **icons** - Motion Icons in 4 styles, static + hover-animated variants; Ultimate license at install
The skill is free and this MCP is free to use; it just needs a ReUI account. On first use your agent opens a browser "Sign in with ReUI" prompt (a free account is created if you don't have one). Free covers components and examples with a daily request allowance; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the limit (see [rules/registry.md](./rules/registry.md)). The same account and skill work in every agent and service the MCP connects to - this skill is agent-agnostic.
Skill + MCP are a team: this skill is the workflow (how to find, install, read the API, and adapt by reuse); the MCP is the live data and the hands (search, get_component, install commands). Your job: find the right item, install it with the shadcn CLI, read its real API, and **adapt by reuse** - wire real data and theme it; do not hand-roll or restyle what ReUI already provides. This skill **layers on the shadcn skill**: follow that for generic rules (spacing, `cn()`, semantic colors, forms); follow this for everything ReUI-specific.
## The core loop (MCP-native)
1. **Find** - call the ReUI MCP `search` tool with the user's intent. It returns a ranked, scored list across components/examples/blocks/icons, each with an `install` command, `previewUrl`, `docsUrl`, and `componentsUsed`. Pass hints (`type`, `component`, `category`, `features`, `free`) when you can infer them.
2. **Install** - run the returned command non-interactively (`npx shadcn@latest add @reui/<name> --yes`). The CLI resolves deps, aliases, and the base/style from `components.json`. See [cli.md](./rules/cli.md).
3. **Read the API (on your base)** - first note your base from `components.json` -> `style` (`base-nova` -> Base UI, `radix-nova` -> Radix UI). For each component an item uses, call `get_component(name)` and read its **inline `api`** (no web fetch); then `get_examples(name)` to install a worked example and copy its composition - the installed files are already in your base. Whenever you work with a component's API, also **share its `docsUrl`** (the primitive's API documentation page) with the user so they have the full reference. See [components.md](./rules/components.md).
4. **Adapt (reuse-first)** - swap demo data for real data, fix icon imports, align tokens. Do not redesign. See [adapting.md](./rules/adapting.md).
**Always show the preview.** Every item a tool returns carries a `previewUrl` (a live preview page). Whenever you list, recommend, or present ReUI items to the user - blocks, components, examples, or icons, whether from `search`, `search_icons`, `list_components`, `compose_page`, or any getter - include each item's `previewUrl` so they can SEE it before installing. Blocks and examples open an individual live preview; icons and components link to their live category/component page. Never present an item without its preview link.
If the ReUI MCP is not configured, fall back to `npx shadcn@latest search @reui -q "..."` then `add` - but the MCP gives scored matches + inline APIs; prefer it.
## Commands
Run ReUI as explicit slash commands (via the ReUI MCP) **or** just ask in plain language - both run the same workflow.
| Command | Invoke | Does |
| ----------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| **build** | `/mcp__reui__build <what>` | Compose a page/section/feature from ReUI: plan → install → read API → adapt → craft → audit. |
| **add** | `/mcp__reui__add <item>` | Find & install one component/example/block/icon and wire it in. |
| **fix** | `/mcp__reui__fix [target]` | Diagnose & fix ReUI usage: wrong/undocumented props, base/radix mismatch, missing states, a11y/scroll. |
| **improve** | `/mcp__reui__improve [target]` | Refine + extend existing ReUI UI to a production-exceptional bar (hierarchy, density, states, responsive, motion). |
Invocation differs slightly per agent (`/mcp__reui__build` in Claude Code/Cursor/Windsurf, `/mcp.reui.build` in VS Code). No command surface? Just describe what you want - this skill drives the identical loop.
## When to reach for ReUI vs plain shadcn
| Need | Reach for |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| A full page or section (dashboard, billing, auth, pricing, settings) | `compose_page` first (plans sections + best blocks), then ReUI **blocks** |
| A data table with sorting/filtering/pagination/virtualization | the **data-grid** component (never hand-roll a `<table>`) |
| A drag-and-drop board | the **kanban** component |
| Advanced column filtering, date range, tree, stepper, ... | the matching ReUI **component** |
| A single generic control already in shadcn (Button, Dialog, Select) | plain **shadcn** |
## Detailed references
- [rules/registry.md](./rules/registry.md) - the four types, the @reui registry, base/radix, free vs premium + license
- [rules/workflow.md](./rules/workflow.md) - the find -> install -> read-API -> adapt loop (most important)
- [rules/components.md](./rules/components.md) - the 22 components, the data-grid contract, base vs radix
- [rules/adapting.md](./rules/adapting.md) - reuse-first: preserve the design (no over-customizing), reuse examples + a block's own elements, real data, don't invent APIs
- [rules/craft.md](./rules/craft.md) - make it exceptional: point of view, hierarchy, density, states, responsive, motion, the bar
- [rules/quality.md](./rules/quality.md) - security, accessibility, and scroll gates (the done gate)
- [rules/styling.md](./rules/styling.md) - ReUI extended tokens, theme adaptation, density
- [rules/icons.md](./rules/icons.md) - portable icons, swapping imports, Motion Icons (static + animated)
- [tools.md](./tools.md) - the ReUI MCP: golden path, the 19 tools, token rules, result shapes, errors
+43
View File
@@ -0,0 +1,43 @@
# Adapting installed ReUI code (reuse-first, no AI slop)
ReUI items ship production-quality. Your job is to **adapt by reuse** - wire real data and fit the app - not to redesign or hand-roll. The output should look like ReUI built it for this product.
## Preserve the design - don't over-customize
The design IS the product. A ReUI block/component encodes senior-designer decisions: spacing, hierarchy, density, color treatment, and component choices. The fastest way to turn a premium block back into generic AI slop is to "improve" its look - so don't.
- Change **data, copy, and props**; keep the **structure and styling** it ships with. Make the **smallest** change that wires the real data. If your diff touches `className` / JSX structure more than data / props, you are over-customizing - stop and reuse.
- Don't swap ReUI components for hand-rolled ones, restructure the layout, re-skin spacing / radius / colors, or add decorative chrome. Let the installed components carry the default spacing, radius, sizing, icon rhythm, density, and state styling; add custom Tailwind only when a component genuinely lacks a contract you need.
- Want a different look? `search` for a block whose design already fits and reuse that - don't restyle this one into a new design.
## Reuse the parts: examples and the block's own elements
- **Examples are building parts.** A free `c-*` example is a correct, single-pattern composition you can reuse. Before composing from scratch, `get_examples(component)`, install the closest one, and reuse its wiring - assemble UI from examples instead of hand-rolling what an example already shows.
- **Reuse a block's own elements.** Need more rows, cards, items, or sections than ship by default? Repeat the block's **existing** element by mapping real data through the same markup - never invent parallel markup that drifts from its design. Need a variant (empty / loading / expanded)? Derive it from an element the block already has.
## Don't invent (read, don't guess)
- Never write a prop, variant value, import path, or `@reui/...` name you didn't read in a component's inline `api`, an installed example, or a `search` result. If you didn't see it, treat it as nonexistent - call `get_component` / `get_examples` / `search` first, or run the MCP `validate_usage` tool to check planned names + props against the docs before writing code.
- If a getter returns `found: false` or `search` returns nothing, say so and fall back (plain shadcn, or ask) - never fabricate an install command or an API.
## What to change vs leave alone
- **Change:** the item's own data, copy, props, and layout to fit the app.
- **Leave alone:** installed component files, hooks, and the shared theme - do not edit vendored ReUI internals; change behavior through props and the documented API.
- Blocks are **portable React** - no `next/link`, `next/image`, or other framework-runtime imports inside them. Keep them portable.
## Demo data -> real data
- Replace every placeholder with the user's real data. Model it as **typed data structures** and **map over arrays** - never duplicate JSX per row/card. Keep small block-specific formatters next to the data.
- Wire the real source (columns, fields, fetch). For `data-grid`, implement the server fetch contract if the user needs server-side data.
- **Type from the component API, derive during render.** Type domain state through the component's own types - e.g. map status to `BadgeProps["variant"]` via a typed `Record<Status, …>` - instead of stringly-typed values. Compute view state during render; don't mirror derived data into `useState`/`useEffect`.
- **Adapt on the right base.** Use the API for the project's base (Base UI vs Radix - see [components.md](./components.md)); the installed files are already base-correct, so reuse their shape rather than translating from memory.
## Believable content (no AI tells)
- Use realistic labels, counts, timestamps, and statuses that map to a real workflow.
- No decorative buttons, fake tabs, meaningless toggles, equal-weight card walls, empty gradients, ornamental icons, or generic SaaS filler. Every element should do something.
## Operational surfaces (settings / profile / admin)
Pick ONE archetype and keep the family consistent: a vertical rail (3-6 sections), horizontal tabs (5-8), or a frame/stack. Prefer `frame` for tool-like surfaces, a card for profile-like ones. Don't mix archetypes in one surface.
+60
View File
@@ -0,0 +1,60 @@
# CLI: registry setup, license, non-interactive install
## Registry setup (one-time, per project)
Free items (the 22 components and all `c-*` examples) need only the plain string registry in `components.json`:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium items (blocks; Motion Icons and templates) require a ReUI license at install:
1. Add the key to `.env.local`:
```bash
REUI_LICENSE_KEY=your-license-key
```
2. Switch `components.json` to the authenticated object form:
```json
{
"registries": {
"@reui": {
"url": "https://reui.io/r/{style}/{name}.json",
"headers": { "Authorization": "Bearer ${REUI_LICENSE_KEY}" }
}
}
}
```
The shadcn CLI expands `${REUI_LICENSE_KEY}` from `.env.local` inside `components.json`, but an MCP client config never expands variables, so a ReUI MCP server config must carry the raw token instead (for example `reui_pat_your_token_here`).
The MCP `get_project_context` tool returns the right config. Full guide: https://reui.io/docs/registry?ref=skill
## Installing
Use the project's package runner (check `packageManager`):
```bash
npx shadcn@latest add @reui/<name> --yes # npm
pnpm dlx shadcn@latest add @reui/<name> --yes # pnpm
bunx --bun shadcn@latest add @reui/<name> --yes # bun
```
`--yes` skips confirmation prompts. The CLI auto-detects the package manager from the lockfile (there is no `--package-manager` flag). It also resolves the correct base+style variant from `components.json`, so do not pass a style.
## Handling prompts and conflicts
- **Always pass `--yes`** so the CLI does not block on confirmation prompts.
- **Do NOT pass `--overwrite` by default.** If the CLI reports an existing file, read the output and resolve deliberately: install under a different name, adjust the path, or ask the user. Only use `--overwrite` when the user explicitly wants to replace a file.
- **Preview first when touching an existing project**: `npx shadcn@latest add @reui/<name> --dry-run` shows what would change; `--diff <file>` shows a specific file's diff. Use these before overwriting.
- Run from the **project root** so `components.json` and `.env.local` are found.
## Free vs premium boundary
- Public, no key: `c-*` examples and the 22 components (`@reui/data-grid`, `@reui/badge`, ...) that those examples depend on.
- Key required at install: blocks (`@reui/<category>-N`) need a Pro or Ultimate license; Motion Icons (`@reui/icons/...`) and templates need Ultimate.
If an install 401/403s, the license key is missing, invalid, or the plan does not cover that resource (blocks: Pro or higher; icons and templates: Ultimate). Point the user to https://reui.io/account?ref=skill (their key) or https://reui.io/pricing?ref=skill (upgrade).
+428
View File
@@ -0,0 +1,428 @@
# ReUI components
The 22 ReUI building blocks: `alert`, `autocomplete`, `badge`, `cascader`, `code-block`, `data-grid`, `date-selector`, `event-calendar`, `filters`, `frame`, `gantt`, `icon-stack`, `icon-tile`, `kanban`, `number-field`, `phone-input`, `rating`, `scrollspy`, `sortable`, `stepper`, `timeline`, `tree`. Examples and blocks are composed from these.
**Rule one: never guess a component's API. Read it first.** Call **`get_component(name)`** for its inline `api` (props + usage, no web fetch), and **share the result's `docsUrl`** (the component's API documentation page) with the user whenever you work with that component's API, so they have the full reference (the `/llms.txt` index is a further fallback). Then call **`get_examples(name)`** to install a worked example and copy real composition. The contracts below are first-try orientation (required props, composition shape, the one gotcha); the inline `api` is the full reference. No single block fits? Compose: search the components you need, read each `get_component`, install a `get_examples` example per component, and adapt.
## data-grid (the flagship - read its API every time)
`data-grid` wraps TanStack Table v9. It is NOT a styled `<table>` and does NOT take `data`/`columns` props directly. The contract:
- Build a TanStack table instance with `useTable({ features: dataGridFeatures, ... })` (columns, data). `dataGridFeatures` is exported by the primitive and already bundles sorting, filtering, pagination, row selection, expanding, pinning, resizing and faceting, so there are no per-table row models to wire.
- Pass that instance to `<DataGrid table={table} recordCount={total}>`.
- Compose the body with `DataGridTable` inside `DataGrid`, and enable features through `tableLayout` (e.g. `{ headerSticky: true, columnsResizable: true }`), not ad-hoc classes.
- Server-side data uses the documented fetch shape (`recordCount` is the total for pagination).
```tsx
const table = useTable({
features: dataGridFeatures,
data,
columns,
})
<DataGrid table={table} recordCount={data.length}>
<DataGridTable />
</DataGrid>
```
Common mistakes:
- **Incorrect:** `<DataGrid data={rows} columns={cols} />` - these props do not exist. **Correct:** build a `useTable({ features: dataGridFeatures, ... })` instance and pass `table={table}` + `recordCount`.
- **Incorrect:** a raw `<table>` / hand-rolled pagination. **Correct:** use `data-grid`; read its API for sticky header, pagination, virtualization, row selection.
- **Incorrect:** styling rows/cells with arbitrary classes. **Correct:** drive layout via `tableLayout` and the primitive's `DataGridColumnMeta` (e.g. `cellClassName`, `headerTitle`), set through the bundle's `columnMeta` slot.
## event-calendar
**Required:** events via `events`/`onEventsChange` (controlled) or `defaultEvents` (uncontrolled), plus a height on the root.
**Shape:**
```tsx
<EventCalendar defaultEvents={events} defaultView="month" className="h-[560px]">
<EventCalendarNav />
<EventCalendarContent />
</EventCalendar>
```
**Gotcha:** headless-first: `EventCalendarContent` renders the active view (month/week/day/days/agenda; a resource view activates when `resources` is passed) - there is no per-view JSX to compose. Events are `{ id, title, start, end (exclusive), allDay?, color?, recurrence?, resourceId? }`. Mutations flow through `onEventUpdate`/`canDropEvent` (return `false` to reject); the root needs an explicit height because it is a min-h-0 flex column.
## gantt
**Required:** `resources` (the left tree) plus bars via `events`/`defaultEvents` attached by `resourceId`.
**Shape:**
```tsx
<Gantt defaultEvents={bars} resources={tasks} defaultScale="month" className="h-[480px]">
<GanttNav />
<GanttView />
</Gantt>
```
**Gotcha:** bars move along the time axis only (never across rows) and are all-day spans with exclusive `end`; `progress` is 0-100. Scales are `day | week | month | quarter | year`. Zoom control, infinite scroll, summary rollups, and row checkboxes are ON by default - turn off what you do not need. Same `onEventUpdate`/`canDropEvent` commit pipeline as `event-calendar`; the root needs an explicit height.
## kanban
**Required:** `value` (`Record<string, T[]>`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Kanban value={cols} onValueChange={setCols} getItemValue={(i) => i.id}>
<KanbanBoard>
{Object.entries(cols).map(([id, items]) => (
<KanbanColumn key={id} value={id}>
<KanbanColumnHandle><h3>{id}</h3></KanbanColumnHandle>
<KanbanColumnContent value={id}>
{items.map((i) => (
<KanbanItem key={i.id} value={i.id}>
<KanbanItemHandle>{i.title}</KanbanItemHandle>
</KanbanItem>
))}
</KanbanColumnContent>
</KanbanColumn>
))}
</KanbanBoard>
<KanbanOverlay><div className="bg-muted size-full rounded-md" /></KanbanOverlay>
</Kanban>
```
**Gotcha:** state is `Record<columnId, T[]>`. Each `KanbanColumnContent value` must match its parent `KanbanColumn value`. Omit `KanbanOverlay` and the drag preview silently breaks.
## sortable
**Required:** `value` (`T[]`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Sortable value={items} onValueChange={setItems} getItemValue={(i) => i.id}>
{items.map((i) => (
<SortableItem key={i.id} value={i.id}>
<SortableItemHandle><GripVertical /></SortableItemHandle>
{i.label}
</SortableItem>
))}
</Sortable>
```
**Gotcha:** a flat 1D reorder list (not columns - that is `kanban`). `getItemValue` must return a stable, unique string. Pass `layout="grid"` or `layout="nested"` for non-list layouts.
## filters
**Required:** `fields` (`FilterField[]`). The value is ONE `FilterQuery` tree - `query` + `onQueryChange`, or uncontrolled `defaultQuery`.
**Shape:**
```tsx
const fields: FilterField[] = [
{ id: "title", label: "Title", type: "text" },
{
id: "status",
label: "Status",
type: "select",
options: [
{ value: "active", label: "Active" },
{ value: "archived", label: "Archived" },
],
},
]
const [query, setQuery] = useState<FilterQuery>(() => createFilterQuery())
<Filters fields={fields} query={query} onQueryChange={setQuery} />
```
**Gotcha:** the state is a TREE, not a list of chips. `FilterQuery` is a group of rules joined by `and`/`or` and a group may hold another group, so `(A and B) or C` is expressible; a rule is `{ id, type: "rule", path: ["status"], operator, value }` and `path` is the whole nested attribute path, root first. The pre-rewrite API is GONE: there is no `filters`/`onChange` prop, no `FilterFieldConfig` (fields are `FilterField`, nested through their own `fields`, keyed `id` not `key`), and no `createFilter()` - it minted ids inside a pure function and broke hydration, so ids now come from `createFilterIdFactory(seed)` seeded off `useId`, and `createFilterQuery()` / `createFilterRule()` take one. Read the query back with `flattenFilterConditions` (`{ path, field, operator, values, negated }` per rule, incomplete rules skipped) and walk the tree yourself when the parentheses carry meaning - the primitive compiles nothing, no SQL, no query string.
`variant` picks the chrome over that one query: `"basic"`, the default, is the flat chip row for a toolbar over a table; `"advanced"` is the condition builder, hung off a trigger or rendered in place with `advancedMode="inline"`. Both read and write the same tree, so a saved view built in one opens in the other. Other props worth knowing before you hand-roll them: `size` is two rungs, `"sm" | "default"`, resolved per style (there is no `lg`); `reorderable` turns on drag and Alt+Arrow row moves in the builder; `onBeforeQueryChange` is the ONE veto point for every write (return `false` to refuse, it cannot rewrite); `editors` registers custom value editors a field selects by `editor` name; `labels` / `operatorLabels` own every rendered string; `pathCollapse` + `maxPathSegments` shorten deep attribute paths; `renderChip` / `renderValue` / `renderEmpty` replace rendered parts. On a field, `loadOptions` supplies async options with paging and `resolveValues` renders a chip restored from a saved view whose option was never loaded. Pairs naturally with `data-grid`.
## cascader
**Required:** `items` (a tree of `{ value, label, children? }`), plus the panel parts inside `CascaderContent`.
**Shape:**
```tsx
<Cascader items={items} value={value} onValueChange={setValue}>
<CascaderTrigger render={<Button variant="outline" />}>
<CascaderValue placeholder="Select an attribute" />
</CascaderTrigger>
<CascaderContent className="w-80">
<CascaderPanel>
<CascaderNav>
<CascaderBreadcrumb />
<CascaderInput />
</CascaderNav>
<CascaderEmpty />
<CascaderList maxHeight={288}>
<CascaderItems />
</CascaderList>
<CascaderStatus />
</CascaderPanel>
</CascaderContent>
</Cascader>
```
**Gotcha:** pressing a branch NAVIGATES, it does not select - only leaves are selectable until you pass `selectable="any"` or a predicate, and once a branch is selectable its chevron becomes the only way to open it. `CascaderInput` must stay inside `CascaderContent` (Base UI refills the query from the selection when the input sits outside the popup). Always include `CascaderStatus`: it is the live region announcing level changes, which the visual breadcrumb does not provide to screen readers. Accepts a flat adjacency list via `getParent` as well as nested `children`. `searchScope="deep"` searches every level and annotates results with their path; `multiple` gives checkbox rows; `inline` + a bare `CascaderPanel` embeds it with no popover.
The shape above is `mode="drill"`, the default. `mode="tree"` keeps the same parts (drop `CascaderBreadcrumb`, pass `showBack={false}`, drive expansion with `expanded`/`onExpandedChange`); `mode="columns"` REPLACES `CascaderList` + `CascaderItems` with a single `CascaderColumns`, and has no breadcrumb. Other props worth knowing before you hand-roll them: `cascade` (multi-select only, parent/child selection with indeterminate branches - pair it with `selectable="any"`, since a leaf-only tree can never cascade), `indicator={false}` to drop the single-select check and its gutter (visual only, no-op with `multiple`), `virtualize`/`virtualizeThreshold` plus `CascaderVirtualItems` for long levels, and `getChildren` for async levels with cursor paging, retry on failure and optional `prefetch`. `CascaderFooter` pins commands below the list (`actions` is the quick path) and `CascaderSubmenu` opens one as a side-anchored flyout with the full menu keyboard model. To head a run of rows use `CascaderGroup` wrapping a `CascaderLabel` - a bare label inside a listbox names nothing and is dropped from the accessibility tree - and `CascaderSeparator` for the rule between runs. Every rendered string comes from `labels`, and the panel is RTL-correct under a `DirectionProvider` or `dir="rtl"`.
## date-selector
**Required:** none, but wire `onChange` to capture the value.
**Shape:**
```tsx
const [value, setValue] = useState<DateSelectorValue | undefined>()
<DateSelector value={value} onChange={setValue} label="Due date" />
```
**Gotcha:** the value is a structured `DateSelectorValue` (period / operator / start+end dates), NOT a `Date` - never pass a raw `Date`. Use `allowRange={false}` to lock single-date picking. Read `get_component("date-selector")` for the value shape.
## tree
**Required:** `tree` (a `@headless-tree/core` instance you construct)
**Shape:**
```tsx
<Tree tree={tree}>
{tree.getItems().map((item) => (
<TreeItem key={item.getId()} item={item}>
<TreeItemLabel />
</TreeItem>
))}
</Tree>
```
**Gotcha:** `Tree` is a styled shell - it takes a headless-tree instance via `tree`, NOT `data`/`items` props. Build the instance with `@headless-tree/react`. External API: https://headless-tree.lukasbach.com/
## stepper
**Required:** `StepperItem step` (number), `StepperContent value` (number)
**Shape:**
```tsx
<Stepper defaultValue={1}>
<StepperNav>
<StepperItem step={1}>
<StepperTrigger><StepperIndicator>1</StepperIndicator></StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger><StepperIndicator>2</StepperIndicator></StepperTrigger>
</StepperItem>
</StepperNav>
<StepperPanel>
<StepperContent value={1}>Step 1 content</StepperContent>
<StepperContent value={2}>Step 2 content</StepperContent>
</StepperPanel>
</Stepper>
```
**Gotcha:** steps are 1-indexed. Without `StepperPanel` + `StepperContent` you render the nav trail but no body. Put `StepperSeparator` in every `StepperItem` except the last.
## timeline
**Required:** `TimelineItem step` (number)
**Shape:**
```tsx
<Timeline>
<TimelineItem step={1}>
<TimelineHeader>
<TimelineDate>March 2024</TimelineDate>
<TimelineTitle>Project initialized</TimelineTitle>
</TimelineHeader>
<TimelineIndicator />
<TimelineSeparator />
<TimelineContent>Repo and architecture set up.</TimelineContent>
</TimelineItem>
</Timeline>
```
**Gotcha:** each item needs a unique `step`. `orientation` is `"vertical"` (default) or `"horizontal"`. This is a static event display, not interactive like `stepper`.
## autocomplete
**Required:** `items` (array; each item has at least `value`)
**Shape:**
```tsx
<Autocomplete items={items}>
<AutocompleteInput placeholder="Search..." />
<AutocompleteContent>
<AutocompleteEmpty>No results found.</AutocompleteEmpty>
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.value} value={item}>{item.label}</AutocompleteItem>
)}
</AutocompleteList>
</AutocompleteContent>
</Autocomplete>
```
**Gotcha:** `AutocompleteList` takes a render-prop `(item) => ReactNode`, NOT a mapped array of children. External API: https://base-ui.com/react/components/autocomplete
## phone-input
**Required:** none, but wire `onChange`.
**Shape:**
```tsx
<PhoneInput placeholder="Enter phone number" defaultCountry="US" value={value} onChange={setValue} />
```
**Gotcha:** `value`/`onChange` use an E.164 string (e.g. `"+14155551234"`), not a display-formatted string; `onChange` can fire `undefined`. `defaultCountry` is a 2-letter ISO code. Wraps `react-phone-number-input`.
## number-field
**Required:** wrap the controls in `NumberFieldGroup`.
**Shape:**
```tsx
<NumberField defaultValue={0}>
<NumberFieldScrubArea label="Quantity" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
```
**Gotcha:** import from `@/components/ui/number-field`. The accessible label goes on `NumberFieldScrubArea`, not `NumberField`. External API: https://base-ui.com/react/components/number-field
## rating
**Required:** `rating` (number)
**Shape:**
```tsx
<Rating rating={4.5} showValue editable onRatingChange={setRating} />
```
**Gotcha:** supports decimals (partial stars). Pass `editable` + `onRatingChange` for interactive input; omit both for a read-only display.
## scrollspy
**Required:** `targetRef` (the scroll container ref)
**Shape:**
```tsx
<Scrollspy targetRef={containerRef}>
<a href="#s1" data-scrollspy-anchor="s1">Section 1</a>
<a href="#s2" data-scrollspy-anchor="s2">Section 2</a>
</Scrollspy>
<div ref={containerRef}>
<div id="s1">...</div>
<div id="s2">...</div>
</div>
```
**Gotcha:** each link's `data-scrollspy-anchor` must match a section `id`. `targetRef` is the scrollable container (defaults to the window).
## frame
**Required:** `Frame` > `FramePanel`
**Shape:**
```tsx
<Frame>
<FramePanel>
<FrameHeader>
<FrameTitle>Title</FrameTitle>
<FrameDescription>Description</FrameDescription>
</FrameHeader>
<div className="p-5">Content</div>
<FrameFooter>Footer</FrameFooter>
</FramePanel>
</Frame>
```
**Gotcha:** a structured card shell for tool-like surfaces. `stacked` connects multiple panels with shared borders; `dense` removes panel padding; radius via the `--frame-radius` CSS variable.
## icon-stack
**Required:** one child icon
**Shape:**
```tsx
<IconStack aria-hidden="true">
<InboxIcon className="size-4" />
</IconStack>
```
**Gotcha:** isometric layered artwork for empty states and illustrations; style the inner icon via its own `className`. Mark purely decorative stacks `aria-hidden="true"` and keep the real label in surrounding copy.
## icon-tile
**Required:** one child icon
**Shape:**
```tsx
<IconTile variant="elevated" size="lg">
<PackageIcon />
</IconTile>
```
**Gotcha:** the square container an icon sits in, so every list row, feature card and empty state shares one affordance. `variant`: `outline` (default) | `elevated` (muted fill, raised ring) | `soft` (tinted nested, tone from currentColor) | `solid` (filled tone, contrasting glyph) | `frame` (double container). `soft` and `solid` retint from one text color class (they default to `text-primary`). `size`: `xs | sm | default | lg | xl` (24/32/40/48/64px tile, glyph scales 12/14/16/20/24px). `radius`: `default | full`. Do not set a `size-*` class on the child icon unless you mean to override the tile's glyph size; recolor with `className` on the tile, not the icon.
## code-block
**Required:** `code` + `language`, or pre-highlighted `lines`.
**Shape:**
```tsx
<CodeBlock code={code} language="tsx" />
<CodeBlock code={code} language="tsx" showLineNumbers maxLines={20}>
<CodeBlockHeader>
<CodeBlockTitle>use-totals.ts</CodeBlockTitle>
<CodeBlockLanguage />
<CodeBlockCopyButton className="ml-auto" />
</CodeBlockHeader>
<CodeBlockExpandButton />
</CodeBlock>
```
**Gotcha:** the one-liner is already a complete block; every child (header, title, language label, copy button, wrap toggle, expand button, line actions) is optional chrome, at any depth. Shiki is the only npm dependency and loads lazily, one chunk per language, on first highlight; `lines` (from `highlightCode` in `code-block-highlight`, which is server-safe and has no `"use client"`) or `highlight={false}` loads nothing at all. `maxLines` caps the height AND marks the block collapsible, which is what makes `CodeBlockExpandButton` appear. Use `variant="ghost"` when the block sits inside a surface that already has a border.
## alert
**Required:** `Alert` > `AlertTitle`
**Shape:**
```tsx
<Alert variant="success">
<ShieldCheckIcon />
<AlertTitle>Security update</AlertTitle>
<AlertDescription>Enable two-factor authentication.</AlertDescription>
<AlertAction><Button size="xs">Update</Button></AlertAction>
</Alert>
```
**Gotcha:** shadcn-compatible API. `variant`: `default | destructive | info | success | warning | invert`. The non-default variants use ReUI extended color tokens (`--success`/`--info`/`--warning`/`--invert`), which the install adds. Defer generic alert rules to the shadcn skill.
## badge
**Required:** none (text child).
**Shape:**
```tsx
<Badge variant="success-light" size="sm">Success</Badge>
<Badge variant="outline" radius="full">Pill</Badge>
```
**Gotcha:** shadcn-compatible. Rich `variant` set (solid, `-outline`, `-light` per color), `size` `xs..xl`, `radius` `default | full`. Like `alert`, the color variants rely on ReUI extended tokens. Prefer `Badge` variants over raw color classes for statuses.
## base vs radix - write for the project's base
ReUI ships every component in two builds: `base` (Base UI) and `radix` (Radix UI). The install command and name are identical, and the CLI installs the build matching the project. But you must write/adapt code against the **right base**, because their APIs differ.
**Detect the base first.** Read `components.json` -> `style` and take the segment before the first `-`:
- `"style": "base-nova"` -> **Base UI**
- `"style": "radix-nova"` -> **Radix UI**
**Then use that base's API.** The deltas mirror shadcn's base-vs-radix split:
- Slot/composition: Base UI `render={<… />}` vs Radix `asChild`.
- `Select`: Base UI takes `items`; Radix uses `<SelectItem>` children.
- `ToggleGroup`: Base UI `multiple` boolean vs Radix `type="single" | "multiple"`.
The safest path is to **read the installed files and `c-*` examples** - they're already in your base, so reuse their wiring instead of guessing. When `get_component`'s inline `api` or an example shows the other base's shape, translate it to your base (or `validate_usage` to confirm). Defer the generic base/radix mechanics to the shadcn skill.
+45
View File
@@ -0,0 +1,45 @@
# Craft: make ReUI UI exceptional, not generic
ReUI items ship senior-designer quality. Your adaptation has to hold that bar, so the result reads like a real product surface a team would keep - not a wireframe an AI generated. Use these alongside the reuse rules in [adapting.md](./adapting.md).
## Have a point of view
Pick an emotional register before you compose - calm, operational, premium, editorial, dense, energetic - and let layout, spacing, surface treatment, and icon behavior all reinforce it. One or two memorable decisions and restraint everywhere else beats ten generic ones. UI with no point of view reads as generated.
## Brutally clear hierarchy
One focal point per card or panel: the dominant metric or task first, its label second, supporting detail third. The first thing the eye lands on should be the right thing; secondary text must read as secondary. Borders, separators, and surfaces do real work to create 2-3 information bands - don't flatten everything to equal weight.
## Spacing rhythm and deliberate density
Gaps are a signal, not a default. Keep them intentional and consistent within a family (`gap-1`/`gap-2` for tight operational rows, larger gaps for section breaks), and smaller within a group than between groups. Match the surrounding ReUI density; don't pad an operational surface like a marketing page, and don't drift density mid-section. The composition should still feel authored in grayscale.
## Cover the real states (the usual miss)
A surface isn't done at the happy path. Compose, and wire:
- **Empty** - a purposeful empty state (short message + the primary action), never a blank panel.
- **Loading** - a **skeleton** that matches the real layout, not a centered spinner.
- **Error** - an inline, recoverable error with a retry, announced via `role="status"`/`aria-live`.
Derive these from an element the block already has (don't invent parallel markup), or `get_examples` for a state-specific example.
## Responsive by default
Mobile-first, not mobile-afterthought. In constrained rows/cards/sidebars, put `min-w-0` on the shrinking container and `truncate` long single-line labels; protect the primary label's width and let secondary content compress. Reflow layouts (multi-column -> single column) rather than just shrinking them. Desktop and mobile should both look designed.
## Motion, subtly
Motion should clarify, not decorate. Use ReUI Motion Icons on primary actions for a subtle hover cue; keep transitions short (~200-300ms) with calm easing; prefer a skeleton pulse over a spinner. No bouncing, no gratuitous entrance animations on every element.
## Real, activated content
Use believable, typed data (realistic labels, counts, timestamps, statuses that map to a real workflow) - never lorem or abstract filler. Every visible control does something: no decorative buttons, fake tabs, meaningless toggles, or stats with no job. It must still hold with long names, empty values, and crowded data.
## Avoid the AI tells
These instantly read as generated - don't ship them: equal-weight card walls, empty gradients, repetitive padding everywhere, generic enterprise copy, ornamental icons, and number tiles that don't earn their place.
## The bar
Before you finish, ask: **would a product team keep this instead of replacing it? Does it still feel strong after swapping in real content?** If not, reuse the shipped ReUI design harder - don't restyle it into something new - then run the [quality.md](./quality.md) gates.
+39
View File
@@ -0,0 +1,39 @@
# Icons (ReUI delta over shadcn)
Follow the shadcn icon rules (use the project's configured `iconLibrary`, `data-icon` on icons inside `Button`, no sizing classes on icons inside components, pass icons as component objects not string keys). ReUI adds the following.
## Portable icons (library-agnostic)
ReUI components, examples, and blocks are authored to be icon-library-agnostic. When `iconLibrary` is set in `components.json`, the shadcn CLI installs each item's icons in **your** library automatically - you swap nothing. If an installed item's icons don't match your project (for example `iconLibrary` isn't set, so they came in from the item's demo library), change the **import source and component name** to your library, keeping the same icon-name semantics:
- `lucide` -> `lucide-react`
- `tabler` -> `@tabler/icons-react`
- `phosphor` -> `@phosphor-icons/react`
- `remix` -> `@remixicon/react`
- `hugeicons` -> `@hugeicons/react`
Don't assume `lucide-react`; read `iconLibrary` from `components.json`.
## Keep icons purposeful
Icons support the hierarchy, they don't replace it: keep them small, matched to the surrounding density, and decorative ones `aria-hidden="true"` (an icon-only control still needs an accessible label on the control). Don't add ornamental icons that do no job.
## Motion Icons (the `@reui/icons/...` set)
ReUI ships its own icon set in 4 styles (outline, solid, duotone, filled), each icon in two variants:
```bash
npx shadcn@latest add @reui/icons/default/<style>/<name> --yes # static
npx shadcn@latest add @reui/icons/animated/<style>/<name> --yes # hover-animated (motion/react)
```
Finding them via the MCP is free; installing requires an Ultimate license (`REUI_LICENSE_KEY`, see [cli.md](./cli.md)). Reach for a Motion Icon on a primary action when a subtle hover cue helps; keep motion restrained.
Finding icons:
- Several icons (the common case): **`search_icons(concepts[])`** - up to 24 concepts in one call, the best icons per concept with install commands. Pass `animated: true` to get only icons with a hover-animated Motion variant.
- One icon: `search` with `type: "icon"`.
- Icon results and `get_icon` carry `animated: true` and `installAnimated` when an animated variant exists - use those install strings, do not construct paths by hand.
- Every icon result carries a `previewUrl` (its live icon-category page) - **share it with the user** so they can SEE the icon before installing.
The `icon-stack` component composes multiple icons into a stacked display.
+22
View File
@@ -0,0 +1,22 @@
# Quality gates (security, accessibility, scroll)
These are the **done gate**, not a nice-to-have: before you call any ReUI work finished, call the MCP `get_audit_checklist` tool and pass every item below (plus the craft bar in [craft.md](./craft.md)). Then typecheck and lint.
## Security
- Never `dangerouslySetInnerHTML`. Render data as text/components.
- External links (`target="_blank"`) must always pair `rel="noopener noreferrer"`.
- No real PII, secrets, or tokens in demo or committed code. Remote media only from sources the project already allows.
## Accessibility
- Implicit list/card items that navigate get real anchors with a standard hover affordance.
- Icon-only or numeric buttons need an `aria-label`; decorative icons get `aria-hidden`.
- Every non-submit button is `type="button"`.
- Keyboard + focus: everything interactive is reachable in a sensible Tab order with a visible focus ring; layers (dialogs/sheets/menus) trap focus and close on `Escape`. ReUI components ship standard keyboard behavior - read each component's inline `api` rather than re-implementing it.
- Announce async UI: loading and error messages use `role="status"` / `aria-live` so they're not silent to screen readers.
## Scroll mechanics
- Make scroll regions with a parent-owned height: a `min-h-0` + flex chain down to the scroll container. Never guess a `max-h`.
- The scroll container owns `overflow-auto`; ancestors stay `min-h-0` so the height resolves.
+35
View File
@@ -0,0 +1,35 @@
# ReUI registry structure
ReUI is a shadcn-compatible registry with four entity types. **Examples and blocks are built FROM components** - reuse them, don't rebuild.
- **component** - one of the 22 ReUI building blocks with a real API (`data-grid`, `kanban`, `filters`, `date-selector`, `tree`, ...). Install directly (`@reui/data-grid`) or let it come in as a dependency of an example/block. Free. Read its API with `get_component(name)`.
- **example** - a free `c-*` single-pattern use-case of a component (`c-kanban-1`, `c-data-grid-3`). Install one and read it to copy real composition. Find a component's examples with `get_examples(name)`.
- **block** - a premium, full-page section that composes several components (`data-grid-2`, `pricing-page-1`). Pro or Ultimate license at install. Adapts to your active theme via semantic tokens.
- **icon** - Motion Icons in 4 styles (outline, solid, duotone, filled), static (`@reui/icons/default/<style>/<name>`) and hover-animated (`@reui/icons/animated/<style>/<name>`). Ultimate license at install. See [icons.md](./icons.md).
## The @reui registry
Install everything through the shadcn CLI: `npx shadcn@latest add @reui/<name> --yes`. The CLI reads the `@reui` registry from the project's `components.json`. Free items need only the plain string form:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium installs need the authenticated form + `REUI_LICENSE_KEY` in `.env.local` - see [cli.md](./cli.md). The MCP `get_project_context` tool returns the right config.
## Know your base: base or radix
ReUI ships every item in two builds - `base` (Base UI) and `radix` (Radix UI) - with mirrored names. The CLI installs the build matching your project automatically, but **you must write code against the right base's API**. Detect it from `components.json` -> `style`: the segment before the first `-` is the base (`base-nova` -> Base UI, `radix-nova` -> Radix UI). The installed files and `c-*` examples are already in your base - read them and adapt on that base. See [components.md](./components.md) for the API deltas.
**So the MCP's own `docsUrl` and `previewUrl` match your base**, send your `style` to the MCP: add `?style=<your components.json style>` to the ReUI MCP server URL (or set an `X-Reui-Style` header) in your MCP client config - set once, applies to every call. The MCP then resolves docs/preview links to YOUR library (`/docs/components/radix/...`, `/preview/radix/...` for a radix project) instead of the default base; `get_project_context` echoes back the style it currently sees so you can confirm it. Install commands are unaffected (the CLI already installs the right variant). If you notice the MCP returning `base` links for a `radix` project, tell the user to add `?style=` to the server URL.
Blocks adapt to your active theme through semantic tokens and CSS variables - change the theme and every block follows.
## Free vs premium
- **Free, no key:** the 22 components, all `c-*` examples, the ReUI MCP, and this skill.
- **Premium, license required at install:** blocks (Pro or Ultimate), Motion Icons and templates (Ultimate). Set `REUI_LICENSE_KEY` (see [cli.md](./cli.md)).
## Component API index
The canonical index of every component's API docs is **https://reui.io/llms.txt** (returned as `componentsApiUrl` in MCP results). Prefer the inline `api` from `get_component`; use the index/docs as the fallback.
+26
View File
@@ -0,0 +1,26 @@
# Styling (ReUI delta over shadcn)
Follow the shadcn skill for the generic rules (semantic colors not raw values, `gap-*` not `space-y-*`, `size-*`, `cn()`, no manual `dark:` overrides, no overlay `z-index`). This file is only the ReUI-specific additions.
## ReUI extended semantic tokens
ReUI adds semantic tokens beyond shadcn's base set. Use these instead of raw colors for status and emphasis:
- `--success` / `--success-foreground`
- `--info` / `--info-foreground`
- `--warning` / `--warning-foreground`
- `--destructive-foreground` (paired with shadcn's `--destructive`)
- `--invert` / `--invert-foreground` (inverted surfaces)
Use them as Tailwind utilities (`bg-success text-success-foreground`, `text-warning`, ...). They are defined in the project's global CSS and registered with Tailwind (`@theme inline` on v4). If a token is missing in the project, add it to the global CSS file (never a new file) following the same `name` / `name-foreground` convention, exactly as the shadcn customization rules describe.
**Incorrect:** `<span className="text-green-600">Active</span>`
**Correct:** `<Badge variant="success">Active</Badge>` or `<span className="text-success">Active</span>`
## Blocks follow your theme
When you install a block it adapts to your active theme through the semantic tokens above and the project's CSS variables. Don't hardcode style-specific values into installed block code and don't fork it to "restyle" - change the theme via the CSS variables / a preset and every block follows. Want a different look? `search` for a block whose design already fits instead of re-skinning one.
## Density and typography rhythm
ReUI operational UI usually feels dense, not airy. Keep the gap between a title and its supporting description tight by default (`gap-0.5`, `space-y-1`, or `space-y-px`), and smaller than the gap between sections. Match the surrounding ReUI density when you add rows or fields; do not pad operational surfaces like a marketing page.
+54
View File
@@ -0,0 +1,54 @@
# Workflow: find -> install -> read API -> adapt
The core ReUI loop. The MCP tells you what to install and gives you the API; the shadcn CLI installs it; you turn the installed files into correct, themed, data-wired code by **reuse**, not redesign.
## 1. Find (ReUI MCP `search` / `compose_page`)
**Full multi-section page ask?** Call `compose_page(intent, sections?)` FIRST, before searching block-by-block. It returns ordered sections, each with the best block for the intent (top pick + alternates); sections listed in `unavailableSections` have no real inventory - compose those from components, do not force a bad block.
For everything else, call `search` with the user's intent. Pass structured hints whenever you can infer them - you are an LLM, so do the parsing the server cannot:
- `type`: `"component"` (one of the 22 building blocks), `"example"` (a c-\* use-case), `"block"` (a full page/section), `"icon"`.
- `component`: the ReUI component the request implies (`"data-grid"`, `"kanban"`, ...).
- `category`, `features` (e.g. `["sortable","pagination"]`), `free`.
Example: "build a users management page with filters" -> `search({ query: "users management page with filters", type: "block", component: "data-grid", features: ["filters"] })`.
Each result has `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `termCoverage`, and `whyMatch`. `score` is relative to the top hit (the top is ~100 by construction), not an absolute quality - compare results to each other, and show the user the top options if several score closely; do not silently guess. A low `termCoverage` means a weak match even with a high score - rephrase or widen.
**Always show the preview link.** Whenever you list or recommend items - from `search`, `search_icons`, `list_components`, `compose_page`, or a getter - include each item's `previewUrl` (a live preview page) so the user can SEE it before you install. Blocks and examples link to an individual live preview; icons and components to their live category/component page. This applies to every listing, not only a single pick.
## 2. Install (shadcn CLI)
Run the result's `install` command from the project root, non-interactively:
```bash
npx shadcn@latest add @reui/<name> --yes
```
The CLI reads `components.json`, installs the correct base+style variant, resolves `registryDependencies` (a block pulls in its components), installs npm deps, and rewrites aliases. Do not pass the base/style. See [cli.md](./cli.md).
## 3. Read the API (do not guess props)
Before writing code against any component an item uses:
1. The item's `componentDigests` already give a 1-line contract per component - often enough to wire it. For the full API, call **`get_component(names)`** with ALL of `componentsUsed` in ONE call (it accepts an array) and read each inline `api` - no web fetch. **Share the component's `docsUrl`** (its API documentation page) with the user whenever you work with that component's API, so they have the full reference; the `/llms.txt` index is a further fallback.
2. Call **`get_examples(name)`** for the free `c-*` examples of that component; install one and **read the added files** to copy the exact composition. This is the fastest correct path - the example shows real wiring you adapt, not invent.
3. About to write a prop you did not see in an `api` or installed file? Run **`validate_usage`** BEFORE writing the code - per-prop documented / notDocumented verdicts plus did-you-mean suggestions. notDocumented means read the API, not push on.
## 4. Adapt (reuse-first) - do not skip
Installing files is not the end, and redesigning them defeats the point. First note the project's **base** so you write the right API - read `components.json` -> `style` and take the segment before the first `-` (`base-nova` -> Base UI, `radix-nova` -> Radix UI), see [components.md](./components.md). After `add`:
1. **Read the added files**; keep the composition intact. For a block, verify the components are wired correctly (for `data-grid`: a `useTable({ features: dataGridFeatures, ... })` instance passed as `table`, `recordCount` set - see [components.md](./components.md)).
2. **Replace demo data with the user's real data** via typed structures (see [adapting.md](./adapting.md)).
3. **Fix icon imports** to the project's icon library (see [icons.md](./icons.md)).
4. **Align styling** to semantic tokens and the active theme - no raw colors (see [styling.md](./styling.md)).
5. **Validate before finalizing**: if your adaptation introduced components or props you did not read in an `api` or example, run `validate_usage` on them.
6. **Hit the craft bar** - clear hierarchy, deliberate density, the empty / loading / error states, subtle motion, and mobile-first responsiveness (see [craft.md](./craft.md)). Generic-looking output means you under-reused the design, not that it needs restyling.
7. **Pass the quality gates** (security, a11y, scroll) - call the MCP `get_audit_checklist` tool and clear every item (see [quality.md](./quality.md)).
8. **Typecheck / lint**.
## If no single block fits
Compose from components (`compose_page` tells you which sections have no block inventory via `unavailableSections`). `search` the components you need, read each `get_component` API, install a worked `get_examples` example for each, and assemble by adapting those examples. A block in the same category is a useful reference - install it and read its files to see how ReUI composes those components, then adapt.
+58
View File
@@ -0,0 +1,58 @@
# ReUI MCP: full reference
The ReUI MCP (`https://mcp.reui.io`, Streamable HTTP) is free to use but needs a ReUI account: on first use the agent signs in with ReUI (a free account is created if the user has none), so every request is tied to an account. Free covers components and examples; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the daily request limit. It does **discovery + guidance** (search, inline APIs, page planning, validation) and never serves source; the shadcn CLI does **installation**, and the license key lives there (the `@reui` entry in `components.json`, backed by `.env.local`). Goal: from the user's intent to correct, themed, data-wired ReUI code in the **fewest tokens and calls**, with **no guessing**.
## Golden path (token-optimal - follow this order)
Most tasks need 2-4 MCP calls and ZERO web fetches:
1. **`search(query, ...hints)`** -> pick the top 1-3 results. Each result already carries `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `whyMatch`. The payload is complete - do not call another tool just to "confirm" a result.
2. **`get_component([...componentsUsed])`** in ONE batched call (one name or an array of up to 20) -> read each inline `api`. This **replaces** fetching docs pages. Often skippable: search responses carry `componentDigests`, a compact API contract per referenced component.
3. **`get_examples(component)`** -> install ONE returned `c-*` example, read the added files, copy the composition.
4. **`get_install_command(item)`** only to validate a name you are unsure of (results already include `install`). Run the install with the shadcn CLI (`--yes`).
5. **`get_audit_checklist()`** before declaring done.
If you already know the exact item name, skip `search`. Everything else is situational.
## The 5 task-specific tools (when to reach for each)
- **`compose_page`** - BEFORE building any full page (dashboard, settings, billing, landing). Pass the intent (and optionally the sections you want); it returns ordered sections, each with the best premium block for the intent (top pick + alternates). Sections with no real inventory are listed honestly in `unavailableSections` - compose those from components instead of forcing a bad block.
- **`search_icons`** - whenever you need icons, especially several. Batch up to 24 concepts in one call; each concept returns its best icons with install commands. Pass `animated: true` to get only icons that have a hover-animated Motion variant.
- **`validate_usage`** - BEFORE writing code with component names or props you have not read in an inline `api` or an installed example. It checks planned names + props against the indexed API docs and registry item names; returns did-you-mean suggestions and per-prop documented / notDocumented verdicts. Deterministic, no inference - a notDocumented prop means stop and read the API, not push on.
- **`whats_new`** - when your registry knowledge might be stale (a name 404s, the user mentions an item you don't know). Returns items added/removed per build, newest first.
- **`report_issue`** - when an installed item is actually broken (bad source, wrong dependency, broken preview). Goes straight to the ReUI team; rate-limited 5/hour. Not for usage questions.
## All 19 tools
`search`, `get_block`, `get_example`, `get_icon`, `list_block_groups`, `list_block_categories`, `list_example_categories`, `list_icon_categories`, `list_components`, `get_component`, `get_examples`, `search_icons`, `compose_page`, `validate_usage`, `whats_new`, `report_issue`, `get_install_command`, `get_project_context`, `get_audit_checklist`. The MCP serves the full parameter schemas; do not guess parameters beyond them.
## Token + speed rules
- **Batch `get_component`** - ONE call with the whole `componentsUsed` array, never N calls. Skip it entirely when `componentDigests` already answers the question.
- **Read source by installing** - the MCP serves no source. To read or analyze an item's real code, install it with the shadcn CLI and open the local files. Learn an API from the inline `api` / `componentDigests`, never by reading raw source.
- **Infer `search` hints yourself** (`type`, `component`, `category`, `features`, `free`) - hints shrink the result set and the tokens. Keep `limit` low; one right result beats ten.
- Run independent calls (and the shadcn install) concurrently in one turn - serial tool calls are the main source of slowness.
- Don't repeat a search for the same intent; don't call `list_*` to "see everything" - `search` is the entry point, `list_*` is only for browsing a taxonomy the user explicitly wants to explore.
- Prefer `get_component`'s inline `api` over `docsUrl` / `/llms.txt`. Fetch a web page only as a last resort.
## Result shapes (so you don't re-fetch)
- `score` is 0-100 RELATIVE to the top hit (the top is ~100 by construction), not absolute - compare results to each other.
- `termCoverage` (0-1) is the share of the query the item matched - low means a weak match even if the score looks high; rephrase or widen the search.
- Each result carries `whyMatch`, `install`, docs/preview URLs, and a `free` flag; premium items carry `requiredPlan` (`"pro"` for blocks, `"ultimate"` for icons).
- `componentDigests` is a top-level map: a compact API contract per referenced component - often enough to wire an item without a `get_component` call.
- Icon results and `get_icon` include `animated: true` and `installAnimated` when a hover-animated Motion variant exists (animated: `@reui/icons/animated/<style>/<name>`; static: `@reui/icons/default/<style>/<name>`).
## Error playbook
- **401** - the MCP requires a signed-in ReUI account. The client prompts "Sign in with ReUI" (OAuth) on first use; a free account is created if needed. For headless/CI, pass a personal token (`reui_pat_...`, created at https://reui.io/account/mcp?ref=skill) as `Authorization: Bearer`.
- **403 / locked result** - a valid account but the plan does not cover the item: premium blocks need Pro, Motion Icons need Ultimate. Point to https://reui.io/pricing?ref=skill (upgrade). Free accounts still get all components + examples.
- **429** - rate limited (120 requests/min per IP); back off, honor `Retry-After`.
- **not found** (`found: false`) - use the returned `suggestions`, or `search`; check `whats_new` if you suspect a stale name. Never run a fabricated install command.
## Fallbacks
- No ReUI MCP: `npx shadcn@latest search @reui -q "..."` then `add` (generic, no scoring / inline API).
- The shadcn project's own MCP also works over the `@reui` registry: https://ui.shadcn.com/docs/mcp.
Per-agent MCP setup: https://reui.io/docs/mcp?ref=skill
-5
View File
@@ -1,5 +0,0 @@
deploy/netflow-agent/*.sh text eol=lf
deploy/netflow-agent/*.py text eol=lf
# Migration hashes authenticate exact reviewed bytes, including existing CRLF files.
# Preserve those bytes on every checkout instead of applying core.autocrlf.
backend/drizzle/*.sql -text
+1 -64
View File
@@ -153,13 +153,8 @@ jobs:
NODE
cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/"
cp -R packages/contracts/src "$STAGING/packages/contracts/"
cp backend/package.json backend/tsconfig.json backend/mcp-coverage.json "$STAGING/backend/"
cp backend/package.json backend/tsconfig.json "$STAGING/backend/"
cp -R backend/src "$STAGING/backend/"
mkdir -p "$STAGING/backend/scripts/writer-profile"
cp backend/scripts/build-database-operator.mjs backend/scripts/database-writer-bootstrap-operator.mjs backend/scripts/database-writer-profile-operator.mts "$STAGING/backend/scripts/"
cp backend/scripts/writer-profile/operator-files.mjs backend/scripts/writer-profile/operator-contract.mjs backend/scripts/writer-profile/publisher.mjs backend/scripts/writer-profile/context.mjs backend/scripts/writer-profile/catalogue.mts backend/scripts/writer-profile/catalogue-value-bridge.ts backend/scripts/writer-profile/catalogue-projection.ts backend/scripts/writer-profile/catalogue-leaves.ts backend/scripts/writer-profile/application-writer-profile-catalogue.ts backend/scripts/writer-profile/application-writer-profile-catalogue-values.ts backend/scripts/writer-profile/application-writer-profile-catalogue-parent-policy.ts backend/scripts/writer-profile/application-writer-profile-canonical.ts backend/scripts/writer-profile/fixed44-build-sql.ts backend/scripts/writer-profile/fixed44-build-definition.ts backend/scripts/writer-profile/expanded44-data-policy.ts "${STAGING}/backend/scripts/writer-profile/"
mkdir -p "$STAGING/deploy/netflow-agent"
cp deploy/netflow-agent/install.sh "$STAGING/deploy/netflow-agent/"
if [ -d backend/drizzle ]; then
cp -R backend/drizzle "$STAGING/backend/"
fi
@@ -334,69 +329,12 @@ jobs:
org.opencontainers.image.revision=${{ gitea.sha }}
org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }}
netflow-agent-image:
needs: prepare-release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure netflow-agent image name
shell: bash
run: |
set -euo pipefail
owner=$(echo "${{ gitea.repository }}" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]')
name=$(echo "${{ gitea.repository }}" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]')
stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//')
echo "IMAGE_NAME=${{ env.REGISTRY }}/${owner}/${stem}-netflow-agent" >> "${GITHUB_ENV}"
- name: Test controller and installer
run: |
python3 -m unittest discover -s deploy/netflow-agent -p "test_*.py" -v
bash -n deploy/netflow-agent/install.sh
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Gitea Registry
uses: docker/login-action@v3
with:
registry: git.shx.one
username: ${{ gitea.actor }}
password: ${{ secrets.ACTIONS_PAT }}
- name: Verify image with real WireGuard
run: |
docker build --target test -t mm-netflow-agent-test deploy/netflow-agent
docker run --rm --cap-drop ALL --cap-add NET_ADMIN --read-only --tmpfs /tmp -e FLOW_AGENT_KERNEL_TEST=1 --entrypoint python3 mm-netflow-agent-test -m unittest discover -s /app -p "test_agent.py" -v
- name: Build and push netflow-agent image
uses: docker/build-push-action@v5
with:
context: deploy/netflow-agent
file: deploy/netflow-agent/Dockerfile
platforms: linux/amd64
push: true
tags: |
${{ env.IMAGE_NAME }}:latest
${{ env.IMAGE_NAME }}:${{ gitea.sha }}
${{ env.IMAGE_NAME }}:${{ needs.prepare-release.outputs.version }}
cache-from: type=gha,scope=netflow-agent
cache-to: type=gha,mode=max,scope=netflow-agent
labels: |
org.opencontainers.image.title=mmapp-netflow-agent
org.opencontainers.image.description=MikroTik Manager local WireGuard controller
org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }}
org.opencontainers.image.revision=${{ gitea.sha }}
org.opencontainers.image.created=${{ gitea.event.head_commit.timestamp }}
publish-release:
needs:
- prepare-release
- backend-image
- frontend-image
- updater-image
- netflow-agent-image
if: needs.prepare-release.outputs.should_release == 'true'
runs-on: ubuntu-latest
steps:
@@ -482,7 +420,6 @@ jobs:
- prepare-release
- backend-image
- frontend-image
- netflow-agent-image
runs-on: ubuntu-latest
env:
WEBHOOK_URL: ${{ secrets.DEPLOY_WEBHOOK_URL }}
-276
View File
@@ -1,276 +0,0 @@
name: MCP isolated acceptance preview images
'on':
workflow_dispatch:
inputs:
expected_sha:
description: Exact reviewed full commit SHA on the selected ref; includes all accepted source changes
required: true
type: string
auth_portal_origin:
description: Canonical isolated browser-facing Auth Portal HTTP(S) origin; no path, credentials or query
required: true
type: string
default: http://127.0.0.1:55175
permissions:
contents: read
packages: write
jobs:
backend-preview:
if: ${{ gitea.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
timeout-minutes: 40
env:
COMPONENT: backend
EXPECTED_SHA: ${{ inputs.expected_sha }}
DISPATCH_SHA: ${{ gitea.sha }}
DISPATCH_EVENT: ${{ gitea.event_name }}
REPOSITORY: ${{ gitea.repository }}
RUN_ID: ${{ gitea.run_id }}
RUN_ATTEMPT: ${{ gitea.run_attempt }}
steps:
- name: Checkout exact dispatch commit
uses: actions/checkout@v4
with:
ref: ${{ gitea.sha }}
persist-credentials: false
- name: Use existing Linux Node22 build toolchain
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Verify source and isolated preview destination
shell: bash
run: |
set -euo pipefail
test "$DISPATCH_EVENT" = workflow_dispatch
[[ "$EXPECTED_SHA" =~ ^[0-9a-f]{40}$ ]]
test "$EXPECTED_SHA" = "$DISPATCH_SHA"
test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"
git diff --exit-code
git diff --cached --exit-code
[[ "$RUN_ID" =~ ^[0-9]+$ && "$RUN_ATTEMPT" =~ ^[0-9]+$ ]]
owner=$(printf '%s' "$REPOSITORY" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]')
name=$(printf '%s' "$REPOSITORY" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]')
[[ "$owner" =~ ^[a-z0-9][a-z0-9._-]*$ && "$name" =~ ^[a-z0-9][a-z0-9._-]*$ ]]
stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//')
echo "IMAGE_NAME=git.shx.one/$owner/$stem-$COMPONENT-mcp-acceptance" >> "$GITHUB_ENV"
echo "PREVIEW_TAG=mcp-acceptance-$EXPECTED_SHA-$RUN_ID-$RUN_ATTEMPT" >> "$GITHUB_ENV"
echo "PREVIEW_CACHE=git.shx.one/$owner/$stem-$COMPONENT-mcp-acceptance-buildcache:node-buildcache" >> "$GITHUB_ENV"
mkdir -p .ci/acceptance-evidence
node <<'NODE'
const fs=require('node:fs'),cp=require('node:child_process'),crypto=require('node:crypto');
const files=cp.execFileSync('git',['ls-files','-z'],{encoding:'utf8'}).split('\0').filter(Boolean);
const inputs=files.map(path=>{const info=fs.lstatSync(path);if(!info.isFile()||info.isSymbolicLink())throw Error('Regular tracked source inputs required');const data=fs.readFileSync(path);return {path,sha256:crypto.createHash('sha256').update(data).digest('hex'),bytes:data.length}});
fs.writeFileSync('.ci/acceptance-evidence/source.json',JSON.stringify({format:'mcp-preview-source-v1',repository:process.env.REPOSITORY,sourceSha:process.env.EXPECTED_SHA,sourceTree:cp.execFileSync('git',['rev-parse','HEAD^{tree}'],{encoding:'utf8'}).trim(),platform:'linux/amd64',component:process.env.COMPONENT,runId:process.env.RUN_ID,runAttempt:process.env.RUN_ATTEMPT,authPortalOrigin:process.env.AUTH_PORTAL_ORIGIN??null,inputs},null,2)+'\n');
NODE
- name: Prepare existing scoped Docker context
shell: bash
run: |
set -euo pipefail
STAGING=".ci/docker/backend"
rm -rf "$STAGING"
mkdir -p "$STAGING/packages/contracts" "$STAGING/backend"
cp package-lock.json "$STAGING/"
node <<'NODE'
const fs = require("node:fs")
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"))
pkg.workspaces = ["packages/*", "backend"]
pkg.dependencies = {}
pkg.devDependencies = {}
delete pkg.scripts
fs.writeFileSync(".ci/docker/backend/package.json", `${JSON.stringify(pkg, null, 2)}\n`)
NODE
cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/"
cp -R packages/contracts/src "$STAGING/packages/contracts/"
cp backend/package.json backend/tsconfig.json backend/mcp-coverage.json "$STAGING/backend/"
cp -R backend/src "$STAGING/backend/"
mkdir -p "$STAGING/deploy/netflow-agent"
cp deploy/netflow-agent/install.sh "$STAGING/deploy/netflow-agent/"
if [ -d backend/drizzle ]; then
cp -R backend/drizzle "$STAGING/backend/"
fi
mkdir -p "$STAGING/backend/scripts/writer-profile"
cp backend/scripts/build-database-operator.mjs backend/scripts/database-writer-bootstrap-operator.mjs backend/scripts/database-writer-profile-operator.mts "$STAGING/backend/scripts/"
cp backend/scripts/writer-profile/operator-files.mjs backend/scripts/writer-profile/operator-contract.mjs backend/scripts/writer-profile/publisher.mjs backend/scripts/writer-profile/context.mjs backend/scripts/writer-profile/catalogue.mts backend/scripts/writer-profile/catalogue-value-bridge.ts backend/scripts/writer-profile/catalogue-projection.ts backend/scripts/writer-profile/catalogue-leaves.ts backend/scripts/writer-profile/application-writer-profile-catalogue.ts backend/scripts/writer-profile/application-writer-profile-catalogue-values.ts backend/scripts/writer-profile/application-writer-profile-catalogue-parent-policy.ts backend/scripts/writer-profile/application-writer-profile-canonical.ts backend/scripts/writer-profile/fixed44-build-sql.ts backend/scripts/writer-profile/fixed44-build-definition.ts backend/scripts/writer-profile/expanded44-data-policy.ts "${STAGING}/backend/scripts/writer-profile/"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to existing Gitea registry
uses: docker/login-action@v3
with:
registry: git.shx.one
username: ${{ gitea.actor }}
password: ${{ secrets.ACTIONS_PAT }}
- name: Build and push isolated acceptance image
id: build
uses: docker/build-push-action@v5
with:
context: .ci/docker/backend
file: backend/Dockerfile
platforms: linux/amd64
push: true
tags: ${{ env.IMAGE_NAME }}:${{ env.PREVIEW_TAG }}
build-args: APP_VERSION=${{ env.PREVIEW_TAG }}
cache-from: type=gha,scope=mcp-acceptance-backend
cache-to: type=gha,scope=mcp-acceptance-backend,mode=max
provenance: mode=min
sbom: true
labels: |-
org.opencontainers.image.revision=${{ gitea.sha }}
org.opencontainers.image.version=${{ env.PREVIEW_TAG }}
io.mmapp.acceptance-only=true
- name: Record exact digest and build metadata
shell: bash
env:
IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
BUILD_METADATA: ${{ steps.build.outputs.metadata }}
run: |
set -euo pipefail
node <<'NODE'
const fs=require('node:fs'),crypto=require('node:crypto');
const digest=process.env.IMAGE_DIGEST;if(!/^sha256:[a-f0-9]{64}$/.test(digest))throw Error('Exact registry digest required');
const metadata=JSON.parse(process.env.BUILD_METADATA);const source=fs.readFileSync('.ci/acceptance-evidence/source.json');
fs.writeFileSync('.ci/acceptance-evidence/image.json',JSON.stringify({format:'mcp-preview-image-v1',sourceSha:process.env.EXPECTED_SHA,sourceProofSha256:crypto.createHash('sha256').update(source).digest('hex'),image:process.env.IMAGE_NAME,tag:process.env.PREVIEW_TAG,digest,pinnedReference:process.env.IMAGE_NAME+'@'+digest,platform:'linux/amd64',component:process.env.COMPONENT,authPortalOrigin:process.env.AUTH_PORTAL_ORIGIN??null,buildMetadata:metadata,productionDeployed:false,wholeInstallation2GbAccepted:false},null,2)+'\n');
console.log('Acceptance image digest: '+process.env.IMAGE_NAME+'@'+digest);
NODE
- name: Retain source and image binding artifacts
uses: actions/upload-artifact@v3
with:
name: mcp-acceptance-${{ env.COMPONENT }}-${{ gitea.sha }}-${{ gitea.run_id }}-${{ gitea.run_attempt }}
path: .ci/acceptance-evidence/*.json
if-no-files-found: error
retention-days: 14
frontend-preview:
if: ${{ gitea.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
timeout-minutes: 40
env:
COMPONENT: frontend
EXPECTED_SHA: ${{ inputs.expected_sha }}
DISPATCH_SHA: ${{ gitea.sha }}
DISPATCH_EVENT: ${{ gitea.event_name }}
REPOSITORY: ${{ gitea.repository }}
RUN_ID: ${{ gitea.run_id }}
RUN_ATTEMPT: ${{ gitea.run_attempt }}
AUTH_PORTAL_ORIGIN_REQUEST: ${{ inputs.auth_portal_origin }}
steps:
- name: Checkout exact dispatch commit
uses: actions/checkout@v4
with:
ref: ${{ gitea.sha }}
persist-credentials: false
- name: Use existing Linux Node22 build toolchain
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Validate explicit isolated portal origin
shell: bash
run: |
set -euo pipefail
node <<'NODE'
const fs=require('node:fs'),assert=require('node:assert/strict');
const raw=process.env.AUTH_PORTAL_ORIGIN_REQUEST;
assert(typeof raw==='string'&&raw.length>0&&raw.length<=2048&&!/[\x00-\x20\x7f]/.test(raw),'Bounded canonical HTTP(S) origin required');
const origin=new URL(raw);
assert(['http:','https:'].includes(origin.protocol)&&!origin.username&&!origin.password&&origin.pathname==='/'&&!origin.search&&!origin.hash&&origin.origin!=='null'&&origin.origin===raw,'Canonical origin only; credentials/path/query/hash refused');
fs.appendFileSync(process.env.GITHUB_ENV,'AUTH_PORTAL_ORIGIN='+origin.origin+'\n');
NODE
- name: Verify source and isolated preview destination
shell: bash
run: |
set -euo pipefail
test "$DISPATCH_EVENT" = workflow_dispatch
[[ "$EXPECTED_SHA" =~ ^[0-9a-f]{40}$ ]]
test "$EXPECTED_SHA" = "$DISPATCH_SHA"
test "$(git rev-parse HEAD)" = "$EXPECTED_SHA"
git diff --exit-code
git diff --cached --exit-code
[[ "$RUN_ID" =~ ^[0-9]+$ && "$RUN_ATTEMPT" =~ ^[0-9]+$ ]]
owner=$(printf '%s' "$REPOSITORY" | cut -d/ -f1 | tr '[:upper:]' '[:lower:]')
name=$(printf '%s' "$REPOSITORY" | cut -d/ -f2 | tr '[:upper:]' '[:lower:]')
[[ "$owner" =~ ^[a-z0-9][a-z0-9._-]*$ && "$name" =~ ^[a-z0-9][a-z0-9._-]*$ ]]
stem=$(printf '%s' "$name" | sed -E 's/-[0-9]+$//')
echo "IMAGE_NAME=git.shx.one/$owner/$stem-$COMPONENT-mcp-acceptance" >> "$GITHUB_ENV"
echo "PREVIEW_TAG=mcp-acceptance-$EXPECTED_SHA-$RUN_ID-$RUN_ATTEMPT" >> "$GITHUB_ENV"
echo "PREVIEW_CACHE=git.shx.one/$owner/$stem-$COMPONENT-mcp-acceptance-buildcache:node-buildcache" >> "$GITHUB_ENV"
mkdir -p .ci/acceptance-evidence
node <<'NODE'
const fs=require('node:fs'),cp=require('node:child_process'),crypto=require('node:crypto');
const files=cp.execFileSync('git',['ls-files','-z'],{encoding:'utf8'}).split('\0').filter(Boolean);
const inputs=files.map(path=>{const info=fs.lstatSync(path);if(!info.isFile()||info.isSymbolicLink())throw Error('Regular tracked source inputs required');const data=fs.readFileSync(path);return {path,sha256:crypto.createHash('sha256').update(data).digest('hex'),bytes:data.length}});
fs.writeFileSync('.ci/acceptance-evidence/source.json',JSON.stringify({format:'mcp-preview-source-v1',repository:process.env.REPOSITORY,sourceSha:process.env.EXPECTED_SHA,sourceTree:cp.execFileSync('git',['rev-parse','HEAD^{tree}'],{encoding:'utf8'}).trim(),platform:'linux/amd64',component:process.env.COMPONENT,runId:process.env.RUN_ID,runAttempt:process.env.RUN_ATTEMPT,authPortalOrigin:process.env.AUTH_PORTAL_ORIGIN??null,inputs},null,2)+'\n');
NODE
- name: Prepare existing scoped Docker context
shell: bash
run: |
set -euo pipefail
STAGING=".ci/docker/frontend"
rm -rf "$STAGING"
mkdir -p "$STAGING/packages/contracts" "$STAGING/public"
cp package-lock.json next.config.ts tsconfig.json postcss.config.mjs eslint.config.mjs components.json "$STAGING/"
node <<'NODE'
const fs = require("node:fs")
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"))
pkg.workspaces = ["packages/*"]
fs.writeFileSync(".ci/docker/frontend/package.json", `${JSON.stringify(pkg, null, 2)}\n`)
NODE
cp packages/contracts/package.json packages/contracts/tsconfig.json "$STAGING/packages/contracts/"
cp -R packages/contracts/src "$STAGING/packages/contracts/"
for dir in app components lib shared entities public hooks; do
if [ -d "$dir" ]; then
cp -R "$dir" "$STAGING/"
fi
done
test -f "$STAGING/public/release-manifest.json"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to existing Gitea registry
uses: docker/login-action@v3
with:
registry: git.shx.one
username: ${{ gitea.actor }}
password: ${{ secrets.ACTIONS_PAT }}
- name: Build and push isolated acceptance image
id: build
uses: docker/build-push-action@v5
with:
context: .ci/docker/frontend
file: Dockerfile.frontend
platforms: linux/amd64
push: true
tags: ${{ env.IMAGE_NAME }}:${{ env.PREVIEW_TAG }}
build-args: |-
BACKEND_INTERNAL_URL=http://backend:8000
NEXT_PUBLIC_BACKEND_URL=same-origin
NEXT_PUBLIC_DEFAULT_DATA_SOURCE=live
NEXT_PUBLIC_ALLOW_MOCK_DATA=false
NEXT_PUBLIC_AUTH_PORTAL_URL=${{ env.AUTH_PORTAL_ORIGIN }}
NEXT_PUBLIC_APP_VERSION=${{ env.PREVIEW_TAG }}
NEXT_PUBLIC_RELEASE_URL=
cache-from: type=gha,scope=mcp-acceptance-frontend
cache-to: type=gha,scope=mcp-acceptance-frontend,mode=max
provenance: mode=min
sbom: true
labels: |-
org.opencontainers.image.revision=${{ gitea.sha }}
org.opencontainers.image.version=${{ env.PREVIEW_TAG }}
io.mmapp.acceptance-only=true
- name: Record exact digest and build metadata
shell: bash
env:
IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
BUILD_METADATA: ${{ steps.build.outputs.metadata }}
run: |
set -euo pipefail
node <<'NODE'
const fs=require('node:fs'),crypto=require('node:crypto');
const digest=process.env.IMAGE_DIGEST;if(!/^sha256:[a-f0-9]{64}$/.test(digest))throw Error('Exact registry digest required');
const metadata=JSON.parse(process.env.BUILD_METADATA);const source=fs.readFileSync('.ci/acceptance-evidence/source.json');
fs.writeFileSync('.ci/acceptance-evidence/image.json',JSON.stringify({format:'mcp-preview-image-v1',sourceSha:process.env.EXPECTED_SHA,sourceProofSha256:crypto.createHash('sha256').update(source).digest('hex'),image:process.env.IMAGE_NAME,tag:process.env.PREVIEW_TAG,digest,pinnedReference:process.env.IMAGE_NAME+'@'+digest,platform:'linux/amd64',component:process.env.COMPONENT,authPortalOrigin:process.env.AUTH_PORTAL_ORIGIN??null,buildMetadata:metadata,productionDeployed:false,wholeInstallation2GbAccepted:false},null,2)+'\n');
console.log('Acceptance image digest: '+process.env.IMAGE_NAME+'@'+digest);
NODE
- name: Retain source and image binding artifacts
uses: actions/upload-artifact@v3
with:
name: mcp-acceptance-${{ env.COMPONENT }}-${{ gitea.sha }}-${{ gitea.run_id }}-${{ gitea.run_attempt }}
path: .ci/acceptance-evidence/*.json
if-no-files-found: error
retention-days: 14
-16
View File
@@ -52,8 +52,6 @@ Thumbs.db
.vercel/
tmp/
temp/
.mcp-state/
backend/.mcp-state/
.ci/docker/
.claude/settings.local.json
@@ -64,17 +62,3 @@ opencode.json
# Local codegraph process
.codegraph/daemon.pid
# Python controller tests
__pycache__/
*.pyc
# NetFlow agent host state (when Compose is installed in a checkout)
/data/netflow-wireguard/
/data/netflow-agent-socket/
# Compiled disposable memory-acceptance entry; not a production image artifact.
backend/dist-mcp-memory/
backend/dist-operator/
# Agent-local permissions can contain commands with deployment credentials.
/.claude/settings.local.json
+67
View File
@@ -0,0 +1,67 @@
---
name: reui
description: Use the ReUI registry from your AI agent - find, install, and correctly use ReUI components (the 22 free building blocks like data-grid, kanban, filters), their free examples, premium blocks, and Motion Icons. Applies in any project using ReUI, the @reui registry, REUI_LICENSE_KEY, or any shadcn project where the user asks for premium blocks, data grids, kanban boards, dashboards, or full pages. Pairs with the free ReUI MCP server for live, scored registry search and inline component APIs.
user-invocable: false
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
---
> **ReUI skill version `fe6742e29c`.** If the ReUI MCP's `get_agent_skill` reports a newer `version`, re-run the ReUI installer (see `get_agent_skill` -> `install.recommended`) to update this skill. Cloud/tools-only agents have no local file and always read the latest - they can ignore this.
# ReUI for Agents
ReUI is a shadcn-compatible registry. It ships four things you **reuse** - never redesign:
- **components** - the 22 ReUI building blocks with real APIs: `data-grid`, `kanban`, `filters`, `date-selector`, `tree`, `stepper`, ... (free)
- **examples** - free `c-*` single-pattern use-cases of a component (`c-kanban-1`); install one and read it to see exact composition
- **blocks** - premium full-page sections that compose components (`data-grid-2`, `pricing-page-1`); Pro or Ultimate license at install
- **icons** - Motion Icons in 4 styles, static + hover-animated variants; Ultimate license at install
The skill is free and this MCP is free to use; it just needs a ReUI account. On first use your agent opens a browser "Sign in with ReUI" prompt (a free account is created if you don't have one). Free covers components and examples with a daily request allowance; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the limit (see [rules/registry.md](./rules/registry.md)). The same account and skill work in every agent and service the MCP connects to - this skill is agent-agnostic.
Skill + MCP are a team: this skill is the workflow (how to find, install, read the API, and adapt by reuse); the MCP is the live data and the hands (search, get_component, install commands). Your job: find the right item, install it with the shadcn CLI, read its real API, and **adapt by reuse** - wire real data and theme it; do not hand-roll or restyle what ReUI already provides. This skill **layers on the shadcn skill**: follow that for generic rules (spacing, `cn()`, semantic colors, forms); follow this for everything ReUI-specific.
## The core loop (MCP-native)
1. **Find** - call the ReUI MCP `search` tool with the user's intent. It returns a ranked, scored list across components/examples/blocks/icons, each with an `install` command, `previewUrl`, `docsUrl`, and `componentsUsed`. Pass hints (`type`, `component`, `category`, `features`, `free`) when you can infer them.
2. **Install** - run the returned command non-interactively (`npx shadcn@latest add @reui/<name> --yes`). The CLI resolves deps, aliases, and the base/style from `components.json`. See [cli.md](./rules/cli.md).
3. **Read the API (on your base)** - first note your base from `components.json` -> `style` (`base-nova` -> Base UI, `radix-nova` -> Radix UI). For each component an item uses, call `get_component(name)` and read its **inline `api`** (no web fetch); then `get_examples(name)` to install a worked example and copy its composition - the installed files are already in your base. Whenever you work with a component's API, also **share its `docsUrl`** (the primitive's API documentation page) with the user so they have the full reference. See [components.md](./rules/components.md).
4. **Adapt (reuse-first)** - swap demo data for real data, fix icon imports, align tokens. Do not redesign. See [adapting.md](./rules/adapting.md).
**Always show the preview.** Every item a tool returns carries a `previewUrl` (a live preview page). Whenever you list, recommend, or present ReUI items to the user - blocks, components, examples, or icons, whether from `search`, `search_icons`, `list_components`, `compose_page`, or any getter - include each item's `previewUrl` so they can SEE it before installing. Blocks and examples open an individual live preview; icons and components link to their live category/component page. Never present an item without its preview link.
If the ReUI MCP is not configured, fall back to `npx shadcn@latest search @reui -q "..."` then `add` - but the MCP gives scored matches + inline APIs; prefer it.
## Commands
Run ReUI as explicit slash commands (via the ReUI MCP) **or** just ask in plain language - both run the same workflow.
| Command | Invoke | Does |
| ----------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| **build** | `/mcp__reui__build <what>` | Compose a page/section/feature from ReUI: plan → install → read API → adapt → craft → audit. |
| **add** | `/mcp__reui__add <item>` | Find & install one component/example/block/icon and wire it in. |
| **fix** | `/mcp__reui__fix [target]` | Diagnose & fix ReUI usage: wrong/undocumented props, base/radix mismatch, missing states, a11y/scroll. |
| **improve** | `/mcp__reui__improve [target]` | Refine + extend existing ReUI UI to a production-exceptional bar (hierarchy, density, states, responsive, motion). |
Invocation differs slightly per agent (`/mcp__reui__build` in Claude Code/Cursor/Windsurf, `/mcp.reui.build` in VS Code). No command surface? Just describe what you want - this skill drives the identical loop.
## When to reach for ReUI vs plain shadcn
| Need | Reach for |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| A full page or section (dashboard, billing, auth, pricing, settings) | `compose_page` first (plans sections + best blocks), then ReUI **blocks** |
| A data table with sorting/filtering/pagination/virtualization | the **data-grid** component (never hand-roll a `<table>`) |
| A drag-and-drop board | the **kanban** component |
| Advanced column filtering, date range, tree, stepper, ... | the matching ReUI **component** |
| A single generic control already in shadcn (Button, Dialog, Select) | plain **shadcn** |
## Detailed references
- [rules/registry.md](./rules/registry.md) - the four types, the @reui registry, base/radix, free vs premium + license
- [rules/workflow.md](./rules/workflow.md) - the find -> install -> read-API -> adapt loop (most important)
- [rules/components.md](./rules/components.md) - the 22 components, the data-grid contract, base vs radix
- [rules/adapting.md](./rules/adapting.md) - reuse-first: preserve the design (no over-customizing), reuse examples + a block's own elements, real data, don't invent APIs
- [rules/craft.md](./rules/craft.md) - make it exceptional: point of view, hierarchy, density, states, responsive, motion, the bar
- [rules/quality.md](./rules/quality.md) - security, accessibility, and scroll gates (the done gate)
- [rules/styling.md](./rules/styling.md) - ReUI extended tokens, theme adaptation, density
- [rules/icons.md](./rules/icons.md) - portable icons, swapping imports, Motion Icons (static + animated)
- [tools.md](./tools.md) - the ReUI MCP: golden path, the 19 tools, token rules, result shapes, errors
+43
View File
@@ -0,0 +1,43 @@
# Adapting installed ReUI code (reuse-first, no AI slop)
ReUI items ship production-quality. Your job is to **adapt by reuse** - wire real data and fit the app - not to redesign or hand-roll. The output should look like ReUI built it for this product.
## Preserve the design - don't over-customize
The design IS the product. A ReUI block/component encodes senior-designer decisions: spacing, hierarchy, density, color treatment, and component choices. The fastest way to turn a premium block back into generic AI slop is to "improve" its look - so don't.
- Change **data, copy, and props**; keep the **structure and styling** it ships with. Make the **smallest** change that wires the real data. If your diff touches `className` / JSX structure more than data / props, you are over-customizing - stop and reuse.
- Don't swap ReUI components for hand-rolled ones, restructure the layout, re-skin spacing / radius / colors, or add decorative chrome. Let the installed components carry the default spacing, radius, sizing, icon rhythm, density, and state styling; add custom Tailwind only when a component genuinely lacks a contract you need.
- Want a different look? `search` for a block whose design already fits and reuse that - don't restyle this one into a new design.
## Reuse the parts: examples and the block's own elements
- **Examples are building parts.** A free `c-*` example is a correct, single-pattern composition you can reuse. Before composing from scratch, `get_examples(component)`, install the closest one, and reuse its wiring - assemble UI from examples instead of hand-rolling what an example already shows.
- **Reuse a block's own elements.** Need more rows, cards, items, or sections than ship by default? Repeat the block's **existing** element by mapping real data through the same markup - never invent parallel markup that drifts from its design. Need a variant (empty / loading / expanded)? Derive it from an element the block already has.
## Don't invent (read, don't guess)
- Never write a prop, variant value, import path, or `@reui/...` name you didn't read in a component's inline `api`, an installed example, or a `search` result. If you didn't see it, treat it as nonexistent - call `get_component` / `get_examples` / `search` first, or run the MCP `validate_usage` tool to check planned names + props against the docs before writing code.
- If a getter returns `found: false` or `search` returns nothing, say so and fall back (plain shadcn, or ask) - never fabricate an install command or an API.
## What to change vs leave alone
- **Change:** the item's own data, copy, props, and layout to fit the app.
- **Leave alone:** installed component files, hooks, and the shared theme - do not edit vendored ReUI internals; change behavior through props and the documented API.
- Blocks are **portable React** - no `next/link`, `next/image`, or other framework-runtime imports inside them. Keep them portable.
## Demo data -> real data
- Replace every placeholder with the user's real data. Model it as **typed data structures** and **map over arrays** - never duplicate JSX per row/card. Keep small block-specific formatters next to the data.
- Wire the real source (columns, fields, fetch). For `data-grid`, implement the server fetch contract if the user needs server-side data.
- **Type from the component API, derive during render.** Type domain state through the component's own types - e.g. map status to `BadgeProps["variant"]` via a typed `Record<Status, …>` - instead of stringly-typed values. Compute view state during render; don't mirror derived data into `useState`/`useEffect`.
- **Adapt on the right base.** Use the API for the project's base (Base UI vs Radix - see [components.md](./components.md)); the installed files are already base-correct, so reuse their shape rather than translating from memory.
## Believable content (no AI tells)
- Use realistic labels, counts, timestamps, and statuses that map to a real workflow.
- No decorative buttons, fake tabs, meaningless toggles, equal-weight card walls, empty gradients, ornamental icons, or generic SaaS filler. Every element should do something.
## Operational surfaces (settings / profile / admin)
Pick ONE archetype and keep the family consistent: a vertical rail (3-6 sections), horizontal tabs (5-8), or a frame/stack. Prefer `frame` for tool-like surfaces, a card for profile-like ones. Don't mix archetypes in one surface.
+60
View File
@@ -0,0 +1,60 @@
# CLI: registry setup, license, non-interactive install
## Registry setup (one-time, per project)
Free items (the 22 components and all `c-*` examples) need only the plain string registry in `components.json`:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium items (blocks; Motion Icons and templates) require a ReUI license at install:
1. Add the key to `.env.local`:
```bash
REUI_LICENSE_KEY=your-license-key
```
2. Switch `components.json` to the authenticated object form:
```json
{
"registries": {
"@reui": {
"url": "https://reui.io/r/{style}/{name}.json",
"headers": { "Authorization": "Bearer ${REUI_LICENSE_KEY}" }
}
}
}
```
The shadcn CLI expands `${REUI_LICENSE_KEY}` from `.env.local` inside `components.json`, but an MCP client config never expands variables, so a ReUI MCP server config must carry the raw token instead (for example `reui_pat_your_token_here`).
The MCP `get_project_context` tool returns the right config. Full guide: https://reui.io/docs/registry?ref=skill
## Installing
Use the project's package runner (check `packageManager`):
```bash
npx shadcn@latest add @reui/<name> --yes # npm
pnpm dlx shadcn@latest add @reui/<name> --yes # pnpm
bunx --bun shadcn@latest add @reui/<name> --yes # bun
```
`--yes` skips confirmation prompts. The CLI auto-detects the package manager from the lockfile (there is no `--package-manager` flag). It also resolves the correct base+style variant from `components.json`, so do not pass a style.
## Handling prompts and conflicts
- **Always pass `--yes`** so the CLI does not block on confirmation prompts.
- **Do NOT pass `--overwrite` by default.** If the CLI reports an existing file, read the output and resolve deliberately: install under a different name, adjust the path, or ask the user. Only use `--overwrite` when the user explicitly wants to replace a file.
- **Preview first when touching an existing project**: `npx shadcn@latest add @reui/<name> --dry-run` shows what would change; `--diff <file>` shows a specific file's diff. Use these before overwriting.
- Run from the **project root** so `components.json` and `.env.local` are found.
## Free vs premium boundary
- Public, no key: `c-*` examples and the 22 components (`@reui/data-grid`, `@reui/badge`, ...) that those examples depend on.
- Key required at install: blocks (`@reui/<category>-N`) need a Pro or Ultimate license; Motion Icons (`@reui/icons/...`) and templates need Ultimate.
If an install 401/403s, the license key is missing, invalid, or the plan does not cover that resource (blocks: Pro or higher; icons and templates: Ultimate). Point the user to https://reui.io/account?ref=skill (their key) or https://reui.io/pricing?ref=skill (upgrade).
+428
View File
@@ -0,0 +1,428 @@
# ReUI components
The 22 ReUI building blocks: `alert`, `autocomplete`, `badge`, `cascader`, `code-block`, `data-grid`, `date-selector`, `event-calendar`, `filters`, `frame`, `gantt`, `icon-stack`, `icon-tile`, `kanban`, `number-field`, `phone-input`, `rating`, `scrollspy`, `sortable`, `stepper`, `timeline`, `tree`. Examples and blocks are composed from these.
**Rule one: never guess a component's API. Read it first.** Call **`get_component(name)`** for its inline `api` (props + usage, no web fetch), and **share the result's `docsUrl`** (the component's API documentation page) with the user whenever you work with that component's API, so they have the full reference (the `/llms.txt` index is a further fallback). Then call **`get_examples(name)`** to install a worked example and copy real composition. The contracts below are first-try orientation (required props, composition shape, the one gotcha); the inline `api` is the full reference. No single block fits? Compose: search the components you need, read each `get_component`, install a `get_examples` example per component, and adapt.
## data-grid (the flagship - read its API every time)
`data-grid` wraps TanStack Table v9. It is NOT a styled `<table>` and does NOT take `data`/`columns` props directly. The contract:
- Build a TanStack table instance with `useTable({ features: dataGridFeatures, ... })` (columns, data). `dataGridFeatures` is exported by the primitive and already bundles sorting, filtering, pagination, row selection, expanding, pinning, resizing and faceting, so there are no per-table row models to wire.
- Pass that instance to `<DataGrid table={table} recordCount={total}>`.
- Compose the body with `DataGridTable` inside `DataGrid`, and enable features through `tableLayout` (e.g. `{ headerSticky: true, columnsResizable: true }`), not ad-hoc classes.
- Server-side data uses the documented fetch shape (`recordCount` is the total for pagination).
```tsx
const table = useTable({
features: dataGridFeatures,
data,
columns,
})
<DataGrid table={table} recordCount={data.length}>
<DataGridTable />
</DataGrid>
```
Common mistakes:
- **Incorrect:** `<DataGrid data={rows} columns={cols} />` - these props do not exist. **Correct:** build a `useTable({ features: dataGridFeatures, ... })` instance and pass `table={table}` + `recordCount`.
- **Incorrect:** a raw `<table>` / hand-rolled pagination. **Correct:** use `data-grid`; read its API for sticky header, pagination, virtualization, row selection.
- **Incorrect:** styling rows/cells with arbitrary classes. **Correct:** drive layout via `tableLayout` and the primitive's `DataGridColumnMeta` (e.g. `cellClassName`, `headerTitle`), set through the bundle's `columnMeta` slot.
## event-calendar
**Required:** events via `events`/`onEventsChange` (controlled) or `defaultEvents` (uncontrolled), plus a height on the root.
**Shape:**
```tsx
<EventCalendar defaultEvents={events} defaultView="month" className="h-[560px]">
<EventCalendarNav />
<EventCalendarContent />
</EventCalendar>
```
**Gotcha:** headless-first: `EventCalendarContent` renders the active view (month/week/day/days/agenda; a resource view activates when `resources` is passed) - there is no per-view JSX to compose. Events are `{ id, title, start, end (exclusive), allDay?, color?, recurrence?, resourceId? }`. Mutations flow through `onEventUpdate`/`canDropEvent` (return `false` to reject); the root needs an explicit height because it is a min-h-0 flex column.
## gantt
**Required:** `resources` (the left tree) plus bars via `events`/`defaultEvents` attached by `resourceId`.
**Shape:**
```tsx
<Gantt defaultEvents={bars} resources={tasks} defaultScale="month" className="h-[480px]">
<GanttNav />
<GanttView />
</Gantt>
```
**Gotcha:** bars move along the time axis only (never across rows) and are all-day spans with exclusive `end`; `progress` is 0-100. Scales are `day | week | month | quarter | year`. Zoom control, infinite scroll, summary rollups, and row checkboxes are ON by default - turn off what you do not need. Same `onEventUpdate`/`canDropEvent` commit pipeline as `event-calendar`; the root needs an explicit height.
## kanban
**Required:** `value` (`Record<string, T[]>`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Kanban value={cols} onValueChange={setCols} getItemValue={(i) => i.id}>
<KanbanBoard>
{Object.entries(cols).map(([id, items]) => (
<KanbanColumn key={id} value={id}>
<KanbanColumnHandle><h3>{id}</h3></KanbanColumnHandle>
<KanbanColumnContent value={id}>
{items.map((i) => (
<KanbanItem key={i.id} value={i.id}>
<KanbanItemHandle>{i.title}</KanbanItemHandle>
</KanbanItem>
))}
</KanbanColumnContent>
</KanbanColumn>
))}
</KanbanBoard>
<KanbanOverlay><div className="bg-muted size-full rounded-md" /></KanbanOverlay>
</Kanban>
```
**Gotcha:** state is `Record<columnId, T[]>`. Each `KanbanColumnContent value` must match its parent `KanbanColumn value`. Omit `KanbanOverlay` and the drag preview silently breaks.
## sortable
**Required:** `value` (`T[]`), `onValueChange`, `getItemValue`
**Shape:**
```tsx
<Sortable value={items} onValueChange={setItems} getItemValue={(i) => i.id}>
{items.map((i) => (
<SortableItem key={i.id} value={i.id}>
<SortableItemHandle><GripVertical /></SortableItemHandle>
{i.label}
</SortableItem>
))}
</Sortable>
```
**Gotcha:** a flat 1D reorder list (not columns - that is `kanban`). `getItemValue` must return a stable, unique string. Pass `layout="grid"` or `layout="nested"` for non-list layouts.
## filters
**Required:** `fields` (`FilterField[]`). The value is ONE `FilterQuery` tree - `query` + `onQueryChange`, or uncontrolled `defaultQuery`.
**Shape:**
```tsx
const fields: FilterField[] = [
{ id: "title", label: "Title", type: "text" },
{
id: "status",
label: "Status",
type: "select",
options: [
{ value: "active", label: "Active" },
{ value: "archived", label: "Archived" },
],
},
]
const [query, setQuery] = useState<FilterQuery>(() => createFilterQuery())
<Filters fields={fields} query={query} onQueryChange={setQuery} />
```
**Gotcha:** the state is a TREE, not a list of chips. `FilterQuery` is a group of rules joined by `and`/`or` and a group may hold another group, so `(A and B) or C` is expressible; a rule is `{ id, type: "rule", path: ["status"], operator, value }` and `path` is the whole nested attribute path, root first. The pre-rewrite API is GONE: there is no `filters`/`onChange` prop, no `FilterFieldConfig` (fields are `FilterField`, nested through their own `fields`, keyed `id` not `key`), and no `createFilter()` - it minted ids inside a pure function and broke hydration, so ids now come from `createFilterIdFactory(seed)` seeded off `useId`, and `createFilterQuery()` / `createFilterRule()` take one. Read the query back with `flattenFilterConditions` (`{ path, field, operator, values, negated }` per rule, incomplete rules skipped) and walk the tree yourself when the parentheses carry meaning - the primitive compiles nothing, no SQL, no query string.
`variant` picks the chrome over that one query: `"basic"`, the default, is the flat chip row for a toolbar over a table; `"advanced"` is the condition builder, hung off a trigger or rendered in place with `advancedMode="inline"`. Both read and write the same tree, so a saved view built in one opens in the other. Other props worth knowing before you hand-roll them: `size` is two rungs, `"sm" | "default"`, resolved per style (there is no `lg`); `reorderable` turns on drag and Alt+Arrow row moves in the builder; `onBeforeQueryChange` is the ONE veto point for every write (return `false` to refuse, it cannot rewrite); `editors` registers custom value editors a field selects by `editor` name; `labels` / `operatorLabels` own every rendered string; `pathCollapse` + `maxPathSegments` shorten deep attribute paths; `renderChip` / `renderValue` / `renderEmpty` replace rendered parts. On a field, `loadOptions` supplies async options with paging and `resolveValues` renders a chip restored from a saved view whose option was never loaded. Pairs naturally with `data-grid`.
## cascader
**Required:** `items` (a tree of `{ value, label, children? }`), plus the panel parts inside `CascaderContent`.
**Shape:**
```tsx
<Cascader items={items} value={value} onValueChange={setValue}>
<CascaderTrigger render={<Button variant="outline" />}>
<CascaderValue placeholder="Select an attribute" />
</CascaderTrigger>
<CascaderContent className="w-80">
<CascaderPanel>
<CascaderNav>
<CascaderBreadcrumb />
<CascaderInput />
</CascaderNav>
<CascaderEmpty />
<CascaderList maxHeight={288}>
<CascaderItems />
</CascaderList>
<CascaderStatus />
</CascaderPanel>
</CascaderContent>
</Cascader>
```
**Gotcha:** pressing a branch NAVIGATES, it does not select - only leaves are selectable until you pass `selectable="any"` or a predicate, and once a branch is selectable its chevron becomes the only way to open it. `CascaderInput` must stay inside `CascaderContent` (Base UI refills the query from the selection when the input sits outside the popup). Always include `CascaderStatus`: it is the live region announcing level changes, which the visual breadcrumb does not provide to screen readers. Accepts a flat adjacency list via `getParent` as well as nested `children`. `searchScope="deep"` searches every level and annotates results with their path; `multiple` gives checkbox rows; `inline` + a bare `CascaderPanel` embeds it with no popover.
The shape above is `mode="drill"`, the default. `mode="tree"` keeps the same parts (drop `CascaderBreadcrumb`, pass `showBack={false}`, drive expansion with `expanded`/`onExpandedChange`); `mode="columns"` REPLACES `CascaderList` + `CascaderItems` with a single `CascaderColumns`, and has no breadcrumb. Other props worth knowing before you hand-roll them: `cascade` (multi-select only, parent/child selection with indeterminate branches - pair it with `selectable="any"`, since a leaf-only tree can never cascade), `indicator={false}` to drop the single-select check and its gutter (visual only, no-op with `multiple`), `virtualize`/`virtualizeThreshold` plus `CascaderVirtualItems` for long levels, and `getChildren` for async levels with cursor paging, retry on failure and optional `prefetch`. `CascaderFooter` pins commands below the list (`actions` is the quick path) and `CascaderSubmenu` opens one as a side-anchored flyout with the full menu keyboard model. To head a run of rows use `CascaderGroup` wrapping a `CascaderLabel` - a bare label inside a listbox names nothing and is dropped from the accessibility tree - and `CascaderSeparator` for the rule between runs. Every rendered string comes from `labels`, and the panel is RTL-correct under a `DirectionProvider` or `dir="rtl"`.
## date-selector
**Required:** none, but wire `onChange` to capture the value.
**Shape:**
```tsx
const [value, setValue] = useState<DateSelectorValue | undefined>()
<DateSelector value={value} onChange={setValue} label="Due date" />
```
**Gotcha:** the value is a structured `DateSelectorValue` (period / operator / start+end dates), NOT a `Date` - never pass a raw `Date`. Use `allowRange={false}` to lock single-date picking. Read `get_component("date-selector")` for the value shape.
## tree
**Required:** `tree` (a `@headless-tree/core` instance you construct)
**Shape:**
```tsx
<Tree tree={tree}>
{tree.getItems().map((item) => (
<TreeItem key={item.getId()} item={item}>
<TreeItemLabel />
</TreeItem>
))}
</Tree>
```
**Gotcha:** `Tree` is a styled shell - it takes a headless-tree instance via `tree`, NOT `data`/`items` props. Build the instance with `@headless-tree/react`. External API: https://headless-tree.lukasbach.com/
## stepper
**Required:** `StepperItem step` (number), `StepperContent value` (number)
**Shape:**
```tsx
<Stepper defaultValue={1}>
<StepperNav>
<StepperItem step={1}>
<StepperTrigger><StepperIndicator>1</StepperIndicator></StepperTrigger>
<StepperSeparator />
</StepperItem>
<StepperItem step={2}>
<StepperTrigger><StepperIndicator>2</StepperIndicator></StepperTrigger>
</StepperItem>
</StepperNav>
<StepperPanel>
<StepperContent value={1}>Step 1 content</StepperContent>
<StepperContent value={2}>Step 2 content</StepperContent>
</StepperPanel>
</Stepper>
```
**Gotcha:** steps are 1-indexed. Without `StepperPanel` + `StepperContent` you render the nav trail but no body. Put `StepperSeparator` in every `StepperItem` except the last.
## timeline
**Required:** `TimelineItem step` (number)
**Shape:**
```tsx
<Timeline>
<TimelineItem step={1}>
<TimelineHeader>
<TimelineDate>March 2024</TimelineDate>
<TimelineTitle>Project initialized</TimelineTitle>
</TimelineHeader>
<TimelineIndicator />
<TimelineSeparator />
<TimelineContent>Repo and architecture set up.</TimelineContent>
</TimelineItem>
</Timeline>
```
**Gotcha:** each item needs a unique `step`. `orientation` is `"vertical"` (default) or `"horizontal"`. This is a static event display, not interactive like `stepper`.
## autocomplete
**Required:** `items` (array; each item has at least `value`)
**Shape:**
```tsx
<Autocomplete items={items}>
<AutocompleteInput placeholder="Search..." />
<AutocompleteContent>
<AutocompleteEmpty>No results found.</AutocompleteEmpty>
<AutocompleteList>
{(item) => (
<AutocompleteItem key={item.value} value={item}>{item.label}</AutocompleteItem>
)}
</AutocompleteList>
</AutocompleteContent>
</Autocomplete>
```
**Gotcha:** `AutocompleteList` takes a render-prop `(item) => ReactNode`, NOT a mapped array of children. External API: https://base-ui.com/react/components/autocomplete
## phone-input
**Required:** none, but wire `onChange`.
**Shape:**
```tsx
<PhoneInput placeholder="Enter phone number" defaultCountry="US" value={value} onChange={setValue} />
```
**Gotcha:** `value`/`onChange` use an E.164 string (e.g. `"+14155551234"`), not a display-formatted string; `onChange` can fire `undefined`. `defaultCountry` is a 2-letter ISO code. Wraps `react-phone-number-input`.
## number-field
**Required:** wrap the controls in `NumberFieldGroup`.
**Shape:**
```tsx
<NumberField defaultValue={0}>
<NumberFieldScrubArea label="Quantity" />
<NumberFieldGroup>
<NumberFieldDecrement />
<NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldGroup>
</NumberField>
```
**Gotcha:** import from `@/components/ui/number-field`. The accessible label goes on `NumberFieldScrubArea`, not `NumberField`. External API: https://base-ui.com/react/components/number-field
## rating
**Required:** `rating` (number)
**Shape:**
```tsx
<Rating rating={4.5} showValue editable onRatingChange={setRating} />
```
**Gotcha:** supports decimals (partial stars). Pass `editable` + `onRatingChange` for interactive input; omit both for a read-only display.
## scrollspy
**Required:** `targetRef` (the scroll container ref)
**Shape:**
```tsx
<Scrollspy targetRef={containerRef}>
<a href="#s1" data-scrollspy-anchor="s1">Section 1</a>
<a href="#s2" data-scrollspy-anchor="s2">Section 2</a>
</Scrollspy>
<div ref={containerRef}>
<div id="s1">...</div>
<div id="s2">...</div>
</div>
```
**Gotcha:** each link's `data-scrollspy-anchor` must match a section `id`. `targetRef` is the scrollable container (defaults to the window).
## frame
**Required:** `Frame` > `FramePanel`
**Shape:**
```tsx
<Frame>
<FramePanel>
<FrameHeader>
<FrameTitle>Title</FrameTitle>
<FrameDescription>Description</FrameDescription>
</FrameHeader>
<div className="p-5">Content</div>
<FrameFooter>Footer</FrameFooter>
</FramePanel>
</Frame>
```
**Gotcha:** a structured card shell for tool-like surfaces. `stacked` connects multiple panels with shared borders; `dense` removes panel padding; radius via the `--frame-radius` CSS variable.
## icon-stack
**Required:** one child icon
**Shape:**
```tsx
<IconStack aria-hidden="true">
<InboxIcon className="size-4" />
</IconStack>
```
**Gotcha:** isometric layered artwork for empty states and illustrations; style the inner icon via its own `className`. Mark purely decorative stacks `aria-hidden="true"` and keep the real label in surrounding copy.
## icon-tile
**Required:** one child icon
**Shape:**
```tsx
<IconTile variant="elevated" size="lg">
<PackageIcon />
</IconTile>
```
**Gotcha:** the square container an icon sits in, so every list row, feature card and empty state shares one affordance. `variant`: `outline` (default) | `elevated` (muted fill, raised ring) | `soft` (tinted nested, tone from currentColor) | `solid` (filled tone, contrasting glyph) | `frame` (double container). `soft` and `solid` retint from one text color class (they default to `text-primary`). `size`: `xs | sm | default | lg | xl` (24/32/40/48/64px tile, glyph scales 12/14/16/20/24px). `radius`: `default | full`. Do not set a `size-*` class on the child icon unless you mean to override the tile's glyph size; recolor with `className` on the tile, not the icon.
## code-block
**Required:** `code` + `language`, or pre-highlighted `lines`.
**Shape:**
```tsx
<CodeBlock code={code} language="tsx" />
<CodeBlock code={code} language="tsx" showLineNumbers maxLines={20}>
<CodeBlockHeader>
<CodeBlockTitle>use-totals.ts</CodeBlockTitle>
<CodeBlockLanguage />
<CodeBlockCopyButton className="ml-auto" />
</CodeBlockHeader>
<CodeBlockExpandButton />
</CodeBlock>
```
**Gotcha:** the one-liner is already a complete block; every child (header, title, language label, copy button, wrap toggle, expand button, line actions) is optional chrome, at any depth. Shiki is the only npm dependency and loads lazily, one chunk per language, on first highlight; `lines` (from `highlightCode` in `code-block-highlight`, which is server-safe and has no `"use client"`) or `highlight={false}` loads nothing at all. `maxLines` caps the height AND marks the block collapsible, which is what makes `CodeBlockExpandButton` appear. Use `variant="ghost"` when the block sits inside a surface that already has a border.
## alert
**Required:** `Alert` > `AlertTitle`
**Shape:**
```tsx
<Alert variant="success">
<ShieldCheckIcon />
<AlertTitle>Security update</AlertTitle>
<AlertDescription>Enable two-factor authentication.</AlertDescription>
<AlertAction><Button size="xs">Update</Button></AlertAction>
</Alert>
```
**Gotcha:** shadcn-compatible API. `variant`: `default | destructive | info | success | warning | invert`. The non-default variants use ReUI extended color tokens (`--success`/`--info`/`--warning`/`--invert`), which the install adds. Defer generic alert rules to the shadcn skill.
## badge
**Required:** none (text child).
**Shape:**
```tsx
<Badge variant="success-light" size="sm">Success</Badge>
<Badge variant="outline" radius="full">Pill</Badge>
```
**Gotcha:** shadcn-compatible. Rich `variant` set (solid, `-outline`, `-light` per color), `size` `xs..xl`, `radius` `default | full`. Like `alert`, the color variants rely on ReUI extended tokens. Prefer `Badge` variants over raw color classes for statuses.
## base vs radix - write for the project's base
ReUI ships every component in two builds: `base` (Base UI) and `radix` (Radix UI). The install command and name are identical, and the CLI installs the build matching the project. But you must write/adapt code against the **right base**, because their APIs differ.
**Detect the base first.** Read `components.json` -> `style` and take the segment before the first `-`:
- `"style": "base-nova"` -> **Base UI**
- `"style": "radix-nova"` -> **Radix UI**
**Then use that base's API.** The deltas mirror shadcn's base-vs-radix split:
- Slot/composition: Base UI `render={<… />}` vs Radix `asChild`.
- `Select`: Base UI takes `items`; Radix uses `<SelectItem>` children.
- `ToggleGroup`: Base UI `multiple` boolean vs Radix `type="single" | "multiple"`.
The safest path is to **read the installed files and `c-*` examples** - they're already in your base, so reuse their wiring instead of guessing. When `get_component`'s inline `api` or an example shows the other base's shape, translate it to your base (or `validate_usage` to confirm). Defer the generic base/radix mechanics to the shadcn skill.
+45
View File
@@ -0,0 +1,45 @@
# Craft: make ReUI UI exceptional, not generic
ReUI items ship senior-designer quality. Your adaptation has to hold that bar, so the result reads like a real product surface a team would keep - not a wireframe an AI generated. Use these alongside the reuse rules in [adapting.md](./adapting.md).
## Have a point of view
Pick an emotional register before you compose - calm, operational, premium, editorial, dense, energetic - and let layout, spacing, surface treatment, and icon behavior all reinforce it. One or two memorable decisions and restraint everywhere else beats ten generic ones. UI with no point of view reads as generated.
## Brutally clear hierarchy
One focal point per card or panel: the dominant metric or task first, its label second, supporting detail third. The first thing the eye lands on should be the right thing; secondary text must read as secondary. Borders, separators, and surfaces do real work to create 2-3 information bands - don't flatten everything to equal weight.
## Spacing rhythm and deliberate density
Gaps are a signal, not a default. Keep them intentional and consistent within a family (`gap-1`/`gap-2` for tight operational rows, larger gaps for section breaks), and smaller within a group than between groups. Match the surrounding ReUI density; don't pad an operational surface like a marketing page, and don't drift density mid-section. The composition should still feel authored in grayscale.
## Cover the real states (the usual miss)
A surface isn't done at the happy path. Compose, and wire:
- **Empty** - a purposeful empty state (short message + the primary action), never a blank panel.
- **Loading** - a **skeleton** that matches the real layout, not a centered spinner.
- **Error** - an inline, recoverable error with a retry, announced via `role="status"`/`aria-live`.
Derive these from an element the block already has (don't invent parallel markup), or `get_examples` for a state-specific example.
## Responsive by default
Mobile-first, not mobile-afterthought. In constrained rows/cards/sidebars, put `min-w-0` on the shrinking container and `truncate` long single-line labels; protect the primary label's width and let secondary content compress. Reflow layouts (multi-column -> single column) rather than just shrinking them. Desktop and mobile should both look designed.
## Motion, subtly
Motion should clarify, not decorate. Use ReUI Motion Icons on primary actions for a subtle hover cue; keep transitions short (~200-300ms) with calm easing; prefer a skeleton pulse over a spinner. No bouncing, no gratuitous entrance animations on every element.
## Real, activated content
Use believable, typed data (realistic labels, counts, timestamps, statuses that map to a real workflow) - never lorem or abstract filler. Every visible control does something: no decorative buttons, fake tabs, meaningless toggles, or stats with no job. It must still hold with long names, empty values, and crowded data.
## Avoid the AI tells
These instantly read as generated - don't ship them: equal-weight card walls, empty gradients, repetitive padding everywhere, generic enterprise copy, ornamental icons, and number tiles that don't earn their place.
## The bar
Before you finish, ask: **would a product team keep this instead of replacing it? Does it still feel strong after swapping in real content?** If not, reuse the shipped ReUI design harder - don't restyle it into something new - then run the [quality.md](./quality.md) gates.
+39
View File
@@ -0,0 +1,39 @@
# Icons (ReUI delta over shadcn)
Follow the shadcn icon rules (use the project's configured `iconLibrary`, `data-icon` on icons inside `Button`, no sizing classes on icons inside components, pass icons as component objects not string keys). ReUI adds the following.
## Portable icons (library-agnostic)
ReUI components, examples, and blocks are authored to be icon-library-agnostic. When `iconLibrary` is set in `components.json`, the shadcn CLI installs each item's icons in **your** library automatically - you swap nothing. If an installed item's icons don't match your project (for example `iconLibrary` isn't set, so they came in from the item's demo library), change the **import source and component name** to your library, keeping the same icon-name semantics:
- `lucide` -> `lucide-react`
- `tabler` -> `@tabler/icons-react`
- `phosphor` -> `@phosphor-icons/react`
- `remix` -> `@remixicon/react`
- `hugeicons` -> `@hugeicons/react`
Don't assume `lucide-react`; read `iconLibrary` from `components.json`.
## Keep icons purposeful
Icons support the hierarchy, they don't replace it: keep them small, matched to the surrounding density, and decorative ones `aria-hidden="true"` (an icon-only control still needs an accessible label on the control). Don't add ornamental icons that do no job.
## Motion Icons (the `@reui/icons/...` set)
ReUI ships its own icon set in 4 styles (outline, solid, duotone, filled), each icon in two variants:
```bash
npx shadcn@latest add @reui/icons/default/<style>/<name> --yes # static
npx shadcn@latest add @reui/icons/animated/<style>/<name> --yes # hover-animated (motion/react)
```
Finding them via the MCP is free; installing requires an Ultimate license (`REUI_LICENSE_KEY`, see [cli.md](./cli.md)). Reach for a Motion Icon on a primary action when a subtle hover cue helps; keep motion restrained.
Finding icons:
- Several icons (the common case): **`search_icons(concepts[])`** - up to 24 concepts in one call, the best icons per concept with install commands. Pass `animated: true` to get only icons with a hover-animated Motion variant.
- One icon: `search` with `type: "icon"`.
- Icon results and `get_icon` carry `animated: true` and `installAnimated` when an animated variant exists - use those install strings, do not construct paths by hand.
- Every icon result carries a `previewUrl` (its live icon-category page) - **share it with the user** so they can SEE the icon before installing.
The `icon-stack` component composes multiple icons into a stacked display.
+22
View File
@@ -0,0 +1,22 @@
# Quality gates (security, accessibility, scroll)
These are the **done gate**, not a nice-to-have: before you call any ReUI work finished, call the MCP `get_audit_checklist` tool and pass every item below (plus the craft bar in [craft.md](./craft.md)). Then typecheck and lint.
## Security
- Never `dangerouslySetInnerHTML`. Render data as text/components.
- External links (`target="_blank"`) must always pair `rel="noopener noreferrer"`.
- No real PII, secrets, or tokens in demo or committed code. Remote media only from sources the project already allows.
## Accessibility
- Implicit list/card items that navigate get real anchors with a standard hover affordance.
- Icon-only or numeric buttons need an `aria-label`; decorative icons get `aria-hidden`.
- Every non-submit button is `type="button"`.
- Keyboard + focus: everything interactive is reachable in a sensible Tab order with a visible focus ring; layers (dialogs/sheets/menus) trap focus and close on `Escape`. ReUI components ship standard keyboard behavior - read each component's inline `api` rather than re-implementing it.
- Announce async UI: loading and error messages use `role="status"` / `aria-live` so they're not silent to screen readers.
## Scroll mechanics
- Make scroll regions with a parent-owned height: a `min-h-0` + flex chain down to the scroll container. Never guess a `max-h`.
- The scroll container owns `overflow-auto`; ancestors stay `min-h-0` so the height resolves.
+35
View File
@@ -0,0 +1,35 @@
# ReUI registry structure
ReUI is a shadcn-compatible registry with four entity types. **Examples and blocks are built FROM components** - reuse them, don't rebuild.
- **component** - one of the 22 ReUI building blocks with a real API (`data-grid`, `kanban`, `filters`, `date-selector`, `tree`, ...). Install directly (`@reui/data-grid`) or let it come in as a dependency of an example/block. Free. Read its API with `get_component(name)`.
- **example** - a free `c-*` single-pattern use-case of a component (`c-kanban-1`, `c-data-grid-3`). Install one and read it to copy real composition. Find a component's examples with `get_examples(name)`.
- **block** - a premium, full-page section that composes several components (`data-grid-2`, `pricing-page-1`). Pro or Ultimate license at install. Adapts to your active theme via semantic tokens.
- **icon** - Motion Icons in 4 styles (outline, solid, duotone, filled), static (`@reui/icons/default/<style>/<name>`) and hover-animated (`@reui/icons/animated/<style>/<name>`). Ultimate license at install. See [icons.md](./icons.md).
## The @reui registry
Install everything through the shadcn CLI: `npx shadcn@latest add @reui/<name> --yes`. The CLI reads the `@reui` registry from the project's `components.json`. Free items need only the plain string form:
```json
{ "registries": { "@reui": "https://reui.io/r/{style}/{name}.json" } }
```
Premium installs need the authenticated form + `REUI_LICENSE_KEY` in `.env.local` - see [cli.md](./cli.md). The MCP `get_project_context` tool returns the right config.
## Know your base: base or radix
ReUI ships every item in two builds - `base` (Base UI) and `radix` (Radix UI) - with mirrored names. The CLI installs the build matching your project automatically, but **you must write code against the right base's API**. Detect it from `components.json` -> `style`: the segment before the first `-` is the base (`base-nova` -> Base UI, `radix-nova` -> Radix UI). The installed files and `c-*` examples are already in your base - read them and adapt on that base. See [components.md](./components.md) for the API deltas.
**So the MCP's own `docsUrl` and `previewUrl` match your base**, send your `style` to the MCP: add `?style=<your components.json style>` to the ReUI MCP server URL (or set an `X-Reui-Style` header) in your MCP client config - set once, applies to every call. The MCP then resolves docs/preview links to YOUR library (`/docs/components/radix/...`, `/preview/radix/...` for a radix project) instead of the default base; `get_project_context` echoes back the style it currently sees so you can confirm it. Install commands are unaffected (the CLI already installs the right variant). If you notice the MCP returning `base` links for a `radix` project, tell the user to add `?style=` to the server URL.
Blocks adapt to your active theme through semantic tokens and CSS variables - change the theme and every block follows.
## Free vs premium
- **Free, no key:** the 22 components, all `c-*` examples, the ReUI MCP, and this skill.
- **Premium, license required at install:** blocks (Pro or Ultimate), Motion Icons and templates (Ultimate). Set `REUI_LICENSE_KEY` (see [cli.md](./cli.md)).
## Component API index
The canonical index of every component's API docs is **https://reui.io/llms.txt** (returned as `componentsApiUrl` in MCP results). Prefer the inline `api` from `get_component`; use the index/docs as the fallback.
+26
View File
@@ -0,0 +1,26 @@
# Styling (ReUI delta over shadcn)
Follow the shadcn skill for the generic rules (semantic colors not raw values, `gap-*` not `space-y-*`, `size-*`, `cn()`, no manual `dark:` overrides, no overlay `z-index`). This file is only the ReUI-specific additions.
## ReUI extended semantic tokens
ReUI adds semantic tokens beyond shadcn's base set. Use these instead of raw colors for status and emphasis:
- `--success` / `--success-foreground`
- `--info` / `--info-foreground`
- `--warning` / `--warning-foreground`
- `--destructive-foreground` (paired with shadcn's `--destructive`)
- `--invert` / `--invert-foreground` (inverted surfaces)
Use them as Tailwind utilities (`bg-success text-success-foreground`, `text-warning`, ...). They are defined in the project's global CSS and registered with Tailwind (`@theme inline` on v4). If a token is missing in the project, add it to the global CSS file (never a new file) following the same `name` / `name-foreground` convention, exactly as the shadcn customization rules describe.
**Incorrect:** `<span className="text-green-600">Active</span>`
**Correct:** `<Badge variant="success">Active</Badge>` or `<span className="text-success">Active</span>`
## Blocks follow your theme
When you install a block it adapts to your active theme through the semantic tokens above and the project's CSS variables. Don't hardcode style-specific values into installed block code and don't fork it to "restyle" - change the theme via the CSS variables / a preset and every block follows. Want a different look? `search` for a block whose design already fits instead of re-skinning one.
## Density and typography rhythm
ReUI operational UI usually feels dense, not airy. Keep the gap between a title and its supporting description tight by default (`gap-0.5`, `space-y-1`, or `space-y-px`), and smaller than the gap between sections. Match the surrounding ReUI density when you add rows or fields; do not pad operational surfaces like a marketing page.
+54
View File
@@ -0,0 +1,54 @@
# Workflow: find -> install -> read API -> adapt
The core ReUI loop. The MCP tells you what to install and gives you the API; the shadcn CLI installs it; you turn the installed files into correct, themed, data-wired code by **reuse**, not redesign.
## 1. Find (ReUI MCP `search` / `compose_page`)
**Full multi-section page ask?** Call `compose_page(intent, sections?)` FIRST, before searching block-by-block. It returns ordered sections, each with the best block for the intent (top pick + alternates); sections listed in `unavailableSections` have no real inventory - compose those from components, do not force a bad block.
For everything else, call `search` with the user's intent. Pass structured hints whenever you can infer them - you are an LLM, so do the parsing the server cannot:
- `type`: `"component"` (one of the 22 building blocks), `"example"` (a c-\* use-case), `"block"` (a full page/section), `"icon"`.
- `component`: the ReUI component the request implies (`"data-grid"`, `"kanban"`, ...).
- `category`, `features` (e.g. `["sortable","pagination"]`), `free`.
Example: "build a users management page with filters" -> `search({ query: "users management page with filters", type: "block", component: "data-grid", features: ["filters"] })`.
Each result has `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `termCoverage`, and `whyMatch`. `score` is relative to the top hit (the top is ~100 by construction), not an absolute quality - compare results to each other, and show the user the top options if several score closely; do not silently guess. A low `termCoverage` means a weak match even with a high score - rephrase or widen.
**Always show the preview link.** Whenever you list or recommend items - from `search`, `search_icons`, `list_components`, `compose_page`, or a getter - include each item's `previewUrl` (a live preview page) so the user can SEE it before you install. Blocks and examples link to an individual live preview; icons and components to their live category/component page. This applies to every listing, not only a single pick.
## 2. Install (shadcn CLI)
Run the result's `install` command from the project root, non-interactively:
```bash
npx shadcn@latest add @reui/<name> --yes
```
The CLI reads `components.json`, installs the correct base+style variant, resolves `registryDependencies` (a block pulls in its components), installs npm deps, and rewrites aliases. Do not pass the base/style. See [cli.md](./cli.md).
## 3. Read the API (do not guess props)
Before writing code against any component an item uses:
1. The item's `componentDigests` already give a 1-line contract per component - often enough to wire it. For the full API, call **`get_component(names)`** with ALL of `componentsUsed` in ONE call (it accepts an array) and read each inline `api` - no web fetch. **Share the component's `docsUrl`** (its API documentation page) with the user whenever you work with that component's API, so they have the full reference; the `/llms.txt` index is a further fallback.
2. Call **`get_examples(name)`** for the free `c-*` examples of that component; install one and **read the added files** to copy the exact composition. This is the fastest correct path - the example shows real wiring you adapt, not invent.
3. About to write a prop you did not see in an `api` or installed file? Run **`validate_usage`** BEFORE writing the code - per-prop documented / notDocumented verdicts plus did-you-mean suggestions. notDocumented means read the API, not push on.
## 4. Adapt (reuse-first) - do not skip
Installing files is not the end, and redesigning them defeats the point. First note the project's **base** so you write the right API - read `components.json` -> `style` and take the segment before the first `-` (`base-nova` -> Base UI, `radix-nova` -> Radix UI), see [components.md](./components.md). After `add`:
1. **Read the added files**; keep the composition intact. For a block, verify the components are wired correctly (for `data-grid`: a `useTable({ features: dataGridFeatures, ... })` instance passed as `table`, `recordCount` set - see [components.md](./components.md)).
2. **Replace demo data with the user's real data** via typed structures (see [adapting.md](./adapting.md)).
3. **Fix icon imports** to the project's icon library (see [icons.md](./icons.md)).
4. **Align styling** to semantic tokens and the active theme - no raw colors (see [styling.md](./styling.md)).
5. **Validate before finalizing**: if your adaptation introduced components or props you did not read in an `api` or example, run `validate_usage` on them.
6. **Hit the craft bar** - clear hierarchy, deliberate density, the empty / loading / error states, subtle motion, and mobile-first responsiveness (see [craft.md](./craft.md)). Generic-looking output means you under-reused the design, not that it needs restyling.
7. **Pass the quality gates** (security, a11y, scroll) - call the MCP `get_audit_checklist` tool and clear every item (see [quality.md](./quality.md)).
8. **Typecheck / lint**.
## If no single block fits
Compose from components (`compose_page` tells you which sections have no block inventory via `unavailableSections`). `search` the components you need, read each `get_component` API, install a worked `get_examples` example for each, and assemble by adapting those examples. A block in the same category is a useful reference - install it and read its files to see how ReUI composes those components, then adapt.
+58
View File
@@ -0,0 +1,58 @@
# ReUI MCP: full reference
The ReUI MCP (`https://mcp.reui.io`, Streamable HTTP) is free to use but needs a ReUI account: on first use the agent signs in with ReUI (a free account is created if the user has none), so every request is tied to an account. Free covers components and examples; a Pro or Ultimate license unlocks premium blocks and Motion Icons and removes the daily request limit. It does **discovery + guidance** (search, inline APIs, page planning, validation) and never serves source; the shadcn CLI does **installation**, and the license key lives there (the `@reui` entry in `components.json`, backed by `.env.local`). Goal: from the user's intent to correct, themed, data-wired ReUI code in the **fewest tokens and calls**, with **no guessing**.
## Golden path (token-optimal - follow this order)
Most tasks need 2-4 MCP calls and ZERO web fetches:
1. **`search(query, ...hints)`** -> pick the top 1-3 results. Each result already carries `install`, `previewUrl`, `docsUrl`, `componentsUsed`, `score`, `whyMatch`. The payload is complete - do not call another tool just to "confirm" a result.
2. **`get_component([...componentsUsed])`** in ONE batched call (one name or an array of up to 20) -> read each inline `api`. This **replaces** fetching docs pages. Often skippable: search responses carry `componentDigests`, a compact API contract per referenced component.
3. **`get_examples(component)`** -> install ONE returned `c-*` example, read the added files, copy the composition.
4. **`get_install_command(item)`** only to validate a name you are unsure of (results already include `install`). Run the install with the shadcn CLI (`--yes`).
5. **`get_audit_checklist()`** before declaring done.
If you already know the exact item name, skip `search`. Everything else is situational.
## The 5 task-specific tools (when to reach for each)
- **`compose_page`** - BEFORE building any full page (dashboard, settings, billing, landing). Pass the intent (and optionally the sections you want); it returns ordered sections, each with the best premium block for the intent (top pick + alternates). Sections with no real inventory are listed honestly in `unavailableSections` - compose those from components instead of forcing a bad block.
- **`search_icons`** - whenever you need icons, especially several. Batch up to 24 concepts in one call; each concept returns its best icons with install commands. Pass `animated: true` to get only icons that have a hover-animated Motion variant.
- **`validate_usage`** - BEFORE writing code with component names or props you have not read in an inline `api` or an installed example. It checks planned names + props against the indexed API docs and registry item names; returns did-you-mean suggestions and per-prop documented / notDocumented verdicts. Deterministic, no inference - a notDocumented prop means stop and read the API, not push on.
- **`whats_new`** - when your registry knowledge might be stale (a name 404s, the user mentions an item you don't know). Returns items added/removed per build, newest first.
- **`report_issue`** - when an installed item is actually broken (bad source, wrong dependency, broken preview). Goes straight to the ReUI team; rate-limited 5/hour. Not for usage questions.
## All 19 tools
`search`, `get_block`, `get_example`, `get_icon`, `list_block_groups`, `list_block_categories`, `list_example_categories`, `list_icon_categories`, `list_components`, `get_component`, `get_examples`, `search_icons`, `compose_page`, `validate_usage`, `whats_new`, `report_issue`, `get_install_command`, `get_project_context`, `get_audit_checklist`. The MCP serves the full parameter schemas; do not guess parameters beyond them.
## Token + speed rules
- **Batch `get_component`** - ONE call with the whole `componentsUsed` array, never N calls. Skip it entirely when `componentDigests` already answers the question.
- **Read source by installing** - the MCP serves no source. To read or analyze an item's real code, install it with the shadcn CLI and open the local files. Learn an API from the inline `api` / `componentDigests`, never by reading raw source.
- **Infer `search` hints yourself** (`type`, `component`, `category`, `features`, `free`) - hints shrink the result set and the tokens. Keep `limit` low; one right result beats ten.
- Run independent calls (and the shadcn install) concurrently in one turn - serial tool calls are the main source of slowness.
- Don't repeat a search for the same intent; don't call `list_*` to "see everything" - `search` is the entry point, `list_*` is only for browsing a taxonomy the user explicitly wants to explore.
- Prefer `get_component`'s inline `api` over `docsUrl` / `/llms.txt`. Fetch a web page only as a last resort.
## Result shapes (so you don't re-fetch)
- `score` is 0-100 RELATIVE to the top hit (the top is ~100 by construction), not absolute - compare results to each other.
- `termCoverage` (0-1) is the share of the query the item matched - low means a weak match even if the score looks high; rephrase or widen the search.
- Each result carries `whyMatch`, `install`, docs/preview URLs, and a `free` flag; premium items carry `requiredPlan` (`"pro"` for blocks, `"ultimate"` for icons).
- `componentDigests` is a top-level map: a compact API contract per referenced component - often enough to wire an item without a `get_component` call.
- Icon results and `get_icon` include `animated: true` and `installAnimated` when a hover-animated Motion variant exists (animated: `@reui/icons/animated/<style>/<name>`; static: `@reui/icons/default/<style>/<name>`).
## Error playbook
- **401** - the MCP requires a signed-in ReUI account. The client prompts "Sign in with ReUI" (OAuth) on first use; a free account is created if needed. For headless/CI, pass a personal token (`reui_pat_...`, created at https://reui.io/account/mcp?ref=skill) as `Authorization: Bearer`.
- **403 / locked result** - a valid account but the plan does not cover the item: premium blocks need Pro, Motion Icons need Ultimate. Point to https://reui.io/pricing?ref=skill (upgrade). Free accounts still get all components + examples.
- **429** - rate limited (120 requests/min per IP); back off, honor `Retry-After`.
- **not found** (`found: false`) - use the returned `suggestions`, or `search`; check `whats_new` if you suspect a stale name. Never run a fabricated install command.
## Fallbacks
- No ReUI MCP: `npx shadcn@latest search @reui -q "..."` then `add` (generic, no scoring / inline API).
- The shadcn project's own MCP also works over the `@reui` registry: https://ui.shadcn.com/docs/mcp.
Per-agent MCP setup: https://reui.io/docs/mcp?ref=skill
@@ -0,0 +1,63 @@
# Локальные GeoLite2-базы (Country + ASN) для потоков по странам и ASN
## Контекст
Сейчас страна и ASN для netflow-потоков резолвятся через внешний RIPEstat API (`backend/src/services/traffic-flow-ripe.ts`): лимит 30 новых префиксов/мин, очередь на 90, кэш в PG `flow_ip_meta`. Новые IP «дозревают» с задержкой, IPv6 не покрывается (кэш индексируется только по IPv4). Локальные mmdb-базы дают мгновенный синхронный lookup всех IP без внешних вызовов.
Решения (подтверждены):
- Источник — **MaxMind GeoLite2 через P3TERX-зеркало**: `https://github.com/P3TERX/GeoLite.mmdb/raw/download/GeoLite2-Country.mmdb` и `.../GeoLite2-ASN.mmdb`. Без регистрации, ключей и tar-распаковки. Точность по стране у GeoLite2 и IPinfo паритетная (<1% ошибок у обоих, arXiv 2026); выбран P3TERX за надёжность зеркала (5.2k звёзд) и преемственность: текущий RIPE-путь и так читает GeoLite (`maxmind-geo-lite`), история в кэше остаётся консистентной.
- **RIPEstat остаётся fallback** (до первой загрузки баз / если lookup не дал результата).
- City-базу не качаем (lat/lng фронтенд не использует).
## Изменения
### 1. Зависимость
- `npm install -w mikrotik-manager-backend maxmind` — sync-чтение mmdb, встроенные TS-типы, без транзитивных зависимостей, Node 22 ок.
### 2. Новый сервис `backend/src/services/traffic-flow-geoip.ts`
(по конвенциям окружения traffic-flow-*: контракт → маршрут → сервис, без БД-логики в маршрутах)
- Каталог: `backend/storage/geoip/` (конвенция `storage/backups`), файлы `GeoLite2-Country.mmdb`, `GeoLite2-ASN.mmdb`.
- `initGeoip()` — открыть ридеры best-effort при старте (из `index.ts` рядом с `startTrafficFlowListener`), независимо от настроек автообновления: файлы есть — работают.
- `lookupGeoip(ip): FlowIpMeta | null` — синхронно: `country.iso_code` (fallback `registered_country.iso_code`) с валидацией `isIsoCountry`, ASN = `autonomous_system_number`, holder = `autonomous_system_organization`; приватные IP → negative-запись как в RIPE (`isNonPublicIp`); в PG не пишем (lookup и так быстрый). IPv6 поддержан ридером.
- `resolveFlowIp(ip)` — фасад: `lookupGeoip(ip) ?? lookupRipeCached(ip)`; главный экспорт для потребителей.
- `geoipStatus()` — loaded, даты сборки баз (метаданные mmdb). Тест-хук `setGeoipReadersForTests`. Смена ридеров после обновления — атомарная замена ссылок.
### 3. Коллектор `backend/src/services/geoip-update-collector.ts`
`collectGeoipUpdateOnce()` по образцу `certificate-renew-collector.ts`:
1. Conditional GET с ETag/If-None-Match из настроек → 304 = skip (фолбэк-сравнение: размер/содержимое).
2. Скачивание в `*.tmp` через глобальный `fetch` + AbortController с таймаутом (внешний HTTP из service-слоя — по правилу fastify-backend-drizzle).
3. Валидация: открыть ридер из tmp-файла, пробой 8.8.8.8 (страна US, ASN 15169).
4. `fs.rename` атомарная подмена, старый файл → `*.prev` (откат, если новый ридер не открылся).
5. Перезагрузка ридеров, статус в настройках; snapshot для `scheduler_runs` (checked/downloaded/skipped/bytes/error).
### 4. Планировщик (`backend/src/services/scheduler.ts`)
- `JOB_KEYS` += `geoip_update`; case в `runSchedulerJobBody`; блок в `refreshScheduler()` по образцу `certificates_renew`: интервал `Math.max(6ч, updateIntervalSec*1000)`, по умолчанию 7 дней (upstream обновляется еженедельно) + немедленный первый запуск при включённой настройке.
### 5. Схема и миграция
- `backend/src/db/schema.ts`: singleton `geoip_settings` — `enabled` (default true), `updateIntervalSec` (default 604800), `lastCheckAt`, `lastSuccessAt`, `lastError`, `countryBuildAt`, `asnBuildAt`, `etagsJson` (jsonb), `createdAt/updatedAt`.
- Миграция: `npm run db:generate` → файл в `backend/drizzle/`.
### 6. API + контракты
- `packages/contracts/src/geoip.ts`: zod-схемы настроек/статуса (все входы — Zod, по правилам проекта).
- Новый `backend/src/routes/geoip.ts`, регистрация в `index.ts` с prefix `/api`:
- `GET /api/geoip` — настройки + статус (ready, даты сборки, последняя проверка/ошибка);
- `PUT /api/geoip` — сохранить настройки, затем `refreshScheduler()`;
- `POST /api/geoip/update` — запустить загрузку сейчас (409, если уже идёт; флаг-гард как в коллекторах).
### 7. Интеграция в пайплайн (geoip-first, RIPE-fallback)
- `traffic-flow-engine.ts` (`queueParsedFlows`, ~строка 336): `lookupRipeCached` → `resolveFlowIp`. Логика misses не меняется: при готовом mmdb публичные IP (v4+v6) резолвятся сразу, очередь RIPE пустеет; до скачивания баз — прежнее поведение.
- Остальные вызовы `lookupRipeCached` → `resolveFlowIp` (grep: как минимум `traffic-flow-analytics.ts` ~258–271).
- `classifyFlowDst`/бренды не трогаем: holder из mmdb (org name) встаёт в существующие `HOLDER_BRANDS`-регулярки как есть.
### 8. Frontend (по next-shadcn-production / ui-guardian: только переиспользование)
- Секция «GeoIP-базы (GeoLite2)» внутри существующей `components/traffic/netflow-settings-panel.tsx`: статус (готово/не скачано, даты сборки Country/ASN, последняя проверка, ошибка), тумблер автообновления, интервал, кнопка «Обновить сейчас» с индикатором. Только уже используемые в панели примитивы (Switch/Button/поля) — никаких новых визуальных паттернов и Card-shell. API-клиент через существующие http-хелперы.
### 9. Хаускипинг, тесты, проверки
- `backend/.gitignore`: `storage/geoip/`.
- Тесты `backend/src/services/traffic-flow-geoip.test.ts` + скрипт `test:geoip` (по образцу `test:traffic-flow`): приоритет фасада (geoip hit → RIPE не зовётся; miss → fallback), negative на приватных IP, фильтрация EU/ZZ через `isIsoCountry`, коллектор с мокнутым fetch (304-skip, битый файл → подмены нет, `.prev` сохранён), dims по стране/ASN с засеянным ридером.
- Проверки после реализации (обязательно по правилам): типы/сборка бэка (`npm run build -w mikrotik-manager-backend`), типы фронта при правке UI (`npx tsc --noEmit`), `npm run test:geoip` и `test:traffic-flow`; предупреждения не игнорировать.
- Коммит: `feat(netflow): <subject по-русски>` — новая пользовательская фича (мгновенные страна/ASN в потоках), по commit-messages-ru.
- README: короткий раздел о GeoIP; примечание, что в Docker `storage/geoip` ephemeral без тома — базы перекачаются после пересоздания контейнера (~17 МБ); при желании смонтировать volume.
## Что это даёт
Страна и ASN появляются у потока мгновенно при ingest (включая IPv6), без ограничения скорости RIPE; dims `country`/`asn` в `flow_daily_dims`, аналитика (карта, топы, monthly) становятся полными сразу. Внешняя зависимость от stat.ripe.net остаётся только как fallback до первой загрузки баз.
@@ -0,0 +1,46 @@
# IKEv2/IPsec VPN: клиенты, IP-адреса и сертификаты — по образцу WireGuard
## Архитектура (как у WireGuard)
Роутер — источник истины: live-чтение через существующий REST-клиент (`mikrotik.ts`), мутации прямыми REST-вызовами, история через `config_revisions` (новая секция `"ipsec"`; `section` — TEXT без CHECK → **SQL-миграция не нужна**). Новых таблиц нет. Объекты, созданные мастером/при создании клиентов, помечаются managed-комментарием `mm-ipsec` (по образцу managed-markers).
## Модель RouterOS (что создаём/читаем)
Мастер инициализации сервера (per router): CA-сертификат (`/certificate add` + `sign`, key-usage=key-cert-sign,crl-sign), серверный сертификат (CN=адрес/домен, SAN, tls-server), `/ip/ipsec/peer` (passive, exchange-mode=ike2, certificate=серверный cert), `/ip/ipsec/profile`+`proposal` (aes-256/sha256/modp2048, pfs), `/ip pool` + `/ip/ipsec/mode-config` «VPN» (address-pool, dns), policy-template, и опциональное (default on, чекбокс) managed srcnat masquerade-правило «интернет клиентам».
Клиент = `/ip/ipsec/identity`: **Сертификат** (`/certificate add` CN=<имя> → `sign` ca=<CA> → `export-certificate type=pkcs12` → файл `.p12` скачивается с роутера → identity match-by=certificate) или **PSK** (auth-method=pre-shared-key, remote-id+secret). IP: «из пула» (общий mode-config) или статический (персональный `mc-<user>` mode-config с `address=x.x.x.x/32`). Онлайн — `/ip/ipsec/active-peers`.
Клиентские загрузки: `.p12` (cert+key+CA, passphrase) + strongSwan `.sswan` (генерация на бэкенде) + текстовая инструкция. Перекачка `.p12` — повторный export с новой passphrase (ключ остаётся на роутере).
## Бэкенд
1. **`mikrotik.ts`**: `downloadFile(name): Promise<Buffer>` — бинарно-безопасный GET `/rest/file/<name>` (с fallback `flash/<name>`, как у `uploadTextFile`; текущий конвейер парсит JSON как utf8 — нужен Buffer-режим); хелперы `signCertificate` (с поллингом готовности, sign небыстрый), `exportCertificatePkcs12(name, passphrase)`.
2. **`services/ipsec-config.ts`** (чистые, тестируемые): генератор `.sswan` и инструкции, поиск свободного IP в пуле, naming-конвенции (`ipsec-ca`, `ipsec-server`, `mc-<user>`, `ipsec-user-<name>`).
3. **`services/ipsec-ca.ts`**: `ensureCa/ensureServerCert/issueClientCert/exportClientP12` — add → sign (poll) → export → download.
4. **`services/ipsec-ros.ts`**: put/patch/delete для `/ip/ipsec/{peer,identity,mode-config,profile,proposal,policy}`, `/ip/pool`, NAT managed-правило.
5. **`services/ipsec-live.ts`**: `fetchIpsecState(server)` — параллельные GET peer/identity/mode-config/pool/active-peers/certificate (+какие из них наши по маркеру/имени) → DTO; `listIpsec()` fan-out с failures; `countIpsecClients()` для сайдбара.
6. **`entity-snapshots.ts`**: `canonicalIpsecSnapshot/parseIpsecSnapshot/planIpsecRestore` (секреты через `isHiddenSecret`); `CONFIG_SECTIONS` += `"ipsec"`; enum в `schema.ts` (миграции нет).
7. **`routes/ipsec.ts`**: `GET /ipsec` (+observed revision), `POST /ipsec/server/init`, `DELETE /ipsec/server/:serverId` (только managed-объекты), `POST /ipsec/users` (ответ включает одноразовый p12 base64), `PATCH /ipsec/users/:serverId/:rosId` (имя/статический IP/psk), `DELETE /ipsec/users/...` (identity + mc + опционально клиентский cert), `POST /ipsec/users/:serverId/:rosId/cert` (перекачка p12), `GET /ipsec/revisions` + `POST /ipsec/revisions/:id/restore` — всё по образцу `routes/wireguard.ts` (`captureAndAppendRevision`). Регистрация в `index.ts`; `/api/ipsec` в обе группы network-правил `permissions.ts`.
8. **Модуль Пользователи**: `InterfaceType` += `"ipsec"` (iface-type.ts, schema.ts enum — TEXT, без миграции); каталог интерфейсов дополняется IPsec-клиентами (identity name + CN в peerPublicKey/peerName) — привязка app-пользователя к VPN-клиенту.
## Контракт
`packages/contracts/src/ipsec.ts` (+`package.json` exports, `index.ts`): `ipsecPeerDto/identityDto/modeConfigDto/poolDto/activePeerDto/certInfoDto/serverSummaryDto`, list-response с failures, `initRequest` (CN/SAN, пул, DNS, NAT-чекбокс), `userCreateRequest` (имя, auth: certificate|psk, psk?, ip: static|pool, passphrase, daysValid), `certDownloadResponse {filename, contentB64, mime}`.
## Фронт
`app/(main)/ipsec/page.tsx` — «одно окно» по образцу WG-страницы: ServerRail + KPI (серверы IKEv2 / клиенты / онлайн / CA) + Tabs **«Клиенты»** (grid: имя, сервер, аутентификация, IP, онлайн, действия — скачать .p12/.sswan, изменить, удалить; Sheet создания клиента), **«Сервер»** (peer/pool/mode-config/certs + мастер инициализации Stepper + статус NAT-правила), **«CLI»** (шпаргалка). История/restore — `ConfigHistorySheet`. Компоненты `components/ipsec/*` на существующих DataGridShell/form-kit/Sheet; скачивание бинарного p12 — Blob из base64 (по образцу `downloadText`), `.sswan`/инструкция — через `CodeExportSheet`. Сайдбар «IPsec / IKEv2» в «Управление» + бейдж (`sidebar-counts.ts` + mock в `sidebar-badges.ts`) + command-palette. Mock-данные в `lib/data.ts` для демо-режима.
## Тесты и проверка
- `ipsec-config.test.ts` (чистые функции: sswan, свободный IP, naming); `entity-snapshots.test.ts` — `planIpsecRestore` + `opsTouchOnly(["/ip/ipsec", ...])`.
- `npm run build -w @mmapp/contracts`; `tsc` backend/root; `npm --prefix backend run test:config-sync && test:wireguard` + новые; eslint без новых ошибок.
- Демо-режим: визуальная проверка страницы в браузере (как в прошлый раз).
## Риски (проверить на живом роутере при внедрении)
- REST-скачивание файла `GET /rest/file/<name>` — еслиRouterOS отдаёт только метаданные, fallback: чтение `contents` маленьких PEM-файлов или сборка p12 из PEM-частей.
- Точные имена полей mode-config (`address` vs `address-pool`) сверить с живым GET и адаптировать.
- `/certificate/sign` — поллинг готовности с таймаутом ~30–60 с.
Объём большой; коммит/пуш — по готовности, отдельным `feat(ipsec): …`.
-4
View File
@@ -568,7 +568,3 @@ docker exec mmapp-postgres pg_dump -Fc -U mmapp mmapp > mikrotik-manager.dump
10. Проверить `curl` на `:8000/health` и `:3000/`.
11. В UI: режим live и URL бэкенда; сверить с `CORS_ORIGIN`.
12. Просмотреть `docker logs mmapp-updater`; настроить регулярный backup тома `backend-data`.
### Автоматизация NetFlow через Docker-агент
[Установка, схема взаимодействия и production-состав](docs/netflow-agent.md).
+1 -4
View File
@@ -58,7 +58,6 @@ import {
} from "@/components/data-grids/data-collection-scheduler-data-grid"
import { DataPageCard } from "@/components/data-page-card"
import { NetflowSettingsPanel } from "@/components/traffic/netflow-settings-panel"
import { LogsCollectorSettings } from "@/components/logs/logs-collector-settings"
import { cn } from "@/lib/utils"
import {
AlertCircleIcon,
@@ -85,7 +84,6 @@ function fmtMs(ms: number): string {
}
function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
if (snap.job === "router_logs") return <div className="flex flex-col gap-2 text-sm">{snap.skipped && <p>Сбор логов выключен или приостановлен.</p>}{snap.servers.map(server => <p key={server.serverId}>{server.name}: +{server.added} событий{server.gap ? " · Возможен разрыв истории" : ""}{server.error ? " · " + server.error : ""}</p>)}</div>
if (snap.job === "traffic") {
const t = snap as TrafficRunSnapshot
return (
@@ -761,7 +759,7 @@ function DataCollectionContent() {
return SCHEDULER_JOB_KEYS.map((jobKey) => {
const j = schedulerJobsByKey[jobKey]
const settingsKey: SettingsKey = jobKey === "traffic" ? "traffic" : jobKey === "servers_rest_ping" ? "rest" : jobKey.startsWith("uptime_") ? "uptime" : jobKey === "certificates_renew" ? "cert" : "internet"
const fixedSchedule = jobKey === "router_logs" || jobKey === "gre_bgp" || jobKey === "alert_engine" || jobKey === "backups" || jobKey === "geoip_update"
const fixedSchedule = jobKey === "gre_bgp" || jobKey === "alert_engine" || jobKey === "backups" || jobKey === "geoip_update"
const enabled = fixedSchedule
? Boolean(j?.enabled ?? true)
: jobKey === "traffic"
@@ -961,7 +959,6 @@ function DataCollectionContent() {
</Alert>
)}
{isLive && can("mm:settings:admin") && <LogsCollectorSettings key={backendUrl} base={backendUrl} onSaved={settings => setSchedulerStatus(previous => previous ? { ...previous, jobs: previous.jobs.map(job => job.jobKey === "router_logs" ? { ...job, enabled: settings.enabled, intervalSec: settings.intervalSec } : job) } : previous)} />}
{isLive && (
<>
<KpiStatGrid
+1 -7
View File
@@ -1676,17 +1676,11 @@ function FiltersPage() {
}
const openEdit = (rule: FilterRule) => {
if (mutationsLocked || applyLock.current) return
// The observed catalog can have new DB IDs after refresh. Recover only an
// explicit recursive reference, never reinterpret a named GRE interface.
const routes = recRoutesByServer[selectedServerId] ?? []
const recursive = isRecursiveGatewayRef(rule.gatewayTunnelId)
? routes.find(r => r.id === rule.gatewayTunnelId.slice(4)) ?? pickRecursiveRouteByGatewayHop(routes, rule.gateway)
: undefined
setSheetInitial({
action: rule.action ?? "route",
community: rule.community,
gateway: rule.gateway,
gatewayTunnelId: recursive ? `rec:${recursive.id}` : rule.gatewayTunnelId,
gatewayTunnelId: rule.gatewayTunnelId,
gatewayKind: isRecursiveGatewayRef(rule.gatewayTunnelId) ? "recursive" : "gre",
description: rule.description,
})
+19 -58
View File
@@ -53,7 +53,6 @@ import {
patchIpsecUser,
restoreIpsecRevision,
} from "@/shared/api/ipsec"
import type { IpsecExistingCertExportOptions } from "@/shared/api/ipsec"
import type {
IpsecCertBundle,
IpsecCertInfoDto,
@@ -204,12 +203,7 @@ function IpsecPage({ selectedServerId, setSelectedServerId, workspaceTab, setWor
const [certError, setCertError] = useState<string | null>(null)
const certEpoch = useRef(0)
const certPending = useRef(false)
const certFailedAttempts = useRef(new Set<string>())
const certActive = useRef<{ targetKey: string; controller: AbortController } | null>(null)
const [certAttemptFailed, setCertAttemptFailed] = useState(false)
const [certRole, setCertRole] = useState<IpsecCertInfoDto["role"]>("client")
const [certSession, setCertSession] = useState(0)
useEffect(() => () => { certEpoch.current += 1; certActive.current?.controller.abort() }, [])
useEffect(() => () => { certEpoch.current += 1 }, [])
const [search, setSearch] = useState("")
const [initOpen, setInitOpen] = useState(false)
@@ -466,9 +460,6 @@ function IpsecPage({ selectedServerId, setSelectedServerId, workspaceTab, setWor
setCertBundle(res.bundle)
setCertClient(res.client ?? null)
setCertByName(null)
setCertRole("client")
setCertAttemptFailed(false)
setCertSession(session => session + 1)
setCertOpen(true)
}
} finally {
@@ -496,71 +487,52 @@ function IpsecPage({ selectedServerId, setSelectedServerId, workspaceTab, setWor
}
}
const loadCert = async (serverId: string, targetKey: string, request: (signal: AbortSignal) => Promise<IpsecCertBundle>) => {
const loadCert = async (serverId: string, request: () => Promise<IpsecCertBundle>) => {
if (certPending.current) return
certPending.current = true
const controller = new AbortController()
certActive.current = { targetKey, controller }
const epoch = ++certEpoch.current
setCertBusy(true)
setCertError(null)
try {
assertDeviceReady(serverId)
const bundle = await request(controller.signal)
if (epoch === certEpoch.current) {
setCertBundle(bundle)
certFailedAttempts.current.delete(targetKey)
setCertAttemptFailed(false)
}
const bundle = await request()
if (epoch === certEpoch.current) setCertBundle(bundle)
} catch (error) {
certFailedAttempts.current.add(targetKey)
if (epoch === certEpoch.current) {
setCertAttemptFailed(true)
setCertError(error instanceof Error ? error.message : "Ошибка экспорта сертификата")
}
if (epoch === certEpoch.current) setCertError(error instanceof Error ? error.message : "Ошибка экспорта сертификата")
} finally {
if (epoch === certEpoch.current) { certPending.current = false; certActive.current = null; setCertBusy(false) }
if (epoch === certEpoch.current) { certPending.current = false; setCertBusy(false) }
}
}
const openCert = (client: IpsecClientDto) => {
if (certPending.current) return
const byName = !client.rosId.startsWith("identity:") && client.kind === "cert" && Boolean(client.certName)
const targetKey = JSON.stringify([client.serverId, byName ? "certificate" : "client", byName ? client.certName : client.rosId])
const openCert = async (client: IpsecClientDto) => {
const byName = client.kind === "cert" && Boolean(client.certName)
certEpoch.current += 1
certPending.current = false
setCertClient(byName ? null : client)
setCertByName(byName ? { serverId: client.serverId, name: client.certName! } : null)
setCertBundle(null)
setCertError(null)
setCertAttemptFailed(certFailedAttempts.current.has(targetKey))
setCertRole(summaries.find(summary => summary.serverId === client.serverId)?.certs?.find(cert => cert.name === client.certName)?.role ?? "client")
setCertSession(session => session + 1)
setCertOpen(true)
const passphrase = crypto.randomUUID()
await loadCert(client.serverId, () => byName
? exportIpsecCertByName(backendUrl, client.serverId, client.certName!, passphrase)
: exportIpsecUserCert(backendUrl, client.serverId, client.rosId, passphrase))
}
const openCertByName = (serverId: string, cert: IpsecCertInfoDto) => {
if (certPending.current) return
const openCertByName = async (serverId: string, cert: IpsecCertInfoDto) => {
certEpoch.current += 1
certPending.current = false
setCertByName({ serverId, name: cert.name })
setCertClient(null)
setCertBundle(null)
setCertError(null)
setCertAttemptFailed(certFailedAttempts.current.has(JSON.stringify([serverId, "certificate", cert.name])))
setCertRole(cert.role ?? "other")
setCertSession(session => session + 1)
setCertOpen(true)
await loadCert(serverId, () => exportIpsecCertByName(backendUrl, serverId, cert.name, crypto.randomUUID()))
}
const reexportCert = async (passphrase: string, options: IpsecExistingCertExportOptions) => {
const reexportCert = async (passphrase: string) => {
if (!certClient && !certByName) return
if (options.privateKeyExport !== true || (certRole === "ca" && !options.caPrivateKeyExport)) return
const serverId = certByName?.serverId ?? certClient!.serverId
const targetKey = JSON.stringify([serverId, certByName ? "certificate" : "client", certByName?.name ?? certClient!.rosId])
await loadCert(serverId, targetKey, signal => certByName
? exportIpsecCertByName(backendUrl, certByName.serverId, certByName.name, passphrase, options, { signal })
: exportIpsecUserCert(backendUrl, certClient!.serverId, certClient!.rosId, passphrase, options, { signal }))
await loadCert(certByName?.serverId ?? certClient!.serverId, () => certByName
? exportIpsecCertByName(backendUrl, certByName.serverId, certByName.name, passphrase)
: exportIpsecUserCert(backendUrl, certClient!.serverId, certClient!.rosId, passphrase))
}
const deletingCertificateIdentity = pendingDelete?.kind === "user" && pendingDelete.client.kind === "cert" && pendingDelete.client.rosId.startsWith("identity:")
@@ -947,25 +919,14 @@ function IpsecPage({ selectedServerId, setSelectedServerId, workspaceTab, setWor
/>
<IpsecCertSheet
key={certSession}
open={certOpen}
onOpenChange={(v) => {
if (!v) {
if (certActive.current) {
certFailedAttempts.current.add(certActive.current.targetKey)
certActive.current.controller.abort()
certActive.current = null
}
certEpoch.current += 1; certPending.current = false; setCertBusy(false); setCertBundle(null); setCertClient(null); setCertByName(null)
}
if (!v) { certEpoch.current += 1; certPending.current = false; setCertBusy(false); setCertBundle(null); setCertClient(null); setCertByName(null) }
setCertOpen(v)
}}
error={certError}
bundle={certBundle}
busy={certBusy}
certificateRole={certRole}
targetName={certByName?.name ?? certClient?.certName ?? certClient?.name}
previousAttemptFailed={certAttemptFailed}
onReexport={reexportCert}
/>
-2
View File
@@ -1,2 +0,0 @@
import { LogsWorkspace } from "@/components/logs/logs-workspace"
export default function LogsPage() { return <LogsWorkspace /> }
-6
View File
@@ -1,6 +0,0 @@
import { Suspense } from "react"
import { McpWorkspace } from "@/components/mcp/mcp-workspace"
export default function McpPage() {
return <Suspense fallback={<p role="status" className="p-6">Загрузка MCP…</p>}><McpWorkspace /></Suspense>
}
+10 -14
View File
@@ -11,7 +11,6 @@ import {Dialog,DialogContent,DialogHeader,DialogTitle,DialogDescription} from "@
import {usePrefersReducedMotion} from "@/hooks/use-prefers-reduced-motion"
import {toast} from "sonner"
import { PageHeader } from "@/components/page-header"
import { NetworkMapLegend } from "@/components/network-map-legend"
import {
servers as mockServers,
greTunnels as mockGreTunnels,
@@ -557,7 +556,7 @@ function GreEdgeMetricBadge({
"Клик — панель справа."
return (
<g
role="button" aria-label={"Метрики "+(outerSummary??"связи")} tabIndex={0} className="network-map-focusable"
role="button" aria-label={"Метрики "+(outerSummary??"связи")} tabIndex={0} className="focus-visible:outline-2 focus-visible:outline-ring"
onKeyDown={e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();e.stopPropagation();onOpen(e)}}}
transform={`translate(${mx},${my})`}
style={{ cursor: "pointer" }}
@@ -565,7 +564,6 @@ function GreEdgeMetricBadge({
onClick={(e) => { e.stopPropagation(); onOpen(e) }}
>
<title>{hint}</title>
<rect className="network-map-focus-ring" x={-bw / 2 - 4} y={-bh / 2 - 4} width={bw + 8} height={bh + 8} rx="9" fill="none" stroke="var(--ring)" strokeWidth="2" />
<rect
x={-bw / 2}
y={-bh / 2}
@@ -609,7 +607,7 @@ function NetflowRateBadge({
const bh = showDir ? 32 : 20
return (
<g
role={onOpen?"button":undefined} aria-label={onOpen?"Поток NetFlow между узлами":undefined} tabIndex={onOpen?0:undefined} className="network-map-focusable"
role={onOpen?"button":undefined} aria-label={onOpen?"Поток NetFlow между узлами":undefined} tabIndex={onOpen?0:undefined} className="focus-visible:outline-2 focus-visible:outline-ring"
onKeyDown={e=>{if(onOpen&&(e.key==="Enter"||e.key===" ")){e.preventDefault();e.stopPropagation();onOpen(e)}}}
transform={`translate(${mx},${my})`}
style={{ cursor: onOpen ? "pointer" : "default" }}
@@ -619,7 +617,6 @@ function NetflowRateBadge({
<title>
Поток NetFlow между узлами (как в «Трафик»: 5 мин, без overlay/mesh). Скорость канала — отдельно.
</title>
<rect className="network-map-focus-ring" x={-bw / 2 - 4} y={-bh / 2 - 4} width={bw + 8} height={bh + 8} rx="9" fill="none" stroke="var(--ring)" strokeWidth="2" />
<rect
x={-bw / 2}
y={-bh / 2}
@@ -690,7 +687,7 @@ function ServerNode({ labelScale = 1, n, isSel, isVis, isDragged, hideCatalogLat
const ts = TYPE_STYLE[n.type]
return (
<g className="network-map-focusable" role="button" aria-label={"Устройство "+n.name} aria-pressed={isSel} tabIndex={isVis?0:-1}
<g className="focus-visible:outline-2 focus-visible:outline-ring" role="button" aria-label={"Устройство "+n.name} aria-pressed={isSel} tabIndex={isVis?0:-1}
onKeyDown={e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();e.stopPropagation();onClick()}}}
transform={`translate(${n.x},${n.y})`}
style={{ cursor: isDragged ? "grabbing" : "grab", transition: isDragged ? "none" : "opacity 0.25s" }}
@@ -702,7 +699,6 @@ function ServerNode({ labelScale = 1, n, isSel, isVis, isDragged, hideCatalogLat
onMouseEnter={() => onHoverChange(true)}
onMouseLeave={() => onHoverChange(false)}
>
<circle className="network-map-focus-ring" r={ts.r + 6} fill="none" stroke="var(--ring)" strokeWidth="2" />
{n.status === "online" && isVis && (
<circle r={ts.r + 14} fill={ss.glow} opacity="0.7">
</circle>
@@ -711,7 +707,7 @@ function ServerNode({ labelScale = 1, n, isSel, isVis, isDragged, hideCatalogLat
<circle r={ts.r + 12} fill={ss.glow} />
)}
{isSel && (
<circle r={ts.r + 10} fill="none" stroke="var(--foreground)" strokeWidth="1.5" strokeDasharray="4 3" opacity="0.45" />
<circle r={ts.r + 10} fill="none" stroke="var(--foreground)" strokeWidth="1.5" strokeDasharray="4 3" />
)}
<circle r={ts.r} fill={ss.fill} stroke={ss.stroke} strokeWidth={isSel ? 2.5 : 1.8} />
<g transform={`translate(${ts.r - 10},-${ts.r - 10})`}>
@@ -811,7 +807,7 @@ function ServiceNode({
const bh = MAP_SERVICE_NODE_H
const flagIso = destMode === "countries" && iso && iso !== "Прочее" ? iso : ""
return (
<g role="button" aria-label={(destMode==="countries"?"Страна ":"Сервис ")+label} aria-pressed={isSel} tabIndex={isVis?0:-1} className="network-map-focusable"
<g role="button" aria-label={(destMode==="countries"?"Страна ":"Сервис ")+label} aria-pressed={isSel} tabIndex={isVis?0:-1} className="focus-visible:outline-2 focus-visible:outline-ring"
onKeyDown={e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();e.stopPropagation();onClick()}}}
transform={`translate(${x},${y})`}
style={{ cursor: isDragged ? "grabbing" : "grab", transition: isDragged ? "none" : "opacity 0.25s" }}
@@ -820,7 +816,6 @@ function ServiceNode({
onClick={(e) => { e.stopPropagation(); onClick() }}
>
<title>{`${label} · ${serviceSharePct(share)} ${shareLabel ?? "payload окна"}`}</title>
<rect className="network-map-focus-ring" x={-bw / 2 - 3} y={-bh / 2 - 3} width={bw + 6} height={bh + 6} rx="9" fill="none" stroke="var(--ring)" strokeWidth="2" />
{isSel && (
<rect
x={-bw / 2 - 6}
@@ -930,13 +925,12 @@ function WanSatNode({ x, y, wan, color, active, isSel, isDragged, onSelect, onPo
}) {
const r = 18
return (
<g role="button" aria-label={"Аплинк "+wan.name} aria-pressed={isSel} tabIndex={0} className="network-map-focusable"
<g role="button" aria-label={"Аплинк "+wan.name} aria-pressed={isSel} tabIndex={0} className="focus-visible:outline-2 focus-visible:outline-ring"
onKeyDown={e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();e.stopPropagation();onSelect()}}}
transform={`translate(${x},${y})`}
style={{ cursor: isDragged ? "grabbing" : "grab" }}
onPointerDown={e => { e.stopPropagation(); onPointerDown(e) }}
onClick={e => { e.stopPropagation(); onSelect() }}>
<circle className="network-map-focus-ring" r={r + 4} fill="none" stroke="var(--ring)" strokeWidth="2" />
{active && (
<circle r={r + 6} fill={color} opacity="0.12">
</circle>
@@ -1724,7 +1718,6 @@ function NetworkMapContent() {
if (!svg) return
// Clone with full viewBox (reset pan/zoom)
const clone = svg.cloneNode(true) as SVGSVGElement
clone.querySelectorAll(".network-map-focus-ring").forEach(ring => ring.remove())
clone.setAttribute("viewBox", `0 0 ${W} ${H}`)
clone.setAttribute("width", String(W))
clone.setAttribute("height", String(H))
@@ -2259,7 +2252,10 @@ function NetworkMapContent() {
{/* ── Canvas ── */}
<div ref={canvasRef} className={cn("flex-1 min-h-0 min-w-0 relative overflow-hidden bg-background select-none", (selected || selectedGreEdge || selectedService) && "hidden lg:block")}>
<NetworkMapLegend />
<details className="absolute top-3 left-3 z-20 rounded-md border bg-popover p-2 text-xs shadow-sm">
<summary className="cursor-pointer focus-visible:outline-2 focus-visible:outline-ring">Обозначения карты</summary>
<div className="mt-2 space-y-1"><p>HR · домашний роутер</p><p>JH · промежуточный узел</p><p>EN · выходной узел</p><p>Круг · устройство / WAN</p><p>Прямоугольник · сервис / страна</p><p>Пунктир WAN → JH · модель связности</p><p>Цвет · статус; неизвестно ≠ оффлайн</p></div>
</details>
<p id="network-map-help" className="sr-only">Стрелки перемещают карту, плюс и минус меняют масштаб, F вписывает объекты. Tab выбирает объект, Enter открывает детали.</p>
<svg
ref={svgRef} role="group" aria-label="Интерактивная карта сети" tabIndex={0} aria-describedby="network-map-help"
-12
View File
@@ -2,18 +2,6 @@
@import "tw-animate-css";
@import "shadcn/tailwind.css";
/* SVG focus follows the object shape, never the text/foreignObject bounding box. */
.network-map-focusable {
outline: none;
}
.network-map-focus-ring {
opacity: 0;
pointer-events: none;
}
.network-map-focusable:focus-visible > .network-map-focus-ring {
opacity: 1;
}
@custom-variant dark (&:is(.dark *));
@theme inline {
+19
View File
@@ -0,0 +1,19 @@
# PostgreSQL connection string
DATABASE_URL=postgres://mmapp:[email protected]:5432/mmapp
# SQLite file used only for one-shot boot ETL (empty PG + existing mikrotik.db)
DATABASE_PATH=./mikrotik.db
# SQLITE_IMPORT_FULL_HISTORY=true
# Port for the Fastify server
PORT=8000
# Allowed CORS origin (Next.js frontend)
CORS_ORIGIN=http://localhost:3000
# Portal SSO (false = open API for local/dev)
AUTH_REQUIRED=false
# Same HS256 secret as auth-portal JWT_SECRET when AUTH_REQUIRED=true
AUTH_JWT_SECRET=dev-secret-change-me
AUTH_ISSUER=https://auth.shnt.top
AUTH_PORTAL_URL=http://localhost:5175
-3
View File
@@ -55,11 +55,8 @@ COPY --from=build /app/package.json /app/package-lock.json ./
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/packages/contracts ./packages/contracts
COPY --from=build /app/backend/dist ./backend/dist
COPY --from=build /app/backend/dist-operator ./backend/dist-operator
COPY --from=build /app/backend/package.json ./backend/package.json
COPY --from=build /app/backend/mcp-coverage.json ./backend/mcp-coverage.json
COPY --from=build /app/backend/drizzle ./backend/drizzle
COPY deploy/netflow-agent/install.sh ./deploy/netflow-agent/install.sh
# Nested install from lockfile (dotenv etc.) — ESM resolves from /app/backend/dist → ../node_modules
COPY --from=build /app/backend/node_modules ./backend/node_modules
EXPOSE 8000
@@ -1,6 +1,6 @@
-- Захват перед POST и удержание неизвестных исходов. Исторические строки не меняются.
BEGIN;
ALTER TABLE alert_outbox DROP CONSTRAINT IF EXISTS alert_outbox_status_check;
ALTER TABLE alert_outbox ADD CONSTRAINT alert_outbox_status_check
CHECK (status IN ('pending', 'processing', 'unknown', 'sent', 'failed'));
COMMIT;
-- Захват перед POST и удержание неизвестных исходов. Исторические строки не меняются.
BEGIN;
ALTER TABLE alert_outbox DROP CONSTRAINT IF EXISTS alert_outbox_status_check;
ALTER TABLE alert_outbox ADD CONSTRAINT alert_outbox_status_check
CHECK (status IN ('pending', 'processing', 'unknown', 'sent', 'failed'));
COMMIT;
@@ -1,4 +1,4 @@
BEGIN;
ALTER TABLE alert_outbox ADD COLUMN IF NOT EXISTS resolution_version integer NOT NULL DEFAULT 0;
ALTER TABLE alert_outbox ADD CONSTRAINT alert_outbox_resolution_version_check CHECK (resolution_version >= 0);
COMMIT;
BEGIN;
ALTER TABLE alert_outbox ADD COLUMN IF NOT EXISTS resolution_version integer NOT NULL DEFAULT 0;
ALTER TABLE alert_outbox ADD CONSTRAINT alert_outbox_resolution_version_check CHECK (resolution_version >= 0);
COMMIT;
@@ -1,3 +1,3 @@
-- A failed command is an attempt, not a measured packet-loss percentage.
ALTER TABLE uptime_probe_samples ADD COLUMN IF NOT EXISTS measurement_known BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE uptime_probe_samples ADD COLUMN IF NOT EXISTS observation_error TEXT;
-- A failed command is an attempt, not a measured packet-loss percentage.
ALTER TABLE uptime_probe_samples ADD COLUMN IF NOT EXISTS measurement_known BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE uptime_probe_samples ADD COLUMN IF NOT EXISTS observation_error TEXT;
@@ -1,8 +1,8 @@
CREATE TABLE IF NOT EXISTS traffic_collection_state (
server_id BIGINT PRIMARY KEY REFERENCES servers(id) ON DELETE CASCADE,
server_updated_at TIMESTAMPTZ NOT NULL,
last_attempt_at TIMESTAMPTZ NOT NULL,
last_succeeded_at TIMESTAMPTZ,
status TEXT NOT NULL CHECK(status IN ('pending', 'ok', 'error')),
error TEXT
);
CREATE TABLE IF NOT EXISTS traffic_collection_state (
server_id BIGINT PRIMARY KEY REFERENCES servers(id) ON DELETE CASCADE,
server_updated_at TIMESTAMPTZ NOT NULL,
last_attempt_at TIMESTAMPTZ NOT NULL,
last_succeeded_at TIMESTAMPTZ,
status TEXT NOT NULL CHECK(status IN ('pending', 'ok', 'error')),
error TEXT
);
@@ -1 +1 @@
ALTER TABLE traffic_samples ADD COLUMN IF NOT EXISTS native_counter_identity TEXT;
ALTER TABLE traffic_samples ADD COLUMN IF NOT EXISTS native_counter_identity TEXT;
@@ -1,4 +1,4 @@
-- Retention runs after each scheduler receipt; avoid scanning the complete history.
-- On a busy existing installation, pre-create this same index CONCURRENTLY
-- outside a transaction before deploying (deploy/PRODUCTION-RETENTION-INDEX.md).
CREATE INDEX IF NOT EXISTS idx_scheduler_runs_finished_at ON scheduler_runs (finished_at);
-- Retention runs after each scheduler receipt; avoid scanning the complete history.
-- On a busy existing installation, pre-create this same index CONCURRENTLY
-- outside a transaction before deploying (deploy/PRODUCTION-RETENTION-INDEX.md).
CREATE INDEX IF NOT EXISTS idx_scheduler_runs_finished_at ON scheduler_runs (finished_at);
-26
View File
@@ -1,26 +0,0 @@
CREATE TABLE IF NOT EXISTS router_log_settings (
id INTEGER PRIMARY KEY CHECK (id = 1), enabled BOOLEAN NOT NULL DEFAULT false,
interval_sec INTEGER NOT NULL DEFAULT 10 CHECK (interval_sec BETWEEN 5 AND 3600),
retention_days INTEGER NOT NULL DEFAULT 14 CHECK (retention_days BETWEEN 1 AND 365)
);
INSERT INTO router_log_settings(id) VALUES (1) ON CONFLICT DO NOTHING;
-- No foreign key: collected history survives device deletion.
CREATE TABLE IF NOT EXISTS router_log_collection_state (
server_id INTEGER PRIMARY KEY, server_name TEXT NOT NULL, host TEXT NOT NULL,
generation INTEGER NOT NULL DEFAULT 0, snapshot JSONB NOT NULL DEFAULT '[]',
last_collected_at TIMESTAMPTZ, last_error TEXT, gap_at TIMESTAMPTZ,
next_poll_at TIMESTAMPTZ, failures INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS router_log_entries (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, server_id INTEGER NOT NULL,
server_name TEXT NOT NULL, host TEXT NOT NULL, router_id TEXT NOT NULL, raw_time TEXT NOT NULL,
event_at TIMESTAMPTZ, received_at TIMESTAMPTZ NOT NULL,
effective_at TIMESTAMPTZ NOT NULL, topics TEXT[] NOT NULL, level TEXT NOT NULL,
message TEXT NOT NULL, generation INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS router_logs_server_time ON router_log_entries(server_id, effective_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS router_logs_time ON router_log_entries(effective_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS router_logs_level_time ON router_log_entries(level, effective_at DESC);
CREATE INDEX IF NOT EXISTS router_logs_topics ON router_log_entries USING gin(topics);
CREATE INDEX IF NOT EXISTS router_logs_received ON router_log_entries(received_at);
CREATE INDEX IF NOT EXISTS router_logs_server_cursor ON router_log_entries(server_id, id);
-101
View File
@@ -1,101 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_settings (
id integer PRIMARY KEY CHECK(id=1), enabled boolean NOT NULL DEFAULT false,
requests_retention_days integer NOT NULL DEFAULT 30 CHECK(requests_retention_days BETWEEN 1 AND 365),
audit_retention_days integer NOT NULL DEFAULT 180 CHECK(audit_retention_days BETWEEN 30 AND 3650),
updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO mcp_settings(id) VALUES(1) ON CONFLICT DO NOTHING;
CREATE TABLE IF NOT EXISTS mcp_connections (
id uuid PRIMARY KEY, name text NOT NULL, owner_id text NOT NULL, owner_permissions jsonb NOT NULL,
policy jsonb NOT NULL, generation text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(), last_used_at timestamptz
);
CREATE TABLE IF NOT EXISTS mcp_credentials (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id) ON DELETE CASCADE,
digest text NOT NULL UNIQUE, kind text NOT NULL CHECK(kind IN ('bearer','access','refresh')),
expires_at timestamptz NOT NULL, revoked_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(),
family_id uuid, client_id text, audience text
);
CREATE INDEX IF NOT EXISTS mcp_credentials_connection ON mcp_credentials(connection_id);
CREATE TABLE IF NOT EXISTS mcp_audit (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, actor_id text NOT NULL, action text NOT NULL,
connection_id uuid, payload jsonb NOT NULL DEFAULT '{}', created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mcp_audit_time ON mcp_audit(created_at,id);
CREATE TABLE IF NOT EXISTS mcp_requests (
id uuid PRIMARY KEY, connection_id uuid REFERENCES mcp_connections(id) ON DELETE SET NULL,
actor_id text, tool text NOT NULL, server_ids bigint[] NOT NULL DEFAULT '{}',
status text NOT NULL, input jsonb NOT NULL DEFAULT '{}', result jsonb, error_code text,
duration_ms integer, created_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz
);
CREATE INDEX IF NOT EXISTS mcp_requests_connection_time ON mcp_requests(connection_id,created_at,id);
CREATE INDEX IF NOT EXISTS mcp_requests_server ON mcp_requests USING gin(server_ids);
CREATE TABLE IF NOT EXISTS mcp_protections (
id uuid PRIMARY KEY, object_ref jsonb NOT NULL, reason text NOT NULL DEFAULT '',
created_by text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS mcp_plans (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id), operation_id text NOT NULL,
operation_version integer NOT NULL, policy_version integer NOT NULL, generation text NOT NULL,
payload jsonb NOT NULL, evidence jsonb NOT NULL, state text NOT NULL, expires_at timestamptz NOT NULL,
approved_by text, approved_at timestamptz, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mcp_plans_connection ON mcp_plans(connection_id,created_at);
CREATE TABLE IF NOT EXISTS mcp_operations (
id uuid PRIMARY KEY, plan_id uuid NOT NULL UNIQUE REFERENCES mcp_plans(id),
connection_id uuid NOT NULL REFERENCES mcp_connections(id), idempotency_key text NOT NULL,
state text NOT NULL DEFAULT 'pending', verification text NOT NULL DEFAULT 'pending',
result jsonb, error_code text, created_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz,
UNIQUE(connection_id,idempotency_key)
);
CREATE TABLE IF NOT EXISTS mcp_operation_steps (
operation_id uuid NOT NULL REFERENCES mcp_operations(id), step_index integer NOT NULL,
server_id bigint, state text NOT NULL, before_state jsonb, intended_state jsonb, result jsonb,
created_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz,
PRIMARY KEY(operation_id,step_index)
);
CREATE TABLE IF NOT EXISTS mcp_budget_buckets (
connection_id uuid NOT NULL REFERENCES mcp_connections(id) ON DELETE CASCADE,
kind text NOT NULL, bucket timestamptz NOT NULL, used integer NOT NULL CHECK(used>=0),
PRIMARY KEY(connection_id,kind,bucket)
);
CREATE TABLE IF NOT EXISTS mcp_read_leases (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id) ON DELETE CASCADE,
expires_at timestamptz NOT NULL
);
CREATE TABLE IF NOT EXISTS mcp_grants (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id) ON DELETE CASCADE,
payload jsonb NOT NULL, revoked_at timestamptz
);
CREATE TABLE IF NOT EXISTS mcp_secrets (
id uuid PRIMARY KEY, purpose text NOT NULL, server_id bigint, ciphertext text NOT NULL,
created_by text NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS mcp_artifacts (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id),
filename text NOT NULL, media_type text NOT NULL, ciphertext text NOT NULL,
expires_at timestamptz NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS mcp_oauth_clients (
id text PRIMARY KEY, name text NOT NULL, redirect_uris jsonb NOT NULL, enabled boolean NOT NULL DEFAULT true,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS mcp_oauth_requests (
id uuid PRIMARY KEY, client_id text NOT NULL REFERENCES mcp_oauth_clients(id),
redirect_uri text NOT NULL, challenge text NOT NULL, state text, resource text NOT NULL,
scope text NOT NULL, expires_at timestamptz NOT NULL, consumed_at timestamptz,
connection_id uuid REFERENCES mcp_connections(id), code_digest text UNIQUE
);
CREATE TABLE IF NOT EXISTS mcp_routing_profiles (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id),
version integer NOT NULL DEFAULT 1, config jsonb NOT NULL, state jsonb NOT NULL DEFAULT '{}',
enabled boolean NOT NULL DEFAULT false, grant_expires_at timestamptz NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS mcp_drift (
id uuid PRIMARY KEY, operation_id uuid NOT NULL REFERENCES mcp_operations(id),
server_id bigint, expected jsonb NOT NULL, observed jsonb NOT NULL,
acknowledged_at timestamptz, detected_at timestamptz NOT NULL DEFAULT now()
);
COMMIT;
@@ -1,12 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_routing_observations (
id uuid PRIMARY KEY, src_server_id bigint NOT NULL, dst_server_id bigint NOT NULL,
src_interface text NOT NULL, dst_interface text NOT NULL, family text NOT NULL CHECK(family IN ('ipv4','ipv6')),
routing_table text NOT NULL DEFAULT 'main', ping_ms double precision, loss_pct double precision,
dl_mbps double precision, ul_mbps double precision, ping_at timestamptz, bandwidth_at timestamptz,
context_verified boolean NOT NULL DEFAULT false, source text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(), CHECK(ping_ms IS NULL OR ping_ms>=0),
CHECK(loss_pct IS NULL OR loss_pct BETWEEN 0 AND 100), CHECK(dl_mbps IS NULL OR dl_mbps>=0), CHECK(ul_mbps IS NULL OR ul_mbps>=0)
);
CREATE INDEX IF NOT EXISTS mcp_routing_observations_leg ON mcp_routing_observations(src_server_id,dst_server_id,src_interface,family,routing_table,created_at DESC);
COMMIT;
-7
View File
@@ -1,7 +0,0 @@
BEGIN;
ALTER TABLE mcp_routing_profiles ADD COLUMN IF NOT EXISTS credential_id uuid REFERENCES mcp_credentials(id);
ALTER TABLE mcp_routing_profiles ADD COLUMN IF NOT EXISTS generation text;
ALTER TABLE mcp_routing_profiles ADD COLUMN IF NOT EXISTS policy_version integer;
CREATE INDEX IF NOT EXISTS mcp_routing_profiles_enabled ON mcp_routing_profiles(updated_at) WHERE enabled;
CREATE UNIQUE INDEX IF NOT EXISTS mcp_drift_open_step ON mcp_drift(operation_id,(expected->>'stepIndex')) WHERE acknowledged_at IS NULL;
COMMIT;
@@ -1,3 +0,0 @@
BEGIN;
ALTER TABLE mcp_connections ADD COLUMN IF NOT EXISTS owner_valid_until timestamptz NOT NULL DEFAULT now();
COMMIT;
-7
View File
@@ -1,7 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_operation_checkpoints (
operation_id uuid NOT NULL REFERENCES mcp_operations(id), step_index integer NOT NULL,
before_sealed text NOT NULL, after_sealed text, created_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY(operation_id,step_index)
);
COMMIT;
-8
View File
@@ -1,8 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_management_channels (
id uuid PRIMARY KEY, server_id bigint NOT NULL UNIQUE REFERENCES servers(id) ON DELETE CASCADE,
config_sealed text NOT NULL, identity_hash text NOT NULL, attestation text NOT NULL,
created_by text NOT NULL, generation text NOT NULL, expires_at timestamptz NOT NULL,
verified_at timestamptz NOT NULL DEFAULT now(), revoked_at timestamptz
);
COMMIT;
-8
View File
@@ -1,8 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_path_checks (
id uuid PRIMARY KEY, connection_id uuid NOT NULL REFERENCES mcp_connections(id),
profile_hash text NOT NULL, candidate_id text NOT NULL, status text NOT NULL CHECK(status IN ('passed','failed','unknown')),
evidence jsonb NOT NULL, created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mcp_path_checks_latest ON mcp_path_checks(connection_id,profile_hash,candidate_id,created_at DESC);
COMMIT;
@@ -1,7 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_recovery_intents (
operation_id uuid PRIMARY KEY REFERENCES mcp_operations(id), state text NOT NULL,
authority_profile_id uuid NOT NULL REFERENCES mcp_routing_profiles(id),
created_at timestamptz NOT NULL DEFAULT now(), finished_at timestamptz
);
COMMIT;
@@ -1,6 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_unauthorized_buckets (
bucket timestamptz NOT NULL, client_hash text NOT NULL, reason text NOT NULL,
attempts integer NOT NULL DEFAULT 1, PRIMARY KEY(bucket,client_hash,reason)
);
COMMIT;
@@ -1,4 +0,0 @@
BEGIN;
ALTER TABLE user_interface_bindings DROP CONSTRAINT IF EXISTS user_interface_bindings_interface_type_check;
ALTER TABLE user_interface_bindings ADD CONSTRAINT user_interface_bindings_interface_type_check CHECK(interface_type IN ('ether','bridge','vlan','vxlan','gre','wg','ipsec','other'));
COMMIT;
@@ -1,11 +0,0 @@
BEGIN;
CREATE TABLE IF NOT EXISTS mcp_owner_revocations (
owner_id text PRIMARY KEY, invalid_before timestamptz NOT NULL,
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS mcp_portal_events (
id uuid PRIMARY KEY, owner_id text NOT NULL, payload_hash text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS mcp_portal_events_time ON mcp_portal_events(created_at);
COMMIT;
@@ -1,3 +0,0 @@
BEGIN;
ALTER TABLE mcp_connections ADD COLUMN IF NOT EXISTS owner_claims_issued_at timestamptz NOT NULL DEFAULT to_timestamp(0);
COMMIT;
-8
View File
@@ -1,8 +0,0 @@
CREATE INDEX IF NOT EXISTS mcp_audit_handshake_cooldown
ON mcp_audit(connection_id, created_at DESC)
WHERE action = 'connection.handshake.start';
CREATE INDEX IF NOT EXISTS mcp_credentials_readiness_expired
ON mcp_credentials(connection_id, expires_at)
WHERE client_id = 'mcp-readiness-probe';
CREATE INDEX IF NOT EXISTS mcp_grants_credential
ON mcp_grants(connection_id, (payload->>'credentialId'));
@@ -1,2 +0,0 @@
ALTER TABLE mcp_artifacts ADD COLUMN IF NOT EXISTS access_scope jsonb;
CREATE INDEX IF NOT EXISTS mcp_artifacts_expiry ON mcp_artifacts(expires_at);
@@ -1,23 +0,0 @@
BEGIN;
ALTER TABLE servers ADD COLUMN IF NOT EXISTS routing_endpoint_epoch uuid NOT NULL DEFAULT gen_random_uuid();
CREATE OR REPLACE FUNCTION mm_routing_endpoint_epoch() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
IF ROW(NEW.host,NEW.port,NEW.username,NEW.password,NEW.use_ssl,NEW.verify_ssl,NEW.enabled)
IS DISTINCT FROM ROW(OLD.host,OLD.port,OLD.username,OLD.password,OLD.use_ssl,OLD.verify_ssl,OLD.enabled) THEN
NEW.routing_endpoint_epoch := gen_random_uuid();
ELSE
NEW.routing_endpoint_epoch := OLD.routing_endpoint_epoch;
END IF;
RETURN NEW;
END $$;
DROP TRIGGER IF EXISTS mm_routing_endpoint_epoch ON servers;
CREATE TRIGGER mm_routing_endpoint_epoch BEFORE UPDATE ON servers FOR EACH ROW EXECUTE FUNCTION mm_routing_endpoint_epoch();
ALTER TABLE mcp_routing_observations ADD COLUMN IF NOT EXISTS source_proof text;
DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='mcp_routing_observation_source_proof' AND conrelid='mcp_routing_observations'::regclass) THEN
ALTER TABLE mcp_routing_observations ADD CONSTRAINT mcp_routing_observation_source_proof CHECK(source_proof IS NULL OR source_proof ~ '^[a-f0-9]{64}$');
END IF;
END $$;
-- Existing measurements stay readable but unsigned. Never attest old samples
-- using today's endpoint: that would invent capture-time provenance.
COMMIT;
@@ -1,6 +0,0 @@
BEGIN;
-- Service-owned exact read-back must survive a subsequent network probe failure.
-- Generic after_sealed remains an array of resource snapshots.
ALTER TABLE mcp_operation_checkpoints ADD COLUMN IF NOT EXISTS configuration_sealed text;
ALTER TABLE mcp_operation_checkpoints ADD COLUMN IF NOT EXISTS configuration_proof text;
COMMIT;
Loaded 100 of 1567 files, more files were not shown because too many files have changed in this diff. Show more