From bcdfc4cf123d2891ebfea3e240087c08b2ecb39c Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Thu, 27 Aug 2026 23:52:32 +0500 Subject: [PATCH] chore: add audit script for compare with refs --- docs/demo-fidelity.md | 84 ++ web/audit/audit-report.json | 1789 +++++++++++++++++++++++++++++++++++ web/audit/audit-report.md | 377 ++++++++ web/package.json | 4 +- web/pnpm-lock.yaml | 29 + web/scripts/audit.mjs | 271 ++++++ 6 files changed, 2553 insertions(+), 1 deletion(-) create mode 100644 docs/demo-fidelity.md create mode 100644 web/audit/audit-report.json create mode 100644 web/audit/audit-report.md create mode 100644 web/scripts/audit.mjs diff --git a/docs/demo-fidelity.md b/docs/demo-fidelity.md new file mode 100644 index 0000000..0a71e64 --- /dev/null +++ b/docs/demo-fidelity.md @@ -0,0 +1,84 @@ +# Demo fidelity: /preview/demo vs refs-html/demo.html + +Метод: Playwright, viewport 1440×900, скриншоты обеих страниц + замер вычисленных +стилей ключевых элементов + пиксель-дифф (без чтения картинки моделью). +Пиксель-дифф: **24.9%** (наш скролл 1731px, реф 1579px). Зоны различий размазаны +по всей высоте → править надо глобально, не точечно. + +Теперь процесс автоматизирован: `pnpm audit` (Playwright) пишет +`web/audit/audit-report.md` — light/dark токены + поэлементный дифф стилей по тексту. +Пиксель-дифф убран (неудобен для работы). Фиксы можно делать в любой момент и +перепроверять аудитом. + +## Баги (объективно, правим в любом случае) + +- **B1. Кнопки/инпуты рендерятся шрифтом Arial, а не IBM Plex Sans.** + Доказательство: пробы `Reset pipeline` → `fontFamily: Arial`. Причина: у `button/input` + не задан `font-family: inherit`. Фикс: добавить в reset `design2.css` + `button, input, select, textarea { font-family: inherit; }`. Влияет на все страницы. (P1) +- **B2. Двойной топбар.** Страница демо рендерит свой `.topbar` (brand / AUTO PIPELINE / + help / theme / lang), который перекрывается глобальным `fixed` Header из + `preview/+layout.svelte` (в нём только theme-toggle). В рефе — один топбар. + Контент демо-топбара (brand, статус, язык) визуально спрятан за фикс-хедером. + Фикс: убрать кастомный `.topbar` из демо и сделать хедер лейаута реф-стайл топбаром + (brand + version + status + help + theme + lang) — улучшит и C9/C10. (P1) + +## Расхождения дизайна (нужно решение) + +- **F1. Фон холста.** Реф: плоский серый `#EEF1F4`. Наш: белый + blueprint-сетка. + Решение: совпасть с рефом (убрать сетку на демо) или оставить сетку как сигнатуру нового дизайна. +- **F2. Бейдж типа шага** (BACKGROUND/TRANSFORM/STYLE). Реф: приглушённый серый `#6D787F`. + Наш (`StepCard` type → `Badge accent`): синий `#1769D2`. Решение: сделать приглушённым + (меняет `StepCard` глобально) или оставить синим. +- **F3. Подписи превью-тайлов** (SOURCE / STEP 01 / FINAL OUTPUT). Реф: синий `#1769D2`, + weight 400. Наш (`PreviewTile` label): серый, weight 500. +- **F4. Раскладка preview-stack.** Реф: одна колонка (5 строк, выше). Наш: 2 колонки (grid). + Решение: одна колонка (как реф) или оставить 2 колонки как выбор нового дизайна. +- **F5. Вес лейблов контролов.** Реф: 400. Наш (`MonoLabel`): 500. Мелко. +- **F6. Мета-подписи** (DIMENSIONS/FORMAT/SIZE): реф fs 9px, наш 10px. Мелко. +- **F7. Копирайт:** `Add tool` у нас `+ Add tool`; кнопка `Download result` — сверить стиль + (в рефе синяя с белым текстом 478×42). Пробы `Download result`/`Add tool`/`DIRECTION` + вернули NOT FOUND — скорее артефакт замера (вложенный текст/чип размера), проверить визуально. +- **F8. h1 line-height** чуть выше (наш 150px vs реф 115px) — мелкий зазор. +- **F9. Вертикальный ритм:** контент на ~152px выше, зазоры между секциями отличаются + (pipeline-head y 434 vs 400 и т.д.) — подровнять паддинги под реф. +- **F10. Внутренние отступы preview-панели** (Hide intermediate x 907 vs 1218) — паддинг панели. + +## Темы (отложено — F1-F4) + +Серый фон `#EEF1F4` — это **светлая** тема. Уточнение по F1: наш токен `--background` +уже равен `#EEF1F4` (совпадает с рефом); видимое отличие — только blueprint-сетка +поверх фона, а не «белый vs серый». То есть F1 = «сетка против плоского», не цвет фона. + +Перед тем как сверять цвета бейджей/тайлов/раскладок (F2-F4), нужно сначала +зафиксировать палитры light/dark как единый источник токенов. Сейчас они есть в +`web/src/lib/styles/design2.css` (`:root` + `[data-theme="dark"]`), но не сверены с рефом. + +**Как проверить темы в refs-html:** + +- Открыть `refs-html/demo.html` (и соседние `gradient.html`, `background-remover.html`) + через Playwright/браузер. +- Нажать theme-toggle в топбаре рефа (иконка луны/солнца) — переключает тему + (скорее всего меняет атрибут/класс на `` или `.page` и переопределяет CSS-переменные). +- Снять скриншоты светлой и тёмной темы, замерить computed-стиль `:root` переменных + рефа (через `getComputedStyle(document.documentElement).getPropertyValue('--...')`) + и сопоставить с нашими токенами в `design2.css`. +- Рефы, похоже, не доделаны по тёмной теме — зафиксировать, какие именно токены + меняются и где расхождения. На основе этого выровнять наши токены, затем вернуться + к F2 (бейдж типа), F3 (тайлы), F4 (раскладка preview-stack). + +Решение по F1-F4 принимается только после сверки токенов светлой/тёмной темы с рефом. + +**Состояние тем (уточнение):** наша dark-тема реализована в `design2.css` +(`[data-theme="dark"]`), но пока подключена только на preview-маршрутах (старые +страницы грузят `app.css`). Этого достаточно для сверки: токены, отличные от light, +соберём по всем `refs-html/*.html` (не только `demo.html`), остальные токены +не меняются. Light-токены уже совпадают с рефом (`--background`, `--panel`, +`--foreground`, `--muted`, `--line`, `--blue` равны). + +## План действий (предлагаю) + +1. Сразу поправить B1 + B2 (чёткие баги, улучшают все страницы). +2. По F1–F4 — решение за тобой (совпадать с рефом дословно ИЛИ оставить как сигнатуру нового дизайна). +3. F5–F10 — мелочи, подчистить в том же проходе, если решаем «совпадать с рефом». +4. Переснять скриншоты и перепроверить пиксель-дифф. diff --git a/web/audit/audit-report.json b/web/audit/audit-report.json new file mode 100644 index 0000000..a3a1562 --- /dev/null +++ b/web/audit/audit-report.json @@ -0,0 +1,1789 @@ +{ + "generatedAt": "2026-08-27T18:45:14.461Z", + "ours": "http://127.0.0.1:5179/preview/demo", + "ref": "C:\\+XBOCTuK\\+life_projects\\easy-png-tools\\refs-html\\demo.html", + "tokenDiffs": { + "light": [ + { + "key": "--danger", + "ours": "#e5484d", + "ref": "—" + }, + { + "key": "--success", + "ours": "#25a96a", + "ref": "—" + }, + { + "key": "--font-sans", + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Sans\", sans-serif" + }, + { + "key": "--font-mono", + "ours": "\"IBM Plex Mono\", ui-monospace, \"SFMono-Regular\", monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + { + "key": "--text-sm--line-height", + "ours": "—", + "ref": "calc(1.25 / .875)" + }, + { + "key": "--text-xs--line-height", + "ours": "—", + "ref": "calc(1 / .75)" + }, + { + "key": "--font-weight-medium", + "ours": "—", + "ref": "500" + }, + { + "key": "--spacing", + "ours": "—", + "ref": ".25rem" + }, + { + "key": "--text-xs", + "ours": "—", + "ref": ".75rem" + }, + { + "key": "--radius-md", + "ours": "—", + "ref": ".375rem" + }, + { + "key": "--radius-lg", + "ours": "—", + "ref": ".5rem" + }, + { + "key": "--text-sm", + "ours": "—", + "ref": ".875rem" + } + ], + "dark": [ + { + "key": "--panel", + "ours": "#182129", + "ref": "#f8fafb" + }, + { + "key": "--blue", + "ours": "#54a2ff", + "ref": "#1769d2" + }, + { + "key": "--muted", + "ours": "#91a0ac", + "ref": "#6d7883" + }, + { + "key": "--background", + "ours": "#11171d", + "ref": "#eef1f4" + }, + { + "key": "--danger", + "ours": "#ff7479", + "ref": "—" + }, + { + "key": "--foreground", + "ours": "#e8eef2", + "ref": "#17212b" + }, + { + "key": "--success", + "ours": "#25a96a", + "ref": "—" + }, + { + "key": "--line", + "ours": "#33414c", + "ref": "#cbd3da" + }, + { + "key": "--font-sans", + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Sans\", sans-serif" + }, + { + "key": "--font-mono", + "ours": "\"IBM Plex Mono\", ui-monospace, \"SFMono-Regular\", monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + { + "key": "--text-sm--line-height", + "ours": "—", + "ref": "calc(1.25 / .875)" + }, + { + "key": "--text-xs--line-height", + "ours": "—", + "ref": "calc(1 / .75)" + }, + { + "key": "--font-weight-medium", + "ours": "—", + "ref": "500" + }, + { + "key": "--spacing", + "ours": "—", + "ref": ".25rem" + }, + { + "key": "--text-xs", + "ours": "—", + "ref": ".75rem" + }, + { + "key": "--radius-md", + "ours": "—", + "ref": ".375rem" + }, + { + "key": "--radius-lg", + "ours": "—", + "ref": ".5rem" + }, + { + "key": "--text-sm", + "ours": "—", + "ref": ".875rem" + } + ] + }, + "elementDiffs": [ + { + "path": "body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "EP", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(1)>span:nth-child(2)", + "tag": "span", + "text": "easy-png-tools", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(0, 0, 0)", + "ref": "rgb(23, 33, 43)" + }, + "rect": { + "ours": { + "x": 98, + "y": 23, + "w": 118, + "h": 18 + }, + "ref": { + "x": 98, + "y": 23, + "w": 108, + "h": 17 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(2)>div:nth-child(4)>button:nth-child(1)", + "tag": "button", + "text": "RU", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "11px", + "ref": "10px" + }, + "color": { + "ours": "rgb(255, 255, 255)", + "ref": "rgb(238, 241, 244)" + }, + "backgroundColor": { + "ours": "rgb(23, 105, 210)", + "ref": "rgb(23, 33, 43)" + }, + "padding": { + "ours": "4.8px 11.2px", + "ref": "6px 9px" + }, + "letterSpacing": { + "ours": "0.66px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1307, + "y": 20, + "w": 37, + "h": 24 + }, + "ref": { + "x": 1323, + "y": 20, + "w": 29, + "h": 24 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(2)>div:nth-child(4)>button:nth-child(2)", + "tag": "button", + "text": "EN", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "11px", + "ref": "10px" + }, + "padding": { + "ours": "4.8px 11.2px", + "ref": "6px 9px" + }, + "letterSpacing": { + "ours": "0.66px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1344, + "y": 20, + "w": 38, + "h": 24 + }, + "ref": { + "x": 1352, + "y": 20, + "w": 29, + "h": 24 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "/", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(23, 105, 210)", + "ref": "rgb(109, 120, 131)" + }, + "rect": { + "ours": { + "x": 173, + "y": 124, + "w": 7, + "h": 13 + }, + "ref": { + "x": 165, + "y": 124, + "w": 13, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(1)>h1:nth-child(1)", + "tag": "h1", + "text": "Build your image pipeline.", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Sans\", sans-serif" + }, + "lineHeight": { + "ours": "normal", + "ref": "57.6px" + }, + "rect": { + "ours": { + "x": 58, + "y": 155, + "w": 474, + "h": 150 + }, + "ref": { + "x": 58, + "y": 154, + "w": 482, + "h": 115 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(1)>p:nth-child(2)", + "tag": "p", + "text": "Chain simple tools together. Every chang", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Sans\", sans-serif" + }, + "rect": { + "ours": { + "x": 58, + "y": 321, + "w": 474, + "h": 51 + }, + "ref": { + "x": 58, + "y": 285, + "w": 482, + "h": 51 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "source.png", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 597, + "y": 347, + "w": 66, + "h": 14 + }, + "ref": { + "x": 606, + "y": 311, + "w": 60, + "h": 13 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(2)>b:nth-child(3)", + "tag": "b", + "text": "1.8 MB", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 671, + "y": 347, + "w": 40, + "h": 14 + }, + "ref": { + "x": 674, + "y": 311, + "w": 36, + "h": 13 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(3)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "PROCESSING PIPELINE", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 58, + "y": 434, + "w": 137, + "h": 13 + }, + "ref": { + "x": 58, + "y": 400, + "w": 127, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(3)>div:nth-child(1)>strong:nth-child(2)>em:nth-child(1)", + "tag": "em", + "text": "• LIVE", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 184, + "y": 457, + "w": 36, + "h": 13 + }, + "ref": { + "x": 173, + "y": 425, + "w": 33, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(1)>span:nth-child(2)", + "tag": "span", + "text": "01", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "padding": { + "ours": "0px", + "ref": "17px 0px 0px 15px" + }, + "rect": { + "ours": { + "x": 95, + "y": 510, + "w": 13, + "h": 14 + }, + "ref": { + "x": 59, + "y": 466, + "w": 46, + "h": 150 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(1)>span:nth-child(3)", + "tag": "span", + "text": "BACKGROUND", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(23, 105, 210)", + "ref": "rgb(109, 120, 131)" + }, + "padding": { + "ours": "2.4px 7.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.8px", + "ref": "1.2px" + }, + "borderRadius": { + "ours": "4px", + "ref": "0px" + }, + "display": { + "ours": "flex", + "ref": "inline" + }, + "gap": { + "ours": "4.8px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 116, + "y": 507, + "w": 84, + "h": 20 + }, + "ref": { + "x": 143, + "y": 489, + "w": 67, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(1)>span:nth-child(4)", + "tag": "span", + "text": "Gradient background", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "14.4px", + "ref": "15px" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 18px" + }, + "rect": { + "ours": { + "x": 208, + "y": 508, + "w": 467, + "h": 19 + }, + "ref": { + "x": 143, + "y": 510, + "w": 157, + "h": 18 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "COLOR", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontWeight": { + "ours": "500", + "ref": "400" + }, + "margin": { + "ours": "0px", + "ref": "0px 0px 8px" + }, + "letterSpacing": { + "ours": "1px", + "ref": "0.8px" + }, + "lineHeight": { + "ours": "12px", + "ref": "normal" + }, + "display": { + "ours": "block", + "ref": "flex" + }, + "rect": { + "ours": { + "x": 71, + "y": 576, + "w": 203, + "h": 12 + }, + "ref": { + "x": 143, + "y": 546, + "w": 189, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(1)", + "tag": "button", + "text": "100", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "11px", + "ref": "10px" + }, + "padding": { + "ours": "4.8px 11.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.66px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 491, + "y": 597, + "w": 44, + "h": 24 + }, + "ref": { + "x": 520, + "y": 567, + "w": 46, + "h": 30 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(2)", + "tag": "button", + "text": "75", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "11px", + "ref": "10px" + }, + "padding": { + "ours": "4.8px 11.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.66px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 535, + "y": 597, + "w": 38, + "h": 24 + }, + "ref": { + "x": 566, + "y": 567, + "w": 46, + "h": 30 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(3)", + "tag": "button", + "text": "50", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "11px", + "ref": "10px" + }, + "padding": { + "ours": "4.8px 11.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.66px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 573, + "y": 597, + "w": 38, + "h": 24 + }, + "ref": { + "x": 612, + "y": 567, + "w": 46, + "h": 30 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(4)", + "tag": "button", + "text": "25", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "11px", + "ref": "10px" + }, + "padding": { + "ours": "4.8px 11.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.66px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 611, + "y": 597, + "w": 38, + "h": 24 + }, + "ref": { + "x": 658, + "y": 567, + "w": 46, + "h": 30 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(2)>div:nth-child(1)>span:nth-child(2)", + "tag": "span", + "text": "02", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "padding": { + "ours": "0px", + "ref": "17px 0px 0px 15px" + }, + "rect": { + "ours": { + "x": 95, + "y": 673, + "w": 13, + "h": 14 + }, + "ref": { + "x": 59, + "y": 628, + "w": 46, + "h": 136 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(2)>div:nth-child(1)>span:nth-child(3)", + "tag": "span", + "text": "TRANSFORM", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(23, 105, 210)", + "ref": "rgb(109, 120, 131)" + }, + "padding": { + "ours": "2.4px 7.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.8px", + "ref": "1.2px" + }, + "borderRadius": { + "ours": "4px", + "ref": "0px" + }, + "display": { + "ours": "flex", + "ref": "inline" + }, + "gap": { + "ours": "4.8px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 116, + "y": 670, + "w": 78, + "h": 20 + }, + "ref": { + "x": 143, + "y": 651, + "w": 60, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(2)>div:nth-child(1)>span:nth-child(4)", + "tag": "span", + "text": "Remove background", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "14.4px", + "ref": "15px" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 18px" + }, + "rect": { + "ours": { + "x": 201, + "y": 671, + "w": 473, + "h": 19 + }, + "ref": { + "x": 143, + "y": 672, + "w": 140, + "h": 18 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(3)>div:nth-child(1)>span:nth-child(2)", + "tag": "span", + "text": "03", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "padding": { + "ours": "0px", + "ref": "17px 0px 0px 15px" + }, + "rect": { + "ours": { + "x": 95, + "y": 788, + "w": 13, + "h": 14 + }, + "ref": { + "x": 59, + "y": 776, + "w": 46, + "h": 150 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(3)>div:nth-child(1)>span:nth-child(3)", + "tag": "span", + "text": "STYLE", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(23, 105, 210)", + "ref": "rgb(109, 120, 131)" + }, + "padding": { + "ours": "2.4px 7.2px", + "ref": "0px" + }, + "letterSpacing": { + "ours": "0.8px", + "ref": "1.2px" + }, + "borderRadius": { + "ours": "4px", + "ref": "0px" + }, + "display": { + "ours": "flex", + "ref": "inline" + }, + "gap": { + "ours": "4.8px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 116, + "y": 785, + "w": 50, + "h": 20 + }, + "ref": { + "x": 143, + "y": 799, + "w": 34, + "h": 12 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(3)>div:nth-child(1)>span:nth-child(4)", + "tag": "span", + "text": "Add outline", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "14.4px", + "ref": "15px" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 18px" + }, + "rect": { + "ours": { + "x": 174, + "y": 785, + "w": 501, + "h": 19 + }, + "ref": { + "x": 143, + "y": 820, + "w": 91, + "h": 18 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(4)>div:nth-child(1)>span:nth-child(2)", + "tag": "span", + "text": "04", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "padding": { + "ours": "0px", + "ref": "17px 0px 0px 15px" + }, + "rect": { + "ours": { + "x": 95, + "y": 951, + "w": 13, + "h": 14 + }, + "ref": { + "x": 59, + "y": 938, + "w": 46, + "h": 142 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(4)>div:nth-child(1)>span:nth-child(4)", + "tag": "span", + "text": "Round corners", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "14.4px", + "ref": "15px" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 18px" + }, + "rect": { + "ours": { + "x": 174, + "y": 948, + "w": 501, + "h": 19 + }, + "ref": { + "x": 143, + "y": 982, + "w": 107, + "h": 18 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(4)>div:nth-child(2)>div:nth-child(2)>label:nth-child(2)>span:nth-child(1)", + "tag": "span", + "text": "Preserve aspect ratio", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Sans\", sans-serif" + }, + "fontSize": { + "ours": "13.6px", + "ref": "16px" + }, + "lineHeight": { + "ours": "normal", + "ref": "24px" + }, + "display": { + "ours": "block", + "ref": "inline" + }, + "rect": { + "ours": { + "x": 399, + "y": 1036, + "w": 129, + "h": 18 + }, + "ref": { + "x": 432, + "y": 1021, + "w": 156, + "h": 17 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "PIPELINE OUTPUTS", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 799, + "y": 177, + "w": 58, + "h": 33 + }, + "ref": { + "x": 799, + "y": 188, + "w": 54, + "h": 36 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(1)>strong:nth-child(2)", + "tag": "strong", + "text": "Visual history", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 799, + "y": 216, + "w": 93, + "h": 36 + }, + "ref": { + "x": 799, + "y": 233, + "w": 87, + "h": 34 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>button:nth-child(2)>span:nth-child(2)>span:nth-child(1)", + "tag": "span", + "text": "1.2 MB", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "11px" + }, + "color": { + "ours": "rgb(255, 255, 255)", + "ref": "rgb(23, 33, 43)" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 0px" + }, + "display": { + "ours": "inline", + "ref": "block" + }, + "rect": { + "ours": { + "x": 1090, + "y": 240, + "w": 36, + "h": 13 + }, + "ref": { + "x": 1246, + "y": 291, + "w": 79, + "h": 13 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "DIMENSIONS", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1150, + "y": 215, + "w": 70, + "h": 13 + }, + "ref": { + "x": 1073, + "y": 271, + "w": 79, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(1)>span:nth-child(2)", + "tag": "span", + "text": "1200 × 800 px", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 0px" + }, + "rect": { + "ours": { + "x": 1236, + "y": 214, + "w": 86, + "h": 14 + }, + "ref": { + "x": 1073, + "y": 291, + "w": 79, + "h": 13 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>span:nth-child(1)", + "tag": "span", + "text": "FORMAT", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1150, + "y": 239, + "w": 42, + "h": 13 + }, + "ref": { + "x": 1159, + "y": 271, + "w": 79, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "PNG-24", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "margin": { + "ours": "0px", + "ref": "5px 0px 0px" + }, + "rect": { + "ours": { + "x": 1282, + "y": 238, + "w": 40, + "h": 14 + }, + "ref": { + "x": 1159, + "y": 291, + "w": 79, + "h": 13 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(3)>span:nth-child(1)", + "tag": "span", + "text": "SIZE", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1150, + "y": 264, + "w": 28, + "h": 13 + }, + "ref": { + "x": 1246, + "y": 271, + "w": 79, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "SOURCE", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "fontWeight": { + "ours": "500", + "ref": "400" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "lineHeight": { + "ours": "12px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 806, + "y": 320, + "w": 262, + "h": 12 + }, + "ref": { + "x": 808, + "y": 663, + "w": 30, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(1)>div:nth-child(1)>span:nth-child(1)", + "tag": "span", + "text": "PNG", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 923, + "y": 498, + "w": 28, + "h": 19 + }, + "ref": { + "x": 923, + "y": 478, + "w": 27, + "h": 18 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "original.png · 1200 × 800", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 925, + "y": 706, + "w": 135, + "h": 11 + }, + "ref": { + "x": 942, + "y": 663, + "w": 124, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(2)>span:nth-child(1)", + "tag": "span", + "text": "STEP 01", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "fontWeight": { + "ours": "500", + "ref": "400" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "lineHeight": { + "ours": "12px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1094, + "y": 320, + "w": 262, + "h": 12 + }, + "ref": { + "x": 1096, + "y": 663, + "w": 35, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(2)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "gradient applied", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 1262, + "y": 706, + "w": 86, + "h": 11 + }, + "ref": { + "x": 1275, + "y": 663, + "w": 79, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(3)>span:nth-child(1)", + "tag": "span", + "text": "STEP 02", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "fontWeight": { + "ours": "500", + "ref": "400" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "lineHeight": { + "ours": "12px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 806, + "y": 752, + "w": 262, + "h": 12 + }, + "ref": { + "x": 808, + "y": 1021, + "w": 35, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "background removed", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 962, + "y": 1138, + "w": 97, + "h": 11 + }, + "ref": { + "x": 977, + "y": 1021, + "w": 89, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(4)>span:nth-child(1)", + "tag": "span", + "text": "STEP 03", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "fontWeight": { + "ours": "500", + "ref": "400" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "lineHeight": { + "ours": "12px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 1094, + "y": 752, + "w": 262, + "h": 12 + }, + "ref": { + "x": 1096, + "y": 1021, + "w": 35, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(4)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "outline added", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 1278, + "y": 1138, + "w": 70, + "h": 11 + }, + "ref": { + "x": 1290, + "y": 1021, + "w": 64, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(5)>span:nth-child(1)", + "tag": "span", + "text": "FINAL OUTPUT", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "fontSize": { + "ours": "10px", + "ref": "9px" + }, + "fontWeight": { + "ours": "500", + "ref": "400" + }, + "color": { + "ours": "rgb(109, 120, 131)", + "ref": "rgb(23, 105, 210)" + }, + "letterSpacing": { + "ours": "1px", + "ref": "normal" + }, + "lineHeight": { + "ours": "12px", + "ref": "normal" + }, + "rect": { + "ours": { + "x": 806, + "y": 1183, + "w": 262, + "h": 12 + }, + "ref": { + "x": 808, + "y": 1378, + "w": 59, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(5)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)", + "tag": "span", + "text": "ready · PNG-24", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Mono\", ui-monospace, SFMono-Regular, monospace", + "ref": "\"IBM Plex Mono\", monospace" + }, + "rect": { + "ours": { + "x": 984, + "y": 1569, + "w": 76, + "h": 11 + }, + "ref": { + "x": 997, + "y": 1378, + "w": 69, + "h": 10 + } + } + } + }, + { + "path": "body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>p:nth-child(3)", + "tag": "p", + "text": "Output is generated in-browser. Your fil", + "deltas": { + "fontFamily": { + "ours": "\"IBM Plex Sans\", system-ui, -apple-system, sans-serif", + "ref": "\"IBM Plex Sans\", sans-serif" + }, + "rect": { + "ours": { + "x": 799, + "y": 1624, + "w": 565, + "h": 17 + }, + "ref": { + "x": 799, + "y": 1425, + "w": 565, + "h": 17 + } + } + } + } + ], + "onlyOurs": [ + "+ Add tool", + "AUTO", + "Gradient color", + "DIRECTION", + "135°", + "OPACITY", + "Automatic subject detection enabled", + "WIDTH", + "Outline width", + "2px", + "Outline color", + "RADIUS", + "Corner radius", + "18px" + ], + "onlyRef": [ + "/ DEMO", + "135\n °", + "100%", + "2\n px", + "18\n px", + "v2.4.0", + "© 2024" + ], + "counts": { + "tokenLight": 12, + "tokenDark": 18, + "elements": 48, + "onlyOurs": 14, + "onlyRef": 7 + } +} \ No newline at end of file diff --git a/web/audit/audit-report.md b/web/audit/audit-report.md new file mode 100644 index 0000000..6008a7b --- /dev/null +++ b/web/audit/audit-report.md @@ -0,0 +1,377 @@ +# Style audit: http://127.0.0.1:5179/preview/demo + +vs C:\+XBOCTuK\+life_projects\easy-png-tools\refs-html\demo.html + +_2026-08-27T18:45:14.461Z_ + +## Сводка + +- Токены light: **12** расх. +- Токены dark: **18** расх. +- Элементы (стиль/геометрия): **48** расх. +- Только у нас: 14, только в рефе: 7 + +## Токены — Light + +| token | ours | ref | +|---|---|---| +| `--danger` | #e5484d | — | +| `--success` | #25a96a | — | +| `--font-sans` | "IBM Plex Sans", system-ui, -apple-system, sans-serif | "IBM Plex Sans", sans-serif | +| `--font-mono` | "IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace | "IBM Plex Mono", monospace | +| `--text-sm--line-height` | — | calc(1.25 / .875) | +| `--text-xs--line-height` | — | calc(1 / .75) | +| `--font-weight-medium` | — | 500 | +| `--spacing` | — | .25rem | +| `--text-xs` | — | .75rem | +| `--radius-md` | — | .375rem | +| `--radius-lg` | — | .5rem | +| `--text-sm` | — | .875rem | + +## Токены — Dark + +| token | ours | ref | +|---|---|---| +| `--panel` | #182129 | #f8fafb | +| `--blue` | #54a2ff | #1769d2 | +| `--muted` | #91a0ac | #6d7883 | +| `--background` | #11171d | #eef1f4 | +| `--danger` | #ff7479 | — | +| `--foreground` | #e8eef2 | #17212b | +| `--success` | #25a96a | — | +| `--line` | #33414c | #cbd3da | +| `--font-sans` | "IBM Plex Sans", system-ui, -apple-system, sans-serif | "IBM Plex Sans", sans-serif | +| `--font-mono` | "IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace | "IBM Plex Mono", monospace | +| `--text-sm--line-height` | — | calc(1.25 / .875) | +| `--text-xs--line-height` | — | calc(1 / .75) | +| `--font-weight-medium` | — | 500 | +| `--spacing` | — | .25rem | +| `--text-xs` | — | .75rem | +| `--radius-md` | — | .375rem | +| `--radius-lg` | — | .5rem | +| `--text-sm` | — | .875rem | + +## Расхождения элементов (топ 48) + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(1)>span:nth-child(3)` — "BACKGROUND" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(23, 105, 210)` → `rgb(109, 120, 131)` + - **padding**: `2.4px 7.2px` → `0px` + - **letterSpacing**: `0.8px` → `1.2px` + - **borderRadius**: `4px` → `0px` + - **display**: `flex` → `inline` + - **gap**: `4.8px` → `normal` + - **rect**: `116,507 84x20` → `143,489 67x12` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(2)>div:nth-child(1)>span:nth-child(3)` — "TRANSFORM" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(23, 105, 210)` → `rgb(109, 120, 131)` + - **padding**: `2.4px 7.2px` → `0px` + - **letterSpacing**: `0.8px` → `1.2px` + - **borderRadius**: `4px` → `0px` + - **display**: `flex` → `inline` + - **gap**: `4.8px` → `normal` + - **rect**: `116,670 78x20` → `143,651 60x12` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(3)>div:nth-child(1)>span:nth-child(3)` — "STYLE" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(23, 105, 210)` → `rgb(109, 120, 131)` + - **padding**: `2.4px 7.2px` → `0px` + - **letterSpacing**: `0.8px` → `1.2px` + - **borderRadius**: `4px` → `0px` + - **display**: `flex` → `inline` + - **gap**: `4.8px` → `normal` + - **rect**: `116,785 50x20` → `143,799 34x12` + +### `body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(2)>div:nth-child(4)>button:nth-child(1)` — "RU" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `11px` → `10px` + - **color**: `rgb(255, 255, 255)` → `rgb(238, 241, 244)` + - **backgroundColor**: `rgb(23, 105, 210)` → `rgb(23, 33, 43)` + - **padding**: `4.8px 11.2px` → `6px 9px` + - **letterSpacing**: `0.66px` → `normal` + - **rect**: `1307,20 37x24` → `1323,20 29x24` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>span:nth-child(1)` — "COLOR" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontWeight**: `500` → `400` + - **margin**: `0px` → `0px 0px 8px` + - **letterSpacing**: `1px` → `0.8px` + - **lineHeight**: `12px` → `normal` + - **display**: `block` → `flex` + - **rect**: `71,576 203x12` → `143,546 189x12` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(1)>span:nth-child(1)` — "SOURCE" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **fontWeight**: `500` → `400` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **letterSpacing**: `1px` → `normal` + - **lineHeight**: `12px` → `normal` + - **rect**: `806,320 262x12` → `808,663 30x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(2)>span:nth-child(1)` — "STEP 01" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **fontWeight**: `500` → `400` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **letterSpacing**: `1px` → `normal` + - **lineHeight**: `12px` → `normal` + - **rect**: `1094,320 262x12` → `1096,663 35x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(3)>span:nth-child(1)` — "STEP 02" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **fontWeight**: `500` → `400` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **letterSpacing**: `1px` → `normal` + - **lineHeight**: `12px` → `normal` + - **rect**: `806,752 262x12` → `808,1021 35x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(4)>span:nth-child(1)` — "STEP 03" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **fontWeight**: `500` → `400` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **letterSpacing**: `1px` → `normal` + - **lineHeight**: `12px` → `normal` + - **rect**: `1094,752 262x12` → `1096,1021 35x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(5)>span:nth-child(1)` — "FINAL OUTPUT" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **fontWeight**: `500` → `400` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **letterSpacing**: `1px` → `normal` + - **lineHeight**: `12px` → `normal` + - **rect**: `806,1183 262x12` → `808,1378 59x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>button:nth-child(2)>span:nth-child(2)>span:nth-child(1)` — "1.2 MB" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `11px` + - **color**: `rgb(255, 255, 255)` → `rgb(23, 33, 43)` + - **margin**: `0px` → `5px 0px 0px` + - **display**: `inline` → `block` + - **rect**: `1090,240 36x13` → `1246,291 79x13` + +### `body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(2)>div:nth-child(4)>button:nth-child(2)` — "EN" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `11px` → `10px` + - **padding**: `4.8px 11.2px` → `6px 9px` + - **letterSpacing**: `0.66px` → `normal` + - **rect**: `1344,20 38x24` → `1352,20 29x24` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(1)` — "100" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `11px` → `10px` + - **padding**: `4.8px 11.2px` → `0px` + - **letterSpacing**: `0.66px` → `normal` + - **rect**: `491,597 44x24` → `520,567 46x30` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(2)` — "75" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `11px` → `10px` + - **padding**: `4.8px 11.2px` → `0px` + - **letterSpacing**: `0.66px` → `normal` + - **rect**: `535,597 38x24` → `566,567 46x30` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(3)` — "50" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `11px` → `10px` + - **padding**: `4.8px 11.2px` → `0px` + - **letterSpacing**: `0.66px` → `normal` + - **rect**: `573,597 38x24` → `612,567 46x30` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>button:nth-child(4)` — "25" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `11px` → `10px` + - **padding**: `4.8px 11.2px` → `0px` + - **letterSpacing**: `0.66px` → `normal` + - **rect**: `611,597 38x24` → `658,567 46x30` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(4)>div:nth-child(2)>div:nth-child(2)>label:nth-child(2)>span:nth-child(1)` — "Preserve aspect ratio" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Sans", sans-serif` + - **fontSize**: `13.6px` → `16px` + - **lineHeight**: `normal` → `24px` + - **display**: `block` → `inline` + - **rect**: `399,1036 129x18` → `432,1021 156x17` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(1)>span:nth-child(2)` — "01" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **padding**: `0px` → `17px 0px 0px 15px` + - **rect**: `95,510 13x14` → `59,466 46x150` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(1)>div:nth-child(1)>span:nth-child(4)` — "Gradient background" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Mono", monospace` + - **fontSize**: `14.4px` → `15px` + - **margin**: `0px` → `5px 0px 18px` + - **rect**: `208,508 467x19` → `143,510 157x18` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(2)>div:nth-child(1)>span:nth-child(2)` — "02" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **padding**: `0px` → `17px 0px 0px 15px` + - **rect**: `95,673 13x14` → `59,628 46x136` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(2)>div:nth-child(1)>span:nth-child(4)` — "Remove background" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Mono", monospace` + - **fontSize**: `14.4px` → `15px` + - **margin**: `0px` → `5px 0px 18px` + - **rect**: `201,671 473x19` → `143,672 140x18` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(3)>div:nth-child(1)>span:nth-child(2)` — "03" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **padding**: `0px` → `17px 0px 0px 15px` + - **rect**: `95,788 13x14` → `59,776 46x150` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(3)>div:nth-child(1)>span:nth-child(4)` — "Add outline" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Mono", monospace` + - **fontSize**: `14.4px` → `15px` + - **margin**: `0px` → `5px 0px 18px` + - **rect**: `174,785 501x19` → `143,820 91x18` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(4)>div:nth-child(1)>span:nth-child(2)` — "04" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(109, 120, 131)` → `rgb(23, 105, 210)` + - **padding**: `0px` → `17px 0px 0px 15px` + - **rect**: `95,951 13x14` → `59,938 46x142` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(4)>div:nth-child(4)>div:nth-child(1)>span:nth-child(4)` — "Round corners" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Mono", monospace` + - **fontSize**: `14.4px` → `15px` + - **margin**: `0px` → `5px 0px 18px` + - **rect**: `174,948 501x19` → `143,982 107x18` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(1)>span:nth-child(1)` — "DIMENSIONS" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **letterSpacing**: `1px` → `normal` + - **rect**: `1150,215 70x13` → `1073,271 79x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>span:nth-child(1)` — "FORMAT" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **letterSpacing**: `1px` → `normal` + - **rect**: `1150,239 42x13` → `1159,271 79x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(3)>span:nth-child(1)` — "SIZE" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **fontSize**: `10px` → `9px` + - **letterSpacing**: `1px` → `normal` + - **rect**: `1150,264 28x13` → `1246,271 79x10` + +### `body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(1)>span:nth-child(2)` — "easy-png-tools" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(0, 0, 0)` → `rgb(23, 33, 43)` + - **rect**: `98,23 118x18` → `98,23 108x17` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(1)>span:nth-child(1)` — "/" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **color**: `rgb(23, 105, 210)` → `rgb(109, 120, 131)` + - **rect**: `173,124 7x13` → `165,124 13x12` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(1)>h1:nth-child(1)` — "Build your image pipeline." + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Sans", sans-serif` + - **lineHeight**: `normal` → `57.6px` + - **rect**: `58,155 474x150` → `58,154 482x115` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(1)>span:nth-child(2)` — "1200 × 800 px" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **margin**: `0px` → `5px 0px 0px` + - **rect**: `1236,214 86x14` → `1073,291 79x13` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>span:nth-child(2)` — "PNG-24" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **margin**: `0px` → `5px 0px 0px` + - **rect**: `1282,238 40x14` → `1159,291 79x13` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(1)>p:nth-child(2)` — "Chain simple tools together. Every chang" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Sans", sans-serif` + - **rect**: `58,321 474x51` → `58,285 482x51` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)` — "source.png" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `597,347 66x14` → `606,311 60x13` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(2)>div:nth-child(2)>b:nth-child(3)` — "1.8 MB" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `671,347 40x14` → `674,311 36x13` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(3)>div:nth-child(1)>span:nth-child(1)` — "PROCESSING PIPELINE" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `58,434 137x13` → `58,400 127x12` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(1)>div:nth-child(3)>div:nth-child(1)>strong:nth-child(2)>em:nth-child(1)` — "• LIVE" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `184,457 36x13` → `173,425 33x12` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(1)>span:nth-child(1)` — "PIPELINE OUTPUTS" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `799,177 58x33` → `799,188 54x36` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(1)>div:nth-child(1)>strong:nth-child(2)` — "Visual history" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `799,216 93x36` → `799,233 87x34` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(1)>div:nth-child(1)>span:nth-child(1)` — "PNG" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `923,498 28x19` → `923,478 27x18` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)` — "original.png · 1200 × 800" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `925,706 135x11` → `942,663 124x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(2)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)` — "gradient applied" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `1262,706 86x11` → `1275,663 79x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(3)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)` — "background removed" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `962,1138 97x11` → `977,1021 89x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(4)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)` — "outline added" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `1278,1138 70x11` → `1290,1021 64x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>div:nth-child(2)>div:nth-child(5)>div:nth-child(2)>div:nth-child(2)>span:nth-child(2)` — "ready · PNG-24" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + - **rect**: `984,1569 76x11` → `997,1378 69x10` + +### `body>div:nth-child(1)>div:nth-child(1)>div:nth-child(2)>div:nth-child(2)>div:nth-child(1)>section:nth-child(2)>p:nth-child(3)` — "Output is generated in-browser. Your fil" + - **fontFamily**: `"IBM Plex Sans", system-ui, -apple-system, sans-serif` → `"IBM Plex Sans", sans-serif` + - **rect**: `799,1624 565x17` → `799,1425 565x17` + +### `body>div:nth-child(1)>div:nth-child(1)>header:nth-child(1)>div:nth-child(1)>span:nth-child(1)` — "EP" + - **fontFamily**: `"IBM Plex Mono", ui-monospace, SFMono-Regular, monospace` → `"IBM Plex Mono", monospace` + +## Только у нас (структурно) + +- `+ Add tool` +- `AUTO` +- `Gradient color` +- `DIRECTION` +- `135°` +- `OPACITY` +- `Automatic subject detection enabled` +- `WIDTH` +- `Outline width` +- `2px` +- `Outline color` +- `RADIUS` +- `Corner radius` +- `18px` + +## Только в рефе (структурно) + +- `/ DEMO` +- `135 + °` +- `100%` +- `2 + px` +- `18 + px` +- `v2.4.0` +- `© 2024` diff --git a/web/package.json b/web/package.json index d19cfe8..840a803 100644 --- a/web/package.json +++ b/web/package.json @@ -12,7 +12,8 @@ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "test": "vitest run", "format": "prettier --write .", - "lint": "eslint ." + "lint": "eslint .", + "audit": "node scripts/audit.mjs" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -23,6 +24,7 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-svelte": "^3.23.0", "globals": "^17.11.0", + "playwright": "^1.62.1", "prettier": "^3.9.6", "prettier-plugin-svelte": "^4.1.1", "svelte": "^5.56.1", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 4bf6cb1..1d15fbc 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: globals: specifier: ^17.11.0 version: 17.11.0 + playwright: + specifier: ^1.62.1 + version: 1.62.1 prettier: specifier: ^3.9.6 version: 3.9.6 @@ -618,6 +621,11 @@ packages: flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -832,6 +840,16 @@ packages: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + playwright-core@1.62.1: + resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==} + engines: {node: '>=20'} + hasBin: true + + playwright@1.62.1: + resolution: {integrity: sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==} + engines: {node: '>=20'} + hasBin: true + postcss-load-config@3.1.4: resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} engines: {node: '>= 10'} @@ -1632,6 +1650,9 @@ snapshots: flatted@3.4.4: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -1788,6 +1809,14 @@ snapshots: picomatch@4.0.5: {} + playwright-core@1.62.1: {} + + playwright@1.62.1: + dependencies: + playwright-core: 1.62.1 + optionalDependencies: + fsevents: 2.3.2 + postcss-load-config@3.1.4(postcss@8.5.26): dependencies: lilconfig: 2.1.0 diff --git a/web/scripts/audit.mjs b/web/scripts/audit.mjs new file mode 100644 index 0000000..abbc6f5 --- /dev/null +++ b/web/scripts/audit.mjs @@ -0,0 +1,271 @@ +// Стиль-аудит: снимает design-токены (light/dark) и дерево вычисленных стилей +// нашей preview-страницы и refs-html страницы, сравнивает и пишет отчёт. +// Запуск: pnpm audit (опции: --ours --ref --no-serve --route ) +import { chromium } from "playwright"; +import { spawn } from "node:child_process"; +import { readFileSync, writeFileSync, mkdirSync } from "node:fs"; +import { pathToFileURL } from "node:url"; +import { resolve } from "node:path"; + +const args = (() => { + const a = {}; + for (let i = 0; i < process.argv.length; i++) { + const v = process.argv[i]; + if (v === "--no-serve") a.noServe = true; + else if (v === "--ours") a.ours = process.argv[++i]; + else if (v === "--ref") a.ref = process.argv[++i]; + else if (v === "--route") a.route = process.argv[++i]; + } + return a; +})(); + +const WEB = process.cwd(); +const PORT = 5179; +const ROUTE = args.route || "/preview/demo"; +const OURS = args.ours || `http://127.0.0.1:${PORT}${ROUTE}`; +const REF = args.ref || resolve(WEB, "../refs-html/demo.html"); + +const FIELDS = [ + "fontFamily", + "fontSize", + "fontWeight", + "color", + "backgroundColor", + "border", + "padding", + "margin", + "letterSpacing", + "lineHeight", + "borderRadius", + "display", + "gap" +]; + +const waitFor = async (url, ms = 60000) => { + const t = Date.now(); + while (Date.now() - t < ms) { + try { + const r = await fetch(url); + if (r.ok) return; + } catch {} + await new Promise((r) => setTimeout(r, 500)); + } + throw new Error("dev server not up: " + url); +}; + +const snapshot = (page, url) => + page.evaluate((url) => { + const readTokens = () => { + const s = getComputedStyle(document.documentElement); + const out = {}; + for (const k of s) if (k.startsWith("--")) out[k] = s.getPropertyValue(k).trim(); + return out; + }; + const tokensLight = readTokens(); + const prev = document.documentElement.getAttribute("data-theme"); + document.documentElement.setAttribute("data-theme", "dark"); + const tokensDark = readTokens(); + if (prev) document.documentElement.setAttribute("data-theme", prev); + else document.documentElement.removeAttribute("data-theme"); + + const sig = (el) => { + const s = getComputedStyle(el); + const o = {}; + for (const f of [ + "fontFamily", + "fontSize", + "fontWeight", + "color", + "backgroundColor", + "borderTopWidth", + "borderTopColor", + "padding", + "margin", + "letterSpacing", + "lineHeight", + "borderRadius", + "display", + "gap" + ]) + o[f] = s[f]; + return o; + }; + const rect = (el) => { + const r = el.getBoundingClientRect(); + return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; + }; + const walk = (el, path) => { + if (["SCRIPT", "STYLE", "NOSCRIPT"].includes(el.tagName)) return []; + const out = []; + const kids = [...el.children]; + out.push({ + path, + tag: el.tagName.toLowerCase(), + text: el.children.length === 0 ? el.textContent.trim().slice(0, 40) : "", + rect: rect(el), + style: sig(el) + }); + for (let i = 0; i < kids.length; i++) + out.push(...walk(kids[i], `${path}>${kids[i].tagName.toLowerCase()}:nth-child(${i + 1})`)); + return out; + }; + return { tokensLight, tokensDark, tree: walk(document.body, "body") }; + }, url); + +async function main() { + let server; + if (!args.noServe) { + const bin = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; + server = spawn(bin, ["dev", "--port", String(PORT), "--strictPort"], { + cwd: WEB, + stdio: "ignore", + shell: true, + detached: process.platform !== "win32" + }); + await waitFor(OURS); + } + + const browser = await chromium.launch(); + const page = await browser.newPage({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1 }); + try { + await page.goto(OURS, { waitUntil: "load" }); + await page.evaluate(() => document.fonts.ready); + await page.waitForTimeout(400); + const ours = await snapshot(page, OURS); + + await page.goto(pathToFileURL(REF).href, { waitUntil: "load" }); + await page.evaluate(() => document.fonts.ready); + await page.waitForTimeout(400); + const ref = await snapshot(page, pathToFileURL(REF).href); + + // token diff (игнорируем служебный мусор Tailwind/lightningcss — он + // ref-only и не относится к нашим смысловым токенам) + const TOK_NOISE = /^(--tw-|--lightningcss-|--default-)/; + const tokDiff = (a, b) => { + const keys = new Set([...Object.keys(a), ...Object.keys(b)]); + const rows = []; + for (const k of keys) { + if (TOK_NOISE.test(k)) continue; + const av = a[k] ?? "—"; + const bv = b[k] ?? "—"; + if (av !== bv) rows.push({ key: k, ours: av, ref: bv }); + } + return rows; + }; + const tokenDiffs = { + light: tokDiff(ours.tokensLight, ref.tokensLight), + dark: tokDiff(ours.tokensDark, ref.tokensDark) + }; + + // element diff: совпадающие по тексту элементы (структуры разные, + // поэтому nth-child-пути не совпадают — матчим по видимому тексту) + const byText = (tree) => { + const m = new Map(); + for (const e of tree) if (e.text) if (!m.has(e.text)) m.set(e.text, e); + return m; + }; + const txtO = byText(ours.tree); + const txtR = byText(ref.tree); + const onlyOurs = []; + const onlyRef = []; + const elementDiffs = []; + for (const [k, o] of txtO) { + const r = txtR.get(k); + if (!r) { + onlyOurs.push(k); + continue; + } + const deltas = {}; + for (const f of FIELDS) { + const ov = (o.style[f] ?? "").toString(); + const rv = (r.style[f] ?? "").toString(); + if (ov !== rv) deltas[f] = { ours: ov, ref: rv }; + } + const dr = o.rect; + const rr = r.rect; + if (Math.abs(dr.x - rr.x) > 2 || Math.abs(dr.y - rr.y) > 2 || Math.abs(dr.w - rr.w) > 2 || Math.abs(dr.h - rr.h) > 2) + deltas.rect = { ours: dr, ref: rr }; + if (Object.keys(deltas).length) elementDiffs.push({ path: o.path, tag: o.tag, text: k, deltas }); + } + for (const [k] of txtR) if (!txtO.has(k)) onlyRef.push(k); + + const report = { + generatedAt: new Date().toISOString(), + ours: OURS, + ref: REF, + tokenDiffs, + elementDiffs, + onlyOurs: onlyOurs.slice(0, 60), + onlyRef: onlyRef.slice(0, 60), + counts: { + tokenLight: tokenDiffs.light.length, + tokenDark: tokenDiffs.dark.length, + elements: elementDiffs.length, + onlyOurs: onlyOurs.length, + onlyRef: onlyRef.length + } + }; + + mkdirSync(resolve(WEB, "audit"), { recursive: true }); + writeFileSync(resolve(WEB, "audit/audit-report.json"), JSON.stringify(report, null, 2)); + writeFileSync(resolve(WEB, "audit/audit-report.md"), toMarkdown(report, ours, ref)); + console.log( + `audit done: tokens light/dark=${tokenDiffs.light.length}/${tokenDiffs.dark.length}, ` + + `elements=${elementDiffs.length}, onlyOurs=${onlyOurs.length}, onlyRef=${onlyRef.length}` + ); + console.log(`report: web/audit/audit-report.md`); + } finally { + await browser.close(); + if (server) { + try { + if (process.platform === "win32") + spawn("taskkill", ["/pid", String(server.pid), "/f", "/t"], { stdio: "ignore" }); + else server.kill("SIGTERM"); + } catch {} + } + } +} + +function toMarkdown(report, ours, ref) { + const tokTable = (rows, a, b) => + rows.length + ? `| token | ours | ref |\n|---|---|---|\n` + + rows.map((r) => `| \`${r.key}\` | ${r.ours} | ${r.ref} |`).join("\n") + : "_все совпадают_"; + const sorted = [...report.elementDiffs].sort( + (x, y) => Object.keys(y.deltas).length - Object.keys(x.deltas).length + ); + const elRows = sorted + .slice(0, 100) + .map((e) => { + const d = Object.entries(e.deltas) + .map(([k, v]) => { + if (k === "rect") { + const f = (r) => `${r.x},${r.y} ${r.w}x${r.h}`; + return ` - **rect**: \`${f(v.ours)}\` → \`${f(v.ref)}\``; + } + return ` - **${k}**: \`${v.ours}\` → \`${v.ref}\``; + }) + .join("\n"); + return `### \`${e.path}\`${e.text ? ` — "${e.text}"` : ""}\n${d}`; + }) + .join("\n\n"); + return `# Style audit: ${report.ours}\n\nvs ${report.ref}\n\n_${report.generatedAt}_\n\n` + + `## Сводка\n\n- Токены light: **${report.counts.tokenLight}** расх.\n` + + `- Токены dark: **${report.counts.tokenDark}** расх.\n` + + `- Элементы (стиль/геометрия): **${report.counts.elements}** расх.\n` + + `- Только у нас: ${report.counts.onlyOurs}, только в рефе: ${report.counts.onlyRef}\n\n` + + `## Токены — Light\n\n${tokTable(report.tokenDiffs.light)}\n\n` + + `## Токены — Dark\n\n${tokTable(report.tokenDiffs.dark)}\n\n` + + `## Расхождения элементов (топ ${Math.min(100, sorted.length)})\n\n${elRows}\n\n` + + `## Только у нас (структурно)\n\n` + + (report.onlyOurs.length ? report.onlyOurs.map((p) => `- \`${p}\``).join("\n") : "_—_") + + `\n\n## Только в рефе (структурно)\n\n` + + (report.onlyRef.length ? report.onlyRef.map((p) => `- \`${p}\``).join("\n") : "_—_") + + `\n`; +} + +main().catch((e) => { + console.error(e); + process.exit(1); +});