v0.9.5 Custom Typography & Kitty Graphics 8 мин 11 октября 2026, 01:25 — 02:15

Акт XVI: Консольная эстетика для Linux-гурманов — кастомные шрифты, каскад Nerd Fonts и архитектура Kitty Graphics

Как мы реализовали выбор шрифта терминала с каскадом глифов Nerd Fonts (U+E000..U+F8FF), внедрили потоковый фильтр APC для протокола Kitty Graphics с рендерингом картинок в yazi, и защитили изолированный курсор от конфликтов слоев.

BUG-057 #typography #nerd-fonts #kitty-graphics #yazi #chafa #terminal-ui #bug-057 #apc #stream-filter
Контекст акта: После закрытия первого публичного бага Issue #1 и релиза v0.9.4 пришло время заняться тем, что делает терминал по-настоящему родным домом для разработчика — типографикой, иконками Nerd Fonts и отображением графических файлов. В этом акте мы выстроили многоуровневый каскад шрифтов, написали потоковый парсер для протокола Kitty Graphics с отсечением base64 до попадания в xterm.dart, и аккуратно встроили графический холст под слой изолированного курсора.

Запись 64. Консольная эстетика для Linux-гурманов: кастомные шрифты, каскад Nerd Fonts и архитектура Kitty Graphics

⏱️ Время: 11 октября 2026, 01:25 — 02:15 (~50 минут)

1. Повод: когда терминалу не хватает лоска и картинок

После успешного закрытия Issue #1 и релиза v0.9.4 мы вернулись к пожеланиям, сформулированным при тестировании Shellit на Arch Linux + Hyprland. Одно дело — стабильно открывать и делить сессии, и совсем другое — чувствовать себя в консоли как дома.

Современные консольные инструменты (yazi, eza, starship, bat, fzf) активно используют символы расширенной таблицы Unicode и приватной области Nerd Fonts (U+E000..U+F8FF): иконки каталогов 📁, файлов 📄, языков программирования, шевронов и статусов git. До сих пор Shellit полагался на базовый моноширинный стек. Если в системе у пользователя стоял любимый JetBrainsMono Nerd Font или FiraCode Nerd Font, терминал не умел гибко под него подстроиться, а иконки в TUI-файловых менеджерах либо подменялись пустыми квадратами, либо разъезжались по ширине сетки.

А вишенкой на торте стала давняя мечта любого линуксоида — отображение превью графических файлов прямо в консоли через протокол Kitty Graphics Protocol (передача картинок для yazi и chafa).

2. Подводные камни: ловушка xterm.dart, APC-последовательности и z-индекс курсора

При проектировании мы сразу наложили строгие ограничения:

  1. Никакого вывода base64 в текстовый буфер: Движок xterm.dart не обучен протоколу Kitty Graphics. Если управляющая последовательность \x1b_G ... \x1b\ попадает в метод _terminal.write(), эмулятор воспринимает ее как обычный текст и засыпает терминал гигантской портянкой из десятков килобайт base64-символов.
  2. Сохранение неприкосновенности курсора (BUG-052): В релизе v0.9.3 мы совершили прорыв, изолировав мигающий курсор в отдельный слой TerminalCursorOverlay поверх RepaintBoundary с 0% нагрузки на процессор в режиме покоя. Графический слой картинок ни в коем случае не должен был сломать позиционирование курсора или перекрыть его: курсор обязан рендериться поверх превью картинок в yazi.
  3. Сохранность меток промпта (OSC 133): Левое поле отступа 22px под цветные индикаторы статуса команд должно оставаться каноничным.

3. Логика решений: надежный конвейер перехвата и каскад шрифтов

Мы реализовали задачу в три синхронных слоя:

  1. Каскад шрифтов и настройки типографики:
    • Создали провайдер terminalFontFamilyProvider и отказоустойчивый список kTerminalFontFallbacks (JetBrainsMono Nerd Font, Symbols Nerd Font Mono, Nerd Font Symbols, monospace). Даже если в выбранном пользователем основном шрифте отсутствует нужный глиф, Flutter автоматически подтягивает его из системного Nerd Font без перекоса знакомест.
    • В карточке настроек TerminalSettingsCard добавили выпадающий список популярных моноширинных шрифтов (JetBrains Mono, JetBrainsMono Nerd Font, Fira Code, FiraCode Nerd Font, Cascadia Code), режим ввода любого системного шрифта (Custom...) и живое превью с глифами 📁 📄  [✓] 🚀. Выбор сохраняется в шифрованное хранилище storage_vault.
  2. Потоковый фильтр KittyGraphicsStreamFilter:
    • Встроили перехватчик preWriteFilter в TerminalStreamCoalescer. Потоковый фильтр анализирует входящий байтовый поток, аккуратно вырезает APC-последовательности \x1b_G ... \x1b\ и \x1b_G ... \x07, предотвращая их попадание в xterm.dart, а чистый текст отдает терминалу.
    • Поддержали многосегментную передачу (m=1 / m=0), сборку чанков и удаление картинок (a=d).
  3. Реестр текстур и слой наложения TerminalGraphicsOverlay:
    • TerminalGraphicsRegistry декодирует полученные байты PNG/RGBA через нативный графический движок ui.instantiateImageCodec и транслирует координаты ячеек (столбец, строка, ширина, высота).
    • В TerminalScreen виджет TerminalGraphicsOverlay смонтирован точно между текстовым холстом TerminalView и слоем изолированного курсора TerminalCursorOverlay.

4. Результат

  • Архитектура полностью протестирована: модульные тесты kitty_graphics_test.dart подтвердили надежную фильтрацию текста и обработку многоблочных команд.
  • Все 210 тестов пакета packages/terminal_ui и 61 тест apps/shellit прошли с безупречным результатом (100% зеленые).
  • Статический анализатор dart analyze --fatal-infos рапортует об абсолютной чистоте: 0 issues.
  • Баг BUG-057 закрыт, подготовлен авторелиз v0.9.5.
📖 Типографика статьи выровнена по алгоритму Кнута — Пласса с помощью mcp-typography-server на базе Justif.
Knuth-Plass • TeX Style