Forge
markdowndeeb25a2
1# ADR 0010: Данные UI-режимов (Focus / Balanced / …) в TOML
2
3**Статус:** Accepted · Implemented
4**Дата:** 2026-04-02
5**Обновлено:** 2026-04-25 — в `[capabilities]` ключи **IDE Health**: `ide_health_*`. Подробности — [§ История](#adr0010-history).
6
7## Связанные ADR
8
9| ADR | Роль |
10|-----|------|
11| [0003](0003-debug-ui-mode-separate-from-power.md) | Отдельный UI-режим Debug (не кокпит Power) |
12| [0006](0006-presentation-layers-and-feature-slices.md) | Слои, вертикальные срезы и роль MainWindowViewModel |
13| [0017](0017-multi-window-workspace-and-agent-surfaces.md) | мультиоконность и топология |
14| [0022](0022-workspace-health-lexicon.md) | канон имён и эволюция для **IDE Health** (пересечение с таблицей `ide_health_*` ниже). |
15
16### Вне ADR
17
18| Документ | Роль |
19|----------|------|
20| [`attention-zone-panel-playbook-v1.md`](../design/attention-zone-panel-playbook-v1.md) | зона ↔ панель ↔ топология |
21
22### Снимок реализации
23
24| Элемент | Значение |
25|---------|----------|
26| — | загрузчик TOML, `UiModeCatalog`, capabilities, bundle `UiModes/` |
27| — | override и `docs/ui-ux` — по мере нужды |
28
29## Резюме
30
31- Режимы UI (Focus/Balanced/…) — **данные в TOML** (`UiModes/`), не только константы в C#.
32- Bundle + merge с `.cascade/workspace.toml`; override без обратной записи динамики в шип.
33- **`[capabilities]`** и ключи **`ide_health_*`** — контур IDE Health в том же каталоге режимов.
34- Tomlyn, snake_case; JSON не дублируем как второй канон конфигов.
35
36---
37## Контекст
38
39Сейчас **спеки режимов** (видимость панелей, группы редактора, слот темы, ширина развёрнутого чата и т.д.) зашиты в **C#** (`UiModeLayoutRegistry`, связанные константы). Глобальные размеры хрома (сплиттеры, минимумы строк и пр.) собраны в **`UiWorkspaceLayoutDimensions`** и применяются через **`UiWorkspaceLayout`**.
40
41Потребность: **менять раскладку режимов без пересборки** (дизайн, пресеты, эксперименты), в одном стиле с уже принятым в продукте хранением настроек в **`settings.toml`** (Tomlyn уже в зависимостях).
42
43**JSON для этого не предлагается** как основной формат: пользовательские настройки уже TOML; дублировать второй «официальный» текстовый формат для конфигов режимов нежелательно.
44
45## Решение
46
47<a id="adr0010-p1"></a>
481. **Источник данных режимов** — **несколько файлов TOML**: **индекс** задаёт **порядок и полный список id** режимов в меню (включая встроенные и **дополнительные**, см. ниже) + **отдельный файл на режим** (`Focus.toml`, `Balanced.toml`, …; шипнутый набор дублирует дефолты из `UiModeLayoutRegistry` / `DefaultsForFamily`), чтобы диффы и смысл «один режим — один файл» были очевидны. Если файла режима нет — загрузчик берёт те же дефолты из кода. Версионируем с приложением (как темы: копия рядом с exe или встроенный ресурс + fallback на диск). **Расположение шипнутого набора** — **подкаталог `UiModes/`** рядом с exe (тот же паттерн, что **`Themes/`**: копия в вывод сборки рядом с exe). Отдельный **путь в настройках пользователя** — возможное расширение позже, **в первой версии не обязательно**.
49<a id="adr0010-p2"></a>
502. **Глобальные метрики хрома** (ширина дерева по умолчанию, толщина сплиттеров, минимумы высот нижней зоны, политика «свёрнуто» и т.п.) — **один раз** в отдельном файле **`workspace.toml`** рядом с индексом и режимами (не копировать в каждый `Debug.toml`).
51<a id="adr0010-p3"></a>
523. **Схема окна (инварианты):** число колонок **MainGrid**, смысл колонок, привязка контролов к колонкам — **намеренно остаются в коде/XAML** в рамках этого ADR. TOML описывает **режимы поверх этого каркаса** (видимость, метрики, слот темы и т.д.), а не альтернативную разметку окна. Возможный вынос схемы окна в данные — **отдельное ADR**, если появится продуктовая необходимость (несколько каркасов, плагины и т.п.).
53<a id="adr0010-p4"></a>
544. **Загрузка при старте:** десериализация в модель, совместимую по смыслу с `UiModeLayoutSpec` (+ числа вроде ширины правой колонки в **развёрнутом** виде для режима, если остаётся в данных).
55<a id="adr0010-p5"></a>
565. **Fallback:** если файл отсутствует, битый или не прошёл валидацию — **встроенные значения по умолчанию** из кода, без падения IDE. См. ниже про роль этих дефолтов.
57<a id="adr0010-p6"></a>
586. **Версия схемы:** поле **`schema_version`** — **только в корне файла индекса** (первый читаемый файл набора). Оно задаёт версию **всего** бандла `UiModes/` — индекс, **`workspace.toml`**, per-mode файлы. В **`workspace.toml` отдельного `schema_version` нет** (избегаем двух чисел и рассинхрона). Миграции формата — по одному номеру из индекса.
59<a id="adr0010-p7"></a>
607. **Валидация при загрузке (минимум):** неизвестный **`theme_slot`** → fallback; в **шипнутом** наборе отсутствует спека для **обязательного** id → предупреждение + встроенная спека из кода. **Минимальный набор обязательных id** (который продукт не имеет права «потерять» в данных) задаётся **в коде** — сейчас это совпадает с **`UiModeLayoutRegistry.OrderedModeIds`**. Индекс может перечислять **больше** id, чем этот минимум (**дополнительные пользовательские или пресетные режимы**). См. ниже про наследование и отдельный пункт меню без замены `Debug`.
61<a id="adr0010-p8"></a>
628. **Capabilities** — тип **`UiModeCapabilities`** (`Features/UiChrome/UiModeCapabilities.cs`). В **`UiModes/<id>.toml`** ключи **предметные** (что в интерфейсе), в **snake_case**; десериализация через **`CascadeTomlSerializer`** (имена свойств модели в PascalCase → snake_case). Мердж: явное значение → при **`inherits`** — от родителя → иначе **`DefaultsForFamily`**. API: **`GetCapabilities`**, **`GetWindowTitleOverride`**.
63
64 | Ключ TOML | Смысл |
65 |-----------|--------|
66 | `active_task_strip` | Полоса активной задачи / Task Cockpit под тулбаром |
67 | `main_window_title` | Полный заголовок главного окна |
68 | `quick_actions` | Быстрые действия у задачи |
69 | `agent_operations_panel` | Блок операций агента в чате (Balanced) |
70 | `agent_trace` | Панель trace агента (Power) |
71 | `autonomous_agent_telemetry` | Кокпит Power: явный доступ к выводу (терминал и подсказки); **не** канал IDE Health |
72 | `ide_health_on_terminal_tab` | Дубль IDE Health на вкладке «Терминал» (Power) |
73 | `ide_health_main_column_span` | Column span области IDE Health в основной сетке (Power) |
74 | `ide_health_strip` | Показывать полосу IDE Health под редактором |
75 | `ide_health_surface` | `bottom_strip` или `dedicated_page` — слой представления IDE Health |
76 | `instrumentation_tabs` | Вкладки событий/тестов/отладки в нижнем доке |
77 | `hypotheses_tab` | Вкладка «Гипотезы» |
78 | `risk_summary_card` / `result_summary_card` | Карточки риска и результата в чате |
79
80### Слои модели (шпаргалка для автора `UiModes/*.toml`)
81
82Чтобы не путать **id режима**, **раскладку**, **семью** и **capabilities**, удобно держать в голове такие уровни:
83
84| Слой | Что это | Откуда берётся |
85|------|---------|----------------|
86| **Id режима** | Стабильная строка (пункт меню, ключ в каталоге, имя файла `Id.toml`) | `index.toml` → `modes`, плюс таблица встроенных id в коде для fallback |
87| **Раскладка** | Видимость панелей, число групп редактора, слот темы, флаги вроде «выбирать вкладку терминала» | База: `UiModeLayoutRegistry` для встроенных id; мердж с полями текущего `*.toml`; при **`inherits`** — база = разрешённый родитель |
88| **`workspace.toml`** | Общие числа хрома (сплиттеры, ширины чата по правилу Power / AgentChat / остальные, минимумы строк и т.д.) | Один файл на бандл; без копирования в каждый режим |
89| **`family`** | Продуктовая роль: Focus, Balanced, Power, AgentChat, Debug — влияет на **дефолты capabilities** и ветки в коде (`UiModeFamily`) | Порядок: явный **`family`** в файле режима → при **`inherits`** — семья **разрешённого родителя** → для встроенных id — **таблица в коде** по id → иначе **Balanced** |
90| **Capabilities** | Что показывать в интерфейсе (гипотезы, quick actions, хром кокпита Power и т.д.) | База: при **`inherits`** — **capabilities разрешённого родителя**; без **`inherits`** — **`DefaultsForFamily(family)`**. Поверх — явные ключи в `*.toml` |
91
92Узкий ключ в том же **`workspace.toml`** про **где монтировать превью Markdown** (не путать с будущей общей топологией нескольких `TopLevel`) — [0026](0026-markdown-preview-surfaces-and-placement.md).
93
94**Конечный пользователь IDE** в комбо «режим интерфейса» видит в основном **id пресета**; оси **`family`** / **capabilities** ему не обязательны, пока в UI нет отдельных переключателей под них.
95
96### Топология презентации зон (будущее расширение)
97
98Сейчас в коде одна топология — **`MainWindowDockedGrid`** (`AttentionLayoutSurfaceKind`, одно главное окно, колонки `MainGrid`). Когда в продукте появятся **альтернативы** (несколько `TopLevel`, сценарии [0017](0017-multi-window-workspace-and-agent-surfaces.md)), **логично** связывать выбранную топологию с merge слоёв [0010](0010-ui-modes-toml-configuration.md) и отдельным учётом **персональной** раскладки по мониторам (см. следующий абзац). **Без обратной записи** динамического ресайза окон в шипнутые файлы (см. подраздел про рантайм ниже).
99
100**Не смешивать** с картой **панель → зона** (`attention_zone_panels` / `AttentionZonePanelRuntime`): там семантика «какая панель в какой зоне», здесь — **в какой геометрии** (одно окно vs несколько) воплощены регионы. Подробнее: [`attention-zone-panel-playbook-v1.md`](../design/attention-zone-panel-playbook-v1.md).
101
102**Поля `presentation` / `zone_screen_layout` (корень `settings.toml`) и токены грамматики** в секции **`[presentation_grammar]`** (`screen_markers`, `screen_separator`, `zone_separator`, литералы якорей `pfd_zone_identifier` / `forward_zone_identifier` / `mfd_zone_identifier`) — строка раскладки по **физическим дисплеям** из [0017](0017-multi-window-workspace-and-agent-surfaces.md) («Несколько мониторов»), например `(PFD+Forward) (MFD)` или **`(P+F) (M)`** при коротких идентификаторах в TOML ([таблица](0017-multi-window-workspace-and-agent-surfaces.md#adr0017-presentation-grammar)); **между якорями внутри одной пары скобок** допускается **только** литерал **`zone_separator`** (**`Z`**, по умолчанию **`+`**); при **`Z = "|"`** в строке — только **`|`**, без подстановки «второго стиля». **EBNF** — [0017](0017-multi-window-workspace-and-agent-surfaces.md#adr0017-presentation-ebnf). **Где хранить:** **прежде всего** **`settings.toml`** пользователя ([0028](0028-user-settings-toml-localappdata-and-secrets.md)). Репозиторный **`.cascade/workspace.toml`** ([0021](0021-pfd-mfd-cockpit-attention-model.md) §2.1) — для **командных** соглашений по панелям и зонам, **не** для обязательной для всех схемы мониторов; при merge **пользовательские** значения **`presentation`** и токенов **перекрывают** одноимённые в бандле/репо. **Альтернативное имя ключа** — **`zone_screen_layout`**; в одном конфиге задаётся **одно** из двух. Перечисление значений `AttentionLayoutSurfaceKind` и **валидация** — в реализации; отдельный ADR под схему TOML **не требуется**, пока объём правил укладывается в этот подпункт.
103
104**`inherits = "BaseId"`** — один родитель: наследуется **весь** уже разрешённый режим (раскладка, capabilities, ширина чата, полоса задачи, заголовок окна — по правилам мержа в коде). В дочернем файле задаются **только отличия** от родителя. Отдельная секция вроде `[inherits]` со списком «какие поля наследовать» **не используется**: при такой модели список дельт в файле и так короче, чем перечисление срезов.
105
106### Порядок разрешения `family` (формально)
107
1081. Если в **`Id.toml`** задано **`family = "…"`** (регистр не важен) — используется оно.
1092. Иначе, если задано **`inherits`** — берётся **`family`** уже разрешённого родителя (рекурсивно).
1103. Иначе — для известных встроенных id (`Focus`, `Balanced`, …) — **`BuiltinFamily(id)`** в коде.
1114. Иначе — **Balanced** (неизвестный id без явной семьи).
112
113### Примеры: `inherits` и опционально `family`
114
115**Дополнительный режим как Debug, но с полосой задачи и своим заголовком** — без дублирования всей спеки:
116
117`index.toml` (фрагмент; полный индекс см. шипнутый `UiModes/index.toml`):
118
119```toml
120schema_version = 1
121modes = [ "Focus", "Balanced", "Power", "AgentChat", "Debug", "MySuperDebug" ]
122```
123
124`MySuperDebug.toml`:
125
126```toml
127inherits = "Debug"
128
129active_task_strip = true
130main_window_title = "CascadeIDE — мой отладочный пресет"
131```
132
133Поле **`family` не нужно**: семья остаётся **Debug** (как у разрешённого родителя), поведение гипотез/инструментирования — как у отладки.
134
135**Цепочка из двух уровней:** каждый файл задаёт только дельту; корень цепочки — встроенный id с полной спекой (или другой уже разрешённый режим).
136
137```toml
138# DeepWork.toml — ответвление от Focus
139inherits = "Focus"
140editor_group_count = 1
141main_window_title = "CascadeIDE — Deep work"
142
143# MyDeep.toml — ответвление от DeepWork
144inherits = "DeepWork"
145chat_expanded_width_pixels = 400
146```
147
148Оба новых id должны быть перечислены в **`modes`** в `index.toml`.
149
150**Редкий случай: `inherits` от `Debug` и явный `family`** (ось `UiModeFamily` отличается от родителя; см. пояснение после примера):
151
152```toml
153inherits = "Debug"
154family = "Balanced"
155```
156
157Раскладка — мердж спеки **Debug** с полями файла. Ось **`family`** для кода (`UiModeFamily`, предикаты вроде **`IsDebugFamily`**) становится **Balanced**. Важно: при **`inherits`** базовый слой **capabilities** в загрузчике берётся от **разрешённого родителя** (`parentResolved.Capabilities`), а не от **`DefaultsForFamily(family)`** — то есть флаги интерфейса по умолчанию остаются «как у Debug», пока их не переопределить ключами в том же `*.toml`. Если когда-нибудь понадобится стартовать capabilities от **`DefaultsForFamily`** выбранной семьи при наследовании раскладки — это отдельное изменение контракта загрузчика.
158
159*См. также подраздел **`inherits` и `family`: это не одно и то же** ниже — там про помодульный мердж и ширину чата.*
160
161### Динамический ресайз после старта и TOML (источник правды)
162
163Файлы **`UiModes/*.toml`**, **`workspace.toml`** и индекс набора режимов — **пресет**: они задают раскладку и метрики **на момент загрузки** конфигурации (старт приложения и, если когда-нибудь появится, явная команда «перечитать конфиг»). Это **не** живой журнал геометрии окна.
164
165**Перетаскивание сплиттеров, изменение ширин/высот панелей и любой динамический ресайз хрома после запуска** — **рантайм-состояние в памяти**. Оно **ни при каком сценарии по умолчанию не записывается обратно** в шипнутые рядом с exe **`UiModes/*.toml`** и **`workspace.toml`**: ни при каждом движении сплиттера, ни при смене режима, ни при выходе из IDE. Так редактор и диффы по репозиторию остаются **источником дизайна режима**; случайный дрейф окна пользователя не портит файлы пресета.
166
167Если позже понадобится **сохранять** геометрию между сеансами, это оформляется **отдельным каналом** (например поля в **`settings.toml`**, отдельный пользовательский override в `%LocalAppData%` — см. «Границы» ниже), а **не** обратной записью в дистрибутивные TOML режимов.
168
169**MCP (`ide_set_panel_size` и аналоги)** и снимки UI для тестов/агента меняют **текущую** раскладку в процессе работы; контракт тот же: **не** считать, что вызов перезаписывает TOML на диске, пока это явно не задокументировано как отдельная операция «экспорт пресета в файл».
170
171### Один и тот же `Debug` vs отдельный `MySuperDebug` (наследование)
172
173- **Правка `Debug.toml` или override на `Debug`** меняет **сам** режим `Debug` для того набора файлов, к которому относится конфиг: **штатный Debug в том виде, как в дистрибутиве, рядом не живёт** — это ожидаемо, если цель именно «переопределить Debug». Для сценария «и **стоковый** Debug доступен, и мой вариант рядом» нужны **два разных id**.
174- **Дополнительный режим с новым id** (например `MySuperDebug`), по смыслу близкий к `Debug`, но с другой раскладкой под контекст: объявляется в **индексе** (отдельная строка в меню, своё сохранённое значение в настройках), отдельный файл **`MySuperDebug.toml`**. Чтобы не дублировать всю спеку, в файле режима поддерживается **наследование от базового id**, например поле **`inherits = "Debug"`** (имя уточняется при реализации): загрузчик **мержит** разрешённую спеку `Debug` с переопределениями из `MySuperDebug.toml`. Так **не нужно** копипастить десятки полей и **не нужно** менять C# только ради нового id — достаточно индекса + файла режима (и при необходимости общих правил для «неизвестных» id в коде: разрешённые базы, fallback).
175- **Подпись в UI** для id (отображаемое имя вместо сырого `MySuperDebug`) — при необходимости отдельные поля в индексе/TOML или локализация; **не** смешивать с обязательным минимумом id из [п. 7](#adr0010-p7).
176
177### Семантика для кода: «family» и устаревшие флаги `Is*Mode`
178
179**Контекст (проблема до одной оси):** проверки вроде «это Debug для гипотез?» через **`UiMode == "Debug"`** или булевы **`IsDebugMode`** ломаются для производного id (**`MySuperDebug`**): строка id другая, а продуктовый смысл «семья отладки» тот же.
180
181**Текущий код** уже переведён на **`UiModeFamily`** и предикаты расширения — см. подраздел **«Реализация в коде»** ниже. Таблица вариантов и **`family` в TOML** — про **данные после загрузчика**; там **`family`** после мержа должна попадать в ту же ось, что и сегодняшний `UiModeFamily`.
182
183Дополнительно, **`NormalizeUiMode`** сейчас сводит неизвестный режим к **`Balanced`**. Режимы с **новыми id** из индекса должны **сохраняться** (после проверки «id есть в загруженном списке»), иначе пользовательский `MySuperDebug` превратится в Balanced без предупреждения.
184
185**История обсуждения альтернатив** (зафиксировано для контекста; **реализован подход A**):
186
187| Подход | Суть | Плюсы | Минусы |
188|--------|------|--------|--------|
189| **A. Семейство (`family`) в данных** | После мержа в результирующей модели есть поле **`family`** (enum или строка: `debug`, `power`, …). В TOML — опционально **`family = "debug"`**; если не задано — **наследуется от базы** при `inherits` (у корня цепочки — как у встроенного id). Код переводит **`IsDebugMode`** на **`family == debug`** (или переименование в **`IsDebugFamily`**). | Явная семантика; `MySuperDebug` без копипаста получает «debug» через наследование; проще тестировать. | Нужна миграция существующих проверок по строке id; перечень `family` всё равно конечный в коде. |
190| **B. Только цепочка `inherits`** | «Отладочность» выводится обходом inherits до известного встроенного id (**`ResolvesToBuiltIn("Debug")`**). | Не дублировать поле `family` в TOML. | Сложнее рассуждать и отлаживать; глубина/циклы; неочевидно, когда оборвать цепочку для UX. |
191| **C. Явный `family` в каждом TOML без автонаследования** | Каждый режим, включая производные, пишет `family` руками. | Максимально явно. | Дублирование; легко ошибиться и задать `MySuperDebug` без `family`. |
192| **D. Только точное совпадение id** | `IsDebugMode` остаётся `== "Debug"`; производные режимы **не** считаются Debug для гипотез и т.д. | Минимум кода. | Противоречит сценарию «MySuperDebug как Debug»; пользователь вводит в заблуждение. |
193
194**Реализовано:** подход **A** — в результирующей модели после загрузки есть **`UiModeFamily`**; в TOML опционально **`family`**; проверки в VM/вью — по семье и capabilities, не по сырой строке id там, где нужна продуктовая роль.
195
196#### `inherits` и `family`: это не одно и то же (разделение ответственности)
197
198- **`inherits`** задаёт **разрешённого родителя**: от него берутся **`UiModeLayoutSpec`** и **базовый слой `UiModeCapabilities`** (не только «каркас»); дочерний файл накладывает **помодульные** переопределения. Для **`chat_expanded_width_pixels`**: явное значение в файле → иначе у родителя по `inherits` → иначе у корневого режима — **`workspace.toml`** (через **`UiWorkspaceLayoutRuntimeMetrics`**) и правило Power / AgentChat / остальные.
199- **`family`** отвечает за **продуктовую роль** в коде: какие вкладки/инструменты/политики считать «режимом отладки», «power» и т.д. Это **другое измерение**, не дублирение `inherits`: теоретически можно представить раскладку, унаследованную от `Debug`, но явно переопределённую семантику (редкий кейс).
200
201Чтобы не казалось, что в TOML **две ручки про одно**, правило умолчаний может быть таким:
202
2031. Для **встроенных** id **`family`** задаётся **таблицей в коде** (тот же смысл, что сейчас заложен в id).
2042. Для режима с **`inherits`** поле **`family` в TOML не обязательно**: по умолчанию **`family` = семья разрешённого родителя** (например `MySuperDebug` → `inherits = "Debug"` → **Debug**). Тогда в файле достаточно **`inherits`**, без отдельной строки `family`, пока семантика совпадает с базой.
2053. **Явный `family` в TOML** — когда нужно **перебить** вычисленную семью для **`UiModeFamily`** (редко; см. ограничение про базу capabilities при **`inherits`** в примере выше).
206
207Так **`inherits` не «задаёт family» сам по себе** — он задаёт **цепочку родителя** для мержа раскладки и capabilities; **family** либо задаётся явно, либо наследуется от родителя, либо берётся из таблицы встроенных id, либо **Balanced** для неизвестного id. Разделение: мердж данных по файлу/родителю vs ось семьи для кода.
208
209**Связанные места (не исчерпывающе):** `MainWindowViewModel.Presentation` (**`UiModeFamily`**, инструментация), **`UiChromeViewModel.NormalizeUiMode`**, bloom по строке режима, **`GetChatPanelExpandedWidthPixels`**, MCP-обработчики с особыми правилами для режима отладки.
210
211#### Не плодить `Is*Mode` в VM
212
213Набор булевых **`IsFocusMode`**, **`IsPowerMode`**, **`IsDebugMode`** и т.д. был типичным **запахом**. В коде они **сняты** в пользу **`UiModeFamily`** и предикатов (см. **«Реализация в коде»**). После загрузчика TOML предпочтительно иметь **один вычисляемый контекст** после загрузки: enum **`UiModeFamily`** и/или компактный объект **capabilities** (что показывать: гипотезы, элементы кокпита Power, …), заполняемый из резолвнутой спеки и правил по умолчанию. **Не** возвращать очередной `IsNewThingMode` и не плодить сравнения с **сырой строкой id** там, где нужна **семья**.
214
215#### Реализация в коде (зафиксировано до загрузчика TOML)
216
217Это **не отдельный документ**: договорённости по оси режима фиксируются **здесь**, в ADR про режимы и TOML, чтобы и люди, и агент не искали разрозненные заметки.
218
219| Элемент | Назначение |
220|--------|------------|
221| **`UiModeFamily`** + **`UiModeFamilyResolver.FromNormalizedMode`** | Одна ось после **`NormalizeUiMode(UiMode)`**; производные id маппятся в семью так же, как сегодня встроенные строки. |
222| **`UiModeFamilyExtensions`** (`IsFocusFamily`, `IsBalancedFamily`, `IsPowerFamily`, `IsAgentChatFamily`, `IsDebugFamily`) | Симметричные предикаты вместо размножения **`== UiModeFamily.*`** по VM. |
223| **Доменное имя там, где не хватает «голого» enum** | Например приватное **`AutonomousCockpitActive`** в автономной сессии: смысл «кокпит автономного агента», внутри — тот же **`IsPowerFamily()`**. |
224| **XAML** | Привязки к **`UiModeFamily`** через конвертеры **`UiModeFamilyEq` / `UiModeFamilyNe`** (параметр — имя члена enum). |
225| **После TOML** | Загрузчик мержит спеку, **`family` → `UiModeFamily`**, **`UiModeCapabilities`** (и опционально **`main_window_title`**), без отката к набору **`Is*Mode`**. |
226
227### Fallback и встроенные дефолты (код vs TOML)
228
229Встроенный fallback **не претендует** быть «главным» источником красивой раскладки: его задача — **предсказуемо поднять IDE**, если данных на диске нет или они битые. **Нормальный путь** — шипнутые TOML рядом с exe; тогда fallback срабатывает редко (чистая установка, сломанный каталог, разработка без копии файлов).
230
231Качество текущих констант в коде (в том числе режимы вокруг **Power**) может быть **средним** — это **отдельная** ветка работы: доводка дефолтов, выравнивание с дизайном, возможно приближение built-in к содержимому шипнутых файлов. Это **не блокирует** механику «TOML + fallback из кода»: сначала надёжная загрузка и тесты, **параллельно или позже** — полировка встроенных значений (или генерация одного из другого в сборке — вне скоупа этого ADR, пока не понадобится).
232
233### Видимость панелей vs «0 px»
234
235В конфиге режима и в смысле продукта **первична семантика видимости** (`visible` / `hidden` для панели; при необходимости позже — отдельное состояние вроде «свёрнуто с полоской», если появится политика не нулевой полоски). **«Скрыто» не должно задаваться главным образом как `width = 0`** в режиме TOML: нулевая ширина колонки и скрытый сплиттер — **производное** от правил видимости и глобальных метрик из `workspace.toml` (и кода инвариантов). Так одна и та же идея покрывает **любую** сворачиваемую колонку/зону, не только чат.
236
237## Границы (что точно не входит в этот ADR)
238
239- **Схема окна** (каркас MainGrid, новые колонки/зоны из данных) — см. [п. 3](#adr0010-p3) решения; не входит.
240- Подсветка синтаксиса **`.toml` в редакторе** — отдельно (см. [EDITOR-LANGUAGES.md](../EDITOR-LANGUAGES.md): грамматики TOML в бандле нет); на загрузку конфига не влияет.
241- **Пользовательский override** в `%LocalAppData%\…` поверх шипнутого файла — возможное расширение **после** базовой схемы, не обязательно в первом коммите.
242
243## Последствия
244
245- **Плюсы:** единый человекочитаемый формат с настройками; правки режимов без компилятора; проще ревью диффов раскладки; дополнительные режимы с **наследованием** без дублирования спеки и без обязательной правки C# под каждый новый id.
246- **Минусы:** парсинг, ошибки на диске, тесты на fallback; документация полей TOML для контрибьюторов; загрузчик с **мержем** `inherits`; комбо режимов в UI — от **загруженного индекса** (код хранит **минимум** для валидации и fallback); **`family`** и **capabilities** должны оставаться согласованы с **`UiModeFamily`** и **`UiModeCapabilities.DefaultsForFamily`**; **`NormalizeUiMode`** сохраняет пользовательские id из индекса; **в коде VM булевые `Is*Mode` сняты** (см. «Реализация в коде»). Основные **`docs/ui-ux`** приведены к **`UiModeFamily`** / capabilities (обзор: **`docs/ui-ux/ui-modes-overview-v1.md`**).
247- **Связь с MCP:** поведение `ide_set_panel_size` и снимков UI не должно ломаться; контракт с диском — см. подраздел **«Динамический ресайз после старта и TOML»** выше (рантайм и MCP **не** перезаписывают `UiModes/*.toml` / `workspace.toml` по умолчанию).
248
249## Документация (в scope)
250
251Навигационные и UX-доки должны описывать **`UiModeFamily`**, **capabilities** и при необходимости TOML, а не устаревшие булевы **`Is*Mode`**. Обзор для читателя: **`docs/ui-ux/ui-modes-overview-v1.md`**; макет окна и карта концепт→код — **`docs/ui-ux/cascade-ide-ui-layout-v1.md`**, **`docs/ui-ux/concept-to-implementation-map-v1.md`**. Исторические ADR (например сравнение подходов A–D) могут упоминать старые имена в прошедшем времени — это норма.
252
253## Отклонённые альтернативы
254
255- **Только JSON** — расходится с `settings.toml` и выбранным стеком Tomlyn для пользовательских конфигов.
256- **Только код** — оставляет текущее состояние; отклоняется как основной путь из-за цели «файл режима» без релиза.
257
258## Следующий шаг (опционально)
259
260Пользовательский override каталога `UiModes/` из `%LocalAppData%` при продуктовой необходимости; точечные правки **`docs/ui-ux`** при изменении поведения режимов в коде.
261
262---
263
264## История изменений
265
266<a id="adr0010-history"></a>
267
268| Дата | Изменение |
269|------|-----------|
270| 2026-04-08 | намерение задать **топологию презентации** зон в TOML после появления альтернатив одному `MainGrid` ([0017](0017-multi-window-workspace-and-agent-surfaces.md)); см. подраздел ниже. |
271| 2026-04-11 | **`presentation`** / **`zone_screen_layout`:** прежде всего **`settings.toml`**, не командный репо — [0017](0017-multi-window-workspace-and-agent-surfaces.md#adr0017-presentation-grammar), [п. 4](0017-multi-window-workspace-and-agent-surfaces.md#adr0017-p4); токены грамматики — секция **`[presentation_grammar]`** (без отдельных **`pfd_zone_alias`** — короткие имена через **`pfd_zone_identifier`** и т.д.); **`screen_markers`** / **`screen_separator`** / **`zone_separator`**, **EBNF** — [0017](0017-multi-window-workspace-and-agent-surfaces.md#adr0017-presentation-ebnf). |
272| 2026-04-11 | якоря `adr0010-p1`…`p8` в разделе «Решение» и ссылки на [п. 3](#adr0010-p3), [п. 7](#adr0010-p7); соглашение — [README ADR](README.md#adr-anchors-policy). |
273| 2026-04-25 | в **`[capabilities]`** ключи контура **IDE Health** в TOML: **`ide_health_*`** (свойства `UiModeCapabilities` — `IdeHealth*`). |
274
View only · write via MCP/CIDE