Перейти к содержимому
EN
Масштаб
Плотность
Тема
Акцент

Сборки

Консоль прогонов

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

Компонент
nightly-4127 7 агентов · main 21 с
audio-pass 1 агент · fix/mixdown 4,2 с
biomes-04 3 агента · main 11,5 с
terrain-88 4 агента · main 18,2 с

nightly-4127

Враждебный аудит: поиск самообмана по классам A–F.

идёт
Модель opus
Агентов 7 из 12
Токенов 186 000
Фазы
разбор 7 агентов, 4 класса 38 200 · 18 с
{ "classes": ["docs-drift", "shared-and-chunk"], "depth": 2 }
опровержение 3 из 7 проверено 21 400 · идёт
14:32:07infoprobes-assert: гипотеза принята
14:32:11warnsilent-failure: нет воспроизведения, понижено
14:32:16infocoverage-hole: проверка запущена
сводка ждёт опровержения —
ожидание
Бюджет 186 000 из 250 000
При текущем темпе хватит ещё примерно на 3 прогона.
Разметка
<div class="inst-shell">
  <aside class="inst-shell-aside">
    <a class="inst-shell-brand" href="#"><span class="inst-dot" data-tone="running"></span>worldgen</a>
    <nav class="inst-nav" aria-label="Разделы">
      <div class="inst-nav-group">
        <span class="inst-nav-label">Работа</span>
        <a class="inst-nav-item" href="#" aria-current="page">Прогоны
          <span class="inst-badge inst-nav-count">4</span></a>
        <a class="inst-nav-item" href="#">Очередь</a>
        <a class="inst-nav-item" href="#">Артефакты</a>
      </div>
      <div class="inst-nav-group">
        <span class="inst-nav-label">Проект</span>
        <a class="inst-nav-item" href="#">Агенты</a>
        <a class="inst-nav-item" href="#">Ключи</a>
      </div>
    </nav>
  </aside>

  <header class="inst-shell-header">
    <nav aria-label="Хлебные крошки">
      <ol class="inst-crumbs">
        <li><a href="#">worldgen</a></li>
        <li><span aria-current="page">Прогоны</span></li>
      </ol>
    </nav>
    <span class="inst-cluster-spacer"></span>
    <span class="inst-search">
      <input class="inst-input inst-input--sm" type="search" placeholder="Поиск" aria-label="Поиск по прогонам">
    </span>
    <button class="inst-btn inst-btn--sm inst-btn--primary" type="button">Запустить</button>
  </header>

  <main class="inst-shell-main" id="main">
    <div class="inst-container inst-container--full">
      <div class="inst-split">

        <div class="inst-split-side inst-panel" data-rank="default">
          <div class="inst-toolbar">
            <div class="inst-segmented" role="radiogroup" aria-label="Фильтр по состоянию">
              <button type="button" role="radio" aria-checked="true"  tabindex="0">Все</button>
              <button type="button" role="radio" aria-checked="false" tabindex="-1">Идут</button>
              <button type="button" role="radio" aria-checked="false" tabindex="-1">Упало</button>
            </div>
          </div>
          <div class="inst-panel-body inst-panel-body--list">
            <div role="listbox" aria-label="Прогоны">
              <div class="inst-task" role="option" data-state="running" aria-selected="true" tabindex="0">
                <span class="inst-task-gutter"><span class="inst-dot"></span></span>
                <span class="inst-task-main">
                  <span class="inst-task-title inst-u-truncate">nightly-4127</span>
                  <span class="inst-task-sub">7 агентов · main</span>
                </span>
                <span class="inst-task-meta">21 с</span>
              </div>
              <div class="inst-task" role="option" data-state="failed" aria-selected="false" tabindex="-1">
                <span class="inst-task-gutter"><span class="inst-dot"></span></span>
                <span class="inst-task-main">
                  <span class="inst-task-title inst-u-truncate">audio-pass</span>
                  <span class="inst-task-sub">1 агент · fix/mixdown</span>
                </span>
                <span class="inst-task-meta">4,2 с</span>
              </div>
              <div class="inst-task" role="option" data-state="warn" aria-selected="false" tabindex="-1">
                <span class="inst-task-gutter"><span class="inst-dot"></span></span>
                <span class="inst-task-main">
                  <span class="inst-task-title inst-u-truncate">biomes-04</span>
                  <span class="inst-task-sub">3 агента · main</span>
                </span>
                <span class="inst-task-meta">11,5 с</span>
              </div>
              <div class="inst-task" role="option" data-state="done" aria-selected="false" tabindex="-1">
                <span class="inst-task-gutter"><span class="inst-dot"></span></span>
                <span class="inst-task-main">
                  <span class="inst-task-title inst-u-truncate">terrain-88</span>
                  <span class="inst-task-sub">4 агента · main</span>
                </span>
                <span class="inst-task-meta">18,2 с</span>
              </div>
            </div>
          </div>
        </div>

        <div class="inst-split-main inst-stack">

          <div class="inst-page-header">
            <div class="inst-page-header-main">
              <h3 class="inst-page-title">nightly-4127</h3>
              <p class="inst-page-desc">Враждебный аудит: поиск самообмана по классам A–F.</p>
            </div>
            <div class="inst-page-actions">
              <span class="inst-badge" data-tone="running"><span class="inst-dot"></span>идёт</span>
              <button class="inst-btn inst-btn--sm inst-btn--danger" type="button">Остановить</button>
            </div>
          </div>

          <div class="inst-props">
            <div class="inst-prop">
              <span class="inst-prop-label" title="Модель">Модель</span>
              <span class="inst-prop-control">opus</span>
            </div>
            <div class="inst-prop">
              <span class="inst-prop-label" title="Агентов">Агентов</span>
              <span class="inst-prop-control">7 из 12</span>
            </div>
            <div class="inst-prop">
              <span class="inst-prop-label" title="Токенов израсходовано">Токенов</span>
              <span class="inst-prop-control">186 000</span>
            </div>
          </div>

          <div class="inst-panel" data-rank="lead">
            <div class="inst-panel-header">
              <span class="inst-panel-title">Фазы</span>
              <span class="inst-panel-actions">
                <button class="inst-btn inst-btn--sm inst-btn--ghost" type="button" aria-expanded="false"
                        data-details-all data-label-expand="Раскрыть все"
                        data-label-collapse="Свернуть все">Раскрыть все</button>
              </span>
            </div>
            <div class="inst-panel-body inst-panel-body--list">
              <details class="inst-step" data-state="ok">
                <summary class="inst-step-head">
                  <span class="inst-step-twist"></span>
                  <span class="inst-dot"></span>
                  <span class="inst-step-name">разбор</span>
                  <span class="inst-step-sub inst-u-truncate">7 агентов, 4 класса</span>
                  <span class="inst-step-meta">38 200 · 18 с</span>
                </summary>
                <div class="inst-step-body">
                  <div class="inst-code">{ "classes": ["docs-drift", "shared-and-chunk"], "depth": 2 }</div>
                </div>
              </details>
              <details class="inst-step" data-state="running" open>
                <summary class="inst-step-head">
                  <span class="inst-step-twist"></span>
                  <span class="inst-dot"></span>
                  <span class="inst-step-name">опровержение</span>
                  <span class="inst-step-sub inst-u-truncate">3 из 7 проверено</span>
                  <span class="inst-step-meta">21 400 · идёт</span>
                </summary>
                <div class="inst-step-body">
                  <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>probes-assert: гипотеза принята</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>silent-failure: нет воспроизведения, понижено</span></div>
                    <div class="inst-log-line"><span class="inst-log-time">14:32:16</span><span class="inst-log-level">info</span><span>coverage-hole: проверка запущена</span></div>
                  </div>
                </div>
              </details>
              <details class="inst-step">
                <summary class="inst-step-head">
                  <span class="inst-step-twist"></span>
                  <span class="inst-dot"></span>
                  <span class="inst-step-name">сводка</span>
                  <span class="inst-step-sub inst-u-truncate">ждёт опровержения</span>
                  <span class="inst-step-meta">—</span>
                </summary>
                <div class="inst-step-body">
                  <div class="inst-code">ожидание</div>
                </div>
              </details>
            </div>
          </div>

          <div class="inst-budget">
            <div class="inst-budget-row">
              <span>Бюджет</span>
              <span class="inst-budget-value">186&nbsp;000 <span class="inst-budget-of">из 250&nbsp;000</span></span>
            </div>
            <div class="inst-meter" role="progressbar" aria-label="Расход бюджета"
                 aria-valuenow="74" aria-valuemin="0" aria-valuemax="100">
              <div class="inst-meter-fill" style="inline-size:74%"></div>
            </div>
            <div class="inst-budget-note">При текущем темпе хватит ещё примерно на 3 прогона.</div>
          </div>

        </div>
      </div>
    </div>
  </main>
