diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
index 74a668e..e410624 100644
--- a/.gitea/workflows/ci.yml
+++ b/.gitea/workflows/ci.yml
@@ -20,6 +20,9 @@ jobs:
echo "DOCKER_REGISTRY=$DOCKER_REGISTRY" >> $GITHUB_ENV
REGISTRY_IMAGE="$DOCKER_REGISTRY/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')"
echo "REGISTRY_IMAGE=$REGISTRY_IMAGE" >> $GITHUB_ENV
+ # Версия сборки: тег если есть, иначе короткий SHA
+ BUILD_VERSION=$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD)
+ echo "BUILD_VERSION=$BUILD_VERSION" >> $GITHUB_ENV
- name: Login to Container Registry
uses: docker/login-action@v3
@@ -47,6 +50,8 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+ build-args: |
+ BUILD_VERSION=${{ env.BUILD_VERSION }}
provenance: false
sbom: false
diff --git a/.gitignore b/.gitignore
index b303b1a..80176b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -134,4 +134,8 @@ dist
# VitePress
.vitepress/cache
.vitepress/dist
-docs/.vitepress
\ No newline at end of file
+docs/.vitepress
+
+# Генерируется через `npm run llms`
+docs/public/
+generated/
\ No newline at end of file
diff --git a/.vitepress/config.ts b/.vitepress/config.ts
index a7c441c..085eca7 100644
--- a/.vitepress/config.ts
+++ b/.vitepress/config.ts
@@ -3,45 +3,45 @@ import { defineConfig } from 'vitepress';
const ruSidebar = [
{
text: 'Workflow',
- link: '/workflow',
+ link: '/ru/workflow',
},
{
text: 'Базовые правила',
items: [
- { text: 'Технологии и библиотеки', link: '/basics/tech-stack' },
- { text: 'Именование', link: '/basics/naming' },
+ { text: 'Технологии и библиотеки', link: '/ru/basics/tech-stack' },
+ { text: 'Именование', link: '/ru/basics/naming' },
{
text: 'Архитектура',
collapsed: true,
items: [
- { text: 'Обзор', link: '/basics/architecture/' },
- { text: 'Слои', link: '/basics/architecture/reference/layers' },
- { text: 'Модули', link: '/basics/architecture/reference/modules' },
- { text: 'Сегменты', link: '/basics/architecture/reference/segments' },
+ { text: 'Обзор', link: '/ru/basics/architecture/' },
+ { text: 'Слои', link: '/ru/basics/architecture/reference/layers' },
+ { text: 'Модули', link: '/ru/basics/architecture/reference/modules' },
+ { text: 'Сегменты', link: '/ru/basics/architecture/reference/segments' },
],
},
- { text: 'Стиль кода', link: '/basics/code-style' },
- { text: 'Документирование', link: '/basics/documentation' },
- { text: 'Типизация', link: '/basics/typing' },
+ { text: 'Стиль кода', link: '/ru/basics/code-style' },
+ { text: 'Документирование', link: '/ru/basics/documentation' },
+ { text: 'Типизация', link: '/ru/basics/typing' },
],
},
{
text: 'Прикладные разделы',
items: [
- { text: 'Структура проекта', link: '/applied/project-structure' },
- { text: 'Компоненты', link: '/applied/components' },
- { text: 'Страницы (App Router)', link: '/applied/page-level' },
- { text: 'Шаблоны и генерация кода', link: '/applied/templates-generation' },
- { text: 'Стили', link: '/applied/styles' },
- { text: 'Изображения', link: '/applied/images-sprites' },
- { text: 'SVG-спрайты', link: '/applied/svg-sprites' },
- { text: 'Видео', link: '/applied/video' },
- { text: 'API', link: '/applied/api' },
- { text: 'Stores', link: '/applied/stores' },
- { text: 'Хуки', link: '/applied/hooks' },
- { text: 'Шрифты', link: '/applied/fonts' },
- { text: 'Локализация', link: '/applied/localization' },
- { text: 'Настройка VS Code', link: '/applied/vscode' },
+ { text: 'Структура проекта', link: '/ru/applied/project-structure' },
+ { text: 'Компоненты', link: '/ru/applied/components' },
+ { text: 'Страницы (App Router)', link: '/ru/applied/page-level' },
+ { text: 'Шаблоны и генерация кода', link: '/ru/applied/templates-generation' },
+ { text: 'Стили', link: '/ru/applied/styles' },
+ { text: 'Изображения', link: '/ru/applied/images-sprites' },
+ { text: 'SVG-спрайты', link: '/ru/applied/svg-sprites' },
+ { text: 'Видео', link: '/ru/applied/video' },
+ { text: 'API', link: '/ru/applied/api' },
+ { text: 'Stores', link: '/ru/applied/stores' },
+ { text: 'Хуки', link: '/ru/applied/hooks' },
+ { text: 'Шрифты', link: '/ru/applied/fonts' },
+ { text: 'Локализация', link: '/ru/applied/localization' },
+ { text: 'Настройка VS Code', link: '/ru/applied/vscode' },
],
},
];
@@ -92,30 +92,71 @@ const enSidebar = [
},
];
+/**
+ * Vite-плагин: отдаёт `.txt` и `.md` с явной кодировкой UTF-8.
+ * Без этого браузер декодирует как ISO-8859-1 и кириллица ломается.
+ */
+const utf8TextPlugin = {
+ name: 'utf8-text-files',
+ configureServer(server: any) {
+ server.middlewares.use((req: any, res: any, next: any) => {
+ const url: string = req.url || '';
+ if (url.endsWith('.txt') || url.endsWith('.md')) {
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
+ }
+ next();
+ });
+ },
+};
+
export default defineConfig({
srcDir: 'docs',
+ // `docs/public/` содержит сгенерированные `.md`-копии и `llms.txt` для LLM
+ // (попадают в корень `dist/` как статика). Исключаем из сканирования
+ // страниц, иначе VitePress рендерит их как HTML-страницы.
+ srcExclude: ['public/**'],
title: 'NextJS Style Guide',
description: 'Правила и стандарты разработки на NextJS и TypeScript',
- rewrites: {
- 'ru/:rest*': ':rest*',
+ vite: {
+ plugins: [utf8TextPlugin],
+ define: {
+ __BUILD_VERSION__: JSON.stringify(process.env.BUILD_VERSION || 'dev'),
+ },
},
locales: {
root: {
+ label: 'Languages',
+ lang: 'en',
+ },
+ ru: {
label: 'Русский',
lang: 'ru-RU',
+ link: '/ru/',
+ description: 'Стандарты разработки на Next.js + TypeScript с архитектурой SLM',
themeConfig: {
sidebar: ruSidebar,
},
+ // Расширенный блок описания для llms.txt — даёт LLM полный
+ // технический контекст: стек, методология, охват тем.
+ llmsBlockquote:
+ 'Стандарты разработки frontend-приложений на Next.js (App Router) + TypeScript + React с архитектурой SLM (Scoped Layered Module Design — модульная архитектура со слоями ответственности, где каждый модуль содержит всё необходимое: компоненты, хуки, сторы, типы, стили).',
+ llmsContext:
+ 'Стек: React, TypeScript, Next.js App Router, Mantine UI, SWR, Zustand, i18next, PostCSS Modules, Vitest, clsx.\n\nДокументация покрывает архитектуру SLM (слои, модули, сегменты, направление зависимостей, публичный API), правила оформления кода (именование, форматирование, импорты, типизация, JSDoc), реализацию компонентов и хуков, работу с App Router, кодогенерацию из шаблонов, стилизацию (Mobile First, токены), работу с API и сокетами, управление состоянием через Zustand, локализацию, ассеты (шрифты, изображения, SVG-спрайты) и настройку VS Code.',
},
en: {
label: 'English',
lang: 'en-US',
link: '/en/',
+ description: 'Next.js + TypeScript development standards with SLM architecture',
themeConfig: {
sidebar: enSidebar,
},
+ llmsBlockquote:
+ 'Frontend development standards for Next.js (App Router) + TypeScript + React projects with SLM architecture (Scoped Layered Module Design — a modular architecture with responsibility layers, where each module contains everything it needs: components, hooks, stores, types, styles).',
+ llmsContext:
+ 'Stack: React, TypeScript, Next.js App Router, Mantine UI, SWR, Zustand, i18next, PostCSS Modules, Vitest, clsx.\n\nThe documentation covers SLM architecture (layers, modules, segments, dependency direction, public API), code conventions (naming, formatting, imports, typing, JSDoc), component and hook implementation, App Router usage, code generation from templates, styling (Mobile First, design tokens), API and socket integration, state management via Zustand, localization, assets (fonts, images, SVG sprites), and VS Code setup.',
},
},
});
diff --git a/AGENTS.md b/AGENTS.md
index cd32e81..28730eb 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -3,6 +3,6 @@
При работе с документацией следовать правилам из CONTRIBUTING.md.
- Язык документации и коммитов — русский.
-- После изменений в `.md`-файлах — запустить `npm run docs` для обновления RULES.md.
-- При добавлении нового раздела — обновить сайдбар (`.vitepress/config.ts`)
- и порядок файлов (`concat-md.js`).
+- После изменений в `.md`-файлах — запустить `npm run llms` для обновления `llms.txt` и README.
+- При добавлении нового раздела — обновить сайдбар (`.vitepress/config.ts`):
+ он же является источником порядка и группировки для `llms.txt`.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 136381d..c408fdc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,7 +17,7 @@
|---------|-----------|
| `npm run dev` | Локальный сервер разработки |
| `npm run build` | Сборка статического сайта |
-| `npm run docs` | Генерация `generated/{lang}/RULES.md` — единый файл для AI-ассистентов |
+| `npm run llms` | Генерация `generated/{lang}/llms.txt` (карта документации для LLM) и README |
## Структура файлов
@@ -51,16 +51,17 @@ docs/
.vitepress/
├── config.ts # Конфигурация VitePress, сайдбары, локали
generated/
-├── ru/RULES.md # Сгенерированный единый файл (ru)
-└── en/RULES.md # Сгенерированный единый файл (en)
-concat-md.js # Скрипт генерации RULES.md
+├── ru/llms.txt # Карта документации для LLM (ru, llmstxt.org)
+└── en/llms.txt # Карта документации для LLM (en, llmstxt.org)
+generate-llms.ts # Скрипт генерации llms.txt и README
```
### Добавление нового раздела
1. Создать `.md`-файл в нужной папке (`basics/` или `applied/`).
2. Добавить пункт в сайдбар — `.vitepress/config.ts` (оба языка, если есть перевод).
-3. Добавить файл в массив `fileOrder` — `concat-md.js` (для генерации RULES.md).
+ Сайдбар — единственный источник порядка и группировки для `llms.txt`.
+3. Запустить `npm run llms` для обновления `generated/{lang}/llms.txt`.
## Два типа документации
diff --git a/Caddyfile b/Caddyfile
index bde2896..8bbac9f 100644
--- a/Caddyfile
+++ b/Caddyfile
@@ -1,5 +1,10 @@
:8080 {
root * /srv
+
+ # Кириллица в .txt и .md ломается без явного charset
+ @text path *.txt *.md
+ header @text Content-Type "text/plain; charset=utf-8"
+
file_server
try_files {path} /index.html
}
diff --git a/Dockerfile b/Dockerfile
index 8577c52..8bf0823 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,13 @@
FROM node:24-alpine AS build
WORKDIR /app
+# zip нужен для упаковки nextjs-style-guide-{lang}.zip
+RUN apk add --no-cache zip
COPY package*.json ./
RUN npm ci
COPY . .
-RUN npm run build
+ARG BUILD_VERSION=dev
+ENV BUILD_VERSION=${BUILD_VERSION}
+RUN npm run llms && npm run build
FROM caddy:2-alpine
COPY Caddyfile /etc/caddy/Caddyfile
diff --git a/README.md b/README.md
index cdd8f00..0baefe1 100644
--- a/README.md
+++ b/README.md
@@ -54,4 +54,5 @@ Rules and standards for NextJS and TypeScript development: architecture, typing,
## For Assistants
-Full documentation in a single MD file: https://gromlab.ru/docs/frontend-style-guide/raw/branch/main/generated/en/RULES.md
+Documentation map with links to all sections ([llmstxt.org](https://llmstxt.org) format):
+https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/en/llms.txt
diff --git a/README_RU.md b/README_RU.md
index 1c3f438..6278457 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -4,7 +4,8 @@
## Для ассистентов
-Полная документация в одном MD файле: https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/RULES.md
+Карта документации со ссылками на все разделы (формат [llmstxt.org](https://llmstxt.org)):
+https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
## Структура документации
diff --git a/concat-md.js b/concat-md.js
deleted file mode 100644
index ce18594..0000000
--- a/concat-md.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import path from "path";
-import fs from "fs";
-
-// Явный порядок файлов внутри каждого языка
-const fileOrder = [
- // index
- "index.md",
- // workflow
- "workflow.md",
- // basics
- "basics/tech-stack.md",
- "basics/naming.md",
- "basics/architecture/index.md",
- "basics/architecture/reference/layers.md",
- "basics/architecture/reference/modules.md",
- "basics/architecture/reference/segments.md",
- "basics/code-style.md",
- "basics/documentation.md",
- "basics/typing.md",
- // applied
- "applied/project-structure.md",
- "applied/components.md",
- "applied/page-level.md",
- "applied/templates-generation.md",
- "applied/styles.md",
- "applied/images-sprites.md",
- "applied/svg-sprites.md",
- "applied/video.md",
- "applied/api.md",
- "applied/stores.md",
- "applied/hooks.md",
- "applied/fonts.md",
- "applied/localization.md",
- "applied/vscode.md",
-];
-
-// Удалить frontmatter из содержимого md-файла
-const stripFrontmatter = (content) =>
- content.replace(/^---[\s\S]*?---\n*/m, "");
-
-// Сдвинуть уровень заголовков на 1 вниз (h1→h2, h2→h3, ...)
-// Не трогает заголовки внутри блоков кода
-const shiftHeadings = (content) => {
- const lines = content.split("\n");
- let inCodeBlock = false;
-
- return lines
- .map((line) => {
- if (line.startsWith("```")) inCodeBlock = !inCodeBlock;
- if (inCodeBlock) return line;
- if (/^#{1,5}\s/.test(line)) return "#" + line;
- return line;
- })
- .join("\n");
-};
-
-// Собрать RULES.md с мета-якорями для каждого файла
-const buildRules = (lang) => {
- const srcDir = `./docs/${lang}`;
- const outDir = `./generated/${lang}`;
- const outFile = path.join(outDir, "RULES.md");
-
- if (!fs.existsSync(srcDir)) {
- console.log(`Пропуск ${lang}: папка ${srcDir} не найдена`);
- return;
- }
-
- fs.mkdirSync(outDir, { recursive: true });
-
- const parts = [];
-
- for (const file of fileOrder) {
- const filePath = path.join(srcDir, file);
- if (!fs.existsSync(filePath)) continue;
-
- const raw = fs.readFileSync(filePath, "utf8");
- const content = stripFrontmatter(raw).trim();
- if (!content) continue;
-
- // Мета-якорь: путь VitePress без расширения
- const route = "/" + file.replace(/\.md$/, "");
- // index.md остаётся без сдвига (его h1 — главный заголовок документа)
- const processed = file === "index.md" ? content : shiftHeadings(content);
- parts.push(`\n${processed}`);
- }
-
- fs.writeFileSync(outFile, parts.join("\n\n"), "utf8");
- console.log(`RULES.md (${lang}) создан: ${outFile}`);
-};
-
-// Собираем RULES.md для обоих языков
-buildRules("ru");
-buildRules("en");
-
-// Генерируем README из index.md
-const buildReadme = (lang, outFile) => {
- const indexPath = `./docs/${lang}/index.md`;
-
- if (!fs.existsSync(indexPath)) {
- console.log(`Пропуск README (${lang}): ${indexPath} не найден`);
- return;
- }
-
- const content = stripFrontmatter(fs.readFileSync(indexPath, "utf8"));
- fs.writeFileSync(outFile, content, "utf8");
- console.log(`${outFile} создан из ${indexPath}`);
-};
-
-buildReadme("en", "./README.md");
-buildReadme("ru", "./README_RU.md");
diff --git a/docs/en/index.md b/docs/en/index.md
index cdd8f00..0baefe1 100644
--- a/docs/en/index.md
+++ b/docs/en/index.md
@@ -54,4 +54,5 @@ Rules and standards for NextJS and TypeScript development: architecture, typing,
## For Assistants
-Full documentation in a single MD file: https://gromlab.ru/docs/frontend-style-guide/raw/branch/main/generated/en/RULES.md
+Documentation map with links to all sections ([llmstxt.org](https://llmstxt.org) format):
+https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/en/llms.txt
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..ac6b102
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,372 @@
+---
+layout: false
+---
+
+
+
+
+
+ NextJS Style Guide
+
+ {{ t.tagline }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
v{{ buildVersion }}
+
+
+
diff --git a/docs/ru/applied/components.md b/docs/ru/applied/components.md
index 5782d43..addc019 100644
--- a/docs/ru/applied/components.md
+++ b/docs/ru/applied/components.md
@@ -6,7 +6,7 @@ title: Компоненты
Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
-Архитектурные слои и их назначение описаны в разделе [Архитектура](/basics/architecture/).
+Архитектурные слои и их назначение описаны в разделе [Архитектура](/ru/basics/architecture/).
## Правила организации
@@ -43,7 +43,7 @@ container/
- **`type` вместо `interface`** — гибче для пропсов: поддерживает union, intersection, mapped types. Declaration merging пропсам не нужно.
- **Без `FC`** — неявно добавляет `children`, усложняет дженерики, не даёт преимуществ перед аннотацией параметра.
- **Типы в `types/`, а не в `.tsx`** — предотвращает циклические зависимости (компонент импортирует хук, хук импортирует тип из компонента) и разделяет ответственность: `.tsx` для рендера, `.type.ts` для данных.
-- **Без возвращаемого типа** — TypeScript выводит из JSX. Осознанное исключение из [базового правила](/basics/typing).
+- **Без возвращаемого типа** — TypeScript выводит из JSX. Осознанное исключение из [базового правила](/ru/basics/typing).
## Реализация
diff --git a/docs/ru/basics/architecture/reference/modules.md b/docs/ru/basics/architecture/reference/modules.md
index 5a245ee..4bb7b0d 100644
--- a/docs/ru/basics/architecture/reference/modules.md
+++ b/docs/ru/basics/architecture/reference/modules.md
@@ -52,7 +52,7 @@ auth/
└── index.ts # публичный API
```
-Подробное описание каждого сегмента — в разделе [Сегменты](/basics/architecture/reference/segments).
+Подробное описание каждого сегмента — в разделе [Сегменты](/ru/basics/architecture/reference/segments).
## Публичный API
diff --git a/docs/ru/basics/tech-stack.md b/docs/ru/basics/tech-stack.md
index 2c20d71..8cbc978 100644
--- a/docs/ru/basics/tech-stack.md
+++ b/docs/ru/basics/tech-stack.md
@@ -13,7 +13,7 @@ title: Технологии и библиотеки
- `Next.js` — для продуктовых сайтов.
### Архитектура
-- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/basics/architecture/).
+- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/ru/basics/architecture/).
### UI компоненты
- `Mantine UI` — базовые UI-компоненты.
diff --git a/docs/ru/index.md b/docs/ru/index.md
index 1c3f438..6278457 100644
--- a/docs/ru/index.md
+++ b/docs/ru/index.md
@@ -4,7 +4,8 @@
## Для ассистентов
-Полная документация в одном MD файле: https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/RULES.md
+Карта документации со ссылками на все разделы (формат [llmstxt.org](https://llmstxt.org)):
+https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/llms.txt
## Структура документации
diff --git a/docs/ru/workflow/code-generation.md b/docs/ru/workflow/code-generation.md
index 2958653..5cce8ed 100644
--- a/docs/ru/workflow/code-generation.md
+++ b/docs/ru/workflow/code-generation.md
@@ -28,4 +28,4 @@ title: Генерация кода
- Повторяющаяся структура появляется больше одного раза.
- Существующий шаблон не покрывает нужный тип модуля.
-Инструменты и синтаксис шаблонов — [Шаблоны и генерация кода](/applied/templates-generation).
+Инструменты и синтаксис шаблонов — [Шаблоны и генерация кода](/ru/applied/templates-generation).
diff --git a/docs/ru/workflow/creating-components.md b/docs/ru/workflow/creating-components.md
index a59d2d6..cacf98c 100644
--- a/docs/ru/workflow/creating-components.md
+++ b/docs/ru/workflow/creating-components.md
@@ -12,11 +12,11 @@ title: Добавление UI-модуля
## Порядок действий
-1. [Сгенерировать](/applied/templates-generation) модуль из соответствующего шаблона в целевой слой.
+1. [Сгенерировать](/ru/applied/templates-generation) модуль из соответствующего шаблона в целевой слой.
2. Заполнить модуль логикой и стилями.
## Дочерние компоненты
-Если модулю нужны внутренние подкомпоненты — [генерировать](/applied/templates-generation) их из шаблона `component` в папку `ui/` внутри родительского модуля. Дочерние компоненты не экспортируются через `index.ts` родителя.
+Если модулю нужны внутренние подкомпоненты — [генерировать](/ru/applied/templates-generation) их из шаблона `component` в папку `ui/` внутри родительского модуля. Дочерние компоненты не экспортируются через `index.ts` родителя.
-Правила написания компонентов — [Компоненты](/applied/components).
+Правила написания компонентов — [Компоненты](/ru/applied/components).
diff --git a/docs/ru/workflow/creating-pages.md b/docs/ru/workflow/creating-pages.md
index 685cde2..1be0eaf 100644
--- a/docs/ru/workflow/creating-pages.md
+++ b/docs/ru/workflow/creating-pages.md
@@ -12,7 +12,7 @@ title: Добавление страницы
## Порядок действий
-1. [Сгенерировать](/applied/templates-generation) экран из шаблона `screen` в папку `src/screens/`.
+1. [Сгенерировать](/ru/applied/templates-generation) экран из шаблона `screen` в папку `src/screens/`.
2. Заполнить экран логикой и стилями.
@@ -20,8 +20,8 @@ title: Добавление страницы
## Правила
-- Ручное создание файловой структуры экрана запрещено — только [генерация](/applied/templates-generation) из шаблона.
+- Ручное создание файловой структуры экрана запрещено — только [генерация](/ru/applied/templates-generation) из шаблона.
- Логика, стили и зависимости размещаются в экране, не в `page.tsx`.
- Каждая страница содержит `metadata` с `title` и `description`.
-Примеры `page.tsx` и `metadata` — [Page-level компоненты](/applied/page-level).
+Примеры `page.tsx` и `metadata` — [Page-level компоненты](/ru/applied/page-level).
diff --git a/docs/ru/workflow/getting-started.md b/docs/ru/workflow/getting-started.md
index 6b45648..b7ed5f4 100644
--- a/docs/ru/workflow/getting-started.md
+++ b/docs/ru/workflow/getting-started.md
@@ -10,7 +10,7 @@ title: Начало работы
**Next.js** (App Router), **Mantine**, **Zustand**, **SLM Design**.
-Подробнее — [Технологии и библиотеки](/basics/tech-stack).
+Подробнее — [Технологии и библиотеки](/ru/basics/tech-stack).
## Ключевые особенности
@@ -19,4 +19,4 @@ title: Начало работы
## Настройка окружения
-Открыть проект в VS Code и установить рекомендуемые расширения — редактор предложит это автоматически. Подробнее — [Настройка VS Code](/applied/vscode).
+Открыть проект в VS Code и установить рекомендуемые расширения — редактор предложит это автоматически. Подробнее — [Настройка VS Code](/ru/applied/vscode).
diff --git a/docs/ru/workflow/styling.md b/docs/ru/workflow/styling.md
index 4bf352f..5d27a1f 100644
--- a/docs/ru/workflow/styling.md
+++ b/docs/ru/workflow/styling.md
@@ -20,4 +20,4 @@ title: Стилизация
- **Магические значения** — произвольные цвета, отступы и скругления запрещены, использовать токены.
- **Глобальные стили** вне `app/styles/` запрещены.
-Правила написания CSS, вложенность, медиа-запросы и токены — [Стили](/applied/styles).
+Правила написания CSS, вложенность, медиа-запросы и токены — [Стили](/ru/applied/styles).
diff --git a/generate-llms.ts b/generate-llms.ts
new file mode 100644
index 0000000..97c6df7
--- /dev/null
+++ b/generate-llms.ts
@@ -0,0 +1,383 @@
+import path from 'node:path';
+import fs from 'node:fs';
+import os from 'node:os';
+import { execFileSync } from 'node:child_process';
+import config from './.vitepress/config';
+
+/** Версия сборки. Передаётся CI через ENV; локально — `dev`. */
+const VERSION = process.env.BUILD_VERSION || 'dev';
+const BUILD_DATE = new Date().toISOString();
+
+/** Корневая папка для генерируемой статики (попадает в build dist). */
+const PUBLIC_DIR = 'docs/public';
+
+type Lang = 'ru' | 'en';
+
+interface SidebarItem {
+ text: string;
+ link?: string;
+ items?: SidebarItem[];
+ collapsed?: boolean;
+}
+
+interface Entry {
+ /** Название группы верхнего уровня (sidebar[].text) */
+ section: string;
+ /** Префикс из вложенной группы (например "Архитектура") */
+ prefix: string | null;
+ /** Текст пункта в sidebar */
+ text: string;
+ /** Ссылка из sidebar */
+ link: string;
+}
+
+/** Разобрать YAML frontmatter (плоский, без вложенностей) */
+const parseFrontmatter = (
+ content: string,
+): { data: Record; body: string } => {
+ const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
+ if (!match) return { data: {}, body: content };
+
+ const data: Record = {};
+ for (const line of match[1].split('\n')) {
+ const lineMatch = line.match(/^([^:]+):\s*(.*)$/);
+ if (!lineMatch) continue;
+ let value = lineMatch[2].trim();
+ if (
+ (value.startsWith('"') && value.endsWith('"')) ||
+ (value.startsWith("'") && value.endsWith("'"))
+ ) {
+ value = value.slice(1, -1);
+ }
+ data[lineMatch[1].trim()] = value;
+ }
+ return { data, body: match[2] };
+};
+
+/** Первый абзац после h1 — однострочное описание для llms.txt */
+const firstParagraphAfterH1 = (body: string): string | null => {
+ const lines = body.split('\n');
+ const h1Idx = lines.findIndex((l) => /^#\s/.test(l));
+ if (h1Idx === -1) return null;
+
+ let i = h1Idx + 1;
+ while (i < lines.length && lines[i].trim() === '') i++;
+
+ const para: string[] = [];
+ while (
+ i < lines.length &&
+ lines[i].trim() !== '' &&
+ !lines[i].startsWith('#')
+ ) {
+ para.push(lines[i].trim());
+ i++;
+ }
+ return para.join(' ').trim() || null;
+};
+
+/**
+ * Преобразовать sidebar `link` в относительный путь файла внутри
+ * `docs/{lang}/`. Sidebar links содержат полный префикс локали
+ * (`/ru/...`, `/en/...`) — отрезаем его.
+ */
+const linkToRel = (link: string, lang: Lang): string => {
+ const prefix = `/${lang}/`;
+ let rel = link.startsWith(prefix) ? link.slice(prefix.length) : link.replace(/^\//, '');
+ if (rel === '' || rel.endsWith('/')) {
+ rel += 'index.md';
+ } else {
+ rel += '.md';
+ }
+ return rel;
+};
+
+const linkToFilePath = (link: string, lang: Lang): string =>
+ path.join('docs', lang, linkToRel(link, lang));
+
+/**
+ * Абсолютный путь от корня сайта к `.md`-копии страницы.
+ * После build файлы лежат в `dist/{lang}/...md` (через `docs/public/`).
+ */
+const linkToSiteUrl = (link: string, lang: Lang): string =>
+ `/${lang}/${linkToRel(link, lang)}`;
+
+/**
+ * Развернуть sidebar в плоский список с сохранением группы и
+ * опционального префикса вложенной группы.
+ */
+const flattenSidebar = (sidebar: SidebarItem[]): Entry[] => {
+ const entries: Entry[] = [];
+
+ for (const top of sidebar) {
+ const section = top.text;
+
+ if (top.link && !top.items) {
+ entries.push({ section, prefix: null, text: top.text, link: top.link });
+ continue;
+ }
+
+ if (!top.items) continue;
+
+ for (const item of top.items) {
+ if (item.items) {
+ for (const sub of item.items) {
+ if (!sub.link) continue;
+ entries.push({
+ section,
+ prefix: item.text,
+ text: sub.text,
+ link: sub.link,
+ });
+ }
+ } else if (item.link) {
+ entries.push({
+ section,
+ prefix: null,
+ text: item.text,
+ link: item.link,
+ });
+ }
+ }
+ }
+
+ return entries;
+};
+
+const groupBySection = (entries: Entry[]): Map => {
+ const map = new Map();
+ for (const entry of entries) {
+ const list = map.get(entry.section);
+ if (list) list.push(entry);
+ else map.set(entry.section, [entry]);
+ }
+ return map;
+};
+
+const buildLlms = (lang: Lang): void => {
+ const localeKey = lang;
+ // VitePress-конфиг типизирован как `UserConfig`, но обращаемся к
+ // фактически переданным значениям — сужаем тип через any.
+ const cfg = config as unknown as {
+ title: string;
+ description: string;
+ locales: Record<
+ string,
+ {
+ description?: string;
+ llmsBlockquote?: string;
+ llmsContext?: string;
+ themeConfig?: { sidebar?: SidebarItem[] };
+ }
+ >;
+ };
+
+ const locale = cfg.locales[localeKey];
+ const sidebar = locale?.themeConfig?.sidebar;
+ if (!sidebar) {
+ console.warn(`[${lang}] sidebar не найден в config`);
+ return;
+ }
+ // Для blockquote предпочитаем расширенный llms-текст; короткий
+ // description — fallback и используется для HTML meta-тега VitePress.
+ const blockquote = locale.llmsBlockquote ?? locale.description ?? cfg.description;
+ const context = locale.llmsContext;
+
+ const entries = flattenSidebar(sidebar);
+ const grouped = groupBySection(entries);
+
+ const lines: string[] = [];
+ lines.push(`# ${cfg.title}`);
+ lines.push('');
+ lines.push(`> ${blockquote}`);
+ lines.push('');
+ if (context) {
+ lines.push(context);
+ lines.push('');
+ }
+
+ for (const [section, items] of grouped) {
+ lines.push(`## ${section}`);
+ lines.push('');
+
+ for (const entry of items) {
+ const filePath = linkToFilePath(entry.link, lang);
+ const url = linkToSiteUrl(entry.link, lang);
+
+ // Текст ссылки берём из sidebar — он специально написан для навигации
+ // и точнее отражает иерархию (например "Обзор" внутри группы "Архитектура").
+ let description: string | null = null;
+
+ if (fs.existsSync(filePath)) {
+ const raw = fs.readFileSync(filePath, 'utf8');
+ const { data, body } = parseFrontmatter(raw);
+ description = data.description || firstParagraphAfterH1(body);
+ } else {
+ console.warn(`[${lang}] файл не найден: ${filePath}`);
+ }
+
+ const display = entry.prefix
+ ? `${entry.prefix}: ${entry.text}`
+ : entry.text;
+ const descPart = description ? `: ${description}` : '';
+ lines.push(`- [${display}](${url})${descPart}`);
+ }
+
+ lines.push('');
+ }
+
+ const outDir = path.join(PUBLIC_DIR, lang);
+ fs.mkdirSync(outDir, { recursive: true });
+ const outFile = path.join(outDir, 'llms.txt');
+ fs.writeFileSync(outFile, lines.join('\n'), 'utf8');
+ console.log(`${outFile} создан`);
+};
+
+/**
+ * Корневой `/llms.txt` — роутер. По стандарту llmstxt.org это
+ * единственный файл в корне сайта; для двуязычного проекта он
+ * указывает LLM на локализованные карты документации.
+ */
+const buildRootIndex = (): void => {
+ const cfg = config as unknown as {
+ title: string;
+ description: string;
+ locales: Record;
+ };
+
+ const ruDesc = cfg.locales.ru?.description ?? cfg.description;
+ const enDesc = cfg.locales.en?.description ?? cfg.description;
+
+ const lines: string[] = [
+ `# ${cfg.title}`,
+ '',
+ `> ${enDesc}.`,
+ '',
+ '## Documentation',
+ '',
+ `- [Русская версия (Russian)](/ru/llms.txt): ${ruDesc}.`,
+ '- English version: in development',
+ '',
+ ];
+
+ fs.mkdirSync(PUBLIC_DIR, { recursive: true });
+ const outFile = path.join(PUBLIC_DIR, 'llms.txt');
+ fs.writeFileSync(outFile, lines.join('\n'), 'utf8');
+ console.log(`${outFile} создан`);
+};
+
+/** Рекурсивно скопировать дерево, фильтруя по предикату. */
+const copyDirSync = (
+ src: string,
+ dest: string,
+ filter: (name: string) => boolean = () => true,
+): number => {
+ let count = 0;
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
+ const srcPath = path.join(src, entry.name);
+ const destPath = path.join(dest, entry.name);
+ if (entry.isDirectory()) {
+ fs.mkdirSync(destPath, { recursive: true });
+ count += copyDirSync(srcPath, destPath, filter);
+ } else if (entry.isFile() && filter(entry.name)) {
+ fs.mkdirSync(dest, { recursive: true });
+ fs.copyFileSync(srcPath, destPath);
+ count++;
+ }
+ }
+ return count;
+};
+
+/**
+ * Скопировать все `.md`-файлы локали в `docs/public/{lang}/`,
+ * чтобы они попали в build `dist/` и были доступны по URL `/lang/path.md`.
+ */
+const copyMdFiles = (lang: Lang): void => {
+ const srcDir = path.join('docs', lang);
+ const destDir = path.join(PUBLIC_DIR, lang);
+ if (!fs.existsSync(srcDir)) return;
+
+ const copied = copyDirSync(srcDir, destDir, (name) => name.endsWith('.md'));
+ console.log(`[${lang}] скопировано ${copied} .md-файлов в ${destDir}`);
+};
+
+/**
+ * Собрать `nextjs-style-guide-{lang}.zip` со всеми `.md` локали и `VERSION`.
+ * Внутри архива — единая папка `nextjs-style-guide/`.
+ *
+ * `llms.txt` в архив не кладём: его ссылки указывают на сайт и локально
+ * не работают. Структура папки сама по себе является картой документации.
+ */
+const buildZip = (lang: Lang): void => {
+ const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'nsg-'));
+ const stage = path.join(tmpRoot, 'nextjs-style-guide');
+ fs.mkdirSync(stage, { recursive: true });
+
+ copyDirSync(path.join('docs', lang), stage, (name) => name.endsWith('.md'));
+
+ fs.writeFileSync(
+ path.join(stage, 'VERSION'),
+ `${VERSION}\n${BUILD_DATE}\n`,
+ );
+
+ const outFile = path.resolve(
+ PUBLIC_DIR,
+ `nextjs-style-guide-${lang}.zip`,
+ );
+ fs.rmSync(outFile, { force: true });
+
+ execFileSync('zip', ['-rq', outFile, 'nextjs-style-guide'], {
+ cwd: tmpRoot,
+ });
+
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
+ console.log(`${outFile} создан (${VERSION})`);
+};
+
+/** Манифест сборки — для лендинга и внешних потребителей. */
+const writeManifest = (): void => {
+ const manifest = {
+ version: VERSION,
+ buildDate: BUILD_DATE,
+ languages: {
+ ru: {
+ llms: '/ru/llms.txt',
+ zip: '/nextjs-style-guide-ru.zip',
+ },
+ en: {
+ llms: '/en/llms.txt',
+ zip: '/nextjs-style-guide-en.zip',
+ },
+ },
+ };
+ fs.mkdirSync(PUBLIC_DIR, { recursive: true });
+ fs.writeFileSync(
+ path.join(PUBLIC_DIR, 'manifest.json'),
+ JSON.stringify(manifest, null, 2),
+ 'utf8',
+ );
+ console.log(`${PUBLIC_DIR}/manifest.json создан`);
+};
+
+/** Скопировать `index.md` локали в корневой README без frontmatter */
+const buildReadme = (lang: Lang, outFile: string): void => {
+ const indexPath = path.join('docs', lang, 'index.md');
+ if (!fs.existsSync(indexPath)) {
+ console.warn(`Пропуск ${outFile}: ${indexPath} не найден`);
+ return;
+ }
+ const raw = fs.readFileSync(indexPath, 'utf8');
+ const { body } = parseFrontmatter(raw);
+ fs.writeFileSync(outFile, body.trimStart(), 'utf8');
+ console.log(`${outFile} обновлён из ${indexPath}`);
+};
+
+buildLlms('ru');
+buildLlms('en');
+buildRootIndex();
+copyMdFiles('ru');
+copyMdFiles('en');
+buildZip('ru');
+buildZip('en');
+writeManifest();
+buildReadme('en', 'README.md');
+buildReadme('ru', 'README_RU.md');
diff --git a/generated/en/RULES.md b/generated/en/RULES.md
deleted file mode 100644
index d342838..0000000
--- a/generated/en/RULES.md
+++ /dev/null
@@ -1,132 +0,0 @@
-
-# NextJS Style Guide
-
-Rules and standards for NextJS and TypeScript development: architecture, typing, styles, components, API, and infrastructure.
-
-## Documentation Structure
-
-### Processes
-
-**What to do** in a specific situation — step-by-step instructions.
-
-| Section | Answers the question |
-|---------|---------------------|
-| Getting Started | What tools to install before starting development? |
-| Creating an App | How to create a new project, where to get a template? |
-| Creating Pages | How to add a page: routing and screen? |
-| Creating Components | How to generate components using templates? |
-| Styling | What to use: Mantine, tokens, or PostCSS? |
-| Data Fetching | How to fetch data: SWR, codegen, sockets? |
-| State Management | When and how to create a store (Zustand)? |
-| Localization | How to add translations and work with i18next? |
-
-### Basic Rules
-
-**What the code should look like** — standards not tied to a specific technology.
-
-| Section | Answers the question |
-|---------|---------------------|
-| Tech Stack | What stack do we use? |
-| Architecture | How are FSD layers, dependencies, and public API structured? |
-| Code Style | How to format code: indentation, quotes, imports, early return? |
-| Naming | How to name files, variables, components, hooks? |
-| Documentation | How to write JSDoc: what to document and what not? |
-| Typing | How to type: type vs interface, any/unknown? |
-
-### Applied Sections
-
-**How a specific area works** — rules, structure, and code examples for specific technologies and tools.
-
-| Section | Answers the question |
-|---------|---------------------|
-| Project Structure | How are folders and files organized by FSD? |
-| Components | How is a component structured: files, props, clsx? |
-| Page-level Components | How to define layout, page, loading, error, not-found? |
-| Templates & Code Generation | How do templates work: syntax, variables, modifiers? |
-| Styles | How to write CSS: PostCSS Modules, nesting, media, tokens? |
-| Images | _(not filled)_ |
-| SVG Sprites | _(not filled)_ |
-| Video | _(not filled)_ |
-| API | _(not filled)_ |
-| Stores | _(not filled)_ |
-| Hooks | _(not filled)_ |
-| Fonts | _(not filled)_ |
-| Localization | _(not filled)_ |
-
-## For Assistants
-
-Full documentation in a single MD file: https://gromlab.ru/docs/frontend-style-guide/raw/branch/main/generated/en/RULES.md
-
-
-## Tech Stack
-
-Base technology stack and libraries used in projects.
-
-
-## Naming
-
-Naming should be predictable, concise, and reflect the meaning of the entity.
-
-
-## Code Style
-
-Unified code formatting rules: indentation, line breaks, quotes, import order, and readability.
-
-
-## Documentation
-
-Documentation should help understand the purpose of an entity, not duplicate its types or obvious details.
-
-
-## Typing
-
-Typing is required for all public interfaces, functions, and components.
-
-
-## Project Structure
-
-Base project structure and principles of module organization at folder and file level.
-
-
-## Components
-
-Rules for creating UI components across all FSD layers.
-
-
-## Page-level Components
-
-Next.js App Router special files used by the framework by convention: `layout.tsx`, `page.tsx`, `loading.tsx`, `error.tsx`, `not-found.tsx`, `template.tsx`.
-
-
-## Templates & Code Generation
-
-Template tools, syntax, and examples for code generation.
-
-
-## Styles
-
-CSS writing rules: PostCSS Modules, nesting, media queries, variables, formatting.
-
-
-## Images
-
-
-## SVG Sprites
-
-
-## Video
-
-
-## API
-
-
-## Stores
-
-
-## Hooks
-
-
-## Fonts
-
-
-## Localization
\ No newline at end of file
diff --git a/generated/ru/RULES.md b/generated/ru/RULES.md
deleted file mode 100644
index bf10628..0000000
--- a/generated/ru/RULES.md
+++ /dev/null
@@ -1,2089 +0,0 @@
-
-# NextJS Style Guide
-
-Правила и стандарты разработки на NextJS и TypeScript: архитектура, типизация, стили, компоненты, API и инфраструктурные разделы.
-
-## Для ассистентов
-
-Полная документация в одном MD файле: https://gromlab.ru/docs/nextjs-style-guide/raw/branch/main/generated/ru/RULES.md
-
-## Структура документации
-
-### Workflow
-
-**Что делать и в каком порядке** — пошаговые инструкции.
-
-| Раздел | Отвечает на вопрос |
-|--------|-------------------|
-| Начало работы | Что нужно знать перед началом разработки? |
-| Создание проекта | Как начать новый проект? |
-| Генерация кода | Какие модули должны генерироваться из шаблонов? |
-| Добавление страницы | Как добавить новую страницу в проект? |
-| Добавление UI-модуля | Как создать компонент, бизнес-модуль, виджет или layout? |
-| Стилизация | Как стилизовать компоненты в проекте? |
-| Получение данных | Как получать данные с сервера? |
-| Управление состоянием | Как работать с состоянием? |
-| Локализация | Как добавлять переводы и подключать локализацию? |
-
-### Базовые правила
-
-**Каким должен быть код** — стандарты, не привязанные к конкретной технологии.
-
-| Раздел | Отвечает на вопрос |
-|--------|-------------------|
-| Технологии и библиотеки | Какой стек используем? |
-| Архитектура | Как устроены слои SLM, зависимости, публичный API? |
-| Стиль кода | Как оформлять код: отступы, кавычки, импорты, early return? |
-| Именование | Как называть файлы, переменные, компоненты, хуки? |
-| Документирование | Как писать JSDoc: что документировать, а что нет? |
-| Типизация | Как типизировать: type vs interface, any/unknown? |
-
-### Прикладные разделы
-
-**Как это настроить и использовать** — конфигурация, структура и примеры кода для конкретных областей.
-
-| Раздел | Отвечает на вопрос |
-|--------|-------------------|
-| Настройка VS Code | Как настроить редактор для проекта? |
-| Структура проекта | Как организованы папки и файлы по SLM? |
-| Компоненты | Как устроен компонент: файлы, пропсы, clsx? |
-| Page-level компоненты | Как описывать layout, page, loading, error, not-found? |
-| Шаблоны и генерация кода | Как работают шаблоны, синтаксис и инструменты генерации? |
-| Стили | Как писать CSS: PostCSS Modules, вложенность, медиа, токены? |
-| Изображения | _(не заполнен)_ |
-| SVG-спрайты | _(не заполнен)_ |
-| Видео | _(не заполнен)_ |
-| API | _(не заполнен)_ |
-| Stores | _(не заполнен)_ |
-| Хуки | _(не заполнен)_ |
-| Шрифты | _(не заполнен)_ |
-| Локализация | _(не заполнен)_ |
-
-
-## Workflow
-
-Порядок действий при разработке — от создания проекта до реализации фич.
-
-### Создание проекта
-
-Инициализация нового проекта из готового шаблона.
-
-1. Создать проект из шаблона:
- ```bash
- npx tiged git@gromlab.ru:templates/nextjs.git my-app
- cd my-app
- npm install
- ```
-2. Проект готов к разработке — стек, структура SLM, конфигурация
- редактора и шаблоны генерации уже настроены.
-
-### Генерация кода
-
-Создание модулей из шаблонов `.templates/` вместо ручного создания файлов.
-
-1. Определить тип модуля и соответствующий шаблон:
-
- | Модуль | Слой | Шаблон |
- |------------|--------------|-------------|
- | Компонент | `ui/` | `component` |
- | Бизнес-модуль | `business/` | `module` |
- | Виджет | `widgets/` | `widget` |
- | Layout | `layouts/` | `layout` |
- | Экран | `screens/` | `screen` |
- | Стор | `stores/` | `store` |
-
-2. Сгенерировать модуль из шаблона.
-3. Если подходящего шаблона нет — сначала создать шаблон, затем использовать.
-
-Ручное создание файловой структуры модулей запрещено.
-
-### Добавление страницы
-
-Создание нового маршрута: экран + точка входа для роутинга.
-
-1. Сгенерировать экран из шаблона `screen` в `src/screens/`.
-2. Заполнить экран логикой и стилями.
-3. Создать `page.tsx` в нужном маршруте `src/app/`.
-
-`page.tsx` — тонкая обёртка: только `metadata` и рендер экрана.
-Логика, стили и хуки размещаются в экране, не в `page.tsx`.
-
-### Добавление UI-модуля
-
-Создание компонента, бизнес-модуля, виджета или layout.
-
-1. Сгенерировать модуль из соответствующего шаблона в целевой слой.
-2. Заполнить модуль логикой и стилями.
-3. Дочерние компоненты — генерировать из шаблона `component` в папку `ui/`
- внутри родителя.
-
-Дочерние компоненты не экспортируются через `index.ts` родителя.
-
-### Стилизация
-
-Выбор инструмента стилизации по приоритету.
-
-1. Использовать Mantine-компоненты и их пропсы.
-2. Если Mantine не покрывает — использовать CSS-токены
- (`--color-*`, `--space-*`, `--radius-*`).
-3. Если нужна кастомная стилизация — PostCSS Modules.
-
-Инлайн-стили (`style`), магические значения и глобальные стили
-вне `app/styles/` запрещены.
-
-### Получение данных
-
-*Раздел в разработке* — SWR, генерация API-клиентов, сокеты.
-
-### Управление состоянием
-
-*Раздел в разработке* — когда создавать стор, что хранить локально и глобально.
-
-### Локализация
-
-*Раздел в разработке* — переводы и i18next.
-
-
-## Технологии и библиотеки
-
-Этот раздел описывает базовый стек технологий и библиотек, принятый в проекте.
-
-### Что используем
-
-#### Стек
-- `React` / `TypeScript` — основной стек для UI и приложения.
-- `Next.js` — для продуктовых сайтов.
-
-#### Архитектура
-- `SLM Design` — собственная модульная архитектура проекта. Подробнее в разделе [Архитектура](/basics/architecture/).
-
-#### UI компоненты
-- `Mantine UI` — базовые UI-компоненты.
-
-#### Работа с данными (API)
-- `@gromlab/api-codegen` — генерация API‑клиентов и типов.
-- `SWR` — получение, кеширование, ревалидация, дедубликация.
-- `SWR (useSWRSubscription)` — сокеты, реалтайм подписки.
-
-#### Store
-- `Zustand` — глобальное состояние.
-
-#### Локализация
-- `i18next (i18n)` — локализация всех пользовательских текстов.
-
-#### Тестирование
-- `Vitest` — тестирование.
-
-#### Стили
-- `PostCSS Modules` — изоляция стилей.
-- `Mobile First` — подход к адаптивной верстке.
-- `clsx` — конкатенация CSS‑классов.
-
-#### Генерация
-- `@gromlab/create` — шаблонизатор для создания слоёв и других файлов из шаблонов.
-
-
-## Именование
-
-Этот раздел описывает соглашения об именовании в проекте. Единые правила делают код предсказуемым и упрощают навигацию по проекту.
-
-### Базовые правила
-
-| Что | Рекомендуется |
-| ---------------- | ---------------------- |
-| Папки | `kebab-case` |
-| Файлы | `kebab-case` |
-| Переменные | `camelCase` |
-| Константы | `SCREAMING_SNAKE_CASE` |
-| Классы | `PascalCase` |
-| React-компоненты | `PascalCase` |
-| Хуки | `useSomething` |
-| CSS классы | `camelCase` |
-| Ключи enum | `SCREAMING_SNAKE_CASE` |
-
-
-### Именование файлов
-
-Суффикс обозначает роль или тип файла. Пишется в единственном числе.
-Формат: `name..ts`.
-
-**Хуки**
-- `use-name.hook.ts` — файл хука, функция именуется `useName`
-
-**Логика**
-- `.store.ts` — стор
-- `.service.ts` — сервис
-
-**Типы и контракты**
-- `.type.ts` — типы и интерфейсы
-- `.interface.ts` — интерфейсы
-- `.enum.ts` — enum
-- `.dto.ts` — внешние DTO
-- `.schema.ts` — схемы валидации
-- `.constant.ts` — константы
-- `.config.ts` — конфигурация
-
-**Утилиты**
-- `.util.ts` — утилиты
-- `.helper.ts` — вспомогательные функции
-- `.lib.ts` — библиотечный код
-
-**Тесты**
-- `.test.ts` — тесты
-- `.mock.ts` — моки
-
-**Хорошо**
-```text
-business/
-└── auth-by-email/
- ├── ui/
- │ └── login-form.tsx
- ├── hooks/
- │ └── use-auth.hook.ts
- ├── stores/
- │ └── auth.store.ts
- ├── types/
- │ └── auth.type.ts
- ├── auth-by-email.tsx
- └── index.ts
-```
-
-**Плохо**
-```text
-business/
-└── authByEmail/
- ├── LoginForm.tsx
- ├── useAuth.ts
- ├── authStore.ts
- └── index.ts
-```
-
-### Булевы значения
-
-- Использовать префиксы `is`, `has`, `can`, `should`.
-
-**Хорошо**
-```ts
-const isReady = true;
-const hasAccess = false;
-const canSubmit = true;
-const shouldRedirect = false;
-```
-
-**Плохо**
-```ts
-// Плохо: неясное булево значение без префикса.
-const ready = true;
-const access = false;
-const submit = true;
-```
-
-### События и обработчики
-
-- Обработчики начинать с `handle`.
-- События и колбэки начинать с `on`.
-
-**Хорошо**
-```ts
-const handleSubmit = () => { ... };
-const onSubmit = () => { ... };
-```
-
-**Плохо**
-```ts
-// Плохо: неочевидное назначение имени.
-const submitClick = () => { ... };
-```
-
-### Коллекции
-
-- Для массивов использовать имена во множественном числе.
-- Для словарей/мап — использовать суффиксы `ById`, `Map`, `Dict`.
-
-**Хорошо**
-```ts
-const users = [];
-const usersById = {} as Record;
-const userIds = ['u1', 'u2'];
-const ordersMap = new Map();
-const featureFlagsDict = { beta: true, legacy: false } as Record;
-```
-
-**Плохо**
-```ts
-// Плохо: имя не отражает, что это коллекция.
-const user = [];
-// Плохо: словарь назван как массив.
-const usersMap = [];
-// Плохо: по имени непонятно, что это словарь.
-const users = {} as Record;
-```
-
-
-## SLM Design
-Scoped Layered Module Design — модульная архитектура фронтенд-приложений. Код организован по слоям ответственности, а модуль содержит всё, что ему нужно: компоненты, хуки, сторы, типы, стили.
-
-### Преимущества
-
-#### Вертикальная организация домена
-
-Бизнес-домен не разбивается по техническим слоям — сценарии, сущности, типы и UI живут в одном модуле. Это сокращает время навигации и упрощает сопровождение: все изменения домена локализованы.
-
-#### Dependency Injection без фреймворков
-
-Cross-domain зависимости в бизнес-слое реализуются через фабрики — модуль декларирует что ему нужно, а точка использования предоставляет зависимости. Домены изолированы без DI-контейнеров, провайдеров и шин событий.
-
-#### Разделение ответственности без перегрузки слоёв
-
-Сервисы приложения (`infrastructure/`), UI-кит (`ui/`) и общие ресурсы (`shared/`) — три разных слоя с разной природой. Ни один слой не превращается в свалку разнородного кода.
-
-#### Горизонтальная инкапсуляция
-
-Вложенные модули (`parts/`) и направление зависимостей позволяют нескольким разработчикам работать над одной областью приложения параллельно, не затрагивая код друг друга.
-
-#### Колокация по умолчанию
-
-Код начинает жизнь рядом с местом использования и поднимается в общие слои только при реальной потребности. Глобальные слои не засоряются преждевременными абстракциями.
-
-#### Явное разделение каркаса и контента
-
-Каркас группы маршрутов (`layouts/`) и контент конкретной страницы (`screens/`) — независимые слои с собственной ответственностью.
-
-#### Масштабирование через группировку
-
-При росте проекта слои не теряют структуру — модули группируются по естественным признакам: бизнес-домены по субдоменам, страницы по разделам, UI-компоненты по уровню абстракции (примитивы и композиции).
-
-### Происхождение
-
-SLM Design вырос на основе:
-
-- **Feature-Sliced Design** — слоистая структура, публичный API модуля, направление зависимостей
-- **Vertical Slice Architecture** — модуль как вертикальный срез, содержащий всё необходимое
-- **Screaming Architecture** — структура проекта «кричит» о назначении: открыл `business/auth` — видишь авторизацию
-- **Colocation Principle** — код живёт рядом с местом использования
-
-### Пример структуры проекта
-
-```text
-src/
-├── app/
-│
-├── layouts/
-│ ├── main/
-│ └── dashboard/
-│
-├── screens/
-│ ├── home/
-│ ├── products/
-│ ├── product-detail/
-│ └── about/
-│
-├── widgets/
-│ ├── page-heading/
-│ ├── hero-section/
-│ └── promo-banner/
-│
-├── business/
-│ ├── auth/
-│ ├── catalog/
-│ ├── orders/
-│ └── chat/
-│
-├── infrastructure/
-│ ├── theme/
-│ ├── i18n/
-│ ├── backend-api/
-│ └── logger/
-│
-├── ui/
-│ ├── button/
-│ ├── input/
-│ ├── modal/
-│ ├── toast/
-│ └── dropdown/
-│
-└── shared/
- ├── lib/
- ├── types/
- └── styles/
-```
-
-### Принципы
-
-- **Домен — единое целое.** Всё, что относится к домену, живёт в одном модуле.
-- **Колокация.** Код рождается рядом с местом использования и поднимается только при необходимости.
-- **Зависимости однонаправлены.** Импорты только сверху вниз, только через публичный API.
-- **Архитектура — каркас, не клетка.** Правила фиксируют направление зависимостей и структуру модуля, остальное определяет команда.
-
-
-## Слои
-
-Раздел описывает слои SLM: что такое слой, какие бывают, как между ними направлены зависимости и какие правила действуют на каждом.
-
-### Определение
-
-**Слой — уровень организации кода внутри `src/`. Каждый слой отвечает за свою область (каркас страницы, бизнес-логика, UI-кит) и задаёт правила для кода внутри: направление импортов, именование, допустимые связи между модулями.**
-
-### Группы слоёв
-
-Слои делятся на три группы:
-
-| Группа | Слои | Описание |
-|--------|------|----------|
-| Композиция | `app`, `layouts`, `screens`, `widgets` | Собирают интерфейс из готовых блоков: маршруты, каркасы, страницы |
-| Ядро | `business`, `infrastructure`, `ui` | Реализация продукта: бизнес-домены, техсервисы, UI-кит |
-| Фундамент | `shared` | Общие ресурсы: утилиты, хелперы, стили, конфиги |
-
-### Направление зависимостей
-
-Любой импорт между модулями — только через публичный API.
-
-```
-app → [ layouts | screens ] → widgets → business → infrastructure → ui → shared
-```
-
-- `layouts` и `screens` — параллельные слои, не импортируют друг друга
-- Модули одного слоя в группе «Композиция» изолированы друг от друга
-- Модули одного слоя `infrastructure` и `ui` могут импортировать друг друга через публичный API
-- Модули `business` — cross-domain зависимости по коду через фабрику, `import type` напрямую
-- Импорт типов (`import type`) в «Ядре» разрешён в обоих направлениях
-
-
-### Слой App
-
-Точка входа приложения. Отвечает за запуск, роутинг и композицию маршрутов из layout и screen.
-
-В отличие от остальных слоёв, `app/` не содержит модулей SLM. Здесь живут только инфраструктурные файлы, которые не могут быть никаким другим слоем: файлы фреймворка роутинга, точка запуска и код инициализации.
-
-#### Требования
-
-- Не содержит модулей SLM — только файлы фреймворка, роутинг, инициализация
-- Содержит: файлы маршрутов, bootstrap, обработку ошибок верхнего уровня (404, error boundary), подключение глобальных стилей и ассетов
-- Провайдеры и гарды — только подключает готовые из нижних слоёв, не реализует
-- Не содержит бизнес-логику, UI-компоненты, хуки, сторы, сервисы
-- Никем не импортируется
-
-### Слой Layouts
-
-Каркас страницы: общие элементы, одинаковые для группы маршрутов (header, footer, sidebar).
-
-```text
-src/layouts/
-├── main/
-├── dashboard/
-└── auth/
-```
-
-#### Требования
-
-- Содержит только модули
-- Не содержит бизнес-логику
-- Контекстно-зависимые блоки принимает через пропсы от `app`, не импортирует напрямую
-
-### Слой Screens
-
-Контент конкретной страницы: собирает её из модулей нижних слоёв.
-
-```text
-src/screens/
-├── home/
-├── products/
-├── product-detail/
-├── about/
-└── contacts/
-```
-
-Когда количество страниц затрудняет навигацию — вводится группировка по разделам. Группа — папка для организации, не модуль (без `index.ts`).
-
-```text
-src/screens/
-├── shop/
-│ ├── home/
-│ ├── products/
-│ ├── product-detail/
-│ └── cart/
-├── account/
-│ ├── profile/
-│ ├── settings/
-│ └── order-history/
-└── info/
- ├── about/
- ├── contacts/
- └── faq/
-```
-
-#### Требования
-
-- Содержит только модули
-- Не содержит бизнес-логику
-- Локальные одноразовые секции живут внутри screen-модуля, не выносятся в `widgets`/`business`
-
-### Слой Widgets
-
-Составной блок интерфейса, который компонует модули ядра, но не принадлежит конкретному бизнес-домену. Widget появляется когда блок используется в нескольких screens или layouts.
-
-Если блок принадлежит домену — он живёт в `business/{area}/`, даже если переиспользуется. Если блок нужен только в одном месте — это `screens/{name}/parts/` или `layouts/{name}/parts/`, а не widget.
-
-```text
-src/widgets/
-├── page-heading/
-├── hero-section/
-├── onboarding-checklist/
-├── promo-banner/
-└── error-boundary/
-```
-
-#### Требования
-
-- Не принадлежит конкретному бизнес-домену. Если блок доменный — он живёт в `business/`
-- Используется в нескольких screens или layouts
-
-### Слой Business
-
-Бизнес-домены приложения: auth, catalog, orders, checkout, chat. Каждый домен — отдельный модуль со своими типами, логикой, UI и сервисами.
-
-Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`. Cross-domain зависимости по коду реализуются через фабрику. `import type` между доменами разрешён напрямую.
-
-Business объединяет то, что в FSD разделено на `features` и `entities`: пользовательские сценарии и бизнес-сущности живут вместе, внутри одного домена. Внутри домена сегменты разделяют ответственность: `types/` — доменная модель, `hooks/` и `services/` — сценарии и логика, `mappers/` — трансформация данных, `parts/` — составные блоки.
-
-```text
-src/business/
-├── auth/
-├── catalog/
-├── orders/
-├── checkout/
-└── chat/
-```
-
-Когда количество доменов затрудняет навигацию — вводится группировка по субдоменам. Группа — папка для организации, не модуль (без `index.ts`).
-
-```text
-src/business/
-├── commerce/
-│ ├── catalog/
-│ ├── cart/
-│ ├── orders/
-│ └── checkout/
-└── communication/
- ├── chat/
- └── notifications/
-```
-
-#### Требования
-
-- Один модуль = один бизнес-домен
-- Циклические зависимости между доменами запрещены
-- Импорт кода между доменами — через фабрику. `import type` — напрямую
-- Доменные типы (`User`, `Product`) живут здесь, не в `shared/`
-
-### Слой Infrastructure
-
-Техсервисы приложения: theme, i18n, API-адаптеры, logger, realtime. Каждый сервис — отдельный модуль.
-
-Слой входит в группу «Ядро». Импортирует `infrastructure/`, `ui/`, `shared/`.
-
-Отличие от `shared/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги).
-
-```text
-src/infrastructure/
-├── theme/
-├── i18n/
-├── backend-api/
-├── maps-api/
-├── logger/
-├── feature-flags/
-└── realtime/
-```
-
-#### Требования
-
-- Один модуль = один техсервис
-- Импортирует `infrastructure/`, `ui/`, `shared/`
-
-### Слой UI
-
-UI-кит без бизнес-логики: button, carousel, toast, modal.
-
-Слой входит в группу «Ядро». Импортирует `ui/` и `shared/`.
-
-Компоненты строятся друг на друге: `button` использует `icon`, `carousel` использует `button`.
-
-```text
-src/ui/
-├── button/
-├── input/
-├── icon/
-├── carousel/
-├── modal/
-├── toast/
-├── dropdown/
-├── tabs/
-└── tooltip/
-```
-
-Когда количество компонентов затрудняет навигацию — вводится группировка на примитивы и композиции. Примитивы (`button`, `icon`, `input`) не импортируют композиции. Композиции (`carousel`, `modal`, `dropdown`) строятся на примитивах.
-
-```text
-src/ui/
-├── primitives/
-│ ├── button/
-│ ├── input/
-│ ├── icon/
-│ └── badge/
-└── composites/
- ├── carousel/
- ├── modal/
- ├── dropdown/
- ├── tabs/
- └── tooltip/
-```
-
-#### Требования
-
-- Не содержит бизнес-логику
-- Импортирует только `ui/` и `shared/`
-
-### Слой Shared
-
-Общие ресурсы: утилиты, хелперы, стили, конфиги. Не знает о бизнес-домене.
-
-Слой входит в группу «Фундамент» — ни о ком не знает, никого не импортирует.
-
-Отличие от `infrastructure/`: infrastructure — инфраструктура приложения (сервисы, темы, адаптеры к API), `shared/` — общие ресурсы (утилиты, хелперы, стили, конфиги).
-
-Отличие от `ui/`: UI-компоненты (button, carousel, modal) живут в слое `ui/`, а не здесь.
-
-```text
-src/shared/
-├── lib/
-├── types/
-├── styles/
-└── sprites/
-```
-
-#### Требования
-
-- Не имеет runtime-состояния
-
-
-## Модули
-
-Раздел описывает модули SLM: что такое модуль, из чего он состоит и как взаимодействует с остальным кодом.
-
-### Определение
-
-**Модуль — универсальный строительный блок архитектуры. Живёт на слое и содержит всё необходимое для своей работы: компоненты, хуки, сторы, сервисы, типы, стили. Набор содержимого не фиксирован — включаются только нужные части.**
-
-### Модуль vs компонент
-
-**Компонент** — один `.tsx` файл. Не имеет своих сегментов, использует сегменты родительского модуля. Живёт в корне или `ui/` сегменте модуля.
-
-**Модуль** — папка, которая может содержать корневой компонент, сегменты (`hooks/`, `types/`, `styles/`, `ui/`, `parts/` и т.д.) и публичный API (`index.ts`).
-
-```text
-auth/
-├── ui/
-│ ├── auth-guard.tsx
-│ └── logout-button.tsx
-├── parts/
-│ ├── login-form/
-│ ├── registration-form/
-│ └── restore-form/
-├── hooks/
-├── stores/
-├── types/
-├── auth.tsx # корневой компонент (опционален)
-└── index.ts
-```
-
-### Структура
-
-Модуль состоит из сегментов. Ни один сегмент не обязателен — модуль может состоять даже из одного `index.ts` с реэкспортом типов.
-
-```text
-{module-name}/
-├── {module-name}.tsx # корневой компонент (опционален)
-├── ui/ # компоненты модуля (только .tsx)
-├── parts/ # вложенные модули (со своими сегментами)
-├── hooks/ # хуки
-├── stores/ # сторы состояния
-├── services/ # внешние источники данных
-├── mappers/ # трансформация данных между форматами
-├── types/ # типы
-├── styles/ # стили
-├── lib/ # утилиты модуля
-├── config/ # константы
-└── index.ts # публичный API
-```
-
-Подробное описание каждого сегмента — в разделе [Сегменты](/basics/architecture/reference/segments).
-
-### Публичный API
-
-Модуль экспортирует наружу только то, что нужно другим. Всё остальное — внутреннее.
-
-```ts
-// business/auth/index.ts
-export type { User, Session } from './types/user.types'
-export { useAuth } from './hooks/use-auth.hook'
-export { AuthGuard } from './ui/auth-guard'
-```
-
-Импорт в обход `index.ts` запрещён:
-
-```ts
-// Плохо
-import { validateToken } from '@/business/auth/lib/tokens'
-
-// Хорошо
-import { useAuth } from '@/business/auth'
-```
-
-### Фабрика
-
-Если модуль зависит от кода другого бизнес-домена — он экспортирует фабрику. Фабрика декларирует необходимые зависимости и возвращает API модуля. Точка использования (screen, widget, layout) предоставляет зависимости при вызове.
-
-Модуль без cross-domain зависимостей экспортирует API напрямую. Типы всегда экспортируются напрямую — `import type` не является runtime-зависимостью.
-
-#### Модуль без зависимостей — прямой экспорт:
-
-```ts
-// business/auth/index.ts
-export { useAuth } from './hooks/use-auth'
-export { useCurrentUser } from './hooks/use-current-user'
-export type { User, Session } from './types'
-```
-
-#### Модуль с зависимостями — фабрика:
-
-```ts
-// business/chat/types/deps.ts
-import type { User } from '@/business/auth'
-
-export interface ChatDeps {
- useCurrentUser: () => User | null
-}
-```
-
-```ts
-// business/chat/index.ts
-import type { ChatDeps } from './types/deps'
-
-export function chatFactory(deps: ChatDeps) {
- return {
- useMessages: (roomId: string) => {
- const user = deps.useCurrentUser()
- // ...
- },
- useSendMessage: (roomId: string) => {
- const user = deps.useCurrentUser()
- return (text: string) => { /* ... */ }
- },
- useChatRooms: () => {
- const user = deps.useCurrentUser()
- // ...
- },
- ChatBadge: ({ count }: { count: number }) => { /* ... */ },
- }
-}
-
-export type { Message, ChatRoom } from './types'
-export type { ChatDeps } from './types/deps'
-```
-
-#### Использование на странице:
-
-```tsx
-// screens/support/support.tsx
-import { useCurrentUser } from '@/business/auth'
-import { chatFactory } from '@/business/chat'
-
-const chat = chatFactory({ useCurrentUser })
-
-export function SupportScreen() {
- const { useMessages, useSendMessage, ChatBadge } = chat
- const messages = useMessages('support')
- const sendMessage = useSendMessage('support')
-
- return (
-
-
- {messages.map(m => )}
-
-
- )
-}
-```
-
-### Жизненный цикл
-
-Модуль рождается на самом низком уровне использования и поднимается выше только при реальной потребности.
-
-- Нужен на одной странице → `screens/{name}/parts/`
-- Появился в 2+ местах → поднимается по природе:
- - абстрактный UI → `ui/`
- - блок с данными/логикой → `widgets/`
- - представление бизнес-домена → `business/{area}/parts/`
-
-Подъём — обычный рефакторинг в рамках задачи, а не отдельная активность.
-
-
-## Сегменты
-
-Раздел описывает сегменты SLM: что такое сегмент, какие бывают и что в каждом из них лежит.
-
-### Определение
-
-**Сегмент — папка внутри модуля, которая группирует файлы по назначению. Набор сегментов не фиксирован — модуль включает только те, которые ему нужны. Команда сама определяет какие сегменты используются в проекте — архитектура даёт рекомендацию.**
-
-### Обзор
-
-| Сегмент | Содержимое |
-|---------|------------|
-| `ui/` | Компоненты модуля — только `.tsx` файлы |
-| `parts/` | Вложенные модули со своими сегментами |
-| `hooks/` | React-хуки |
-| `stores/` | Сторы состояния |
-| `services/` | Работа с внешними источниками данных |
-| `mappers/` | Трансформация данных между форматами |
-| `types/` | TypeScript-типы и интерфейсы |
-| `styles/` | Стили |
-| `lib/` | Утилиты и хелперы модуля |
-| `config/` | Константы и конфигурация |
-
-### Сегмент ui/
-
-Компоненты, принадлежащие модулю. Содержит только `.tsx` файлы — без своих сегментов, стилей, типов, хуков. Использует сегменты родительского модуля.
-
-```text
-auth/
-├── ui/
-│ ├── auth-provider.tsx
-│ ├── auth-guard.tsx
-│ └── logout-button.tsx
-├── types/
-├── hooks/
-└── index.ts
-```
-
-Если компоненту нужны собственные сегменты — это уже не `ui/`, а `parts/`.
-
-### Сегмент parts/
-
-Вложенные модули со своими сегментами. Каждый элемент `parts/` — полноценный модуль: папка с компонентом, хуками, стилями, типами и т.д.
-
-```text
-home/
-├── parts/
-│ ├── hero-section/
-│ │ ├── hero-section.tsx
-│ │ ├── styles/
-│ │ └── parts/
-│ │ └── top-banner/
-│ │ └── top-banner.tsx
-│ └── features-section/
-│ ├── features-section.tsx
-│ └── hooks/
-├── home.screen.tsx
-└── index.ts
-```
-
-Отличие от `ui/`: элемент `parts/` — модуль со своими сегментами. Элемент `ui/` — компонент, один `.tsx` файл.
-
-Вложенность `parts/` инкапсулирует область разработки горизонтально: каждый разработчик работает в своём `parts/`-модуле, не затрагивая чужие. Это снижает конфликты при параллельной разработке.
-
-Если вложенный модуль обрастает своими `parts/` — это сигнал, что он достаточно самостоятельный для подъёма на уровень выше.
-
-### Сегмент hooks/
-
-React-хуки модуля. Инкапсулируют логику, состояние, подписки, побочные эффекты.
-
-```text
-hooks/
-├── use-auth.hook.ts
-├── use-session.hook.ts
-└── use-permissions.hook.ts
-```
-
-### Сегмент stores/
-
-Сторы состояния модуля. Конкретная реализация зависит от выбранного стейт-менеджера (Zustand, MobX, Redux и т.д.).
-
-```text
-stores/
-├── auth.store.ts
-└── session.store.ts
-```
-
-### Сегмент services/
-
-Работа с внешними источниками данных: API-вызовы, запросы, подписки.
-
-```text
-services/
-├── auth.service.ts
-└── token.service.ts
-```
-
-### Сегмент mappers/
-
-Функции трансформации данных из одного формата в другой: DTO в доменный тип, доменный тип в DTO, доменный тип в ViewModel.
-
-```text
-mappers/
-├── map-user.ts
-├── map-product.ts
-└── map-order-to-dto.ts
-```
-
-### Сегмент types/
-
-TypeScript-типы и интерфейсы модуля. Доменные типы, DTO, пропсы компонентов.
-
-```text
-types/
-├── user.type.ts
-└── session.type.ts
-```
-
-### Сегмент styles/
-
-Стили модуля. Формат зависит от выбранного подхода (CSS Modules, SCSS, CSS-in-JS и т.д.).
-
-```text
-styles/
-├── auth.module.css
-└── login-form.module.css
-```
-
-### Сегмент lib/
-
-Утилиты и хелперы, специфичные для модуля. Чистые функции без побочных эффектов.
-
-```text
-lib/
-├── validate-email.ts
-└── format-phone.ts
-```
-
-Отличие от `shared/lib/`: здесь лежат утилиты, нужные только этому модулю. Общие утилиты — в `shared/lib/`.
-
-### Сегмент config/
-
-Константы и конфигурация модуля: маршруты, лимиты, дефолтные значения.
-
-```text
-config/
-├── routes.ts
-└── constants.ts
-```
-
-
-## Стиль кода
-
-Раздел описывает единые правила оформления кода: отступы, переносы, кавычки, порядок импортов и базовую читаемость.
-
-### Отступы
-
-- 2 пробела (не табы).
-
-### Длина строк
-
-- Ориентироваться на 100 символов, но превышение допустимо, если строка читается легко.
-- Переносить выражение на новые строки, когда строка становится плохо читаемой.
-- Не переносить строку внутри строковых литералов без необходимости.
-
-**Хорошо**
-```ts
-const config = createRequestConfig(
- endpoint,
- {
- headers: {
- 'X-Request-Id': requestId,
- 'X-User-Id': userId,
- },
- params: {
- page,
- pageSize,
- sort: 'createdAt',
- },
- },
- timeoutMs,
-);
-```
-
-**Плохо**
-```ts
-// Плохо: длинная строка с вложенными структурами плохо читается.
-const config = createRequestConfig(endpoint, { headers: { 'X-Request-Id': requestId, 'X-User-Id': userId }, params: { page, pageSize, sort: 'createdAt' } }, timeoutMs);
-```
-
-### Кавычки
-
-- В JavaScript/TypeScript использовать одинарные кавычки.
-- В JSX/TSX для атрибутов использовать двойные кавычки.
-- Шаблонные строки использовать только при интерполяции или многострочном тексте.
-
-**Хорошо**
-```ts
-const label = 'Сохранить';
-const title = `Привет, ${name}`;
-```
-
-```tsx
-
-```
-
-**Плохо**
-```ts
-// Плохо: двойные кавычки в TS и конкатенация вместо шаблонной строки.
-const label = "Сохранить";
-const title = 'Привет, ' + name;
-```
-
-```tsx
-// Плохо: одинарные кавычки в JSX-атрибутах.
-
-```
-
-### Точки с запятой и запятые
-
-- Допускаются упущения точки с запятой, если код остаётся читаемым и однозначным.
-- В многострочных массивах, объектах и параметрах функции запятая в конце допускается, но не обязательна.
-
-### Импорты
-
-- В именованных импортах использовать пробелы внутри фигурных скобок.
-- Типы импортировать через `import type`.
-- `default` экспорт избегать, использовать именованные. `default` импорт допустим (например, стили CSS Modules, сторонние библиотеки).
-- Избегать импорта всего модуля через `*`.
-
-**Хорошо**
-```ts
-import { MyComponent } from 'MyComponent';
-import type { User } from '../model/types';
-import styles from './styles/button.module.css';
-```
-
-**Плохо**
-```ts
-// Плохо: отсутствие пробелов в именованном импорте.
-import type {User} from '../model/types';
-// Плохо: default экспорт.
-export default MyComponent;
-```
-
-### Ранние возвраты (early return)
-
-- Использовать ранние возвраты для упрощения чтения.
-- Избегать `else` после `return`.
-
-**Хорошо**
-```ts
-const getName = (user?: { name: string }) => {
- if (!user) {
- return 'Гость';
- }
-
- return user.name;
-};
-```
-
-**Плохо**
-```ts
-// Плохо: лишний else после return усложняет чтение.
-const getName = (user?: { name: string }) => {
- if (user) {
- return user.name;
- } else {
- return 'Гость';
- }
-};
-```
-
-### Форматирование объектов и массивов
-
-- В многострочных объектах каждое свойство на новой строке.
-- В многострочных массивах каждый элемент на новой строке.
-- Объекты и массивы можно писать в одну строку, если длина строки не превышает 100 символов.
-- В однострочных объектах и массивах использовать пробелы после запятых.
-
-**Хорошо**
-```ts
-const roles = ['admin', 'editor', 'viewer'];
-const options = { id: 1, name: 'User' };
-
-const config = {
- url: '/api/users',
- method: 'GET',
- params: { page: 1, pageSize: 20 },
-};
-```
-
-**Плохо**
-```ts
-// Плохо: нет пробелов после запятых и объект слишком длинный для одной строки.
-const roles = ['admin','editor','viewer'];
-const options = { id: 1,name: 'User' };
-const config = { url: '/api/users', method: 'GET', params: { page: 1, pageSize: 20 } };
-```
-
-
-## Документирование
-
-Этот раздел описывает правила документирования кода: когда и как писать
-комментарии к компонентам, функциям, типам и интерфейсам.
-
-### Общие правила
-
-- Документировать публичные функции, компоненты, типы, интерфейсы и enum.
-- Не документировать очевидное — если название говорит само за себя, комментарий не нужен.
-- Не документировать параметры, возвращаемые значения и типы пропсов — они видны из сигнатуры.
-- Описание через пользу и назначение, а не через внутреннюю реализацию.
-- Описание завершается точкой.
-
-### Функции
-
-Для документирования функций используется шаблон. Описание механики опционально —
-добавляется когда логика нетривиальна.
-
-**Шаблон**
-```ts
-/**
- * <Что делает функция в 1 строке>.
- *
- * <Опционально: описание сложной механики или важных нюансов>.
- */
-```
-
-**Хорошо**
-```ts
-/**
- * Форматирует цену с символом валюты.
- */
-export const formatPrice = (value: number): string => { ... }
-
-/**
- * Рекурсивно собирает дерево категорий из плоского списка.
- *
- * Группирует элементы по parentId, начиная с корневых (parentId = null).
- * Категории без родителя попадают в корень дерева.
- */
-export const buildCategoryTree = (categories: Category[]): CategoryTree[] => { ... }
-```
-
-**Плохо**
-```ts
-// Плохо: дублирует сигнатуру.
-/**
- * @param value - число
- * @returns строка с ценой
- */
-```
-
-### Компоненты
-
-Компонент описывает своё **назначение** и **сценарии применения** — это помогает понять, когда и где его использовать, без необходимости читать реализацию.
-
-**Шаблон**
-```ts
-/**
- * <Назначение компонента в 1 строке>.
- *
- * Используется для:
- * - <сценарий 1>
- * - <сценарий 2>
- * - <сценарий 3>
- */
-```
-
-**Хорошо**
-```tsx
-/**
- * Контейнер с адаптивной максимальной шириной.
- *
- * Используется для:
- * - обёртки контента страниц с ограничением ширины
- * - центрирования блоков в лейауте
- */
-export const Container = (props: ContainerProps) => { ... }
-```
-
-**Плохо**
-```tsx
-// Плохо: описывает реализацию, а не назначение.
-/**
- * Рендерит div с className и htmlAttr.
- */
-
-// Плохо: нет описания вообще.
-export const Container = (props: ContainerProps) => { ... }
-```
-
-### Типы, интерфейсы, enum
-
-Документируются назначение сущности и каждое её поле.
-
-**Хорошо**
-```ts
-/**
- * Фильтры списка задач.
- */
-export enum TodoFilter {
- /** Все задачи. */
- ALL = 'all',
- /** Только активные. */
- ACTIVE = 'active',
- /** Только завершённые. */
- COMPLETED = 'completed',
-}
-
-/**
- * Задача пользователя.
- */
-export interface TodoItem {
- /** Уникальный идентификатор задачи. */
- id: string;
- /** Текст задачи. */
- text: string;
- /** Статус выполнения. */
- completed: boolean;
-}
-```
-
-**Плохо**
-```ts
-// Плохо: описывает очевидное.
-export interface TodoItem {
- /** id — это id */
- id: string;
-}
-```
-
-
-## Типизация
-
-Этот раздел описывает правила типизации: как типизировать компоненты, функции и работу с `any`/`unknown`.
-
-### Общие правила
-
-- Указывать типы для параметров компонентов, возвращаемых значений и параметров функций.
-- Предпочитать `type` для описания сущностей и `interface` для расширяемых контрактов.
-- Избегать `any` и `unknown` без необходимости.
-- Не использовать `ts-ignore`, кроме крайних случаев с явным комментарием причины.
-
-### Функции
-
-- Для публичных функций указывать возвращаемый тип.
-- Не полагаться на неявный вывод для важных API.
-
-**Хорошо**
-```ts
-export const formatPrice = (value: number): string => {
- return `${value} ₽`;
-};
-```
-
-**Плохо**
-```ts
-// Плохо: нет явного возвращаемого типа.
-export const formatPrice = (value: number) => {
- return `${value} ₽`;
-};
-```
-
-### Работа с any/unknown
-
-- `any` использовать только для временных заглушек.
-- `unknown` сужать через проверки перед использованием.
-
-**Хорошо**
-```ts
-const parse = (value: unknown): string => {
- if (typeof value === 'string') {
- return value;
- }
-
- return '';
-};
-```
-
-**Плохо**
-```ts
-// Плохо: any отключает проверку типов.
-const parse = (value: any) => value;
-```
-
-
-## Структура проекта
-
-Раздел описывает расположение файлов и папок в проекте Next.js (App Router).
-
-### Корень репозитория
-
-```text
-project-root/
-├── .templates/ # Шаблоны для генерации модулей
-├── .vscode/ # Настройки и рекомендуемые расширения VS Code
-├── public/ # Статика, доступная по прямому URL
-├── src/ # Исходный код приложения
-├── .env.example # Переменные окружения проекта (шаблон)
-├── .env # Переменные окружения проекта (не коммитить)
-├── .gitignore
-├── AGENTS.md # Инструкции для AI-агентов
-├── biome.json # Линтер и форматтер (вместо ESLint + Prettier)
-├── next.config.ts # Конфигурация Next.js
-├── package.json # Зависимости и скрипты
-├── postcss.config.mjs # Конфигурация PostCSS
-└── tsconfig.json # Конфигурация TypeScript
-```
-
-### Папка `public/`
-
-Хранит статические файлы, которые отдаются по прямому URL без обработки сборщиком:
-
-```text
-public/
-└── og-image.png
-```
-
-Компоненты, стили и другой исходный код здесь не размещаются.
-
-### Папка `src/`
-
-```text
-src/
-├── app/ # Роутинг Next.js, провайдеры, глобальные стили
-├── layouts/ # Каркасы страниц (header, footer, sidebar)
-├── screens/ # Контент конкретной страницы
-├── widgets/ # Составные блоки интерфейса, не привязанные к домену
-├── business/ # Бизнес-домены (auth, catalog, orders)
-├── infrastructure/ # Техсервисы (theme, i18n, API-адаптеры)
-├── ui/ # UI-кит без бизнес-логики
-└── shared/ # Общие ресурсы (утилиты, типы, стили)
-```
-
-Принципы организации слоёв описаны в разделе [Архитектура](../basics/architecture/).
-
-#### Папка `app/`
-
-Точка входа приложения: инициализация (провайдеры, глобальные стили) и файловый роутинг Next.js (`layout.tsx`, `page.tsx`, route-сегменты).
-
-```text
-src/app/
-├── providers/ # Провайдеры приложения
-├── styles/ # Глобальные стили
-├── layout.tsx # Корневой layout
-└── page.tsx # Главная страница
-```
-
-### Папка `.templates/`
-
-Содержит шаблоны для генерации кода. Каждый подкаталог — шаблон отдельного типа модуля:
-
-```text
-.templates/
-├── component/ # Шаблон компонента
-├── screen/ # Шаблон экрана
-├── layout/ # Шаблон layout
-├── widget/ # Шаблон виджета
-├── module/ # Шаблон бизнес-модуля
-└── store/ # Шаблон стора
-```
-
-Подробнее о генерации описано в разделе [Шаблоны и генерация кода](./templates-generation).
-
-### Конфигурационные файлы
-
-| Файл | Назначение |
-|---|---|
-| `next.config.ts` | Настройки Next.js: редиректы, переменные окружения, webpack |
-| `tsconfig.json` | Настройки TypeScript: пути, строгость, таргет |
-| `biome.json` | Правила линтера и форматтера Biome |
-| `postcss.config.mjs` | Подключение PostCSS-плагинов (CSS Modules, custom media) |
-| `package.json` | Зависимости, версии, npm-скрипты |
-| `AGENTS.md` | Инструкции для AI-агентов, работающих в проекте |
-
-### Переменные окружения
-
-- `.env` — переменные окружения проекта, запрещено коммитить
-- `.env.example` — шаблон, коммитится в репозиторий
-
-Переменные с префиксом `NEXT_PUBLIC_` доступны в клиентском коде. Остальные доступны только на сервере.
-
-
-## Компоненты
-
-Правила написания React-компонентов: файловая структура модуля, типизация пропсов, документирование и реализация. Раздел охватывает компоненты всех слоёв — от `shared/ui` до `screens`.
-
-Архитектурные слои и их назначение описаны в разделе [Архитектура](/basics/architecture/).
-
-
-### Правила организации
-
-1. Один компонент — один файл.
-2. Компонент не содержит бизнес-логики — логика и сайд-эффекты выносятся в хуки или сторы.
-3. Дочерние компоненты размещаются в сегменте `ui/` и подчиняются тем же правилам структуры.
-4. Публичный API модуля — только `index.ts`. Прямые импорты внутренних файлов запрещены.
-
-### Базовая структура компонента
-
-Минимальный набор файлов: компонент, стили, типы и публичный экспорт.
-
-```text
-container/
-├── styles/
-│ └── container.module.css
-├── types/
-│ └── container.type.ts
-├── container.tsx
-└── index.ts
-```
-
-### Именования
-
-- Имя корневого css класса всегда `.root`
-- Тип пропсов именуется `{ComponentName}Props`.
-- Тип пользовательских параметров именуется `{ComponentName}Params`.
-
-### Типизация
-
-Структура типов компонента показана в [примере](#пример). Ниже — обоснования ключевых решений.
-
-- **`type` вместо `interface`** — гибче для пропсов: поддерживает union, intersection, mapped types. Declaration merging пропсам не нужно.
-- **Без `FC`** — неявно добавляет `children`, усложняет дженерики, не даёт преимуществ перед аннотацией параметра.
-- **Типы в `types/`, а не в `.tsx`** — предотвращает циклические зависимости (компонент импортирует хук, хук импортирует тип из компонента) и разделяет ответственность: `.tsx` для рендера, `.type.ts` для данных.
-- **Без возвращаемого типа** — TypeScript выводит из JSX. Осознанное исключение из [базового правила](/basics/typing).
-
-### Реализация
-
-- Пропсы деструктурируются в теле компонента, не в параметрах.
-- Порядок: пользовательские → системные (`children`, `className`) → `...htmlAttr`.
-- `className` объединяется с корневым классом через `cl()`: `cl(styles.root, className)`.
-- `...htmlAttr` прокидывается на корневой элемент.
-
-### Пример
-
-`container/types/container.type.ts`
-
-```ts
-import type { HTMLAttributes } from 'react'
-
-/**
- * Параметры компонента Container.
- */
-export type ContainerParams = {}
-
-/** HTML-атрибуты корневого элемента. */
-type RootAttrs = HTMLAttributes
-
-export type ContainerProps = RootAttrs & ContainerParams
-```
-
-`container/styles/container.module.css`
-
-```css
-.root {
- max-width: var(--content-width);
- margin: 0 auto;
- padding: 0 var(--spacing-4);
-}
-```
-
-`container/container.tsx`
-
-```tsx
-import cl from 'clsx'
-import type { ContainerProps } from './types/container.type'
-import styles from './styles/container.module.css'
-
-/**
- * Контейнер с адаптивной максимальной шириной.
- *
- * Используется для:
- * - обёртки контента страниц с ограничением ширины
- * - центрирования блоков в лейауте
- */
-export const Container = (props: ContainerProps) => {
- const { children, className, ...htmlAttr } = props
-
- return (
-
- {children}
-
- )
-}
-```
-
-`container/index.ts`
-
-```ts
-export { Container } from './container'
-```
-
-
-## Файлы роутинга
-
-Правила для специальных файлов App Router (`page.tsx`, `layout.tsx`, `error.tsx`, `not-found.tsx` и др.) — чем наш подход отличается от дефолтного.
-
-### Организация
-
-- `page.tsx` — тонкий файл: только `metadata` и рендер экрана. Логика, стили и зависимости живут в экране, не в `page.tsx`.
-- `error.tsx` и `not-found.tsx` делегируют разметку экранам по тому же принципу.
-- `layout.tsx` — точка подключения провайдеров и глобальных стилей. Вёрстка layout-обёрток выносится в слой `layouts/`.
-- Стили в файлах роутинга не используются — стилизация только внутри вызываемых компонентов.
-
-### Реализация
-
-- Каждый `page.tsx` экспортирует `metadata` с `title` — он подставляется в шаблон корневого layout (`%s | App`).
-- Корневой `layout.tsx` задаёт `metadata` с `title.template`, `description`, `metadataBase` и OpenGraph-настройками.
-
-### Примеры
-
-`src/app/profile/[id]/page.tsx`
-
-```tsx
-import type { Metadata } from 'next'
-import { ProfileScreen } from '@/screens/profile'
-
-export const metadata: Metadata = {
- title: 'Профиль',
- description: 'Страница профиля пользователя',
-}
-
-type ProfilePageProps = {
- params: Promise<{ id: string }>
-}
-
-export default async function ProfilePage({ params }: ProfilePageProps) {
- const { id } = await params
-
- return
-}
-```
-
-`src/app/error.tsx`
-
-```tsx
-'use client'
-
-import { ErrorScreen } from '@/screens/error'
-
-type ErrorPageProps = {
- error: Error & { digest?: string }
- reset: () => void
-}
-
-const ErrorPage = ({ error, reset }: ErrorPageProps) => {
- return
-}
-
-export default ErrorPage
-```
-
-
-
-::: v-pre
-
-## Шаблоны и генерация кода
-
-Как работают шаблоны, как их создавать, синтаксис переменных и как генерировать код с помощью расширения VS Code и CLI.
-
-### Структура шаблонов
-
-Все шаблоны лежат в `.templates/` в корне проекта. Каждая папка — отдельный шаблон.
-
-```text
-.templates/
-├── component/ # шаблон компонента
-│ └── {{name.kebabCase}}/
-│ ├── styles/
-│ │ └── {{name.kebabCase}}.module.css
-│ ├── types/
-│ │ └── {{name.kebabCase}}.type.ts
-│ ├── {{name.kebabCase}}.tsx
-│ └── index.ts
-└── store/ # шаблон Zustand стора
- └── {{name.kebabCase}}/
- ├── {{name.kebabCase}}.store.ts
- ├── {{name.kebabCase}}.type.ts
- └── index.ts
-```
-
-### Синтаксис шаблонов
-
-Переменные работают в именах файлов/папок и внутри файлов. Базовая переменная — `name`.
-
-```text
-{{variable}}
-```
-
-Модификаторы меняют регистр и формат записи:
-
-```text
-{{name.pascalCase}} → MyButton
-{{name.camelCase}} → myButton
-{{name.kebabCase}} → my-button
-{{name.snakeCase}} → my_button
-{{name.screamingSnakeCase}} → MY_BUTTON
-```
-
-### Как создать новый шаблон
-
-1. Создать папку в `.templates/` с именем шаблона (например `hook`).
-2. Внутри разместить файлы и папки, используя `{{name}}` и модификаторы в именах и содержимом.
-3. Шаблон сразу доступен и в расширении VS Code, и в CLI.
-
-Пример — создание шаблона для хука:
-
-```text
-.templates/
-└── hook/
- └── {{name.kebabCase}}/
- ├── {{name.kebabCase}}.hook.ts
- └── index.ts
-```
-
-```ts
-// .templates/hook/{{name.kebabCase}}.hook.ts
-export const {{name.camelCase}} = () => {
-
-}
-```
-
-```ts
-// .templates/hook/index.ts
-export { {{name.camelCase}} } from './{{name.kebabCase}}.hook'
-```
-
-### Примеры шаблонов
-
-#### Шаблон компонента
-
-```ts
-// .templates/component/index.ts
-export { {{name.pascalCase}} } from './{{name.kebabCase}}'
-```
-
-```ts
-// .templates/component/types/{{name.kebabCase}}.type.ts
-import type { HTMLAttributes } from 'react'
-
-/**
- * Параметры {{name.pascalCase}}.
- */
-export type {{name.pascalCase}}Params = {}
-
-/** HTML-атрибуты корневого элемента. */
-type RootAttrs = HTMLAttributes
-
-export type {{name.pascalCase}}Props = RootAttrs & {{name.pascalCase}}Params
-```
-
-```tsx
-// .templates/component/{{name.kebabCase}}.tsx
-import cl from 'clsx'
-import type { {{name.pascalCase}}Props } from './types/{{name.kebabCase}}.type'
-import styles from './styles/{{name.kebabCase}}.module.css'
-
-/**
- * {{name.pascalCase}}.
- */
-export const {{name.pascalCase}} = (props: {{name.pascalCase}}Props) => {
- const { children, className, ...htmlAttr } = props
-
- return (
-
- {children}
-
- )
-}
-```
-
-```css
-/* .templates/component/styles/{{name.kebabCase}}.module.css */
-.root {
-
-}
-```
-
-### Генерация через VS Code
-
-Template File Generator | gromlab ([Marketplace](https://marketplace.visualstudio.com/items?itemName=gromlab.vscode-templateFileGenerator), [Open VSX](https://open-vsx.org/extension/gromlab/vscode-templateFileGenerator)) — расширение для генерации файлов и папок из шаблонов через интерфейс редактора.
-
-1. ПКМ на целевой папке в проводнике VS Code.
-2. **Generate from template** → выбрать шаблон.
-3. Ввести имя (например `button`) — расширение подставит его во все переменные `{{name}}`.
-
-### Генерация через CLI
-
-[@gromlab/create](https://www.npmjs.com/package/@gromlab/create) — CLI для генерации из тех же шаблонов. Используется через npx, глобальная установка не требуется.
-
-```bash
-npx @gromlab/create <шаблон> <имя> <путь>
-```
-
-| Команда | Что создаёт |
-|---|---|
-| `npx @gromlab/create component button src/shared/ui` | Компонент |
-| `npx @gromlab/create module auth src/business` | Бизнес-модуль |
-| `npx @gromlab/create widget header src/widgets` | Виджет |
-| `npx @gromlab/create layout admin src/layouts` | Layout |
-| `npx @gromlab/create store auth src/business/auth/stores` | Стор |
-
-:::
-
-
-## Стили
-
-Раздел описывает правила написания CSS: PostCSS Modules, вложенность, медиа-запросы, переменные, форматирование.
-
-### Общие правила
-
-- Только **PostCSS** и **CSS Modules** для кастомной стилизации.
-- Подход **Mobile First** — стили пишутся от мобильных к десктопу.
-- Именование классов — `camelCase` (`.root`, `.buttonNext`, `.itemTitle`).
-- Модификаторы — отдельный класс с `_`, применяется через `&._modifier`.
-
-**Хорошо**
-```css
-.submitButton {
- padding: 8px 16px;
-
- &._disabled {
- opacity: 0.5;
- }
-}
-```
-
-**Плохо**
-```css
-/* Плохо: kebab-case и вложенный элемент вместо отдельного класса. */
-.submit-button {
- padding: 8px 16px;
-
- &__icon {
- margin-right: 8px;
- }
-}
-```
-
-### Вложенность
-
-- Вложенность селекторов запрещена.
-- Исключения:
- - Псевдоклассы: `&:hover`, `&:active`, `&:focus`, `&:disabled` и т.д.
- - Псевдоэлементы: `&::before`, `&::after`.
- - Медиа-запросы: `@media`.
- - Модификаторы: `&._active`, `&._disabled`.
-- Каждый вложенный блок отделяется пустой строкой от предыдущих свойств.
-
-**Хорошо**
-```css
-.card {
- padding: 16px;
- background-color: var(--color-bg);
-
- &:hover {
- background-color: var(--color-bg-hover);
- }
-
- &::after {
- content: '';
- display: block;
- }
-
- &._highlighted {
- border-color: var(--color-primary);
- }
-
- @media (--md) {
- padding: 24px;
- }
-}
-
-.cardTitle {
- font-size: 16px;
-
- @media (--md) {
- font-size: 20px;
- }
-}
-```
-
-**Плохо**
-```css
-/* Плохо: вложенность селекторов, нет пустых строк между блоками. */
-.card {
- padding: 16px;
- .cardTitle {
- font-size: 16px;
- }
- &:hover {
- background-color: var(--color-bg-hover);
- }
-}
-```
-
-### Медиа-запросы
-
-- Только **Custom Media Queries**: `@media (--md) {}`.
-- Запрещены произвольные breakpoints: `@media (min-width: 768px)`.
-- `@media` пишется только **внутри** селектора.
-- Запрещено писать `@media` на верхнем уровне с селекторами внутри.
-
-**Хорошо**
-```css
-.sidebar {
- display: none;
-
- @media (--md) {
- display: block;
- }
-}
-
-.sidebarTitle {
- font-size: 14px;
-
- @media (--md) {
- font-size: 18px;
- }
-}
-```
-
-**Плохо**
-```css
-/* Плохо: @media на верхнем уровне с селекторами внутри. */
-@media (--md) {
- .sidebar {
- display: block;
- }
-
- .sidebarTitle {
- font-size: 18px;
- }
-}
-
-/* Плохо: произвольный breakpoint вместо custom media. */
-.sidebar {
- @media (min-width: 992px) {
- display: block;
- }
-}
-```
-
-### CSS-переменные
-
-- Цвета (`--color-*`), отступы (`--space-*`), скругления (`--radius-*`) определяются в `app/styles/variables.css` через `:root`.
-- Файл переменных подключается один раз в корневом layout/entry point — после этого переменные доступны глобально через каскад.
-- Не дублировать магические значения в компонентах.
-
-**Хорошо**
-```css
-/* app/styles/variables.css */
-:root {
- --color-primary: #3b82f6;
- --color-bg: #ffffff;
- --color-bg-hover: #f5f5f5;
- --space-1: 4px;
- --space-2: 8px;
- --space-3: 12px;
- --radius-1: 4px;
- --radius-2: 8px;
-}
-```
-
-```css
-/* компонент */
-.card {
- padding: var(--space-3);
- border-radius: var(--radius-2);
- background-color: var(--color-bg);
-}
-```
-
-**Плохо**
-```css
-/* Плохо: магические значения вместо переменных. */
-.card {
- padding: 12px;
- border-radius: 8px;
- background-color: #ffffff;
-}
-```
-
-### Custom Media
-
-- Breakpoints определяются через Custom Media Queries в `app/styles/media.css`.
-- Custom media подключаются глобально через конфиг PostCSS (плагин `postcss-custom-media`) — не импортировать в файлы стилей.
-
-```css
-/* app/styles/media.css */
-@custom-media --sm (min-width: 36em);
-@custom-media --md (min-width: 62em);
-@custom-media --lg (min-width: 82em);
-```
-
-### Импорт стилей
-
-- Стили компонента импортируются только внутри своего компонента.
-- Запрещено импортировать стили одного компонента в другой.
-- Custom media не импортируются в файлы стилей — они подключаются глобально через конфиг PostCSS.
-
-### Форматирование
-
-- Пустая строка между селекторами верхнего уровня.
-- Пустая строка перед каждым вложенным блоком (медиа, псевдокласс, модификатор).
-
-**Хорошо**
-```css
-.userBar {
- display: none;
- color: var(--color-text);
-
- @media (--md) {
- display: flex;
- }
-}
-
-.userBarButton {
- background-color: var(--color-bg);
-
- &:hover {
- background-color: var(--color-bg-hover);
- }
-
- &._active {
- background-color: var(--color-primary);
- }
-}
-```
-
-**Плохо**
-```css
-/* Плохо: нет пустых строк между селекторами и вложенными блоками. */
-.userBar {
- display: none;
- color: var(--color-text);
- @media (--md) {
- display: flex;
- }
-}
-.userBarButton {
- background-color: var(--color-bg);
- &:hover {
- background-color: var(--color-bg-hover);
- }
- &._active {
- background-color: var(--color-primary);
- }
-}
-```
-
-### Единицы измерения
-
-- `px` — основная единица измерения.
-- Остальные (`em`, `rem`, `%`, `vh`/`vw`) — допускаются по необходимости дизайна.
-
-### Порядок CSS-свойств
-
-В стилях рекомендуется придерживаться логического порядка свойств:
-
-1. Позиционирование (`position`, `top`, `left`, `z-index`).
-2. Блочная модель (`display`, `width`, `height`, `margin`, `padding`).
-3. Оформление (`background`, `border`, `box-shadow`, `border-radius`).
-4. Текст (`font`, `color`, `text-align`, `line-height`).
-5. Прочее (`transition`, `animation`, `opacity`, `cursor`).
-
-### Комментарии
-
-- Желательно не писать комментарии в CSS.
-- Исключение — нетривиальные хаки и обходные решения, к которым стоит оставить пояснение.
-
-
-## SVG-спрайты
-
-
-## Настройка VS Code
-
-Каждый проект содержит папку `.vscode/` с конфигурацией редактора. Это гарантирует, что все участники команды работают с одинаковыми настройками форматирования, линтинга и расширениями.
-
-### Структура `.vscode/`
-
-```text
-.vscode/
-├── extensions.json # Рекомендуемые расширения
-└── settings.json # Настройки редактора для проекта
-```
-
-Оба файла коммитятся в репозиторий.
-
-### Расширения
-
-Файл `.vscode/extensions.json` определяет список расширений, которые VS Code предложит установить при открытии проекта.
-
-```json
-// .vscode/extensions.json
-{
- "recommendations": [
- "biomejs.biome",
- "MyTemplateGenerator.mytemplategenerator",
- "csstools.postcss"
- ]
-}
-```
-
-| Расширение | Назначение |
-|---|---|
-| [Biome](https://marketplace.visualstudio.com/items?itemName=biomejs.biome) | Линтинг и форматирование кода. Заменяет ESLint и Prettier |
-| Template File Generator \| gromlab ([Marketplace](https://marketplace.visualstudio.com/items?itemName=gromlab.vscode-templateFileGenerator), [Open VSX](https://open-vsx.org/extension/gromlab/vscode-templateFileGenerator)) | Генерация файлов и папок из шаблонов `.templates/` через контекстное меню |
-| [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss) | Подсветка синтаксиса и автодополнение для PostCSS (`@custom-media`, `@nest` и др.) |
-
-#### Зачем это нужно
-
-- Новый участник команды получает все нужные расширения одним кликом.
-- Нет разночтений: все используют одинаковый форматтер и линтер.
-- Расширения привязаны к проекту, а не к конкретному разработчику.
-
-### Настройки редактора
-
-Файл `.vscode/settings.json` переопределяет пользовательские настройки VS Code на уровне проекта.
-
-```json
-// .vscode/settings.json
-{
- "editor.defaultFormatter": "biomejs.biome",
- "editor.formatOnSave": true,
- "editor.codeActionsOnSave": {
- "source.fixAll.biome": "explicit",
- "source.organizeImports.biome": "explicit"
- },
- "files.associations": {
- "*.css": "postcss"
- }
-}
-```
-
-#### Разбор настроек
-
-| Настройка | Значение | Что делает |
-|---|---|---|
-| `editor.defaultFormatter` | `biomejs.biome` | Biome используется как единственный форматтер для всех файлов |
-| `editor.formatOnSave` | `true` | Код автоматически форматируется при каждом сохранении |
-| `codeActionsOnSave.source.fixAll.biome` | `explicit` | Biome автоматически применяет безопасные исправления при сохранении |
-| `codeActionsOnSave.source.organizeImports.biome` | `explicit` | Импорты сортируются и группируются автоматически при сохранении |
-| `files.associations` | `"*.css": "postcss"` | Все CSS-файлы открываются с подсветкой PostCSS вместо стандартного CSS |
-
-#### Зачем это нужно
-
-- **Единый стиль кода** -- форматирование происходит автоматически, невозможно закоммитить неформатированный код.
-- **Автофикс при сохранении** -- распространённые ошибки линтинга исправляются без ручного вмешательства.
-- **Сортировка импортов** -- импорты всегда в одном порядке, без конфликтов при мерже.
-- **PostCSS-подсветка** -- кастомные at-правила (`@custom-media`, `@define-mixin`) подсвечиваются корректно, а не как ошибки.
-
-### Что не должно быть в `.vscode/`
-
-Не коммитятся файлы, специфичные для конкретного разработчика:
-
-- **Не коммитить**: отладочные конфигурации с локальными путями, персональные сниппеты, настройки тем оформления.
-- **Коммитить**: только `extensions.json` и `settings.json` с общими для команды настройками.
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index d6047c8..ac12932 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"name": "nextjs-style-guide",
"version": "0.0.0",
"devDependencies": {
+ "tsx": "^4.19.2",
"vitepress": "^1.6.3"
}
},
@@ -643,6 +644,23 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/netbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
@@ -660,6 +678,23 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/openbsd-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
@@ -677,6 +712,23 @@
"node": ">=12"
}
},
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
+ "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@esbuild/sunos-x64": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
@@ -1659,6 +1711,19 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
+ "node_modules/get-tsconfig": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz",
+ "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
"node_modules/hast-util-to-html": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz",
@@ -2088,6 +2153,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
@@ -2248,6 +2323,459 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/tsx": {
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
+ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.27.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
+ "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
+ "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
+ "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/android-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
+ "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
+ "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
+ "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
+ "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
+ "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
+ "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
+ "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
+ "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
+ "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
+ "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
+ "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
+ "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
+ "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/linux-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
+ "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
+ "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
+ "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
+ "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
+ "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/@esbuild/win32-x64": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
+ "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/tsx/node_modules/esbuild": {
+ "version": "0.27.7",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
+ "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.7",
+ "@esbuild/android-arm": "0.27.7",
+ "@esbuild/android-arm64": "0.27.7",
+ "@esbuild/android-x64": "0.27.7",
+ "@esbuild/darwin-arm64": "0.27.7",
+ "@esbuild/darwin-x64": "0.27.7",
+ "@esbuild/freebsd-arm64": "0.27.7",
+ "@esbuild/freebsd-x64": "0.27.7",
+ "@esbuild/linux-arm": "0.27.7",
+ "@esbuild/linux-arm64": "0.27.7",
+ "@esbuild/linux-ia32": "0.27.7",
+ "@esbuild/linux-loong64": "0.27.7",
+ "@esbuild/linux-mips64el": "0.27.7",
+ "@esbuild/linux-ppc64": "0.27.7",
+ "@esbuild/linux-riscv64": "0.27.7",
+ "@esbuild/linux-s390x": "0.27.7",
+ "@esbuild/linux-x64": "0.27.7",
+ "@esbuild/netbsd-arm64": "0.27.7",
+ "@esbuild/netbsd-x64": "0.27.7",
+ "@esbuild/openbsd-arm64": "0.27.7",
+ "@esbuild/openbsd-x64": "0.27.7",
+ "@esbuild/openharmony-arm64": "0.27.7",
+ "@esbuild/sunos-x64": "0.27.7",
+ "@esbuild/win32-arm64": "0.27.7",
+ "@esbuild/win32-ia32": "0.27.7",
+ "@esbuild/win32-x64": "0.27.7"
+ }
+ },
"node_modules/unist-util-position": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
diff --git a/package.json b/package.json
index 5e00454..c7ffd0a 100644
--- a/package.json
+++ b/package.json
@@ -4,13 +4,14 @@
"type": "module",
"version": "0.0.0",
"scripts": {
- "docs": "node ./concat-md.js",
+ "llms": "tsx ./generate-llms.ts",
"dev": "vitepress dev .",
"build": "vitepress build .",
"serve": "vitepress serve ."
},
"dependencies": {},
"devDependencies": {
+ "tsx": "^4.19.2",
"vitepress": "^1.6.3"
}
}