Перейти к содержимому
Плотность
Тема

Агентный слой

Лог

JS Копирование делает кит. Прокрутка за хвостом и виртуализация — слой приложения

Поток строк от машины: время, уровень, сообщение. Моноширинный набор и колонки фиксированной ширины, поэтому лог читается столбцами, а не сплошным текстом.

Компонент
14:32:07infoЗапуск worldgen-01
14:32:09infoПрочитано 4 файла
14:32:11warnchunks.bin занят, повтор через 1 с
14:32:16errorEBUSY: не удалось прочитать chunks.bin
Разметка
<div class="inst-log" role="log" aria-label="Лог прогона">
  <div class="inst-log-line"><span class="inst-log-time">14:32:07</span><span class="inst-log-level">info</span><span>Запуск worldgen-01</span></div>
  <div class="inst-log-line"><span class="inst-log-time">14:32:09</span><span class="inst-log-level">info</span><span>Прочитано 4 файла</span></div>
  <div class="inst-log-line" data-tone="warn"><span class="inst-log-time">14:32:11</span><span class="inst-log-level">warn</span><span>chunks.bin занят, повтор через 1 с</span></div>
  <div class="inst-log-line" data-tone="error"><span class="inst-log-time">14:32:16</span><span class="inst-log-level">error</span><span>EBUSY: не удалось прочитать chunks.bin</span></div>
</div>

Использование

<div class="inst-log" role="log" aria-label="Лог прогона">
  <div class="inst-log-line">
    <span class="inst-log-time">14:32:07</span>
    <span class="inst-log-level">info</span>
    <span>Запуск worldgen-01</span>
  </div>
</div>
Что Обязательно Почему
role="log" и aria-label да Строки, прибывающие во время работы, иначе не объявляются вовсе
Три узла в строке: время, уровень, сообщение да Колонки заданы шириной в ch; узел не на своём месте ломает выравнивание всех строк
data-tone только у warn и error да Обычная строка тона не несёт: в потоке из тысячи строк подсвечено то, что требует внимания
Моноширинный набор да Приходит от inst-log. Пропорциональный шрифт рассыпает колонку времени

Когда использовать

Используйте Возьмите другое
Поток однородных строк от машины События с разным составомлента
Строк сотни и тысячи Вызовы инструментов с выводомшаги: у них есть тело
Порядок строго хронологический Фрагмент кода или командаблок кода

Устройство

Сетка объявлена на строке, то есть у каждой строки она своя. Если задать колонку уровня по содержимому, её ширина будет меняться от слова к слову: info уже, чем error, и сообщения поедут лесенкой. Поэтому inst-log-level получает фиксированную ширину в 5ch.

Варианты

<div class="inst-log-line" data-tone="error">…</div>
Тон Что красится
нет атрибута Только уровень, приглушённо
warn Уровень
error Уровень и всё сообщение

Ошибка красится целиком намеренно: её надо найти в тысяче строк, пролистывая глазом, а не читая.

JS

Подключите модуль один раз на страницу — инициализировать компоненты по отдельности не нужно, кит работает делегированием и видит узлы, пришедшие позже.

<script type="module" src="src/kit.js"></script>

Что делает кит

Пример в шапке живой: наведите на лог и нажмите кнопку копирования.

Копирование: кнопка .inst-copy внутри .inst-copyable кладёт текст в буфер и отвечает цветом. Подробности — на странице блока кода.

<div class="inst-log inst-copyable">
  <button class="inst-copy" type="button" aria-label="Скопировать лог"></button>
  …
</div>

Что остаётся приложению

Что Почему не кит
Прокрутка за хвостом «Держаться низа, пока человек не отлистал вверх» — политика, а не оформление
Виртуализация Лог агента идёт десятками тысяч строк; способ зависит от источника
Фильтр по уровню Данные

Прокрутку к хвосту приложение делает само, и это одна строка:

if (atBottom) log.scrollTop = log.scrollHeight;

Правила

Так Колонки шириной в ch

Время и уровень занимают предсказуемое место, и строки выстраиваются друг под друга при любом содержимом.

Не так Ширина колонок по содержимому

Одна длинная строка сдвигает колонку времени во всём логе, и поток перестаёт сканироваться.

Так Тон только у warn и error

В потоке из тысячи строк подсвечено то, что требует внимания. Раскрашенный целиком лог не подсвечивает ничего.

Не так Лог без role="log"

Строки, прибывающие во время работы, иначе не объявляются вовсе.

Доступность

role="log" Область, в которую добавляются записи. Скринридер объявит новые строки, не перечитывая весь лог
Живость role="log" подразумевает aria-live="polite". Для потока в сотни строк в секунду это надо отключить явно: непрерывное озвучивание делает интерфейс неработоспособным
Имя области aria-label обязателен: двa лога на экране без имён неразличимы на слух
Время — данные inst-log-time берёт --text-muted (4.5:1), а не --text-faint: таймстемп читают, а не разглядывают
Не только цвет Уровень написан словом (warn, error) рядом с цветом
Прокрутка Область достижима с клавиатуры. Автопрокрутка вниз обязана останавливаться, когда пользователь прокрутил вверх
Перенос white-space: pre-wrap и overflow-wrap: anywhere только на ячейке сообщения: на контейнере это схлопнуло бы колонки

API

ИмяЗначениеЧто делает
класс
inst-logКонтейнер, прокручивается
inst-log-lineСтрока: три колонки
inst-log-timeОтметка времени
inst-log-levelУровень, ширина 5ch
атрибут
data-tonewarn · error
переменная
--level-ink--text-muted
токен
--font-monoui-monospace, "Cascadia Code", "JetBrains Mono", "SF Mono", Consolas, "Liberation Mono", monospace
--text-xs0.75rem
--surface-sunkenlight-dark(var(--n-2), var(--n-14))
--radius-md7px
--space-48px
--pad-panelvar(--space-5)
--gap-inlinevar(--space-4)
--warn-textlight-dark(var(--warn-5), var(--warn-3))
--err-textlight-dark(var(--err-5), var(--err-3))

Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument