Forge
markdowndeeb25a2
1# ADR 0017: Несколько окон приложения (мультиоконность), зоны экрана и поверхности агента
2
3**Статус:** Accepted · Implemented
4**Дата:** 2026-04-05
5**Обновлено:** 2026-04-18 — канон числа `TopLevel` и `display.screens` / topology; подробности — [§ История](#adr0017-history).
6
7## Связанные ADR
8
9| ADR | Роль |
10|-----|------|
11| [0010](0010-ui-modes-toml-configuration.md) | режимы и TOML поверх каркаса окна; **топология презентации зон** — отдельный подраздел в 0010, не смешивать с `attention_zone_panels` |
12| [0012](0012-floating-workspace-chrome.md) | плавающий хром; отдельные окна как одна из механик |
13| [0021](0021-pfd-mfd-cockpit-attention-model.md) | семантика зон PFD / лобовое / MFD — **не** синоним числа окон |
14| [0025](0025-sdk-attention-zones-and-capabilities.md) | зоны в SDK |
15| [0016](0016-agent-client-protocol-external-agent.md) | внешний агент по ACP — ортогонально тому, *в скольких окнах* показывается встроенный UI |
16| [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md) | идентификаторы команд, **`Hotkeys/hotkeys.toml`** + пользовательский оверлей, палитра |
17| [0031](0031-agent-chat-clarification-batches-and-threading.md) | пакеты уточнений в чате, не путать с PFD-подтверждениями |
18| [0002](0002-debug-human-agent-parity.md) | паритет MCP |
19| [0008](0008-mcp-contracts-and-testable-infrastructure.md) | контракты MCP |
20| [0063](0063-instrument-deck-named-composition-one-anchor.md) | ось **instrument deck** и слово *presentation* vs CDS — см. § там; **ключи топологии** — ниже в этом ADR |
21| [0120](0120-primary-work-surface-intercom-or-editor.md) | `primary_work_surface` — Intercom или Editor в Forward (ортогонально `presentation`) |
22
23### Вне ADR
24
25| Документ | Роль |
26|----------|------|
27| [`attention-zone-panel-playbook-v1.md`](../design/attention-zone-panel-playbook-v1.md) | зона ↔ панель ↔ топология; в коде — `AttentionLayoutSurfaceKind` |
28| [`concept-pfd-mfd-cascade-v1.md`](../ui-ux/concept-pfd-mfd-cascade-v1.md) | UX-концепт PFD/MFD (superseded текстом [0021](0021-pfd-mfd-cockpit-attention-model.md)) |
29| [`concept-to-implementation-map-v1.md`](../ui-ux/concept-to-implementation-map-v1.md) | карта концепт → код |
30| [skia-surfaces-vs-overlays-v1.md](../design/skia-surfaces-vs-overlays-v1.md) | Skia-поверхности vs оверлеи |
31
32## Резюме
33
34- **Несколько `TopLevel`** — продуктовая модель разнесения зон по мониторам; семантика зон ([0021](0021-pfd-mfd-cockpit-attention-model.md)) **не равна** числу окон.
35- Источник истины раскладки — строка **`presentation`** / **`zone_screen_layout`** в **`settings.toml`** ([0028](0028-user-settings-toml-localappdata-and-secrets.md)), парсер + `[presentation_grammar]`.
36- **`(P+F)(M)`** → два окна; **`(P)(F)(M)`** → три; порядок групп в строке = слева направо на экранах.
37- **`MfdHostWindow` / `PfdHostWindow`** — полный shell зоны; плейсмент только через `PresentationHostWindowPlacement`.
38- MCP **`ide_get_ui_layout`** уже отдаёт все окна; roadmap — EICAS на втором экране, fallbacks, подтверждения агента ([«Открытые вопросы»](#adr0017-open-questions)).
39
40## Состояние реализации (актуально по репозиторию)
41
42Ниже — **что уже есть в коде**, чтобы не путать с продуктовым backlog из [«Открытых вопросов»](#adr0017-open-questions).
43
44### Уже реализовано
45
46| Область | Где в коде / что делает |
47|--------|-------------------------|
48| Разбор строки `presentation` | `Services/Presentation/PresentationParser.cs`, **`PresentationInnerEtoGrammar`** (Eto.Parse), `PresentationGrammarTokens`, `PresentationLayoutAnalyzer`, `PresentationAnchorKind`, **`PresentationAnchorSlot`** (веса); тесты `CascadeIDE.Tests/PresentationParserTests.cs`. **Веса зон** внутри группы — [§](#adr0017-zone-weights). **Колонки главного окна:** `PresentationMainGridColumnDefinitions.Get` → **`MainGridColumnDefinitions`** у `MainWindowViewModel`, применение в **`MainWindow.PresentationLayout.axaml.cs`** (`ColumnDefinitions.Parse` — Avalonia не биндит строку к `ColumnDefinitions` из VM); хвост MFD при двухякорном пресете — `340` или `0` при скрытии колонки под хост ([реализация](../../Services/Presentation/PresentationMainGridColumnDefinitions.cs)). |
49| Топология ↔ дисплеи | `PresentationMonitorTopology.OrderScreensForPresentation` — сортировка `Screen` слева направо, затем сверху вниз (общая сетка координат; не primary ОС как якорь). |
50| Плейсмент окон-хостов (`MfdHostWindow`, `PfdHostWindow`) | `Services/PresentationHostWindowPlacement` — целевой экран по индексу из пресета; иначе первый «не главный» экран; при сохранённых bounds — восстановление с прижатием к `WorkingArea`. На выделенном мониторе по умолчанию **`WindowState.Maximized`**; отключение — `[display]` **`maximize_presentation_host_windows_on_dedicated_screens`** (`DisplaySettings.MaximizePresentationHostWindowsOnDedicatedScreens`). |
51| Инвариант плейсмента | Позиционирование и восстановление геометрии `PfdHostWindow` / `MfdHostWindow` — **только** через `PresentationHostWindowPlacement` (и общий жизненный цикл во `MainWindow.PresentationHostWindows.axaml.cs`); не плодить параллельную логику экрана / `WorkingArea` в других Views без расширения этого сервиса. |
52| Персистентность геометрии хостов | `CascadeIdeSettings` / `[display]`: для MFD — `MfdHostWindowPixelX` / `PixelY` / `Width` / `Height`; для PFD — `PfdHostWindowPixelX` / `PixelY` / `Width` / `Height`; запись при `Closing` соответствующего окна (`Views/MainWindow.PresentationHostWindows.axaml.cs`). |
53| Окно-хост MFD | `Views/MfdHostWindow.axaml` — полный `MfdShellView` ([п. 8](#adr0017-p8-mfd-host-wide)), тот же `DataContext`, что у `MainWindow`; слой подсветки MCP как у главного окна. Колонка под якорем Mfd в `MainWindow` — **один** `MfdShellView` (страницы: чат, терминал, обозреватель решения и т.д.), без отдельного зашитого сплита «дерево + shell»; второй `TopLevel` повторяет **тот же** вторичный контур ([skia-surfaces-vs-overlays-v1.md](../design/skia-surfaces-vs-overlays-v1.md)), не обязательно пиксель-в-пиксель по ширине. |
54| Жизненный цикл | Нет отдельного пункта меню «второе окно» — источник истины строка `presentation` / `zone_screen_layout`; автозапуск при `Loaded` (см. `MainWindow.PresentationHostWindows.axaml.cs`): при подходящей топологии && `OpenPfdHostWindowOnStartup` / `OpenMfdHostWindowOnStartup` && достаточно мониторов — открываются `PfdHostWindow` и/или `MfdHostWindow`; опционально MCP `toggle_mfd_host_window` при той же топологии (`CanExecute`). При открытом хосте соответствующая колонка в `MainWindow` скрывается (`SetPfdHostWindowShellOpen` / `SetMfdHostWindowShellOpen`). |
55| Топология внимания | `AttentionLayoutSurfaceKind`: при хостах — `MainWindowPlusMfdHostTopLevel`, `MainWindowPlusPfdHostTopLevel`, `MainWindowPlusPfdMfdHostTopLevel` (`MainWindowViewModel.Presentation.cs`). |
56| MCP / снимки | `UiLayoutSnapshot`, роли `mfd_host` / `pfd_host` для соответствующих окон; `ide_get_ui_layout` по всем top-level. |
57
58### Частично или вне текущего кода (остаётся roadmap)
59
60- **Пресет с тремя группами `(…) (…) (…)`** (любой порядок якорей, напр. `(P) (F) (M)` или `(M) (F) (P)`): по **канону** — **три** `TopLevel`, по одной зоне на окно; **пространственно** слева направо = порядок групп в строке (см. §«Пространственный канон»). **В коде:** `PresentationLayoutAnalyzer.IsTripleOneAnchorPerZonePreset`, индексы экранов для хостов — `TryGetPfdHostPresentationScreenIndex` / `TryGetMfdHostPresentationScreenIndex`; окна **`PfdHostWindow`** и **`MfdHostWindow`**, главное — Forward; плейсмент и persist — `PresentationHostWindowPlacement` / `[display]`. Остаётся roadmap: продуктовая доводка UX, fallback при смене мониторов, расширения MCP вне базового `ide_get_ui_layout`.
61- **Fallback** при исчезновении монитора / сильной смене раскладки ОС: есть clamp к рабочим областям; отдельный UX «монитор недоступен» не описан здесь как обязательный.
62- **Закрытие главного окна:** при сбросе `DataContext` у `MainWindow` вызывается закрытие хоста (`CloseMfdHostWindowIfOpen`); явная подписка на `MainWindow.Closing` для каскада — не зафиксирована в этом списке (зависит от жизненного цикла приложения).
63- **EICAS / IDE Health на втором экране**, плотность чата/терминала — продуктовые открытые пункты ([«Открытые вопросы»](#adr0017-open-questions)).
64- **Подтверждения агента** при двух `TopLevel` — направление в [п. 6](#adr0017-p6), детали реализации — открыты.
65
66---
67## Контекст
68
69Сейчас **одно главное окно** (`MainWindow`) задаёт весь видимый кокпит: редактор, дерево, чат, колонка Mfd и т.д. Конфигурация режимов ([0010](0010-ui-modes-toml-configuration.md)) описывает **видимость и метрики поверх фиксированного каркаса**; альтернативная «схема окна» целиком в данных явно отложена.
70
71Потребность: **разнести роли по экрану** без обязательной ужимки всего в одну колонку — в том числе:
72
73- **Несколько физических мониторов** — в идеале **`(PFD) (Forward) (MFD)`** (три дисплея); см. подраздел «Несколько мониторов» и таблицу схем **`(PFD+Forward+MFD)`** при дефолтном **`Z`** или **`(PFD|Forward|MFD)`** при **`zone_separator = "|"`** / **`(PFD+Forward) (MFD)`**; или **один** широкий/изогнутый дисплей с одной парой скобок и тремя якорями в одном окне и явными зонами (см. концепт PFD/MFD, §6).
74- **Вынести семантическую зону внимания на другой дисплей** — в первую очередь **MFD** (чат, терминал, trace, тяжёлые панели агента); при необходимости **PFD** или компактную полосу подтверждений/статуса. Продуктовая формулировка — **зона ↔ второй или третий монитор**, а не абстрактное «второе окно». В коде тип `MfdHostWindow`, в снимке MCP поле `role` = `mfd_host` для такого второго `TopLevel`.
75- **Эксперименты с раскладкой** изолированно от стабильных пресетов — режим **Flight** как песочница; мультиоконность логично отрабатывать там первыми (см. §5 `concept-pfd-mfd-cascade-v1.md`).
76
77[0012](0012-floating-workspace-chrome.md) уже допускает **отдельные окна** для части хрома (телеметрия, полоски). **Базовый MCP под мультиоконность уже есть:** `ide_get_ui_layout` отдаёт JSON с массивом `windows` (по одному дереву на каждое открытое `Window`, роли `main` / `mfd_host` / `other`); поиск контрола по имени для инспекции/действий — с приоритетом главного окна и обходом остальных (`Cockpit/Surface/UiLayoutSnapshot`, `UiControlAppearance`). Этот ADR **расширяет предмет** с технического «несколько корней в снимке» на **продуктовую модель** нескольких окон (какие поверхности, жизненный цикл, состояние, связь с режимами и агентом); точечные **дополнения контракта** — при появлении новых типов окон или семантик регионов ([см. п. 7](#adr0017-p7)).
78
79### Один монитор и три семантические зоны ([0021](0021-pfd-mfd-cockpit-attention-model.md))
80
81Три пространственных якоря внимания (PFD, лобовое, MFD) **не требуют** трёх отдельных окон. Текущая реализация — **одно** главное окно, колонки `MainGrid`, топология `AttentionLayoutSurfaceKind.MainWindowDockedGrid` (см. playbook).
82
83**Опциональный** сценарий: разнести те же семантики по **нескольким `TopLevel` на одном физическом мониторе** (например очень широкий дисплей) — один процесс, та же карта панель→зона ([`AttentionZonePanelRuntime`](../design/attention-zone-panel-playbook-v1.md)), иная только **геометрия презентации**. **По умолчанию это не цель:** достаточно **главного окна + вынесенной зоны на другом мониторе** и/или **пресета внутри одного `TopLevel`** ([0021](0021-pfd-mfd-cockpit-attention-model.md), мотив FancyZones). Отдельно обсуждать **три связанных `TopLevel` на одном мониторе** имеет смысл только при явной обратной связи или ультрашироких сценариях; иначе риск и сложность перевешивают выигрыш над двумя окнами или сеткой в одном окне.
84
85**Не** считается аргументом за мультиоконность: «заблокировать перемещение окон **на уровне ОС**». Прилипание, сетка и предсказуемая раскладка достигаются в рамках **одного** `TopLevel` (сплиттеры, пресеты) или программным позиционированием без обещания запрета средствами оболочки; см. [0022](0022-mfd-visual-design-surface-axaml-blazor.md) (второй монитор как геометрия одного окна — ортогонально числу окон).
86
87<a id="adr0017-several-monitors"></a>
88
89### Несколько мониторов
90
91**Обозначения (чтобы не смешивать «зоны на одном экране» и «число дисплеев»):**
92
93- **`( … )`** — **один физический дисплей** (одна область в конфигурации мониторов ОС).
94- **Разделитель якорей внутри одной пары скобок** — **ровно** литерал **`Z`** из **`zone_separator`** (по умолчанию **`+`**). Несколько семантических якорей (**PFD**, **Forward**, **MFD**) на **этом же** дисплее (как колонки в одном `MainWindow`) записываются как **`anchor` `Z` `anchor` `Z` `anchor`**. В прозе этого ADR **`+`** и **`|`** — две привычные **иллюстрации** одного смысла; **в строке `presentation`** при **`Z = "+"`** допустимы только **`+`**, при **`Z = "|"`** — только **`|`** (без подстановки «синонима» в парсере).
95- **Краткая запись** — задаётся **тем же** ключом **`pfd_zone_identifier`** / **`forward_zone_identifier`** / **`mfd_zone_identifier`**, например **`"P"`**, **`"F"`**, **`"M"`**; в строке `presentation` допустим **только** выбранный литерал (без второго «синонима» в TOML).
96- **Несколько подряд `(…) (…) (…)`** — **разные** дисплеи; слева направо в тексте — удобная ориентировка, фактический порядок экранов задаёт геометрия и пресет. **Три дисплея под три якоря** — только **три пары скобок**, например **`(PFD) (Forward) (MFD)`**. Запись **без скобок** вроде голого `PFD | Forward | MFD` **двусмысленна** — в конфиге и доках не использовать; символ между якорями **внутри** скобок — это **`Z`**, не разделитель экранов.
97
98<a id="adr0017-display-screens-topology-naming"></a>
99
100### Ёмкость нотации и направление имён конфига (`display.screens` / `topology`)
101
102**Намеренная «ёмкость»:** одна и та же строка топологии (**`presentation`** в корне `settings.toml`, в будущем — ключ **`topology`** внутри таблицы ниже) **намеренно смешивает в одном слое** (а) **сколько** групп дисплеев в пресете (несколько `(…) (…) (…)` — см. [§ «Несколько мониторов»](#adr0017-several-monitors)) и (б) **как эти группы соотносятся** друг с другом и с якорями (пространственный порядок в тексте, семантика зон внутри группы). Развести «число дисплеев» и «описание топологии» **отдельными** ключами можно было бы явнее; **цена** — длиннее и многословнее. Текущая нотация платит за краткость тем, что смыслы (а) и (б) **не разводятся** без контекста грамматики и примеров ([§ грамматика](#adr0017-presentation-grammar), [EBNF](#adr0017-presentation-ebnf)).
103
104**Направление переименования TOML (снять путаницу слова *presentation* с «презентацией» UI и с другими слоями — см. [0063 § CDS](0063-instrument-deck-named-composition-one-anchor.md#adr0063-cds-not-presentation)):** канон — вложить описание топологии в уже смысловой **`[display]`** ([0050](0050-declarative-instrument-zone-placement-toml.md), `Display` в модели):
105
106- Таблица уровня «вся топология в одном месте» (сегодня корневые ключи **`presentation`** / **`zone_screen_layout`** и связанные поля — см. код) → **`[display.screens]`** (группы экранов пресета, не «презентация» в бытовом смысле).
107- Строка EBNF / размещения якорей — ключ **`topology`** (явное имя роли).
108- Грамматика: секция **`[presentation_grammar]`** → **`[display.screens.grammar]`** (или согласованное вложение под тем же префиксом; имя **`presentation_grammar`** в коде — `CascadeIdeSettings.PresentationGrammar`).
109
110**Что такое *screen* в имени `display.screens`:** в разговорном английском *screen* часто читается как «монитор ОС». Здесь **screen** — в том же духе, что **`screen_markers`** в [таблице токенов](#adr0017-presentation-grammar): **группа** в строке, граница одной презентационной поверхности пресета, кокпитная метафора PFD/MFD. Семантика «одна пара `( … )` ↔ один физический дисплей в конфигурации ОС» зафиксирована [выше](#adr0017-several-monitors); будущее имя таблицы **не** меняет парсер и семантику скобок **без** явной миграции кода.
111
112**Почему не `display.layout`:** слово **layout** уже перегружено — **CockpitPresentationLayout**, [0046](0046-presentation-layout-authority-and-cockpit-invariants.md), плюс раскладка **внутри** якоря (**instrument deck**, [0063](0063-instrument-deck-named-composition-one-anchor.md)). Отдельная таблица **`[display.layout]`** с ключом **`topology`** была бы читаемее без «экранов», но риск смешения с *внутренней* раскладкой региона выше, чем у **`display.screens`** после абзаца выше. Если продукт выберет **`layout`**, в доке и комментарии к TOML зафиксировать: *только многооконная / многоякорная топология пресета, не deck и не внутренняя сетка региона*.
113
114**Миграция:** читать старые ключи как **legacy** N релизов; при конфликте старого и нового — приоритет в реализации (новый канон побеждает при наличии); обновить примеры `settings.toml` при внедрении. Немедленная смена ключей в коде **не** требуется этим разделом — только зафиксированное направление; детали сроков — в задачах на загрузчик TOML.
115
116<a id="adr0017-presentation-grammar"></a>
117**Где хранить и зачем не только `workspace.toml`:** раскладка по **физическим дисплеям** — **личная** история (у членов команды разное железо). Репозиторный **`.cascade/workspace.toml`** ([0021](0021-pfd-mfd-cockpit-attention-model.md) §2.1, [0028](0028-user-settings-toml-localappdata-and-secrets.md)) — про **соглашение команды** по панелям и зонам в общей модели внимания, **не** про обязательную для всех схему «три монитора». В **`settings.toml`** в **корне** задаются строки **`presentation`** и/или **`zone_screen_layout`**; **опциональные** токены грамматики (**`screen_markers`**, **`screen_separator`**, **`zone_separator`**, литералы якорей — см. таблицу ниже) — в секции **`[presentation_grammar]`**. Всё это задаёт **топологию именно под железо пользователя** — **прежде всего** в **`settings.toml`** (`%LocalAppData%\CascadeIDE\`, [0028](0028-user-settings-toml-localappdata-and-secrets.md)); при merge с бандлом `UiModes/` и overlay репо **эти** ключи берутся из **пользовательского** слоя и **перекрывают** одноимённые, если они когда-либо появятся в шипнутом или репозиторном `workspace.toml` (дефолт продукта, не «решение команды о мониторах»).
118
119**Грамматика строки `presentation` (настраивается в TOML):** в том же файле, что и **`presentation`** / **`zone_screen_layout`**, в секции **`[presentation_grammar]`** задаются **опциональные** ключи — **какие символы** считать маркерами экрана, разделителем экранов, разделителем якорей и **какими строками обозначать три зоны** (длинные или короткие — один ключ на зону). Значения **на усмотрение пользователя**; ниже — **дефолты**, с которыми совпадают примеры по всему этому ADR:
120
121| Ключ в TOML (внутри `[presentation_grammar]`) | Дефолт | Смысл |
122|-------------|--------|--------|
123| **`screen_markers`** | **`"()"`** | строка из **двух** символов: открывающий и закрывающий границу **одного** дисплея |
124| **`screen_separator`** | **`" "`** | разделитель **между** группами (дисплеями), обычно пробел между `)` и следующим `(` |
125| **`zone_separator`** | **`"+"`** | разделитель **якорей** внутри одной пары маркеров; **в строке `presentation` между якорями допускается только этот литерал** (не другой символ «для красоты») |
126| **`pfd_zone_identifier`** | **`"PFD"`** | литерал якоря PFD в строке `presentation` (регистр и написание — как задано) |
127| **`forward_zone_identifier`** | **`"Forward"`** | литерал якоря лобового экрана |
128| **`mfd_zone_identifier`** | **`"MFD"`** | литерал якоря MFD |
129
130Три литерала должны быть **попарно различимы** (сравнение без учёта регистра). При коллизии реализация **сбрасывает** все три идентификатора на **дефолты** из таблицы.
131
132<a id="adr0017-anchor-aliases"></a>
133**Идентификаторы якорей.** Пользователь может задать, например, **`forward_zone_identifier = "Lob"`** и писать **`(PFD+Lob+MFD)`**. Короткие буквы **P** / **F** / **M** — тем же ключом: **`pfd_zone_identifier = "P"`** и т.д.; тогда в строке только **`(P+F+M)`**, без отдельных ключей «алиас».
134
135Примеры при **дефолтных** идентификаторах (**`PFD`**, **`Forward`**, **`MFD`**): в строке — полные литералы, например **`(PFD+Forward+MFD)`**, **`(PFD) (Forward) (MFD)`**. Примеры с одной буквой на якорь — при явной настройке **`[presentation_grammar]`** с короткими **`pfd_zone_identifier`** / **`forward_zone_identifier`** / **`mfd_zone_identifier`**.
136
137<a id="adr0017-presentation-ebnf"></a>
138**Грамматика (EBNF).** Ниже — **однозначное** описание; литералы якорей берутся из ключей TOML (см. таблицу). Пробелы **внутри** пары `screen_markers` между якорями не допускаются (только `zone_sep`).
139
140```ebnf
141(*--- Параметры из TOML: O, C, Z, S; идентификаторы якорей PfdId, ForwardId, MfdId (дефолты — см. таблицу) ---*)
142
143presentation ::= [ SP ] screen { SP screen } [ SP ]
144screen ::= "(" anchor { zone_sep anchor } ")"
145zone_sep ::= Z
146anchor ::= pfd_zone_identifier | forward_zone_identifier | mfd_zone_identifier
147
148pfd_zone_identifier ::= (* литерал из ключа pfd_zone_identifier *)
149forward_zone_identifier ::= (* литерал из ключа forward_zone_identifier *)
150mfd_zone_identifier ::= (* литерал из ключа mfd_zone_identifier *)
151
152(* после разбора: семантика PFD / лобовое / MFD — [0021](0021-pfd-mfd-cockpit-attention-model.md) *)
153
154(* лексер: три строки якоря; совпадение в порядке убывания длины литерала; для литерала длины 1 — без учёта регистра *)
155
156SP ::= U+0020 { U+0020 } (* один или более пробелов — разделитель экранов при дефолте *)
157```
158
159**Параметризация из TOML:** пусть **`O`** и **`C`** — первый и второй символ строки **`screen_markers`**, **`Z`** — строка **`zone_separator`**, **`S`** — строка **`screen_separator`**, литералы якорей — значения ключей **`pfd_zone_identifier`**, **`forward_zone_identifier`**, **`mfd_zone_identifier`**. Тогда:
160
161```ebnf
162presentation ::= [ S ] screen { S screen } [ S ]
163screen ::= O anchor { Z anchor } C
164(* anchor — как выше; токены якорей из TOML *)
165```
166
167Между якорями в одном экране допускается **только** литерал **`Z`** из TOML (при дефолте — **`+`**). Парсер **не** подставляет **`|`**, **`+`** или другой символ вместо выбранного **`Z`**. Строка **`presentation`** должна использовать **те же** литералы, что и выбранные токены. Парсер читает сначала ключи грамматики из **`[presentation_grammar]`** (или дефолты), затем применяет соответствующую продукцию. В коде — свойство **`CascadeIdeSettings.PresentationGrammar`**; merge с бандлом — в реализации ([0010](0010-ui-modes-toml-configuration.md), [0028](0028-user-settings-toml-localappdata-and-secrets.md)).
168
169**В конфиге (например TOML):** то же компактное значение можно сопроводить **обычным комментарием** `# …` — кратко пояснить для себя якоря и число дисплеев; отдельные отсылки к ADR в пользовательском файле для этого **не обязательны**.
170
171**Продуктовый слой (вне этого ADR):** где и как давать пользователю развёрнутые объяснения (внешняя документация, справка в IDE, приоритеты) — решение **уровня продукта**, не ADR; см. [architecture-policy.md](../architecture-policy.md).
172
173**Три типовых раскладки по дисплеям:**
174
175| Схема | Смысл |
176|--------|--------|
177| **`(PFD+Forward+MFD)`** … (дефолтный **`Z`**); **`(PFD|Forward|MFD)`** … при **`zone_separator = "|"`**; **`(P+F+M)`** — если в **`[presentation_grammar]`** заданы короткие **`pfd_zone_identifier`** / **`forward_zone_identifier`** / **`mfd_zone_identifier`** | Один дисплей: три якоря в **одном** `TopLevel` (`MainGrid`). |
178| **`(PFD+Forward) (MFD)`** …; **`(P+F) (M)`** — при тех же коротких идентификаторах в TOML | **Два `TopLevel`:** главное окно — **PFD и лобовое вместе**; второе окно — зона **MFD** (`MfdHostWindow`, вторичный контур). Типичный компромисс «два монитора». |
179| **`(PFD) (Forward) (MFD)`** …; **`(P) (F) (M)`** — при коротких идентификаторах | **Три `TopLevel`:** по **одной зоне на окно** (PFD; лобовое; MFD). Идеал при трёх мониторах ([0021](0021-pfd-mfd-cockpit-attention-model.md)); см. расхождение с v1 в [«Состояние реализации»](#adr0017-implementation-status). |
180
181**Канон числа окон:** **`(P+F)(M)`** ⇔ **два** верхнеуровневых окна (главное держит P+F, M вынесен); **`(P)(F)(M)`** ⇔ **три** верхнеуровневых окна (P, F, M раздельно). Это **семантика строки `presentation`**, не «одно окно на три экрана ОС» и не три окна на одном мониторе как цель по умолчанию.
182
183**Пространственный канон (три дисплея, типичный ряд):** **слева направо** на экранах (в порядке `PresentationMonitorTopology.OrderScreensForPresentation`: слева направо, затем сверху вниз) идут группы **`(…) (…) (…)`** в **том же порядке, в каком они записаны в строке `presentation`**. Первая группа — левый экран в этом порядке, вторая — средний, третья — правый. Примеры: **`(P) (F) (M)`** — слева PFD, по центру Forward, справа MFD; **`(M) (F) (P)`** — слева MFD, по центру Forward, справа PFD. Фиксированной привязки «P всегда слева» нет — задаёт **только** порядок скобок. Если физическая конфигурация мониторов не в один ряд, пользователь сопоставляет **i-ю** группу **i-му** экрану в выбранном порядке (или подгоняет раскладку ОС).
184
185<a id="adr0017-zone-weights"></a>
186
187### Доли зон на одном дисплее (опциональные веса у якорей)
188
189**Принято:** внутри **одной** пары **`screen_markers`** (один физический дисплей), если якорей **больше одного**, допускаются **опциональные положительные коэффициенты** — **литералы вещественных чисел** **перед** литералом якоря (в канонической записи **без** пробела между числом и идентификатором: **`0.25P`**). Перед разбором парсер **удаляет все символы Unicode whitespace** внутри пары скобок экрана, поэтому удобочитаемые варианты вроде **`(0.25P + 0.75F)(M)`** эквивалентны **`(0.25P+0.75F)(M)`**. Смысл: **доля** ширины основной полосы колонок (типичный случай — три колонки в `MainGrid`), сумма коэффициентов по **всем якорям этой группы** = **1**. Запись читается естественно, например **`(0.25P+0.75F)(M)`** — на первом экране P и F делят ширину в пропорции 1∶3; на втором экране один якорь **M** на весь дисплей, **отдельные веса не задаются** (и **между** группами **`(…) (…)`** весов нет: граница между мониторами — стол/геометрия ОС, не доля в строке).
190
191**Инвариант:** коэффициенты меняют только **доли** внутри одной группы (одного экрана). **Топология** — сколько групп `(…)`, какие якоря на каком экране, сколько физических дисплеев, и продуктовые следствия вроде максимизации главного окна при старте — задаются **составом якорей и скобок**, а не числами; подбор **`0.25` vs `0.5`** не переключает «два монитора на три» и не меняет семантику «на первом экране есть P и F».
192
193**Когда веса не указываются** — поведение как сейчас: равные доли между якорями в группе или существующий дефолт сетки (реализация).
194
195**Внутри одной группы** с **двумя и более** якорями: либо у **каждого** якоря задан коэффициент и **сумма = 1**, либо **ни у одного** (равные доли). Смешение вроде **`(0.25P+F+M)`** — **ошибка разбора** (неоднозначно).
196
197**Одна зона в скобках** — только литерал якоря, без коэффициента (или эквивалентно один якорь на весь экран).
198
199**Формат числа:** десятичная точка **`.`** (как в TOML/JSON); локаль с запятой в строке `presentation` **не** поддерживается, чтобы не дублировать неоднозначность.
200
201**Связь с парсером:** `PresentationParser` разбирает опциональные префиксы весов; внутри одной пары `screen_markers` структура списка якорей задаётся грамматикой **Eto.Parse** (`PresentationInnerEtoGrammar`, пакет NuGet **Eto.Parse**). Результат — `PresentationParseResult.Screens` как списки **`PresentationAnchorSlot`** (`Kind` + `Weight?`). Строки **без** коэффициентов по-прежнему каноничны (`Weight == null` на всех якорях группы).
202
203Ниже — **расширение EBNF** относительно [базовой продукции](#adr0017-presentation-ebnf); при желании объединить с основным блоком EBNF выше в одну продукцию (документация).
204
205```ebnf
206(*--- Дополнение: веса только внутри screen с двумя и более якорями; сумма weight = 1 ---*)
207
208weight ::= (* положительный литерал: целое или десятичное, десятичная точка U+002E *)
209weighted_anchor ::= weight anchor | anchor
210screen ::= O weighted_anchor { Z weighted_anchor } C
211(* семантика: в одном screen либо каждый weighted_anchor — это «weight anchor», либо каждый — просто «anchor»; смешение запрещено; при весах sum=1 *)
212
213(* Базовая форма без весов — как сейчас: только anchor *)
214```
215
216**Примеры** (короткие идентификаторы **`P`**, **`F`**, **`M`** в **`[presentation_grammar]`**): **`(0.25P+0.75F)(M)`**; **`(0.2P+0.3F+0.5M)`**; **`(P+F+M)`** — без весов, три равные доли.
217
218<a id="adr0017-nested-vh-notation"></a>
219
220### Вложенные оси **v** и **h** (T-layout; пояснение для агентов и обзорной документации)
221
222Иногда раскладку на **одном** физическом экране описывают **формулой с явными осями**, например:
223
224**`0.3vPFD + 0.7v(0.8hForward + 0.2hMFD)`**
225
226**Расшифровка (смысл для человека и для LLM):**
227
2281. Сначала делим экран **вертикально** (**`v`**): **30%** ширины слева — зона **PFD** (контекст workspace, дерево связей, первичные индикаторы в модели кокпита).
2292. Оставшиеся **70%** справа — отдельная вертикальная «полоса»; внутри неё делим **горизонтально** (**`h`**): **верхние 80%** — **Forward** (лобовое стекло: редактор, объект работы), **нижние 20%** — **MFD** (телеметрия, вторичный контур, настройки «под рукой»).
230
231Это классический **T-layout** кокпита: главное (код и статус «полёта») — в центре поля зрения, вспомогательное — внизу. Без указания **`v`** / **`h`** и порядка вложенности анализатор или агент **не могут однозначно** сопоставить доли с **`RowDefinitions` / `ColumnDefinitions`** в Avalonia: сначала нужно знать, **какой сплит идёт первым** (здесь — вертикальный 0.3/0.7), затем — как делится **правая** часть по **строкам** (0.8/0.2).
232
233**Связь с текущей строкой `presentation` (реализация v1):** парсер и веса в [§ «Доли зон»](#adr0017-zone-weights) сейчас задают **одну ось** — типично **три колонки** `MainGrid` (**PFD | Forward | MFD**) с долями по ширине. **Вложенная** запись **`v` / `h`** в этом ADR — **целевая семантика** для будущего расширения грамматики и/или отдельного слоя композиции (см. [0036](0036-cds-channel-compositor-surface-pipeline.md)); до появления парсера с вложенными группами её **нельзя** считать эквивалентом одной плоской суммы **`(0.3P+0.7F+…)`** без отдельной спецификации.
234
235<a id="adr0017-weight-fuse-policy"></a>
236
237**Предохранитель (политика стабильности геометрии):** если веса и оси заданы **явно в конфиге**, продукт **не** должен **динамически пересчитывать** пропорции в рантайме «по удобству» (изменение размера окна не должно подменять доли из настроек).
238
239- **Анализатор** (разбор / валидация конфигурации): в **каждой группе вложенности** сумма коэффициентов на этой оси **= 1** (как уже для плоского экрана с весами у якорей).
240- **Surface compositor** (конвейер [0036](0036-cds-channel-compositor-surface-pipeline.md)): при старте сессии **жёстко фиксирует** вычисленные из конфига **нормализованные** доли (и при необходимости пиксельные границы после измерения), без последующего «плавающего» пересчёта долей при обычном ресайзе; исключения (если появятся) — отдельное явное правило продукта, не молчаливое изменение коэффициентов.
241
242**Идеал (три физических дисплея):** **`(PFD) (Forward) (MFD)`** — целевая кокпитная раскладка при достаточном железе: контекст workspace / обозреватель и первичные индикаторы на первом экране, объект работы (редактор) на втором, вторичные тяжёлые панели (чат, терминал, trace…) на третьем. **По канону** пресет с **тремя группами** `(…) (…) (…)` означает **три отдельных `TopLevel`** (см. таблицу «Три типовых раскладки» выше), а не одно окно на три монитора без разбиения зон.
243
244**Метафора внимания (как в кокпите):** помимо **лобового** поля зрения есть **боковые зоны** — их можно подхватить **одним взглядом** (периферийное внимание), не отрываясь от работы «вперёд» надолго. **PFD** и **MFD** в этом смысле — **боковые** относительно **лобового (Forward)**, а не «второстепенные экраны»; мультиоконность и якорь направлений от экрана лобового согласуются с этой моделью.
245
246**Типичный компромисс (два дисплея):** **`(PFD+Forward) (MFD)`** при дефолтном **`Z`**, либо **`(PFD|Forward) (MFD)`** при **`zone_separator = "|"`** — первый монитор: PFD и лобовое вместе (как сейчас в одном `MainGrid`); второй — зона **MFD**. Удобно и распространённо; от идеала «три экрана» отличается объединением PFD и лобового на одном дисплее.
247
248### Матрица инстансов `SkiaHost` (зафиксировано для v1)
249
250`SkiaHost` считается **на слот**, а не «один на всё окно». Общее число рабочих поверхностей в типовых топологиях — три (PFD / Forward / MFD), меняется только распределение между `TopLevel`.
251
252| Топология (формула `presentation`) | `MainWindow` | `MfdHostWindow` | Итого |
253|--------|--------|--------|--------|
254| **`(PFD+Forward+MFD)`** | `SkiaHost(PFD)`, `SkiaHost(Forward)`, `SkiaHost(MFD)` | — | **3** слота / **1** `TopLevel` |
255| **`(PFD+Forward) (MFD)`** / **`(P+F) (M)`** | `SkiaHost(PFD)`, `SkiaHost(Forward)` | `SkiaHost(MFD)` | **3** слота / **2** `TopLevel` (канон) |
256| **`(PFD) (Forward) (MFD)`** / **`(P) (F) (M)`** | по канону — только один якорь на окно (отдельные хосты PFD и Forward — roadmap) | `SkiaHost(MFD)` | **3** слота / **3** `TopLevel` (канон); **v1:** как строка выше, т.к. P+F в `MainWindow` |
257
258**Следствие:** дополнительный «агрегирующий» `SkiaHost` на весь `MainWindow` не требуется; базовая геометрия задаётся `presentation`/`MainGridColumnDefinitions`, а поверхности остаются слот-ориентированными.
259
260**Четвёртый и далее** дисплей — опционально (например полноэкранная телеметрия, второй контур Mfd, внешний браузер доков — по пресету и политике продукта).
261
262Не единственная возможная схема; пресеты задают соответствия дисплей↔зона. **Канон** числа `TopLevel` для строки `presentation` — в таблице «Три типовых раскладки» и в матрице `SkiaHost` выше; факт кода для тройной группы — в [«Состояние реализации»](#adr0017-implementation-status). Снимок MCP для нескольких окон уже поддерживается ([п. 7](#adr0017-p7)), при новых сценариях — уточнение полей/ролей в той же поставке.
263
264## Решение (принципы; детализация — по итогам обсуждения)
265
266<a id="adr0017-p1"></a>
2671. **Один процесс, несколько `TopLevel`.** Окна принадлежат одному экземпляру IDE; общее состояние решения, агента, настроек — **единый** граф VM/сервисов за фасадом, без второго «независимого» экземпляра приложения. *Исключения* (если когда-нибудь понадобятся) — отдельное решение и отдельный ADR.
268
269<a id="adr0017-p2"></a>
2702. **Главный фрейм.** Сохраняется понятие **основного окна** (редактор, решение, главная навигация по умолчанию). Дополнительные `TopLevel` — это в первую очередь **носители вынесенных зон** (см. контекст: MFD/PFD на **втором или третьем мониторе**); они **не** обязаны дублировать полный кокпит. Состав и привязка к дисплею — пресет режима и/или явное действие пользователя (например «вынести MFD» / «вынести страницу чата» (как часть вторичного контура в зоне Mfd)). Роль `mfd_host` в снимке MCP относится к окну-хосту зоны Mfd и не ограничивает продукт только «чатом». Закрытие главного окна — **политика выхода из приложения** (закрыть все дочерние или запрос подтверждения) — фиксируется в реализации, не оставлять неявным.
271
272<a id="adr0017-p3"></a>
2733. **Что может жить во втором и следующих окнах (кандидаты, не обязательно всё в v1):** прежде всего **регион зоны Mfd** на **отдельном физическом дисплее** — в терминах [0021](0021-pfd-mfd-cockpit-attention-model.md) это семантика вторичного внимания; в коде тот же **хост** `MfdShellView`, внутри которого переключаются **страницы** `SecondaryShellPage` (чат, терминал, сборка, …) — **страница не является «зоной»**, зона — **Mfd** как якорь внимания; при необходимости **PFD** или компактная полоса подтверждений/критичных индикаторов на другом мониторе; выносимые фрагменты хрома в смысле [0012](0012-floating-workspace-chrome.md). **Документы редактора как floating MDI** остаются **вне объёма этого ADR**, пока не будет отдельного решения (как в [0012](0012-floating-workspace-chrome.md)).
274
275<a id="adr0017-p4"></a>
2764. **Связь с режимами UI ([0010](0010-ui-modes-toml-configuration.md)).** Пресеты задают **видимость и слоты** поверх доступных поверхностей: одно окно или несколько. **Топология презентации** зон (`AttentionLayoutSurfaceKind`: одно окно / несколько `TopLevel` и т.д.) согласуется с merge слоёв [0010](0010-ui-modes-toml-configuration.md) и отделением от карты панель→зона — см. подраздел «Топология презентации зон» в [0010](0010-ui-modes-toml-configuration.md). **Строка раскладки по дисплеям** — **`presentation`** (короткое имя) **или** **`zone_screen_layout`**; **одно** из двух, не оба. Значение — **литерал** из «Несколько мониторов» (например `(PFD+Forward) (MFD)`); рядом **опционально** токены грамматики из [таблицы](#adr0017-presentation-grammar) (**`screen_markers`**, **`screen_separator`**, **`zone_separator`**, литералы якорей) ([EBNF](#adr0017-presentation-ebnf)). **Хранение:** прежде всего **`settings.toml`** пользователя ([§ выше](#adr0017-presentation-grammar), [0028](0028-user-settings-toml-localappdata-and-secrets.md)), чтобы не смешивать **личную** схему мониторов с **командным** репозиторным `workspace.toml`. Расширение: ключи уровня «какие панели во вынесенной зоне по умолчанию» — **в TOML режима** / Flight при необходимости. Валидация и enum — в реализации.
277
278<a id="adr0017-p5"></a>
2795. **Сохранение геометрии.** Позиции и размеры окон **вынесенных зон** (в т.ч. на 2-м/3-м мониторе) — **не** обратная запись в шипнутые `UiModes/*.toml` (правило [0010](0010-ui-modes-toml-configuration.md)). **В коде v1:** геометрия **`MfdHostWindow`** персистится в пользовательском **`settings.toml`** (`MfdHostWindowPixelX` / `PixelY` / `Width` / `Height` — см. [«Состояние реализации»](#adr0017-implementation-status)); иные окна / полный snapshot workspace — при появлении отдельной договорённости.
280
281 **Пресеты и выбор дисплея для вынесенной зоны (принятое направление):** помимо сырого идентификатора дисплея ОС и сохранённых прямоугольников допускается **семантика, естественная пользователю** — направление соседства относительно **экрана, на котором в текущей раскладке представлена зона лобового (forward)** ([0021](0021-pfd-mfd-cockpit-attention-model.md)), а не от **primary** монитора ОС по умолчанию. **Кроссплатформенно:** целевой стек — **.NET + Avalonia**; перечисление дисплеев и их **границы в общей координатной сетке** (по ним вычисляется сосед в духе `left | right | up | down`) — через API графической платформы на **Windows, Linux и macOS**; это **не** привязка к одной ОС. Различия между ОС (имена дисплеев, hotplug, Wayland vs X11 и т.д.) — **в реализации и тестах**, не в продуктовой модели якоря forward. Имена ключей и схема в данных — в той же поставке, что и мультиоконность / [0010](0010-ui-modes-toml-configuration.md). **Tie-break:** если в выбранном направлении **несколько** соседних дисплеев, **не** навязывать автоматический выбор «лучшего» — **на усмотрение пользователя** (явный выбор экрана в UI, уточнение пресета, либо переход на сырой идентификатор/сохранённый прямоугольник). **Лобовое, разнесённое на несколько физических дисплеев** — редкий случай; для **v1** достаточно **одного экрана якоря** для forward; обобщение — при явной потребности. При смене конфигурации мониторов — **fallback** (сохранённая геометрия, возврат в главное окно и т.д.) остаётся предметом реализации.
282
283 <a id="adr0017-p5-primary-vs-forward"></a>
284 **Основной дисплей ОС и якорь Forward (принято):** системное понятие **«основной / primary»** монитора (в Windows — «сделать основным дисплеем») **не отождествляется** с семантикой **лобового экрана (Forward)** в модели кокпита. Primary нужен ОС и драйверам (расположение панели задач, масштаб и т.п.); типичный **конфликт** — сенсорный монитор, который **должен** быть основным из‑за калибровки касания, хотя в рабочей раскладке пользователя лобовым по смыслу является **другой** дисплей. Считать Forward по умолчанию равным primary **опасно** — ломает такие конфигурации. **Согласование** физической расстановки мониторов, их порядка в настройках ОС и строки **`presentation`** — **ответственность пользователя**; продукт не подменяет расстановку стола и не выводит семантику якорей только из флага primary. Реализация сопоставляет группы `(…) (…) …` с дисплеями по принятой схеме (геометрия рабочих областей и порядок в строке — см. код), без тождества «первый в `presentation` = primary ОС».
285
286<a id="adr0017-p6"></a>
2876. **Агент (ACP, [0016](0016-agent-client-protocol-external-agent.md)).** Транспорт к внешнему агенту **не зависит** от числа окон: один канал stdio/сессия. Решение этого ADR — только **куда монтируется** встроенный UI ответов и подтверждений (главное окно vs дополнительное). Краткие подтверждения и критичные сигналы по-прежнему должны быть доступны в **PFD-зоне** внимания (концепт §3); при втором окне — либо дубль компактной полосы, либо явное правило «фокус на главном окне для подтверждения».
288
289 <a id="adr0017-p6-confirmations"></a>
290 **Подтверждения при нескольких `TopLevel` (принятое направление):** не опираться только на системную модалку поверх «не того» окна. Показ запроса — в **потоке внимания PFD** (баннер/строка; презентация может быть без классической модалки на весь экран). Опционально **вторичный канал внимания** (звук, мигание в области уведомлений ОС), если фокус на MFD/втором окне. **Ответ пользователя** — те же семантики, что и у кнопок: **палитра команд** и **жесты из [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md)** — шипнутый [`Hotkeys/hotkeys.toml`](../../Hotkeys/hotkeys.toml) и пользовательский оверлей `%LocalAppData%\CascadeIDE\hotkeys.toml` ([0028](0028-user-settings-toml-localappdata-and-secrets.md)). До отдельного решения по протоколу: один вызов `request_confirmation` (и аналоги) по-прежнему завершается **ok/cancel** после ответа; «неблокирующее» относится к **UX презентации**, не к обязательной смене модели MCP.
291
292<a id="adr0017-p7"></a>
2937. **MCP и паритет ([0002](0002-debug-human-agent-parity.md), [0008](0008-mcp-contracts-and-testable-infrastructure.md), [0012](0012-floating-workspace-chrome.md)).** **Уже реализовано:** снимок UI для агента/тестов не ограничен одним `MainWindow` — `ide_get_ui_layout` строит **`windows[]`** по всем открытым `Window` процесса; действия по имени контрола учитывают окна помимо главного (см. контекст выше). **При появлении новых продуктовых окон или новой семантики** (например выделенный второй `TopLevel` под MFD с отдельной таксономией зон) **точечно расширять** контракт в **той же** поставке: новые значения `role`, поля идентификации региона/поверхности, тесты паритета — по аналогии с [0012](0012-floating-workspace-chrome.md). Мультиоконные поверхности не должны оставаться «только для человека» без осознанного исключения.
294
295<a id="adr0017-p8-mfd-host-wide"></a>
2968. **`MfdHostWindow` — только полный вторичный контур.** Для раскладки вроде **`(PFD+Forward) (MFD)`** / **`(PFD|Forward) (MFD)`** второй дисплей семантически несёт **всю зону Mfd**: одно окно **`MfdHostWindow`** с **полным** **`MfdShellView`** — тем же хостом **страниц** (`SecondaryShellPage`: чат, терминал, сборка, обозреватель решения, …) и тем же `DataContext` (`MainWindowViewModel`), что и **вторичный контур** в главном окне. **Паритет** здесь — совпадение **семантики вторичного контура** (набор страниц внутри `MfdShellView`), а **не** обязательное воспроизведение **всей** визуальной колонки под якорем Mfd в `MainWindow`: в главном окне под якорем Mfd — **один** `MfdShellView`; инструменты вторичного внимания (включая обозреватель решения) переключаются **страницами**, а не отдельным зашитым сплитом «дерево + shell» ([0021](0021-pfd-mfd-cockpit-attention-model.md): **зона** внимания ≠ **страница**). Отдельный `TopLevel` **не обязан** дублировать прочие панели главного окна вне семантики вторичного контура. **Не** делаем отдельного продукта «второе окно только с одной страницей» (узкий хост); альтернатива «только чат» **отклонена** как целевое состояние. Плейсмент, первичное сохранение геометрии и привязка к индексу экрана из `presentation` — см. [«Состояние реализации»](#adr0017-implementation-status); открыты: доводка fallback при смене мониторов, продуктовый UX ([п. 5](#adr0017-p5)).
297
298## Последствия
299
300- Потребуется **дизайн владения VM**: общие сервисы, дочерние окна как представления или отдельные лёгкие VM с подпиской на общий сессионный слой.
301- Тесты UI и сценарии, которые жёстко предполагали **только** дерево `MainWindow` без учёта `windows[]` в MCP, потребуют обновления или явного двойного режима (одно окно / несколько окон); сам MCP-снимок по нескольким `TopLevel` уже поддерживается.
302- Документация для пользователя (позже): как вынести панель, как вернуть, где сохраняется геометрия.
303
304## Отклонённые альтернативы (как целевое состояние)
305
306- **Полагаться только на внешний Cursor/IDE на втором мониторе** без встроенной мультиоконности — отклонено как не закрывающее сценарий «одна Cascade + зоны экрана» и PFD/MFD внутри продукта (см. концепт §8 — внешние инструменты **дополняют**, не заменяют осмысленную раскладку Cascade).
307- **Сразу вводить полный MDI документов** — вне объёма; см. [0012](0012-floating-workspace-chrome.md).
308- **Несколько окон на одном мониторе ради «запрета перетаскивания средствами ОС»** — отклонено как мотивация: такая цель слаба и закрывается раскладкой **внутри одного окна** (см. подраздел «Один монитор и три семантические зоны» выше).
309
310<a id="adr0017-modes-scope"></a>
311
312## Уточнение: режимы UI (Power, Flight и др.)
313
314**Принято:** переработка **Power**, **Balanced** и остальных пресетов/семей в смысле [0010](0010-ui-modes-toml-configuration.md) — **отдельная** линия работ; в объёме **первой поставки** второго `TopLevel` эти режимы **не трогаем**; последующая переработка пресетов и семей — **отдельная** линия. Мультиоконность v1 **не** блокируется и **не** смешивается с согласованием «как будет в Power». Вопрос «только **Flight** / **Power** с флагом» для мультиоконности **снят** до отдельной дорожной карты режимов. **Flight** по-прежнему удобен как полигон для экспериментов (см. контекст выше), без обязательства ограничивать продукт только им.
315
316<a id="adr0017-open-questions"></a>
317## Открытые вопросы (для обсуждения перед кодом)
318
319- **Три `TopLevel` на одном физическом мониторе** (три якоря PFD/лобовое/MFD как три рамки ОС): **по умолчанию не цель** — достаточно связки «главное окно + вынесенная **зона** на другом мониторе» (2-й/3-й дисплей), мультимонитора по [0021 §13](0021-pfd-mfd-cockpit-attention-model.md) и оси **одно окно + пресет/FancyZones** в [0021](0021-pfd-mfd-cockpit-attention-model.md). Отдельный пересмотр «три на одном» — только при явной обратной связи (например ультраширокий один дисплей).
320- Второй `TopLevel` под зону Mfd на отдельном мониторе — см. [п. 8](#adr0017-p8-mfd-host-wide) и [«Состояние реализации»](#adr0017-implementation-status): **полный** `MfdShellView` в **`MfdHostWindow`**; семантика зон и страниц — [0021](0021-pfd-mfd-cockpit-attention-model.md). **Базовый** плейсмент, сохранение bounds и автозапуск — в коде. **Открыто:** EICAS/IDE Health при вынесении, **UX чата и терминала** (плотность, клавиатура), полнота fallback при смене мониторов; переработка режимов UI — см. [«Уточнение: режимы UI»](#adr0017-modes-scope).
321- **Подтверждения агента (детали продукта):** таймауты, деструктивные операции, нужен ли отдельный модальный слой процесса — при реализации поверх принятого направления ([п. 6](#adr0017-p6) выше).
322- **Пресеты и мониторы (детали реализации):** принятый ориентир — [см. п. 5](#adr0017-p5) выше (якорь **forward**, соседство `left | right | up | down`). **Tie-break** при нескольких соседях в одном направлении — **на стороне пользователя** ([см. п. 5](#adr0017-p5)), не обязательная эвристика в продукте. **Ключи раскладки по дисплеям** — **`presentation`** / **`zone_screen_layout`** и токены грамматики — **персональные**, см. [п. 4](#adr0017-p4) и [§ слой хранения](#adr0017-presentation-grammar); **открытая деталь реализации:** полнота fallback при несовпадении раскладки ОС.
323
324## Принятие
325
326**2026-04-11** — статус **Accepted**; ссылка из [architecture-policy.md](../architecture-policy.md). [concept-to-implementation-map-v1.md](../ui-ux/concept-to-implementation-map-v1.md) §6 — мультиоконность и `MfdHostWindow` (актуальная привязка к файлам). **2026-04-11** — раздел [«Состояние реализации»](#adr0017-implementation-status): синхронизация ADR с кодом (топология, плейсмент, персистентность bounds).
327
328---
329
330## История изменений
331
332<a id="adr0017-history"></a>
333
334| Дата | Изменение |
335|------|-----------|
336| 2026-04-08 | [0021](0021-pfd-mfd-cockpit-attention-model.md), [0010](0010-ui-modes-toml-configuration.md); отклонённая мотивация «запрет перетаскивания ОС». |
337| 2026-04-11 | **[доли зон внутри одного дисплея](#adr0017-zone-weights)** (опциональные коэффициенты у якорей; между группами `(…) (…)` весов нет). |
338| 2026-04-11 | **[п. 5 доп. primary vs Forward](#adr0017-p5-primary-vs-forward)**. |
339| 2026-04-11 | **[п. 8](#adr0017-p8-mfd-host-wide):** `MfdHostWindow` — только полный `MfdShellView` (все страницы); узкий одностраничный хост **не** делаем. |
340| 2026-04-11 | **`(PFD+Forward) (MFD)` и аналоги:** второй экран = полный MFD. |
341| 2026-04-11 | **`presentation`** / **`zone_screen_layout`:** прежде всего **`settings.toml`** ([0028](0028-user-settings-toml-localappdata-and-secrets.md)), не командный репо — см. [§ слой хранения](#adr0017-presentation-grammar), [п. 4](#adr0017-p4), [0010](0010-ui-modes-toml-configuration.md). |
342| 2026-04-11 | **`zone_separator` строгий:** парсер не подставляет **`\|`** вместо **`+`** и наоборот — см. подраздел «Несколько мониторов», [EBNF](#adr0017-presentation-ebnf). |
343| 2026-04-11 | **без отдельных `*_zone_alias`:** один литерал на зону — только **`pfd_zone_identifier`** / **`forward_zone_identifier`** / **`mfd_zone_identifier`**; короткие имена — тем же ключом (например **`pfd_zone_identifier = "P"`**). |
344| 2026-04-11 | **грамматика `presentation`:** EBNF в [§](#adr0017-presentation-ebnf); ключи TOML — [§](#adr0017-presentation-grammar). |
345| 2026-04-11 | **идеальная мультимониторная раскладка:** **`(PFD) (Forward) (MFD)`** (три дисплея); метафора **боковых зон кокпита** (лобовое + боковые поля одним взглядом); компромисс «два монитора»; окно-хост зоны Mfd (`MfdHostWindow`), роль в MCP `mfd_host`; **три `TopLevel` на одном мониторе** — не цель по умолчанию; **v1 / `MfdShellView`** уточнён по коду (`MainWindow.axaml`, `SecondaryShellPage`); MCP `windows[]` ([п. 7](#adr0017-p7)); подтверждения агента ([0030](0030-command-ids-hotkeys-and-ui-registry-layers.md)); **пресеты и второй дисплей:** якорь направлений — **экран forward (лобовое)**, не primary ОС; соседство `left \| right \| up \| down`; дисплеи — **кроссплатформенно** (.NET + Avalonia: Windows, Linux, macOS); лобовое на нескольких дисплеях — вне v1; **tie-break** (несколько соседей в одном направлении) — **на усмотрение пользователя**, не автоэвристика; |
346| 2026-04-11 | **литералы якорей в TOML** (`pfd_zone_identifier`, …) — [§ таблица](#adr0017-presentation-grammar), [EBNF](#adr0017-presentation-ebnf). |
347| 2026-04-11 | **обозначения дисплеев:** разделитель якорей внутри скобок — литерал **`zone_separator`** (**`Z`**, по умолчанию **`+`**); в прозе **`+`** и **`\|`** иллюстрируют один смысл, в строке `presentation` — только выбранный **`Z`**; несколько пар `(…) (…) (…)` — несколько дисплеев — см. «Несколько мониторов»; в TOML — пояснение в `#` без обязательных отсылок к ADR; продуктовая документация — [architecture-policy.md](../architecture-policy.md). |
348| 2026-04-11 | **режимы UI (Power и др.):** не в scope первой поставки второго `TopLevel`; переработка режимов — отдельно; вопрос Flight vs Power для мультиоконности снят до той линии. |
349| 2026-04-11 | **статус:** разделение «уже реализовано» / «дорожная карта» (парсер и TOML — не backlog). |
350| 2026-04-11 | **терминология:** зона внимания **Mfd** vs **страницы** `SecondaryShellPage` во `MfdShellView` (чат — страница, не зона). |
351| 2026-04-11 | **токены грамматики** в **`settings.toml`** — секция **`[presentation_grammar]`** (модель **`CascadeIdeSettings.PresentationGrammar`**). |
352| 2026-04-11 | статус **Accepted**; навигатор [architecture-policy.md](../architecture-policy.md). |
353| 2026-04-16 | [skia-surfaces-vs-overlays-v1.md](../design/skia-surfaces-vs-overlays-v1.md) (Skia-поверхности vs оверлеи, отказ от зашитых сплитов в пользу зон презентации); [п. 8](#adr0017-p8-mfd-host-wide) уточнён: обозреватель решения — страница вторичного контура, не отдельный сплит в колонке Mfd `MainWindow`. |
354| 2026-04-17 | **Канон числа `TopLevel`:** `(P+F)(M)` ⇒ два окна; `(P)(F)(M)` ⇒ три; слева направо = порядок групп в строке; плейсмент хостов — только `PresentationHostWindowPlacement`. |
355| 2026-04-18 | [§ `display.screens` / topology](#adr0017-display-screens-topology-naming); согласование с [0063](0063-instrument-deck-named-composition-one-anchor.md). |
View only · write via MCP/CIDE