Агентный слой
Лог
Поток строк от машины: время, уровень, сообщение. Моноширинный набор и колонки фиксированной ширины, поэтому лог читается столбцами, а не сплошным текстом.
Разметка
<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-tone | — | warn · error |
| переменная | ||
--level-ink | --text-muted | |
| токен | ||
--font-mono | ui-monospace, "Cascadia Code", "JetBrains Mono", "SF Mono", Consolas, "Liberation Mono", monospace | |
--text-xs | 0.75rem | |
--surface-sunken | light-dark(var(--n-2), var(--n-14)) | |
--radius-md | 7px | |
--space-4 | 8px | |
--pad-panel | var(--space-5) | |
--gap-inline | var(--space-4) | |
--warn-text | light-dark(var(--warn-5), var(--warn-3)) | |
--err-text | light-dark(var(--err-5), var(--err-3)) | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument