style: format docs markdown

This commit is contained in:
2026-09-09 04:13:13 +05:00
parent 46da54140c
commit 64e35f4fdf
36 changed files with 2092 additions and 1187 deletions
+195 -181
View File
@@ -1,40 +1,41 @@
# План: переезд на новый дизайн (refs) — параллельная сборка
> Статус: план к выполнению. Шаги 5–6 (C17–C21) остаются **следующим этапом**.
> Они больше не привязаны к «верности против рефа»: `docs/archive/plan-design-fix.md`
> закрыт (2026-09-07, дизайн ушёл вперёд от `refs-html/`), аудит против рефа
> не информативен и прекращён. Дальнейшее качество — ручное ревью + `lint:all`.
> Шаг 37 (переезд старых файлов в папки `old/`) вынесен в `backlog.md`.
> Они больше не привязаны к «верности против рефа»:
> `docs/archive/plan-design-fix.md` закрыт (2026-09-07, дизайн ушёл вперёд от
> `refs-html/`), аудит против рефа не информативен и прекращён. Дальнейшее
> качество — ручное ревью + `lint:all`. Шаг 37 (переезд старых файлов в папки
> `old/`) вынесен в `backlog.md`.
>
> Источники: `refs/` — Next/React-референс (правда по пикселям и интерактиву),
> `refs-html/` — статические HTML-снимки для быстрого просмотра в браузере.
>
> **Стратегия изменена.** Вместо in-place рестайла (старый план A–F, правка
> общих компонентов на месте с алиасами токенов) — **параллельная сборка**:
> новый дизайн строится отдельно, изолируется от старого, проверяется
> целиком, затем становится основным и старый удаляется. Старое приложение
> остаётся рабочим и шippable на всём протяжении перехода.
> новый дизайн строится отдельно, изолируется от старого, проверяется целиком,
> затем становится основным и старый удаляется. Старое приложение остаётся
> рабочим и шippable на всём протяжении перехода.
## 0. Почему меняем стратегию
- **Старый план (A–F):** правим `ui/*` и страницы на месте, держим старые
токены как алиасы, чтобы ничего не сломалось до этапа F. Риск: общие
компоненты редактируются вживую → регресс старого UI во время работы,
новый дизайн нельзя принять изолированно.
- **Новый подход (5 фаз):** (1) строим новый ui-kit, (2) строим новые
страницы рядом, не трогая старые, (3) проверяем новый дизайн, (4) делаем
его основным, (5) удаляем старый. Ниже фазы 1–5 — это требуемые шаги.
- **Старый план (A–F):** правим `ui/*` и страницы на месте, держим старые токены
как алиасы, чтобы ничего не сломалось до этапа F. Риск: общие компоненты
редактируются вживую → регресс старого UI во время работы, новый дизайн нельзя
принять изолированно.
- **Новый подход (5 фаз):** (1) строим новый ui-kit, (2) строим новые страницы
рядом, не трогая старые, (3) проверяем новый дизайн, (4) делаем его основным,
(5) удаляем старый. Ниже фазы 1–5 — это требуемые шаги.
- **Главный технический риск и его решение.** Сейчас `web/src/app.css`
импортируется в **корневом** `web/src/routes/+layout.svelte:2` и потому
грузится на ВСЕХ маршрутах, включая новые. Решение — **не префиксовать
новый CSS (никакого `.ds2`), а разнести глобальные стили по layout'ам без
наследования**: (а) убрать `import '../app.css'` из корневого layout;
(б) старые маршруты обернуть группой `(old)/` с собственным layout,
импортирующим `app.css`; (в) новые маршруты (`preview/*`) — собственный
layout, импортирующий `design2.css`. SvelteKit делает code-splitting CSS
по маршрутам, а стили компонентов скоупятся сами — поэтому глобальные
стили каждого дизайна живут только в своей ветке, и префикс-обёртка
`.ds2` не нужна. Старые `ui/` и страницы не трогаются до фазы 5.
грузится на ВСЕХ маршрутах, включая новые. Решение — **не префиксовать новый
CSS (никакого `.ds2`), а разнести глобальные стили по layout'ам без
наследования**: (а) убрать `import '../app.css'` из корневого layout; (б)
старые маршруты обернуть группой `(old)/` с собственным layout, импортирующим
`app.css`; (в) новые маршруты (`preview/*`) — собственный layout,
импортирующий `design2.css`. SvelteKit делает code-splitting CSS по маршрутам,
а стили компонентов скоупятся сами — поэтому глобальные стили каждого дизайна
живут только в своей ветке, и префикс-обёртка `.ds2` не нужна. Старые `ui/` и
страницы не трогаются до фазы 5.
## 1. Источники и их оценка
@@ -57,10 +58,11 @@
`panel-heading`, `setting-group`, `settings-footer`, `settings-panel`,
`segmented`, `status`, `step-card` (`step-index`/`step-heading`/`step-type`/
`step-body`/`drag`), `color-field` (`swatch`/`native-color`),
`preview-toolbar`/`checker` (`tile-canvas`/`remover-canvas`/`comparison-grid`),
`result-meta`/`meta-row`, `tool-card`/`image-card` (`tool-icon`/`tool-index`/
`tool-copy`), `code-block`, `icon-btn`, `download-btn`, `reset-btn`, `lede`,
`auto-note`, `label`, `toggle`/`toggle-group`, `direction-grid`, `gradient-bar`.
`preview-toolbar`/`checker`
(`tile-canvas`/`remover-canvas`/`comparison-grid`), `result-meta`/`meta-row`,
`tool-card`/`image-card` (`tool-icon`/`tool-index`/ `tool-copy`),
`code-block`, `icon-btn`, `download-btn`, `reset-btn`, `lede`, `auto-note`,
`label`, `toggle`/`toggle-group`, `direction-grid`, `gradient-bar`.
- **Дыры в refs-html:** нет состояний empty/error/loading, нет глобальных
модалок/тултипов, i18n не представлен (все лейблы — английские брендовые).
Решения — см. §9.
@@ -68,9 +70,9 @@
## 2. Токены (фундамент нового дизайна)
Живут в `src/lib/styles/design2.css` как обычный `:root { … }` (плюс
`[data-theme='dark'] { … }`). Файл грузится **только** layout'ом новой
ветки (§5), поэтому глобально не конфликтует со старым `app.css`. Маппинг
старых токенов `web/src/app.css` → новые:
`[data-theme='dark'] { … }`). Файл грузится **только** layout'ом новой ветки
(§5), поэтому глобально не конфликтует со старым `app.css`. Маппинг старых
токенов `web/src/app.css` → новые:
| Старый | Новый | Light | Dark |
| -------------- | -------------- | --------- | ------------------------ |
@@ -90,45 +92,45 @@
- Радиусы: один `--radius: 4px` (мягкость старой темы уходит).
- Шрифты: `IBM Plex Sans` + `IBM Plex Mono`, self-host через `@fontsource`
(offline-first, CDN нельзя). Mono — рабочий шрифт для лейблов/метаданных/
цифр; Sans — заголовки и текст. Задаются на `.app-shell` (корень новой
ветки), чтобы не зависеть от старых глобальных правил `body`.
- Фон-сетка (32px blueprint-grid) и шахматка канвасов — утилиты `.page-grid`
и `.checker-canvas` внутри `.ds2`, не токены.
- Тёмная тема — на `[data-theme='dark']` (инлайн-скрипт уже есть в
`app.html`); класс `.dark-mode` из рефа не переносим, используем наш
механизм. На переходный период **никаких алиасов старых токенов** (старые
страницы сами живут на своём `app.css`).
цифр; Sans — заголовки и текст. Задаются на `.app-shell` (корень новой ветки),
чтобы не зависеть от старых глобальных правил `body`.
- Фон-сетка (32px blueprint-grid) и шахматка канвасов — утилиты `.page-grid` и
`.checker-canvas` внутри `.ds2`, не токены.
- Тёмная тема — на `[data-theme='dark']` (инлайн-скрипт уже есть в `app.html`);
класс `.dark-mode` из рефа не переносим, используем наш механизм. На
переходный период **никаких алиасов старых токенов** (старые страницы сами
живут на своём `app.css`).
## 3. Базовые примитивы дизайн-системы (все — компоненты)
Правило фазы 1: **любой повторяющийся паттерн — компонент**, даже «просто
div с двумя стилями». Никакой копипасты классов между страницами. Каждый
примитив ниже — отдельный `.svelte` в `src/lib/components/kit/`.
Правило фазы 1: **любой повторяющийся паттерн — компонент**, даже «просто div с
двумя стилями». Никакой копипасты классов между страницами. Каждый примитив ниже
— отдельный `.svelte` в `src/lib/components/kit/`.
- `MonoLabel` — микро-лейбл: mono 10px, uppercase, letter-spacing .1em,
muted; слот-модификатор accent (синие eyebrow/version).
- `StatusDot` / `StatusLine` — точка `--success` + подпись
(«AUTO PIPELINE», «LIVE PREVIEW»).
- `MonoLabel` — микро-лейбл: mono 10px, uppercase, letter-spacing .1em, muted;
слот-модификатор accent (синие eyebrow/version).
- `StatusDot` / `StatusLine` — точка `--success` + подпись («AUTO PIPELINE»,
«LIVE PREVIEW»).
- `Panel` / `PanelHeading` / `SettingGroup` / `SettingsFooter` — панель с
1px-бордером, составные части с разделителями.
- `CheckerCanvas` — рабочая поверхность превью: тёмная подложка + шахматка.
- `MetaRow` / `MetaList` — пары «CAPTION значение».
- `Segmented` — сегмент-переключатель (язык, тип градиента, пресеты).
- `Badge` — тег типа шага (BACKGROUND/TRANSFORM/STYLE), чип «✓ AUTO».
- `StepCard` — нумерованная карточка этапа: индекс `01`, drag-handle,
Badge типа, заголовок, кнопка удаления; слот для параметров.
- `StepCard` — нумерованная карточка этапа: индекс `01`, drag-handle, Badge
типа, заголовок, кнопка удаления; слот для параметров.
- `ToolCard` — карточка каталога (icon, index, title, copy, arrow).
- `ImageCard` / `PreviewTile` — label сверху + canvas-превью.
- `AppShell` — корневой контейнер `.app-shell` + `.page-grid` (обёртка
новой ветки маршрутов); задаёт шрифты и фон нового дизайна.
- `AppShell` — корневой контейнер `.app-shell` + `.page-grid` (обёртка новой
ветки маршрутов); задаёт шрифты и фон нового дизайна.
- `BlueprintGrid` — фоновая сетка (используется в `AppShell`).
- `IconButton`, `Icon` (обёртка `lucide-svelte`, 1617px, muted).
## 4. Минимальный UI-kit (`src/lib/components/kit/`)
Контролы, поверх примитивов §3. API проектируем сразу под новый дизайн
(обратной совместимости со старым `ui/` не нужно — старые страницы его
продолжают использовать).
Контролы, поверх примитивов §3. API проектируем сразу под новый дизайн (обратной
совместимости со старым `ui/` не нужно — старые страницы его продолжают
использовать).
| Компонент | Назначение |
| ------------------------- | ----------------------------------------------------------------- |
@@ -156,33 +158,33 @@ div с двумя стилями». Никакой копипасты класс
- **Корневой layout `src/routes/+layout.svelte`** перестаёт импортировать
`../app.css`. В нём остаётся только общая логика (init темы/locale) и
нейтральный `<div>{children}</div>` без глобальных стилей дизайна.
- **Старая ветка `src/routes/(old)/+layout.svelte`** импортирует `app.css`
и содержит текущую шапку/подвал (разметку из нынешнего корневого layout).
Все старые страницы переносятся внутрь `(old)/` (группа в скобках не
меняет URL): `(old)/+page.svelte`, `(old)/demo/...`,
`(old)/list-tools/...`, `(old)/tools/[id]/...`.
- **Новая ветка `src/routes/preview/+layout.svelte`** импортирует
`design2.css` и рендерит `<AppShell>`. Сюда же — новые страницы (§6).
- Благодаря code-splitting CSS в SvelteKit: на старых маршрутах грузится
только `app.css`, на `preview/*` — только `design2.css`. Скоуп стилей
- **Старая ветка `src/routes/(old)/+layout.svelte`** импортирует `app.css` и
содержит текущую шапку/подвал (разметку из нынешнего корневого layout). Все
старые страницы переносятся внутрь `(old)/` (группа в скобках не меняет URL):
`(old)/+page.svelte`, `(old)/demo/...`, `(old)/list-tools/...`,
`(old)/tools/[id]/...`.
- **Новая ветка `src/routes/preview/+layout.svelte`** импортирует `design2.css`
и рендерит `<AppShell>`. Сюда же — новые страницы (§6).
- Благодаря code-splitting CSS в SvelteKit: на старых маршрутах грузится только
`app.css`, на `preview/*` — только `design2.css`. Скоуп стилей
Svelte-компонентов добавляет второй уровень изоляции. Никаких `.ds2`.
- Переключатель для ручной проверки: временный флаг `previewDesign` в
`localStorage` + кнопка в старой шапке («Preview v2»), ведущая на
`/preview`. На фазе 4 флаг убираем.
`localStorage` + кнопка в старой шапке («Preview v2»), ведущая на `/preview`.
На фазе 4 флаг убираем.
> Почему не `(new)`-группа с теми же путями вместо `preview/*`: две группы
> не могут одновременно определять один и тот же URL (`(old)/+page.svelte`
> и `(new)/+page.svelte` оба → `/` = конфликт маршрутов). Поэтому новая
> ветка живёт под префиксом `preview/`, а на фазе 4 файлы переносятся на
> реальные пути и группа `(old)` удаляется.
> Почему не `(new)`-группа с теми же путями вместо `preview/*`: две группы не
> могут одновременно определять один и тот же URL (`(old)/+page.svelte` и
> `(new)/+page.svelte` оба → `/` = конфликт маршрутов). Поэтому новая ветка
> живёт под префиксом `preview/`, а на фазе 4 файлы переносятся на реальные пути
> и группа `(old)` удаляется.
>
> Это совпадает с рекомендованным в документации SvelteKit паттерном:
> «put your entire app inside one or more groups except the routes that
> should not inherit the common layouts» — старое целиком в `(old)`, новое
> снаружи. Синтаксис `@`-breakout (`+layout@.svelte`, `+page@.svelte`) здесь
> не требуется: «выломаться» можно лишь до корня, а нам нужно, чтобы именно
> у корня НЕ было `app.css`; если позже захотим держать новые маршруты внутри
> группы, `@` пригодится для сброса родительского layout.
> Это совпадает с рекомендованным в документации SvelteKit паттерном: «put your
> entire app inside one or more groups except the routes that should not inherit
> the common layouts» — старое целиком в `(old)`, новое снаружи. Синтаксис
> `@`-breakout (`+layout@.svelte`, `+page@.svelte`) здесь не требуется:
> «выломаться» можно лишь до корня, а нам нужно, чтобы именно у корня НЕ было
> `app.css`; если позже захотим держать новые маршруты внутри группы, `@`
> пригодится для сброса родительского layout.
## 6. Маршрутизация параллельных страниц
@@ -202,55 +204,50 @@ src/routes/preview/list-tools/+page.svelte
src/routes/preview/tools/[id]/+page.svelte
```
- Каждая новая страница импортирует **только** `kit/*`. Старые страницы
не изменяются (только перемещены в `(old)/`).
- Фаза 4: содержимое `preview/*` переносится на реальные маршруты
(move-файлы), группа `(old)/` удаляется, `design2.css` подключается в
корневом `+layout.svelte` (как новый основной слой), временный флаг/кнопка
убираются.
- Каждая новая страница импортирует **только** `kit/*`. Старые страницы не
изменяются (только перемещены в `(old)/`).
- Фаза 4: содержимое `preview/*` переносится на реальные маршруты (move-файлы),
группа `(old)/` удаляется, `design2.css` подключается в корневом
`+layout.svelte` (как новый основной слой), временный флаг/кнопка убираются.
## 7. Фазы выполнения (1–5 по запросу)
Каждая фаза заканчивается зелёными `test`/`check`/`build` и ревью в браузере
(обе темы) против `refs-html/`.
- **Фаза 1 — Фундамент + UI-kit.** Токены §2, `design2.css` по §5, все
примитивы §3 и контролы §4 как отдельные компоненты. Критерий:
демо-страница kit'а (`/preview` или временная) показывает все состояния
(light/dark, hover/disabled, все варианты Button/Segmented/Badge);
unit-тесты форм не падают; **старые страницы визуально не изменились**
(изоляция работает).
- **Фаза 1 — Фундамент + UI-kit.** Токены §2, `design2.css` по §5, все примитивы
§3 и контролы §4 как отдельные компоненты. Критерий: демо-страница kit'а
(`/preview` или временная) показывает все состояния (light/dark,
hover/disabled, все варианты Button/Segmented/Badge); unit-тесты форм не
падают; **старые страницы визуально не изменились** (изоляция работает).
- **Фаза 2 — Новые страницы.** Под `preview/*` (§6): воркспейс, demo,
list-tools, tool-page (двухпанельная раскладка из рефа: settings-panel +
preview-panel, step-cards для chain). Критерий: разметка соответствует
`refs-html/index.html`, `gradient.html`, `background-remover.html`,
`list-tools.html`; интерактив (слайдеры/тоглы/загрузка/автозапуск) работает
в новом стиле; старые маршруты продолжают работать как раньше.
`list-tools.html`; интерактив (слайдеры/тоглы/загрузка/автозапуск) работает в
новом стиле; старые маршруты продолжают работать как раньше.
- **Фазa 3 — Проверка нового дизайна.** Смоук §8 (обе темы, узкий экран,
800px), скриншоты против `refs-html/`, прогон старого тест-сьюта для
доказательства отсутствия регресса, RU/EN-локализация новых лейблов.
Критерий: визуальное совпадение с рефом, зелёные тесты, записанные
скриншоты приняты.
- **Фазa 3 — Проверка нового дизайна.** Смоук §8 (обе темы, узкий экран, 800px),
скриншоты против `refs-html/`, прогон старого тест-сьюта для доказательства
отсутствия регресса, RU/EN-локализация новых лейблов. Критерий: визуальное
совпадение с рефом, зелёные тесты, записанные скриншоты приняты.
- **Фаза 4 — Сделать новый дизайн основным.** Перенос `preview/*`
реальные маршруты; `design2.css` подключается в корневом `+layout.svelte`;
удаление preview-группы и временного флага/кнопки. Старые страницы теперь
не используются (мёртвый код), но физически ещё в репо.
Критерий: реальные маршруты отдают новый дизайн, тесты зелёные, билд без
предупреждений.
- **Фаза 4 — Сделать новый дизайн основным.** Перенос `preview/*` реальные
маршруты; `design2.css` подключается в корневом `+layout.svelte`; удаление
preview-группы и временного флага/кнопки. Старые страницы теперь не
используются (мёртвый код), но физически ещё в репо. Критерий: реальные
маршруты отдают новый дизайн, тесты зелёные, билд без предупреждений.
- **Фаза 5 — Удалить старый дизайн.** Удаление старых страниц, старого
`ui/`, старого `app.css` (и его токенов), неиспользуемых классов;
переименование `kit/``ui/` (финальное имя) либо оставление `kit/` как
основного — решается на фазе 1. Финальный проход по брейкпоинтам
1200/1100/800/480. Критерий: `grep` по старым токенам/классам пуст,
build чистый, старый дизайн нигде не упоминается.
- **Фаза 5 — Удалить старый дизайн.** Удаление старых страниц, старого `ui/`,
старого `app.css` (и его токенов), неиспользуемых классов; переименование
`kit/``ui/` (финальное имя) либо оставление `kit/` как основного — решается
на фазе 1. Финальный проход по брейкпоинтам 1200/1100/800/480. Критерий:
`grep` по старым токенам/классам пуст, build чистый, старый дизайн нигде не
упоминается.
Оценка объёма: Фаза 1 — фундамент+kit (~1 день), Фаза 2 — страницы
(~12 дня), Фаза 3 — проверка (~полдня), Фаза 4–5 — переключение и чистка
(~полдня).
Оценка объёма: Фаза 1 — фундамент+kit (~1 день), Фаза 2 — страницы (~12 дня),
Фаза 3 — проверка (~полдня), Фаза 4–5 — переключение и чистка (~полдня).
## 8. Смоук (после фазы 3, контроль на фазе 4)
@@ -262,35 +259,34 @@ src/routes/preview/tools/[id]/+page.svelte
шахматке.
4. Chain из 3+ инструментов: step-cards рендерятся, удаление/сброс работают.
5. Узкий экран 800px: настройки над превью, sticky выключается как в рефе.
6. RU/EN: новые лейблы локализованы; mono-лейблы рефа — либо ключи i18n,
либо осознанно английские брендовые элементы (см. §9).
6. RU/EN: новые лейблы локализованы; mono-лейблы рефа — либо ключи i18n, либо
осознанно английские брендовые элементы (см. §9).
## 9. Открытые вопросы
- **Имя финального каталога компонентов:** оставить `kit/` как основное имя
или переименовать в `ui/` на фазе 5 (удалив старое `ui/`). Рекомендую
сразу писать в `kit/`, на фазе 5 переименовать в `ui/`. Решить на фазе 1.
- **Имя финального каталога компонентов:** оставить `kit/` как основное имя или
переименовать в `ui/` на фазе 5 (удалив старое `ui/`). Рекомендую сразу писать
в `kit/`, на фазе 5 переименовать в `ui/`. Решить на фазе 1.
- **Изоляция через layout:** `app.css` вынесен из корневого layout в
`(old)/+layout.svelte`; новый `design2.css` — только в `preview/+layout`.
Проверить на фазе 1, что на `/preview/*` не попадают стили старого
дизайна (DevTools → вкладка Styles, нет правил из `app.css`).
- **Переключатель для проверки:** рекомендую `localStorage`-флаг +
кнопка «Preview v2» в старой шапке + группа `preview/*`. Убрать на фазе 4.
Проверить на фазе 1, что на `/preview/*` не попадают стили старого дизайна
(DevTools → вкладка Styles, нет правил из `app.css`).
- **Переключатель для проверки:** рекомендую `localStorage`-флаг + кнопка
«Preview v2» в старой шапке + группа `preview/*`. Убрать на фазе 4.
- **Mono-лейблы рефа на английском** (`PROCESSING PIPELINE`): завести i18n-
ключи, но допустить часть английских брендовых («приборная панель»).
- **Версия в шапке/футере:** из `package.json` через `define` или
захардкодить — мелочь, решить при сборке topbar.
- **`refs/` и `refs-html/` — одноразовый источник, НЕ поддерживаемый.**
После внедрения нового дизайна все правки дизайна делаются сразу в
продакшен-коде (`design2.css` + `kit/`), а не в `refs/`. Механизм
синхронизации «ref → код» не нужен и не создаётся. `refs/` и `refs-html/`
можно удалить из репо на фазе 5 (по желанию), они не являются частью
рантайма.
- **Версия в шапке/футере:** из `package.json` через `define` или захардкодить —
мелочь, решить при сборке topbar.
- **`refs/` и `refs-html/` — одноразовый источник, НЕ поддерживаемый.** После
внедрения нового дизайна все правки дизайна делаются сразу в продакшен-коде
(`design2.css` + `kit/`), а не в `refs/`. Механизм синхронизации «ref → код»
не нужен и не создаётся. `refs/` и `refs-html/` можно удалить из репо на фазе
5 (по желанию), они не являются частью рантайма.
## 10. План исполнения (мелкие коммиты)
Ветка `feat/redesign`. Каждый шаг — отдельный коммит, цель < ~500 строк.
Перед стартом: `test`/`build` зелёные на базе (якорь регрессии).
Ветка `feat/redesign`. Каждый шаг — отдельный коммит, цель < ~500 строк. Перед
стартом: `test`/`build` зелёные на базе (якорь регрессии).
### Шаг 0. Изоляция layout (без дизайна)
@@ -299,13 +295,15 @@ src/routes/preview/tools/[id]/+page.svelte
`<div>{children}</div>`.
- [x] **[C2]** Создать `routes/(old)/+layout.svelte` (импорт `app.css` + старая
шапка/подвал) и перенести старые страницы (`+page`, `demo`, `list-tools`,
`tools/[id]`) в `(old)/`. URL не меняются. Проверка: старый сайт идентичен.
_(файлы — move, объём строк минимален)_
`tools/[id]`) в `(old)/`. URL не меняются. Проверка: старый сайт
идентичен. _(файлы — move, объём строк минимален)_
### Шаг 1. Фундамент нового дизайна
- [x] **[C3]** Создать `src/lib/styles/design2.css` (токены `:root` + `[data-theme='dark']` по §2) и подключить `@fontsource/ibm-plex-sans` + `@fontsource/ibm-plex-mono` (self-host, offline-first).
подключить шрифты в `design2.css` (self-host, offline-first).
- [x] **[C3]** Создать `src/lib/styles/design2.css` (токены `:root` +
`[data-theme='dark']` по §2) и подключить `@fontsource/ibm-plex-sans` +
`@fontsource/ibm-plex-mono` (self-host, offline-first). подключить шрифты
в `design2.css` (self-host, offline-first).
- [x] **[C4]** _(слит в C3: шрифты нельзя подключить до создания `design2.css`)_
`[data-theme='dark']` (таблица §2). Без утилитарных классов.
- [x] **[C5]** Примитивы batch 1: `AppShell`, `BlueprintGrid`, `CheckerCanvas`,
@@ -320,87 +318,103 @@ src/routes/preview/tools/[id]/+page.svelte
### Шаг 2. Витрина kit (проверка компонентов)
- [x] **[C8]** `preview/+layout.svelte` (импорт `design2.css` + `<AppShell>` + переключатель темы) и
временная `preview/kit` страница со всеми состояниями (light/dark,
hover/disabled, варианты Button/Segmented/Badge и пр.)
- [x] **[C8]** `preview/+layout.svelte` (импорт `design2.css` + `<AppShell>` +
переключатель темы) и временная `preview/kit` страница со всеми
состояниями (light/dark, hover/disabled, варианты Button/Segmented/Badge и
пр.)
### Шаг 3. Новые страницы
- [x] **[C9]** `/preview` — воркспейс (eyebrow, h1, lede, поиск, «открыть последний»).
Поиск фильтрует `TOOLS` из `$lib/registry`; карточки результата пока
ведут на `#` (реальные `/preview/tools/[id]` подключим на C12, иначе
prerender падает на 404).
- [x] **[C9]** `/preview` — воркспейс (eyebrow, h1, lede, поиск, «открыть
последний»). Поиск фильтрует `TOOLS` из `$lib/registry`; карточки
результата пока ведут на `#` (реальные `/preview/tools/[id]` подключим на
C12, иначе prerender падает на 404).
- [x] **[C10]** `/preview/list-tools` — каталог карточек (группировка по
`CATEGORIES`, `ToolCard`). Ссылки пока `#` (реальные `/preview/tools/[id]`
подключим на C12).
- [x] **[C11]** `/preview/demo` — зеркало воркспейса (4 шага + панель превью), статичный приёмочный стенд.
- [x] **[C12]** `/preview/tools/[id]` — data-driven settings-panel + preview-panel (gradient live, остальные — плейсхолдер); `NumberField`, `entries()` для prerender, ToolCard-ссылки подключены.
- [x] **[C13]** `/preview/tools/[id]`comparison-grid для file-source инструментов (`Dropzone` + before/after `CheckerCanvas`, `result-meta`); охватывает background-remover и прочие.
- [x] **[C14]** `/preview/tools/[id]` — исполняемая цепочка: `StepCard`-шаги (параметры inline) + «Add step» (chainable), живой прогон пайплайна на исходнике, превью каждого шага + итог + Download.
- [x] **[C11]** `/preview/demo` — зеркало воркспейса (4 шага + панель превью),
статичный приёмочный стенд.
- [x] **[C12]** `/preview/tools/[id]`data-driven settings-panel +
preview-panel (gradient live, остальные — плейсхолдер); `NumberField`,
`entries()` для prerender, ToolCard-ссылки подключены.
- [x] **[C13]** `/preview/tools/[id]` — comparison-grid для file-source
инструментов (`Dropzone` + before/after `CheckerCanvas`, `result-meta`);
охватывает background-remover и прочие.
- [x] **[C14]** `/preview/tools/[id]` — исполняемая цепочка: `StepCard`-шаги
(параметры inline) + «Add step» (chainable), живой прогон пайплайна на
исходнике, превью каждого шага + итог + Download.
### Шаг 4. Переключатель и проверка
- [x] **[C15]** В старой шапке ссылка «Preview v2» → `/preview`, ставит `localStorage['easy-png-tools:preview-v2']='1'`.
- [x] **[C16]** База под дизайн: рабочий переключатель тем (persist в localStorage, применяется на `<html>`; проверено — фон `#eef1f4``#11171d`), `TopBar` приведён к виду `demo.html` (бренд + синий хлебный крош `/* страница */`, безрамочные иконки, язык — active = `--foreground`/`--background`), страницы `/preview/*` на всю ширину (убран `max-width`). Точечное выравнивание к рефу отложено: реф-дизайн неконсистентен.
(доказательство отсутствия регресса). Фиксы по результатам — отдельными
мелкими коммитами.
- [x] **[C15]** В старой шапке ссылка «Preview v2» → `/preview`, ставит
`localStorage['easy-png-tools:preview-v2']='1'`.
- [x] **[C16]** База под дизайн: рабочий переключатель тем (persist в
localStorage, применяется на `<html>`; проверено — фон
`#eef1f4``#11171d`), `TopBar` приведён к виду `demo.html` (бренд + синий
хлебный крош `/* страница */`, безрамочные иконки, язык — active =
`--foreground`/`--background`), страницы `/preview/*` на всю ширину (убран
`max-width`). Точечное выравнивание к рефу отложено: реф-дизайн
неконсистентен. (доказательство отсутствия регресса). Фиксы по результатам
— отдельными мелкими коммитами.
### Шаг 5. Сделать новый дизайн основным — СЛЕДУЮЩИЙ ЭТАП
> Раньше планировалось после «верности к рефу» (Этапы 0–1
> `docs/archive/plan-design-fix.md`) — план закрыт, гейт снят (2026-09-07).
> Перед стартом C17 выполнить переезд старых файлов в папки `old/`
> (backlog: «Переезд старых файлов в `old/`», шаг 37 закрытого
> `docs/archive/plan-composite-params.md`). Пока не трогаем `preview/*` и `(old)/`.
> Перед стартом C17 выполнить переезд старых файлов в папки `old/` (backlog:
> «Переезд старых файлов в `old/`», шаг 37 закрытого
> `docs/archive/plan-composite-params.md`). Пока не трогаем `preview/*` и
> `(old)/`.
- [ ] **[C17]** Перенести `preview/*` → реальные маршруты (move), удалить
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый shell.
Убрать флаг/кнопку. **И обновить `newCode` в `web/eslint.config.js`:** заменить
`src/routes/preview/**` на `src/routes/**` (после переезда все маршруты —
новый дизайн), компонентный glob `src/lib/components/kit/**` пока оставить.
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый
shell. Убрать флаг/кнопку. **И обновить `newCode` в
`web/eslint.config.js`:** заменить `src/routes/preview/**` на
`src/routes/**` (после переезда все маршруты — новый дизайн), компонентный
glob `src/lib/components/kit/**` пока оставить.
- [ ] **[C18]** Правки импортов/редиректов после переноса, проверка билда.
### Шаг 6. Удаление старого
- [ ] **[C19]** Удалить старое `ui/`, прочие старые компоненты, старый `app.css`,
неиспользуемые классы. Переименовать `kit/``ui/` (если решено на C1 —
тогда в `eslint.config.js` обновить и компонентный glob).
- [ ] **[C19]** Удалить старое `ui/`, прочие старые компоненты, старый
`app.css`, неиспользуемые классы. Переименовать `kit/``ui/` (если
решено на C1 — тогда в `eslint.config.js` обновить и компонентный glob).
**И раскрыть ESLint `recommended` на весь код** — убрать scoped-блок
`newCode` в `web/eslint.config.js` (см. AGENTS.md), прогнать `lint` и
починить всплывшие ошибки в оставшемся коде.
_Примечание: доменные модули (`lib/core`, `lib/registry`, тесты и т.п.) —
это не «старый дизайн», у них свой линт-долг; расширение `recommended` на
них может выдать много ошибок. Решить на C19: либо чиним сразу, либо
расширяем scoped только на `src/routes/**` + `src/lib/components/**`._
починить всплывшие ошибки в оставшемся коде. _Примечание: доменные модули
(`lib/core`, `lib/registry`, тесты и т.п.) — это не «старый дизайн», у них
свой линт-долг; расширение `recommended` на них может выдать много ошибок.
Решить на C19: либо чиним сразу, либо расширяем scoped только на
`src/routes/**` + `src/lib/components/**`._
- [ ] **[C20]** Финальный проход по брейкпоинтам 1200/1100/800/480; build без
предупреждений; `grep` по старым токенам/классам пуст.
- [ ] **[C21]** _(опционально)_ Удалить `refs/` и `refs-html/` из репо — они
больше не источник правды.
> Если какой-то коммит тянет за собой > 500 строк (например, C6/C7),
> разбивать на под-коммиты по 2–4 компонента. Тесты/билд прогонять после
> каждого коммита, чтобы регрессия локализовалась одним шагом назад.
> Если какой-то коммит тянет за собой > 500 строк (например, C6/C7), разбивать
> на под-коммиты по 2–4 компонента. Тесты/билд прогонять после каждого коммита,
> чтобы регрессия локализовалась одним шагом назад.
>
> **Атомарность и точки «поломки» между коммитами.**
>
> - C1+C2 уже сделаны вместе (связаны: нейтральный root без `(old)`-layout
> ломает старый сайт). Аналогично связанных пар больше нет.
> - C3 и C4 слиты: шрифты подключаются внутри `design2.css`, поэтому файл
> должен существовать в том же коммите.
> - Коммиты **C3→C16 не трогают старый сайт** `(old)/` — они только
> добавляют новые файлы/маршруты, сломать старый сайт на них нельзя.
> - C3 и C4 слиты: шрифты подключаются внутри `design2.css`, поэтому файл должен
> существовать в том же коммите.
> - Коммиты **C3→C16 не трогают старый сайт** `(old)/` — они только добавляют
> новые файлы/маршруты, сломать старый сайт на них нельзя.
> - **C17 — единственный атомарный переключатель**: перенос `preview/*` на
> реальные пути + удаление `(old)/` + смена root-layout должны быть в
> ОДНОМ коммите (иначе конфликт маршрутов или пропажа страниц). Не дробить.
> - **C19 должен идти строго после C17** (старый `app.css`/`ui/` удаляем
> только когда old-сайт уже выключен).
> - Очерёдность зависимостей: `design2.css` (C3) и kit-компоненты (C5–C7)
> должны существовать до страниц, которые их импортируют (C8–C14).
> реальные пути + удаление `(old)/` + смена root-layout должны быть в ОДНОМ
> коммите (иначе конфликт маршрутов или пропажа страниц). Не дробить.
> - **C19 должен идти строго после C17** (старый `app.css`/`ui/` удаляем только
> когда old-сайт уже выключен).
> - Очерёдность зависимостей: `design2.css` (C3) и kit-компоненты (C5–C7) должны
> существовать до страниц, которые их импортируют (C8–C14).
## Бэклог полировки (после C9–C21, отдельным проходом)
Конкретные расхождения витрины `preview/kit` с `refs-html/`. Править
системно в конце, а не по ходу сборки базы.
Конкретные расхождения витрины `preview/kit` с `refs-html/`. Править системно в
конце, а не по ходу сборки базы.
- **Toggle** в дизайне прямоугольный, у нас — закруглённый. Сделать
прямоугольным (или уточнить радиус у свича отдельно от общих токенов).
@@ -408,11 +422,11 @@ src/routes/preview/tools/[id]/+page.svelte
ненулевой `--radius` для всех бордеров. Проверить токен `--radius` против
референсов; возможно он должен быть 0 (или близко), а скругления оставить
только там, где они реально есть в дизайне.
- **DownloadButton**: нет отступа между текстом и размером файла
(`size`-чип). Добавить `gap`/отступ внутри кнопки.
- **DownloadButton**: нет отступа между текстом и размером файла (`size`-чип).
Добавить `gap`/отступ внутри кнопки.
- **Поля ввода (controls)**: выглядят без паддингов (возможно артефакт самой
витрины, но визуально плохо). Проверить паддинги в `TextField`/`SelectField`/
`ColorField`/`SliderField` против референса и поправить.
- **Segmented**: на витрине последняя кнопка растянута и нечитаемо, что это
сегмент. Проверить, как `Segmented` выглядит в реальных раскладках
(C9C14); возможно нужен фиксированный/авто размер сегментов или перенос.
сегмент. Проверить, как `Segmented` выглядит в реальных раскладках (C9C14);
возможно нужен фиксированный/авто размер сегментов или перенос.