ADR 0017: Несколько окон приложения (мультиоконность), зоны экрана и поверхности агента¶
Статус: Accepted · Implemented
Дата: 2026-04-05
Обновлено: 2026-04-18 — канон числа TopLevel и display.screens / topology; подробности — § История.
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0010 | режимы и TOML поверх каркаса окна; топология презентации зон — отдельный подраздел в 0010, не смешивать с attention_zone_panels |
| 0012 | плавающий хром; отдельные окна как одна из механик |
| 0021 | семантика зон PFD / лобовое / MFD — не синоним числа окон |
| 0025 | зоны в SDK |
| 0016 | внешний агент по ACP — ортогонально тому, в скольких окнах показывается встроенный UI |
| 0030 | идентификаторы команд, Hotkeys/hotkeys.toml + пользовательский оверлей, палитра |
| 0031 | пакеты уточнений в чате, не путать с PFD-подтверждениями |
| 0002 | паритет MCP |
| 0008 | контракты MCP |
| 0063 | ось instrument deck и слово presentation vs CDS — см. § там; ключи топологии — ниже в этом ADR |
| 0120 | primary_work_surface — Intercom или Editor в Forward (ортогонально presentation) |
Вне ADR¶
| Документ | Роль |
|---|---|
attention-zone-panel-playbook-v1.md |
зона ↔ панель ↔ топология; в коде — AttentionLayoutSurfaceKind |
concept-pfd-mfd-cascade-v1.md |
UX-концепт PFD/MFD (superseded текстом 0021) |
concept-to-implementation-map-v1.md |
карта концепт → код |
| skia-surfaces-vs-overlays-v1.md | Skia-поверхности vs оверлеи |
Резюме¶
- Несколько
TopLevel— продуктовая модель разнесения зон по мониторам; семантика зон (0021) не равна числу окон. - Источник истины раскладки — строка
presentation/zone_screen_layoutвsettings.toml(0028), парсер +[presentation_grammar]. (P+F)(M)→ два окна;(P)(F)(M)→ три; порядок групп в строке = слева направо на экранах.MfdHostWindow/PfdHostWindow— полный shell зоны; плейсмент только черезPresentationHostWindowPlacement.- MCP
ide_get_ui_layoutуже отдаёт все окна; roadmap — EICAS на втором экране, fallbacks, подтверждения агента («Открытые вопросы»).
Состояние реализации (актуально по репозиторию)¶
Ниже — что уже есть в коде, чтобы не путать с продуктовым backlog из «Открытых вопросов».
Уже реализовано¶
| Область | Где в коде / что делает |
|---|---|
Разбор строки presentation |
Services/Presentation/PresentationParser.cs, PresentationInnerEtoGrammar (Eto.Parse), PresentationGrammarTokens, PresentationLayoutAnalyzer, PresentationAnchorKind, PresentationAnchorSlot (веса); тесты CascadeIDE.Tests/PresentationParserTests.cs. Веса зон внутри группы — §. Колонки главного окна: PresentationMainGridColumnDefinitions.Get → MainGridColumnDefinitions у MainWindowViewModel, применение в MainWindow.PresentationLayout.axaml.cs (ColumnDefinitions.Parse — Avalonia не биндит строку к ColumnDefinitions из VM); хвост MFD при двухякорном пресете — 340 или 0 при скрытии колонки под хост (реализация). |
| Топология ↔ дисплеи | PresentationMonitorTopology.OrderScreensForPresentation — сортировка Screen слева направо, затем сверху вниз (общая сетка координат; не primary ОС как якорь). |
Плейсмент окон-хостов (MfdHostWindow, PfdHostWindow) |
Services/PresentationHostWindowPlacement — целевой экран по индексу из пресета; иначе первый «не главный» экран; при сохранённых bounds — восстановление с прижатием к WorkingArea. На выделенном мониторе по умолчанию WindowState.Maximized; отключение — [display] maximize_presentation_host_windows_on_dedicated_screens (DisplaySettings.MaximizePresentationHostWindowsOnDedicatedScreens). |
| Инвариант плейсмента | Позиционирование и восстановление геометрии PfdHostWindow / MfdHostWindow — только через PresentationHostWindowPlacement (и общий жизненный цикл во MainWindow.PresentationHostWindows.axaml.cs); не плодить параллельную логику экрана / WorkingArea в других Views без расширения этого сервиса. |
| Персистентность геометрии хостов | CascadeIdeSettings / [display]: для MFD — MfdHostWindowPixelX / PixelY / Width / Height; для PFD — PfdHostWindowPixelX / PixelY / Width / Height; запись при Closing соответствующего окна (Views/MainWindow.PresentationHostWindows.axaml.cs). |
| Окно-хост MFD | Views/MfdHostWindow.axaml — полный MfdShellView (п. 8), тот же DataContext, что у MainWindow; слой подсветки MCP как у главного окна. Колонка под якорем Mfd в MainWindow — один MfdShellView (страницы: чат, терминал, обозреватель решения и т.д.), без отдельного зашитого сплита «дерево + shell»; второй TopLevel повторяет тот же вторичный контур (skia-surfaces-vs-overlays-v1.md), не обязательно пиксель-в-пиксель по ширине. |
| Жизненный цикл | Нет отдельного пункта меню «второе окно» — источник истины строка presentation / zone_screen_layout; автозапуск при Loaded (см. MainWindow.PresentationHostWindows.axaml.cs): при подходящей топологии && OpenPfdHostWindowOnStartup / OpenMfdHostWindowOnStartup && достаточно мониторов — открываются PfdHostWindow и/или MfdHostWindow; опционально MCP toggle_mfd_host_window при той же топологии (CanExecute). При открытом хосте соответствующая колонка в MainWindow скрывается (SetPfdHostWindowShellOpen / SetMfdHostWindowShellOpen). |
| Топология внимания | AttentionLayoutSurfaceKind: при хостах — MainWindowPlusMfdHostTopLevel, MainWindowPlusPfdHostTopLevel, MainWindowPlusPfdMfdHostTopLevel (MainWindowViewModel.Presentation.cs). |
| MCP / снимки | UiLayoutSnapshot, роли mfd_host / pfd_host для соответствующих окон; ide_get_ui_layout по всем top-level. |
Частично или вне текущего кода (остаётся roadmap)¶
- Пресет с тремя группами
(…) (…) (…)(любой порядок якорей, напр.(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. - Fallback при исчезновении монитора / сильной смене раскладки ОС: есть clamp к рабочим областям; отдельный UX «монитор недоступен» не описан здесь как обязательный.
- Закрытие главного окна: при сбросе
DataContextуMainWindowвызывается закрытие хоста (CloseMfdHostWindowIfOpen); явная подписка наMainWindow.Closingдля каскада — не зафиксирована в этом списке (зависит от жизненного цикла приложения). - EICAS / IDE Health на втором экране, плотность чата/терминала — продуктовые открытые пункты («Открытые вопросы»).
- Подтверждения агента при двух
TopLevel— направление в п. 6, детали реализации — открыты.
Контекст¶
Сейчас одно главное окно (MainWindow) задаёт весь видимый кокпит: редактор, дерево, чат, колонка Mfd и т.д. Конфигурация режимов (0010) описывает видимость и метрики поверх фиксированного каркаса; альтернативная «схема окна» целиком в данных явно отложена.
Потребность: разнести роли по экрану без обязательной ужимки всего в одну колонку — в том числе:
- Несколько физических мониторов — в идеале
(PFD) (Forward) (MFD)(три дисплея); см. подраздел «Несколько мониторов» и таблицу схем(PFD+Forward+MFD)при дефолтномZили(PFD|Forward|MFD)приzone_separator = "|"/(PFD+Forward) (MFD); или один широкий/изогнутый дисплей с одной парой скобок и тремя якорями в одном окне и явными зонами (см. концепт PFD/MFD, §6). - Вынести семантическую зону внимания на другой дисплей — в первую очередь MFD (чат, терминал, trace, тяжёлые панели агента); при необходимости PFD или компактную полосу подтверждений/статуса. Продуктовая формулировка — зона ↔ второй или третий монитор, а не абстрактное «второе окно». В коде тип
MfdHostWindow, в снимке MCP полеrole=mfd_hostдля такого второгоTopLevel. - Эксперименты с раскладкой изолированно от стабильных пресетов — режим Flight как песочница; мультиоконность логично отрабатывать там первыми (см. §5
concept-pfd-mfd-cascade-v1.md).
0012 уже допускает отдельные окна для части хрома (телеметрия, полоски). Базовый MCP под мультиоконность уже есть: ide_get_ui_layout отдаёт JSON с массивом windows (по одному дереву на каждое открытое Window, роли main / mfd_host / other); поиск контрола по имени для инспекции/действий — с приоритетом главного окна и обходом остальных (Cockpit/Surface/UiLayoutSnapshot, UiControlAppearance). Этот ADR расширяет предмет с технического «несколько корней в снимке» на продуктовую модель нескольких окон (какие поверхности, жизненный цикл, состояние, связь с режимами и агентом); точечные дополнения контракта — при появлении новых типов окон или семантик регионов (см. п. 7).
Один монитор и три семантические зоны (0021)¶
Три пространственных якоря внимания (PFD, лобовое, MFD) не требуют трёх отдельных окон. Текущая реализация — одно главное окно, колонки MainGrid, топология AttentionLayoutSurfaceKind.MainWindowDockedGrid (см. playbook).
Опциональный сценарий: разнести те же семантики по нескольким TopLevel на одном физическом мониторе (например очень широкий дисплей) — один процесс, та же карта панель→зона (AttentionZonePanelRuntime), иная только геометрия презентации. По умолчанию это не цель: достаточно главного окна + вынесенной зоны на другом мониторе и/или пресета внутри одного TopLevel (0021, мотив FancyZones). Отдельно обсуждать три связанных TopLevel на одном мониторе имеет смысл только при явной обратной связи или ультрашироких сценариях; иначе риск и сложность перевешивают выигрыш над двумя окнами или сеткой в одном окне.
Не считается аргументом за мультиоконность: «заблокировать перемещение окон на уровне ОС». Прилипание, сетка и предсказуемая раскладка достигаются в рамках одного TopLevel (сплиттеры, пресеты) или программным позиционированием без обещания запрета средствами оболочки; см. 0022 (второй монитор как геометрия одного окна — ортогонально числу окон).
Несколько мониторов¶
Обозначения (чтобы не смешивать «зоны на одном экране» и «число дисплеев»):
( … )— один физический дисплей (одна область в конфигурации мониторов ОС).- Разделитель якорей внутри одной пары скобок — ровно литерал
Zизzone_separator(по умолчанию+). Несколько семантических якорей (PFD, Forward, MFD) на этом же дисплее (как колонки в одномMainWindow) записываются какanchorZanchorZanchor. В прозе этого ADR+и|— две привычные иллюстрации одного смысла; в строкеpresentationприZ = "+"допустимы только+, приZ = "|"— только|(без подстановки «синонима» в парсере). - Краткая запись — задаётся тем же ключом
pfd_zone_identifier/forward_zone_identifier/mfd_zone_identifier, например"P","F","M"; в строкеpresentationдопустим только выбранный литерал (без второго «синонима» в TOML). - Несколько подряд
(…) (…) (…)— разные дисплеи; слева направо в тексте — удобная ориентировка, фактический порядок экранов задаёт геометрия и пресет. Три дисплея под три якоря — только три пары скобок, например(PFD) (Forward) (MFD). Запись без скобок вроде гологоPFD | Forward | MFDдвусмысленна — в конфиге и доках не использовать; символ между якорями внутри скобок — этоZ, не разделитель экранов.
Ёмкость нотации и направление имён конфига (display.screens / topology)¶
Намеренная «ёмкость»: одна и та же строка топологии (presentation в корне settings.toml, в будущем — ключ topology внутри таблицы ниже) намеренно смешивает в одном слое (а) сколько групп дисплеев в пресете (несколько (…) (…) (…) — см. § «Несколько мониторов») и (б) как эти группы соотносятся друг с другом и с якорями (пространственный порядок в тексте, семантика зон внутри группы). Развести «число дисплеев» и «описание топологии» отдельными ключами можно было бы явнее; цена — длиннее и многословнее. Текущая нотация платит за краткость тем, что смыслы (а) и (б) не разводятся без контекста грамматики и примеров (§ грамматика, EBNF).
Направление переименования TOML (снять путаницу слова presentation с «презентацией» UI и с другими слоями — см. 0063 § CDS): канон — вложить описание топологии в уже смысловой [display] (0050, Display в модели):
- Таблица уровня «вся топология в одном месте» (сегодня корневые ключи
presentation/zone_screen_layoutи связанные поля — см. код) →[display.screens](группы экранов пресета, не «презентация» в бытовом смысле). - Строка EBNF / размещения якорей — ключ
topology(явное имя роли). - Грамматика: секция
[presentation_grammar]→[display.screens.grammar](или согласованное вложение под тем же префиксом; имяpresentation_grammarв коде —CascadeIdeSettings.PresentationGrammar).
Что такое screen в имени display.screens: в разговорном английском screen часто читается как «монитор ОС». Здесь screen — в том же духе, что screen_markers в таблице токенов: группа в строке, граница одной презентационной поверхности пресета, кокпитная метафора PFD/MFD. Семантика «одна пара ( … ) ↔ один физический дисплей в конфигурации ОС» зафиксирована выше; будущее имя таблицы не меняет парсер и семантику скобок без явной миграции кода.
Почему не display.layout: слово layout уже перегружено — CockpitPresentationLayout, 0046, плюс раскладка внутри якоря (instrument deck, 0063). Отдельная таблица [display.layout] с ключом topology была бы читаемее без «экранов», но риск смешения с внутренней раскладкой региона выше, чем у display.screens после абзаца выше. Если продукт выберет layout, в доке и комментарии к TOML зафиксировать: только многооконная / многоякорная топология пресета, не deck и не внутренняя сетка региона.
Миграция: читать старые ключи как legacy N релизов; при конфликте старого и нового — приоритет в реализации (новый канон побеждает при наличии); обновить примеры settings.toml при внедрении. Немедленная смена ключей в коде не требуется этим разделом — только зафиксированное направление; детали сроков — в задачах на загрузчик TOML.
Где хранить и зачем не только workspace.toml: раскладка по физическим дисплеям — личная история (у членов команды разное железо). Репозиторный .cascade/workspace.toml (0021 §2.1, 0028) — про соглашение команды по панелям и зонам в общей модели внимания, не про обязательную для всех схему «три монитора». В settings.toml в корне задаются строки presentation и/или zone_screen_layout; опциональные токены грамматики (screen_markers, screen_separator, zone_separator, литералы якорей — см. таблицу ниже) — в секции [presentation_grammar]. Всё это задаёт топологию именно под железо пользователя — прежде всего в settings.toml (%LocalAppData%\CascadeIDE\, 0028); при merge с бандлом UiModes/ и overlay репо эти ключи берутся из пользовательского слоя и перекрывают одноимённые, если они когда-либо появятся в шипнутом или репозиторном workspace.toml (дефолт продукта, не «решение команды о мониторах»).
Грамматика строки presentation (настраивается в TOML): в том же файле, что и presentation / zone_screen_layout, в секции [presentation_grammar] задаются опциональные ключи — какие символы считать маркерами экрана, разделителем экранов, разделителем якорей и какими строками обозначать три зоны (длинные или короткие — один ключ на зону). Значения на усмотрение пользователя; ниже — дефолты, с которыми совпадают примеры по всему этому ADR:
Ключ в TOML (внутри [presentation_grammar]) |
Дефолт | Смысл |
|---|---|---|
screen_markers |
"()" |
строка из двух символов: открывающий и закрывающий границу одного дисплея |
screen_separator |
" " |
разделитель между группами (дисплеями), обычно пробел между ) и следующим ( |
zone_separator |
"+" |
разделитель якорей внутри одной пары маркеров; в строке presentation между якорями допускается только этот литерал (не другой символ «для красоты») |
pfd_zone_identifier |
"PFD" |
литерал якоря PFD в строке presentation (регистр и написание — как задано) |
forward_zone_identifier |
"Forward" |
литерал якоря лобового экрана |
mfd_zone_identifier |
"MFD" |
литерал якоря MFD |
Три литерала должны быть попарно различимы (сравнение без учёта регистра). При коллизии реализация сбрасывает все три идентификатора на дефолты из таблицы.
Идентификаторы якорей. Пользователь может задать, например, forward_zone_identifier = "Lob" и писать (PFD+Lob+MFD). Короткие буквы P / F / M — тем же ключом: pfd_zone_identifier = "P" и т.д.; тогда в строке только (P+F+M), без отдельных ключей «алиас».
Примеры при дефолтных идентификаторах (PFD, Forward, MFD): в строке — полные литералы, например (PFD+Forward+MFD), (PFD) (Forward) (MFD). Примеры с одной буквой на якорь — при явной настройке [presentation_grammar] с короткими pfd_zone_identifier / forward_zone_identifier / mfd_zone_identifier.
Грамматика (EBNF). Ниже — однозначное описание; литералы якорей берутся из ключей TOML (см. таблицу). Пробелы внутри пары screen_markers между якорями не допускаются (только zone_sep).
(*--- Параметры из TOML: O, C, Z, S; идентификаторы якорей PfdId, ForwardId, MfdId (дефолты — см. таблицу) ---*)
presentation ::= [ SP ] screen { SP screen } [ SP ]
screen ::= "(" anchor { zone_sep anchor } ")"
zone_sep ::= Z
anchor ::= pfd_zone_identifier | forward_zone_identifier | mfd_zone_identifier
pfd_zone_identifier ::= (* литерал из ключа pfd_zone_identifier *)
forward_zone_identifier ::= (* литерал из ключа forward_zone_identifier *)
mfd_zone_identifier ::= (* литерал из ключа mfd_zone_identifier *)
(* после разбора: семантика PFD / лобовое / MFD — [0021](0021-pfd-mfd-cockpit-attention-model.md) *)
(* лексер: три строки якоря; совпадение в порядке убывания длины литерала; для литерала длины 1 — без учёта регистра *)
SP ::= U+0020 { U+0020 } (* один или более пробелов — разделитель экранов при дефолте *)
Параметризация из TOML: пусть O и C — первый и второй символ строки screen_markers, Z — строка zone_separator, S — строка screen_separator, литералы якорей — значения ключей pfd_zone_identifier, forward_zone_identifier, mfd_zone_identifier. Тогда:
presentation ::= [ S ] screen { S screen } [ S ]
screen ::= O anchor { Z anchor } C
(* anchor — как выше; токены якорей из TOML *)
Между якорями в одном экране допускается только литерал Z из TOML (при дефолте — +). Парсер не подставляет |, + или другой символ вместо выбранного Z. Строка presentation должна использовать те же литералы, что и выбранные токены. Парсер читает сначала ключи грамматики из [presentation_grammar] (или дефолты), затем применяет соответствующую продукцию. В коде — свойство CascadeIdeSettings.PresentationGrammar; merge с бандлом — в реализации (0010, 0028).
В конфиге (например TOML): то же компактное значение можно сопроводить обычным комментарием # … — кратко пояснить для себя якоря и число дисплеев; отдельные отсылки к ADR в пользовательском файле для этого не обязательны.
Продуктовый слой (вне этого ADR): где и как давать пользователю развёрнутые объяснения (внешняя документация, справка в IDE, приоритеты) — решение уровня продукта, не ADR; см. architecture-policy.md.
Три типовых раскладки по дисплеям:
| Схема | Смысл |
|---|---|
(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). |
(PFD+Forward) (MFD) …; (P+F) (M) — при тех же коротких идентификаторах в TOML |
Два TopLevel: главное окно — PFD и лобовое вместе; второе окно — зона MFD (MfdHostWindow, вторичный контур). Типичный компромисс «два монитора». |
(PFD) (Forward) (MFD) …; (P) (F) (M) — при коротких идентификаторах |
Три TopLevel: по одной зоне на окно (PFD; лобовое; MFD). Идеал при трёх мониторах (0021); см. расхождение с v1 в «Состояние реализации». |
Канон числа окон: (P+F)(M) ⇔ два верхнеуровневых окна (главное держит P+F, M вынесен); (P)(F)(M) ⇔ три верхнеуровневых окна (P, F, M раздельно). Это семантика строки presentation, не «одно окно на три экрана ОС» и не три окна на одном мониторе как цель по умолчанию.
Пространственный канон (три дисплея, типичный ряд): слева направо на экранах (в порядке PresentationMonitorTopology.OrderScreensForPresentation: слева направо, затем сверху вниз) идут группы (…) (…) (…) в том же порядке, в каком они записаны в строке presentation. Первая группа — левый экран в этом порядке, вторая — средний, третья — правый. Примеры: (P) (F) (M) — слева PFD, по центру Forward, справа MFD; (M) (F) (P) — слева MFD, по центру Forward, справа PFD. Фиксированной привязки «P всегда слева» нет — задаёт только порядок скобок. Если физическая конфигурация мониторов не в один ряд, пользователь сопоставляет i-ю группу i-му экрану в выбранном порядке (или подгоняет раскладку ОС).
Доли зон на одном дисплее (опциональные веса у якорей)¶
Принято: внутри одной пары 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 на весь дисплей, отдельные веса не задаются (и между группами (…) (…) весов нет: граница между мониторами — стол/геометрия ОС, не доля в строке).
Инвариант: коэффициенты меняют только доли внутри одной группы (одного экрана). Топология — сколько групп (…), какие якоря на каком экране, сколько физических дисплеев, и продуктовые следствия вроде максимизации главного окна при старте — задаются составом якорей и скобок, а не числами; подбор 0.25 vs 0.5 не переключает «два монитора на три» и не меняет семантику «на первом экране есть P и F».
Когда веса не указываются — поведение как сейчас: равные доли между якорями в группе или существующий дефолт сетки (реализация).
Внутри одной группы с двумя и более якорями: либо у каждого якоря задан коэффициент и сумма = 1, либо ни у одного (равные доли). Смешение вроде (0.25P+F+M) — ошибка разбора (неоднозначно).
Одна зона в скобках — только литерал якоря, без коэффициента (или эквивалентно один якорь на весь экран).
Формат числа: десятичная точка . (как в TOML/JSON); локаль с запятой в строке presentation не поддерживается, чтобы не дублировать неоднозначность.
Связь с парсером: PresentationParser разбирает опциональные префиксы весов; внутри одной пары screen_markers структура списка якорей задаётся грамматикой Eto.Parse (PresentationInnerEtoGrammar, пакет NuGet Eto.Parse). Результат — PresentationParseResult.Screens как списки PresentationAnchorSlot (Kind + Weight?). Строки без коэффициентов по-прежнему каноничны (Weight == null на всех якорях группы).
Ниже — расширение EBNF относительно базовой продукции; при желании объединить с основным блоком EBNF выше в одну продукцию (документация).
(*--- Дополнение: веса только внутри screen с двумя и более якорями; сумма weight = 1 ---*)
weight ::= (* положительный литерал: целое или десятичное, десятичная точка U+002E *)
weighted_anchor ::= weight anchor | anchor
screen ::= O weighted_anchor { Z weighted_anchor } C
(* семантика: в одном screen либо каждый weighted_anchor — это «weight anchor», либо каждый — просто «anchor»; смешение запрещено; при весах sum=1 *)
(* Базовая форма без весов — как сейчас: только anchor *)
Примеры (короткие идентификаторы P, F, M в [presentation_grammar]): (0.25P+0.75F)(M); (0.2P+0.3F+0.5M); (P+F+M) — без весов, три равные доли.
Вложенные оси v и h (T-layout; пояснение для агентов и обзорной документации)¶
Иногда раскладку на одном физическом экране описывают формулой с явными осями, например:
0.3vPFD + 0.7v(0.8hForward + 0.2hMFD)
Расшифровка (смысл для человека и для LLM):
- Сначала делим экран вертикально (
v): 30% ширины слева — зона PFD (контекст workspace, дерево связей, первичные индикаторы в модели кокпита). - Оставшиеся 70% справа — отдельная вертикальная «полоса»; внутри неё делим горизонтально (
h): верхние 80% — Forward (лобовое стекло: редактор, объект работы), нижние 20% — MFD (телеметрия, вторичный контур, настройки «под рукой»).
Это классический T-layout кокпита: главное (код и статус «полёта») — в центре поля зрения, вспомогательное — внизу. Без указания v / h и порядка вложенности анализатор или агент не могут однозначно сопоставить доли с RowDefinitions / ColumnDefinitions в Avalonia: сначала нужно знать, какой сплит идёт первым (здесь — вертикальный 0.3/0.7), затем — как делится правая часть по строкам (0.8/0.2).
Связь с текущей строкой presentation (реализация v1): парсер и веса в § «Доли зон» сейчас задают одну ось — типично три колонки MainGrid (PFD | Forward | MFD) с долями по ширине. Вложенная запись v / h в этом ADR — целевая семантика для будущего расширения грамматики и/или отдельного слоя композиции (см. 0036); до появления парсера с вложенными группами её нельзя считать эквивалентом одной плоской суммы (0.3P+0.7F+…) без отдельной спецификации.
Предохранитель (политика стабильности геометрии): если веса и оси заданы явно в конфиге, продукт не должен динамически пересчитывать пропорции в рантайме «по удобству» (изменение размера окна не должно подменять доли из настроек).
- Анализатор (разбор / валидация конфигурации): в каждой группе вложенности сумма коэффициентов на этой оси = 1 (как уже для плоского экрана с весами у якорей).
- Surface compositor (конвейер 0036): при старте сессии жёстко фиксирует вычисленные из конфига нормализованные доли (и при необходимости пиксельные границы после измерения), без последующего «плавающего» пересчёта долей при обычном ресайзе; исключения (если появятся) — отдельное явное правило продукта, не молчаливое изменение коэффициентов.
Идеал (три физических дисплея): (PFD) (Forward) (MFD) — целевая кокпитная раскладка при достаточном железе: контекст workspace / обозреватель и первичные индикаторы на первом экране, объект работы (редактор) на втором, вторичные тяжёлые панели (чат, терминал, trace…) на третьем. По канону пресет с тремя группами (…) (…) (…) означает три отдельных TopLevel (см. таблицу «Три типовых раскладки» выше), а не одно окно на три монитора без разбиения зон.
Метафора внимания (как в кокпите): помимо лобового поля зрения есть боковые зоны — их можно подхватить одним взглядом (периферийное внимание), не отрываясь от работы «вперёд» надолго. PFD и MFD в этом смысле — боковые относительно лобового (Forward), а не «второстепенные экраны»; мультиоконность и якорь направлений от экрана лобового согласуются с этой моделью.
Типичный компромисс (два дисплея): (PFD+Forward) (MFD) при дефолтном Z, либо (PFD|Forward) (MFD) при zone_separator = "|" — первый монитор: PFD и лобовое вместе (как сейчас в одном MainGrid); второй — зона MFD. Удобно и распространённо; от идеала «три экрана» отличается объединением PFD и лобового на одном дисплее.
Матрица инстансов SkiaHost (зафиксировано для v1)¶
SkiaHost считается на слот, а не «один на всё окно». Общее число рабочих поверхностей в типовых топологиях — три (PFD / Forward / MFD), меняется только распределение между TopLevel.
Топология (формула presentation) |
MainWindow |
MfdHostWindow |
Итого |
|---|---|---|---|
(PFD+Forward+MFD) |
SkiaHost(PFD), SkiaHost(Forward), SkiaHost(MFD) |
— | 3 слота / 1 TopLevel |
(PFD+Forward) (MFD) / (P+F) (M) |
SkiaHost(PFD), SkiaHost(Forward) |
SkiaHost(MFD) |
3 слота / 2 TopLevel (канон) |
(PFD) (Forward) (MFD) / (P) (F) (M) |
по канону — только один якорь на окно (отдельные хосты PFD и Forward — roadmap) | SkiaHost(MFD) |
3 слота / 3 TopLevel (канон); v1: как строка выше, т.к. P+F в MainWindow |
Следствие: дополнительный «агрегирующий» SkiaHost на весь MainWindow не требуется; базовая геометрия задаётся presentation/MainGridColumnDefinitions, а поверхности остаются слот-ориентированными.
Четвёртый и далее дисплей — опционально (например полноэкранная телеметрия, второй контур Mfd, внешний браузер доков — по пресету и политике продукта).
Не единственная возможная схема; пресеты задают соответствия дисплей↔зона. Канон числа TopLevel для строки presentation — в таблице «Три типовых раскладки» и в матрице SkiaHost выше; факт кода для тройной группы — в «Состояние реализации». Снимок MCP для нескольких окон уже поддерживается (п. 7), при новых сценариях — уточнение полей/ролей в той же поставке.
Решение (принципы; детализация — по итогам обсуждения)¶
1. Один процесс, несколько TopLevel. Окна принадлежат одному экземпляру IDE; общее состояние решения, агента, настроек — единый граф VM/сервисов за фасадом, без второго «независимого» экземпляра приложения. Исключения (если когда-нибудь понадобятся) — отдельное решение и отдельный ADR.
2. Главный фрейм. Сохраняется понятие основного окна (редактор, решение, главная навигация по умолчанию). Дополнительные TopLevel — это в первую очередь носители вынесенных зон (см. контекст: MFD/PFD на втором или третьем мониторе); они не обязаны дублировать полный кокпит. Состав и привязка к дисплею — пресет режима и/или явное действие пользователя (например «вынести MFD» / «вынести страницу чата» (как часть вторичного контура в зоне Mfd)). Роль mfd_host в снимке MCP относится к окну-хосту зоны Mfd и не ограничивает продукт только «чатом». Закрытие главного окна — политика выхода из приложения (закрыть все дочерние или запрос подтверждения) — фиксируется в реализации, не оставлять неявным.
3. Что может жить во втором и следующих окнах (кандидаты, не обязательно всё в v1): прежде всего регион зоны Mfd на отдельном физическом дисплее — в терминах 0021 это семантика вторичного внимания; в коде тот же хост MfdShellView, внутри которого переключаются страницы SecondaryShellPage (чат, терминал, сборка, …) — страница не является «зоной», зона — Mfd как якорь внимания; при необходимости PFD или компактная полоса подтверждений/критичных индикаторов на другом мониторе; выносимые фрагменты хрома в смысле 0012. Документы редактора как floating MDI остаются вне объёма этого ADR, пока не будет отдельного решения (как в 0012).
4. Связь с режимами UI (0010). Пресеты задают видимость и слоты поверх доступных поверхностей: одно окно или несколько. Топология презентации зон (AttentionLayoutSurfaceKind: одно окно / несколько TopLevel и т.д.) согласуется с merge слоёв 0010 и отделением от карты панель→зона — см. подраздел «Топология презентации зон» в 0010. Строка раскладки по дисплеям — presentation (короткое имя) или zone_screen_layout; одно из двух, не оба. Значение — литерал из «Несколько мониторов» (например (PFD+Forward) (MFD)); рядом опционально токены грамматики из таблицы (screen_markers, screen_separator, zone_separator, литералы якорей) (EBNF). Хранение: прежде всего settings.toml пользователя (§ выше, 0028), чтобы не смешивать личную схему мониторов с командным репозиторным workspace.toml. Расширение: ключи уровня «какие панели во вынесенной зоне по умолчанию» — в TOML режима / Flight при необходимости. Валидация и enum — в реализации.
5. Сохранение геометрии. Позиции и размеры окон вынесенных зон (в т.ч. на 2-м/3-м мониторе) — не обратная запись в шипнутые UiModes/*.toml (правило 0010). В коде v1: геометрия MfdHostWindow персистится в пользовательском settings.toml (MfdHostWindowPixelX / PixelY / Width / Height — см. «Состояние реализации»); иные окна / полный snapshot workspace — при появлении отдельной договорённости.
Пресеты и выбор дисплея для вынесенной зоны (принятое направление): помимо сырого идентификатора дисплея ОС и сохранённых прямоугольников допускается семантика, естественная пользователю — направление соседства относительно экрана, на котором в текущей раскладке представлена зона лобового (forward) (0021), а не от primary монитора ОС по умолчанию. Кроссплатформенно: целевой стек — .NET + Avalonia; перечисление дисплеев и их границы в общей координатной сетке (по ним вычисляется сосед в духе left | right | up | down) — через API графической платформы на Windows, Linux и macOS; это не привязка к одной ОС. Различия между ОС (имена дисплеев, hotplug, Wayland vs X11 и т.д.) — в реализации и тестах, не в продуктовой модели якоря forward. Имена ключей и схема в данных — в той же поставке, что и мультиоконность / 0010. Tie-break: если в выбранном направлении несколько соседних дисплеев, не навязывать автоматический выбор «лучшего» — на усмотрение пользователя (явный выбор экрана в UI, уточнение пресета, либо переход на сырой идентификатор/сохранённый прямоугольник). Лобовое, разнесённое на несколько физических дисплеев — редкий случай; для v1 достаточно одного экрана якоря для forward; обобщение — при явной потребности. При смене конфигурации мониторов — fallback (сохранённая геометрия, возврат в главное окно и т.д.) остаётся предметом реализации.
Основной дисплей ОС и якорь Forward (принято): системное понятие «основной / primary» монитора (в Windows — «сделать основным дисплеем») не отождествляется с семантикой лобового экрана (Forward) в модели кокпита. Primary нужен ОС и драйверам (расположение панели задач, масштаб и т.п.); типичный конфликт — сенсорный монитор, который должен быть основным из‑за калибровки касания, хотя в рабочей раскладке пользователя лобовым по смыслу является другой дисплей. Считать Forward по умолчанию равным primary опасно — ломает такие конфигурации. Согласование физической расстановки мониторов, их порядка в настройках ОС и строки presentation — ответственность пользователя; продукт не подменяет расстановку стола и не выводит семантику якорей только из флага primary. Реализация сопоставляет группы (…) (…) … с дисплеями по принятой схеме (геометрия рабочих областей и порядок в строке — см. код), без тождества «первый в presentation = primary ОС».
6. Агент (ACP, 0016). Транспорт к внешнему агенту не зависит от числа окон: один канал stdio/сессия. Решение этого ADR — только куда монтируется встроенный UI ответов и подтверждений (главное окно vs дополнительное). Краткие подтверждения и критичные сигналы по-прежнему должны быть доступны в PFD-зоне внимания (концепт §3); при втором окне — либо дубль компактной полосы, либо явное правило «фокус на главном окне для подтверждения».
Подтверждения при нескольких TopLevel (принятое направление): не опираться только на системную модалку поверх «не того» окна. Показ запроса — в потоке внимания PFD (баннер/строка; презентация может быть без классической модалки на весь экран). Опционально вторичный канал внимания (звук, мигание в области уведомлений ОС), если фокус на MFD/втором окне. Ответ пользователя — те же семантики, что и у кнопок: палитра команд и жесты из 0030 — шипнутый Hotkeys/hotkeys.toml и пользовательский оверлей %LocalAppData%\CascadeIDE\hotkeys.toml (0028). До отдельного решения по протоколу: один вызов request_confirmation (и аналоги) по-прежнему завершается ok/cancel после ответа; «неблокирующее» относится к UX презентации, не к обязательной смене модели MCP.
7. MCP и паритет (0002, 0008, 0012). Уже реализовано: снимок UI для агента/тестов не ограничен одним MainWindow — ide_get_ui_layout строит windows[] по всем открытым Window процесса; действия по имени контрола учитывают окна помимо главного (см. контекст выше). При появлении новых продуктовых окон или новой семантики (например выделенный второй TopLevel под MFD с отдельной таксономией зон) точечно расширять контракт в той же поставке: новые значения role, поля идентификации региона/поверхности, тесты паритета — по аналогии с 0012. Мультиоконные поверхности не должны оставаться «только для человека» без осознанного исключения.
8. MfdHostWindow — только полный вторичный контур. Для раскладки вроде (PFD+Forward) (MFD) / (PFD|Forward) (MFD) второй дисплей семантически несёт всю зону Mfd: одно окно MfdHostWindow с полным MfdShellView — тем же хостом страниц (SecondaryShellPage: чат, терминал, сборка, обозреватель решения, …) и тем же DataContext (MainWindowViewModel), что и вторичный контур в главном окне. Паритет здесь — совпадение семантики вторичного контура (набор страниц внутри MfdShellView), а не обязательное воспроизведение всей визуальной колонки под якорем Mfd в MainWindow: в главном окне под якорем Mfd — один MfdShellView; инструменты вторичного внимания (включая обозреватель решения) переключаются страницами, а не отдельным зашитым сплитом «дерево + shell» (0021: зона внимания ≠ страница). Отдельный TopLevel не обязан дублировать прочие панели главного окна вне семантики вторичного контура. Не делаем отдельного продукта «второе окно только с одной страницей» (узкий хост); альтернатива «только чат» отклонена как целевое состояние. Плейсмент, первичное сохранение геометрии и привязка к индексу экрана из presentation — см. «Состояние реализации»; открыты: доводка fallback при смене мониторов, продуктовый UX (п. 5).
Последствия¶
- Потребуется дизайн владения VM: общие сервисы, дочерние окна как представления или отдельные лёгкие VM с подпиской на общий сессионный слой.
- Тесты UI и сценарии, которые жёстко предполагали только дерево
MainWindowбез учётаwindows[]в MCP, потребуют обновления или явного двойного режима (одно окно / несколько окон); сам MCP-снимок по несколькимTopLevelуже поддерживается. - Документация для пользователя (позже): как вынести панель, как вернуть, где сохраняется геометрия.
Отклонённые альтернативы (как целевое состояние)¶
- Полагаться только на внешний Cursor/IDE на втором мониторе без встроенной мультиоконности — отклонено как не закрывающее сценарий «одна Cascade + зоны экрана» и PFD/MFD внутри продукта (см. концепт §8 — внешние инструменты дополняют, не заменяют осмысленную раскладку Cascade).
- Сразу вводить полный MDI документов — вне объёма; см. 0012.
- Несколько окон на одном мониторе ради «запрета перетаскивания средствами ОС» — отклонено как мотивация: такая цель слаба и закрывается раскладкой внутри одного окна (см. подраздел «Один монитор и три семантические зоны» выше).
Уточнение: режимы UI (Power, Flight и др.)¶
Принято: переработка Power, Balanced и остальных пресетов/семей в смысле 0010 — отдельная линия работ; в объёме первой поставки второго TopLevel эти режимы не трогаем; последующая переработка пресетов и семей — отдельная линия. Мультиоконность v1 не блокируется и не смешивается с согласованием «как будет в Power». Вопрос «только Flight / Power с флагом» для мультиоконности снят до отдельной дорожной карты режимов. Flight по-прежнему удобен как полигон для экспериментов (см. контекст выше), без обязательства ограничивать продукт только им.
Открытые вопросы (для обсуждения перед кодом)¶
- Три
TopLevelна одном физическом мониторе (три якоря PFD/лобовое/MFD как три рамки ОС): по умолчанию не цель — достаточно связки «главное окно + вынесенная зона на другом мониторе» (2-й/3-й дисплей), мультимонитора по 0021 §13 и оси одно окно + пресет/FancyZones в 0021. Отдельный пересмотр «три на одном» — только при явной обратной связи (например ультраширокий один дисплей). - Второй
TopLevelпод зону Mfd на отдельном мониторе — см. п. 8 и «Состояние реализации»: полныйMfdShellViewвMfdHostWindow; семантика зон и страниц — 0021. Базовый плейсмент, сохранение bounds и автозапуск — в коде. Открыто: EICAS/IDE Health при вынесении, UX чата и терминала (плотность, клавиатура), полнота fallback при смене мониторов; переработка режимов UI — см. «Уточнение: режимы UI». - Подтверждения агента (детали продукта): таймауты, деструктивные операции, нужен ли отдельный модальный слой процесса — при реализации поверх принятого направления (п. 6 выше).
- Пресеты и мониторы (детали реализации): принятый ориентир — см. п. 5 выше (якорь forward, соседство
left | right | up | down). Tie-break при нескольких соседях в одном направлении — на стороне пользователя (см. п. 5), не обязательная эвристика в продукте. Ключи раскладки по дисплеям —presentation/zone_screen_layoutи токены грамматики — персональные, см. п. 4 и § слой хранения; открытая деталь реализации: полнота fallback при несовпадении раскладки ОС.
Принятие¶
2026-04-11 — статус Accepted; ссылка из architecture-policy.md. concept-to-implementation-map-v1.md §6 — мультиоконность и MfdHostWindow (актуальная привязка к файлам). 2026-04-11 — раздел «Состояние реализации»: синхронизация ADR с кодом (топология, плейсмент, персистентность bounds).
История изменений¶
| Дата | Изменение |
|---|---|
| 2026-04-08 | 0021, 0010; отклонённая мотивация «запрет перетаскивания ОС». |
| 2026-04-11 | доли зон внутри одного дисплея (опциональные коэффициенты у якорей; между группами (…) (…) весов нет). |
| 2026-04-11 | п. 5 доп. primary vs Forward. |
| 2026-04-11 | п. 8: MfdHostWindow — только полный MfdShellView (все страницы); узкий одностраничный хост не делаем. |
| 2026-04-11 | (PFD+Forward) (MFD) и аналоги: второй экран = полный MFD. |
| 2026-04-11 | presentation / zone_screen_layout: прежде всего settings.toml (0028), не командный репо — см. § слой хранения, п. 4, 0010. |
| 2026-04-11 | zone_separator строгий: парсер не подставляет \| вместо + и наоборот — см. подраздел «Несколько мониторов», EBNF. |
| 2026-04-11 | без отдельных *_zone_alias: один литерал на зону — только pfd_zone_identifier / forward_zone_identifier / mfd_zone_identifier; короткие имена — тем же ключом (например pfd_zone_identifier = "P"). |
| 2026-04-11 | грамматика presentation: EBNF в §; ключи TOML — §. |
| 2026-04-11 | идеальная мультимониторная раскладка: (PFD) (Forward) (MFD) (три дисплея); метафора боковых зон кокпита (лобовое + боковые поля одним взглядом); компромисс «два монитора»; окно-хост зоны Mfd (MfdHostWindow), роль в MCP mfd_host; три TopLevel на одном мониторе — не цель по умолчанию; v1 / MfdShellView уточнён по коду (MainWindow.axaml, SecondaryShellPage); MCP windows[] (п. 7); подтверждения агента (0030); пресеты и второй дисплей: якорь направлений — экран forward (лобовое), не primary ОС; соседство left \| right \| up \| down; дисплеи — кроссплатформенно (.NET + Avalonia: Windows, Linux, macOS); лобовое на нескольких дисплеях — вне v1; tie-break (несколько соседей в одном направлении) — на усмотрение пользователя, не автоэвристика; |
| 2026-04-11 | литералы якорей в TOML (pfd_zone_identifier, …) — § таблица, EBNF. |
| 2026-04-11 | обозначения дисплеев: разделитель якорей внутри скобок — литерал zone_separator (Z, по умолчанию +); в прозе + и \| иллюстрируют один смысл, в строке presentation — только выбранный Z; несколько пар (…) (…) (…) — несколько дисплеев — см. «Несколько мониторов»; в TOML — пояснение в # без обязательных отсылок к ADR; продуктовая документация — architecture-policy.md. |
| 2026-04-11 | режимы UI (Power и др.): не в scope первой поставки второго TopLevel; переработка режимов — отдельно; вопрос Flight vs Power для мультиоконности снят до той линии. |
| 2026-04-11 | статус: разделение «уже реализовано» / «дорожная карта» (парсер и TOML — не backlog). |
| 2026-04-11 | терминология: зона внимания Mfd vs страницы SecondaryShellPage во MfdShellView (чат — страница, не зона). |
| 2026-04-11 | токены грамматики в settings.toml — секция [presentation_grammar] (модель CascadeIdeSettings.PresentationGrammar). |
| 2026-04-11 | статус Accepted; навигатор architecture-policy.md. |
| 2026-04-16 | skia-surfaces-vs-overlays-v1.md (Skia-поверхности vs оверлеи, отказ от зашитых сплитов в пользу зон презентации); п. 8 уточнён: обозреватель решения — страница вторичного контура, не отдельный сплит в колонке Mfd MainWindow. |
| 2026-04-17 | Канон числа TopLevel: (P+F)(M) ⇒ два окна; (P)(F)(M) ⇒ три; слева направо = порядок групп в строке; плейсмент хостов — только PresentationHostWindowPlacement. |
| 2026-04-18 | § display.screens / topology; согласование с 0063. |