Skip to content

UI Layer ru

Se.Cherkasov edited this page Aug 23, 2026 · 1 revision

Слой UI

English · Русский

Один модуль Compose Multiplatform (composeApp/) рисует все платформы. Телефонный UI — общий код, а не Android-ветка; именно поэтому его гоняют десктопные тесты.

Структура

composeApp/src/commonMain/kotlin/app/skerry/ui/
  app/          состояние оболочки, locals, UI-теги, бэкенд отрисовки
  design/       дизайн-система: примитивы, диалоги, меню, поля, папки
  desktop/      десктопная оболочка: рейка, вьюпорт, рабочая панель
  mobile/       телефонная оболочка: вкладки, шторки, экраны
  terminal/ session/ files/ sftp/ remote/ vnc/ rdp/   рабочие поверхности
  host/ vault/ identity/ known/ trust/ tunnel/ snippet/ runbook/ teams/ share/ ai/ sync/ metrics/ settings/
  i18n/ theme/ nav/ update/ keepalive/
  AppDependencies.kt

AppDependencies — граф, который собирает платформенная точка входа и отдаёт корневому composable (DesktopDesignApp / MobileDesignApp). Не подключённая на платформе подсистема приходит как null, и UI показывает заглушку, а не падает.

Дизайн-примитивы

Экраны не используют сырые Material-компоненты. Каталог в docs/coding-guidelines.md нормативен; несущие позиции:

Нужно Примитив
Любой текст Txt — сырой Text() не используется нигде (100+ файлов)
Любая иконка Sym (глиф Material Symbols) + DesignFonts
Кнопки PrimaryButton, GhostButton, CancelButton, IconBtn, GlyphButton
Мелкие контролы Toggle, Badge, Dot, MeterBar, NumberStepper, HoverTooltip
Строки ToggleRow, KeyValueRow, RowNote / NoteBlock
Меню MenuPanel + MenuItem / MenuActionRow — каждое меню по правому клику и «⋮», включая контекстное меню текста
Диалоги ConfirmActionDialog, NoticeDialog, ModalScrim, modalBody
Команда, которую цитирует диалог CommandQuote (+ ClippedNotice); команда в строке — CommandQuote.CommandLine
Поля FormField + Modifier.fieldName(), ModalTextField, FieldDraft
Папки Folders + FolderSectionHeader, GroupSelectField (десктоп) / MobileGroupSelectField
Недоверенный текст UntrustedText.untrustedLabel, sanitizeServerText, sanitizeServerHost
Вопрос, который задаёт подключение PromptQueue
Объявления для скринридера StatusAnnouncer
Удержание клавиатуры на живом виджете KeyboardClaim

За большинством из них стоят два правила: одно определение на форму (третья самодельная копия — баг) и доступность приходит из примитива — подпись, опубликованная FormField, становится доступным именем поля, поэтому поле не может остаться безымянным.

Токены и тема

Цвета берутся из дизайн-токенов D.*, зеркалящих блок :root прототипа (docs/design/Skerry Tablet.html), и никогда не пишутся hex'ом в экране. Новый оттенок — это новый токен, сверенный с прототипом. Темы: тёмная, светлая и «Системная» за ОС; терминал следует теме приложения.

Хром повторяет прототипы 1:1 — UI собирается по ним, а не придумывается.

i18n

Каждая видимая пользователю строка — ресурс в composeResources, переведённый сразу на английский, русский и упрощённый китайский. Захардкоженный литерал валит детерминированные проверки.

Две грабли, зафиксированные в коде:

  • у каждой локали свои кавычки (" " в en и zh, « » в ru — прямые " не используются);
  • в composeResources плагин раскрывает только \n, \t, \uXXXX и \\, а подстановка — это регексп по %N$s / %N$d, а не String.format: Android-стиль \" или голый %s — баг. StringEscapeTest прочёсывает все строки во всех локалях по всем четырём пунктам.

Паритет десктоп ⇆ Android

Десктоп и мобайл делят состояние формы и логику (*FormState); различается только раскладка. Новая форма начинается с общего класса состояния и получает два тонких вида. Паритет проверяется на ревью, а Android-компиляция UI-изменения — стадия гейта (:androidApp:compileDebugKotlin).

Контроллеры

Логика не живёт в composable-функциях. Контроллеры (ConnectionController, SessionsController, RemoteDesktopController, SyncCoordinator, RunbookRunner, …) получают зависимости через конструктор — класс, который нельзя протестировать без сети, диска или UI, спроектирован неверно. Конкурентные контроллеры приходят с тестами на отмену и повторный вход.

Тесты по настоящей оболочке

desktopTest/ui/desktop/ShellHarness запускает реальный UI на демо-графе: runDesktopShell, runMobileShell, runForm, плюс onField / onCatalog / onTab / press. Обе оболочки идут на десктопной JVM — мобильный UI общий, поэтому Android-паритет проверяется без эмулятора. Навигация адресуется через ui/app/UiTags, потому что кнопка рейки рисует лигатуру шрифта, а каждая подпись локализована.

Clone this wiki locally