14 KiB
План: аудит верности preview/* против refs-html (DOM → CSS)
Статус: к выполнению. Ветка
feat/redesign. Источник правды по пикселям:refs-html/*.html(статический экспорт рефа). Актуальный рабочий аудит сейчас —pnpm refs-audit(scripts/audit.mjs), но он сломан (см. «Почему текущий аудит не работает») и подлежит замене.
Почему текущий аудит не работает
scripts/audit.mjs сопоставляет элементы только по тексту и берёт в
сравнение только листовые узлы (у которых нет детей). Контейнеры/карточки/
панели, несущие border-radius, отступы и позиционирование, в сравнение
вообще не попадают (byText кладёт в мапу только элементы с непустым
text). Следствия:
- разница радиусов у карточек невидима (карточка — родитель без своего текста);
- позиция кнопки (
Download result) не ловится — это структурная разница; onlyOurs / onlyRef— это просто счётчик текстовых строк на одной стороне, не метрика верности.
Плюс он не делает пиксельного сравнения, то есть принципиально не может сказать «выглядит по-другому». Это текстовый дифф стилей листовых текстов — и всё.
Решения (подтверждены автором)
- Копирайт превью — через i18n
t()с EN-базой. Сейчас RU-литералы хардкодом только вpreview/+page.svelte:26-27иpreview/list-tools/+page.svelte:31(эти страницы не используют i18n, в отличие от(old)/и компонентовDownloadButton/ChainToolBlock/…). Заводим их черезt()— совпадёт с рефом (EN). - Имена классов НЕ сводим к рефу. Матчинг делаем по структурному пути,
а не по классу. Компоненты
kit/остаются идиоматичными, Stage 0 не откатываем. Требуется только эквивалентность структуры (теги, вложенность, порядок сиблингов, наличие узлов, текст). - Пиксельный дифф (Playwright + pixelmatch) пока НЕ делаем. DOM+CSS-аудита достаточно; добавим как сеть позже, если стили что-то упустят.
- Порядок прогона маршрутов:
demo→list-tools→ затем 2 инструмента (tools/linear-gradient-png↔gradient.html,tools/remove-background-png↔background-remover.html).
Принципы
- Никаких правок старого дизайна
(old)/и старогоui/. - Коммиты атомарные (< ~500 строк), по 1–2 компонента/скрипта; после каждого —
lint+svelte-check+build+ соответствующий аудит (diff не должен расти без причины). - Коммиты делает автор (агент не коммитит).
- Шрифты уже совпадают:
design2.css:1-6грузит IBM Plex Sans/Mono через@fontsource, токены:34-35—"IBM Plex Sans"/"IBM Plex Mono"(как в рефеdemo.html:67-68). Пиксельный шум от шрифтов не предвидится.
Подход: две фазы
Корень проблемы — нет стабильного ключа сопоставления. Разбиваем на две фазы: сначала доводим DOM до структурного равенства с рефом (фаза A), затем на стабильном DOM сравниваем стили по тому же пути (фаза B). Фаза A сама по себе ловит именно то, из-за чего возникли претензии (кнопка не в том родителе, лишние/недостающие обёртки, перепутанный порядок) — и без эстетических решений.
Фаза A — DOM/структурный аудит (scripts/audit-dom.mjs)
Цель: Svelte рендерит структуру, идентичную refs-html (дерево тегов,
вложенность, порядок сиблингов, наличие узлов, текст). Имена классов могут
отличаться.
Нормализация узла (ключ сравнения)
Для каждого элемента (пропускаем SCRIPT/STYLE/NOSCRIPT и скрытые
поддеревья, игнорируем служебное):
- тег (
tagNameв lower-case); - классы — как отсортированный набор (для инфо, не для матчинга);
- нормализованный текст —
trim,collapse-whitespace, без SVG-иконок внутри (текст иконок игнорируем); - порядок детей — позиция среди сиблингов (
nth-child).
Матчинг — по структурному пути от корня сравнения:
tag:nth-child(i) > tag:nth-child(j) …. Не по классу и не по тексту.
Полное соответствие, без IGNORE
Оболочка (наш TopBar/AppShell/preview-footer vs рефовый
.topbar/.footer/.language) тоже сводим к рефу — IGNORE-список не
заводим. Сравниваем всё дерево от <body> (без пропуска служебного, кроме
Svelte-атрибутов и комментов гидрации), чтобы получить полное структурное
соответствие, а не только контента. Шапка/футер/переключатель языка — часть
верности; позже, после полного фикса дизайна, всё равно планируется рефакторинг
компонентов, где оболочку приведём в порядок и унифицируем. Аудит на этом этапе
просто показывает все расхождения, включая оболочечные.
Дифф
Для каждого маршрута — набор дельт:
added— у нас, нет в рефе;removed— в рефе, нет у нас;moved— тот же текст/тег, другой путь (перепутанный порядок/родитель);textMismatch— нормализованный текст различается (в т.ч. RU vs EN).
Класс-мисматч — только информационно, не блокирует (классы свои по
решению №2). Счётчики: added / removed / moved / textMismatch.
Рендеринг
- наше: dev-сервер на
5179(как сейчас вaudit.mjs). - реф:
pathToFileURLкrefs-html/<ref>.html(file://). Реф — самодостаточный (инлайн<style>), рендерится полностью. Шрифты для фазы A не важны (сравниваем структуру/текст, не растеризацию). - Переиспользуем из текущего
audit.mjs:discoverRoutes(),EXCLUDE(/preview),DYNAMIC(tools/[id]→gradient.html/background-remover.html),refNameFor().
Шаги (на каждом маршруте)
- Прогнать
pnpm refs-dom-audit, получить дельты. - Поправить структуру Svelte под реф (добавить/убрать/переставить узлы,
вынести тексты в
t()с EN-базой). - Перезапустить, пока
added/removed/moved/textMismatch≃ 0 (кромеIGNORE).
Фаза B — CSS-аудит по стабильному DOM (scripts/audit-css.mjs)
Цель: на совпадающем дереве сравнить вычисленные стили по тому же структурному пути и получить «элемент по пути X: свойство → наше/реф».
Сравниваемые свойства (computed)
borderRadius, позиция/размер (getBoundingClientRect: x/y/w/h),
padding (все стороны), margin, color, backgroundColor,
borderTop/Right/Bottom/Left Width|Color, fontFamily, fontSize,
fontWeight, letterSpacing, lineHeight, gap, display, boxShadow.
Дифф
Для каждого состыкованного по пути элемента — список свойств с дельтой
(наше значение → реф). Элементы, не совпавшие по пути (остаток из фазы A),
идут в unmatched. Отчёт: элемент → свойство → наше/реф. Правится одной
строкой CSS, без брутфорса.
Шаги
- Прогнать
pnpm refs-css-audit(только после того, как фаза A по маршруту дала ≃ 0 структурных расхождений). - Поправить стили в
design2.css/ стилях компонентов. - Перезапустить, пока delta% по свойствам ≃ 0.
Порядок маршрутов и особенности
/preview/demo(↔demo.html) — уже ближе всего; идеальная «обкатка» тулинга фаз A и B. Здесь же проверим, чтоDownload resultстоит в шапке.preview-top(как в рефе), а не внизу панели./preview/list-tools(↔list-tools.html) — объёмнее; зависит отdocs/plan-list-tools.md: реальный аудит имеет смысл только после того, как страница переписана наCatalogHeader/CatalogToolbar/CatalogGroup(шаги 1–5 плана list-tools). То есть фаза A/B для list-tools стартует после landing этих шагов.tools/linear-gradient-png(↔gradient.html) иtools/remove-background-png(↔background-remover.html) — последние. Известные расхождения: наши инструменты могут иметь другие наборы параметров / UI, чем конкретные рефовые тулзы. Ожидаются структурные и стилевые дельты, которые не сводятся к «подровнять паддинг». Для них — триаж после прогона:- реальный разрыв верности → фиксим;
- принципиальное отличие (параметр, которого нет в реф-тулзе; иной контрол) → либо аккуратно приводим наш UI параметра к рефу, либо документируем как допустимое расхождение / заносим в allowlist конфига аудита (селектор + причина). «Скипы/фиксы» для инструментов оформляем явно, чтобы аудит не шумел на осознанных отличиях.
Структура скриптов
scripts/audit-dom.mjs— фаза A (нормализация + структурный дифф).scripts/audit-css.mjs— фаза B (матчинг по пути + дифф стилей).package.json:refs-dom-audit,refs-css-audit(опциональноrefs-audit, прогоняющий обе фазы).- Переиспользуем из старого
audit.mjs:discoverRoutes,EXCLUDE,DYNAMIC,refNameFor,waitFor(fast-fail на 4xx/5xx). - Отчёты:
web/audit/dom-report.{md,json}иweb/audit/css-report.{md,json}(старыйaudit-report.*заменяем).
Верификация (после каждого коммита)
pnpm lint,pnpm exec svelte-check --tsconfig ./tsconfig.json,pnpm build— без ошибок.pnpm refs-dom-audit(фаза A) /pnpm refs-css-audit(фаза B) по затронутому маршруту: счётчики расхождений не растут без причины.
Риски / заметки
- Стабильность матчинга: нужна аккуратная нормализация (сортировка
классов, игнор svelte-атрибутов/
data-*, игнор whitespace-текстовых узлов, игнор внутренностей SVG-иконок). Подстраивается по первым прогонам demo. - Оболочка (TopBar/footer/язык) сейчас отличается по дизайну — сводим к рефу в рамках полного соответствия (без IGNORE); финальная унификация компонентов — отдельным рефакторингом позже.
- Динамические маршруты:
DYNAMICуже есть; оставляем. - Hover / адаптив / тёмная тема — вне объёма статического аудита (initial state, 1440×900, light). Отмечаем как будущие прогоны.
- list-tools зависит от landing
plan-list-tools.md(шаги 1–5) — не аудируем вслепую.