mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 13:36:36 +00:00
Compare commits
13
Commits
8a2584ab54
...
f72ec96320
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f72ec96320 | ||
|
|
4223ee6aa9 | ||
|
|
75f6891e7c | ||
|
|
0160b316c9 | ||
|
|
33e3f3c4f0 | ||
|
|
1e5e096d5d | ||
|
|
f8a1da6ff7 | ||
|
|
f7623ab3bb | ||
|
|
45dc54b6aa | ||
|
|
85effce7d9 | ||
|
|
e970b47720 | ||
|
|
b6318f03ab | ||
|
|
b36a865a0c |
+17
-4
@@ -1,5 +1,14 @@
|
|||||||
# Backlog
|
# Backlog
|
||||||
|
|
||||||
|
## lint и прочие правила
|
||||||
|
|
||||||
|
- [ ] линт правило для `interface Props {...}` + `let {}: Props = $props()`
|
||||||
|
- [ ] линт правило для `object as const`, нежелательно использовать
|
||||||
|
`type Kind = 'a' | 'b' | 'c'` - это приводит к дублированию описаний в
|
||||||
|
разных местах, и к непонятным новым типам типа `type Kind2 = 'a' | 'c'`
|
||||||
|
- [ ] можно ли сделать правило для минимизации новых `const` определений? чтобы
|
||||||
|
дубли автоматически определялись? ИИ агент иногда дублирует определения
|
||||||
|
|
||||||
## Мелочи всякие
|
## Мелочи всякие
|
||||||
|
|
||||||
- [ ] сгенерировать или найти файлы для ручной проверки `A. Краевые PNG-файлы`
|
- [ ] сгенерировать или найти файлы для ручной проверки `A. Краевые PNG-файлы`
|
||||||
@@ -39,10 +48,13 @@
|
|||||||
- [ ] придумать процесс редактирования и настройки pipeline - drag-n-drop,
|
- [ ] придумать процесс редактирования и настройки pipeline - drag-n-drop,
|
||||||
удаление, добавление инструментов. переход один инструмент - pipeline без
|
удаление, добавление инструментов. переход один инструмент - pipeline без
|
||||||
перезагрузки
|
перезагрузки
|
||||||
|
- [ ] Слаги инструментов (`*-png`) — надо обдумать, возможно что ссылки\маршруты
|
||||||
|
должны быть с png, а названия инструментов - без. Это упростит названия
|
||||||
|
инструментов и сделает их более понятными
|
||||||
|
|
||||||
## Переезд
|
## Переезд
|
||||||
|
|
||||||
- [ ] составить план переноса preview -> корень, старый корень -> old
|
- [x] составить план переноса preview -> корень, старый корень -> old
|
||||||
- [ ] добавить i18n в preview
|
- [ ] добавить i18n в preview
|
||||||
|
|
||||||
## SEO/GEO
|
## SEO/GEO
|
||||||
@@ -178,9 +190,10 @@
|
|||||||
|
|
||||||
14. ~~**Переезд старых файлов в папки `old/`**~~ — **ЗАКРЫТО.** Старые файлы
|
14. ~~**Переезд старых файлов в папки `old/`**~~ — **ЗАКРЫТО.** Старые файлы
|
||||||
(`lib/registry.ts`, `lib/registry/`, `lib/registry-helpers.ts`,
|
(`lib/registry.ts`, `lib/registry/`, `lib/registry-helpers.ts`,
|
||||||
`lib/categories.ts`, `lib/tools/`, `lib/components/` кроме `kit/`) перенесены
|
`lib/categories.ts`, `lib/tools/`, `lib/components/` кроме `kit/`)
|
||||||
в `lib/old/`. Isolation-плагин упрощён до `lib/old/**`. Все импорты обновлены.
|
перенесены в `lib/old/`. Isolation-плагин упрощён до `lib/old/**`. Все
|
||||||
svelte-check 0, тесты 618/618, lint:all чист, билд проходит.
|
импорты обновлены. svelte-check 0, тесты 618/618, lint:all чист, билд
|
||||||
|
проходит.
|
||||||
|
|
||||||
15. ~~**Техдолг (чинить по заведённым задачам, не игнорировать правила)**~~
|
15. ~~**Техдолг (чинить по заведённым задачам, не игнорировать правила)**~~
|
||||||
ЗАКРЫТО. Подробности: `docs/archive/plan-tech-debt.md`.
|
ЗАКРЫТО. Подробности: `docs/archive/plan-tech-debt.md`.
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# План C17: перенос preview → корень + (old) → /v1/
|
||||||
|
|
||||||
|
> Статус: план к выполнению. Предпосылки: tech debt закрыт, backlog №14 (старые
|
||||||
|
> файлы → lib/old/) выполнен. Домен НЕ блокер (см. plan-redesign.md §10,
|
||||||
|
> plan-seo.md §6).
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Новый дизайн становится основным на корневых маршрутах (`/`, `/tools/[id]`,
|
||||||
|
`/list-tools`). Старый дизайн переезжает на `/v1/*` как архив для справки.
|
||||||
|
Удаление старого кода (C19) откладывается до полной приёмки нового дизайна.
|
||||||
|
|
||||||
|
## Структура маршрутов (до → после)
|
||||||
|
|
||||||
|
**До:** `(old)/` даёт URLs `/`, `/demo`, `/list-tools`, `/tools/[id]`.
|
||||||
|
`preview/` даёт `/preview`, `/preview/list-tools`, `/preview/tools/[id]`,
|
||||||
|
`/preview/kit`.
|
||||||
|
|
||||||
|
**После:** `v1/` даёт `/v1/`, `/v1/demo`, `/v1/list-tools`, `/v1/tools/[id]`.
|
||||||
|
Корень даёт `/`, `/list-tools`, `/tools/[id]`. Витрина kit — на реальном
|
||||||
|
маршруте `/kit` (дизайн в стадии тестирования).
|
||||||
|
|
||||||
|
## Решения
|
||||||
|
|
||||||
|
1. **`v1/` — реальная папка, НЕ route group** — group `(v1)/` не меняет URL, а
|
||||||
|
нам нужно чтобы старые страницы жили по `/v1/*`.
|
||||||
|
2. **Корневой layout: shell нового дизайна** — TopBar, Footer, тема, хлебные
|
||||||
|
крошки, импорт `app.css`, `@layer app, design;`.
|
||||||
|
3. **`v1/+layout.svelte` — только импорт `app_v1.css`** — без
|
||||||
|
header/footer/shell.
|
||||||
|
4. **Тема:** использовать `theme.svelte.ts` (не дублировать локальный `$state`).
|
||||||
|
5. **`@layer app, design;`** остаётся — design выигрывает у app везде.
|
||||||
|
6. **`noindex` остаётся** на `tools/[id]/+page.svelte` до покупки домена.
|
||||||
|
7. **CSS-файлы переименовываются:** `preview.css` → `app.css`, `old.css` →
|
||||||
|
`app_v1.css`. Все ссылки обновляются.
|
||||||
|
8. **`kit/` витрина → `/kit`** — переносится на реальный маршрут вместе с
|
||||||
|
остальными preview-страницами; отдельный `preview/+layout` удаляется (`/kit`
|
||||||
|
наследует корневой shell).
|
||||||
|
|
||||||
|
## Что делать (шаги)
|
||||||
|
|
||||||
|
### 1. Rename `(old)/` → `v1/`
|
||||||
|
|
||||||
|
Переименовать папку. Обновить `v1/+layout.svelte`: убрать «Preview v2» ссылку,
|
||||||
|
обновить пути навигации, поправить импорт CSS.
|
||||||
|
|
||||||
|
### 2. Новый корневой `+layout.svelte` + rename CSS
|
||||||
|
|
||||||
|
Перезаписать текущий нейтральный layout содержимым из `preview/+layout.svelte`
|
||||||
|
(с правками: тема через `theme.svelte.ts`, путь к CSS). Переименовать
|
||||||
|
`src/preview.css` → `src/app.css`, `src/old.css` → `src/app_v1.css`. Обновить
|
||||||
|
все ссылки на эти файлы в проекте ( layout, лайнтеры, eslint-плагины, скрипты,
|
||||||
|
тесты).
|
||||||
|
|
||||||
|
### 3. Move preview pages → корень
|
||||||
|
|
||||||
|
Переместить страницы из `preview/` в корень `routes/`: `+page` → `/`,
|
||||||
|
`list-tools` → `/`, `tools/[id]` → `/`, витрину `kit` → `/kit`. Удалить
|
||||||
|
`preview/+layout.svelte` (корневой shell заменяет его).
|
||||||
|
|
||||||
|
### 4. Update hardcoded `/preview/` URLs
|
||||||
|
|
||||||
|
В компонентах с захардкоженными путями `/preview/` заменить на корневые.
|
||||||
|
|
||||||
|
### 5. Flatten `kit/` → `components/`
|
||||||
|
|
||||||
|
Перенести компоненты из `lib/components/kit/` в `lib/components/`, обновить все
|
||||||
|
импорты, удалить пустую `kit/`.
|
||||||
|
|
||||||
|
### 6. Flatten `preview/` → `lib/`
|
||||||
|
|
||||||
|
Перенести код из `lib/previw/` в `lib/`, обновить все импорты, удалить пустую
|
||||||
|
`preview/`.
|
||||||
|
|
||||||
|
### 7. Перенести `old/` → `v1/`
|
||||||
|
|
||||||
|
- `$lib/old/` — переименовать в `lib/v1/`, обновить все импорты
|
||||||
|
|
||||||
|
### 8. E2E тесты + playwright config
|
||||||
|
|
||||||
|
Заменить `/preview/...` на `/...` во всех e2e файлах и конфиге playwright.
|
||||||
|
|
||||||
|
### 9. ESLint config + AGENTS.md
|
||||||
|
|
||||||
|
Обновить glob-паттерны: `routes/preview/**` → `routes/**`, `components/kit/**` →
|
||||||
|
`components/**`. Обновить `AGENTS.md`.
|
||||||
|
|
||||||
|
### 10. Isolation plugin
|
||||||
|
|
||||||
|
Обновить `DEFAULT_OLD`: `routes/(old)/**` → `routes/v1/**`. Убрать
|
||||||
|
`routes/preview/**` из `DEFAULT_NEW`.
|
||||||
|
|
||||||
|
### 11. Archive audit scripts
|
||||||
|
|
||||||
|
Перенести скрипты аудита в `scripts/archive/`, убрать вызовы из манифестов.
|
||||||
|
|
||||||
|
## Что НЕ меняется
|
||||||
|
|
||||||
|
- `routes/+layout.ts` — prerender = true остаётся
|
||||||
|
- Слаги инструментов (`*-png`) — без изменений
|
||||||
|
- `/kit` — витрина дизайна остаётся (в стадии тестирования)
|
||||||
|
|
||||||
|
## Риски
|
||||||
|
|
||||||
|
- **v1/ без shell** — старый дизайн без header/footer.
|
||||||
|
- **`@layer` взаимодействие** — проверить что design-токены перекрывают
|
||||||
|
app-токены на обоих маршрутах.
|
||||||
|
- **Isolation: shared-классификация** — маршруты нового дизайна shared до C19.
|
||||||
+20
-12
@@ -3,8 +3,10 @@
|
|||||||
> Статус: план к выполнению. Шаги 5–6 (C17–C21) остаются **следующим этапом**.
|
> Статус: план к выполнению. Шаги 5–6 (C17–C21) остаются **следующим этапом**.
|
||||||
> Они больше не привязаны к «верности против рефа»:
|
> Они больше не привязаны к «верности против рефа»:
|
||||||
> `docs/archive/plan-design-fix.md` закрыт, аудит против рефа прекращён.
|
> `docs/archive/plan-design-fix.md` закрыт, аудит против рефа прекращён.
|
||||||
> Tech debt закрыт (`docs/archive/plan-tech-debt.md`). Перед C17: backlog №14
|
> Tech debt закрыт (`docs/archive/plan-tech-debt.md`). Backlog №14 (переезд
|
||||||
> (переезд старых файлов в `old/`) и покупка домена (`plan-domain.local.md`).
|
> старых файлов в `old/`) — **выполнено**. Покупка домена НЕ блокер C17:
|
||||||
|
> домен нужен для S1 (`plan-seo.md`) — canonical, sitemap, снятие `noindex`.
|
||||||
|
> C17 можно запускать без домена (см. §10).
|
||||||
>
|
>
|
||||||
> Источники: `refs/` — Next/React-референс (правда по пикселям и интерактиву),
|
> Источники: `refs/` — Next/React-референс (правда по пикселям и интерактиву),
|
||||||
> `refs-html/` — статические HTML-снимки для быстрого просмотра в браузере.
|
> `refs-html/` — статические HTML-снимки для быстрого просмотра в браузере.
|
||||||
@@ -358,18 +360,23 @@ src/routes/preview/tools/[id]/+page.svelte
|
|||||||
|
|
||||||
### Шаг 5. Сделать новый дизайн основным — СЛЕДУЮЩИЙ ЭТАП
|
### Шаг 5. Сделать новый дизайн основным — СЛЕДУЮЩИЙ ЭТАП
|
||||||
|
|
||||||
> Tech debt закрыт. Перед стартом C17: покупка домена (`plan-domain.local.md`).
|
> Tech debt закрыт. Backlog №14 (переезд старых файлов в `lib/old/`) — **выполнено**.
|
||||||
> Переезд старых файлов в `lib/old/` (backlog №14) — **выполнено**.
|
> Покупка домена — НЕ блокер C17. C17 переносит маршруты и делает новый дизайн
|
||||||
|
> основным; `noindex` остаётся до покупки домена и запуска S1 (`plan-seo.md`).
|
||||||
|
> Детальный план см. в `docs/plan-c17.md` (старый `(old)/` → `/v1/`, удаление
|
||||||
|
> отложено до C19).
|
||||||
|
|
||||||
- [ ] **[C17]** Перенести `preview/*` → реальные маршруты (move), удалить
|
- [ ] **[C17]** Перенести `preview/*` → реальные маршруты (move), удалить
|
||||||
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый
|
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый
|
||||||
shell. Убрать флаг/кнопку. **И обновить `newCode` в
|
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/**` на
|
`web/eslint.config.js`:** заменить `src/routes/preview/**` на
|
||||||
`src/routes/**` (после переезда все маршруты — новый дизайн), компонентный
|
`src/routes/**` (после переезда все маршруты — новый дизайн), компонентный
|
||||||
glob `src/lib/components/kit/**` пока оставить. **И снять
|
glob `src/lib/components/kit/**` пока оставить.
|
||||||
`noindex, nofollow`** со страницы инструмента
|
|
||||||
(`preview/tools/[id]/+page.svelte`) — страницы становятся боевыми
|
|
||||||
лендингами ровно в момент переезда (SEO: `plan-seo.md`, S1a).
|
|
||||||
- [ ] **[C18]** Правки импортов/редиректов после переноса, проверка билда.
|
- [ ] **[C18]** Правки импортов/редиректов после переноса, проверка билда.
|
||||||
|
|
||||||
### Шаг 6. Удаление старого
|
### Шаг 6. Удаление старого
|
||||||
@@ -401,9 +408,10 @@ src/routes/preview/tools/[id]/+page.svelte
|
|||||||
> существовать в том же коммите.
|
> существовать в том же коммите.
|
||||||
> - Коммиты **C3→C16 не трогают старый сайт** `(old)/` — они только добавляют
|
> - Коммиты **C3→C16 не трогают старый сайт** `(old)/` — они только добавляют
|
||||||
> новые файлы/маршруты, сломать старый сайт на них нельзя.
|
> новые файлы/маршруты, сломать старый сайт на них нельзя.
|
||||||
> - **C17 — единственный атомарный переключатель**: перенос `preview/*` на
|
> - **C17 — атомарный переключатель**: перенос `preview/*` на реальные пути +
|
||||||
> реальные пути + удаление `(old)/` + смена root-layout должны быть в ОДНОМ
|
> удаление `(old)/` + смена root-layout — в ОДНОМ коммите. Снятие `noindex`
|
||||||
> коммите (иначе конфликт маршрутов или пропажа страниц). Не дробить.
|
> также в C17, **если домен уже куплен**; иначе — отложить на S1. Не дробить
|
||||||
|
> сам C17.
|
||||||
> - **C19 должен идти строго после C17** (старый `app.css`/`ui/` удаляем только
|
> - **C19 должен идти строго после C17** (старый `app.css`/`ui/` удаляем только
|
||||||
> когда old-сайт уже выключен).
|
> когда old-сайт уже выключен).
|
||||||
> - Очерёдность зависимостей: `design2.css` (C3) и kit-компоненты (C5–C7) должны
|
> - Очерёдность зависимостей: `design2.css` (C3) и kit-компоненты (C5–C7) должны
|
||||||
|
|||||||
+40
-22
@@ -1,8 +1,11 @@
|
|||||||
# План: SEO + GEO — индексация и конкуренция в image tools
|
# План: SEO + GEO — индексация и конкуренция в image tools
|
||||||
|
|
||||||
> **Статус (2026-09-09):** план к выполнению. Старт — после C17
|
> **Статус (2026-09-10):** план к выполнению. Старт — после C17
|
||||||
> (`plan-redesign.md`): до переезда `preview/*` на реальные маршруты страницы
|
> (`plan-redesign.md`): до переезда `preview/*` на реальные маршруты страницы
|
||||||
> инструментов в `noindex`, применять техминимум некуда.
|
> инструментов в `noindex`, применять техминимум некуда.
|
||||||
|
> **Уточнено:** домен НЕ блокер C17 (см. §6). Задачи S1 разбиты на
|
||||||
|
> domain-free (можно делать параллельно с покупкой домена) и domain-required
|
||||||
|
> (после покупки и настройки домена на Vercel).
|
||||||
>
|
>
|
||||||
> **Цель:** каждая страница инструмента — индексируемый лендинг под конкретный
|
> **Цель:** каждая страница инструмента — индексируемый лендинг под конкретный
|
||||||
> интент («crop png», «make png transparent», «jpg to png»…), конкуренция с
|
> интент («crop png», «make png transparent», «jpg to png»…), конкуренция с
|
||||||
@@ -133,22 +136,16 @@
|
|||||||
|
|
||||||
### S1. Технический минимум (сразу после C17)
|
### S1. Технический минимум (сразу после C17)
|
||||||
|
|
||||||
- [ ] **[S1a]** Снять `noindex, nofollow` со страницы инструмента
|
Разбито на две группы: **domain-free** (делаем параллельно с покупкой домена)
|
||||||
(`preview/tools/[id]/+page.svelte`) — встроено в C17 как атомарный шаг
|
и **domain-required** (после покупки и DNS-настройки).
|
||||||
(см. `plan-redesign.md`).
|
|
||||||
- [ ] **[S1b]** Скрипт `web/scripts/gen-sitemap.mjs`: генерирует `sitemap.xml` в
|
#### S1-free: без домена (параллельно с C18–C19)
|
||||||
`build/` из `TOOLS` реестра (`registry-new`) + статических страниц. Вызов
|
|
||||||
из `pnpm build` после `vite build` (adapter-static пишет в `build/`,
|
|
||||||
дописываем файл там же). Origin — из env (`PUBLIC_SITE_ORIGIN`), учёт
|
|
||||||
опционального `BASE_PATH`.
|
|
||||||
- [ ] **[S1c]** `robots.txt`: `Sitemap:` + закрыть служебные маршруты
|
|
||||||
(`/preview/kit` до C17; после — вернуть минимальный allow-all).
|
|
||||||
- [ ] **[S1d]** Компонент `Seo.svelte` (kit): canonical (абсолютный URL по
|
|
||||||
актуальному маршруту), OG-теги (title/description/type/url/image),
|
|
||||||
twitter:card. Подключить в root-layout.
|
|
||||||
- [ ] **[S1e]** OG-картинки per-tool: генерация PNG-карточек скриптом на
|
- [ ] **[S1e]** OG-картинки per-tool: генерация PNG-карточек скриптом на
|
||||||
существующем ядре (мы сами умеем рисовать PNG) — имя инструмента + бренд,
|
существующем ядре (мы сами умеем рисовать PNG) — имя инструмента + бренд,
|
||||||
файл `static/og/[id].png`, ссылка из `Seo.svelte`.
|
файл `static/og/[id].png`, ссылка из `Seo.svelte`. Относительные пути
|
||||||
|
(`/og/crop-png.png`) работают без домена; при деплое на Vercel с
|
||||||
|
собственным доменом OG-картинки будут корректными.
|
||||||
- [ ] **[S1f]** Download-кнопка с селектором формата PNG/JPG/WebP/BMP (+quality
|
- [ ] **[S1f]** Download-кнопка с селектором формата PNG/JPG/WebP/BMP (+quality
|
||||||
для lossy) для всех инструментов — механика `ToolEntry.output` уже есть
|
для lossy) для всех инструментов — механика `ToolEntry.output` уже есть
|
||||||
(backlog №7, райз «выбор формата/качества»). Продуктовая основа «image
|
(backlog №7, райз «выбор формата/качества»). Продуктовая основа «image
|
||||||
@@ -159,8 +156,27 @@
|
|||||||
png. Существующие страницы не трогать: `jpg-to-png`, `png-to-jpg`,
|
png. Существующие страницы не трогать: `jpg-to-png`, `png-to-jpg`,
|
||||||
`png-to-webp`, `png-to-bmp` остаются самостоятельными лендингами со своим
|
`png-to-webp`, `png-to-bmp` остаются самостоятельными лендингами со своим
|
||||||
контентом (§5).
|
контентом (§5).
|
||||||
- Гейты: build зелёный; в `build/` лежит корректный `sitemap.xml` со всеми
|
|
||||||
инструментами; canonical/OG присутствуют на всех страницах; e2e зелёные.
|
#### S1-domain: с доменом (после покупки + DNS на Vercel)
|
||||||
|
|
||||||
|
- [ ] **[S1a]** Снять `noindex, no_follow` со страницы инструмента
|
||||||
|
(`tools/[id]/+page.svelte`) — делается одновременно с S1d (canonical)
|
||||||
|
чтобы Google сразу проиндексировал с правильным доменом.
|
||||||
|
- [ ] **[S1b]** Скрипт `web/scripts/gen-sitemap.mjs`: генерирует `sitemap.xml` в
|
||||||
|
`build/` из `TOOLS` реестра (`registry-new`) + статических страниц. Вызов
|
||||||
|
из `pnpm build` после `vite build` (adapter-static пишет в `build/`,
|
||||||
|
дописываем файл там же). Origin — из env (`PUBLIC_SITE_ORIGIN`), учёт
|
||||||
|
опционального `BASE_PATH`.
|
||||||
|
- [ ] **[S1c]** `robots.txt`: `Sitemap:` + закрыть служебные маршруты
|
||||||
|
(`/kit` до C19; после — вернуть минимальный allow-all).
|
||||||
|
- [ ] **[S1d]** Компонент `Seo.svelte` (kit): canonical (абсолютный URL по
|
||||||
|
актуальному маршруту), OG-теги (title/description/type/url/image),
|
||||||
|
twitter:card. Подключить в root-layout. **Требует `PUBLIC_SITE_ORIGIN`**
|
||||||
|
для абсолютных URL в canonical и OG:url.
|
||||||
|
|
||||||
|
> **Порядок S1:** S1e/S1f/S1g → покупка домена → S1a+S1b+S1c+S1d.
|
||||||
|
> Гейты: build зелёный; в `build/` лежит корректный `sitemap.xml` со всеми
|
||||||
|
> инструментами; canonical/OG присутствуют на всех страницах; e2e зелёные.
|
||||||
|
|
||||||
### S2. Контентный слой (параллельно C18–C19, волнами по категориям)
|
### S2. Контентный слой (параллельно C18–C19, волнами по категориям)
|
||||||
|
|
||||||
@@ -297,7 +313,7 @@ with PNG, JPG, WebP» в тексте + селектор формата в Downl
|
|||||||
интенты, которые подтверждены вордстатом, и сразу с контентом S2 (страница без
|
интенты, которые подтверждены вордстатом, и сразу с контентом S2 (страница без
|
||||||
текста — потраченный URL).
|
текста — потраченный URL).
|
||||||
|
|
||||||
## 6. Переименование бренда и домен (сделать до S1, дёшево сейчас)
|
## 6. Переименование бренда и домен (сделать до S1-domain, дёшево сейчас)
|
||||||
|
|
||||||
Новое имя и конкретный домен **не записаны в этом репозитории** — детальный
|
Новое имя и конкретный домен **не записаны в этом репозитории** — детальный
|
||||||
чек-лист в `docs/plan-domain.local.md` (исключён из git через `.gitignore`),
|
чек-лист в `docs/plan-domain.local.md` (исключён из git через `.gitignore`),
|
||||||
@@ -311,10 +327,12 @@ with PNG, JPG, WebP» в тексте + селектор формата в Downl
|
|||||||
ключи localStorage (`easy-png-tools:*` — или мигрировать отдельной
|
ключи localStorage (`easy-png-tools:*` — или мигрировать отдельной
|
||||||
мелочью), историю docs/archive.
|
мелочью), историю docs/archive.
|
||||||
- [ ] **[R2]** Купить выбранный домен (кандидаты и пробы — в локальном плане) и
|
- [ ] **[R2]** Купить выбранный домен (кандидаты и пробы — в локальном плане) и
|
||||||
настроить деплой без `BASE_PATH` (§0). Домен нужен до S1: canonical и
|
настроить деплой на Vercel без `BASE_PATH` (§0). Домен нужен до S1-domain:
|
||||||
sitemap генерятся от `PUBLIC_SITE_ORIGIN`.
|
canonical и sitemap генерятся от `PUBLIC_SITE_ORIGIN`.
|
||||||
- Порядок: R1+R2 в любой момент до C17; после индексации переименование =
|
- **Порядок:** R1+R2 можно делать параллельно с C17/C18/C19 (не блокируют).
|
||||||
переезд с 301, потеря позиций и ссылок — не делать.
|
R2 (домен) — блокер только для S1-domain (canonical + sitemap + снятие
|
||||||
|
`noindex`). После индексации переименование = переезд с 301, потеря позиций
|
||||||
|
и ссылок — не делать. Поэтому R1/R2 — до S1-domain, но не обязательно до C17.
|
||||||
|
|
||||||
## 7. Что осознанно НЕ делаем
|
## 7. Что осознанно НЕ делаем
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -3,7 +3,9 @@
|
|||||||
> **Статус:** Фаза 1 (полноценный TS-сайт) в основном выполнена —
|
> **Статус:** Фаза 1 (полноценный TS-сайт) в основном выполнена —
|
||||||
> сайт живёт, каталог переведён в типизированный `registry-new` (121/125), идёт
|
> сайт живёт, каталог переведён в типизированный `registry-new` (121/125), идёт
|
||||||
> редизайн на `preview/*` по `plan-redesign.md` (параллельная ветка, старый UI
|
> редизайн на `preview/*` по `plan-redesign.md` (параллельная ветка, старый UI
|
||||||
> на `(old)/`). Tech debt закрыт. Фазы 2–8 — будущие. Фазы 9–10
|
> на `(old)/`). Tech debt закрыт. Переезд старых файлов в `old/` выполнен.
|
||||||
|
> Следующий этап: C17 (перенос preview → корневые маршруты). Домен НЕ блокер
|
||||||
|
> C17, нужен для SEO (`plan-seo.md`, S1-domain). Фазы 2–8 — будущие. Фазы 9–10
|
||||||
> спланированы в `docs/plan-platform.md`.
|
> спланированы в `docs/plan-platform.md`.
|
||||||
|
|
||||||
## 0. Решения
|
## 0. Решения
|
||||||
|
|||||||
+2
-4
@@ -4,14 +4,12 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "pnpm --dir web test",
|
||||||
"dev": "pnpm --dir web dev",
|
"dev": "pnpm --dir web dev",
|
||||||
"build": "pnpm --dir web build",
|
"build": "pnpm --dir web build",
|
||||||
"format": "pnpm format:docs && pnpm --dir web format",
|
"format": "pnpm format:docs && pnpm --dir web format",
|
||||||
"format:docs": "prettier --write docs --log-level warn",
|
"format:docs": "prettier --write docs --log-level warn",
|
||||||
"check:docs": "prettier --check docs",
|
"check:docs": "prettier --check docs"
|
||||||
"html": "pnpm --dir refs build && node scripts/extract-static.mjs refs/.next/server/app refs-html",
|
|
||||||
"cdp-audit": "pnpm --dir web refs-cdp-audit:large"
|
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Ku6epXBOCTuK",
|
"author": "Ku6epXBOCTuK",
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { expect, test } from "playwright/test";
|
import { expect, test } from "playwright/test";
|
||||||
import { trackErrors, expectNoErrors } from "./helpers/page";
|
import { expectNoErrors, trackErrors } from "./helpers/page";
|
||||||
|
|
||||||
const TOTAL = 121;
|
const TOTAL = 121;
|
||||||
const GROUPS = 8;
|
const GROUPS = 8;
|
||||||
|
|
||||||
test("catalog shows total and all groups", async ({ page }) => {
|
test("catalog shows total and all groups", async ({ page }) => {
|
||||||
const sink = trackErrors(page);
|
const sink = trackErrors(page);
|
||||||
await page.goto("/preview/list-tools");
|
await page.goto("/list-tools");
|
||||||
await expect(page.locator(".catalog-total b")).toHaveText(String(TOTAL));
|
await expect(page.locator(".catalog-total b")).toHaveText(String(TOTAL));
|
||||||
await expect(page.locator(".catalog-group")).toHaveCount(GROUPS);
|
await expect(page.locator(".catalog-group")).toHaveCount(GROUPS);
|
||||||
expectNoErrors(sink);
|
expectNoErrors(sink);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("catalog search narrows results", async ({ page }) => {
|
test("catalog search narrows results", async ({ page }) => {
|
||||||
await page.goto("/preview/list-tools");
|
await page.goto("/list-tools");
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await page.getByRole("textbox", { name: "Search tools" }).fill("resize");
|
await page.getByRole("textbox", { name: "Search tools" }).fill("resize");
|
||||||
await expect(page.locator(".tool-card")).toHaveCount(1);
|
await expect(page.locator(".tool-card")).toHaveCount(1);
|
||||||
@@ -24,7 +24,7 @@ test("catalog search narrows results", async ({ page }) => {
|
|||||||
test("category filter shows only matching group and resets on ALL", async ({
|
test("category filter shows only matching group and resets on ALL", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
await page.goto("/preview/list-tools");
|
await page.goto("/list-tools");
|
||||||
const allButtons = page.locator(".tool-card");
|
const allButtons = page.locator(".tool-card");
|
||||||
const allCount = await allButtons.count();
|
const allCount = await allButtons.count();
|
||||||
expect(allCount).toBe(TOTAL);
|
expect(allCount).toBe(TOTAL);
|
||||||
@@ -46,7 +46,7 @@ test("category filter shows only matching group and resets on ALL", async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("workspace search matches a tool by title", async ({ page }) => {
|
test("workspace search matches a tool by title", async ({ page }) => {
|
||||||
await page.goto("/preview");
|
await page.goto("/");
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await page.getByRole("textbox", { name: "Search tools" }).fill("flip");
|
await page.getByRole("textbox", { name: "Search tools" }).fill("flip");
|
||||||
await expect(page.locator(".tool-card")).toHaveCount(1);
|
await expect(page.locator(".tool-card")).toHaveCount(1);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { expect, test } from "playwright/test";
|
import { expect, test } from "playwright/test";
|
||||||
import { trackErrors, openTool, expectNoErrors } from "./helpers/page";
|
import { expectNoErrors, openTool, trackErrors } from "./helpers/page";
|
||||||
|
|
||||||
test.describe("generators — known UI gap (#checklist, п.1)", () => {
|
test.describe("generators — known UI gap (#checklist, п.1)", () => {
|
||||||
// Генераторы (21/121) открываются, но в UI нет ни полей схемы, ни кнопки
|
// FIXME: Генераторы (21/121) открываются, но в UI нет ни полей схемы, ни кнопки
|
||||||
// «Generate» — только RU/EN/Reset. Пока баг открыт — fixme с ожидаемым
|
// «Generate» — только RU/EN/Reset. Пока баг открыт — fixme с ожидаемым
|
||||||
// сценарием; после фикса убрать fixme.
|
// сценарием; после фикса убрать fixme.
|
||||||
test.fixme("single-color-png renders Generate controls and produces a result", async ({
|
test.fixme("single-color-png renders Generate controls and produces a result", async ({
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function trackErrors(page: Page): ErrorSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function openTool(page: Page, id: string): Promise<void> {
|
export async function openTool(page: Page, id: string): Promise<void> {
|
||||||
await page.goto(`/preview/tools/${id}`);
|
await page.goto(`/tools/${id}`);
|
||||||
await expect(page.locator(".schema-tool h1")).toBeVisible();
|
await expect(page.locator(".schema-tool h1")).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect, test } from "playwright/test";
|
|||||||
import { opaquePng } from "./helpers/fixtures";
|
import { opaquePng } from "./helpers/fixtures";
|
||||||
import { openTool, uploadImage } from "./helpers/page";
|
import { openTool, uploadImage } from "./helpers/page";
|
||||||
|
|
||||||
// Весь файл — зарегистрированные баги preview (см. docs/checklist-manual-testing.md
|
// FIXME: Весь файл — зарегистрированные баги preview (см. docs/checklist-manual-testing.md
|
||||||
// → «Известные баги preview»). Тела assert'ят ОЖИДАЕМОЕ поведение. Статус fixme
|
// → «Известные баги preview»). Тела assert'ят ОЖИДАЕМОЕ поведение. Статус fixme
|
||||||
// означает «мы знаем, что сейчас падает»; когда баг починят — убрать fixme и
|
// означает «мы знаем, что сейчас падает»; когда баг починят — убрать fixme и
|
||||||
// тест станет зелёным «сам по себе».
|
// тест станет зелёным «сам по себе».
|
||||||
|
|||||||
+14
-10
@@ -1,12 +1,7 @@
|
|||||||
import { expect, test } from "playwright/test";
|
import { expect, test } from "playwright/test";
|
||||||
import { trackErrors, expectNoErrors } from "./helpers/page";
|
import { expectNoErrors, trackErrors } from "./helpers/page";
|
||||||
|
|
||||||
const ROUTES = [
|
const ROUTES = ["/", "/list-tools", "/kit", "/tools/flip-png"];
|
||||||
"/preview",
|
|
||||||
"/preview/list-tools",
|
|
||||||
"/preview/kit",
|
|
||||||
"/preview/tools/flip-png",
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const route of ROUTES) {
|
for (const route of ROUTES) {
|
||||||
test(`page ${route} loads without errors`, async ({ page }) => {
|
test(`page ${route} loads without errors`, async ({ page }) => {
|
||||||
@@ -19,14 +14,23 @@ for (const route of ROUTES) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("unknown tool route responds 404 on static build", async ({ page }) => {
|
test("unknown tool route responds 404 on static build", async ({ page }) => {
|
||||||
const resp = await page.goto("/preview/tools/definitely-not-a-tool");
|
const resp = await page.goto("/tools/definitely-not-a-tool");
|
||||||
expect(resp?.status()).toBe(404);
|
expect(resp?.status()).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
// Error: expect(received).toBe(expected) // Object.is equality
|
||||||
|
|
||||||
|
// Expected: "dark"
|
||||||
|
// Received: null
|
||||||
|
|
||||||
|
// 36 | expect(["light", "dark"]).toContain(before);
|
||||||
|
// 37 | expect(["light", "dark"]).toContain(after);
|
||||||
|
// > 38 | expect(stored).toBe(after);
|
||||||
test("theme toggle flips preview theme and persists to localStorage", async ({
|
test("theme toggle flips preview theme and persists to localStorage", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
await page.goto("/preview");
|
await page.goto("/");
|
||||||
const root = page.locator("main.preview-root");
|
const root = page.locator("main.preview-root");
|
||||||
const before = await root.getAttribute("data-theme");
|
const before = await root.getAttribute("data-theme");
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
@@ -44,7 +48,7 @@ test("theme toggle flips preview theme and persists to localStorage", async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("language toggle marks the active button", async ({ page }) => {
|
test("language toggle marks the active button", async ({ page }) => {
|
||||||
await page.goto("/preview");
|
await page.goto("/");
|
||||||
const group = page.getByRole("group", { name: "Language" });
|
const group = page.getByRole("group", { name: "Language" });
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await group.getByRole("button", { name: "EN" }).click();
|
await group.getByRole("button", { name: "EN" }).click();
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ export default {
|
|||||||
hardcodedZIndex:
|
hardcodedZIndex:
|
||||||
"Hardcoded z-index '{{value}}'. Use a var(--z-...) token.",
|
"Hardcoded z-index '{{value}}'. Use a var(--z-...) token.",
|
||||||
hardcodedBreakpoint:
|
hardcodedBreakpoint:
|
||||||
"Hardcoded breakpoint '{{value}}' in @media. Use the @custom-media name instead: declare `@custom-media --bp-* (...)` in preview.css and write `@media (--bp-*)`.",
|
"Hardcoded breakpoint '{{value}}' in @media. Use the @custom-media name instead: declare `@custom-media --bp-* (...)` in app.css and write `@media (--bp-*)`.",
|
||||||
varInMedia:
|
varInMedia:
|
||||||
"var() inside @media '{{value}}'. Custom properties do not resolve in media queries — declare `@custom-media --bp-* (...)` in preview.css and use `@media (--bp-*)`.",
|
"var() inside @media '{{value}}'. Custom properties do not resolve in media queries — declare `@custom-media --bp-* (...)` in app.css and use `@media (--bp-*)`.",
|
||||||
colorMix:
|
colorMix:
|
||||||
"color-mix() in component ({{value}}). Tokenize the result in the design CSS file.",
|
"color-mix() in component ({{value}}). Tokenize the result in the design CSS file.",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// A custom property may be DEFINED inside a component's <style> block, but its
|
// A custom property may be DEFINED inside a component's <style> block, but its
|
||||||
// value must not introduce a design primitive: a raw color literal (hex/rgb/
|
// value must not introduce a design primitive: a raw color literal (hex/rgb/
|
||||||
// oklch/named) or an absolute size (px/rem/em). This keeps the design surface
|
// oklch/named) or an absolute size (px/rem/em). This keeps the design surface
|
||||||
// (colors/sizes) a single source of truth in preview.css, while still allowing
|
// (colors/sizes) a single source of truth in app.css, while still allowing
|
||||||
// local DERIVED variables built from tokens: var(--...), calc(), unitless
|
// local DERIVED variables built from tokens: var(--...), calc(), unitless
|
||||||
// ratios (--ratio: 1.5) — those are legitimate component-local state.
|
// ratios (--ratio: 1.5) — those are legitimate component-local state.
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ export default {
|
|||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
tokenPrimitive:
|
tokenPrimitive:
|
||||||
"Custom property '{{prop}}' defines a primitive '{{value}}' in a component. Move it to preview.css or derive it from tokens via var()/calc().",
|
"Custom property '{{prop}}' defines a primitive '{{value}}' in a component. Move it to app.css or derive it from tokens via var()/calc().",
|
||||||
},
|
},
|
||||||
schema: [],
|
schema: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Rule: NO UNDEFINED CSS-TOKEN USAGE IN COMPONENTS.
|
// Rule: NO UNDEFINED CSS-TOKEN USAGE IN COMPONENTS.
|
||||||
// A var(--...) referenced in a Svelte <style> block must exist in the design
|
// A var(--...) referenced in a Svelte <style> block must exist in the design
|
||||||
// token file (src/preview.css) or be a local override defined in the same
|
// token file (src/app.css) or be a local override defined in the same
|
||||||
// component. Catches typos and drop-in tokens that were never added to the
|
// component. Catches typos and drop-in tokens that were never added to the
|
||||||
// "single source of truth".
|
// "single source of truth".
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ const VAR_REF_RE = /var\(\s*(--[\w-]+)/g;
|
|||||||
const definedCache = new Map();
|
const definedCache = new Map();
|
||||||
|
|
||||||
function getDefinedTokens(cwd) {
|
function getDefinedTokens(cwd) {
|
||||||
const file = resolve(cwd, "src/preview.css");
|
const file = resolve(cwd, "src/app.css");
|
||||||
if (!existsSync(file)) return null;
|
if (!existsSync(file)) return null;
|
||||||
if (definedCache.has(file)) return definedCache.get(file);
|
if (definedCache.has(file)) return definedCache.get(file);
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@ export default {
|
|||||||
type: "problem",
|
type: "problem",
|
||||||
docs: {
|
docs: {
|
||||||
description:
|
description:
|
||||||
"Disallow referencing a CSS variable that is not defined in preview.css (design token file) and not defined locally in the component.",
|
"Disallow referencing a CSS variable that is not defined in app.css (design token file) and not defined locally in the component.",
|
||||||
category: "Design tokens",
|
category: "Design tokens",
|
||||||
recommended: true,
|
recommended: true,
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
undefinedToken:
|
undefinedToken:
|
||||||
"CSS variable '{{token}}' is not defined in preview.css and not locally in this component.",
|
"CSS variable '{{token}}' is not defined in app.css and not locally in this component.",
|
||||||
},
|
},
|
||||||
schema: [],
|
schema: [],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Goal: a single source of truth for design. In Svelte components, hardcoded
|
* Goal: a single source of truth for design. In Svelte components, hardcoded
|
||||||
* colors and sizes are banned; everything must come from CSS variables (tokens
|
* colors and sizes are banned; everything must come from CSS variables (tokens
|
||||||
* in preview.css). Rules inspect the postcss AST of Svelte <style> blocks
|
* in app.css). Rules inspect the postcss AST of Svelte <style> blocks
|
||||||
* exposed by svelte-eslint-parser.
|
* exposed by svelte-eslint-parser.
|
||||||
*/
|
*/
|
||||||
import noCategoryMismatch from "./design-tokens/no-category-mismatch.js";
|
import noCategoryMismatch from "./design-tokens/no-category-mismatch.js";
|
||||||
|
|||||||
@@ -17,13 +17,16 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
const DEFAULT_OLD = ["routes/(old)/**", "lib/old/**"];
|
// FIXME: change paths to new ones
|
||||||
|
// TODO: test case for this lint rules
|
||||||
|
|
||||||
|
const DEFAULT_OLD = ["routes/v1/**", "lib/v1/**"];
|
||||||
|
|
||||||
const DEFAULT_NEW = [
|
const DEFAULT_NEW = [
|
||||||
"routes/preview/**",
|
"routes/**",
|
||||||
"lib/components/kit/**",
|
"lib/components/kit/**",
|
||||||
"lib/preview/**",
|
"lib/**",
|
||||||
"lib/registry-new/**",
|
"lib/registry/**",
|
||||||
"lib/registry-schema.ts",
|
"lib/registry-schema.ts",
|
||||||
"lib/registry-schema.test.ts",
|
"lib/registry-schema.test.ts",
|
||||||
];
|
];
|
||||||
|
|||||||
+34
-10
@@ -1,16 +1,18 @@
|
|||||||
import js from "@eslint/js";
|
import js from "@eslint/js";
|
||||||
import prettier from "eslint-config-prettier";
|
import prettier from "eslint-config-prettier";
|
||||||
import svelte from "eslint-plugin-svelte";
|
import svelte from "eslint-plugin-svelte";
|
||||||
import designTokens from "./eslint-plugins/index.js";
|
|
||||||
import isolationPlugin from "./eslint-plugins/isolation/index.js";
|
|
||||||
import globals from "globals";
|
import globals from "globals";
|
||||||
import svelteParser from "svelte-eslint-parser";
|
import svelteParser from "svelte-eslint-parser";
|
||||||
import tseslint from "typescript-eslint";
|
import tseslint from "typescript-eslint";
|
||||||
|
import designTokens from "./eslint-plugins/index.js";
|
||||||
|
import isolationPlugin from "./eslint-plugins/isolation/index.js";
|
||||||
|
|
||||||
|
// FIXME: надо игнорировать старые файлы, после переноса пути новых компонентов включают старые
|
||||||
// Новый код редизайна: к нему применяем полные recommended-наборы уже сейчас.
|
// Новый код редизайна: к нему применяем полные recommended-наборы уже сейчас.
|
||||||
// Когда старый дизайн удалим (C19), этот scoped-блок убирается и recommended
|
// Когда старый дизайн удалим (C19), этот scoped-блок убирается и recommended
|
||||||
// включается на весь код (см. план-redesign §10, шаг 6).
|
// включается на весь код (см. план-redesign §10, шаг 6).
|
||||||
const newCode = ["**/src/lib/components/kit/**", "**/src/routes/preview/**"];
|
const newCode = ["**/src/lib/components/**", "**/src/routes/**"];
|
||||||
|
const oldCode = ["**/src/lib/v1/**", "**/src/routes/v1/**"];
|
||||||
|
|
||||||
// Полные recommended-наборы — только на новый код (см. ниже, блок перед prettier).
|
// Полные recommended-наборы — только на новый код (см. ниже, блок перед prettier).
|
||||||
const jsRecommended = Array.isArray(js.configs.recommended)
|
const jsRecommended = Array.isArray(js.configs.recommended)
|
||||||
@@ -21,10 +23,19 @@ const svelteRecommended = Array.isArray(svelte.configs["flat/recommended"])
|
|||||||
: [svelte.configs["flat/recommended"]];
|
: [svelte.configs["flat/recommended"]];
|
||||||
// Svelte-рекомендации применяем только к .svelte-файлам нового кода, иначе
|
// Svelte-рекомендации применяем только к .svelte-файлам нового кода, иначе
|
||||||
// svelte-eslint-parser "съедает" обычные .ts в тех же папках (напр. +page.ts).
|
// svelte-eslint-parser "съедает" обычные .ts в тех же папках (напр. +page.ts).
|
||||||
const svelteFiles = [
|
const newSvelteFiles = [
|
||||||
"**/src/lib/components/kit/**/*.svelte",
|
"**/src/lib/components/**/*.svelte",
|
||||||
"**/src/routes/preview/**/*.svelte",
|
"**/src/routes/**/*.svelte",
|
||||||
];
|
];
|
||||||
|
const oldSvelteFiles = [
|
||||||
|
"**/src/lib/v1/**/*.svelte",
|
||||||
|
"**/src/routes/v1/**/*.svelte",
|
||||||
|
];
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
// The signature '(...configs: InfiniteDepthConfigWithExtends[]): ConfigArray' of 'tseslint.config' is deprecated.ts
|
||||||
|
// Migrate to defineConfig(...)
|
||||||
|
// The core defineConfig(...) helper is a nearly exact clone of tseslint.config(...)
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
{
|
{
|
||||||
@@ -100,7 +111,8 @@ export default tseslint.config(
|
|||||||
// Применяется к новому коду редизайна (см. newCode выше). Когда старый дизайн
|
// Применяется к новому коду редизайна (см. newCode выше). Когда старый дизайн
|
||||||
// удалят, расширить glob на весь код, исключив (old)/.
|
// удалят, расширить glob на весь код, исключив (old)/.
|
||||||
{
|
{
|
||||||
files: svelteFiles,
|
files: newSvelteFiles,
|
||||||
|
ignores: oldSvelteFiles,
|
||||||
plugins: {
|
plugins: {
|
||||||
"design-tokens": designTokens,
|
"design-tokens": designTokens,
|
||||||
},
|
},
|
||||||
@@ -113,9 +125,21 @@ export default tseslint.config(
|
|||||||
},
|
},
|
||||||
// Полные recommended-наборы — только на новый код.
|
// Полные recommended-наборы — только на новый код.
|
||||||
...[
|
...[
|
||||||
...jsRecommended.map((cfg) => ({ ...cfg, files: newCode })),
|
...jsRecommended.map((cfg) => ({
|
||||||
...tseslint.configs.recommended.map((cfg) => ({ ...cfg, files: newCode })),
|
...cfg,
|
||||||
...svelteRecommended.map((cfg) => ({ ...cfg, files: svelteFiles })),
|
files: newCode,
|
||||||
|
ignores: oldCode,
|
||||||
|
})),
|
||||||
|
...tseslint.configs.recommended.map((cfg) => ({
|
||||||
|
...cfg,
|
||||||
|
files: newCode,
|
||||||
|
ignores: oldCode,
|
||||||
|
})),
|
||||||
|
...svelteRecommended.map((cfg) => ({
|
||||||
|
...cfg,
|
||||||
|
files: newSvelteFiles,
|
||||||
|
ignores: oldSvelteFiles,
|
||||||
|
})),
|
||||||
],
|
],
|
||||||
|
|
||||||
// В Svelte 5 пропсы деструктурируются через `let` (конвенция документации и
|
// В Svelte 5 пропсы деструктурируются через `let` (конвенция документации и
|
||||||
|
|||||||
+2
-6
@@ -16,12 +16,7 @@
|
|||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:css": "stylelint \"./src/**/*.css\"",
|
"lint:css": "stylelint \"./src/**/*.css\"",
|
||||||
"lint:all": "node scripts/lint-all.mjs",
|
"lint:all": "node scripts/lint-all.mjs",
|
||||||
"lint:tokens": "node scripts/check-tokens.mjs",
|
"lint:tokens": "node scripts/check-tokens.mjs"
|
||||||
"refs-cdp-audit": "node scripts/audit-cdp.mjs",
|
|
||||||
"refs-cdp-audit:large": "node scripts/audit-cdp.mjs --viewport 2560x1440",
|
|
||||||
"refs-cdp-audit:responsive": "node scripts/audit-cdp-responsive.mjs",
|
|
||||||
"refs-css-audit": "node scripts/audit-css.mjs",
|
|
||||||
"refs-dom-audit": "node scripts/audit-dom.mjs"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@csstools/postcss-global-data": "^4.0.0",
|
"@csstools/postcss-global-data": "^4.0.0",
|
||||||
@@ -30,6 +25,7 @@
|
|||||||
"@sveltejs/adapter-static": "^3.0.10",
|
"@sveltejs/adapter-static": "^3.0.10",
|
||||||
"@sveltejs/kit": "^2.63.0",
|
"@sveltejs/kit": "^2.63.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
||||||
|
"@types/node": "^26.5.0",
|
||||||
"diff": "^9.0.0",
|
"diff": "^9.0.0",
|
||||||
"eslint": "^10.9.0",
|
"eslint": "^10.9.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
webServer: {
|
webServer: {
|
||||||
command: "pnpm build && node scripts/serve-static.mjs --port 4173",
|
command: "pnpm build && node scripts/serve-static.mjs --port 4173",
|
||||||
url: "http://127.0.0.1:4173/preview",
|
url: "http://127.0.0.1:4173/",
|
||||||
reuseExistingServer: !process.env.CI,
|
reuseExistingServer: !process.env.CI,
|
||||||
timeout: 180_000,
|
timeout: 180_000,
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+39
-21
@@ -29,13 +29,16 @@ importers:
|
|||||||
version: 0.4.2
|
version: 0.4.2
|
||||||
'@sveltejs/adapter-static':
|
'@sveltejs/adapter-static':
|
||||||
specifier: ^3.0.10
|
specifier: ^3.0.10
|
||||||
version: 3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1))
|
version: 3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@26.5.0)))
|
||||||
'@sveltejs/kit':
|
'@sveltejs/kit':
|
||||||
specifier: ^2.63.0
|
specifier: ^2.63.0
|
||||||
version: 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1)
|
version: 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@26.5.0))
|
||||||
'@sveltejs/vite-plugin-svelte':
|
'@sveltejs/vite-plugin-svelte':
|
||||||
specifier: ^7.1.2
|
specifier: ^7.1.2
|
||||||
version: 7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1)
|
version: 7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0))
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^26.5.0
|
||||||
|
version: 26.5.0
|
||||||
diff:
|
diff:
|
||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.0.0
|
version: 9.0.0
|
||||||
@@ -89,10 +92,10 @@ importers:
|
|||||||
version: 8.67.0(eslint@10.9.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
|
version: 8.67.0(eslint@10.9.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)
|
||||||
vite:
|
vite:
|
||||||
specifier: ^8.0.16
|
specifier: ^8.0.16
|
||||||
version: 8.2.1
|
version: 8.2.1(@types/node@26.5.0)
|
||||||
vitest:
|
vitest:
|
||||||
specifier: ^4.1.11
|
specifier: ^4.1.11
|
||||||
version: 4.1.11(vite@8.2.1)
|
version: 4.1.11(@types/node@26.5.0)(vite@8.2.1(@types/node@26.5.0))
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
@@ -439,6 +442,9 @@ packages:
|
|||||||
'@types/json-schema@7.0.15':
|
'@types/json-schema@7.0.15':
|
||||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||||
|
|
||||||
|
'@types/node@26.5.0':
|
||||||
|
resolution: {integrity: sha512-dVSGpriSoCgz8WnDNTuSSuSv1PC/ALXihO4ulRZt7Md8k9mlbdin3lGOcDE8SnWOgf513ByWlXd7BK4azmyg/A==}
|
||||||
|
|
||||||
'@types/trusted-types@2.0.7':
|
'@types/trusted-types@2.0.7':
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
@@ -1450,6 +1456,9 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
undici-types@8.9.0:
|
||||||
|
resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==}
|
||||||
|
|
||||||
unicorn-magic@0.4.0:
|
unicorn-magic@0.4.0:
|
||||||
resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==}
|
resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==}
|
||||||
engines: {node: '>=20'}
|
engines: {node: '>=20'}
|
||||||
@@ -1799,15 +1808,15 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.18.0
|
acorn: 8.18.0
|
||||||
|
|
||||||
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1))':
|
'@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@26.5.0)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1)
|
'@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@26.5.0))
|
||||||
|
|
||||||
'@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1)':
|
'@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0)))(svelte@5.56.9(@typescript-eslint/types@8.67.0))(typescript@6.0.3)(vite@8.2.1(@types/node@26.5.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
'@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0)
|
'@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0)
|
||||||
'@sveltejs/vite-plugin-svelte': 7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1)
|
'@sveltejs/vite-plugin-svelte': 7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0))
|
||||||
'@types/cookie': 0.6.0
|
'@types/cookie': 0.6.0
|
||||||
acorn: 8.18.0
|
acorn: 8.18.0
|
||||||
cookie: 0.6.0
|
cookie: 0.6.0
|
||||||
@@ -1819,20 +1828,20 @@ snapshots:
|
|||||||
set-cookie-parser: 3.1.2
|
set-cookie-parser: 3.1.2
|
||||||
sirv: 3.0.2
|
sirv: 3.0.2
|
||||||
svelte: 5.56.9(@typescript-eslint/types@8.67.0)
|
svelte: 5.56.9(@typescript-eslint/types@8.67.0)
|
||||||
vite: 8.2.1
|
vite: 8.2.1(@types/node@26.5.0)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 6.0.3
|
typescript: 6.0.3
|
||||||
|
|
||||||
'@sveltejs/load-config@0.2.3': {}
|
'@sveltejs/load-config@0.2.3': {}
|
||||||
|
|
||||||
'@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1)':
|
'@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.9(@typescript-eslint/types@8.67.0))(vite@8.2.1(@types/node@26.5.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
deepmerge: 4.3.1
|
deepmerge: 4.3.1
|
||||||
magic-string: 1.2.0
|
magic-string: 1.2.0
|
||||||
obug: 2.1.4
|
obug: 2.1.4
|
||||||
svelte: 5.56.9(@typescript-eslint/types@8.67.0)
|
svelte: 5.56.9(@typescript-eslint/types@8.67.0)
|
||||||
vite: 8.2.1
|
vite: 8.2.1(@types/node@26.5.0)
|
||||||
vitefu: 1.1.3(vite@8.2.1)
|
vitefu: 1.1.3(vite@8.2.1(@types/node@26.5.0))
|
||||||
|
|
||||||
'@types/chai@5.2.3':
|
'@types/chai@5.2.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1849,6 +1858,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/json-schema@7.0.15': {}
|
'@types/json-schema@7.0.15': {}
|
||||||
|
|
||||||
|
'@types/node@26.5.0':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 8.9.0
|
||||||
|
|
||||||
'@types/trusted-types@2.0.7': {}
|
'@types/trusted-types@2.0.7': {}
|
||||||
|
|
||||||
'@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.9.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
|
'@typescript-eslint/eslint-plugin@8.67.0(@typescript-eslint/parser@8.67.0(eslint@10.9.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.9.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)':
|
||||||
@@ -1951,13 +1964,13 @@ snapshots:
|
|||||||
chai: 6.2.2
|
chai: 6.2.2
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
|
|
||||||
'@vitest/mocker@4.1.11(vite@8.2.1)':
|
'@vitest/mocker@4.1.11(vite@8.2.1(@types/node@26.5.0))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/spy': 4.1.11
|
'@vitest/spy': 4.1.11
|
||||||
estree-walker: 3.0.3
|
estree-walker: 3.0.3
|
||||||
magic-string: 0.30.21
|
magic-string: 0.30.21
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 8.2.1
|
vite: 8.2.1(@types/node@26.5.0)
|
||||||
|
|
||||||
'@vitest/pretty-format@4.1.11':
|
'@vitest/pretty-format@4.1.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -2847,6 +2860,8 @@ snapshots:
|
|||||||
|
|
||||||
typescript@6.0.3: {}
|
typescript@6.0.3: {}
|
||||||
|
|
||||||
|
undici-types@8.9.0: {}
|
||||||
|
|
||||||
unicorn-magic@0.4.0: {}
|
unicorn-magic@0.4.0: {}
|
||||||
|
|
||||||
uri-js@4.4.1:
|
uri-js@4.4.1:
|
||||||
@@ -2855,7 +2870,7 @@ snapshots:
|
|||||||
|
|
||||||
util-deprecate@1.0.2: {}
|
util-deprecate@1.0.2: {}
|
||||||
|
|
||||||
vite@8.2.1:
|
vite@8.2.1(@types/node@26.5.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
lightningcss: 1.33.0
|
lightningcss: 1.33.0
|
||||||
picomatch: 4.0.5
|
picomatch: 4.0.5
|
||||||
@@ -2863,16 +2878,17 @@ snapshots:
|
|||||||
rolldown: 1.2.4
|
rolldown: 1.2.4
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
'@types/node': 26.5.0
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
vitefu@1.1.3(vite@8.2.1):
|
vitefu@1.1.3(vite@8.2.1(@types/node@26.5.0)):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
vite: 8.2.1
|
vite: 8.2.1(@types/node@26.5.0)
|
||||||
|
|
||||||
vitest@4.1.11(vite@8.2.1):
|
vitest@4.1.11(@types/node@26.5.0)(vite@8.2.1(@types/node@26.5.0)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@vitest/expect': 4.1.11
|
'@vitest/expect': 4.1.11
|
||||||
'@vitest/mocker': 4.1.11(vite@8.2.1)
|
'@vitest/mocker': 4.1.11(vite@8.2.1(@types/node@26.5.0))
|
||||||
'@vitest/pretty-format': 4.1.11
|
'@vitest/pretty-format': 4.1.11
|
||||||
'@vitest/runner': 4.1.11
|
'@vitest/runner': 4.1.11
|
||||||
'@vitest/snapshot': 4.1.11
|
'@vitest/snapshot': 4.1.11
|
||||||
@@ -2889,8 +2905,10 @@ snapshots:
|
|||||||
tinyexec: 1.3.0
|
tinyexec: 1.3.0
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
tinyrainbow: 3.1.1
|
tinyrainbow: 3.1.1
|
||||||
vite: 8.2.1
|
vite: 8.2.1(@types/node@26.5.0)
|
||||||
why-is-node-running: 2.3.0
|
why-is-node-running: 2.3.0
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/node': 26.5.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- msw
|
- msw
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import postcssCustomMedia from "postcss-custom-media";
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
plugins: [
|
plugins: [
|
||||||
// Breakpoints live only in preview.css (next to the --bp-* tokens);
|
// Breakpoints live only in app.css (next to the --bp-* tokens);
|
||||||
// global-data injects them so @media (--bp-*) expands in every file.
|
// global-data injects them so @media (--bp-*) expands in every file.
|
||||||
postcssGlobalData({
|
postcssGlobalData({
|
||||||
files: ["src/preview.css"],
|
files: ["src/app.css"],
|
||||||
}),
|
}),
|
||||||
postcssCustomMedia({
|
postcssCustomMedia({
|
||||||
preserve: false,
|
preserve: false,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Design-token audit, one command: theme parity, hct-only color authorship and
|
// Design-token audit, one command: theme parity, hct-only color authorship and
|
||||||
// unused-token warnings over src/preview.css.
|
// unused-token warnings over src/app.css.
|
||||||
//
|
//
|
||||||
// Usage: pnpm --dir web exec node scripts/check-tokens.mjs
|
// Usage: pnpm --dir web exec node scripts/check-tokens.mjs
|
||||||
// Exit code 1 when a failing check (parity or color authorship) reports.
|
// Exit code 1 when a failing check (parity or color authorship) reports.
|
||||||
@@ -36,10 +36,10 @@ async function main() {
|
|||||||
|
|
||||||
const unused = checkUnused(root);
|
const unused = checkUnused(root);
|
||||||
if (unused.length > 0) {
|
if (unused.length > 0) {
|
||||||
console.log("\nUnused tokens (defined in preview.css, never used):");
|
console.log("\nUnused tokens (defined in app.css, never used):");
|
||||||
for (const token of unused) console.log(` ${token}`);
|
for (const token of unused) console.log(` ${token}`);
|
||||||
} else {
|
} else {
|
||||||
console.log("All preview.css tokens are used somewhere.");
|
console.log("All app.css tokens are used somewhere.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed) process.exitCode = 1;
|
if (failed) process.exitCode = 1;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Color authorship: every color value in preview.css must be authored as hct()
|
// Color authorship: every color value in app.css must be authored as hct()
|
||||||
// — literals AND derived forms (hct(from var(...) h c t) with channel math) —
|
// — literals AND derived forms (hct(from var(...) h c t) with channel math) —
|
||||||
// so the whole palette is computed through HCT channels and output as sRGB by
|
// so the whole palette is computed through HCT channels and output as sRGB by
|
||||||
// the postcss-hct plugin. Exceptions: the seed tokens --brand-main/--brand-alt
|
// the postcss-hct plugin. Exceptions: the seed tokens --brand-main/--brand-alt
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Shared plumbing for the design-token audits: reading preview.css, scanning
|
// Shared plumbing for the design-token audits: reading app.css, scanning
|
||||||
// for var() usages across src, and the color/form predicates the checks use.
|
// for var() usages across src, and the color/form predicates the checks use.
|
||||||
|
|
||||||
import { readdirSync, readFileSync, statSync } from "node:fs";
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||||||
@@ -7,7 +7,7 @@ import { join } from "node:path";
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import postcss from "postcss";
|
import postcss from "postcss";
|
||||||
|
|
||||||
export const FILE = new URL("../../src/preview.css", import.meta.url);
|
export const FILE = new URL("../../src/app.css", import.meta.url);
|
||||||
const SRC_DIR = fileURLToPath(new URL("../../src/", import.meta.url));
|
const SRC_DIR = fileURLToPath(new URL("../../src/", import.meta.url));
|
||||||
|
|
||||||
// A var(--x) REFERENCE anywhere in the app (primary argument only).
|
// A var(--x) REFERENCE anywhere in the app (primary argument only).
|
||||||
@@ -65,7 +65,7 @@ export function collectTokens(root, selector) {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse preview.css once and hand out the postcss root plus both theme maps.
|
// Parse app.css once and hand out the postcss root plus both theme maps.
|
||||||
export async function parsePreview() {
|
export async function parsePreview() {
|
||||||
const css = await readFile(FILE, "utf8");
|
const css = await readFile(FILE, "utf8");
|
||||||
const root = postcss.parse(css);
|
const root = postcss.parse(css);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Unused tokens: defined in preview.css but never referenced via var()
|
// Unused tokens: defined in app.css but never referenced via var()
|
||||||
// anywhere in src. A dead token is not the single source of truth — it's dust.
|
// anywhere in src. A dead token is not the single source of truth — it's dust.
|
||||||
// Reported as a warning; it does not fail the run.
|
// Reported as a warning; it does not fail the run.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CATEGORY_IDS } from "./categories";
|
import { CATEGORY_IDS } from "./categories";
|
||||||
import { TOOLS, type ToolEntry } from "../registry-new";
|
import { TOOLS, type ToolEntry } from "./registry";
|
||||||
|
|
||||||
export type PreviewGroup = {
|
export type PreviewGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import MetaList from "$lib/components/kit/MetaList.svelte";
|
import MetaList from "$lib/components/MetaList.svelte";
|
||||||
import SchemaActions from "$lib/components/kit/SchemaActions.svelte";
|
import SchemaActions from "$lib/components/SchemaActions.svelte";
|
||||||
import SchemaResultTile from "$lib/components/kit/SchemaResultTile.svelte";
|
import SchemaResultTile from "$lib/components/SchemaResultTile.svelte";
|
||||||
import SchemaSourceTile from "$lib/components/kit/SchemaSourceTile.svelte";
|
import SchemaSourceTile from "$lib/components/SchemaSourceTile.svelte";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
import type { InputMode, ResultKind } from "$lib/registry-new";
|
import type { InputMode, ResultKind } from "$lib/registry";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
inputMode: InputMode;
|
inputMode: InputMode;
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toDataUrl } from "$lib/core/io";
|
import { toDataUrl } from "$lib/core/io";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
import type { InputMode } from "$lib/registry-new";
|
import type { InputMode } from "$lib/registry";
|
||||||
import SchemaTextSource from "./SchemaTextSource.svelte";
|
import SchemaTextSource from "./SchemaTextSource.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
+4
-4
@@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SchemaFields from "$lib/components/kit/SchemaFields.svelte";
|
import SchemaFields from "$lib/components/SchemaFields.svelte";
|
||||||
import SchemaPreview from "$lib/components/kit/SchemaPreview.svelte";
|
import SchemaPreview from "$lib/components/SchemaPreview.svelte";
|
||||||
import { debounce } from "$lib/core/debounce";
|
import { debounce } from "$lib/core/debounce";
|
||||||
import { ToolError } from "$lib/core/errors";
|
import { ToolError } from "$lib/core/errors";
|
||||||
import { decodeFile, encode } from "$lib/core/io";
|
import { decodeFile, encode } from "$lib/core/io";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
|
import { execute } from "$lib/executor";
|
||||||
import { t } from "$lib/i18n/t";
|
import { t } from "$lib/i18n/t";
|
||||||
import { execute } from "$lib/preview/executor";
|
import type { ToolEntry } from "$lib/registry";
|
||||||
import type { ToolEntry } from "$lib/registry-new";
|
|
||||||
import {
|
import {
|
||||||
defaultSchemaParams,
|
defaultSchemaParams,
|
||||||
sanitizeSchemaParams,
|
sanitizeSchemaParams,
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
let { title, id, description, index, icon }: Props = $props();
|
let { title, id, description, index, icon }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a class="tool-card" href={resolve("/preview/tools/[id]", { id })}>
|
<a class="tool-card" href={resolve("/tools/[id]", { id })}>
|
||||||
{#if icon}<span class="tool-icon"><Icon {icon} size={19} /></span>{/if}
|
{#if icon}<span class="tool-icon"><Icon {icon} size={19} /></span>{/if}
|
||||||
<span class="tool-copy">
|
<span class="tool-copy">
|
||||||
<strong>{title}</strong>
|
<strong>{title}</strong>
|
||||||
-4
@@ -35,10 +35,6 @@
|
|||||||
padding: var(--space-s) var(--space-m);
|
padding: var(--space-s) var(--space-m);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.lang-div {
|
|
||||||
color: var(--color-text);
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
}
|
|
||||||
.lang-btn.active {
|
.lang-btn.active {
|
||||||
background: var(--color-text);
|
background: var(--color-text);
|
||||||
color: var(--color-background);
|
color: var(--color-background);
|
||||||
+3
-3
@@ -3,9 +3,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a href={resolve("/preview")}>Home page</a>
|
<a href={resolve("/")}>Home page</a>
|
||||||
<a href={resolve("/preview/list-tools")}>Catalog</a>
|
<a href={resolve("/list-tools")}>Catalog</a>
|
||||||
<a href={resolve("/preview/kit")}> UI Kit </a>
|
<a href={resolve("/kit")}> UI Kit </a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ToolError } from "$lib/core/errors";
|
import { ToolError } from "$lib/core/errors";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
|
import type { ToolContext, ToolEntry, ToolResult } from "$lib/registry";
|
||||||
import { sanitizeSchemaParams } from "$lib/registry-schema";
|
import { sanitizeSchemaParams } from "$lib/registry-schema";
|
||||||
import type { ToolContext, ToolEntry, ToolResult } from "$lib/registry-new";
|
|
||||||
|
|
||||||
/** Вход единой точки исполнения. `source`/`text` — по контракту `tool.input`. */
|
/** Вход единой точки исполнения. `source`/`text` — по контракту `tool.input`. */
|
||||||
export type ExecuteContext = {
|
export type ExecuteContext = {
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
/// <reference lib="webworker" />
|
/// <reference lib="webworker" />
|
||||||
import { ToolError } from "$lib/core/errors";
|
import { ToolError } from "$lib/core/errors";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
import { getTool } from "$lib/registry-new";
|
import { getTool } from "$lib/registry";
|
||||||
import { sanitizeSchemaParams } from "$lib/registry-schema";
|
import { sanitizeSchemaParams } from "$lib/registry-schema";
|
||||||
|
|
||||||
type WorkerRequest = {
|
type WorkerRequest = {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { CategoryId } from "../old/categories";
|
import type { CategoryId } from "../v1/categories";
|
||||||
|
|
||||||
export const LOCALES = ["ru", "en"] as const;
|
export const LOCALES = ["ru", "en"] as const;
|
||||||
export type Locale = (typeof LOCALES)[number];
|
export type Locale = (typeof LOCALES)[number];
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { TOOLS } from "../old/registry";
|
import { TOOLS } from "../v1/registry";
|
||||||
import { normalizeForSearch, scoreDoc, type SearchDoc } from "./matching";
|
|
||||||
import { setLocale } from "./locale.svelte";
|
import { setLocale } from "./locale.svelte";
|
||||||
|
import { normalizeForSearch, scoreDoc, type SearchDoc } from "./matching";
|
||||||
import { toolSearchDoc } from "./tool-strings";
|
import { toolSearchDoc } from "./tool-strings";
|
||||||
|
|
||||||
describe("normalizeForSearch", () => {
|
describe("normalizeForSearch", () => {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { TOOLS } from "../old/registry";
|
import { isChainable, TOOLS } from "../v1/registry";
|
||||||
import { normalizeForSearch, scoreDoc } from "./matching";
|
import { normalizeForSearch, scoreDoc } from "./matching";
|
||||||
import { toolSearchDoc } from "./tool-strings";
|
import { toolSearchDoc } from "./tool-strings";
|
||||||
import { isChainable } from "../old/registry";
|
|
||||||
|
|
||||||
function hits(q: string): string[] {
|
function hits(q: string): string[] {
|
||||||
const nq = normalizeForSearch(q);
|
const nq = normalizeForSearch(q);
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
import { setLocale } from "./locale.svelte";
|
import { TOOLS } from "../v1/registry";
|
||||||
import { t } from "./t";
|
|
||||||
import { LOCALE_TAGS } from "./dict";
|
import { LOCALE_TAGS } from "./dict";
|
||||||
|
import { setLocale } from "./locale.svelte";
|
||||||
import { normalizeForSearch, scoreDoc } from "./matching";
|
import { normalizeForSearch, scoreDoc } from "./matching";
|
||||||
|
import { t } from "./t";
|
||||||
import { toolSearchDoc } from "./tool-strings";
|
import { toolSearchDoc } from "./tool-strings";
|
||||||
import { TOOLS } from "../old/registry";
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
setLocale("ru");
|
setLocale("ru");
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user