Files
easy-png-tools2/AGENTS.md
T

12 KiB
Raw Blame History

AGENTS.md

Инструкции для агентов (и IDE-ассистентов) по работе с этим репозиторием.

Стек

  • SvelteKit (web/) — основное приложение, Svelte 5 (runes).
  • Менеджер пакетов: pnpm. Запускать команды из web/, либо через обёртки в корне: pnpm build, pnpm dev (это pnpm --dir web ...). Не использовать npm.
  • Сборка: pnpm --dir web build, проверка типов: pnpm --dir web exec svelte-check --tsconfig ./tsconfig.json, тесты: pnpm --dir web test.
  • Форматирование: pnpm --dir web format (Prettier + prettier-plugin-svelte, конфиг web/.prettierrc, игнор web/.prettierignore). Проверка без записи: pnpm --dir web exec prettier --check ..
  • Линтинг: pnpm --dir web lint (ESLint, flat-конфиг web/eslint.config.js). Устроен инкрементально:
    • На весь код — парсинг TS/Svelte + правило @typescript-eslint/consistent-type-imports (запрет инлайн-тип-импортов).
    • Полные recommended-наборы (eslint + typescript-eslint + eslint-plugin-svelte) навешены только на новый код (src/lib/components/kit/**, src/routes/preview/**), чтобы старый код не засыпался предсуществующими ошибками. Когда старый дизайн удалён (C19), scoped-блок убирается и recommended включается на весь код.
    • Scoped-пути двигаются вместе с папками (см. plan-redesign.md §10): на C17 src/routes/preview/**src/routes/** (preview переезжает на реальные маршруты), на C19 при переименовании kit/ui/ — и компонентный glob. Не оставлять устаревшие пути в eslint.config.js.
    • Для *.svelte выключен prefer-const (пропсы в Svelte 5 пишутся через let).
  • Единый запуск всей дизайн-проверки: pnpm --dir web lint:all (web/scripts/lint-all.mjs) — гоняет по очереди: ESLint (все правила, включая design-tokens), stylelint (lint:css) и токен-аудит (lint:tokens). Каждый шаг выполняется, даже если предыдущий упал; выход ненулевой, если хоть один не прошёл. Текущие «ожидаемые долги» (hardcoded размеры в kit, не-Prefix токены в preview.css и т.п.) — техдолг: чинить только по заведённым tasks, не игнорировать правилом.

Правила кода

Svelte 5: типизация props через interface Props

Все типизированные пропсы компонентов описываются через локальный interface Props, а деструктуризация идёт через аннотацию типа при $props():

<script lang="ts">
  import type { Snippet } from 'svelte';

  interface Props {
    label: string;
    accent?: boolean;
    children?: Snippet;
  }

  let { label, accent = false, children }: Props = $props();
</script>

Не использовать инлайн-дженерик $props<{ ... }>() — он тяжело читается и разносит тип и деструктуризацию по разным местам. Также не использовать инлайн-импорты в типах (children?: import('svelte').Snippet;) — все import type поднимаются наверх файла. Это правило включено в ESLint (@typescript-eslint/consistent-type-imports, prefer: 'type-imports').

Правило «всегда interface Props + let {...}: Props = $props()» стандартным ESLint-правилом не покрывается — остаётся конвенцией. Кастомное правило (eslint-plugin-svelte / свой visitor) — TODO, позже.

Дизайн: новый визуальный язык

Переезд на новый дизайн описан в docs/plan-redesign.md. Ключевые ограничения при работе с веткой feat/redesign:

  • Глобальные стили старого и нового дизайна разнесены по layout без наследования: старое в web/src/app.css (группа (old)/), новое в web/src/lib/styles/design2.css (ветка preview/*). Префикс .ds2 не используется.
  • Все повторяющиеся визуальные элементы — отдельные компоненты в web/src/lib/components/kit/, даже «просто div с двумя стилями».

Линтинг дизайн-токенов («единая точка правды» дизайна)

Запрещено «захардкоживать» дизайн: цвета, размеры, длительности и z-index обязаны приходить из CSS-переменных. Стек: stylelint прогоняется по всем CSS-файлам (web/stylelint.config.js), ESLint лезет в <style>-блоки svelte-компонентов через локальный плагин web/eslint-plugins/design-tokens/ (постпрефес postcss AST от svelte-eslint-parser). Правила применяются к тем же scoped-путям (kit/**, preview/**).

Команды:

  • pnpm --dir web lint:css — stylelint по всем src/**/*.css (old.css и node_modules/build/.svelte-kit/static игнорируются).
  • pnpm --dir web exec node scripts/check-tokens.mjs — токен-аудит по preview.css (лёгкий оркестратор поверх web/scripts/token-audit/*); проверяет три вещи. Каждый цветовой токен из :root обязан иметь пару в [data-theme="dark"] и наоборот. Производные токены (значение содержит var(), напр. hct(from var(--...))) из пары исключены — они наследуют тему автоматически. Выводит варнинг о неиспользуемых токенах preview.css (определены, но нигде не используются) — выход он не меняет. Провалом (exit 1) считаются только parity и hct-авторство.
  • Тот же скрипт проверяет hct-only для цветов: ЛЮБОЕ цветовое значение в preview.css обязано быть hct(...) — и литерал, и производное hct(from var(...) h c t) (вся палитра считается через hct-каналы; эммит в sRGB-hex делает postcss-плагин web/scripts/postcss-hct.mjs). Исключения: только --brand-main / --brand-alt (seed-токены, любая форма) и color-mix(...) (единственный легальный способ смешать два токена). oklch()/rgb()/#hex в --color-* запрещены.

Токены-префиксы (целевой словарь дизайна):

  • Цвета: --color-*, бренд --brand-main / --brand-alt — единственные две переменные, которым разрешено быть hex/rgb, остальные цвета — только hct() (эммит в sRGB-hex делает postcss-плагин web/scripts/postcss-hct.mjs) и только в preview.css.
  • Размеры: --space-*, --text-* (font-size), --radius-*, --size-*.
  • Брейкпоинты: @custom-media --bp-mobile (max-width: 640px) / --bp-tablet (800px) / --bp-desktop (1100px) — объявляются в preview.css, используются как @media (--bp-*). CSS-переменные в @media не работают, поэтому отдельных --bp-* токенов нет; раскрытие делает postcss-плагин postcss-custom-media (конфиг web/postcss.config.js, определения подтягиваются через @csstools/postcss-global-data).
  • z-index: --z-*; длительности/анимации: --duration-*, --ease-*.
  • Правило custom-property-pattern в stylelint тестирует паттерн без -- (--xx), а declaration-property-value-disallowed-list — целиком с --.
  • custom-property-empty-line-before перенастроен (after-custom-property в ignore, не в except): пустые строки между подряд идущими токенами свободны и --fix их не удаляет — можно группировать цветовые и размерные токены в preview.css отдельными блоками.

Правила плагина (компоненты, eslint.config.js):

  • design-tokens/no-hardcoded-in-svelte — запрет прямых цветов/размеров/ длительностей/z-index и color-mix() (результат токенизировать в CSS). Исключения размеров: 0, 0px, 1px, проценты и unitless (line-height).
  • design-tokens/no-category-mismatch — size-свойство не может использовать color-токен (--color-*/--brand-*) и наоборот.
  • design-tokens/no-token-definition-in-svelte — определение --x: в компоненте не может содержать примитив (hex/rgb/oklch/px/rem); допустимы производные от токенов (var(), calc(), unitless-числа).
  • design-tokens/no-undefined-in-sveltevar(--x) в <style> должен быть определён в src/preview.css (словарь токенов) или локально в компоненте. Файл читается один раз и кэшируется (не glob-зависим).

Изоляция веток old ↔ preview

isolation/no-mixed-imports (web/eslint-plugins/isolation/no-mixed-imports.js, включён на весь **/*.{ts,svelte}) — полная взаимная изоляция старого UI и новой preview-ветки. В отличие от no-restricted-imports, правило резолвит каждый импорт (и $lib/..., и относительные .//../) до реального файла и классифицирует стороны по фактическому пути, поэтому относительным импортом правило не обойти.

  • Старое: routes/(old)/**, lib/registry.ts, lib/registry/**, lib/registry-helpers.ts, lib/categories.ts, lib/tools/**, lib/components/** (кроме kit/).
  • Новое: routes/preview/**, lib/registry-new/**, lib/preview/**, lib/registry-schema.ts, lib/registry-schema.test.ts, lib/components/kit/**.
  • Общее (разрешено обоим): core/, i18n/, theme, assets/, корневой lib.
  • Плагин конфигурируем (опции old/new + root/alias в eslint.config.js): перенос старых файлов в папки old/ — это правка glob-паттернов в настройке, а не код правила.
  • Изоляция уже достигнута: старый registry/ не тянет registry-schema (пилоты add-border/add-stroke работают через params), registry.ts не импортирует ToolSchema; preview-list-tools использует копию preview/tool-icons.ts, а не старый tools/tool-icons.ts.

Линтер только показывает ошибки, старый код (непрефиксованные токены, hex в preview.css) — известный техдолг, его НЕ чинить и не игнорировать правилами.

  • Коммиты мелкие (< ~500 строк), атомарные