</div>

Из чего собрано

Ни одного нового класса. Экран целиком — существующие компоненты, поставленные рядом.

Часть Чем собрана
Каркас Оболочка приложения: колонка, шапка, рабочая область
Две колонки Сплит. Переносится сам, без медиазапроса
Фильтр Тулбар с сегментированным контролом
Список Строка задачи в role="listbox": клавиатура от instrument.js
Название и действия Шапка экрана, бейдж, кнопка
Факты о прогоне Список свойств — inst-props
Ход работы Шаг со свёрткой и лог внутри
Остаток Бюджет прогона с мерой

Что здесь видно

Экран отвечает на «что сейчас», а не на «сколько всего». Ряда крупных чисел наверху нет: сводка за сутки — это отдельная задача и отдельный экран, а здесь человек смотрит на одну работу и хочет знать её состояние. Числа стоят там, где к ним возникает вопрос: расход — рядом с бюджетом, длительность — в строке прогона.

Фильтр живёт в строке, а не в панели. Три состояния — это сегментированный контрол на одну строку, а не выпадающий список и не боковая форма с кнопкой «Применить».

Выбор — это состояние списка, а не переход. role="listbox" плюс aria-selected: стрелки водят по прогонам, деталь справа меняется. Строка не ссылка, потому что никуда не ведёт.

