| 1 | # ADR 0162: Monaco как опциональный хост Forward-редактора (WebView2, не Photino-shell) |
| 2 | |
| 3 | **Статус:** Proposed |
| 4 | **Дата:** 2026-06-20 |
| 5 | **Обновляет:** [0103 §2.3](0103-editor-hud-substrate-semantic-projection-and-surface-adapter.md#adr0103-web-vs-native-forward) — Monaco перестаёт быть «молчаливо отклонённым» baseline; становится **явно разрешённой опциональной линией** при включении в конфиге. |
| 6 | |
| 7 | ## Связанные ADR |
| 8 | |
| 9 | | ADR | Роль | |
| 10 | |-----|------| |
| 11 | | [0103](0103-editor-hud-substrate-semantic-projection-and-surface-adapter.md) | `IEditorSurfaceAdapter`, Editor HUD substrate, hi-freq bounded-контур | |
| 12 | | [0108](0108-web-ai-portal-host-object-tools-bridge.md) | WebView2 в процессе IDE, Host Object / `WebMessageReceived` — транспортный прецедент | |
| 13 | | [0035](0035-mfd-embedded-webview-external-llm-and-mcp-boundary.md) | Граница доверия веб ≠ MCP; **редактор Forward** — controlled origin, не чужой HTTPS | |
| 14 | | [0084](0084-agent-edits-editor-source-of-truth-presence-channel.md) | Буфер редактора — источник применяемой правды; versioned apply | |
| 15 | | [0085](0085-editor-hud-inline-layer-and-hud-banner.md) | Inline vs HUD banner | |
| 16 | | [0009](0009-strangler-migration-and-exceptions.md) | Dual-path: AvaloniaEdit остаётся до parity | |
| 17 | | [0120](0120-primary-work-surface-intercom-or-editor.md) | Forward / primary work surface | |
| 18 | | [0152](0152-editor-control-flow-virtual-spacing.md) | Virtual spacing CF — перенос на Monaco decorations / gutter lane | |
| 19 | |
| 20 | ### Вне ADR |
| 21 | |
| 22 | | Документ | Роль | |
| 23 | |----------|------| |
| 24 | | [editor-surface-candidates-comparison-v1](../design/editor-surface-candidates-comparison-v1.md) | Сравнение хостов (аппендикс) | |
| 25 | | [editor-hud-inline-migration-inventory-v1](../design/editor-hud-inline-migration-inventory-v1.md) | Inventory inline-рендера для strangler | |
| 26 | | [editor-forward-ui-cleanup-roadmap-v1](../ui-ux/editor-forward-ui-cleanup-roadmap-v1.md) | Roadmap полировки Forward | |
| 27 | |
| 28 | --- |
| 29 | |
| 30 | <a id="adr0162-context"></a> |
| 31 | |
| 32 | ## 1. Контекст |
| 33 | |
| 34 | **Продуктовая боль:** Avalonia как **фюзеляж** приемлема (окна, dock, focus, OS integration), но **текстовый UX Forward** на **AvaloniaEdit** систематически слабее зрелых web-редакторов: |
| 35 | |
| 36 | - рендеринг текста, caret, selection, scroll; |
| 37 | - IntelliSense / completion / hover / Quick Info — кастомный chrome, визуальный и поведенческий разрыв с VS Code-подобным опытом; |
| 38 | - inline HUD (squiggles, inlays, ghost) — дорогая борьба с `IBackgroundRenderer` и adorners в `DockDocumentView`. |
| 39 | |
| 40 | [0103](0103-editor-hud-substrate-semantic-projection-and-surface-adapter.md) уже ввёл **`IEditorSurfaceAdapter`** и зафиксировал AvaloniaEdit как baseline; Monaco/WebView2 — «вне спайка v1». Субстрат HUD (`SemanticProjectionPipeline`, `EditorHudEngine`, DAL/CCU) **не привязан** к AvaloniaEdit — второй адаптер архитектурно ожидаем. |
| 41 | |
| 42 | **Внешние рекомендации (Photino / «лёгкий Electron»):** часто смешивают три разных тезиса: |
| 43 | |
| 44 | 1. OS WebView легче упаковки Electron — **верно**; |
| 45 | 2. «C# и JS в одном address space», «zero-latency IPC» — **неверно** для WebView2 (renderer out-of-process); |
| 46 | 3. «30–40 МБ на IDE» — **нереалистично** для Monaco + нескольких поверхностей; память уходит в процессы Edge/WebView2, а не исчезает. |
| 47 | |
| 48 | **Photino-shell** (весь UI в web) **не** требуется, чтобы снять боль редактора: достаточно **Monaco-island** в уже принятом **WebView2** (`Avalonia.Controls.WebView` / `NativeWebView`, см. [0108](0108-web-ai-portal-host-object-tools-bridge.md)). |
| 49 | |
| 50 | --- |
| 51 | |
| 52 | <a id="adr0162-decision"></a> |
| 53 | |
| 54 | ## 2. Решение |
| 55 | |
| 56 | ### 2.1 Направление |
| 57 | |
| 58 | 1. Ввести **`MonacoWebViewSurfaceAdapter`** — реализация **`IEditorSurfaceAdapter`** поверх **Monaco Editor** во **встроенном WebView2** в слоте `DockDocumentView` (Forward), **без** миграции всего CIDE на Photino. |
| 59 | |
| 60 | 2. **Avalonia остаётся фюзеляжом:** dock, окна, terminal, Skia-острова (Intercom, cockpit), MFD — без изменения [0044](0044-avalonia-host-skia-agent-chat-surface.md) / [0123](0123-intercom-full-skia-surface-evolution.md). |
| 61 | |
| 62 | 3. **Strangler dual-path** ([0009](0009-strangler-migration-and-exceptions.md)): конфиг **`[editor].forward_host`** = `avalonia_edit` (default) \| `monaco_webview2`. Переключатель на уровне workspace или глобально в `settings.toml`; без silent fallback между хостами на одном документе. |
| 63 | |
| 64 | 4. **LSP, Roslyn, DAL, agent MCP** — **без изменения контрактов**; только транспорт текста/offsets/decorations через адаптер. |
| 65 | |
| 66 | ### 2.2 Что **не** делаем |
| 67 | |
| 68 | - **Полный Photino-shell** (все панели в HTML) — отдельная линия, не блокер и не замена этого ADR. |
| 69 | - **Electron** — не рассматриваем (bundled Chromium). |
| 70 | - **WebView для forge write path** — по-прежнему [FORGE ADR-0002](https://github.com/AI-Guiders/agent-forge/blob/main/design/FORGE-ADR-0002-mcp-first-web-view-only.md) / CIDE native; Monaco — **редактор кода**, не forge SPA. |
| 71 | - **Monaco как единственный default** — только после явного sign-off parity (фазы ниже). |
| 72 | |
| 73 | ### 2.3 Инварианты (не ломать) |
| 74 | |
| 75 | - [0084](0084-agent-edits-editor-source-of-truth-presence-channel.md): канонический текст для apply — **версионированный буфер**; Monaco не «тихий master» без sync protocol. |
| 76 | - [0085](0085-editor-hud-inline-layer-and-hud-banner.md): inline и banner — разные слои; маппинг на Monaco `deltaDecorations` / content widgets / overlay, не один DOM-хак. |
| 77 | - [0103 §2.2](0103-editor-hud-substrate-semantic-projection-and-surface-adapter.md#adr0103-layering-table): hi-freq caret/pointer — **bounded channel + throttle**, не DataBus на каждую клавишу. |
| 78 | - [0035](0035-mfd-embedded-webview-external-llm-and-mcp-boundary.md): origin редактора — **`cide-editor://` / bundled asset**, не произвольный HTTPS; Host Object allowlist **отдельный** от Web AI Portal ([0108](0108-web-ai-portal-host-object-tools-bridge.md)). |
| 79 | |
| 80 | --- |
| 81 | |
| 82 | <a id="adr0162-ipc"></a> |
| 83 | |
| 84 | ## 3. IPC и память (честные ожидания) |
| 85 | |
| 86 | ### 3.1 WebView2 — не «zero latency» |
| 87 | |
| 88 | | Утверждение | Факт | |
| 89 | |-------------|------| |
| 90 | | C# и страница в одном процессе | **Нет:** renderer WebView2 — **out-of-process** (Edge/Chromium) | |
| 91 | | IPC быстрее localhost HTTP | **Да:** `PostWebMessage` / Host Object | |
| 92 | | IPC быстрее in-process AvaloniaEdit | **Нет:** async marshaling; hi-freq path **coalesce** (16–50 ms throttle уже в 0103) | |
| 93 | | Photino даёт иной IPC, чем Avalonia WebView | **Нет** на Windows: тот же WebView2 под капотом | |
| 94 | |
| 95 | ### 3.2 Память |
| 96 | |
| 97 | - Пустое окно Photino/WebView — десятки МБ; **IDE + Monaco + language services + N webviews** — **сотни МБ** суммарно по процессам Edge — норма. |
| 98 | - Выигрыш vs Electron — **не тащим свой Chromium**; vs AvaloniaEdit-only — **дополнительный** renderer process на Forward-документ. |
| 99 | |
| 100 | --- |
| 101 | |
| 102 | <a id="adr0162-bridge"></a> |
| 103 | |
| 104 | ## 4. Контракт `cide-editor` (мост C# ↔ Monaco) |
| 105 | |
| 106 | **Namespace сообщений:** `cide-editor/*` (ортогонально `executeIdeCommand` из [0108](0108-web-ai-portal-host-object-tools-bridge.md)). |
| 107 | |
| 108 | **Транспорт:** `WebMessageReceived` + опционально Host Object для sync read (caret snapshot); JSON camelCase. |
| 109 | |
| 110 | **Минимальный набор (v0):** |
| 111 | |
| 112 | | Направление | Message | Назначение | |
| 113 | |-------------|---------|------------| |
| 114 | | C# → JS | `editor/setModel` | uri, languageId, text, version | |
| 115 | | C# → JS | `editor/applyEdits` | edits[], expectedVersion | |
| 116 | | C# → JS | `editor/setDecorations` | decoration sets (diagnostics, agent range, CF gutter) | |
| 117 | | C# → JS | `editor/setTheme` | tokens из UI theme pipeline [0086](0086-ui-theme-toml-canonical-json-mcp-wire.md) | |
| 118 | | JS → C# | `editor/didChange` | contentChanges[], version | |
| 119 | | JS → C# | `editor/didChangeCursorSelection` | offsets, reason | |
| 120 | | JS → C# | `editor/ready` | handshake после load | |
| 121 | |
| 122 | **Source of truth (0084):** v0 — **C# master**: push `setModel` / `applyEdits` после локальных и agent mutations; JS шлёт deltas, C# мержит и инкрементирует version. Инверсия (Monaco master) — только с отдельным ADR и тестами race. |
| 123 | |
| 124 | **Assets:** bundled static (`Assets/editor/` или `wwwroot/cide-editor/`), загрузка через `WebView2` virtual host или `file://` под контролируемым path — детали реализации, origin **не** third-party. |
| 125 | |
| 126 | --- |
| 127 | |
| 128 | <a id="adr0162-phases"></a> |
| 129 | |
| 130 | ## 5. Фазы внедрения (strangler) |
| 131 | |
| 132 | | Фаза | Объём | Критерий | |
| 133 | |------|--------|----------| |
| 134 | | **M0** | Monaco в WebView: open/edit file, caret events | Лог + ручной smoke | |
| 135 | | **M1** | `MonacoWebViewSurfaceAdapter` + `[editor].forward_host` | Переключение host без падения dock | |
| 136 | | **M2** | Diagnostics → decorations из `EditorHudEngine` snapshot | Parity squiggles с AvaloniaEdit на одном файле | |
| 137 | | **M3** | Inline inventory → Monaco (hover, inlay, agent reveal [0130](0130-editor-agent-range-reveal-without-selection.md)) | Item-by-item из migration inventory | |
| 138 | | **M4** | Agent applyEdits + 0084 integration tests | Version mismatch → reject, не silent corrupt | |
| 139 | | **M5** | CF virtual spacing [0152](0152-editor-control-flow-virtual-spacing.md) на Monaco gutter lane | Визуальный sign-off | |
| 140 | | **M6** | Default flip to `monaco_webview2` (optional) | Product sign-off; ADR status → Accepted · Implemented | |
| 141 | |
| 142 | **Dual-path до M6:** AvaloniaEdit не удаляем; regression на `avalonia_edit` для CI matrix optional. |
| 143 | |
| 144 | --- |
| 145 | |
| 146 | <a id="adr0162-consequences"></a> |
| 147 | |
| 148 | ## 6. Последствия |
| 149 | |
| 150 | ### Положительные |
| 151 | |
| 152 | - Текст, selection, completion UI, diff editor — зрелый API Monaco; снимает класс боли AvaloniaEdit без смены фюзеляжа. |
| 153 | - Переиспользование WebView2-инфраструктуры и паттернов [0108](0108-web-ai-portal-host-object-tools-bridge.md). |
| 154 | - `IEditorSurfaceAdapter` и Editor HUD substrate получают **реальную вторую реализацию** — проверка абстракции. |
| 155 | |
| 156 | ### Отрицательные / trade-offs |
| 157 | |
| 158 | - Две темы (Avalonia + Monaco CSS) — генератор из канона [0086](0086-ui-theme-toml-canonical-json-mcp-wire.md), не ручной drift. |
| 159 | - IPC-latency на hi-freq path — дисциплина throttle; возможен micro-jank vs native. |
| 160 | - `DockDocumentView` strangler: AvaloniaEdit-specific renderers отмирают **по inventory**, не одним PR. |
| 161 | - Windows-first (текущий `win-x64` RID); Linux/macOS WebView — отдельный roadmap ([0093](0093-mfd-embedded-browser-for-launch-url.md)), не блокер Proposed. |
| 162 | |
| 163 | --- |
| 164 | |
| 165 | <a id="adr0162-alternatives"></a> |
| 166 | |
| 167 | ## 7. Отклонённые альтернативы |
| 168 | |
| 169 | | Альтернатива | Почему нет | |
| 170 | |--------------|------------| |
| 171 | | **Photino-shell** для всего CIDE | Снимает не ту боль; переписывает dock/terminal/Skia; тот же WebView2 IPC | |
| 172 | | **Electron** | Bundled Chromium + Node; против целей lean desktop | |
| 173 | | **Оставить только AvaloniaEdit** | Принято как **default до M6**; не отменяет optional Monaco | |
| 174 | | **Monaco в MFD, не Forward** | Не заменяет primary editor; только preview | |
| 175 | | **CodeMirror 6** вместо Monaco | Допустимый spike; ADR фиксирует Monaco как первый кандидат (LSP ecosystem, diff, VS Code parity) | |
| 176 | |
| 177 | --- |
| 178 | |
| 179 | <a id="adr0162-config"></a> |
| 180 | |
| 181 | ## 8. Конфиг (sketch) |
| 182 | |
| 183 | ```toml |
| 184 | [editor] |
| 185 | # avalonia_edit | monaco_webview2 |
| 186 | forward_host = "avalonia_edit" |
| 187 | ``` |
| 188 | |
| 189 | Per-workspace override — через существующий stack `settings.toml` / workspace profile (детали при M1). |
| 190 | |
| 191 | --- |
| 192 | |
| 193 | <a id="adr0162-implementation"></a> |
| 194 | |
| 195 | ## 9. Ownership (sketch) |
| 196 | |
| 197 | | Компонент | Расположение | |
| 198 | |-----------|--------------| |
| 199 | | `MonacoWebViewSurfaceAdapter` | `Features/Editor/Application/` | |
| 200 | | `cide-editor` bridge (TS) | `Assets/cide-editor/` или `wwwroot/cide-editor/` | |
| 201 | | WebView host control | `Views/` или `Features/Editor/Presentation/` | |
| 202 | | Theme token export | рядом с [0086](0086-ui-theme-toml-canonical-json-mcp-wire.md) pipeline | |
| 203 | |
| 204 | --- |
| 205 | |
| 206 | <a id="adr0162-history"></a> |
| 207 | |
| 208 | ## История изменений |
| 209 | |
| 210 | | Дата | Изменение | |
| 211 | |------|-----------| |
| 212 | | 2026-06-20 | Proposed: Monaco Forward host, WebView2 island, anti-hype IPC/memory, strangler phases | |
| 213 | | 2026-06-21 | Уточнение: ветка `feature/monaco-forward-editor-m0` убрала Avalonia `TextEditor` из dock; полный перевод и bus — [0163](0163-monaco-native-capability-bus-full-forward-migration.md) | |
| 214 | |