| 1 | # Playbook: TCI (Text-Command Interface) v1 |
| 2 | |
| 3 | **TCI** — семейство Skia-полос ввода со slash-иерархией, popup и отдельным буфером в VM. Не путать с MFD Terminal (OS/process). |
| 4 | |
| 5 | ## Экземпляры |
| 6 | |
| 7 | | Экземпляр | Буфер VM | Enter | Strip | |
| 8 | |-----------|----------|-------|-------| |
| 9 | | Composer | `ChatInput` / `ComposerText` | send агенту | `SkiaComposerStrip` | |
| 10 | | CCL (Cockpit Command Line) | `CockpitCommandLineText` | execute slash | `SkiaCommandLineStrip` | |
| 11 | | Navigator search | `TopicNavigatorSearchQuery` | фильтр тем | однострочный hit-test | |
| 12 | | Chord overlay | короткий ввод | handoff | отдельный UX | |
| 13 | |
| 14 | ## Общий слой |
| 15 | |
| 16 | - **`SkiaPlainTextLayout`** — RichTextKit: measure, caret, hit-test. |
| 17 | - **`SkiaTciTextField`** — selection, horizontal scroll (CCL), caret draw, merge preedit. |
| 18 | |
| 19 | Синхронизация VM: |
| 20 | |
| 21 | 1. Control держит текст и caret (`ComposerText`, `CommandLineText`, …). |
| 22 | 2. События `ComposerDraftChanged` / `CommandLineDraftChanged` — **caret до текста**, затем autocomplete. |
| 23 | 3. Avalonia binding догоняет VM; не полагаться на порядок `PropertyChanged` от setter VM. |
| 24 | |
| 25 | ## CCL (ADR 0138 фаза A) |
| 26 | |
| 27 | - Mono: `Cascadia Mono` через RichTextKit (`SkiaCommandLineStrip.MonoFontFamily`). |
| 28 | - Click-to-caret, selection (Shift+стрелки), Ctrl+A/C/X/V, Home/End. |
| 29 | - Горизонтальный scroll колёсиком при длинной команде. |
| 30 | - Slash-validation — **pill вокруг команды** (как attach-chip / CodeAnchors): примитив `SkiaStatusChip` + обёртка `SkiaSlashCommandChip`; severity из `SlashCommandPreviewKind`; отдельной строки под полем нет. |
| 31 | - Высота полосы масштабируется с `command_line_pt` (defaults 15pt): строка ввода ≈ `fontSize × 22/12`, preview ≈ `previewPt × 16/10`; не фиксированные 22px (иначе при 15pt режется снизу). |
| 32 | - IME: `IntercomSkiaTextInputClient` в режиме `IsCommandLineInputActive` (без preedit). |
| 33 | |
| 34 | ### Preview severity (не канал EICAS) |
| 35 | |
| 36 | Визуальная валидация slash — **отдельный контур** от W/C/A в [ADR 0021](../adr/0021-pfd-mfd-cockpit-attention-model.md): не попадает в `EicasAlertsBar`/CAS, Enter не блокируется (как chip attach, ADR 0128 §9.1). Рисование: `SkiaStatusChip` (примитив) → `SkiaSlashCommandChip` / `SkiaSlashPreviewChrome`. Контракт глифов ✓ / ✕ / **P(n)** — [ADR 0140](../adr/0140-tci-slash-status-glyphs-and-args-counter.md). |
| 37 | |
| 38 | **Иконка в pill:** по умолчанию **справа** от slash-текста (`[intercom] tci_validation_icon = right`). Варианты: `left` (gutter + clip union), `highlight_only` (без глифа). Clip поля ввода — union `textBounds` и `chipRect`, иначе глиф обрезается. |
| 39 | |
| 40 | **Палитру** берём из кокпита (красный / янтарь / зелёный / серый), **семантику** — свою: |
| 41 | |
| 42 | | TCI (`SlashCommandPreviewKind`) | Глиф (целевой) | Смысл | Цвет | |
| 43 | |---------------------------------------|----------------|-------|------| |
| 44 | | `Ok` (**Ready**) | ✓ | команда + args готовы | зелёный | |
| 45 | | `Incomplete` | **P** / **P(n)** (сейчас ⚠) | не хватает args, id допечатывается | янтарь | |
| 46 | | `Error` (**Invalid**) | ✕ | нет команды, опечатка, синтаксис | красный | |
| 47 | | `Hint` | ℹ | мягкая подсказка (редко) | серый | |
| 48 | |
| 49 | Примеры: `/intercom test` → `Error` (✕); `/intercom message select` → `Incomplete` (P); `… select 5 7` → `Ok` (✓); `/intercom mesage select 5?7` → `Error` (✕). |
| 50 | |
| 51 | **Слой:** `SlashCommandPreviewService` → `SlashCommandPreviewRulePipeline` (правила); маппинг severity — **только** `SlashCommandPreviewVisualMapper` → `SkiaSlashPreviewChrome.ToChipSeverity`; chrome — `SkiaStatusChip`. В UI — pill, не вторая строка. |
| 52 | |
| 53 | **A11y (P1):** при `Error` / `Incomplete` / `Hint` полный текст — `IntercomSlashPreviewToolTip` на `IntercomSkiaSurface` (`ToolTip.Tip`), плюс поля `CommandLineSlashPreview` / `ComposerSlashPreview` в VM. Сессия CCL: `ICockpitCommandLineSession.PreviewAccessibilityToolTip`. |
| 54 | |
| 55 | ### SkiaStatusChip (общий примитив) |
| 56 | |
| 57 | `Views/SkiaKit/SkiaStatusChip.cs` — скруглённая рамка, заливка, иконка слева; палитра `SkiaStatusChipColors` (Border, Fill, Icon, Accent). Используют: `SkiaSlashCommandChip`, `SkiaIntercomAttachLinkChip` (лента). API: `ComputeRectAroundTextStart`, `DrawFrame`, `DrawChrome`, `ResolveColors(theme, severity)`. |
| 58 | |
| 59 | **Composer:** `EvaluateComposerAtCaret` (линия по каретке); VM: `ComposerSlashPreview` / `ComposerSlashPreviewKind`. **CCL:** `Evaluate(buffer)`; VM: `CommandLineSlashPreview` / `CommandLineSlashPreviewKind`. При открытом CCL composer-preview скрывается. |
| 60 | |
| 61 | ## Composer |
| 62 | |
| 63 | См. `playbook-skia-text-surface-v1.md`: многострочный scroll по Y, preedit, send. |
| 64 | |
| 65 | ## Чеклист при новом TCI-поле |
| 66 | |
| 67 | 1. Strip на `SkiaPlainTextLayout` + при необходимости `SkiaTciTextField`. |
| 68 | 2. `*DraftChanged` с caret-before-text. |
| 69 | 3. Hit-test и caret rect для IME (`Get*CaretScreenRect`). |
| 70 | 4. Chrome-only invalidate при blink (`InvalidateComposerChrome`). |
| 71 | 5. Тесты hit-test/caret в `CascadeIDE.Tests`. |
| 72 | |
| 73 | ## Связанные ADR |
| 74 | |
| 75 | - 0119 — slash commands Intercom |
| 76 | - 0123 — full Skia surface |
| 77 | - 0138 — CCL и parametric ranges (фаза B — preview ranges) |
| 78 | |