Тон — один словарь. data-tone на точке бренда, бейдже прогона, строке лога и штрихе фазы означает одно и то же и красится из одного места.

Идущее пульсирует, а не подсвечено. Носитель состояния «идёт» — пульсация точки; тон здесь вторичен, и поэтому он же может означать интеракцию.

Состояния

Настоящий экран половину времени показывает не данные. Эти три случая — часть контракта, а не отдельная работа «на потом».

Пример
Под фильтр «упало» ничего не подошло
За последние сутки все прогоны завершились успешно.
Разметка
<div class="inst-panel">
  <div class="inst-empty">
    <div class="inst-empty-title">Под фильтр «упало» ничего не подошло</div>
    <div class="inst-empty-desc">За последние сутки все прогоны завершились успешно.</div>
    <button class="inst-btn inst-btn--sm" type="button">Сбросить фильтр</button>
  </div>
</div>

Пока список едет, место занимает скелетон — столько строк, сколько ожидается, и той же высоты:

Пример
Разметка
<div class="inst-panel">
  <div class="inst-panel-body inst-stack inst-stack--tight">
    <div class="inst-skeleton inst-skeleton--title"></div>
    <div class="inst-skeleton inst-skeleton--line"></div>
    <div class="inst-skeleton inst-skeleton--line inst-skeleton--short"></div>
  </div>
</div>

Отказ — это блок отказа, а не строка красного текста: у него есть причина, перечень попыток и хотя бы один выход.

Пример
Разметка
<div class="inst-failure" role="alert" data-state="pending">
  <div class="inst-failure-head">audio-pass не запустился</div>
  <div class="inst-failure-reason">EBUSY: не удалось прочитать <code>chunks.bin</code> — файл занят другим прогоном.</div>
  <ul class="inst-failure-tried">
    <li>Повтор через 1 с — тот же код</li>
    <li>Повтор через 4 с — тот же код</li>
  </ul>
  <div class="inst-failure-actions">
    <button class="inst-btn inst-btn--sm" type="button">Повторить</button>
    <button class="inst-btn inst-btn--sm inst-btn--ghost" type="button">Открыть лог</button>
  </div>
</div>

Параметры — шторкой, а не экраном

Настройки прогона не заслуживают собственного экрана с полосой «Отмена / Сохранить»: это тот же объект, только с другой стороны. Шторка открывается поверх, ничего не теряя из виду.

Пример
Параметры
на прогон
Изменения применяются к следующему прогону и сохраняются сразу.
Разметка
<button class="inst-btn" type="button"
        onclick="document.getElementById('con-params').showModal()">Параметры прогона</button>

<dialog class="inst-dialog inst-sheet" id="con-params">
  <form method="dialog">
    <div class="inst-dialog-head">
      <span class="inst-dialog-title">Параметры</span>
      <button class="inst-btn inst-btn--sm inst-btn--ghost inst-btn--icon inst-dialog-close" type="submit" aria-label="Закрыть">
        <svg class="inst-icon" aria-hidden="true"><use href="#i-close"/></svg>
      </button>
    </div>
    <div class="inst-dialog-body">
      <form class="inst-form inst-form--side">
        <div class="inst-field">
          <label class="inst-label" for="con-model">Модель</label>
          <span class="inst-select-wrap">
            <select class="inst-select" id="con-model"><option>opus</option><option>sonnet</option></select>
          </span>
        </div>
        <div class="inst-field">
          <label class="inst-label" for="con-limit">Лимит токенов</label>
          <div class="inst-input-group">
            <input class="inst-input" id="con-limit" value="250000">
            <span class="inst-input-affix">на прогон</span>
          </div>
        </div>
        <div class="inst-field">
          <label class="inst-label" for="con-par">Агентов сразу</label>
          <input class="inst-slider" type="range" min="1" max="16" value="12" id="con-par" style="--fill:73%">
        </div>
      </form>
      <div class="inst-note" data-tone="neutral">Изменения применяются к следующему прогону и сохраняются сразу.</div>
    </div>
  </form>
</dialog>

Полосы «Отмена / Сохранить» здесь нет. Она сообщает, что до нажатия ничего не произошло, тогда как свитч уже сработал, а поле уже отправило запрос. Изменения применяются сразу, и об этом сказано строкой под формой.