Агентный слой
Диф
Основной артефакт работы агента: что именно он изменил в файле.
Разметка
<div class="inst-diff">
<div class="inst-diff-head">
<span class="inst-diff-path">terrain/heightmap.ts</span>
<span class="inst-diff-stat"><span data-kind="add">+3</span> <span data-kind="del">−2</span></span>
</div>
<div class="inst-diff-body">
<div class="inst-diff-line"><span class="inst-diff-num">41</span><span class="inst-diff-code">const size = 256;</span></div>
<div class="inst-diff-line" data-kind="del"><span class="inst-diff-num">42</span><span class="inst-diff-code">let seed = 0;</span></div>
<div class="inst-diff-line" data-kind="add"><span class="inst-diff-num">42</span><span class="inst-diff-code">let seed = Date.now();</span></div>
<div class="inst-diff-fold">— пропущено 18 строк —</div>
</div>
</div>
Использование
<div class="inst-diff">
<div class="inst-diff-head">
<span class="inst-diff-path">terrain/heightmap.ts</span>
<span class="inst-diff-stat"><span data-kind="add">+3</span> <span data-kind="del">−2</span></span>
</div>
<div class="inst-diff-body">
<div class="inst-diff-line" data-kind="del">
<span class="inst-diff-num">42</span><span class="inst-diff-code">let seed = 0;</span></div>
<div class="inst-diff-line" data-kind="add">
<span class="inst-diff-num">42</span><span class="inst-diff-code">let seed = Date.now();</span></div>
</div>
</div>
| Что | Обязательно | Почему |
|---|---|---|
data-kind на строке |
да | Носитель категории. Строка без него — контекст, и это верное умолчание |
inst-diff-num отдельным узлом |
да | Номер не выделяется мышью: скопированный диф иначе приходит вперемешку с номерами строк |
inst-diff-code вокруг кода |
да | Только он держит моноширинный набор и не переносит строку |
inst-diff-fold на месте пропуска |
нет, но обычно да | Свёрнутый участок — данные о том, что показано не всё |
Когда использовать
| Используйте | Возьмите другое |
|---|---|
| Показать, что агент изменил в файле | Показать файл целиком — блок кода: диф без изменений это просто код |
| Изменения нужно принять или отклонить | Перечислить затронутые файлы без содержания — список в запросе подтверждения |
| Изменений в файле десятки строк | Изменений сотни файлов — таблица со счётчиками, диф каждого по требованию |
| — | Поток строк от машины — лог |
Устройство
Строка добавления несёт +, строка удаления −. Это выполнение закона кита:
цвет не имеет права быть единственным носителем состояния. Диф, где
изменения различаются только зелёным и красным фоном, для дальтоника — и на
чёрно-белой печати — превращается в сплошной текст.
Знак рисуется псевдоэлементом из data-kind, поэтому он не попадает в
выделение и не копируется вместе с кодом.
Варианты
<div class="inst-diff-line" data-kind="add">…</div>
| Значение | Что значит |
|---|---|
| нет атрибута | Строка контекста, не изменялась |
add |
Добавлена |
del |
Удалена |
Добавленная строка не находится в состоянии «ok» — она относится к виду
«добавление». Поэтому здесь свой атрибут, а не data-tone: тон соврал бы про
смысл. Тот же data-kind несут и числа в шапке дифа — одна ось, одна запись.
Поведение
<div class="inst-diff-fold">— пропущено 18 строк —</div>
Как и свёрнутый вывод шага, участок называет своё число словами. Молча пропустить строки — то же враньё про объём.
Сценарии
Диф с шапкой, статистикой и свёрнутым участком.
Что проверить руками: выделите несколько строк и скопируйте. В буфер
уйдёт чистый код — без номеров строк и без знаков + и −. Номера несут
user-select: none, знак рисуется псевдоэлементом. Диф, из которого нельзя
скопировать код, бесполезен в тот момент, когда он нужен.
Разметка
<div class="inst-diff">
<div class="inst-diff-head">
<span class="inst-diff-path">src/world/biomes.ts</span>
<span class="inst-diff-stat"><span data-kind="add">+3</span> <span data-kind="del">−2</span></span>
</div>
<div class="inst-diff-body">
<div class="inst-diff-fold">свёрнуто 118 строк</div>
<div class="inst-diff-line"><span class="inst-diff-num">119</span><span class="inst-diff-num">119</span><span class="inst-diff-code"> const seed = opts.seed ?? 0;</span></div>
<div class="inst-diff-line" data-kind="del"><span class="inst-diff-num">120</span><span class="inst-diff-num"></span><span class="inst-diff-code"> const noise = simplex2(seed);</span></div>
<div class="inst-diff-line" data-kind="del"><span class="inst-diff-num">121</span><span class="inst-diff-num"></span><span class="inst-diff-code"> if (noise > 0.5) return TUNDRA;</span></div>
<div class="inst-diff-line" data-kind="add"><span class="inst-diff-num"></span><span class="inst-diff-num">120</span><span class="inst-diff-code"> const noise = simplex2(seed, { octaves: 3 });</span></div>
<div class="inst-diff-line" data-kind="add"><span class="inst-diff-num"></span><span class="inst-diff-num">121</span><span class="inst-diff-code"> // тундра и степь перекрывались в 12 клетках — разводим по высоте</span></div>
<div class="inst-diff-line" data-kind="add"><span class="inst-diff-num"></span><span class="inst-diff-num">122</span><span class="inst-diff-code"> if (noise > 0.5 && height > 0.72) return TUNDRA;</span></div>
<div class="inst-diff-line"><span class="inst-diff-num">122</span><span class="inst-diff-num">123</span><span class="inst-diff-code"> return STEPPE;</span></div>
</div>
</div>
Правила
Так Знак в первой колонке
+ и − печатаются и читаются без цвета. Тон — второй признак, а не
единственный.
Не так Диф только цветом
Зелёное и красное неразличимы примерно для 8% мужчин и ни для одной чёрно-белой распечатки.
Так Номер строки невыделяемым
Скопированный диф иначе приходит вперемешку с номерами и не вставляется никуда.
Не так Перенос строки в коде
Перенос меняет смысл строки. Тело дифа прокручивается по горизонтали.
Доступность
| Не только цвет | Знак +/− в первой колонке. Это главное требование компонента |
| Номера строк | user-select: none — при копировании фрагмента номера не попадают в буфер вместе с кодом |
| Копирование | Знак — псевдоэлемент, поэтому копируется чистый код, а не +const size |
| Путь к файлу | Обрезается с конца многоточием. Полный путь обязан оставаться в title — иначе диф безымянен |
| Прокрутка | Тело прокручивается по горизонтали и должно быть достижимо с клавиатуры |
| Печать | Тонированные фоны в @media print заменяются на печатаемые, знак остаётся: на бумаге тон исчезает вовсе |
API
| Имя | Значение | Что делает |
|---|---|---|
| класс | ||
inst-diff | — | Контейнер |
inst-diff-head | — | Шапка: путь и статистика |
inst-diff-path | — | Путь к файлу. Обрезается с конца |
inst-diff-stat | — | +N / −M |
inst-diff-body | — | Тело, прокручивается |
inst-diff-line | — | Строка |
inst-diff-num | — | Номер строки. Не выделяется мышью |
inst-diff-code | — | Сам код |
inst-diff-fold | — | Свёрнутый участок |
| атрибут | ||
data-kind | add · del | на inst-diff-line и на числах в inst-diff-stat |
| токен | ||
--font-mono | ui-monospace, "Cascadia Code", "JetBrains Mono", "SF Mono", Consolas, "Liberation Mono", monospace | |
--text-xs | 0.75rem | |
--ok-bg | light-dark(var(--ok-1), color-mix(in oklab, var(--ok-4) 16%, transparent)) | |
--ok-text | light-dark(var(--ok-5), var(--ok-3)) | |
--err-bg | light-dark(var(--err-1), color-mix(in oklab, var(--err-4) 16%, transparent)) | |
--err-text | light-dark(var(--err-5), var(--err-3)) | |
--border-subtle | light-dark(oklch(0 0 0 / 0.07), oklch(1 0 0 / 0.06)) | |
--hairline | 1px | |
--radius-lg | 11px | |
--pad-cell-x | var(--space-4) | |
--text-muted | light-dark(var(--n-8), var(--n-6)) | |
Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument