Запись 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-индекс курсора
При проектировании мы сразу наложили строгие ограничения:
- Никакого вывода base64 в текстовый буфер: Движок
xterm.dartне обучен протоколу Kitty Graphics. Если управляющая последовательность\x1b_G ... \x1b\попадает в метод_terminal.write(), эмулятор воспринимает ее как обычный текст и засыпает терминал гигантской портянкой из десятков килобайт base64-символов. - Сохранение неприкосновенности курсора (BUG-052): В релизе v0.9.3 мы совершили прорыв, изолировав мигающий курсор в отдельный слой
TerminalCursorOverlayповерхRepaintBoundaryс 0% нагрузки на процессор в режиме покоя. Графический слой картинок ни в коем случае не должен был сломать позиционирование курсора или перекрыть его: курсор обязан рендериться поверх превью картинок вyazi. - Сохранность меток промпта (OSC 133): Левое поле отступа
22pxпод цветные индикаторы статуса команд должно оставаться каноничным.
3. Логика решений: надежный конвейер перехвата и каскад шрифтов
Мы реализовали задачу в три синхронных слоя:
- Каскад шрифтов и настройки типографики:
- Создали провайдер
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.
- Создали провайдер
- Потоковый фильтр
KittyGraphicsStreamFilter:- Встроили перехватчик
preWriteFilterвTerminalStreamCoalescer. Потоковый фильтр анализирует входящий байтовый поток, аккуратно вырезает APC-последовательности\x1b_G ... \x1b\и\x1b_G ... \x07, предотвращая их попадание вxterm.dart, а чистый текст отдает терминалу. - Поддержали многосегментную передачу (
m=1/m=0), сборку чанков и удаление картинок (a=d).
- Встроили перехватчик
- Реестр текстур и слой наложения
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.