13 KiB
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(Vitest), e2e:pnpm --dir web test:e2e(Playwright,web/e2e/, свой webServer наpnpm build+scripts/serve-static.mjs, порт 4173). - Форматирование: два независимых прогона. Корневой prettier форматирует
только markdown
docs/(.prettierrcв корне,pnpm format:docs, проверка —pnpm check:docs). Корневой.prettierignore— только точечные исключения (node_modules, refs-html…), без/*-allowlist: он глушит формат-он-сейв для всегоweb/(файлы резолвятся как ignored и дочерние конфиги не применяются). Кодweb/форматируется отдельно:pnpm --dir web format(Prettier +prettier-plugin-svelte, конфигweb/.prettierrc, игнорweb/.prettierignore). Проверка без записи:pnpm --dir web exec prettier --check .. Оба прогона сразу:pnpm formatв корне. - Линтинг:
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): на C17src/routes/preview/**→src/routes/**(preview переезжает на реальные маршруты), на C19 при переименованииkit/→ui/— и компонентный glob. Не оставлять устаревшие пути вeslint.config.js. - Для
*.svelteвыключенprefer-const(пропсы в Svelte 5 пишутся черезlet).
- На весь код — парсинг TS/Svelte + правило
- Единый запуск всей дизайн-проверки:
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 тестирует паттерн без--(--x→x), а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-svelte—var(--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/old/**. - Новое:
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 строк), атомарные