Files
easy-png-tools2/docs/plan-redesign.md
T

383 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# План: переезд на новый дизайн (refs) — параллельная сборка
> Статус: C17 **выполнен** (`docs/archive/plan-c17.md`), C18 **выполнен**, Q3
> (удаление старого UI) **выполнен**. Остался C20 (шаг 6, проверка нового
> дизайна) — **следующий этап**. C21 (удалить refs) остаётся отдельной уборкой.
> Общий tech-debt baseline и следующие quality-задачи зафиксированы в
> `docs/plan-codebase-quality.md`. Покупка домена НЕ была блокером C17: домен
> нужен для S1 (`plan-seo.md`) — canonical, sitemap, снятие `noindex`.
>
> Источники: `refs/` — Next/React-референс (правда по пикселям и интерактиву),
> `refs-html/` — статические HTML-снимки для быстрого просмотра в браузере.
>
> **Историческая стратегия перехода:** сначала новый UI собирался отдельно,
> затем стал production, после чего legacy-ветка была удалена Q3. Текущие
> критерии относятся только к production UI; старые решения и детали перехода
> остаются в архивных планах.
## 0. Почему меняем стратегию
- Переход выполнялся параллельной сборкой, чтобы новый UI можно было проверить
независимо; теперь production использует только новые маршруты и `app.css`.
- Дизайн-токены и глобальные стили живут в `web/src/app.css`; отдельные layout
для удалённого UI-v1 больше не нужны.
## 1. Источники и их оценка
### `refs-html/` — витрина разметки
Покрытие достаточное:
| Файл | Что показывает | Оценка |
| ------------------------- | ----------------------------------------- | ---------------- |
| `index.html` | Главная: pipeline-воркспейс + превью | ✅ |
| `demo.html` | Зеркало `index.html` (идентичны) | ⚠️ дубликат |
| `gradient.html` | Инструмент «Градиент»: настройки + превью | ✅ (тёмная тема) |
| `background-remover.html` | Инструмент «Удаление фона»: source/result | ✅ (тёмная тема) |
| `list-tools.html` | Каталог инструментов | ✅ |
- **Статика без JS** — интерактив (тумблеры/слайдеры/перетаскивание) брать из
кода `refs/`, не из HTML. HTML — только визуальная правда по разметке/классам.
- По 4 файлам собран инвентарь классов — это и есть спецификация kit'а:
`app-shell`, `page-grid`, `eyebrow`, `brand-mark`, `topbar`, `footer`,
`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`.
- **Дыры в refs-html:** нет состояний empty/error/loading, нет глобальных
модалок/тултипов, i18n не представлен (все лейблы — английские брендовые).
Решения — см. §9.
## 2. Токены (фундамент нового дизайна)
Живут в `src/lib/styles/design2.css` как обычный `:root { … }` (плюс
`[data-theme='dark'] { … }`). Файл грузится **только** layout'ом новой ветки
(§5), поэтому глобально не конфликтует со старым `app.css`. Маппинг старых
токенов `web/src/app.css` → новые:
| Старый | Новый | Light | Dark |
| -------------- | -------------- | --------- | ------------------------ |
| `--bg` | `--background` | `#eef1f4` | `#11171d` |
| `--surface` | `--panel` | `#f8fafb` | `#182129` |
| `--text` | `--foreground` | `#17212b` | `#e8eef2` |
| `--text-muted` | `--muted` | `#6d7883` | `#91a0ac` |
| `--border` | `--line` | `#cbd3da` | `#33414c` |
| `--accent` | `--blue` | `#1769d2` | `#54a2ff` |
| — | `--cyan` | `#00a8c7` | `#00a8c7` |
| — | `--amber` | `#bd7411` | `#bd7411` |
| — | `--success` | `#25a96a` | `#25a96a` (статус-точки) |
| `--danger` | без изменений | `#e5484d` | `#ff7479` |
Прочее:
- Радиусы: один `--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`).
## 3. Базовые примитивы дизайн-системы (все — компоненты)
Правило фазы 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»).
- `Panel` / `PanelHeading` / `SettingGroup` / `SettingsFooter` — панель с
1px-бордером, составные части с разделителями.
- `CheckerCanvas` — рабочая поверхность превью: тёмная подложка + шахматка.
- `MetaRow` / `MetaList` — пары «CAPTION значение».
- `Segmented` — сегмент-переключатель (язык, тип градиента, пресеты).
- `Badge` — тег типа шага (BACKGROUND/TRANSFORM/STYLE), чип «✓ AUTO».
- `StepCard` — нумерованная карточка этапа: индекс `01`, drag-handle, Badge
типа, заголовок, кнопка удаления; слот для параметров.
- `ToolCard` — карточка каталога (icon, index, title, copy, arrow).
- `ImageCard` / `PreviewTile` — label сверху + canvas-превью.
- `AppShell` — корневой контейнер `.app-shell` + `.page-grid` (обёртка новой
ветки маршрутов); задаёт шрифты и фон нового дизайна.
- `BlueprintGrid` — фоновая сетка (используется в `AppShell`).
- `IconButton`, `Icon` (обёртка `lucide-svelte`, 16–17px, muted).
## 4. Минимальный UI-kit (`src/lib/components/kit/`)
Контролы, поверх примитивов §3. API проектируем сразу под новый дизайн (обратной
совместимости со старым `ui/` не нужно — старые страницы его продолжают
использовать).
| Компонент | Назначение |
| ------------------------- | ----------------------------------------------------------------- |
| `Button.svelte` | `primary` (solid blue) / `secondary` (outline) / `ghost` + `icon` |
| `Field.svelte` | обёртка: `MonoLabel` + слот значения/контрола |
| `SliderField.svelte` | range + `<output>`, опц. строка подсказок |
| `ColorField.svelte` | swatch + hex-input + нативный picker |
| `TextField.svelte` | текстовый ввод в стиле дизайна |
| `SelectField.svelte` | селект в стиле дизайна |
| `CheckboxField.svelte` | чекбокс → switch-toggle 32×18 |
| `Toggle.svelte` | переключатель (для toggle-group) |
| `SegmentedControl.svelte` | на базе `Segmented` — RU/EN, Linear/Radial, пресеты |
| `Badge.svelte` | на базе `Badge` |
| `MetaList.svelte` | на базе `MetaRow` |
| `EmptyState.svelte` | пустое состояние под новый стиль |
| `CodeBlock.svelte` | блок кода (gradient-art / direction-grid) |
| `DownloadButton.svelte` | кнопка скачивания (можно переиспользовать идею старой) |
Иконки: `lucide-svelte` (те же пути, MIT), 16–17px, stroke, muted.
## 5. Изоляция старого и нового (критично)
Без префиксов и обёрток — только за счёт того, какие layout грузят какой CSS.
- **Корневой 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`. Скоуп стилей
Svelte-компонентов добавляет второй уровень изоляции. Никаких `.ds2`.
- Переключатель для ручной проверки: временный флаг `previewDesign` в
`localStorage` + кнопка в старой шапке («Preview v2»), ведущая на `/preview`.
На фазе 4 флаг убираем.
> Почему не `(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.
## 6. Маршрутизация: историческая схема перехода
Во время перехода UI-kit проверялся в отдельной ветке, затем его страницы были
перенесены на production-маршруты. Эта схема нужна только для восстановления
контекста C17; текущая структура описана в разделе 7, отдельных preview- или
legacy-layouts в production больше нет.
## 7. Фазы выполнения
- **C1–C16 — UI-kit и проверка нового дизайна.** Токены, компоненты и
интерактивные состояния приняты; детали перехода остаются в архивном
`plan-design-fix.md`.
- **C17/C18 — production-переход.** Новые страницы перенесены на реальные
корневые маршруты; `app.css` стал единственным production-стилем.
- **Q3/C19 — cleanup.** Удалены UI-v1, legacy CSS, v1-тесты, fixtures и
isolation-конфигурация; production build и verify проверяются отдельно.
- **C20 — следующий этап.** Финальный проход по брейкпоинтам и визуальной
приёмке production UI.
- **C21 — отдельная уборка.** Удалить `refs/` и `refs-html/`, если они больше не
нужны.
Критерии каждого этапа — зелёные `test`/`check`/`build`, отсутствие остаточных
legacy-путей в production-коде и ревью в браузере для UI-изменений.
## 8. Смоук (после фазы 3, контроль на фазе 4)
1. Светлая и тёмная тема: сетка фона, панели, шахматка канвасов читаются,
контраст mono-лейблов достаточный.
2. Инструмент с параметрами (градиент): slider/color/toggle в стиле
setting-groups, автозапуск не регрессировал.
3. Удаление фона: сравнение source/result, маска, пипетка работают на новой
шахматке.
4. Chain из 3+ инструментов: step-cards рендерятся, удаление/сброс работают.
5. Узкий экран 800px: настройки над превью, sticky выключается как в рефе.
6. RU/EN: новые лейблы локализованы; mono-лейблы рефа — либо ключи i18n, либо
осознанно английские брендовые элементы (см. §9).
## 9. Открытые вопросы
- **Имя финального каталога компонентов:** решено на C1 — писали в `kit/`, C17
разложил его в `components/` (подпапки fields/layout/ui). На C21 — только
навести порядок внутри `components/`, переименование не требуется.
- **Единый production CSS:** `app.css` подключается в корневом layout; отдельных
legacy-layouts и переключателей версий больше нет.
- **Mono-лейблы рефа на английском** (`PROCESSING PIPELINE`): завести i18n-
ключи, но допустить часть английских брендовых («приборная панель»).
- **Версия в шапке/футере:** из `package.json` через `define` или захардкодить —
мелочь, решить при сборке topbar.
- **`refs/` и `refs-html/` — одноразовый источник, НЕ поддерживаемый.** После
внедрения нового дизайна все правки дизайна делаются сразу в продакшен-коде
(`design2.css` + `kit/`), а не в `refs/`. Механизм синхронизации «ref → код»
не нужен и не создаётся. `refs/` и `refs-html/` — только историческая справка;
их можно удалить в C21, они не являются частью рантайма.
## 10. План исполнения (мелкие коммиты)
Ветка `feat/redesign`. Каждый шаг — отдельный коммит, цель < ~500 строк. Перед
стартом: `test`/`build` зелёные на базе (якорь регрессии).
### Шаг 0. Изоляция layout (без дизайна)
- [x] **[C1]** Сделать корневой `routes/+layout.svelte` нейтральным: убрать
`import '../app.css'`, оставить только init темы/locale и
`<div>{children}</div>`.
- [x] **[C2]** Создать `routes/(old)/+layout.svelte` (импорт `app.css` + старая
шапка/подвал) и перенести старые страницы (`+page`, `demo`, `list-tools`,
`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] **[C4]** _(слит в C3: шрифты нельзя подключить до создания `design2.css`)_
`[data-theme='dark']` (таблица §2). Без утилитарных классов.
- [x] **[C5]** Примитивы batch 1: `AppShell`, `BlueprintGrid`, `CheckerCanvas`,
`MonoLabel`, `StatusDot`/`StatusLine`.
- [x] **[C6]** Примитивы batch 2: `Panel`, `PanelHeading`, `SettingGroup`,
`SettingsFooter`, `MetaRow`/`MetaList`, `Segmented`, `Badge`, `StepCard`,
`ToolCard`, `ImageCard`/`PreviewTile`.
- [x] **[C7]** Контролы: `Icon`, `IconButton`, `Button`, `Field`, `SliderField`,
`ColorField`, `TextField`, `SelectField`, `CheckboxField`, `Toggle`,
`SegmentedControl` (= `Segmented` из C6), `EmptyState`, `CodeBlock`,
`DownloadButton`.
### Шаг 2. Витрина kit (проверка компонентов)
- [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] **[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.
### Шаг 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`). Точечное выравнивание к рефу отложено: реф-дизайн
неконсистентен. (доказательство отсутствия регресса). Фиксы по результатам
— отдельными мелкими коммитами.
### Шаг 5. Сделать новый дизайн основным
> C17 — **выполнен** (`docs/archive/plan-c17.md`): production-маршруты
> перенесены в корень, `app.css` стал корневым. C18 (правки импортов/редиректов
> после переноса, проверка билда) — **выполнен**. Q3 удалил старые UI-маршруты и
> код. Остались C20 и C21.
- [x] **[C17]** Перенести `preview/*` → реальные маршруты (move), удалить
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый
shell. Убрать флаг/кнопку. **Снять `noindex, nofollow`** со страницы
инструмента (`preview/tools/[id]/+page.svelte`) — страницы становятся
боевыми лендингами ровно в момент переезда (SEO: `plan-seo.md`, S1a).
**Временное решение:** если домен ещё не куплен, `noindex` можно оставить
до S1 — тогда C17 делается без снятия `noindex`, а снятие происходит
вместе с подключением canonical в S1. **И обновить `newCode` в
`web/eslint.config.js`:** заменить `src/routes/preview/**` на
`src/routes/**` (после переезда все маршруты — новый дизайн), компонентный
glob `src/lib/components/kit/**` пока оставить. _(Итог: `newCode` обновлён
(`components/**`, `routes/**`), `noindex` оставлен до S1 — домен ещё не
куплен. Детали: `docs/archive/plan-c17.md`.)_
- [x] **[C18]** Правки импортов/редиректов после переноса, проверка билда.
_(Билд собирается без ошибок, линт/свелт-чека нет — выполнен.)_
### Шаг 6. Проверка нового дизайна
- [ ] **[C20]** Финальный проход по брейкпоинтам 1200/1100/800/480; build без
предупреждений; `grep` по старым токенам/классам пуст. Доказательство
отсутствия регресса: production-маршруты и основные пользовательские
потоки работают.
### Шаг 7. Cleanup
- [x] **[C19/Q3]** Удалить старое: маршруты `routes/v1/**`, код `lib/v1/**`,
`app_v1.css`, v1-тесты, fixtures и isolation-плагин; убрать v1-исключения
из конфигурации и активной документации.
- [ ] **[C21]** Удалить `refs/` и `refs-html/` из репо — они больше не источник
правды.
- [ ] Навести порядок в `components/` (согласовать подпапки, финальное имя
каталога).
> C19/Q3 — отдельная cleanup-задача, не источник пользовательской ценности.
> После удаления проверяются только production-маршруты и общие модули.
>
> Если какой-то коммит тянет за собой > 500 строк (например, C6/C7), разбивать
> на под-коммиты по 2–4 компонента. Тесты/билд прогонять после каждого коммита,
> чтобы регрессия локализовалась одним шагом назад.
>
> **Атомарность и точки «поломки» между коммитами.**
>
> - C1+C2 уже сделаны вместе (связаны: нейтральный root без `(old)`-layout
> ломает старый сайт). Аналогично связанных пар больше нет.
> - C3 и C4 слиты: шрифты подключаются внутри `design2.css`, поэтому файл должен
> существовать в том же коммите.
> - Коммиты **C3→C16 не трогают старый сайт** `(old)/` — они только добавляют
> новые файлы/маршруты, сломать старый сайт на них нельзя.
> - **C17 — атомарный переключатель**: перенос `preview/*` на реальные пути +
> удаление `(old)/` + смена root-layout — в ОДНОМ коммите. Снятие `noindex`
> также в C17, **если домен уже куплен**; иначе — отложить на S1. Не дробить
> сам C17.
> - Очерёдность зависимостей: `design2.css` (C3) и kit-компоненты (C5–C7) должны
> существовать до страниц, которые их импортируют (C8–C14).
## Бэклог полировки (после C9–C21, отдельным проходом)
Конкретные расхождения витрины `preview/kit` с `refs-html/`. Править системно в
конце, а не по ходу сборки базы.
- **Toggle** в дизайне прямоугольный, у нас — закруглённый. Сделать
прямоугольным (или уточнить радиус у свича отдельно от общих токенов).
- **Радиусы бордеров** не совпадают: в `refs` радиусы вообще 0, а мы взяли
ненулевой `--radius` для всех бордеров. Проверить токен `--radius` против
референсов; возможно он должен быть 0 (или близко), а скругления оставить
только там, где они реально есть в дизайне.
- **DownloadButton**: нет отступа между текстом и размером файла (`size`-чип).
Добавить `gap`/отступ внутри кнопки.
- **Поля ввода (controls)**: выглядят без паддингов (возможно артефакт самой
витрины, но визуально плохо). Проверить паддинги в `TextField`/`SelectField`/
`ColorField`/`SliderField` против референса и поправить.
- **Segmented**: на витрине последняя кнопка растянута и нечитаемо, что это
сегмент. Проверить, как `Segmented` выглядит в реальных раскладках (C9–C14);
возможно нужен фиксированный/авто размер сегментов или перенос.