diff --git a/components/data-grids/ospf-routes-data-grid.tsx b/components/data-grids/ospf-routes-data-grid.tsx
index 154334ea..7a7fd7b2 100644
--- a/components/data-grids/ospf-routes-data-grid.tsx
+++ b/components/data-grids/ospf-routes-data-grid.tsx
@@ -49,12 +49,12 @@ function OspfRoutesDataGrid({ routes, emptyTitle }: OspfRoutesDataGridProps) {
() => [
{
id: "destination",
- size: 150,
+ size: 180,
accessorKey: "destination",
header: ({ column }) => (
),
- cell: ({ row }) => {row.original.destination},
+ cell: ({ row }) => {row.original.destination},
meta: {
headerTitle: "Назначение",
headerClassName: DATA_GRID_CELL_PAD_FIRST,
diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md
index 94ea858a..2d5420ff 100644
--- a/docs/ux-ui-external-acceptance.md
+++ b/docs/ux-ui-external-acceptance.md
@@ -835,3 +835,15 @@ Production image не содержит dev-only jose: первый fixture impor
**Очистка.** Exact ID/name/comment/address/interface/interfaces/addresses/public key preflight; удалены только свои templates/area/instance/BFD/IP/bridge/GRE/WG peers/interfaces. Исходные IDs10 коллекций×2 совпали, public/default routes и195.195.195.195/32 сохранены. WG secret fields не включены в report. Оба CHR доступны, passwords неизменны; временных контейнеров не осталось. Backend diagnostics89/PG healthy, frontend optimizer87 сохранён. Один локальный commit без push.
Схема: primary → BFD/adjacency loss → alternate GRE over WG → проверка data plane → восстановленный primary. Транспорт подтверждён [GRE](https://help.mikrotik.com/docs/spaces/ROS/pages/24805531/GRE) и [WireGuard](https://help.mikrotik.com/docs/spaces/ROS/pages/69664792/WireGuard); таблицы сохраняют референсы общего плана. Продолжение91 — обрезка CIDR, затем BGP propagation/остальные внешние границы. BFD-only устойчивый запрет/physical WAN/IPv6/scale/maximum throughput/Portal SSO остаются открыты. Квота66% использовано; этап12.5 открыт.
+
+## 12.5.91 — OSPF Data Grid: целый IPv4 CIDR (2026-10-09)
+
+Уточнена визуальная проблема90: это не потеря API-данных, а перенос последней цифры /32 на вторую строку; первый ряд выглядел как /3. Столбец назначения расширен150→180px, span использует whitespace-nowrap и title полного значения. Установленные Data Grid/Table/ScrollArea, pinned column, theme и структура сохранены. ReUI inline API и audit checklist прочитаны; [API](https://reui.io/docs/components/base/data-grid?ref=mcp), [превью](https://reui.io/components/data-grid?ref=mcp).
+
+**Проверки.** Новый browser test ждёт document.fonts.ready и проверяет Range rectangles: ровно одна строка, текст внутри ячейки. До исправления1280px failed: /3 и2 дали два rect. Первоначальная проверка только границ проходила и была уточнена, чтобы не пропустить разрыв CIDR. После изменения4 размера320/390/1280/1440 и IPv4 CIDR15/18 символов проходят; сортировка/количество строк/document fit/no writes сохранены. С OSPF unknown cost/disabled/inactive —7/7. Scoped TypeScript, ESLint/diff passed. Это meaningful visual regression, не проверка буквального size в исходнике.
+
+**Live.** Backend diagnostics89 и обновлённый frontend ospf91, без mocks: два CHR×1280/320 =4/4. Свои GRE/OSPF private link198.18.93.0/30/loopbacks198.18.193.1/32↔2/32; Full/active learned route/Ping3/3. Целый /32 виден в одной строке после загрузки шрифтов; title, границы ячейки, URL/reload, document fit/pageerror0 подтверждены. [Browser отчёт](ux-ui/acceptance/browser-ospf-cidr-2026-10-09.json), [native/cleanup](ux-ui/acceptance/ospf-cidr-native-2026-10-09.json), [ПК после](ux-ui/screens/ospf-real-cidr-server1-1280-2026-10-09.png), [320px после](ux-ui/screens/ospf-real-cidr-server1-320-2026-10-09.png). PC снимок просмотрен:198.18.193.2/32 полностью читается. Проверка не обещает одновременную видимость всех колонок на телефоне: сохраняется внутренняя горизонтальная прокрутка.
+
+**Контроль/runtime.** Свои objects удалены по exact IDs/атрибутам, baseline7 коллекций×2 совпал, оба CHR доступны, default/public/195.195.195.195/32 и credentials сохранены. Нормализованные SHA source/deployed DataGrid renderer и OSPF consumer совпадали до изменения, поэтому проблема не была вызвана отставшим компонентом стенда. Один frontend Docker build, production TypeScript/build, entry/node --check/compose --wait passed. mmapp-test/frontend:ux-ospf91/version1.0.0-test.ospf91, image sha256:b4bade515234115b3cad257ceb87ef7beb347e754ca92d6a97a25adc2199fd33. Context optimizer87 frontend +один consumer file, override SHA27a27569335c377bf8662fb5b39d200ce98d52925cafdf51dee165464c1532078, label не Git SHA. .env backup приватный, backend/migrations не менялись, полная регрессия/failover не повторялись. Один локальный commit без push.
+
+Следующий92 — BGP propagation собственных private prefixes, withdrawal/re-advertisement и UI counters; только собственные GRE/instances/filter chains. Квота84% использовано после блока, этап12.5 и внешние границы остаются открытыми.
diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md
index 57990c0c..6516b6fb 100644
--- a/docs/ux-ui-plan.md
+++ b/docs/ux-ui-plan.md
@@ -1,6 +1,6 @@
# План реализации UX/UI по пятичасовым окнам
-Актуальное продолжение 2026-10-09: 12.5.90 принял контролируемый OSPF/BFD failover primary GRE→alternate GRE over WireGuard→primary; steady Ping3/3×2 во всех фазах,4 UI cases. BFD/not-Full≈798ms, first alternate route≈4250ms — HTTP observations, не SLA. Отдельный переходный Ping18/40/55% loss сохранён, бесшовность не заявлена. Baseline10 коллекций×2 восстановлен. Runtime diagnostics89/optimizer87. Следующий12.5.91 — устранить обрезку IPv4 CIDR в OSPF Data Grid, выявленную live desktop снимком; затем BGP route propagation и оставшиеся внешние границы. Квота66% использовано; этап12.5 открыт.
+Актуальное продолжение 2026-10-09: 12.5.91 устранил неоднозначное отображение IPv4 CIDR в OSPF: /32 больше не разбивается на /3 и2. Column180px/nowrap/title, red-before/green-after geometry test,7 E2E и4 live UI cases. Baseline7 коллекций×2 восстановлен. Backend ux-diagnostics89/frontend ux-ospf91. Следующий12.5.92 — обмен только собственным BGP prefix через GRE, отзыв и повторное объявление, API/UI контекст. Квота84% использовано; этап12.5 открыт.
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-09. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md
index 7e93ab50..04586ec1 100644
--- a/docs/ux-ui-progress.md
+++ b/docs/ux-ui-progress.md
@@ -1,6 +1,6 @@
# UX/UI: журнал реализации
-Актуальное продолжение 2026-10-09: 12.5.90 принял контролируемый OSPF/BFD failover primary GRE→alternate GRE over WireGuard→primary; steady Ping3/3×2 во всех фазах,4 UI cases. BFD/not-Full≈798ms, first alternate route≈4250ms — HTTP observations, не SLA. Отдельный переходный Ping18/40/55% loss сохранён, бесшовность не заявлена. Baseline10 коллекций×2 восстановлен. Runtime diagnostics89/optimizer87. Следующий12.5.91 — устранить обрезку IPv4 CIDR в OSPF Data Grid, выявленную live desktop снимком; затем BGP route propagation и оставшиеся внешние границы. Квота66% использовано; этап12.5 открыт.
+Актуальное продолжение 2026-10-09: 12.5.91 устранил неоднозначное отображение IPv4 CIDR в OSPF: /32 больше не разбивается на /3 и2. Column180px/nowrap/title, red-before/green-after geometry test,7 E2E и4 live UI cases. Baseline7 коллекций×2 восстановлен. Backend ux-diagnostics89/frontend ux-ospf91. Следующий12.5.92 — обмен только собственным BGP prefix через GRE, отзыв и повторное объявление, API/UI контекст. Квота84% использовано; этап12.5 открыт.
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
@@ -36,7 +36,19 @@
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
-| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–90 и ограничения ниже. Runtime backend ux-diagnostics89/frontend ux-optimizer87. |
+| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–91 и ограничения ниже. Runtime backend ux-diagnostics89/frontend ux-ospf91. |
+
+## 12.5.91 — OSPF Data Grid: целый IPv4 CIDR (2026-10-09)
+
+Уточнена визуальная проблема90: это не потеря API-данных, а перенос последней цифры /32 на вторую строку; первый ряд выглядел как /3. Столбец назначения расширен150→180px, span использует whitespace-nowrap и title полного значения. Установленные Data Grid/Table/ScrollArea, pinned column, theme и структура сохранены. ReUI inline API и audit checklist прочитаны; [API](https://reui.io/docs/components/base/data-grid?ref=mcp), [превью](https://reui.io/components/data-grid?ref=mcp).
+
+**Проверки.** Новый browser test ждёт document.fonts.ready и проверяет Range rectangles: ровно одна строка, текст внутри ячейки. До исправления1280px failed: /3 и2 дали два rect. Первоначальная проверка только границ проходила и была уточнена, чтобы не пропустить разрыв CIDR. После изменения4 размера320/390/1280/1440 и IPv4 CIDR15/18 символов проходят; сортировка/количество строк/document fit/no writes сохранены. С OSPF unknown cost/disabled/inactive —7/7. Scoped TypeScript, ESLint/diff passed. Это meaningful visual regression, не проверка буквального size в исходнике.
+
+**Live.** Backend diagnostics89 и обновлённый frontend ospf91, без mocks: два CHR×1280/320 =4/4. Свои GRE/OSPF private link198.18.93.0/30/loopbacks198.18.193.1/32↔2/32; Full/active learned route/Ping3/3. Целый /32 виден в одной строке после загрузки шрифтов; title, границы ячейки, URL/reload, document fit/pageerror0 подтверждены. [Browser отчёт](ux-ui/acceptance/browser-ospf-cidr-2026-10-09.json), [native/cleanup](ux-ui/acceptance/ospf-cidr-native-2026-10-09.json), [ПК после](ux-ui/screens/ospf-real-cidr-server1-1280-2026-10-09.png), [320px после](ux-ui/screens/ospf-real-cidr-server1-320-2026-10-09.png). PC снимок просмотрен:198.18.193.2/32 полностью читается. Проверка не обещает одновременную видимость всех колонок на телефоне: сохраняется внутренняя горизонтальная прокрутка.
+
+**Контроль/runtime.** Свои objects удалены по exact IDs/атрибутам, baseline7 коллекций×2 совпал, оба CHR доступны, default/public/195.195.195.195/32 и credentials сохранены. Нормализованные SHA source/deployed DataGrid renderer и OSPF consumer совпадали до изменения, поэтому проблема не была вызвана отставшим компонентом стенда. Один frontend Docker build, production TypeScript/build, entry/node --check/compose --wait passed. mmapp-test/frontend:ux-ospf91/version1.0.0-test.ospf91, image sha256:b4bade515234115b3cad257ceb87ef7beb347e754ca92d6a97a25adc2199fd33. Context optimizer87 frontend +один consumer file, override SHA27a27569335c377bf8662fb5b39d200ce98d52925cafdf51dee165464c1532078, label не Git SHA. .env backup приватный, backend/migrations не менялись, полная регрессия/failover не повторялись. Один локальный commit без push.
+
+Следующий92 — BGP propagation собственных private prefixes, withdrawal/re-advertisement и UI counters; только собственные GRE/instances/filter chains. Квота84% использовано после блока, этап12.5 и внешние границы остаются открытыми.
## 12.5.90 — OSPF/BFD: переход на резервный GRE и возврат (2026-10-09)
diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md
index e0b9edcd..886521f0 100644
--- a/docs/ux-ui-stage-12.md
+++ b/docs/ux-ui-stage-12.md
@@ -2144,3 +2144,15 @@ Production image не содержит dev-only jose: первый fixture impor
**Очистка.** Exact ID/name/comment/address/interface/interfaces/addresses/public key preflight; удалены только свои templates/area/instance/BFD/IP/bridge/GRE/WG peers/interfaces. Исходные IDs10 коллекций×2 совпали, public/default routes и195.195.195.195/32 сохранены. WG secret fields не включены в report. Оба CHR доступны, passwords неизменны; временных контейнеров не осталось. Backend diagnostics89/PG healthy, frontend optimizer87 сохранён. Один локальный commit без push.
Схема: primary → BFD/adjacency loss → alternate GRE over WG → проверка data plane → восстановленный primary. Транспорт подтверждён [GRE](https://help.mikrotik.com/docs/spaces/ROS/pages/24805531/GRE) и [WireGuard](https://help.mikrotik.com/docs/spaces/ROS/pages/69664792/WireGuard); таблицы сохраняют референсы общего плана. Продолжение91 — обрезка CIDR, затем BGP propagation/остальные внешние границы. BFD-only устойчивый запрет/physical WAN/IPv6/scale/maximum throughput/Portal SSO остаются открыты. Квота66% использовано; этап12.5 открыт.
+
+## 12.5.91 — OSPF Data Grid: целый IPv4 CIDR (2026-10-09)
+
+Уточнена визуальная проблема90: это не потеря API-данных, а перенос последней цифры /32 на вторую строку; первый ряд выглядел как /3. Столбец назначения расширен150→180px, span использует whitespace-nowrap и title полного значения. Установленные Data Grid/Table/ScrollArea, pinned column, theme и структура сохранены. ReUI inline API и audit checklist прочитаны; [API](https://reui.io/docs/components/base/data-grid?ref=mcp), [превью](https://reui.io/components/data-grid?ref=mcp).
+
+**Проверки.** Новый browser test ждёт document.fonts.ready и проверяет Range rectangles: ровно одна строка, текст внутри ячейки. До исправления1280px failed: /3 и2 дали два rect. Первоначальная проверка только границ проходила и была уточнена, чтобы не пропустить разрыв CIDR. После изменения4 размера320/390/1280/1440 и IPv4 CIDR15/18 символов проходят; сортировка/количество строк/document fit/no writes сохранены. С OSPF unknown cost/disabled/inactive —7/7. Scoped TypeScript, ESLint/diff passed. Это meaningful visual regression, не проверка буквального size в исходнике.
+
+**Live.** Backend diagnostics89 и обновлённый frontend ospf91, без mocks: два CHR×1280/320 =4/4. Свои GRE/OSPF private link198.18.93.0/30/loopbacks198.18.193.1/32↔2/32; Full/active learned route/Ping3/3. Целый /32 виден в одной строке после загрузки шрифтов; title, границы ячейки, URL/reload, document fit/pageerror0 подтверждены. [Browser отчёт](ux-ui/acceptance/browser-ospf-cidr-2026-10-09.json), [native/cleanup](ux-ui/acceptance/ospf-cidr-native-2026-10-09.json), [ПК после](ux-ui/screens/ospf-real-cidr-server1-1280-2026-10-09.png), [320px после](ux-ui/screens/ospf-real-cidr-server1-320-2026-10-09.png). PC снимок просмотрен:198.18.193.2/32 полностью читается. Проверка не обещает одновременную видимость всех колонок на телефоне: сохраняется внутренняя горизонтальная прокрутка.
+
+**Контроль/runtime.** Свои objects удалены по exact IDs/атрибутам, baseline7 коллекций×2 совпал, оба CHR доступны, default/public/195.195.195.195/32 и credentials сохранены. Нормализованные SHA source/deployed DataGrid renderer и OSPF consumer совпадали до изменения, поэтому проблема не была вызвана отставшим компонентом стенда. Один frontend Docker build, production TypeScript/build, entry/node --check/compose --wait passed. mmapp-test/frontend:ux-ospf91/version1.0.0-test.ospf91, image sha256:b4bade515234115b3cad257ceb87ef7beb347e754ca92d6a97a25adc2199fd33. Context optimizer87 frontend +один consumer file, override SHA27a27569335c377bf8662fb5b39d200ce98d52925cafdf51dee165464c1532078, label не Git SHA. .env backup приватный, backend/migrations не менялись, полная регрессия/failover не повторялись. Один локальный commit без push.
+
+Следующий92 — BGP propagation собственных private prefixes, withdrawal/re-advertisement и UI counters; только собственные GRE/instances/filter chains. Квота84% использовано после блока, этап12.5 и внешние границы остаются открытыми.
diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md
index b5956892..d1991df4 100644
--- a/docs/ux-ui-test-server.md
+++ b/docs/ux-ui-test-server.md
@@ -1,6 +1,6 @@
# Тестовый сервер UX/UI — 2026-10-09
-Сервер161.104.46.222 предоставлен для временного тестирования. Backend ux-diagnostics89/version1.0.0-test.diagnostics89, frontend ux-optimizer87/version1.0.0-test.optimizer87; backend/PG healthy, frontend HTTP/browser passed. Основной блок IPsec77 принят в границах журнала, own fixtures/fake DB rows удалены; каталог flash CHR2 сохранён из-за неподтверждённого ownership. SSH/оба CHR доступны, пароли прежние.
+Сервер161.104.46.222 предоставлен для временного тестирования. Backend ux-diagnostics89/version1.0.0-test.diagnostics89, frontend ux-ospf91/version1.0.0-test.ospf91; backend/PG healthy, frontend HTTP/browser passed. Основной блок IPsec77 принят в границах журнала, own fixtures/fake DB rows удалены; каталог flash CHR2 сохранён из-за неподтверждённого ownership. SSH/оба CHR доступны, пароли прежние.
| Параметр | Проверенное состояние |
| --- | --- |
@@ -717,3 +717,5 @@ flowchart LR
12.5.88 (2026-10-09): recursion/GRE/Ping, apply/restore и4 browser checks passed; native baseline4 коллекций×2, cache/history сохранены. Backend recursive88, frontend optimizer87.
12.5.89 (2026-10-09): терминал/диагностика/RBAC и8 live UI checks passed, baseline7 коллекций×2 сохранён. Production auth не менялся.
+
+12.5.91 (2026-10-09): frontend ospf91; целый CIDR подтверждён4 live cases, baseline7 коллекций×2 сохранён. Backend diagnostics89 неизменён.
diff --git a/docs/ux-ui/acceptance/browser-ospf-cidr-2026-10-09.json b/docs/ux-ui/acceptance/browser-ospf-cidr-2026-10-09.json
new file mode 100644
index 00000000..173fdae6
--- /dev/null
+++ b/docs/ux-ui/acceptance/browser-ospf-cidr-2026-10-09.json
@@ -0,0 +1,43 @@
+{
+ "date": "2026-10-09",
+ "phase": "2 320",
+ "cases": [
+ {
+ "serverId": 1,
+ "width": 1280,
+ "destination": "198.18.193.2/32",
+ "oneLine": true,
+ "noCellClipping": true,
+ "documentFits": true,
+ "urlRestore": true
+ },
+ {
+ "serverId": 1,
+ "width": 320,
+ "destination": "198.18.193.2/32",
+ "oneLine": true,
+ "noCellClipping": true,
+ "documentFits": true,
+ "urlRestore": true
+ },
+ {
+ "serverId": 2,
+ "width": 1280,
+ "destination": "198.18.193.1/32",
+ "oneLine": true,
+ "noCellClipping": true,
+ "documentFits": true,
+ "urlRestore": true
+ },
+ {
+ "serverId": 2,
+ "width": 320,
+ "destination": "198.18.193.1/32",
+ "oneLine": true,
+ "noCellClipping": true,
+ "documentFits": true,
+ "urlRestore": true
+ }
+ ],
+ "errors": []
+}
\ No newline at end of file
diff --git a/docs/ux-ui/acceptance/ospf-cidr-native-2026-10-09.json b/docs/ux-ui/acceptance/ospf-cidr-native-2026-10-09.json
new file mode 100644
index 00000000..cb18d77d
--- /dev/null
+++ b/docs/ux-ui/acceptance/ospf-cidr-native-2026-10-09.json
@@ -0,0 +1,161 @@
+{
+ "date": "2026-10-09",
+ "name": "mm-ospf91-1791559266208",
+ "observations": [
+ {
+ "serverId": 1,
+ "nativeNeighbor": {
+ ".id": "*F3FDDC00",
+ "router-id": "198.18.193.2",
+ "address": "198.18.93.2",
+ "interface": "mm-ospf91-1791559266208",
+ "state": "Full",
+ "state-changes": "6"
+ },
+ "nativeRoute": {
+ ".id": "*2014C000",
+ "dst-address": "198.18.193.2/32",
+ "gateway": "198.18.93.2%mm-ospf91-1791559266208",
+ "ospf": "true",
+ "active": "true",
+ "distance": "110"
+ },
+ "catalogNeighbors": [
+ {
+ "id": "*F3FDDC00",
+ "serverId": 1,
+ "serverName": "CHR",
+ "serverSite": "",
+ "serverCountry": "",
+ "address": "198.18.93.2",
+ "routerId": "198.18.193.2",
+ "instance": "mm-ospf91-1791559266208",
+ "area": "mm-ospf91-1791559266208",
+ "areaId": "0.0.0.0",
+ "interface": "mm-ospf91-1791559266208",
+ "state": "Full",
+ "uptime": "3s",
+ "stateChanges": 6,
+ "priority": null
+ }
+ ],
+ "catalogRoutes": [
+ {
+ "id": "*2014C000",
+ "serverId": 1,
+ "serverName": "CHR",
+ "serverSite": "",
+ "destination": "198.18.193.2/32",
+ "type": "Unknown",
+ "cost": null,
+ "nextHop": "198.18.93.2",
+ "via": "mm-ospf91-1791559266208",
+ "area": ""
+ }
+ ],
+ "pingReceived": 3
+ },
+ {
+ "serverId": 2,
+ "nativeNeighbor": {
+ ".id": "*F3FDDC00",
+ "router-id": "198.18.193.1",
+ "address": "198.18.93.1",
+ "interface": "mm-ospf91-1791559266208",
+ "state": "Full",
+ "state-changes": "5"
+ },
+ "nativeRoute": {
+ ".id": "*2014C000",
+ "dst-address": "198.18.193.1/32",
+ "gateway": "198.18.93.1%mm-ospf91-1791559266208",
+ "ospf": "true",
+ "active": "true",
+ "distance": "110"
+ },
+ "catalogNeighbors": [
+ {
+ "id": "*F3FDDC00",
+ "serverId": 2,
+ "serverName": "CHR",
+ "serverSite": "",
+ "serverCountry": "",
+ "address": "198.18.93.1",
+ "routerId": "198.18.193.1",
+ "instance": "mm-ospf91-1791559266208",
+ "area": "mm-ospf91-1791559266208",
+ "areaId": "0.0.0.0",
+ "interface": "mm-ospf91-1791559266208",
+ "state": "Full",
+ "uptime": "3s",
+ "stateChanges": 5,
+ "priority": null
+ }
+ ],
+ "catalogRoutes": [
+ {
+ "id": "*2014C000",
+ "serverId": 2,
+ "serverName": "CHR",
+ "serverSite": "",
+ "destination": "198.18.193.1/32",
+ "type": "Unknown",
+ "cost": null,
+ "nextHop": "198.18.93.1",
+ "via": "mm-ospf91-1791559266208",
+ "area": ""
+ }
+ ],
+ "pingReceived": 3
+ }
+ ],
+ "cleanup": [
+ {
+ "serverId": 1,
+ "/routing/ospf/interface-template": true,
+ "/routing/ospf/area": true,
+ "/routing/ospf/instance": true,
+ "/interface/gre": true,
+ "/interface/bridge": true,
+ "/ip/address": true,
+ "/ip/route": true
+ },
+ {
+ "serverId": 2,
+ "/routing/ospf/interface-template": true,
+ "/routing/ospf/area": true,
+ "/routing/ospf/instance": true,
+ "/interface/gre": true,
+ "/interface/bridge": true,
+ "/ip/address": true,
+ "/ip/route": true
+ }
+ ],
+ "currentRoutes": [
+ {
+ ".id": "*20145050",
+ "dst-address": "198.18.93.0/30",
+ "gateway": "mm-ospf91-1791559266208",
+ "active": "true",
+ "distance": "0",
+ "routing-table": "main"
+ },
+ {
+ ".id": "*2014C000",
+ "dst-address": "198.18.193.1/32",
+ "gateway": "198.18.93.1%mm-ospf91-1791559266208",
+ "active": "true",
+ "ospf": "true",
+ "distance": "110",
+ "routing-table": "main"
+ },
+ {
+ ".id": "*20145060",
+ "dst-address": "198.18.193.2/32",
+ "gateway": "mm-ospf91-1791559266208-loop",
+ "active": "true",
+ "distance": "0",
+ "routing-table": "main"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/docs/ux-ui/screens/ospf-real-cidr-server1-1280-2026-10-09.png b/docs/ux-ui/screens/ospf-real-cidr-server1-1280-2026-10-09.png
new file mode 100644
index 00000000..ca59e073
Binary files /dev/null and b/docs/ux-ui/screens/ospf-real-cidr-server1-1280-2026-10-09.png differ
diff --git a/docs/ux-ui/screens/ospf-real-cidr-server1-320-2026-10-09.png b/docs/ux-ui/screens/ospf-real-cidr-server1-320-2026-10-09.png
new file mode 100644
index 00000000..12988316
Binary files /dev/null and b/docs/ux-ui/screens/ospf-real-cidr-server1-320-2026-10-09.png differ
diff --git a/docs/ux-ui/screens/ospf-real-cidr-server2-1280-2026-10-09.png b/docs/ux-ui/screens/ospf-real-cidr-server2-1280-2026-10-09.png
new file mode 100644
index 00000000..35e2aabd
Binary files /dev/null and b/docs/ux-ui/screens/ospf-real-cidr-server2-1280-2026-10-09.png differ
diff --git a/docs/ux-ui/screens/ospf-real-cidr-server2-320-2026-10-09.png b/docs/ux-ui/screens/ospf-real-cidr-server2-320-2026-10-09.png
new file mode 100644
index 00000000..2eb8445a
Binary files /dev/null and b/docs/ux-ui/screens/ospf-real-cidr-server2-320-2026-10-09.png differ
diff --git a/tests/e2e/ospf-route-cidr.spec.ts b/tests/e2e/ospf-route-cidr.spec.ts
new file mode 100644
index 00000000..04113459
--- /dev/null
+++ b/tests/e2e/ospf-route-cidr.spec.ts
@@ -0,0 +1,42 @@
+import { test, expect } from "@playwright/test"
+
+for (const width of [320, 390, 1280, 1440]) test("OSPF: CIDR целиком помещается в закреплённой ячейке " + width, async ({ page }) => {
+ await page.setViewportSize({ width, height: 1000 })
+ const destinations = ["198.18.190.2/32", "255.255.255.255/32"]
+ const host = { serverId: 1, serverName: "CHR", serverSite: "", serverCountry: "RU" }
+ let writes = 0
+ await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
+ await page.route("**/api/**", r => {
+ const path = new URL(r.request().url()).pathname
+ if (r.request().method() !== "GET") writes++
+ return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? [{ id: 1, name: "CHR", host: "192.0.2.1", enabled: true, country: "RU", status: null }] : path === "/api/ospf/catalog" ? {
+ neighbors: [], interfaces: [], instances: [], bfdSessions: [], failures: [],
+ routes: destinations.map((destination, i) => ({ ...host, id: "route-" + i, destination, type: "Unknown", cost: null, nextHop: "198.18.91.2", via: "GRE-reserve", area: "" })),
+ } : {} })
+ })
+ await page.goto("/ospf?server=1&tab=routes")
+ await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
+ await page.evaluate(() => document.fonts.ready)
+ for (const destination of destinations) {
+ const text = page.getByText(destination, { exact: true })
+ await text.scrollIntoViewIfNeeded()
+ const bounds = await text.evaluate(element => {
+ const cell = element.closest("td")!, cellRect = cell.getBoundingClientRect(), style = getComputedStyle(cell)
+ const range = document.createRange(); range.selectNodeContents(element)
+ const rects = Array.from(range.getClientRects())
+ return { cell: { left: cellRect.left + parseFloat(style.paddingLeft), right: cellRect.right - parseFloat(style.paddingRight), top: cellRect.top, bottom: cellRect.bottom }, text: rects.map(r => ({ left: r.left, right: r.right, top: r.top, bottom: r.bottom })) }
+ })
+ // Splitting /32 into /3 + 2 makes the prefix visually ambiguous.
+ expect(bounds.text).toHaveLength(1)
+ for (const rect of bounds.text) {
+ expect(rect.left).toBeGreaterThanOrEqual(bounds.cell.left - 1)
+ expect(rect.right).toBeLessThanOrEqual(bounds.cell.right + 1)
+ expect(rect.top).toBeGreaterThanOrEqual(bounds.cell.top)
+ expect(rect.bottom).toBeLessThanOrEqual(bounds.cell.bottom)
+ }
+ }
+ expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
+ await page.getByRole("button", { name: "Назначение", exact: true }).click()
+ await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
+ expect(writes).toBe(0)
+})