diff --git a/.gitignore b/.gitignore index 09957d3..57f6b28 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ dist/ public/ test/public/ .tmp/ +skills/artifacts/ *.generated.ts *.tgz .DS_Store diff --git a/README.md b/README.md index 055a165..63251c7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ A CLI for generating SVG sprites and typed icon components for React and Next.js - [🇬🇧 Download the English skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) - [🇷🇺 Download the Russian skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip) -- [Verify downloads with SHA-256 checksums](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/SHA256SUMS) ## Navigation diff --git a/README_RU.md b/README_RU.md index adbe8bf..20f72f7 100644 --- a/README_RU.md +++ b/README_RU.md @@ -12,7 +12,6 @@ CLI для генерации SVG-спрайтов и типизированны - [🇬🇧 Скачать английский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) - [🇷🇺 Скачать русский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip) -- [Проверить SHA-256-суммы файлов](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/SHA256SUMS) ## Навигация diff --git a/docs/en/react-vite.md b/docs/en/react-vite.md index 1e37cc4..9589037 100644 --- a/docs/en/react-vite.md +++ b/docs/en/react-vite.md @@ -83,7 +83,7 @@ TypeScript checks the `icon` value against the file names: // TypeScript error ``` -Types, display methods, and color controls are described in the [main documentation](../../README.md#display-methods). +Types, rendering methods, and color controls are described in the [main documentation](../../README.md#rendering-methods). Vite emits the sprite as a separate file named like `assets/sprite-.svg`. SVG path data is not included in JavaScript. diff --git a/docs/en/react-webpack.md b/docs/en/react-webpack.md index 15383db..1198ba5 100644 --- a/docs/en/react-webpack.md +++ b/docs/en/react-webpack.md @@ -81,7 +81,7 @@ TypeScript checks the `icon` value against the file names: // TypeScript error ``` -Types, display methods, and color controls are described in the [main documentation](../../README.md#display-methods). +Types, rendering methods, and color controls are described in the [main documentation](../../README.md#rendering-methods). Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` through Asset Modules and emits a separate SVG asset. diff --git a/skills/README.md b/skills/README.md index 1b8676e..42d3a6b 100644 --- a/skills/README.md +++ b/skills/README.md @@ -1,12 +1,48 @@ # AI skills -Исходники скилов `svg-sprites` и `svg-sprites-ru` находятся в `skills/svg-sprites/`. Готовые самодостаточные артефакты записываются в `skills/artifacts/svg-sprites/` и `skills/artifacts/svg-sprites-ru/` и коммитятся в Git. +Исходники английского и русского скиллов находятся в `skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow. -Основные `README.md`, `README_RU.md` и файлы `docs/{en,ru}/*.md` являются источником истины. Сборка копирует их в `references/` готового скила, поэтому вручную редактировать файлы внутри `skills/artifacts/` нельзя. +Обе языковые версии имеют одинаковую структуру: + +```text +src// +├── SKILL.md +├── core/ +│ ├── 00-package-overview.md +│ ├── 10-mode-selection.md +│ ├── 20-project-inspection.md +│ ├── 30-react-next-setup.md +│ ├── 40-generated-contract.md +│ ├── 50-usage-and-colors.md +│ ├── 60-verification.md +│ └── 70-diagnostics.md +└── references/ + ├── react-vite.md + ├── react-webpack.md + ├── next-app.md + ├── next-pages.md + ├── legacy.md + ├── migration-1.md + ├── programmatic-api.md + └── complex-svg.md +``` + +`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. `references/` содержит самостоятельные инструкции для агента по конкретным стекам и редким сценариям. Пользовательские `README*.md` и `docs/{en,ru}/*.md` дополнительно копируются в `references/upstream/` как вторичный источник полного публичного API. + +## Композиция Markdown + +В любой собираемый документ можно включать фрагменты: + +```md + +``` + +Include раскрываются рекурсивно, путь считается относительно включающего файла. Циклы, отсутствующие файлы, выход за `skills/svg-sprites/`, frontmatter во фрагментах и нераскрытые include завершают сборку ошибкой. Заголовки не сдвигаются автоматически: entry содержит единственный `# H1`, inline-фрагменты начинаются с `##`. + +## Локальная сборка ```bash npm run build:skill -npm run check:skill ``` -`build:skill` обновляет оба артефакта, а `check:skill` без изменения файлов проверяет их содержимое и синхронность с документацией. Версия без суффикса использует английский язык, версия с суффиксом `-ru` — русский. +Команда собирает и валидирует обе языковые версии, затем записывает их в игнорируемый каталог `skills/artifacts/`. Сборщик проверяет точный список файлов, безопасные пути, symlink, Markdown fences, локальные ссылки и anchors, единственный H1, frontmatter, размер основного документа и отсутствие `TODO`. diff --git a/skills/artifacts/svg-sprites-ru/SKILL.md b/skills/artifacts/svg-sprites-ru/SKILL.md deleted file mode 100644 index b5ff569..0000000 --- a/skills/artifacts/svg-sprites-ru/SKILL.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -name: svg-sprites-ru -description: "Используй при настройке, генерации, миграции или диагностике SVG-спрайтов через @gromlab/svg-sprites. Триггеры: SVG sprite, SVG-спрайт, svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, icon=\"...\", --icon-color-N, generated-компонент или иконка не появилась в превью и автодополнении. НЕ используй для favicon, растровых изображений, icon fonts, выбора набора иконок или inline SVG без спрайтов." ---- - - - -# SVG Sprites - -## Назначение - -Используй этот скил для работы с `@gromlab/svg-sprites`: первичной настройки, добавления и переиспользования иконок, генерации React-компонентов, подключения `SpriteViewer`, миграции legacy-конфигурации и диагностики ошибок. - -Не навязывай проекту конкретную архитектуру каталогов. Сначала изучи существующие `package.json`, конфигурацию спрайта, используемый фреймворк, роутер и сборщик. - -## Рабочий алгоритм - -1. Определи существующий режим и не смешивай его API с другим режимом. -2. Для React выбери `react@vite` или `react@webpack` и открой соответствующий reference. -3. Для Next.js определи App Router или Pages Router, затем Turbopack или Webpack, и открой соответствующий reference. -4. Для существующего `svg-sprites.config.ts` с несколькими спрайтами используй legacy-документацию. Не мигрируй такой проект без явного запроса. -5. Изучи локальные scripts и добавляй генерацию перед `dev`, `build` и `typecheck`, если generated-файлы не хранятся в Git. -6. После изменения конфигурации или SVG запусти генерацию, затем доступную проверку типов или сборку проекта. - -## Правила React и Next.js - -- Используй локальный `svg-sprite.config.ts` и подходящий config helper: `defineReactSpriteConfig` или `defineNextSpriteConfig`. -- Не редактируй `generated/`, `index.ts`, `manifest.ts` и созданный генератором `.gitignore` вручную. -- Имена исходных SVG становятся допустимыми значениями prop `icon`; используй generated-компонент и его публичные типы вместо deep imports. -- Объединяй локальную папку и `inputFiles`, когда общая иконка нужна нескольким спрайтам. Не создавай копии одного SVG без необходимости. -- В Next.js generated-компонент можно использовать в Server Components, SSR и SSG. Не добавляй `'use client'` только ради иконки. -- Спрайт должен оставаться внешним asset сборщика: не переносить SVG path-данные в JavaScript и не класть generated-файл вручную в `public`. - -## Цвета и трансформации - -- По умолчанию генератор удаляет `width` и `height`, заменяет поддерживаемые `fill` и `stroke` на CSS-переменные и добавляет transitions. -- Для монохромной иконки сначала управляй `color`; для многоцветной используй `--icon-color-N`. -- Не обещай автоматическую замену цветов внутри внешних stylesheets, gradients, patterns, filters и значений `url(#...)` без проверки результата. -- CSS-переменные страницы работают при ``, но не проникают внутрь `` и `background-image`. - -## Превью - -Для React и Next.js подключай `` отдельной debug-страницей приложения. Передай ему manifests или lazy loaders спрайтов. Viewer поддерживает поиск, светлую и тёмную темы, настройку цветов и примеры React, SVG, IMG и CSS. - -`SpriteViewer` является клиентским debug-инструментом и импортируется из `@gromlab/svg-sprites/react`; production-компоненты иконок от него не зависят. - -## Диагностика - -- Если имя иконки отсутствует в автодополнении, проверь входную папку и `inputFiles`, затем перезапусти генерацию. -- Если два файла имеют одинаковое имя иконки, устрани конфликт вместо выбора одного файла неявно. -- Если генератор отказывается перезаписывать файл, не удаляй защитный marker и не обходи writer: перенеси пользовательский файл или выбери другой каталог спрайта. -- Если asset не загружается, сначала проверь соответствие CLI mode реальному сборщику проекта и обработку generated SVG его asset pipeline. -- Если проект использует старый API, сверь установленную версию пакета и legacy reference перед изменениями. - -## References - -- [Основная документация и API](./references/README_RU.md) -- [React + Vite](./references/docs/ru/react-vite.md) -- [React + Webpack 5](./references/docs/ru/react-webpack.md) -- [Next.js App Router](./references/docs/ru/next-app.md) -- [Next.js Pages Router](./references/docs/ru/next-pages.md) -- [Legacy mode](./references/docs/ru/legacy.md) -- [Миграция с 0.1.x](./references/docs/ru/migration-1.md) -- [Программный API](./references/docs/ru/programmatic-api.md) diff --git a/skills/artifacts/svg-sprites-ru/references/README.md b/skills/artifacts/svg-sprites-ru/references/README.md deleted file mode 100644 index 055a165..0000000 --- a/skills/artifacts/svg-sprites-ru/references/README.md +++ /dev/null @@ -1,405 +0,0 @@ -# @gromlab/svg-sprites - -🇬🇧 English | [🇷🇺 Русский](README_RU.md) - -![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) - -A CLI for generating SVG sprites and typed icon components for React and Next.js. - -![Preview](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png) - -## AI Skills - -- [🇬🇧 Download the English skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) -- [🇷🇺 Download the Russian skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip) -- [Verify downloads with SHA-256 checksums](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/SHA256SUMS) - -## Navigation - -- [AI Skills](#ai-skills) -- [Features](#features) -- [Support matrix](#support-matrix) -- [Requirements](#requirements) -- [Quick start](#quick-start) - - [React + Vite](docs/en/react-vite.md) - - [React + Webpack 5](docs/en/react-webpack.md) - - [Next.js App Router](docs/en/next-app.md) - - [Next.js Pages Router](docs/en/next-pages.md) -- [Configuration](#configuration) - - [React](#react) - - [Next.js](#nextjs) -- [Multiple sprites](#multiple-sprites) -- [TypeScript](#typescript) -- [Sprite formats](#sprite-formats) -- [Rendering methods](#rendering-methods) -- [Transformations](#transformations) -- [Icon color management](#icon-color-management) -- [Caching](#caching) -- [SpriteViewer](#spriteviewer) -- [Migrating from 0.1.x](docs/en/migration-1.md) -- [Documentation](#documentation) - -## Features - -- **AI-agent friendly** - the repository includes a ready-to-use skill with up-to-date documentation for configuring, migrating, and troubleshooting `@gromlab/svg-sprites`. -- **TypeScript-friendly** - typed React components, union types, and runtime lists of available icons. -- **Clean generation** - generated files are automatically excluded from Git, the sprite does not need to be placed in `public` manually, and the generator updates only files it owns. -- **Shared icons without copying** - SVGs from the local folder and `inputFiles` are merged into a single sprite; one file can be used in multiple sprites. -- **Built-in interactive preview** - `` is integrated as an application page and displays the provided React and Next.js sprites with search, color controls, and usage examples. -- **Configurable SVG transformations** - remove `width` and `height` while preserving `viewBox`, replace source colors with CSS variables, and add transitions for `fill` and `stroke`. -- **Separate cacheable SVG asset** - SVG path data does not end up in JavaScript chunks, and the bundler emits a file with a content hash. -- **Multiple sprites** - independent React and Next.js modules with their own components, types, and SVG assets. -- **Server-first Next.js** - generated components work in Server Components, SSR, and SSG without the `'use client'` directive. -- **Formats for different use cases** - React and Next.js use `stack`; legacy mode also supports `symbol` for existing integrations. - -## Support matrix - -| Environment | API mode key | Status | -|---|---|---| -| React + Vite | `react@vite` | Ready | -| React + Webpack 5 | `react@webpack` | Ready | -| Next.js 16.2+ App Router + Turbopack | `next@app/turbopack` | Ready | -| Next.js 13.4+ App Router + Webpack 5 | `next@app/webpack` | Ready | -| Next.js 16.2+ Pages Router + Turbopack | `next@pages/turbopack` | Ready | -| Next.js 12.2+ Pages Router + Webpack 5 | `next@pages/webpack` | Ready | -| Vue | - | Coming soon | -| Standalone | - | Coming soon | - -## Requirements - -- Node.js 18 or newer; -- the package is distributed as ESM only and is loaded via `import`; -- React 18 or 19 is required only for generated components and the `@gromlab/svg-sprites/react` entry point; -- for subpath export typings, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -## Quick start - -For a quick start, follow the guide for your stack: - -- [React + Vite](docs/en/react-vite.md) -- [React + Webpack 5](docs/en/react-webpack.md) -- [Next.js App Router](docs/en/next-app.md) -- [Next.js Pages Router](docs/en/next-pages.md) - -## Configuration - -### React - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -| Option | Type | Default | Purpose | -|---|---|---|---| -| `name` | `string` | Folder name | Name of the sprite, component, and public types | -| `description` | `string` | None | Description for types and the debug manifest | -| `inputFolder` | `string` | `./icons` | Folder containing source SVGs, relative to the config | -| `inputFiles` | `string[]` | `[]` | Additional SVG files, relative to the config | -| `transform` | `TransformOptions` | All enabled | [Transformation settings](#transformations) for source SVGs | -| `generatedNotice` | `boolean` | `true` | Full or short warning in generated files | - -`inputFolder` and `inputFiles` are merged into a single sprite, so one SVG file can be used in multiple sprites without copying. If the implicit `./icons` folder does not exist but `inputFiles` is populated, generation continues using only the list. An explicitly specified missing folder is an error. Duplicate paths are deduplicated, while different files with the same icon name are treated as an error. - -`name` is stored in kebab-case and must start with a Latin letter. The React and Next.js presets produce the `stack` format. - -### Next.js - -Next.js uses the same `svg-sprite.config.ts` and set of options. For type checking, you can use a dedicated helper: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', -}) -``` - -The router and bundler are selected through the mode key, so switching between Turbopack and Webpack is always explicitly reflected in the generation command. - -## Multiple sprites - -An application can contain several independent sprites for different scopes: - -**Problem:** one global sprite loads icons that the current screen does not need. - -**Solution:** keep shared icons globally, and place icon sets for pages and large components in separate sprites that load alongside them. - -```text -global -> GlobalIcon -> shared application icons -analytics-page -> AnalyticsPageIcon -> icons for a specific page -file-manager -> FileManagerIcon -> icons for a large component -``` - -- **Global sprite** contains a small set of shared icons used in different parts of the application: navigation, states, and basic actions. -- **Page sprite** loads with a specific section and does not increase the shared sprite with icons that are not needed anywhere else. -- **Large component sprite** encapsulates the icon set of a complex UI module, such as a file manager or editor. - -Each group gets: - -- its own SVG asset; -- its own typed component; -- a separate list of icon names; -- a separate debug manifest; -- an independent cache lifecycle. - - -## TypeScript - -The main feature of the TypeScript API is icon name autocomplete directly in the `icon` prop: - -```tsx - -// ^ the editor suggests every icon in the sprite -``` - -SVG file names become valid `icon` values. A typo or unknown name immediately becomes a TypeScript error: - -```tsx - // TypeScript error -``` - -For programmatic access, the generated module exports a readonly array of all icons available in a specific sprite: - -```ts -import { fileManagerIconNames } from './svg-sprite' - -// readonly ['check', 'folder', ...] -``` - -You can use this list in custom catalogs, select components, tests, and other runtime scenarios. The `FileManagerIconName` union type is also derived from it. - -File names containing spaces and other characters unsafe for SVG IDs remain part of the public TypeScript API. For the internal ``, the generator creates a stable hash ID. - -```text -folder open.svg -> icon="folder open" -> id="icon-" -``` - -For such names, use the generated component or the `id` from the debug manifest. The manual examples below using `#` are suitable only for names that are already safe SVG IDs. - -## Sprite formats - -`stack` is the more modern format, so it is used by default. Icons can be rendered through ``, ``, and CSS `background-image`. - -`symbol` is retained for compatibility with existing integrations and supports rendering only through ``. - -## Rendering methods - -### React component - recommended - -The generated component provides type safety and icon name autocomplete, and constructs the SVG asset URL itself. - -```tsx - -``` - -Monochrome and multicolor icons are supported through `color` and `--icon-color-N`. - -### Manually with `` - -A good low-level method that provides full control over dimensions and colors. This is exactly what the React component uses under the hood. - -How you obtain `spriteUrl` depends on the bundler. - -**Vite:** - -```tsx -import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline' -``` - -**Webpack 5:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -**Next.js with Webpack 5 or Turbopack:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -After obtaining the URL, the icon is rendered the same way: - -```tsx - - - -``` - -Vite, Webpack 5, and Next.js replace the source path with the final hashed asset URL automatically. - -### With `` - less efficient - -```tsx -Done -``` - -The SVG loads as an isolated image: its colors cannot be changed through `color` or `--icon-color-N`. - -### With CSS `background-image` - less efficient - -```css -.icon { - background: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -Like ``, this method does not allow you to control internal SVG colors. The path is specified relative to the CSS file, and Vite/Webpack replaces it with the final hashed URL during the build. - -### With CSS mask - less efficient - -```css -.icon { - background-color: currentColor; - mask: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -A mask retains only the silhouette and colors it with a single color. The original colors, gradients, and distinctions between `fill` and `stroke` are lost. - -## Transformations - -All transformations are enabled by default and configured independently through `transform`. - -| Option | Default | What it does | -|---|---|---| -| `removeSize` | `true` | Removes `width` and `height` from the root `` while preserving the existing `viewBox`. The icon size is then set externally. | -| `replaceColors` | `true` | Replaces `fill` and `stroke` colors with `--icon-color-N`. For a monochrome icon, the fallback becomes `currentColor`; for a multicolor icon, the original colors are preserved. | -| `addTransition` | `true` | Adds `style="transition:fill 0.3s,stroke 0.3s;"` directly to colored SVG elements. An existing `transition` is not overwritten. | - -To disable a transformation, pass `false` for the corresponding option. For more details about the result of `replaceColors`, see [Icon color management](#icon-color-management). - -## Icon color management - -When color replacement is enabled, the generator analyzes `fill` and `stroke` and converts them to CSS custom properties. - -### Monochrome icons - -If one color is found, the fallback is replaced with `currentColor`: - -```svg -stroke="var(--icon-color-1, currentColor)" -``` - -The color is controlled by the CSS `color` property of the outer `` or its parent. - -### Multicolor icons - -Each unique color gets a separate variable with the original fallback: - -```svg -fill="var(--icon-color-1, #798198)" -fill="var(--icon-color-2, #ffffff)" -fill="var(--icon-color-3, #129d9d)" -``` - -The page can override only the required colors: - -```css -.icon { - --icon-color-1: #4b5563; - --icon-color-3: #14b8a6; -} -``` - -### Color limitations - -- `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced; -- colors in `fill`, `stroke`, and inline `style` attributes are handled most reliably; -- CSS classes and external stylesheets inside the source SVG are not the primary transformation use case; -- gradients, patterns, filters, and `url(#...)` values require separate verification and may be incompatible with automatic color replacement; -- page CSS variables are available with ``, but are not available inside `` and `background-image`. - -## Caching - -The Vite, Webpack, and Next.js targets emit the sprite as a separate asset with a content hash: - -```text -/assets/sprite-.svg -``` - -This provides the following properties: - -- the SVG is cached independently of JavaScript; -- changes to React code do not alter the sprite contents; -- icon changes produce a new hashed asset; -- one file is used by every instance of the generated component; -- SVG path data is absent from JavaScript chunks. - -The Vite target prevents inlining through `?no-inline`. The Webpack 5 target uses Asset Modules through `new URL(..., import.meta.url)`. - -## SpriteViewer - -`SpriteViewer` is a React component for viewing generated sprites inside an application's debug route. - -It uses separate manifests and displays: - -- sprite groups; -- the icon list and count; -- search and the system light/dark theme; -- a preview modal with the `viewBox` and color variable controls; -- React, SVG, IMG, and CSS examples with code copying. - -Production components do not import debug manifests. How you integrate the Viewer depends on the bundler: - -- [React + Vite: automatic `import.meta.glob`](docs/en/react-vite.md#6-add-a-debug-page); -- [React + Webpack 5: static `import()`](docs/en/react-webpack.md#6-add-a-debug-page); -- [Next.js App Router](docs/en/next-app.md#5-add-spriteviewer); -- [Next.js Pages Router](docs/en/next-pages.md#5-add-spriteviewer). - -The Viewer is imported from the separate `@gromlab/svg-sprites/react` client entry point and is not included in production icon components. - -### Viewer theme - -By default, `colorTheme="auto"`: the Viewer follows `prefers-color-scheme` and responds to system theme changes. The application theme can be passed explicitly: - -```tsx - -``` - -Valid `colorTheme` values are `auto`, `light`, and `dark`. When the theme is controlled externally, the built-in switch is hidden. To keep it and update the application theme through the Viewer, pass a callback: - -```tsx - -``` - -## Documentation - -- [React + Vite](docs/en/react-vite.md) -- [React + Webpack 5](docs/en/react-webpack.md) -- [Next.js App Router](docs/en/next-app.md) -- [Next.js Pages Router](docs/en/next-pages.md) -- [Legacy mode](docs/en/legacy.md) -- [Migrating from 0.1.x](docs/en/migration-1.md) -- [Programmatic API](docs/en/programmatic-api.md) - -## License - -MIT diff --git a/skills/artifacts/svg-sprites-ru/references/README_RU.md b/skills/artifacts/svg-sprites-ru/references/README_RU.md deleted file mode 100644 index adbe8bf..0000000 --- a/skills/artifacts/svg-sprites-ru/references/README_RU.md +++ /dev/null @@ -1,405 +0,0 @@ -# @gromlab/svg-sprites - -[🇬🇧 English](README.md) | 🇷🇺 Русский - -![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) - -CLI для генерации SVG-спрайтов и типизированных компонентов иконок для React и Next.js. - -![Preview](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png) - -## AI-скиллы - -- [🇬🇧 Скачать английский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) -- [🇷🇺 Скачать русский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip) -- [Проверить SHA-256-суммы файлов](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/SHA256SUMS) - -## Навигация - -- [AI-скиллы](#ai-скиллы) -- [Возможности](#возможности) -- [Таблица поддержки](#таблица-поддержки) -- [Требования](#требования) -- [Быстрый старт](#быстрый-старт) - - [React + Vite](docs/ru/react-vite.md) - - [React + Webpack 5](docs/ru/react-webpack.md) - - [Next.js App Router](docs/ru/next-app.md) - - [Next.js Pages Router](docs/ru/next-pages.md) -- [Конфигурация](#конфигурация) - - [React](#react) - - [Next.js](#nextjs) -- [Множественные спрайты](#множественные-спрайты) -- [TypeScript](#typescript) -- [Форматы спрайтов](#форматы-спрайтов) -- [Способы отображения](#способы-отображения) -- [Трансформации](#трансформации) -- [Управление цветом иконок](#управление-цветом-иконок) -- [Кеширование](#кеширование) -- [SpriteViewer](#spriteviewer) -- [Миграция с 0.1.x](docs/ru/migration-1.md) -- [Документация](#документация) - -## Возможности - -- **AI-agent friendly** — репозиторий содержит готовый skill с актуальной документацией для настройки, миграции и диагностики `@gromlab/svg-sprites`. -- **TypeScript-friendly** — типизированные React-компоненты, union-типы и runtime-списки доступных иконок. -- **Чистая генерация** — generated-файлы автоматически исключаются из Git, спрайт не нужно вручную размещать в `public`, а генератор обновляет только принадлежащие ему файлы. -- **Общие иконки без копирования** — SVG из локальной папки и `inputFiles` объединяются в один спрайт; один файл можно использовать в нескольких спрайтах. -- **Встроенное интерактивное превью** — `` подключается как страница приложения и показывает переданные React- и Next.js-спрайты с поиском, настройкой цветов и примерами использования. -- **Настраиваемые трансформации SVG** — удаление `width` и `height` с сохранением `viewBox`, замена исходных цветов на CSS-переменные и transitions для `fill` и `stroke`. -- **Отдельный кешируемый SVG asset** — SVG path-данные не попадают в JavaScript chunks, а сборщик выпускает файл с content hash. -- **Множественные спрайты** — независимые React- и Next.js-модули со своими компонентами, типами и SVG assets. -- **Server-first Next.js** — generated-компоненты работают в Server Components, SSR и SSG без директивы `'use client'`. -- **Форматы под разные сценарии** — React и Next.js используют `stack`, legacy-режим также поддерживает `symbol` для существующих интеграций. - -## Таблица поддержки - -| Среда | Ключ мода API | Статус | -|---|---|---| -| React + Vite | `react@vite` | Готово | -| React + Webpack 5 | `react@webpack` | Готово | -| Next.js 16.2+ App Router + Turbopack | `next@app/turbopack` | Готово | -| Next.js 13.4+ App Router + Webpack 5 | `next@app/webpack` | Готово | -| Next.js 16.2+ Pages Router + Turbopack | `next@pages/turbopack` | Готово | -| Next.js 12.2+ Pages Router + Webpack 5 | `next@pages/webpack` | Готово | -| Vue | — | Скоро | -| Standalone | — | Скоро | - -## Требования - -- Node.js 18 или новее; -- пакет распространяется только как ESM и подключается через `import`; -- React 18 или 19 требуется только для generated-компонентов и точки входа `@gromlab/svg-sprites/react`; -- для типизации subpath exports используйте TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -## Быстрый старт - -Для быстрого старта воспользуйтесь инструкцией для вашего стека: - -- [React + Vite](docs/ru/react-vite.md) -- [React + Webpack 5](docs/ru/react-webpack.md) -- [Next.js App Router](docs/ru/next-app.md) -- [Next.js Pages Router](docs/ru/next-pages.md) - -## Конфигурация - -### React - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -| Опция | Тип | По умолчанию | Назначение | -|---|---|---|---| -| `name` | `string` | Имя папки | Имя спрайта, компонента и публичных типов | -| `description` | `string` | Нет | Описание для типов и debug-манифеста | -| `inputFolder` | `string` | `./icons` | Папка с исходными SVG относительно конфига | -| `inputFiles` | `string[]` | `[]` | Дополнительные SVG-файлы относительно конфига | -| `transform` | `TransformOptions` | Все включены | [Настройки трансформации](#трансформации) исходных SVG | -| `generatedNotice` | `boolean` | `true` | Полное либо короткое предупреждение в generated-файлах | - -`inputFolder` и `inputFiles` объединяются в один спрайт, поэтому один SVG-файл можно использовать в нескольких спрайтах без копирования. Если неявной папки `./icons` нет, но `inputFiles` заполнен, генерация продолжается только по списку. Явно указанная отсутствующая папка считается ошибкой. Одинаковые пути дедуплицируются, а разные файлы с одинаковым именем иконки считаются ошибкой. - -`name` записывается в kebab-case и должно начинаться с латинской буквы. React и Next.js presets создают формат `stack`. - -### Next.js - -Next.js использует тот же `svg-sprite.config.ts` и набор опций. Для типизации можно использовать отдельный хелпер: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', -}) -``` - -Роутер и сборщик выбираются через mode key, поэтому переключение между Turbopack и Webpack всегда явно отражено в команде генерации. - -## Множественные спрайты - -Приложение может содержать несколько независимых спрайтов с разной областью использования: - -**Проблема:** один глобальный спрайт загружает иконки, которые текущему экрану не нужны. - -**Решение:** общие иконки хранить глобально, а наборы страниц и крупных компонентов — в отдельных спрайтах, загружаемых вместе с ними. - -```text -global → GlobalIcon → общие иконки приложения -analytics-page → AnalyticsPageIcon → иконки отдельной страницы -file-manager → FileManagerIcon → иконки крупного компонента -``` - -- **Глобальный спрайт** содержит небольшие общие иконки, используемые в разных частях приложения: навигацию, состояния и базовые действия. -- **Спрайт страницы** загружается вместе с конкретным разделом и не увеличивает общий спрайт иконками, которые больше нигде не нужны. -- **Спрайт крупного компонента** инкапсулирует собственный набор иконок сложного UI-модуля, например файлового менеджера или редактора. - -Каждая группа получает: - -- собственный SVG asset; -- собственный типизированный компонент; -- отдельный список имён иконок; -- отдельный debug-манифест; -- независимый cache lifecycle. - - -## TypeScript - -Главная возможность TypeScript API — автодополнение имён иконок непосредственно в prop `icon`: - -```tsx - -// ↑ редактор предлагает все иконки спрайта -``` - -Имена SVG-файлов становятся допустимыми значениями `icon`. Опечатка или неизвестное имя сразу становятся ошибкой TypeScript: - -```tsx - // ошибка TypeScript -``` - -Для программного доступа generated-модуль экспортирует readonly-массив всех доступных иконок конкретного спрайта: - -```ts -import { fileManagerIconNames } from './svg-sprite' - -// readonly ['check', 'folder', ...] -``` - -Этот список можно использовать в собственных каталогах, select-компонентах, тестах и других runtime-сценариях. Из него также выводится union-тип `FileManagerIconName`. - -Имена файлов с пробелами и другими небезопасными для SVG ID символами остаются частью публичного TypeScript API. Для внутреннего `` генератор создаёт стабильный hash ID. - -```text -folder open.svg → icon="folder open" → id="icon-" -``` - -Для таких имён используйте generated-компонент или `id` из debug-манифеста. Ручные примеры ниже с `#<имя>` подходят только для имён, которые уже являются безопасными SVG ID. - -## Форматы спрайтов - -`stack` — более современный формат, поэтому он используется по умолчанию. Иконки можно отображать через ``, `` и CSS `background-image`. - -`symbol` сохраняется для совместимости с существующими интеграциями и поддерживает отображение только через ``. - -## Способы отображения - -### React-компонент — рекомендуется - -Generated-компонент предоставляет типизацию, автодополнение имён иконок и сам формирует URL SVG asset. - -```tsx - -``` - -Через `color` и `--icon-color-N` доступны одноцветные и многоцветные иконки. - -### Самостоятельно через `` - -Хороший низкоуровневый способ с полным управлением размерами и цветами. React-компонент под капотом использует именно его. - -Способ получения `spriteUrl` зависит от сборщика. - -**Vite:** - -```tsx -import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline' -``` - -**Webpack 5:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -**Next.js с Webpack 5 или Turbopack:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -После получения URL иконка отображается одинаково: - -```tsx - - - -``` - -Vite, Webpack 5 и Next.js сами заменяют исходный путь на итоговый URL asset с hash. - -### Через `` — менее эффективно - -```tsx -Готово -``` - -SVG загружается как изолированное изображение: изменить его цвета через `color` или `--icon-color-N` нельзя. - -### Через CSS `background-image` — менее эффективно - -```css -.icon { - background: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -Как и ``, этот способ не позволяет управлять внутренними цветами SVG. Путь указывается относительно CSS-файла, а Vite/Webpack заменяет его на итоговый URL с hash при сборке. - -### Через CSS mask — менее эффективно - -```css -.icon { - background-color: currentColor; - mask: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -Mask оставляет только силуэт и окрашивает его одним цветом. Исходные цвета, gradients и различия между `fill` и `stroke` теряются. - -## Трансформации - -Все трансформации включены по умолчанию и настраиваются независимо через `transform`. - -| Опция | По умолчанию | Что делает | -|---|---|---| -| `removeSize` | `true` | Удаляет `width` и `height` с корневого ``, сохраняя существующий `viewBox`. Размер иконки после этого задаётся снаружи. | -| `replaceColors` | `true` | Заменяет цвета `fill` и `stroke` на `--icon-color-N`. Для одноцветной иконки fallback становится `currentColor`, для многоцветной сохраняются исходные цвета. | -| `addTransition` | `true` | Добавляет `style="transition:fill 0.3s,stroke 0.3s;"` непосредственно цветным элементам SVG. Существующий `transition` не перезаписывается. | - -Чтобы отключить преобразование, передайте для соответствующей опции `false`. Подробнее о результате `replaceColors` — в разделе [«Управление цветом иконок»](#управление-цветом-иконок). - -## Управление цветом иконок - -При включённой замене цветов генератор анализирует `fill` и `stroke` и преобразует их в CSS custom properties. - -### Монохромные иконки - -Если найден один цвет, fallback заменяется на `currentColor`: - -```svg -stroke="var(--icon-color-1, currentColor)" -``` - -Цветом управляет CSS-свойство `color` внешнего `` или его родителя. - -### Многоцветные иконки - -Каждый уникальный цвет получает отдельную переменную с исходным fallback: - -```svg -fill="var(--icon-color-1, #798198)" -fill="var(--icon-color-2, #ffffff)" -fill="var(--icon-color-3, #129d9d)" -``` - -Страница может заменить только необходимые цвета: - -```css -.icon { - --icon-color-1: #4b5563; - --icon-color-3: #14b8a6; -} -``` - -### Ограничения цветов - -- `none`, `transparent`, `inherit`, `unset` и `initial` не заменяются; -- цвета в атрибутах `fill`, `stroke` и inline `style` обрабатываются надёжнее всего; -- CSS-классы и внешние stylesheets внутри исходного SVG не являются основным сценарием трансформации; -- gradients, patterns, filters и значения `url(#...)` требуют отдельной проверки и могут быть несовместимы с автоматической заменой цветов; -- CSS-переменные страницы доступны при ``, но недоступны внутри `` и `background-image`. - -## Кеширование - -Vite, Webpack и Next.js target выпускают спрайт отдельным asset с content hash: - -```text -/assets/sprite-.svg -``` - -Это даёт следующие свойства: - -- SVG кешируется независимо от JavaScript; -- изменение React-кода не меняет содержимое спрайта; -- изменение иконок создаёт новый hash asset; -- один файл используется всеми экземплярами generated-компонента; -- SVG path-данные отсутствуют в JavaScript chunks. - -Vite target запрещает inline через `?no-inline`. Webpack 5 target использует Asset Modules через `new URL(..., import.meta.url)`. - -## SpriteViewer - -`SpriteViewer` — React-компонент для просмотра generated-спрайтов внутри debug-маршрута приложения. - -Он использует отдельные манифесты и показывает: - -- группы спрайтов; -- список и количество иконок; -- поиск и системную светлую/тёмную тему; -- модальное превью с `viewBox` и настройкой цветовых переменных; -- примеры React, SVG, IMG и CSS с копированием кода. - -Production-компоненты не импортируют debug-манифесты. Способ подключения Viewer зависит от сборщика: - -- [React + Vite: автоматический `import.meta.glob`](docs/ru/react-vite.md#6-добавьте-debug-страницу); -- [React + Webpack 5: статические `import()`](docs/ru/react-webpack.md#6-добавьте-debug-страницу); -- [Next.js App Router](docs/ru/next-app.md#5-добавьте-spriteviewer); -- [Next.js Pages Router](docs/ru/next-pages.md#5-добавьте-spriteviewer). - -Viewer подключается из отдельной клиентской точки входа `@gromlab/svg-sprites/react` и не попадает в production-компоненты иконок. - -### Тема Viewer - -По умолчанию `colorTheme="auto"`: Viewer следует `prefers-color-scheme` и реагирует на смену системной темы. Тему приложения можно передать явно: - -```tsx - -``` - -Допустимые значения `colorTheme`: `auto`, `light`, `dark`. При управлении темой извне встроенный переключатель скрывается. Чтобы оставить его и обновлять тему приложения через Viewer, передайте callback: - -```tsx - -``` - -## Документация - -- [React + Vite](docs/ru/react-vite.md) -- [React + Webpack 5](docs/ru/react-webpack.md) -- [Next.js App Router](docs/ru/next-app.md) -- [Next.js Pages Router](docs/ru/next-pages.md) -- [Legacy mode](docs/ru/legacy.md) -- [Миграция с 0.1.x](docs/ru/migration-1.md) -- [Программный API](docs/ru/programmatic-api.md) - -## Лицензия - -MIT diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/legacy.md b/skills/artifacts/svg-sprites-ru/references/docs/en/legacy.md deleted file mode 100644 index 10b6ea1..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/legacy.md +++ /dev/null @@ -1,102 +0,0 @@ -# Legacy mode - -[← Back to home](../../README.md) - -A quick guide to generating centralized SVG sprites in `symbol` and `stack` formats, with an optional HTML preview. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Prepare the icons and config - -```text -project/ -├── src/assets/icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprites.config.ts -``` - -```ts -// svg-sprites.config.ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -## 3. Run generation - -```bash -npx svg-sprites --mode legacy . -``` - -Result: - -```text -public/sprites/ -├── icons.sprite.svg -└── preview.html -``` - -With `preview: false`, the HTML file is not created. For the `stack` format, specify `format: 'stack'`. - -## 4. Use the symbol sprite - -```html - - - -``` - -## 5. Add a package script - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -## Multiple sprites - -Add multiple entries to `sprites`: - -```ts -sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: 'src/assets/logos', - format: 'stack', - }, -] -``` - -All output files and the shared `preview.html` will be written to `output`. - -## Troubleshooting - -- Config not found: make sure `svg-sprites.config.ts` is located in the specified root directory. -- No icons: check `sprites[].input` and the `.svg` extension. -- Preview not needed: set `preview: false`. - -For programmatic use, see [`generateLegacy`](programmatic-api.md#generatelegacy). diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/migration-1.md b/skills/artifacts/svg-sprites-ru/references/docs/en/migration-1.md deleted file mode 100644 index b53b915..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/migration-1.md +++ /dev/null @@ -1,96 +0,0 @@ -# Migrating from 0.1.x to 1.0 - -[← Back to home](../../README.md) - -Version 1.0 separates local generation for React and Next.js from the centralized legacy mode. The old config cannot be mixed with the new API in a single CLI invocation. - -## CLI - -The CLI now always requires an explicit `--mode` and a path to the configuration directory: - -```text -svg-sprites -→ svg-sprites --mode -``` - -Choose a mode based on your environment: - -| Environment | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Centralized legacy setup | `legacy` | - -## React and Next.js - -Instead of a root-level `svg-sprites.config.ts`, create a local `svg-sprite.config.ts` next to the icon set: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'global', - inputFolder: './icons', -}) -``` - -For regular React, use `defineReactSpriteConfig`. A folder and an explicit list of shared SVG files can be combined using `inputFolder` and `inputFiles`. - -The old `publicPath` and `react` options are no longer needed. The generated module is created next to the config and adds its own `.gitignore`, while Vite, Webpack, or Next.js emits the SVG as a separate asset with a content hash. - -The `` component is replaced by a component whose name is derived from `name`: - -```tsx - -``` - -To browse the icons, add `` as a debug page in the application. A separate `preview.html` is available only in legacy mode. - -## Legacy mode - -If you need to preserve the centralized structure, rename the helper and the format fields: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'stack', - }, - ], -}) -``` - -- `defineConfig` has been replaced with `defineLegacyConfig`; -- `sprites[].mode` has been renamed to `sprites[].format`; -- `generate` has been replaced with `generateLegacy`; -- `loadConfig` has been replaced with `loadLegacyConfig`; -- `publicPath` and generation of the old shared React component have been removed. - -Run: - -```bash -svg-sprites --mode legacy . -``` - -## Programmatic API - -The package is distributed as ESM only. Replace `require()` with `import`. - -`compileSpriteContent` now returns `Promise` so that the public declarations do not require `@types/node` to be installed. In Node.js, the actual result is compatible with APIs that accept `Uint8Array`. - -## After migration - -1. Remove the old generated files and rules that ignored the entire directory containing the source icons. -2. Add an explicit generation command before `dev`, `build`, and `typecheck`. -3. Run generation and type checking. -4. Check all icons and color variables using `SpriteViewer` or the legacy `preview.html`. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/next-app.md b/skills/artifacts/svg-sprites-ru/references/docs/en/next-app.md deleted file mode 100644 index 1e334ce..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/next-app.md +++ /dev/null @@ -1,102 +0,0 @@ -# Next.js App Router - -[← Back to home](../../README.md) - -Two explicit modes are supported: - -| Bundler | Mode key | Next.js version | -|---|---|---| -| Turbopack | `next@app/turbopack` | 16.2+ | -| Webpack 5 | `next@app/webpack` | 13.4+ | - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create a sprite module - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -## 3. Add generation - -For Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -For Webpack, replace the mode key with `next@app/webpack`. In Next 13–15, Webpack is used with the regular `next build` command; in Next 16, use `next build --webpack`. - -## 4. Use it in a Server Component - -The generated component does not contain `'use client'`, so it can be imported directly into `page.tsx` or `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -Next.js emits a separate SVG asset with a content hash. The same generated code is used during SSR and in the browser, with no URL mismatch. - -## 5. Add SpriteViewer - -The viewer is interactive, so it requires a separate Client Component boundary: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Verify the bundler - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -For Next 13–15 with Webpack, use `npx next build` without the flag. - -The Next.js command and the generator mode key must target the same bundler. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/next-pages.md b/skills/artifacts/svg-sprites-ru/references/docs/en/next-pages.md deleted file mode 100644 index 0de0136..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/next-pages.md +++ /dev/null @@ -1,96 +0,0 @@ -# Next.js Pages Router - -[← Back to home](../../README.md) - -Two explicit modes are supported: - -| Bundler | Mode key | Next.js version | -|---|---|---| -| Turbopack | `next@pages/turbopack` | 16.2+ | -| Webpack 5 | `next@pages/webpack` | 12.2+ | - -Next.js 12.2 requires React 18. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create a sprite module - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -## 3. Add generation - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -For Next.js 16.2 with Turbopack, replace the mode key with `next@pages/turbopack`. - -## 4. Use it on a page - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -The component works the same way with SSR, SSG, and client-side navigation. Next.js emits a separate SVG asset with a content hash. - -## 5. Add SpriteViewer - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Verify the bundler - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -For Next 12–15 with Webpack, use `npx next build` without the flag. - -The Next.js command and the generator mode key must target the same bundler. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/programmatic-api.md b/skills/artifacts/svg-sprites-ru/references/docs/en/programmatic-api.md deleted file mode 100644 index 7a4d44b..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/programmatic-api.md +++ /dev/null @@ -1,203 +0,0 @@ -# Programmatic API - -[← Back to home](../../README.md) - -The package provides a main Node.js entry point and a separate React runtime entry point. Both are distributed as ESM only and must be loaded with `import`. - -To resolve `@gromlab/svg-sprites/react` in TypeScript, use `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -## Main entry point - -```ts -import { - defineNextSpriteConfig, - defineReactSpriteConfig, - generateNextSprite, - generateReactSprite, -} from '@gromlab/svg-sprites' -``` - -The main entry point does not import React and can be used in CLIs, build scripts, and Node.js tools. - -## `generateReactSprite` - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', -) -``` - -The second argument is required: - -```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -Result: - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -```ts -console.log(result.name) -console.log(result.iconCount) -console.log(result.spritePath) -console.log(result.manifestPath) -``` - -The function loads `svg-sprite.config.ts` from the specified root, compiles the SVG files, and safely updates managed files. - -## `generateNextSprite` - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', - }, -) -``` - -Available values: - -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} -``` - -The result also contains the selected `router`, `bundler`, and the full target in the form `next@app/turbopack`. - -## `defineReactSpriteConfig` - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -`inputFolder` and `inputFiles` are combined. The helper returns the configuration without runtime transformations and provides TypeScript autocomplete. - -## `defineNextSpriteConfig` - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', -}) -``` - -Next.js uses the same configuration contract as the React presets. - -## `generateLegacy` - -```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -Returns an array: - -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` - -For details, see [Legacy mode](legacy.md). - -## Low-level functions - -The main entry point also exports: - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, - generatePreview, - loadLegacyConfig, - loadReactSpriteConfig, - resolveSpriteEntry, - resolveSprites, -} from '@gromlab/svg-sprites' -``` - -These functions are intended for custom orchestration built on top of the existing compiler and writer. For standard usage, prefer `generateReactSprite` and `generateLegacy`. - -## React runtime entry point - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -Types: - -```ts -import type { - SpriteManifest, - SpriteManifestColor, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSource, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -The React entry point contains `'use client'` and is intended for debug tools. Generated production components are imported from the application's local sprite modules, not from the package's React entry point. - -`SpriteViewerProps.colorTheme` accepts `auto | light | dark`. The default is `auto`, which follows `prefers-color-scheme`; to synchronize it with the application theme, pass the computed `light` or `dark` value. - -## Related guides - -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/react-vite.md b/skills/artifacts/svg-sprites-ru/references/docs/en/react-vite.md deleted file mode 100644 index 1e37cc4..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/react-vite.md +++ /dev/null @@ -1,116 +0,0 @@ -# React + Vite - -[← Back to home](../../README.md) - -A quick guide to installing and using SVG sprites in a React and Vite project. - -The result is a typed React component and a separate cacheable SVG asset. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Place the source SVG files in `icons/`. - -## 3. Add the configuration - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. - -The complete list of options is available under [Configuration → React](../../README.md#react). - -## 4. Add generation to package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated files are excluded from Git, so generation must run before `dev`, `build`, and `typecheck`. - -First run: - -```bash -npm run sprite:file-manager -``` - -## 5. Use the component - -The name `file-manager` is converted to `FileManagerIcon`: - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -TypeScript checks the `icon` value against the file names: - -```tsx - // valid - // TypeScript error -``` - -Types, display methods, and color controls are described in the [main documentation](../../README.md#display-methods). - -Vite emits the sprite as a separate file named like `assets/sprite-.svg`. SVG path data is not included in JavaScript. - -## 6. Add a debug page - -After integrating the icons, you can display all React sprites with `SpriteViewer`: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', -) - -export const IconsDebugPage = () => ( - -) -``` - -Vite automatically finds the generated `manifest.ts` for each React sprite. The `import.meta.glob` pattern must be a string literal, and generation must run before Vite starts. - -Only include the Viewer on a debug route or in an internal tool. - -## Troubleshooting - -- Missing `index.ts`: run `npm run sprite:file-manager`. -- The Viewer cannot find the sprite: check the glob path and make sure `manifest.ts` exists. -- `Refusing to overwrite a user file` error: there is a user file at a generated path. -- The icon does not change color: use `color` or `--icon-color-N`. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/en/react-webpack.md b/skills/artifacts/svg-sprites-ru/references/docs/en/react-webpack.md deleted file mode 100644 index 15383db..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/en/react-webpack.md +++ /dev/null @@ -1,118 +0,0 @@ -# React + Webpack 5 - -[← Back to home](../../README.md) - -A quick guide to installing and using SVG sprites in a React and Webpack 5 project. - -The result is a typed React component and a separate SVG asset emitted through Webpack Asset Modules. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Place the source SVG files in `icons/`. - -## 3. Add the configuration - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. - -The complete list of options is available under [Configuration → React](../../README.md#react). - -## 4. Add generation to package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated files are excluded from Git, so generation must run before `dev`, `build`, and `typecheck`. - -First run: - -```bash -npm run sprite:file-manager -``` - -## 5. Use the component - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -TypeScript checks the `icon` value against the file names: - -```tsx - // valid - // TypeScript error -``` - -Types, display methods, and color controls are described in the [main documentation](../../README.md#display-methods). - -Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` through Asset Modules and emits a separate SVG asset. - -If the project already uses a custom SVG loader, make sure it does not intercept the generated `sprite.svg` instead of Asset Modules. - -## 6. Add a debug page - -Webpack does not support Vite's `import.meta.glob` API, so provide static loaders: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), -] - -export const IconsDebugPage = () => ( - -) -``` - -The paths in `import()` must be string literals. Webpack creates chunks for the manifests and associates them with the SVG assets. - -Only include the Viewer on a debug route or in an internal tool. - -## Troubleshooting - -- Missing `index.ts`: run `npm run sprite:file-manager`. -- The Viewer does not load the sprite: check the path in `import()` and make sure `manifest.ts` exists. -- Incorrect asset URL: check `output.publicPath`. -- Another loader intercepts the SVG: exclude the generated sprite from the incompatible rule. - -For Next.js, use the separate mode keys described in the [App Router](next-app.md) and [Pages Router](next-pages.md) guides. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/legacy.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/legacy.md deleted file mode 100644 index 3d722b8..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/legacy.md +++ /dev/null @@ -1,102 +0,0 @@ -# Legacy mode - -[← Главная](../../README_RU.md) - -Краткая инструкция по генерации централизованных SVG-спрайтов форматов `symbol` и `stack` с optional HTML preview. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Подготовьте иконки и конфиг - -```text -project/ -├── src/assets/icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprites.config.ts -``` - -```ts -// svg-sprites.config.ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -## 3. Запустите генерацию - -```bash -npx svg-sprites --mode legacy . -``` - -Результат: - -```text -public/sprites/ -├── icons.sprite.svg -└── preview.html -``` - -При `preview: false` HTML-файл не создаётся. Для формата `stack` укажите `format: 'stack'`. - -## 4. Используйте symbol-спрайт - -```html - - - -``` - -## 5. Добавьте package script - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -## Несколько спрайтов - -Добавьте несколько записей в `sprites`: - -```ts -sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: 'src/assets/logos', - format: 'stack', - }, -] -``` - -Все результаты и общий `preview.html` будут записаны в `output`. - -## Если что-то не работает - -- Не найден конфиг: убедитесь, что `svg-sprites.config.ts` находится в переданном корне. -- Нет иконок: проверьте `sprites[].input` и расширение `.svg`. -- Не нужен preview: установите `preview: false`. - -Для программного запуска используйте [`generateLegacy`](programmatic-api.md#generatelegacy). diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/migration-1.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/migration-1.md deleted file mode 100644 index 39b15d8..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/migration-1.md +++ /dev/null @@ -1,96 +0,0 @@ -# Миграция с 0.1.x на 1.0 - -[← Главная](../../README_RU.md) - -Версия 1.0 разделяет локальную генерацию для React и Next.js и централизованный legacy-режим. Старый config нельзя смешивать с новым API в одном вызове CLI. - -## CLI - -CLI теперь всегда требует явный `--mode` и путь к каталогу конфигурации: - -```text -svg-sprites -→ svg-sprites --mode -``` - -Выберите mode по окружению: - -| Окружение | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Централизованная старая схема | `legacy` | - -## React и Next.js - -Вместо корневого `svg-sprites.config.ts` создайте локальный `svg-sprite.config.ts` рядом с набором иконок: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'global', - inputFolder: './icons', -}) -``` - -Для обычного React используйте `defineReactSpriteConfig`. Папку и явный список общих SVG можно объединить через `inputFolder` и `inputFiles`. - -Старые `publicPath` и `react` больше не нужны. Generated-модуль создаётся рядом с конфигом, сам добавляет `.gitignore`, а Vite, Webpack или Next.js выпускает SVG как отдельный asset с content hash. - -Компонент `` заменяется компонентом, имя которого выводится из `name`: - -```tsx - -``` - -Для просмотра иконок добавьте `` как debug-страницу приложения. Отдельный `preview.html` остаётся только в legacy-режиме. - -## Legacy-режим - -Если централизованную структуру нужно сохранить, переименуйте helper и поля формата: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'stack', - }, - ], -}) -``` - -- `defineConfig` заменён на `defineLegacyConfig`; -- `sprites[].mode` переименован в `sprites[].format`; -- `generate` заменён на `generateLegacy`; -- `loadConfig` заменён на `loadLegacyConfig`; -- `publicPath` и генерация старого общего React-компонента удалены. - -Запуск: - -```bash -svg-sprites --mode legacy . -``` - -## Программный API - -Пакет распространяется только как ESM. Замените `require()` на `import`. - -`compileSpriteContent` теперь возвращает `Promise`, чтобы публичные декларации не требовали установки `@types/node`. В Node.js фактический результат совместим с API, принимающими `Uint8Array`. - -## После миграции - -1. Удалите старые generated-файлы и правила, которые игнорировали целиком каталог с исходными иконками. -2. Добавьте явную команду генерации перед `dev`, `build` и `typecheck`. -3. Запустите генерацию и проверку типов. -4. Проверьте все иконки и цветовые переменные через `SpriteViewer` или legacy `preview.html`. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/next-app.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/next-app.md deleted file mode 100644 index 06049c6..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/next-app.md +++ /dev/null @@ -1,102 +0,0 @@ -# Next.js App Router - -[← Главная](../../README_RU.md) - -Поддерживаются два явных режима: - -| Сборщик | Mode key | Версия Next.js | -|---|---|---| -| Turbopack | `next@app/turbopack` | 16.2+ | -| Webpack 5 | `next@app/webpack` | 13.4+ | - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте sprite-модуль - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -## 3. Добавьте генерацию - -Для Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -Для Webpack замените mode key на `next@app/webpack`. В Next 13–15 Webpack используется обычной командой `next build`, в Next 16 — командой `next build --webpack`. - -## 4. Используйте в Server Component - -Generated-компонент не содержит `'use client'`, поэтому его можно импортировать непосредственно в `page.tsx` или `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -Next.js выпустит отдельный SVG asset с content hash. Один generated-код используется при SSR и в браузере без расхождения URL. - -## 5. Добавьте SpriteViewer - -Viewer интерактивен, поэтому для него нужна отдельная Client Component граница: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Проверка сборщика - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -Для Next 13–15 с Webpack используйте `npx next build` без флага. - -Команда Next.js и mode key генератора должны указывать один и тот же сборщик. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/next-pages.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/next-pages.md deleted file mode 100644 index 926f0ef..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/next-pages.md +++ /dev/null @@ -1,96 +0,0 @@ -# Next.js Pages Router - -[← Главная](../../README_RU.md) - -Поддерживаются два явных режима: - -| Сборщик | Mode key | Версия Next.js | -|---|---|---| -| Turbopack | `next@pages/turbopack` | 16.2+ | -| Webpack 5 | `next@pages/webpack` | 12.2+ | - -Для Next.js 12.2 требуется React 18. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте sprite-модуль - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -## 3. Добавьте генерацию - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -Для Next.js 16.2 с Turbopack замените mode key на `next@pages/turbopack`. - -## 4. Используйте на странице - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -Компонент одинаково работает при SSR, SSG и клиентских переходах. Next.js выпускает отдельный SVG asset с content hash. - -## 5. Добавьте SpriteViewer - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Проверка сборщика - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -Для Next 12–15 с Webpack используйте `npx next build` без флага. - -Команда Next.js и mode key генератора должны указывать один и тот же сборщик. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/programmatic-api.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/programmatic-api.md deleted file mode 100644 index 999cd01..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/programmatic-api.md +++ /dev/null @@ -1,203 +0,0 @@ -# Программный API - -[← Главная](../../README_RU.md) - -Пакет предоставляет основную Node.js точку входа и отдельный React runtime entry. Обе точки распространяются только как ESM и подключаются через `import`. - -Для разрешения `@gromlab/svg-sprites/react` в TypeScript используйте `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -## Основной entry - -```ts -import { - defineNextSpriteConfig, - defineReactSpriteConfig, - generateNextSprite, - generateReactSprite, -} from '@gromlab/svg-sprites' -``` - -Основной entry не импортирует React и может использоваться в CLI, build scripts и Node.js инструментах. - -## `generateReactSprite` - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', -) -``` - -Второй аргумент обязателен: - -```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -Результат: - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -```ts -console.log(result.name) -console.log(result.iconCount) -console.log(result.spritePath) -console.log(result.manifestPath) -``` - -Функция загружает `svg-sprite.config.ts` из указанного корня, компилирует SVG и безопасно обновляет managed-файлы. - -## `generateNextSprite` - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', - }, -) -``` - -Доступные значения: - -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} -``` - -Результат дополнительно содержит выбранные `router`, `bundler` и полный target вида `next@app/turbopack`. - -## `defineReactSpriteConfig` - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -`inputFolder` и `inputFiles` объединяются. Хелпер возвращает конфиг без runtime-преобразований и предоставляет TypeScript autocomplete. - -## `defineNextSpriteConfig` - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', -}) -``` - -Next.js использует тот же контракт конфигурации, что и React presets. - -## `generateLegacy` - -```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -Возвращается массив: - -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` - -Подробнее: [Legacy mode](legacy.md). - -## Низкоуровневые функции - -Основная точка входа также экспортирует: - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, - generatePreview, - loadLegacyConfig, - loadReactSpriteConfig, - resolveSpriteEntry, - resolveSprites, -} from '@gromlab/svg-sprites' -``` - -Эти функции предназначены для собственного orchestration поверх существующего compiler и writer. Для стандартного использования предпочтительны `generateReactSprite` и `generateLegacy`. - -## React runtime entry - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -Типы: - -```ts -import type { - SpriteManifest, - SpriteManifestColor, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSource, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -React entry содержит `'use client'` и предназначен для debug-инструментов. Generated production-компоненты импортируются из локальных sprite-модулей приложения, а не из React entry пакета. - -`SpriteViewerProps.colorTheme` принимает `auto | light | dark`. Значение `auto` используется по умолчанию и следует `prefers-color-scheme`; для синхронизации с темой приложения передавайте вычисленное `light` или `dark`. - -## Связанные руководства - -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/react-vite.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/react-vite.md deleted file mode 100644 index 2ae3b39..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/react-vite.md +++ /dev/null @@ -1,116 +0,0 @@ -# React + Vite - -[← Главная](../../README_RU.md) - -Краткая инструкция по установке и использованию SVG-спрайтов в проекте на React и Vite. - -В результате вы получите типизированный React-компонент и отдельный кешируемый SVG asset. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте папку спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Поместите исходные SVG-файлы в `icons/`. - -## 3. Добавьте конфиг - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. - -Полный список опций находится в разделе [Конфигурация → React](../../README_RU.md#react). - -## 4. Добавьте генерацию в package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated-файлы исключаются из Git, поэтому генерация должна выполняться перед `dev`, `build` и `typecheck`. - -Первый запуск: - -```bash -npm run sprite:file-manager -``` - -## 5. Используйте компонент - -Имя `file-manager` преобразуется в `FileManagerIcon`: - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -Значение `icon` проверяется TypeScript по именам файлов: - -```tsx - // допустимо - // ошибка TypeScript -``` - -Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения). - -Vite выпустит спрайт отдельным файлом вида `assets/sprite-.svg`. SVG path-данные не попадут в JavaScript. - -## 6. Добавьте debug-страницу - -После подключения иконок можно вывести все React-спрайты через `SpriteViewer`: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', -) - -export const IconsDebugPage = () => ( - -) -``` - -Vite автоматически найдёт generated `manifest.ts` каждого React-спрайта. Шаблон `import.meta.glob` должен быть строковым литералом, а генерация должна выполниться до запуска Vite. - -Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. - -## Если что-то не работает - -- Нет `index.ts`: запустите `npm run sprite:file-manager`. -- Viewer не видит спрайт: проверьте путь glob и наличие `manifest.ts`. -- Ошибка `Refusing to overwrite a user file`: в generated-пути находится пользовательский файл. -- Иконка не меняет цвет: используйте `color` или `--icon-color-N`. diff --git a/skills/artifacts/svg-sprites-ru/references/docs/ru/react-webpack.md b/skills/artifacts/svg-sprites-ru/references/docs/ru/react-webpack.md deleted file mode 100644 index 2a3c015..0000000 --- a/skills/artifacts/svg-sprites-ru/references/docs/ru/react-webpack.md +++ /dev/null @@ -1,118 +0,0 @@ -# React + Webpack 5 - -[← Главная](../../README_RU.md) - -Краткая инструкция по установке и использованию SVG-спрайтов в проекте на React и Webpack 5. - -В результате вы получите типизированный React-компонент и отдельный SVG asset через Webpack Asset Modules. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте папку спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Поместите исходные SVG-файлы в `icons/`. - -## 3. Добавьте конфиг - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. - -Полный список опций находится в разделе [Конфигурация → React](../../README_RU.md#react). - -## 4. Добавьте генерацию в package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated-файлы исключаются из Git, поэтому генерация должна выполняться перед `dev`, `build` и `typecheck`. - -Первый запуск: - -```bash -npm run sprite:file-manager -``` - -## 5. Используйте компонент - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -Значение `icon` проверяется TypeScript по именам файлов: - -```tsx - // допустимо - // ошибка TypeScript -``` - -Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения). - -Webpack обработает generated `new URL('./sprite.svg', import.meta.url)` через Asset Modules и выпустит отдельный SVG asset. - -Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules. - -## 6. Добавьте debug-страницу - -Webpack не поддерживает Vite API `import.meta.glob`, поэтому передайте статические loaders: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), -] - -export const IconsDebugPage = () => ( - -) -``` - -Пути в `import()` должны быть строковыми литералами. Webpack создаст chunks для манифестов и свяжет их с SVG assets. - -Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. - -## Если что-то не работает - -- Нет `index.ts`: запустите `npm run sprite:file-manager`. -- Viewer не загружает спрайт: проверьте путь в `import()` и наличие `manifest.ts`. -- Неверный URL asset: проверьте `output.publicPath`. -- SVG перехватывает другой loader: исключите generated sprite из несовместимого правила. - -Для Next.js используйте отдельные mode key из руководств [App Router](next-app.md) и [Pages Router](next-pages.md). diff --git a/skills/artifacts/svg-sprites/SKILL.md b/skills/artifacts/svg-sprites/SKILL.md deleted file mode 100644 index b33bec8..0000000 --- a/skills/artifacts/svg-sprites/SKILL.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -name: svg-sprites -description: "Use when configuring, generating, migrating, or troubleshooting SVG sprites with @gromlab/svg-sprites. Triggers: SVG sprite, svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, icon=\"...\", --icon-color-N, generated component, or an icon missing from preview or autocomplete. Do NOT use for favicons, raster images, icon fonts, choosing an icon set, or inline SVG without sprites." ---- - - - -# SVG Sprites - -## Purpose - -Use this skill when working with `@gromlab/svg-sprites`: initial setup, adding and reusing icons, generating React components, integrating `SpriteViewer`, migrating legacy configurations, and troubleshooting errors. - -Do not impose a specific directory architecture on the project. First inspect the existing `package.json`, sprite configuration, framework, router, and bundler. - -## Workflow - -1. Identify the existing mode and do not mix its API with another mode. -2. For React, choose `react@vite` or `react@webpack` and open the corresponding reference. -3. For Next.js, identify the App Router or Pages Router, then Turbopack or Webpack, and open the corresponding reference. -4. For an existing `svg-sprites.config.ts` with multiple sprites, use the legacy documentation. Do not migrate such a project unless explicitly requested. -5. Inspect local scripts and run generation before `dev`, `build`, and `typecheck` when generated files are not committed to Git. -6. After changing a configuration or SVG file, run generation followed by the available type check or project build. - -## React And Next.js Rules - -- Use a local `svg-sprite.config.ts` and the appropriate config helper: `defineReactSpriteConfig` or `defineNextSpriteConfig`. -- Do not manually edit `generated/`, `index.ts`, `manifest.ts`, or the generator-created `.gitignore`. -- Source SVG names become valid values for the `icon` prop; use the generated component and its public types instead of deep imports. -- Combine the local folder with `inputFiles` when multiple sprites need a shared icon. Do not create unnecessary copies of the same SVG. -- In Next.js, generated components work in Server Components, SSR, and SSG. Do not add `'use client'` only for an icon. -- Keep the sprite as an external bundler asset: do not move SVG path data into JavaScript or manually place the generated file in `public`. - -## Colors And Transformations - -- By default, the generator removes `width` and `height`, replaces supported `fill` and `stroke` values with CSS variables, and adds transitions. -- For a monochrome icon, control `color` first; for a multicolor icon, use `--icon-color-N`. -- Do not promise automatic color replacement inside external stylesheets, gradients, patterns, filters, or `url(#...)` values without checking the result. -- Page CSS variables work with ``, but do not propagate into `` or `background-image`. - -## Preview - -For React and Next.js, add `` as a separate debug page in the application. Pass sprite manifests or lazy loaders to it. The Viewer supports search, light and dark themes, color controls, and React, SVG, IMG, and CSS examples. - -`SpriteViewer` is a client-side debug tool imported from `@gromlab/svg-sprites/react`; production icon components do not depend on it. - -## Troubleshooting - -- If an icon name is missing from autocomplete, check the input folder and `inputFiles`, then rerun generation. -- If two files have the same icon name, resolve the conflict instead of implicitly selecting one file. -- If the generator refuses to overwrite a file, do not remove the protection marker or bypass the writer: move the user file or choose another sprite directory. -- If an asset fails to load, first confirm that the CLI mode matches the project's actual bundler and that its asset pipeline handles the generated SVG. -- If the project uses the old API, check the installed package version and the legacy reference before making changes. - -## References - -- [Main documentation and API](./references/README.md) -- [React + Vite](./references/docs/en/react-vite.md) -- [React + Webpack 5](./references/docs/en/react-webpack.md) -- [Next.js App Router](./references/docs/en/next-app.md) -- [Next.js Pages Router](./references/docs/en/next-pages.md) -- [Legacy mode](./references/docs/en/legacy.md) -- [Migrating from 0.1.x](./references/docs/en/migration-1.md) -- [Programmatic API](./references/docs/en/programmatic-api.md) diff --git a/skills/artifacts/svg-sprites/references/README.md b/skills/artifacts/svg-sprites/references/README.md deleted file mode 100644 index 055a165..0000000 --- a/skills/artifacts/svg-sprites/references/README.md +++ /dev/null @@ -1,405 +0,0 @@ -# @gromlab/svg-sprites - -🇬🇧 English | [🇷🇺 Русский](README_RU.md) - -![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) - -A CLI for generating SVG sprites and typed icon components for React and Next.js. - -![Preview](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png) - -## AI Skills - -- [🇬🇧 Download the English skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) -- [🇷🇺 Download the Russian skill (latest)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip) -- [Verify downloads with SHA-256 checksums](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/SHA256SUMS) - -## Navigation - -- [AI Skills](#ai-skills) -- [Features](#features) -- [Support matrix](#support-matrix) -- [Requirements](#requirements) -- [Quick start](#quick-start) - - [React + Vite](docs/en/react-vite.md) - - [React + Webpack 5](docs/en/react-webpack.md) - - [Next.js App Router](docs/en/next-app.md) - - [Next.js Pages Router](docs/en/next-pages.md) -- [Configuration](#configuration) - - [React](#react) - - [Next.js](#nextjs) -- [Multiple sprites](#multiple-sprites) -- [TypeScript](#typescript) -- [Sprite formats](#sprite-formats) -- [Rendering methods](#rendering-methods) -- [Transformations](#transformations) -- [Icon color management](#icon-color-management) -- [Caching](#caching) -- [SpriteViewer](#spriteviewer) -- [Migrating from 0.1.x](docs/en/migration-1.md) -- [Documentation](#documentation) - -## Features - -- **AI-agent friendly** - the repository includes a ready-to-use skill with up-to-date documentation for configuring, migrating, and troubleshooting `@gromlab/svg-sprites`. -- **TypeScript-friendly** - typed React components, union types, and runtime lists of available icons. -- **Clean generation** - generated files are automatically excluded from Git, the sprite does not need to be placed in `public` manually, and the generator updates only files it owns. -- **Shared icons without copying** - SVGs from the local folder and `inputFiles` are merged into a single sprite; one file can be used in multiple sprites. -- **Built-in interactive preview** - `` is integrated as an application page and displays the provided React and Next.js sprites with search, color controls, and usage examples. -- **Configurable SVG transformations** - remove `width` and `height` while preserving `viewBox`, replace source colors with CSS variables, and add transitions for `fill` and `stroke`. -- **Separate cacheable SVG asset** - SVG path data does not end up in JavaScript chunks, and the bundler emits a file with a content hash. -- **Multiple sprites** - independent React and Next.js modules with their own components, types, and SVG assets. -- **Server-first Next.js** - generated components work in Server Components, SSR, and SSG without the `'use client'` directive. -- **Formats for different use cases** - React and Next.js use `stack`; legacy mode also supports `symbol` for existing integrations. - -## Support matrix - -| Environment | API mode key | Status | -|---|---|---| -| React + Vite | `react@vite` | Ready | -| React + Webpack 5 | `react@webpack` | Ready | -| Next.js 16.2+ App Router + Turbopack | `next@app/turbopack` | Ready | -| Next.js 13.4+ App Router + Webpack 5 | `next@app/webpack` | Ready | -| Next.js 16.2+ Pages Router + Turbopack | `next@pages/turbopack` | Ready | -| Next.js 12.2+ Pages Router + Webpack 5 | `next@pages/webpack` | Ready | -| Vue | - | Coming soon | -| Standalone | - | Coming soon | - -## Requirements - -- Node.js 18 or newer; -- the package is distributed as ESM only and is loaded via `import`; -- React 18 or 19 is required only for generated components and the `@gromlab/svg-sprites/react` entry point; -- for subpath export typings, use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -## Quick start - -For a quick start, follow the guide for your stack: - -- [React + Vite](docs/en/react-vite.md) -- [React + Webpack 5](docs/en/react-webpack.md) -- [Next.js App Router](docs/en/next-app.md) -- [Next.js Pages Router](docs/en/next-pages.md) - -## Configuration - -### React - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -| Option | Type | Default | Purpose | -|---|---|---|---| -| `name` | `string` | Folder name | Name of the sprite, component, and public types | -| `description` | `string` | None | Description for types and the debug manifest | -| `inputFolder` | `string` | `./icons` | Folder containing source SVGs, relative to the config | -| `inputFiles` | `string[]` | `[]` | Additional SVG files, relative to the config | -| `transform` | `TransformOptions` | All enabled | [Transformation settings](#transformations) for source SVGs | -| `generatedNotice` | `boolean` | `true` | Full or short warning in generated files | - -`inputFolder` and `inputFiles` are merged into a single sprite, so one SVG file can be used in multiple sprites without copying. If the implicit `./icons` folder does not exist but `inputFiles` is populated, generation continues using only the list. An explicitly specified missing folder is an error. Duplicate paths are deduplicated, while different files with the same icon name are treated as an error. - -`name` is stored in kebab-case and must start with a Latin letter. The React and Next.js presets produce the `stack` format. - -### Next.js - -Next.js uses the same `svg-sprite.config.ts` and set of options. For type checking, you can use a dedicated helper: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', -}) -``` - -The router and bundler are selected through the mode key, so switching between Turbopack and Webpack is always explicitly reflected in the generation command. - -## Multiple sprites - -An application can contain several independent sprites for different scopes: - -**Problem:** one global sprite loads icons that the current screen does not need. - -**Solution:** keep shared icons globally, and place icon sets for pages and large components in separate sprites that load alongside them. - -```text -global -> GlobalIcon -> shared application icons -analytics-page -> AnalyticsPageIcon -> icons for a specific page -file-manager -> FileManagerIcon -> icons for a large component -``` - -- **Global sprite** contains a small set of shared icons used in different parts of the application: navigation, states, and basic actions. -- **Page sprite** loads with a specific section and does not increase the shared sprite with icons that are not needed anywhere else. -- **Large component sprite** encapsulates the icon set of a complex UI module, such as a file manager or editor. - -Each group gets: - -- its own SVG asset; -- its own typed component; -- a separate list of icon names; -- a separate debug manifest; -- an independent cache lifecycle. - - -## TypeScript - -The main feature of the TypeScript API is icon name autocomplete directly in the `icon` prop: - -```tsx - -// ^ the editor suggests every icon in the sprite -``` - -SVG file names become valid `icon` values. A typo or unknown name immediately becomes a TypeScript error: - -```tsx - // TypeScript error -``` - -For programmatic access, the generated module exports a readonly array of all icons available in a specific sprite: - -```ts -import { fileManagerIconNames } from './svg-sprite' - -// readonly ['check', 'folder', ...] -``` - -You can use this list in custom catalogs, select components, tests, and other runtime scenarios. The `FileManagerIconName` union type is also derived from it. - -File names containing spaces and other characters unsafe for SVG IDs remain part of the public TypeScript API. For the internal ``, the generator creates a stable hash ID. - -```text -folder open.svg -> icon="folder open" -> id="icon-" -``` - -For such names, use the generated component or the `id` from the debug manifest. The manual examples below using `#` are suitable only for names that are already safe SVG IDs. - -## Sprite formats - -`stack` is the more modern format, so it is used by default. Icons can be rendered through ``, ``, and CSS `background-image`. - -`symbol` is retained for compatibility with existing integrations and supports rendering only through ``. - -## Rendering methods - -### React component - recommended - -The generated component provides type safety and icon name autocomplete, and constructs the SVG asset URL itself. - -```tsx - -``` - -Monochrome and multicolor icons are supported through `color` and `--icon-color-N`. - -### Manually with `` - -A good low-level method that provides full control over dimensions and colors. This is exactly what the React component uses under the hood. - -How you obtain `spriteUrl` depends on the bundler. - -**Vite:** - -```tsx -import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline' -``` - -**Webpack 5:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -**Next.js with Webpack 5 or Turbopack:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -After obtaining the URL, the icon is rendered the same way: - -```tsx - - - -``` - -Vite, Webpack 5, and Next.js replace the source path with the final hashed asset URL automatically. - -### With `` - less efficient - -```tsx -Done -``` - -The SVG loads as an isolated image: its colors cannot be changed through `color` or `--icon-color-N`. - -### With CSS `background-image` - less efficient - -```css -.icon { - background: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -Like ``, this method does not allow you to control internal SVG colors. The path is specified relative to the CSS file, and Vite/Webpack replaces it with the final hashed URL during the build. - -### With CSS mask - less efficient - -```css -.icon { - background-color: currentColor; - mask: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -A mask retains only the silhouette and colors it with a single color. The original colors, gradients, and distinctions between `fill` and `stroke` are lost. - -## Transformations - -All transformations are enabled by default and configured independently through `transform`. - -| Option | Default | What it does | -|---|---|---| -| `removeSize` | `true` | Removes `width` and `height` from the root `` while preserving the existing `viewBox`. The icon size is then set externally. | -| `replaceColors` | `true` | Replaces `fill` and `stroke` colors with `--icon-color-N`. For a monochrome icon, the fallback becomes `currentColor`; for a multicolor icon, the original colors are preserved. | -| `addTransition` | `true` | Adds `style="transition:fill 0.3s,stroke 0.3s;"` directly to colored SVG elements. An existing `transition` is not overwritten. | - -To disable a transformation, pass `false` for the corresponding option. For more details about the result of `replaceColors`, see [Icon color management](#icon-color-management). - -## Icon color management - -When color replacement is enabled, the generator analyzes `fill` and `stroke` and converts them to CSS custom properties. - -### Monochrome icons - -If one color is found, the fallback is replaced with `currentColor`: - -```svg -stroke="var(--icon-color-1, currentColor)" -``` - -The color is controlled by the CSS `color` property of the outer `` or its parent. - -### Multicolor icons - -Each unique color gets a separate variable with the original fallback: - -```svg -fill="var(--icon-color-1, #798198)" -fill="var(--icon-color-2, #ffffff)" -fill="var(--icon-color-3, #129d9d)" -``` - -The page can override only the required colors: - -```css -.icon { - --icon-color-1: #4b5563; - --icon-color-3: #14b8a6; -} -``` - -### Color limitations - -- `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced; -- colors in `fill`, `stroke`, and inline `style` attributes are handled most reliably; -- CSS classes and external stylesheets inside the source SVG are not the primary transformation use case; -- gradients, patterns, filters, and `url(#...)` values require separate verification and may be incompatible with automatic color replacement; -- page CSS variables are available with ``, but are not available inside `` and `background-image`. - -## Caching - -The Vite, Webpack, and Next.js targets emit the sprite as a separate asset with a content hash: - -```text -/assets/sprite-.svg -``` - -This provides the following properties: - -- the SVG is cached independently of JavaScript; -- changes to React code do not alter the sprite contents; -- icon changes produce a new hashed asset; -- one file is used by every instance of the generated component; -- SVG path data is absent from JavaScript chunks. - -The Vite target prevents inlining through `?no-inline`. The Webpack 5 target uses Asset Modules through `new URL(..., import.meta.url)`. - -## SpriteViewer - -`SpriteViewer` is a React component for viewing generated sprites inside an application's debug route. - -It uses separate manifests and displays: - -- sprite groups; -- the icon list and count; -- search and the system light/dark theme; -- a preview modal with the `viewBox` and color variable controls; -- React, SVG, IMG, and CSS examples with code copying. - -Production components do not import debug manifests. How you integrate the Viewer depends on the bundler: - -- [React + Vite: automatic `import.meta.glob`](docs/en/react-vite.md#6-add-a-debug-page); -- [React + Webpack 5: static `import()`](docs/en/react-webpack.md#6-add-a-debug-page); -- [Next.js App Router](docs/en/next-app.md#5-add-spriteviewer); -- [Next.js Pages Router](docs/en/next-pages.md#5-add-spriteviewer). - -The Viewer is imported from the separate `@gromlab/svg-sprites/react` client entry point and is not included in production icon components. - -### Viewer theme - -By default, `colorTheme="auto"`: the Viewer follows `prefers-color-scheme` and responds to system theme changes. The application theme can be passed explicitly: - -```tsx - -``` - -Valid `colorTheme` values are `auto`, `light`, and `dark`. When the theme is controlled externally, the built-in switch is hidden. To keep it and update the application theme through the Viewer, pass a callback: - -```tsx - -``` - -## Documentation - -- [React + Vite](docs/en/react-vite.md) -- [React + Webpack 5](docs/en/react-webpack.md) -- [Next.js App Router](docs/en/next-app.md) -- [Next.js Pages Router](docs/en/next-pages.md) -- [Legacy mode](docs/en/legacy.md) -- [Migrating from 0.1.x](docs/en/migration-1.md) -- [Programmatic API](docs/en/programmatic-api.md) - -## License - -MIT diff --git a/skills/artifacts/svg-sprites/references/README_RU.md b/skills/artifacts/svg-sprites/references/README_RU.md deleted file mode 100644 index adbe8bf..0000000 --- a/skills/artifacts/svg-sprites/references/README_RU.md +++ /dev/null @@ -1,405 +0,0 @@ -# @gromlab/svg-sprites - -[🇬🇧 English](README.md) | 🇷🇺 Русский - -![npm](https://img.shields.io/npm/v/@gromlab/svg-sprites) ![license](https://img.shields.io/npm/l/@gromlab/svg-sprites) - -CLI для генерации SVG-спрайтов и типизированных компонентов иконок для React и Next.js. - -![Preview](https://raw.githubusercontent.com/gromov-sergei/svg-sprites/master/preview-image.png) - -## AI-скиллы - -- [🇬🇧 Скачать английский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites.zip) -- [🇷🇺 Скачать русский скилл (последняя версия)](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/svg-sprites-ru.zip) -- [Проверить SHA-256-суммы файлов](https://github.com/gromov-sergei/svg-sprites/releases/latest/download/SHA256SUMS) - -## Навигация - -- [AI-скиллы](#ai-скиллы) -- [Возможности](#возможности) -- [Таблица поддержки](#таблица-поддержки) -- [Требования](#требования) -- [Быстрый старт](#быстрый-старт) - - [React + Vite](docs/ru/react-vite.md) - - [React + Webpack 5](docs/ru/react-webpack.md) - - [Next.js App Router](docs/ru/next-app.md) - - [Next.js Pages Router](docs/ru/next-pages.md) -- [Конфигурация](#конфигурация) - - [React](#react) - - [Next.js](#nextjs) -- [Множественные спрайты](#множественные-спрайты) -- [TypeScript](#typescript) -- [Форматы спрайтов](#форматы-спрайтов) -- [Способы отображения](#способы-отображения) -- [Трансформации](#трансформации) -- [Управление цветом иконок](#управление-цветом-иконок) -- [Кеширование](#кеширование) -- [SpriteViewer](#spriteviewer) -- [Миграция с 0.1.x](docs/ru/migration-1.md) -- [Документация](#документация) - -## Возможности - -- **AI-agent friendly** — репозиторий содержит готовый skill с актуальной документацией для настройки, миграции и диагностики `@gromlab/svg-sprites`. -- **TypeScript-friendly** — типизированные React-компоненты, union-типы и runtime-списки доступных иконок. -- **Чистая генерация** — generated-файлы автоматически исключаются из Git, спрайт не нужно вручную размещать в `public`, а генератор обновляет только принадлежащие ему файлы. -- **Общие иконки без копирования** — SVG из локальной папки и `inputFiles` объединяются в один спрайт; один файл можно использовать в нескольких спрайтах. -- **Встроенное интерактивное превью** — `` подключается как страница приложения и показывает переданные React- и Next.js-спрайты с поиском, настройкой цветов и примерами использования. -- **Настраиваемые трансформации SVG** — удаление `width` и `height` с сохранением `viewBox`, замена исходных цветов на CSS-переменные и transitions для `fill` и `stroke`. -- **Отдельный кешируемый SVG asset** — SVG path-данные не попадают в JavaScript chunks, а сборщик выпускает файл с content hash. -- **Множественные спрайты** — независимые React- и Next.js-модули со своими компонентами, типами и SVG assets. -- **Server-first Next.js** — generated-компоненты работают в Server Components, SSR и SSG без директивы `'use client'`. -- **Форматы под разные сценарии** — React и Next.js используют `stack`, legacy-режим также поддерживает `symbol` для существующих интеграций. - -## Таблица поддержки - -| Среда | Ключ мода API | Статус | -|---|---|---| -| React + Vite | `react@vite` | Готово | -| React + Webpack 5 | `react@webpack` | Готово | -| Next.js 16.2+ App Router + Turbopack | `next@app/turbopack` | Готово | -| Next.js 13.4+ App Router + Webpack 5 | `next@app/webpack` | Готово | -| Next.js 16.2+ Pages Router + Turbopack | `next@pages/turbopack` | Готово | -| Next.js 12.2+ Pages Router + Webpack 5 | `next@pages/webpack` | Готово | -| Vue | — | Скоро | -| Standalone | — | Скоро | - -## Требования - -- Node.js 18 или новее; -- пакет распространяется только как ESM и подключается через `import`; -- React 18 или 19 требуется только для generated-компонентов и точки входа `@gromlab/svg-sprites/react`; -- для типизации subpath exports используйте TypeScript 5+ с `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -## Быстрый старт - -Для быстрого старта воспользуйтесь инструкцией для вашего стека: - -- [React + Vite](docs/ru/react-vite.md) -- [React + Webpack 5](docs/ru/react-webpack.md) -- [Next.js App Router](docs/ru/next-app.md) -- [Next.js Pages Router](docs/ru/next-pages.md) - -## Конфигурация - -### React - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -| Опция | Тип | По умолчанию | Назначение | -|---|---|---|---| -| `name` | `string` | Имя папки | Имя спрайта, компонента и публичных типов | -| `description` | `string` | Нет | Описание для типов и debug-манифеста | -| `inputFolder` | `string` | `./icons` | Папка с исходными SVG относительно конфига | -| `inputFiles` | `string[]` | `[]` | Дополнительные SVG-файлы относительно конфига | -| `transform` | `TransformOptions` | Все включены | [Настройки трансформации](#трансформации) исходных SVG | -| `generatedNotice` | `boolean` | `true` | Полное либо короткое предупреждение в generated-файлах | - -`inputFolder` и `inputFiles` объединяются в один спрайт, поэтому один SVG-файл можно использовать в нескольких спрайтах без копирования. Если неявной папки `./icons` нет, но `inputFiles` заполнен, генерация продолжается только по списку. Явно указанная отсутствующая папка считается ошибкой. Одинаковые пути дедуплицируются, а разные файлы с одинаковым именем иконки считаются ошибкой. - -`name` записывается в kebab-case и должно начинаться с латинской буквы. React и Next.js presets создают формат `stack`. - -### Next.js - -Next.js использует тот же `svg-sprite.config.ts` и набор опций. Для типизации можно использовать отдельный хелпер: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', -}) -``` - -Роутер и сборщик выбираются через mode key, поэтому переключение между Turbopack и Webpack всегда явно отражено в команде генерации. - -## Множественные спрайты - -Приложение может содержать несколько независимых спрайтов с разной областью использования: - -**Проблема:** один глобальный спрайт загружает иконки, которые текущему экрану не нужны. - -**Решение:** общие иконки хранить глобально, а наборы страниц и крупных компонентов — в отдельных спрайтах, загружаемых вместе с ними. - -```text -global → GlobalIcon → общие иконки приложения -analytics-page → AnalyticsPageIcon → иконки отдельной страницы -file-manager → FileManagerIcon → иконки крупного компонента -``` - -- **Глобальный спрайт** содержит небольшие общие иконки, используемые в разных частях приложения: навигацию, состояния и базовые действия. -- **Спрайт страницы** загружается вместе с конкретным разделом и не увеличивает общий спрайт иконками, которые больше нигде не нужны. -- **Спрайт крупного компонента** инкапсулирует собственный набор иконок сложного UI-модуля, например файлового менеджера или редактора. - -Каждая группа получает: - -- собственный SVG asset; -- собственный типизированный компонент; -- отдельный список имён иконок; -- отдельный debug-манифест; -- независимый cache lifecycle. - - -## TypeScript - -Главная возможность TypeScript API — автодополнение имён иконок непосредственно в prop `icon`: - -```tsx - -// ↑ редактор предлагает все иконки спрайта -``` - -Имена SVG-файлов становятся допустимыми значениями `icon`. Опечатка или неизвестное имя сразу становятся ошибкой TypeScript: - -```tsx - // ошибка TypeScript -``` - -Для программного доступа generated-модуль экспортирует readonly-массив всех доступных иконок конкретного спрайта: - -```ts -import { fileManagerIconNames } from './svg-sprite' - -// readonly ['check', 'folder', ...] -``` - -Этот список можно использовать в собственных каталогах, select-компонентах, тестах и других runtime-сценариях. Из него также выводится union-тип `FileManagerIconName`. - -Имена файлов с пробелами и другими небезопасными для SVG ID символами остаются частью публичного TypeScript API. Для внутреннего `` генератор создаёт стабильный hash ID. - -```text -folder open.svg → icon="folder open" → id="icon-" -``` - -Для таких имён используйте generated-компонент или `id` из debug-манифеста. Ручные примеры ниже с `#<имя>` подходят только для имён, которые уже являются безопасными SVG ID. - -## Форматы спрайтов - -`stack` — более современный формат, поэтому он используется по умолчанию. Иконки можно отображать через ``, `` и CSS `background-image`. - -`symbol` сохраняется для совместимости с существующими интеграциями и поддерживает отображение только через ``. - -## Способы отображения - -### React-компонент — рекомендуется - -Generated-компонент предоставляет типизацию, автодополнение имён иконок и сам формирует URL SVG asset. - -```tsx - -``` - -Через `color` и `--icon-color-N` доступны одноцветные и многоцветные иконки. - -### Самостоятельно через `` - -Хороший низкоуровневый способ с полным управлением размерами и цветами. React-компонент под капотом использует именно его. - -Способ получения `spriteUrl` зависит от сборщика. - -**Vite:** - -```tsx -import spriteUrl from './svg-sprite/generated/sprite.svg?no-inline' -``` - -**Webpack 5:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -**Next.js с Webpack 5 или Turbopack:** - -```tsx -const spriteUrl = new URL( - './svg-sprite/generated/sprite.svg', - import.meta.url, -).href -``` - -После получения URL иконка отображается одинаково: - -```tsx - - - -``` - -Vite, Webpack 5 и Next.js сами заменяют исходный путь на итоговый URL asset с hash. - -### Через `` — менее эффективно - -```tsx -Готово -``` - -SVG загружается как изолированное изображение: изменить его цвета через `color` или `--icon-color-N` нельзя. - -### Через CSS `background-image` — менее эффективно - -```css -.icon { - background: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -Как и ``, этот способ не позволяет управлять внутренними цветами SVG. Путь указывается относительно CSS-файла, а Vite/Webpack заменяет его на итоговый URL с hash при сборке. - -### Через CSS mask — менее эффективно - -```css -.icon { - background-color: currentColor; - mask: url('./svg-sprite/generated/sprite.svg#check') center / contain no-repeat; -} -``` - -Mask оставляет только силуэт и окрашивает его одним цветом. Исходные цвета, gradients и различия между `fill` и `stroke` теряются. - -## Трансформации - -Все трансформации включены по умолчанию и настраиваются независимо через `transform`. - -| Опция | По умолчанию | Что делает | -|---|---|---| -| `removeSize` | `true` | Удаляет `width` и `height` с корневого ``, сохраняя существующий `viewBox`. Размер иконки после этого задаётся снаружи. | -| `replaceColors` | `true` | Заменяет цвета `fill` и `stroke` на `--icon-color-N`. Для одноцветной иконки fallback становится `currentColor`, для многоцветной сохраняются исходные цвета. | -| `addTransition` | `true` | Добавляет `style="transition:fill 0.3s,stroke 0.3s;"` непосредственно цветным элементам SVG. Существующий `transition` не перезаписывается. | - -Чтобы отключить преобразование, передайте для соответствующей опции `false`. Подробнее о результате `replaceColors` — в разделе [«Управление цветом иконок»](#управление-цветом-иконок). - -## Управление цветом иконок - -При включённой замене цветов генератор анализирует `fill` и `stroke` и преобразует их в CSS custom properties. - -### Монохромные иконки - -Если найден один цвет, fallback заменяется на `currentColor`: - -```svg -stroke="var(--icon-color-1, currentColor)" -``` - -Цветом управляет CSS-свойство `color` внешнего `` или его родителя. - -### Многоцветные иконки - -Каждый уникальный цвет получает отдельную переменную с исходным fallback: - -```svg -fill="var(--icon-color-1, #798198)" -fill="var(--icon-color-2, #ffffff)" -fill="var(--icon-color-3, #129d9d)" -``` - -Страница может заменить только необходимые цвета: - -```css -.icon { - --icon-color-1: #4b5563; - --icon-color-3: #14b8a6; -} -``` - -### Ограничения цветов - -- `none`, `transparent`, `inherit`, `unset` и `initial` не заменяются; -- цвета в атрибутах `fill`, `stroke` и inline `style` обрабатываются надёжнее всего; -- CSS-классы и внешние stylesheets внутри исходного SVG не являются основным сценарием трансформации; -- gradients, patterns, filters и значения `url(#...)` требуют отдельной проверки и могут быть несовместимы с автоматической заменой цветов; -- CSS-переменные страницы доступны при ``, но недоступны внутри `` и `background-image`. - -## Кеширование - -Vite, Webpack и Next.js target выпускают спрайт отдельным asset с content hash: - -```text -/assets/sprite-.svg -``` - -Это даёт следующие свойства: - -- SVG кешируется независимо от JavaScript; -- изменение React-кода не меняет содержимое спрайта; -- изменение иконок создаёт новый hash asset; -- один файл используется всеми экземплярами generated-компонента; -- SVG path-данные отсутствуют в JavaScript chunks. - -Vite target запрещает inline через `?no-inline`. Webpack 5 target использует Asset Modules через `new URL(..., import.meta.url)`. - -## SpriteViewer - -`SpriteViewer` — React-компонент для просмотра generated-спрайтов внутри debug-маршрута приложения. - -Он использует отдельные манифесты и показывает: - -- группы спрайтов; -- список и количество иконок; -- поиск и системную светлую/тёмную тему; -- модальное превью с `viewBox` и настройкой цветовых переменных; -- примеры React, SVG, IMG и CSS с копированием кода. - -Production-компоненты не импортируют debug-манифесты. Способ подключения Viewer зависит от сборщика: - -- [React + Vite: автоматический `import.meta.glob`](docs/ru/react-vite.md#6-добавьте-debug-страницу); -- [React + Webpack 5: статические `import()`](docs/ru/react-webpack.md#6-добавьте-debug-страницу); -- [Next.js App Router](docs/ru/next-app.md#5-добавьте-spriteviewer); -- [Next.js Pages Router](docs/ru/next-pages.md#5-добавьте-spriteviewer). - -Viewer подключается из отдельной клиентской точки входа `@gromlab/svg-sprites/react` и не попадает в production-компоненты иконок. - -### Тема Viewer - -По умолчанию `colorTheme="auto"`: Viewer следует `prefers-color-scheme` и реагирует на смену системной темы. Тему приложения можно передать явно: - -```tsx - -``` - -Допустимые значения `colorTheme`: `auto`, `light`, `dark`. При управлении темой извне встроенный переключатель скрывается. Чтобы оставить его и обновлять тему приложения через Viewer, передайте callback: - -```tsx - -``` - -## Документация - -- [React + Vite](docs/ru/react-vite.md) -- [React + Webpack 5](docs/ru/react-webpack.md) -- [Next.js App Router](docs/ru/next-app.md) -- [Next.js Pages Router](docs/ru/next-pages.md) -- [Legacy mode](docs/ru/legacy.md) -- [Миграция с 0.1.x](docs/ru/migration-1.md) -- [Программный API](docs/ru/programmatic-api.md) - -## Лицензия - -MIT diff --git a/skills/artifacts/svg-sprites/references/docs/en/legacy.md b/skills/artifacts/svg-sprites/references/docs/en/legacy.md deleted file mode 100644 index 10b6ea1..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/legacy.md +++ /dev/null @@ -1,102 +0,0 @@ -# Legacy mode - -[← Back to home](../../README.md) - -A quick guide to generating centralized SVG sprites in `symbol` and `stack` formats, with an optional HTML preview. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Prepare the icons and config - -```text -project/ -├── src/assets/icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprites.config.ts -``` - -```ts -// svg-sprites.config.ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -## 3. Run generation - -```bash -npx svg-sprites --mode legacy . -``` - -Result: - -```text -public/sprites/ -├── icons.sprite.svg -└── preview.html -``` - -With `preview: false`, the HTML file is not created. For the `stack` format, specify `format: 'stack'`. - -## 4. Use the symbol sprite - -```html - - - -``` - -## 5. Add a package script - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -## Multiple sprites - -Add multiple entries to `sprites`: - -```ts -sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: 'src/assets/logos', - format: 'stack', - }, -] -``` - -All output files and the shared `preview.html` will be written to `output`. - -## Troubleshooting - -- Config not found: make sure `svg-sprites.config.ts` is located in the specified root directory. -- No icons: check `sprites[].input` and the `.svg` extension. -- Preview not needed: set `preview: false`. - -For programmatic use, see [`generateLegacy`](programmatic-api.md#generatelegacy). diff --git a/skills/artifacts/svg-sprites/references/docs/en/migration-1.md b/skills/artifacts/svg-sprites/references/docs/en/migration-1.md deleted file mode 100644 index b53b915..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/migration-1.md +++ /dev/null @@ -1,96 +0,0 @@ -# Migrating from 0.1.x to 1.0 - -[← Back to home](../../README.md) - -Version 1.0 separates local generation for React and Next.js from the centralized legacy mode. The old config cannot be mixed with the new API in a single CLI invocation. - -## CLI - -The CLI now always requires an explicit `--mode` and a path to the configuration directory: - -```text -svg-sprites -→ svg-sprites --mode -``` - -Choose a mode based on your environment: - -| Environment | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Centralized legacy setup | `legacy` | - -## React and Next.js - -Instead of a root-level `svg-sprites.config.ts`, create a local `svg-sprite.config.ts` next to the icon set: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'global', - inputFolder: './icons', -}) -``` - -For regular React, use `defineReactSpriteConfig`. A folder and an explicit list of shared SVG files can be combined using `inputFolder` and `inputFiles`. - -The old `publicPath` and `react` options are no longer needed. The generated module is created next to the config and adds its own `.gitignore`, while Vite, Webpack, or Next.js emits the SVG as a separate asset with a content hash. - -The `` component is replaced by a component whose name is derived from `name`: - -```tsx - -``` - -To browse the icons, add `` as a debug page in the application. A separate `preview.html` is available only in legacy mode. - -## Legacy mode - -If you need to preserve the centralized structure, rename the helper and the format fields: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'stack', - }, - ], -}) -``` - -- `defineConfig` has been replaced with `defineLegacyConfig`; -- `sprites[].mode` has been renamed to `sprites[].format`; -- `generate` has been replaced with `generateLegacy`; -- `loadConfig` has been replaced with `loadLegacyConfig`; -- `publicPath` and generation of the old shared React component have been removed. - -Run: - -```bash -svg-sprites --mode legacy . -``` - -## Programmatic API - -The package is distributed as ESM only. Replace `require()` with `import`. - -`compileSpriteContent` now returns `Promise` so that the public declarations do not require `@types/node` to be installed. In Node.js, the actual result is compatible with APIs that accept `Uint8Array`. - -## After migration - -1. Remove the old generated files and rules that ignored the entire directory containing the source icons. -2. Add an explicit generation command before `dev`, `build`, and `typecheck`. -3. Run generation and type checking. -4. Check all icons and color variables using `SpriteViewer` or the legacy `preview.html`. diff --git a/skills/artifacts/svg-sprites/references/docs/en/next-app.md b/skills/artifacts/svg-sprites/references/docs/en/next-app.md deleted file mode 100644 index 1e334ce..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/next-app.md +++ /dev/null @@ -1,102 +0,0 @@ -# Next.js App Router - -[← Back to home](../../README.md) - -Two explicit modes are supported: - -| Bundler | Mode key | Next.js version | -|---|---|---| -| Turbopack | `next@app/turbopack` | 16.2+ | -| Webpack 5 | `next@app/webpack` | 13.4+ | - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create a sprite module - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -## 3. Add generation - -For Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -For Webpack, replace the mode key with `next@app/webpack`. In Next 13–15, Webpack is used with the regular `next build` command; in Next 16, use `next build --webpack`. - -## 4. Use it in a Server Component - -The generated component does not contain `'use client'`, so it can be imported directly into `page.tsx` or `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -Next.js emits a separate SVG asset with a content hash. The same generated code is used during SSR and in the browser, with no URL mismatch. - -## 5. Add SpriteViewer - -The viewer is interactive, so it requires a separate Client Component boundary: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Verify the bundler - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -For Next 13–15 with Webpack, use `npx next build` without the flag. - -The Next.js command and the generator mode key must target the same bundler. diff --git a/skills/artifacts/svg-sprites/references/docs/en/next-pages.md b/skills/artifacts/svg-sprites/references/docs/en/next-pages.md deleted file mode 100644 index 0de0136..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/next-pages.md +++ /dev/null @@ -1,96 +0,0 @@ -# Next.js Pages Router - -[← Back to home](../../README.md) - -Two explicit modes are supported: - -| Bundler | Mode key | Next.js version | -|---|---|---| -| Turbopack | `next@pages/turbopack` | 16.2+ | -| Webpack 5 | `next@pages/webpack` | 12.2+ | - -Next.js 12.2 requires React 18. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create a sprite module - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -## 3. Add generation - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -For Next.js 16.2 with Turbopack, replace the mode key with `next@pages/turbopack`. - -## 4. Use it on a page - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -The component works the same way with SSR, SSG, and client-side navigation. Next.js emits a separate SVG asset with a content hash. - -## 5. Add SpriteViewer - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Verify the bundler - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -For Next 12–15 with Webpack, use `npx next build` without the flag. - -The Next.js command and the generator mode key must target the same bundler. diff --git a/skills/artifacts/svg-sprites/references/docs/en/programmatic-api.md b/skills/artifacts/svg-sprites/references/docs/en/programmatic-api.md deleted file mode 100644 index 7a4d44b..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/programmatic-api.md +++ /dev/null @@ -1,203 +0,0 @@ -# Programmatic API - -[← Back to home](../../README.md) - -The package provides a main Node.js entry point and a separate React runtime entry point. Both are distributed as ESM only and must be loaded with `import`. - -To resolve `@gromlab/svg-sprites/react` in TypeScript, use `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`. - -## Main entry point - -```ts -import { - defineNextSpriteConfig, - defineReactSpriteConfig, - generateNextSprite, - generateReactSprite, -} from '@gromlab/svg-sprites' -``` - -The main entry point does not import React and can be used in CLIs, build scripts, and Node.js tools. - -## `generateReactSprite` - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', -) -``` - -The second argument is required: - -```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -Result: - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -```ts -console.log(result.name) -console.log(result.iconCount) -console.log(result.spritePath) -console.log(result.manifestPath) -``` - -The function loads `svg-sprite.config.ts` from the specified root, compiles the SVG files, and safely updates managed files. - -## `generateNextSprite` - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', - }, -) -``` - -Available values: - -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} -``` - -The result also contains the selected `router`, `bundler`, and the full target in the form `next@app/turbopack`. - -## `defineReactSpriteConfig` - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -`inputFolder` and `inputFiles` are combined. The helper returns the configuration without runtime transformations and provides TypeScript autocomplete. - -## `defineNextSpriteConfig` - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', - inputFolder: './icons', -}) -``` - -Next.js uses the same configuration contract as the React presets. - -## `generateLegacy` - -```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -Returns an array: - -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` - -For details, see [Legacy mode](legacy.md). - -## Low-level functions - -The main entry point also exports: - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, - generatePreview, - loadLegacyConfig, - loadReactSpriteConfig, - resolveSpriteEntry, - resolveSprites, -} from '@gromlab/svg-sprites' -``` - -These functions are intended for custom orchestration built on top of the existing compiler and writer. For standard usage, prefer `generateReactSprite` and `generateLegacy`. - -## React runtime entry point - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -Types: - -```ts -import type { - SpriteManifest, - SpriteManifestColor, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSource, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -The React entry point contains `'use client'` and is intended for debug tools. Generated production components are imported from the application's local sprite modules, not from the package's React entry point. - -`SpriteViewerProps.colorTheme` accepts `auto | light | dark`. The default is `auto`, which follows `prefers-color-scheme`; to synchronize it with the application theme, pass the computed `light` or `dark` value. - -## Related guides - -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) diff --git a/skills/artifacts/svg-sprites/references/docs/en/react-vite.md b/skills/artifacts/svg-sprites/references/docs/en/react-vite.md deleted file mode 100644 index 1e37cc4..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/react-vite.md +++ /dev/null @@ -1,116 +0,0 @@ -# React + Vite - -[← Back to home](../../README.md) - -A quick guide to installing and using SVG sprites in a React and Vite project. - -The result is a typed React component and a separate cacheable SVG asset. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Place the source SVG files in `icons/`. - -## 3. Add the configuration - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. - -The complete list of options is available under [Configuration → React](../../README.md#react). - -## 4. Add generation to package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated files are excluded from Git, so generation must run before `dev`, `build`, and `typecheck`. - -First run: - -```bash -npm run sprite:file-manager -``` - -## 5. Use the component - -The name `file-manager` is converted to `FileManagerIcon`: - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -TypeScript checks the `icon` value against the file names: - -```tsx - // valid - // TypeScript error -``` - -Types, display methods, and color controls are described in the [main documentation](../../README.md#display-methods). - -Vite emits the sprite as a separate file named like `assets/sprite-.svg`. SVG path data is not included in JavaScript. - -## 6. Add a debug page - -After integrating the icons, you can display all React sprites with `SpriteViewer`: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', -) - -export const IconsDebugPage = () => ( - -) -``` - -Vite automatically finds the generated `manifest.ts` for each React sprite. The `import.meta.glob` pattern must be a string literal, and generation must run before Vite starts. - -Only include the Viewer on a debug route or in an internal tool. - -## Troubleshooting - -- Missing `index.ts`: run `npm run sprite:file-manager`. -- The Viewer cannot find the sprite: check the glob path and make sure `manifest.ts` exists. -- `Refusing to overwrite a user file` error: there is a user file at a generated path. -- The icon does not change color: use `color` or `--icon-color-N`. diff --git a/skills/artifacts/svg-sprites/references/docs/en/react-webpack.md b/skills/artifacts/svg-sprites/references/docs/en/react-webpack.md deleted file mode 100644 index 15383db..0000000 --- a/skills/artifacts/svg-sprites/references/docs/en/react-webpack.md +++ /dev/null @@ -1,118 +0,0 @@ -# React + Webpack 5 - -[← Back to home](../../README.md) - -A quick guide to installing and using SVG sprites in a React and Webpack 5 project. - -The result is a typed React component and a separate SVG asset emitted through Webpack Asset Modules. - -## 1. Install the package - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Create the sprite directory - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Place the source SVG files in `icons/`. - -## 3. Add the configuration - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'File manager icons', -}) -``` - -By default, SVG files are loaded from `./icons`. You can add shared icons from other directories through `inputFiles`: the directory and file list are combined into a single sprite. - -The complete list of options is available under [Configuration → React](../../README.md#react). - -## 4. Add generation to package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated files are excluded from Git, so generation must run before `dev`, `build`, and `typecheck`. - -First run: - -```bash -npm run sprite:file-manager -``` - -## 5. Use the component - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -TypeScript checks the `icon` value against the file names: - -```tsx - // valid - // TypeScript error -``` - -Types, display methods, and color controls are described in the [main documentation](../../README.md#display-methods). - -Webpack processes the generated `new URL('./sprite.svg', import.meta.url)` through Asset Modules and emits a separate SVG asset. - -If the project already uses a custom SVG loader, make sure it does not intercept the generated `sprite.svg` instead of Asset Modules. - -## 6. Add a debug page - -Webpack does not support Vite's `import.meta.glob` API, so provide static loaders: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), -] - -export const IconsDebugPage = () => ( - -) -``` - -The paths in `import()` must be string literals. Webpack creates chunks for the manifests and associates them with the SVG assets. - -Only include the Viewer on a debug route or in an internal tool. - -## Troubleshooting - -- Missing `index.ts`: run `npm run sprite:file-manager`. -- The Viewer does not load the sprite: check the path in `import()` and make sure `manifest.ts` exists. -- Incorrect asset URL: check `output.publicPath`. -- Another loader intercepts the SVG: exclude the generated sprite from the incompatible rule. - -For Next.js, use the separate mode keys described in the [App Router](next-app.md) and [Pages Router](next-pages.md) guides. diff --git a/skills/artifacts/svg-sprites/references/docs/ru/legacy.md b/skills/artifacts/svg-sprites/references/docs/ru/legacy.md deleted file mode 100644 index 3d722b8..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/legacy.md +++ /dev/null @@ -1,102 +0,0 @@ -# Legacy mode - -[← Главная](../../README_RU.md) - -Краткая инструкция по генерации централизованных SVG-спрайтов форматов `symbol` и `stack` с optional HTML preview. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Подготовьте иконки и конфиг - -```text -project/ -├── src/assets/icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprites.config.ts -``` - -```ts -// svg-sprites.config.ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -## 3. Запустите генерацию - -```bash -npx svg-sprites --mode legacy . -``` - -Результат: - -```text -public/sprites/ -├── icons.sprite.svg -└── preview.html -``` - -При `preview: false` HTML-файл не создаётся. Для формата `stack` укажите `format: 'stack'`. - -## 4. Используйте symbol-спрайт - -```html - - - -``` - -## 5. Добавьте package script - -```json -{ - "scripts": { - "sprites": "svg-sprites --mode legacy .", - "prebuild": "npm run sprites" - } -} -``` - -## Несколько спрайтов - -Добавьте несколько записей в `sprites`: - -```ts -sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - { - name: 'logos', - input: 'src/assets/logos', - format: 'stack', - }, -] -``` - -Все результаты и общий `preview.html` будут записаны в `output`. - -## Если что-то не работает - -- Не найден конфиг: убедитесь, что `svg-sprites.config.ts` находится в переданном корне. -- Нет иконок: проверьте `sprites[].input` и расширение `.svg`. -- Не нужен preview: установите `preview: false`. - -Для программного запуска используйте [`generateLegacy`](programmatic-api.md#generatelegacy). diff --git a/skills/artifacts/svg-sprites/references/docs/ru/migration-1.md b/skills/artifacts/svg-sprites/references/docs/ru/migration-1.md deleted file mode 100644 index 39b15d8..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/migration-1.md +++ /dev/null @@ -1,96 +0,0 @@ -# Миграция с 0.1.x на 1.0 - -[← Главная](../../README_RU.md) - -Версия 1.0 разделяет локальную генерацию для React и Next.js и централизованный legacy-режим. Старый config нельзя смешивать с новым API в одном вызове CLI. - -## CLI - -CLI теперь всегда требует явный `--mode` и путь к каталогу конфигурации: - -```text -svg-sprites -→ svg-sprites --mode -``` - -Выберите mode по окружению: - -| Окружение | Mode | -|---|---| -| React + Vite | `react@vite` | -| React + Webpack 5 | `react@webpack` | -| Next.js App Router + Turbopack | `next@app/turbopack` | -| Next.js App Router + Webpack 5 | `next@app/webpack` | -| Next.js Pages Router + Turbopack | `next@pages/turbopack` | -| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | -| Централизованная старая схема | `legacy` | - -## React и Next.js - -Вместо корневого `svg-sprites.config.ts` создайте локальный `svg-sprite.config.ts` рядом с набором иконок: - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'global', - inputFolder: './icons', -}) -``` - -Для обычного React используйте `defineReactSpriteConfig`. Папку и явный список общих SVG можно объединить через `inputFolder` и `inputFiles`. - -Старые `publicPath` и `react` больше не нужны. Generated-модуль создаётся рядом с конфигом, сам добавляет `.gitignore`, а Vite, Webpack или Next.js выпускает SVG как отдельный asset с content hash. - -Компонент `` заменяется компонентом, имя которого выводится из `name`: - -```tsx - -``` - -Для просмотра иконок добавьте `` как debug-страницу приложения. Отдельный `preview.html` остаётся только в legacy-режиме. - -## Legacy-режим - -Если централизованную структуру нужно сохранить, переименуйте helper и поля формата: - -```ts -import { defineLegacyConfig } from '@gromlab/svg-sprites' - -export default defineLegacyConfig({ - output: 'public/sprites', - preview: true, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'stack', - }, - ], -}) -``` - -- `defineConfig` заменён на `defineLegacyConfig`; -- `sprites[].mode` переименован в `sprites[].format`; -- `generate` заменён на `generateLegacy`; -- `loadConfig` заменён на `loadLegacyConfig`; -- `publicPath` и генерация старого общего React-компонента удалены. - -Запуск: - -```bash -svg-sprites --mode legacy . -``` - -## Программный API - -Пакет распространяется только как ESM. Замените `require()` на `import`. - -`compileSpriteContent` теперь возвращает `Promise`, чтобы публичные декларации не требовали установки `@types/node`. В Node.js фактический результат совместим с API, принимающими `Uint8Array`. - -## После миграции - -1. Удалите старые generated-файлы и правила, которые игнорировали целиком каталог с исходными иконками. -2. Добавьте явную команду генерации перед `dev`, `build` и `typecheck`. -3. Запустите генерацию и проверку типов. -4. Проверьте все иконки и цветовые переменные через `SpriteViewer` или legacy `preview.html`. diff --git a/skills/artifacts/svg-sprites/references/docs/ru/next-app.md b/skills/artifacts/svg-sprites/references/docs/ru/next-app.md deleted file mode 100644 index 06049c6..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/next-app.md +++ /dev/null @@ -1,102 +0,0 @@ -# Next.js App Router - -[← Главная](../../README_RU.md) - -Поддерживаются два явных режима: - -| Сборщик | Mode key | Версия Next.js | -|---|---|---| -| Turbopack | `next@app/turbopack` | 16.2+ | -| Webpack 5 | `next@app/webpack` | 13.4+ | - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте sprite-модуль - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -## 3. Добавьте генерацию - -Для Turbopack: - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@app/turbopack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -Для Webpack замените mode key на `next@app/webpack`. В Next 13–15 Webpack используется обычной командой `next build`, в Next 16 — командой `next build --webpack`. - -## 4. Используйте в Server Component - -Generated-компонент не содержит `'use client'`, поэтому его можно импортировать непосредственно в `page.tsx` или `layout.tsx`: - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function Page() { - return ( -
- -
- ) -} -``` - -Next.js выпустит отдельный SVG asset с content hash. Один generated-код используется при SSR и в браузере без расхождения URL. - -## 5. Добавьте SpriteViewer - -Viewer интерактивен, поэтому для него нужна отдельная Client Component граница: - -```tsx -'use client' - -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Проверка сборщика - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -Для Next 13–15 с Webpack используйте `npx next build` без флага. - -Команда Next.js и mode key генератора должны указывать один и тот же сборщик. diff --git a/skills/artifacts/svg-sprites/references/docs/ru/next-pages.md b/skills/artifacts/svg-sprites/references/docs/ru/next-pages.md deleted file mode 100644 index 926f0ef..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/next-pages.md +++ /dev/null @@ -1,96 +0,0 @@ -# Next.js Pages Router - -[← Главная](../../README_RU.md) - -Поддерживаются два явных режима: - -| Сборщик | Mode key | Версия Next.js | -|---|---|---| -| Turbopack | `next@pages/turbopack` | 16.2+ | -| Webpack 5 | `next@pages/webpack` | 12.2+ | - -Для Next.js 12.2 требуется React 18. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте sprite-модуль - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -## 3. Добавьте генерацию - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode next@pages/webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager" - } -} -``` - -Для Next.js 16.2 с Turbopack замените mode key на `next@pages/turbopack`. - -## 4. Используйте на странице - -```tsx -import { FileManagerIcon } from '@/ui/file-manager/svg-sprite' - -export default function FilesPage() { - return -} - -export function getServerSideProps() { - return { props: {} } -} -``` - -Компонент одинаково работает при SSR, SSG и клиентских переходах. Next.js выпускает отдельный SVG asset с content hash. - -## 5. Добавьте SpriteViewer - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('@/ui/file-manager/svg-sprite/manifest'), -] - -export default function SpritesPage() { - return -} -``` - -## Проверка сборщика - -```bash -# Turbopack -npx next build --turbopack - -# Webpack 5 -npx next build --webpack -``` - -Для Next 12–15 с Webpack используйте `npx next build` без флага. - -Команда Next.js и mode key генератора должны указывать один и тот же сборщик. diff --git a/skills/artifacts/svg-sprites/references/docs/ru/programmatic-api.md b/skills/artifacts/svg-sprites/references/docs/ru/programmatic-api.md deleted file mode 100644 index 999cd01..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/programmatic-api.md +++ /dev/null @@ -1,203 +0,0 @@ -# Программный API - -[← Главная](../../README_RU.md) - -Пакет предоставляет основную Node.js точку входа и отдельный React runtime entry. Обе точки распространяются только как ESM и подключаются через `import`. - -Для разрешения `@gromlab/svg-sprites/react` в TypeScript используйте `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`. - -## Основной entry - -```ts -import { - defineNextSpriteConfig, - defineReactSpriteConfig, - generateNextSprite, - generateReactSprite, -} from '@gromlab/svg-sprites' -``` - -Основной entry не импортирует React и может использоваться в CLI, build scripts и Node.js инструментах. - -## `generateReactSprite` - -```ts -import { generateReactSprite } from '@gromlab/svg-sprites' - -const result = await generateReactSprite( - 'src/ui/file-manager/svg-sprite', - 'vite', -) -``` - -Второй аргумент обязателен: - -```ts -type ReactAssetTarget = 'vite' | 'webpack' -``` - -Результат: - -```ts -type ReactSpriteGenerationResult = { - name: string - rootDir: string - generatedDir: string - spritePath: string - manifestPath: string - iconCount: number - target: 'vite' | 'webpack' -} -``` - -```ts -console.log(result.name) -console.log(result.iconCount) -console.log(result.spritePath) -console.log(result.manifestPath) -``` - -Функция загружает `svg-sprite.config.ts` из указанного корня, компилирует SVG и безопасно обновляет managed-файлы. - -## `generateNextSprite` - -```ts -import { generateNextSprite } from '@gromlab/svg-sprites' - -const result = await generateNextSprite( - 'src/ui/file-manager/svg-sprite', - { - router: 'app', - bundler: 'turbopack', - }, -) -``` - -Доступные значения: - -```ts -type NextSpriteGenerationOptions = { - router: 'app' | 'pages' - bundler: 'turbopack' | 'webpack' -} -``` - -Результат дополнительно содержит выбранные `router`, `bundler` и полный target вида `next@app/turbopack`. - -## `defineReactSpriteConfig` - -```ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', - inputFiles: [ - '../../shared/icons/check.svg', - ], - transform: { - removeSize: true, - replaceColors: true, - addTransition: true, - }, - generatedNotice: true, -}) -``` - -`inputFolder` и `inputFiles` объединяются. Хелпер возвращает конфиг без runtime-преобразований и предоставляет TypeScript autocomplete. - -## `defineNextSpriteConfig` - -```ts -import { defineNextSpriteConfig } from '@gromlab/svg-sprites' - -export default defineNextSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', - inputFolder: './icons', -}) -``` - -Next.js использует тот же контракт конфигурации, что и React presets. - -## `generateLegacy` - -```ts -import { generateLegacy } from '@gromlab/svg-sprites' - -const results = await generateLegacy({ - output: 'public/sprites', - preview: false, - sprites: [ - { - name: 'icons', - input: 'src/assets/icons', - format: 'symbol', - }, - ], -}) -``` - -Возвращается массив: - -```ts -type SpriteResult = { - name: string - format: 'symbol' | 'stack' - spritePath: string - iconCount: number -} -``` - -Подробнее: [Legacy mode](legacy.md). - -## Низкоуровневые функции - -Основная точка входа также экспортирует: - -```ts -import { - compileSprite, - compileSpriteContent, - createShapeTransform, - generatePreview, - loadLegacyConfig, - loadReactSpriteConfig, - resolveSpriteEntry, - resolveSprites, -} from '@gromlab/svg-sprites' -``` - -Эти функции предназначены для собственного orchestration поверх существующего compiler и writer. Для стандартного использования предпочтительны `generateReactSprite` и `generateLegacy`. - -## React runtime entry - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -``` - -Типы: - -```ts -import type { - SpriteManifest, - SpriteManifestColor, - SpriteManifestIcon, - SpriteManifestLoader, - SpriteManifestModule, - SpriteViewerColorTheme, - SpriteViewerProps, - SpriteViewerSource, - SpriteViewerSources, -} from '@gromlab/svg-sprites/react' -``` - -React entry содержит `'use client'` и предназначен для debug-инструментов. Generated production-компоненты импортируются из локальных sprite-модулей приложения, а не из React entry пакета. - -`SpriteViewerProps.colorTheme` принимает `auto | light | dark`. Значение `auto` используется по умолчанию и следует `prefers-color-scheme`; для синхронизации с темой приложения передавайте вычисленное `light` или `dark`. - -## Связанные руководства - -- [React + Vite](react-vite.md) -- [React + Webpack 5](react-webpack.md) diff --git a/skills/artifacts/svg-sprites/references/docs/ru/react-vite.md b/skills/artifacts/svg-sprites/references/docs/ru/react-vite.md deleted file mode 100644 index 2ae3b39..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/react-vite.md +++ /dev/null @@ -1,116 +0,0 @@ -# React + Vite - -[← Главная](../../README_RU.md) - -Краткая инструкция по установке и использованию SVG-спрайтов в проекте на React и Vite. - -В результате вы получите типизированный React-компонент и отдельный кешируемый SVG asset. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте папку спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Поместите исходные SVG-файлы в `icons/`. - -## 3. Добавьте конфиг - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. - -Полный список опций находится в разделе [Конфигурация → React](../../README_RU.md#react). - -## 4. Добавьте генерацию в package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@vite src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated-файлы исключаются из Git, поэтому генерация должна выполняться перед `dev`, `build` и `typecheck`. - -Первый запуск: - -```bash -npm run sprite:file-manager -``` - -## 5. Используйте компонент - -Имя `file-manager` преобразуется в `FileManagerIcon`: - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -Значение `icon` проверяется TypeScript по именам файлов: - -```tsx - // допустимо - // ошибка TypeScript -``` - -Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения). - -Vite выпустит спрайт отдельным файлом вида `assets/sprite-.svg`. SVG path-данные не попадут в JavaScript. - -## 6. Добавьте debug-страницу - -После подключения иконок можно вывести все React-спрайты через `SpriteViewer`: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' -import type { SpriteManifestModule } from '@gromlab/svg-sprites/react' - -const sources = import.meta.glob( - '/src/**/svg-sprite/manifest.ts', -) - -export const IconsDebugPage = () => ( - -) -``` - -Vite автоматически найдёт generated `manifest.ts` каждого React-спрайта. Шаблон `import.meta.glob` должен быть строковым литералом, а генерация должна выполниться до запуска Vite. - -Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. - -## Если что-то не работает - -- Нет `index.ts`: запустите `npm run sprite:file-manager`. -- Viewer не видит спрайт: проверьте путь glob и наличие `manifest.ts`. -- Ошибка `Refusing to overwrite a user file`: в generated-пути находится пользовательский файл. -- Иконка не меняет цвет: используйте `color` или `--icon-color-N`. diff --git a/skills/artifacts/svg-sprites/references/docs/ru/react-webpack.md b/skills/artifacts/svg-sprites/references/docs/ru/react-webpack.md deleted file mode 100644 index 2a3c015..0000000 --- a/skills/artifacts/svg-sprites/references/docs/ru/react-webpack.md +++ /dev/null @@ -1,118 +0,0 @@ -# React + Webpack 5 - -[← Главная](../../README_RU.md) - -Краткая инструкция по установке и использованию SVG-спрайтов в проекте на React и Webpack 5. - -В результате вы получите типизированный React-компонент и отдельный SVG asset через Webpack Asset Modules. - -## 1. Установите пакет - -```bash -npm install @gromlab/svg-sprites -``` - -## 2. Создайте папку спрайта - -```text -src/ui/file-manager/svg-sprite/ -├── icons/ -│ ├── check.svg -│ └── folder.svg -└── svg-sprite.config.ts -``` - -Поместите исходные SVG-файлы в `icons/`. - -## 3. Добавьте конфиг - -```ts -// src/ui/file-manager/svg-sprite/svg-sprite.config.ts -import { defineReactSpriteConfig } from '@gromlab/svg-sprites' - -export default defineReactSpriteConfig({ - name: 'file-manager', - description: 'Иконки файлового менеджера', -}) -``` - -По умолчанию SVG берутся из `./icons`. Общие иконки из других папок можно добавить через `inputFiles`: папка и список объединяются в один спрайт. - -Полный список опций находится в разделе [Конфигурация → React](../../README_RU.md#react). - -## 4. Добавьте генерацию в package.json - -```json -{ - "scripts": { - "sprite:file-manager": "svg-sprites --mode react@webpack src/ui/file-manager/svg-sprite", - "predev": "npm run sprite:file-manager", - "prebuild": "npm run sprite:file-manager", - "pretypecheck": "npm run sprite:file-manager" - } -} -``` - -Generated-файлы исключаются из Git, поэтому генерация должна выполняться перед `dev`, `build` и `typecheck`. - -Первый запуск: - -```bash -npm run sprite:file-manager -``` - -## 5. Используйте компонент - -```tsx -import { FileManagerIcon } from './svg-sprite' - -export const OpenFolderButton = () => ( - -) -``` - -Значение `icon` проверяется TypeScript по именам файлов: - -```tsx - // допустимо - // ошибка TypeScript -``` - -Типы, способы отображения и управление цветами описаны в [основной документации](../../README_RU.md#способы-отображения). - -Webpack обработает generated `new URL('./sprite.svg', import.meta.url)` через Asset Modules и выпустит отдельный SVG asset. - -Если проект уже использует собственный SVG loader, убедитесь, что он не перехватывает generated `sprite.svg` вместо Asset Modules. - -## 6. Добавьте debug-страницу - -Webpack не поддерживает Vite API `import.meta.glob`, поэтому передайте статические loaders: - -```tsx -import { SpriteViewer } from '@gromlab/svg-sprites/react' - -const sources = [ - () => import('./ui/file-manager/svg-sprite/manifest'), - () => import('./ui/navigation/svg-sprite/manifest'), -] - -export const IconsDebugPage = () => ( - -) -``` - -Пути в `import()` должны быть строковыми литералами. Webpack создаст chunks для манифестов и свяжет их с SVG assets. - -Размещайте Viewer только на debug-маршруте или во внутреннем инструменте. - -## Если что-то не работает - -- Нет `index.ts`: запустите `npm run sprite:file-manager`. -- Viewer не загружает спрайт: проверьте путь в `import()` и наличие `manifest.ts`. -- Неверный URL asset: проверьте `output.publicPath`. -- SVG перехватывает другой loader: исключите generated sprite из несовместимого правила. - -Для Next.js используйте отдельные mode key из руководств [App Router](next-app.md) и [Pages Router](next-pages.md). diff --git a/skills/svg-sprites/build.mjs b/skills/svg-sprites/build.mjs index 4923526..6b421d9 100644 --- a/skills/svg-sprites/build.mjs +++ b/skills/svg-sprites/build.mjs @@ -1,15 +1,14 @@ import { - copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, + renameSync, rmSync, writeFileSync, } from 'node:fs' -import { tmpdir } from 'node:os' import path from 'node:path' import { fileURLToPath } from 'node:url' @@ -17,6 +16,7 @@ import configs from './skill.config.mjs' const skillDir = path.dirname(fileURLToPath(import.meta.url)) const artifactsDir = path.resolve(skillDir, '../artifacts') +const includePattern = //g const isCheck = process.argv.slice(2).includes('--check') function assertSafeRelativePath(relativePath) { @@ -30,34 +30,14 @@ function assertSafeRelativePath(relativePath) { } } -function assertInside(parentDir, childPath) { +function assertInside(parentDir, childPath, { allowSame = false } = {}) { const relativePath = path.relative(parentDir, childPath) - if (relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath))) return + if ((allowSame && relativePath === '') || (relativePath !== '' && !relativePath.startsWith('..') && !path.isAbsolute(relativePath))) { + return + } throw new Error(`Path is outside ${parentDir}: ${childPath}`) } -function validateConfig(config, outputDir) { - if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.name)) { - throw new Error(`Invalid skill name: ${config.name}`) - } - if (typeof config.description !== 'string' || config.description.trim() === '') { - throw new Error('Skill description must be a non-empty string') - } - if (!Array.isArray(config.references) || config.references.length === 0) { - throw new Error('Skill references must be a non-empty array') - } - - assertInside(artifactsDir, outputDir) - assertSafeRelativePath(config.source) - - const targets = new Set() - for (const reference of config.references) { - assertSafeRelativePath(reference.to) - if (targets.has(reference.to)) throw new Error(`Duplicate reference target: ${reference.to}`) - targets.add(reference.to) - } -} - function readRegularFile(filePath) { if (!existsSync(filePath)) throw new Error(`Source file not found: ${filePath}`) const stats = lstatSync(filePath) @@ -67,40 +47,6 @@ function readRegularFile(filePath) { return readFileSync(filePath, 'utf8') } -function renderSkill(config) { - const sourcePath = path.resolve(skillDir, config.source) - assertInside(skillDir, sourcePath) - const body = readRegularFile(sourcePath).trim() - if (body.startsWith('---')) throw new Error('Source SKILL.md must not contain frontmatter') - - return [ - '---', - `name: ${config.name}`, - `description: ${JSON.stringify(config.description)}`, - '---', - '', - ``, - '', - body, - '', - ].join('\n') -} - -function buildSkill(config, targetDir) { - rmSync(targetDir, { recursive: true, force: true }) - mkdirSync(targetDir, { recursive: true }) - writeFileSync(path.join(targetDir, 'SKILL.md'), renderSkill(config)) - - for (const reference of config.references) { - const sourcePath = path.resolve(skillDir, reference.from) - const targetPath = path.resolve(targetDir, reference.to) - assertInside(targetDir, targetPath) - readRegularFile(sourcePath) - mkdirSync(path.dirname(targetPath), { recursive: true }) - copyFileSync(sourcePath, targetPath) - } -} - function listFiles(directory, prefix = '') { const files = [] for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) { @@ -114,62 +60,256 @@ function listFiles(directory, prefix = '') { return files } +function listDirectoryFiles(directory, extensions, prefix = '') { + if (!existsSync(directory)) throw new Error(`Source directory not found: ${directory}`) + const files = [] + for (const entry of readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) { + const relativePath = path.posix.join(prefix, entry.name) + const filePath = path.join(directory, entry.name) + if (entry.isSymbolicLink()) throw new Error(`Source directory must not contain symlinks: ${filePath}`) + if (entry.isDirectory()) files.push(...listDirectoryFiles(filePath, extensions, relativePath)) + else if (entry.isFile() && (extensions.length === 0 || extensions.includes(path.extname(entry.name)))) files.push(relativePath) + else if (!entry.isFile()) throw new Error(`Unsupported source entry: ${filePath}`) + } + return files +} + +function resolveIncludes(filePath, stack = []) { + assertInside(skillDir, filePath) + if (stack.includes(filePath)) { + const cycle = [...stack, filePath].map((entry) => path.relative(skillDir, entry)).join(' -> ') + throw new Error(`Circular Markdown include: ${cycle}`) + } + + const content = readRegularFile(filePath) + if (content.startsWith('---')) { + throw new Error(`Source Markdown must not contain frontmatter: ${path.relative(skillDir, filePath)}`) + } + + return content.replace(includePattern, (match, includePath) => { + const trimmedPath = includePath.trim() + assertSafeRelativePath(trimmedPath) + if (path.extname(trimmedPath) !== '.md') { + throw new Error(`Included source must be Markdown: ${trimmedPath}`) + } + const includedFile = path.resolve(path.dirname(filePath), trimmedPath) + assertInside(skillDir, includedFile) + return `${resolveIncludes(includedFile, [...stack, filePath]).trim()}\n` + }) +} + +function renderSkill(config, document) { + const entryPath = path.resolve(skillDir, document.entry) + const body = resolveIncludes(entryPath).trim() + if (includePattern.test(body)) throw new Error(`Unresolved Markdown include: ${document.entry}`) + includePattern.lastIndex = 0 + + const frontmatter = [ + '---', + `name: ${config.name}`, + `description: ${JSON.stringify(config.description)}`, + ] + frontmatter.push('---') + + return [ + ...frontmatter, + '', + ``, + '', + body, + '', + ].join('\n') +} + +function expandCopies(config) { + const copies = [] + for (const entry of config.copy ?? []) { + if (entry.from && entry.to) { + assertSafeRelativePath(entry.to) + copies.push({ + from: path.resolve(skillDir, entry.from), + to: entry.to, + }) + continue + } + + if (entry.fromDirectory && entry.toDirectory) { + assertSafeRelativePath(entry.toDirectory) + const sourceDirectory = path.resolve(skillDir, entry.fromDirectory) + const extensions = entry.extensions ?? [] + for (const relativePath of listDirectoryFiles(sourceDirectory, extensions)) { + copies.push({ + from: path.join(sourceDirectory, relativePath), + to: path.posix.join(entry.toDirectory, relativePath), + }) + } + continue + } + + throw new Error(`Invalid copy entry in ${config.name}`) + } + return copies +} + +function prepareConfig(config) { + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.name)) { + throw new Error(`Invalid skill name: ${config.name}`) + } + if (typeof config.description !== 'string' || config.description.trim() === '') { + throw new Error('Skill description must be a non-empty string') + } + if (!Array.isArray(config.documents) || config.documents.length === 0) { + throw new Error(`Skill documents must be a non-empty array: ${config.name}`) + } + + const outputDir = path.resolve(skillDir, config.output) + assertInside(artifactsDir, outputDir) + + const documents = config.documents.map((document) => { + assertSafeRelativePath(document.entry) + assertSafeRelativePath(document.to) + const entryPath = path.resolve(skillDir, document.entry) + assertInside(skillDir, entryPath) + return { ...document, entryPath } + }) + const skillDocuments = documents.filter((document) => document.skill === true) + if (skillDocuments.length !== 1 || skillDocuments[0].to !== 'SKILL.md') { + throw new Error(`Exactly one skill document targeting SKILL.md is required: ${config.name}`) + } + + const copies = expandCopies(config) + const targets = new Set() + for (const entry of [...documents, ...copies]) { + if (targets.has(entry.to)) throw new Error(`Duplicate artifact target in ${config.name}: ${entry.to}`) + targets.add(entry.to) + } + + return { config, outputDir, documents, copies, expectedFiles: [...targets].sort() } +} + +function writeArtifactFile(targetDir, relativePath, content) { + const targetPath = path.resolve(targetDir, relativePath) + assertInside(targetDir, targetPath) + mkdirSync(path.dirname(targetPath), { recursive: true }) + writeFileSync(targetPath, content) +} + +function buildSkill(prepared, targetDir) { + mkdirSync(targetDir, { recursive: true }) + for (const document of prepared.documents) { + const content = document.skill + ? renderSkill(prepared.config, document) + : `${resolveIncludes(document.entryPath).trim()}\n` + writeArtifactFile(targetDir, document.to, content) + } + + for (const copy of prepared.copies) { + writeArtifactFile(targetDir, copy.to, readRegularFile(copy.from)) + } +} + +function withoutCodeFences(content, relativePath) { + const visibleLines = [] + let fence = null + for (const line of content.split('\n')) { + const match = line.match(/^\s*(`{3,}|~{3,})/) + if (match) { + if (!fence) fence = match[1] + else if (match[1][0] === fence[0] && match[1].length >= fence.length) fence = null + continue + } + if (!fence) visibleLines.push(line) + } + if (fence) throw new Error(`Unbalanced code fence: ${relativePath}`) + return visibleLines.join('\n') +} + +function markdownAnchors(content) { + const anchors = new Set() + const occurrences = new Map() + for (const match of withoutCodeFences(content, 'Markdown').matchAll(/^#{1,6}\s+(.+?)\s*#*$/gm)) { + const base = match[1] + .toLowerCase() + .replace(/<[^>]+>/g, '') + .replace(/[^\p{L}\p{N} _-]/gu, '') + .trim() + .replace(/\s+/g, '-') + const occurrence = occurrences.get(base) ?? 0 + occurrences.set(base, occurrence + 1) + anchors.add(occurrence === 0 ? base : `${base}-${occurrence}`) + } + return anchors +} + function validateMarkdown(skillRoot, relativePath) { const filePath = path.join(skillRoot, relativePath) const content = readFileSync(filePath, 'utf8') - const fences = content.match(/^```/gm)?.length ?? 0 - if (fences % 2 !== 0) throw new Error(`Unbalanced code fences: ${relativePath}`) + const visibleContent = withoutCodeFences(content, relativePath) + if (includePattern.test(visibleContent)) throw new Error(`Unresolved Markdown include: ${relativePath}`) + includePattern.lastIndex = 0 - for (const match of content.matchAll(/\]\(([^)]+)\)/g)) { + for (const match of visibleContent.matchAll(/\]\(([^)]+)\)/g)) { const target = match[1].trim().split(/\s+['"]/)[0] - if (!target || target.startsWith('#') || /^[a-z][a-z0-9+.-]*:/i.test(target)) continue - const targetPath = decodeURIComponent(target.split('#')[0].split('?')[0]) - const resolvedPath = path.resolve(path.dirname(filePath), targetPath) + if (!target || /^[a-z][a-z0-9+.-]*:/i.test(target)) continue + + const [rawTargetPath, rawAnchor] = target.split('#', 2) + let targetPath + let anchor + try { + targetPath = decodeURIComponent(rawTargetPath.split('?')[0]) + anchor = rawAnchor ? decodeURIComponent(rawAnchor).toLowerCase() : '' + } catch { + throw new Error(`Invalid encoded link in ${relativePath}: ${target}`) + } + + const resolvedPath = targetPath + ? path.resolve(path.dirname(filePath), targetPath) + : filePath assertInside(skillRoot, resolvedPath) - if (!existsSync(resolvedPath)) { + if (!existsSync(resolvedPath) || !lstatSync(resolvedPath).isFile()) { throw new Error(`Broken local link in ${relativePath}: ${target}`) } + if (anchor) { + const anchors = markdownAnchors(readFileSync(resolvedPath, 'utf8')) + if (!anchors.has(anchor)) throw new Error(`Broken local anchor in ${relativePath}: ${target}`) + } } } -function validateArtifact(config, skillRoot) { - const expectedFiles = [ - 'SKILL.md', - ...config.references.map((reference) => reference.to), - ].sort() +function validateArtifact(prepared, skillRoot) { const actualFiles = listFiles(skillRoot).sort() - - if (JSON.stringify(actualFiles) !== JSON.stringify(expectedFiles)) { - throw new Error(`Unexpected skill files:\n${actualFiles.join('\n')}`) + if (JSON.stringify(actualFiles) !== JSON.stringify(prepared.expectedFiles)) { + throw new Error(`Unexpected skill files in ${prepared.config.name}:\n${actualFiles.join('\n')}`) } const skill = readFileSync(path.join(skillRoot, 'SKILL.md'), 'utf8') - if (!skill.startsWith(`---\nname: ${config.name}\ndescription: `)) { - throw new Error('Generated SKILL.md has invalid frontmatter') + if (!skill.startsWith(`---\nname: ${prepared.config.name}\ndescription: `)) { + throw new Error(`Generated SKILL.md has invalid frontmatter: ${prepared.config.name}`) + } + if (/\bTODO\b/.test(skill)) throw new Error(`Generated SKILL.md contains TODO: ${prepared.config.name}`) + const visibleSkill = withoutCodeFences(skill, 'SKILL.md') + const h1Count = visibleSkill.match(/^#\s+/gm)?.length ?? 0 + if (h1Count !== 1) throw new Error(`Generated SKILL.md must contain exactly one H1: ${prepared.config.name}`) + if (prepared.config.maxSkillBytes && Buffer.byteLength(skill) > prepared.config.maxSkillBytes) { + throw new Error(`Generated SKILL.md exceeds ${prepared.config.maxSkillBytes} bytes: ${prepared.config.name}`) } - if (/\bTODO\b/.test(skill)) throw new Error('Generated SKILL.md contains TODO') for (const relativePath of actualFiles.filter((filePath) => filePath.endsWith('.md'))) { validateMarkdown(skillRoot, relativePath) } } -function compareArtifacts(expectedDir, actualDir) { - if (!existsSync(actualDir)) throw new Error(`Skill artifact is missing: ${actualDir}`) - - const expectedFiles = listFiles(expectedDir) - const actualFiles = listFiles(actualDir) - if (JSON.stringify(actualFiles) !== JSON.stringify(expectedFiles)) { - throw new Error('Committed skill file list is out of date. Run npm run build:skill') - } - - const changedFiles = expectedFiles.filter((relativePath) => { - const expected = readFileSync(path.join(expectedDir, relativePath)) - const actual = readFileSync(path.join(actualDir, relativePath)) - return !expected.equals(actual) - }) - if (changedFiles.length > 0) { - throw new Error(`Committed skill is out of date:\n${changedFiles.join('\n')}`) +function replaceDirectory(stagedDir, outputDir) { + const backupDir = `${outputDir}.backup-${process.pid}` + rmSync(backupDir, { recursive: true, force: true }) + if (existsSync(outputDir)) renameSync(outputDir, backupDir) + try { + renameSync(stagedDir, outputDir) + rmSync(backupDir, { recursive: true, force: true }) + } catch (error) { + rmSync(outputDir, { recursive: true, force: true }) + if (existsSync(backupDir)) renameSync(backupDir, outputDir) + throw error } } @@ -177,32 +317,40 @@ if (!Array.isArray(configs) || configs.length === 0) { throw new Error('Skill configs must be a non-empty array') } +const preparedConfigs = configs.map(prepareConfig) const names = new Set() -const outputs = new Set() - -for (const config of configs) { - const outputDir = path.resolve(skillDir, config.output) - validateConfig(config, outputDir) - - if (names.has(config.name)) throw new Error(`Duplicate skill name: ${config.name}`) - if (outputs.has(outputDir)) throw new Error(`Duplicate skill output: ${config.output}`) - names.add(config.name) - outputs.add(outputDir) - - if (isCheck) { - const temporaryRoot = mkdtempSync(path.join(tmpdir(), `${config.name}-skill-`)) - try { - const expectedDir = path.join(temporaryRoot, config.name) - buildSkill(config, expectedDir) - validateArtifact(config, expectedDir) - compareArtifacts(expectedDir, outputDir) - console.log(`Skill is up to date: ${path.relative(process.cwd(), outputDir)}`) - } finally { - rmSync(temporaryRoot, { recursive: true, force: true }) +for (const prepared of preparedConfigs) { + if (names.has(prepared.config.name)) throw new Error(`Duplicate skill name: ${prepared.config.name}`) + names.add(prepared.config.name) +} +for (const [index, prepared] of preparedConfigs.entries()) { + for (const other of preparedConfigs.slice(index + 1)) { + const overlap = path.relative(prepared.outputDir, other.outputDir) + const reverseOverlap = path.relative(other.outputDir, prepared.outputDir) + if (overlap === '' || (!overlap.startsWith('..') && !path.isAbsolute(overlap)) || (!reverseOverlap.startsWith('..') && !path.isAbsolute(reverseOverlap))) { + throw new Error(`Overlapping skill outputs: ${prepared.config.output} and ${other.config.output}`) } - } else { - buildSkill(config, outputDir) - validateArtifact(config, outputDir) - console.log(`Built skill: ${path.relative(process.cwd(), outputDir)}`) } } + +mkdirSync(artifactsDir, { recursive: true }) +const temporaryRoot = mkdtempSync(path.join(artifactsDir, '.skills-build-')) +try { + for (const prepared of preparedConfigs) { + const stagedDir = path.join(temporaryRoot, prepared.config.name) + buildSkill(prepared, stagedDir) + validateArtifact(prepared, stagedDir) + } + + for (const prepared of preparedConfigs) { + const stagedDir = path.join(temporaryRoot, prepared.config.name) + if (isCheck) { + console.log(`Skill sources are valid: ${prepared.config.name}`) + } else { + replaceDirectory(stagedDir, prepared.outputDir) + console.log(`Built skill: ${path.relative(process.cwd(), prepared.outputDir)}`) + } + } +} finally { + rmSync(temporaryRoot, { recursive: true, force: true }) +} diff --git a/skills/svg-sprites/skill.config.mjs b/skills/svg-sprites/skill.config.mjs index 4de3b7d..2be3967 100644 --- a/skills/svg-sprites/skill.config.mjs +++ b/skills/svg-sprites/skill.config.mjs @@ -1,35 +1,54 @@ -const references = [ - { from: '../../README.md', to: 'references/README.md' }, - { from: '../../README_RU.md', to: 'references/README_RU.md' }, - { from: '../../docs/en/react-vite.md', to: 'references/docs/en/react-vite.md' }, - { from: '../../docs/en/react-webpack.md', to: 'references/docs/en/react-webpack.md' }, - { from: '../../docs/en/next-app.md', to: 'references/docs/en/next-app.md' }, - { from: '../../docs/en/next-pages.md', to: 'references/docs/en/next-pages.md' }, - { from: '../../docs/en/legacy.md', to: 'references/docs/en/legacy.md' }, - { from: '../../docs/en/migration-1.md', to: 'references/docs/en/migration-1.md' }, - { from: '../../docs/en/programmatic-api.md', to: 'references/docs/en/programmatic-api.md' }, - { from: '../../docs/ru/react-vite.md', to: 'references/docs/ru/react-vite.md' }, - { from: '../../docs/ru/react-webpack.md', to: 'references/docs/ru/react-webpack.md' }, - { from: '../../docs/ru/next-app.md', to: 'references/docs/ru/next-app.md' }, - { from: '../../docs/ru/next-pages.md', to: 'references/docs/ru/next-pages.md' }, - { from: '../../docs/ru/legacy.md', to: 'references/docs/ru/legacy.md' }, - { from: '../../docs/ru/migration-1.md', to: 'references/docs/ru/migration-1.md' }, - { from: '../../docs/ru/programmatic-api.md', to: 'references/docs/ru/programmatic-api.md' }, +const agentReferences = [ + 'react-vite.md', + 'react-webpack.md', + 'next-app.md', + 'next-pages.md', + 'legacy.md', + 'migration-1.md', + 'programmatic-api.md', + 'complex-svg.md', +] + +function documents(language) { + return [ + { entry: `src/${language}/SKILL.md`, to: 'SKILL.md', skill: true }, + ...agentReferences.map((file) => ({ + entry: `src/${language}/references/${file}`, + to: `references/${file}`, + })), + ] +} + +const upstream = [ + { from: '../../README.md', to: 'references/upstream/README.md' }, + { from: '../../README_RU.md', to: 'references/upstream/README_RU.md' }, + { + fromDirectory: '../../docs/en', + toDirectory: 'references/upstream/docs/en', + extensions: ['.md'], + }, + { + fromDirectory: '../../docs/ru', + toDirectory: 'references/upstream/docs/ru', + extensions: ['.md'], + }, ] export default [ { name: 'svg-sprites', - description: 'Use when configuring, generating, migrating, or troubleshooting SVG sprites with @gromlab/svg-sprites. Triggers: SVG sprite, svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, icon="...", --icon-color-N, generated component, or an icon missing from preview or autocomplete. Do NOT use for favicons, raster images, icon fonts, choosing an icon set, or inline SVG without sprites.', - source: 'src/SKILL.md', + description: 'Use only when configuring, generating, migrating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, defineLegacyConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.', output: '../artifacts/svg-sprites', - references, + maxSkillBytes: 48_000, + documents: documents('en'), + copy: upstream, }, { name: 'svg-sprites-ru', - description: 'Используй при настройке, генерации, миграции или диагностике SVG-спрайтов через @gromlab/svg-sprites. Триггеры: SVG sprite, SVG-спрайт, svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, icon="...", --icon-color-N, generated-компонент или иконка не появилась в превью и автодополнении. НЕ используй для favicon, растровых изображений, icon fonts, выбора набора иконок или inline SVG без спрайтов.', - source: 'src/SKILL_RU.md', + description: 'Используй только при настройке, изменении, миграции или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, svg-sprites.config.ts, defineReactSpriteConfig, defineNextSpriteConfig, defineLegacyConfig, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.', output: '../artifacts/svg-sprites-ru', - references, + maxSkillBytes: 48_000, + documents: documents('ru'), + copy: upstream, }, ] diff --git a/skills/svg-sprites/src/SKILL.md b/skills/svg-sprites/src/SKILL.md deleted file mode 100644 index e3e868c..0000000 --- a/skills/svg-sprites/src/SKILL.md +++ /dev/null @@ -1,57 +0,0 @@ -# SVG Sprites - -## Purpose - -Use this skill when working with `@gromlab/svg-sprites`: initial setup, adding and reusing icons, generating React components, integrating `SpriteViewer`, migrating legacy configurations, and troubleshooting errors. - -Do not impose a specific directory architecture on the project. First inspect the existing `package.json`, sprite configuration, framework, router, and bundler. - -## Workflow - -1. Identify the existing mode and do not mix its API with another mode. -2. For React, choose `react@vite` or `react@webpack` and open the corresponding reference. -3. For Next.js, identify the App Router or Pages Router, then Turbopack or Webpack, and open the corresponding reference. -4. For an existing `svg-sprites.config.ts` with multiple sprites, use the legacy documentation. Do not migrate such a project unless explicitly requested. -5. Inspect local scripts and run generation before `dev`, `build`, and `typecheck` when generated files are not committed to Git. -6. After changing a configuration or SVG file, run generation followed by the available type check or project build. - -## React And Next.js Rules - -- Use a local `svg-sprite.config.ts` and the appropriate config helper: `defineReactSpriteConfig` or `defineNextSpriteConfig`. -- Do not manually edit `generated/`, `index.ts`, `manifest.ts`, or the generator-created `.gitignore`. -- Source SVG names become valid values for the `icon` prop; use the generated component and its public types instead of deep imports. -- Combine the local folder with `inputFiles` when multiple sprites need a shared icon. Do not create unnecessary copies of the same SVG. -- In Next.js, generated components work in Server Components, SSR, and SSG. Do not add `'use client'` only for an icon. -- Keep the sprite as an external bundler asset: do not move SVG path data into JavaScript or manually place the generated file in `public`. - -## Colors And Transformations - -- By default, the generator removes `width` and `height`, replaces supported `fill` and `stroke` values with CSS variables, and adds transitions. -- For a monochrome icon, control `color` first; for a multicolor icon, use `--icon-color-N`. -- Do not promise automatic color replacement inside external stylesheets, gradients, patterns, filters, or `url(#...)` values without checking the result. -- Page CSS variables work with ``, but do not propagate into `` or `background-image`. - -## Preview - -For React and Next.js, add `` as a separate debug page in the application. Pass sprite manifests or lazy loaders to it. The Viewer supports search, light and dark themes, color controls, and React, SVG, IMG, and CSS examples. - -`SpriteViewer` is a client-side debug tool imported from `@gromlab/svg-sprites/react`; production icon components do not depend on it. - -## Troubleshooting - -- If an icon name is missing from autocomplete, check the input folder and `inputFiles`, then rerun generation. -- If two files have the same icon name, resolve the conflict instead of implicitly selecting one file. -- If the generator refuses to overwrite a file, do not remove the protection marker or bypass the writer: move the user file or choose another sprite directory. -- If an asset fails to load, first confirm that the CLI mode matches the project's actual bundler and that its asset pipeline handles the generated SVG. -- If the project uses the old API, check the installed package version and the legacy reference before making changes. - -## References - -- [Main documentation and API](./references/README.md) -- [React + Vite](./references/docs/en/react-vite.md) -- [React + Webpack 5](./references/docs/en/react-webpack.md) -- [Next.js App Router](./references/docs/en/next-app.md) -- [Next.js Pages Router](./references/docs/en/next-pages.md) -- [Legacy mode](./references/docs/en/legacy.md) -- [Migrating from 0.1.x](./references/docs/en/migration-1.md) -- [Programmatic API](./references/docs/en/programmatic-api.md) diff --git a/skills/svg-sprites/src/SKILL_RU.md b/skills/svg-sprites/src/SKILL_RU.md deleted file mode 100644 index e1a1a78..0000000 --- a/skills/svg-sprites/src/SKILL_RU.md +++ /dev/null @@ -1,57 +0,0 @@ -# SVG Sprites - -## Назначение - -Используй этот скил для работы с `@gromlab/svg-sprites`: первичной настройки, добавления и переиспользования иконок, генерации React-компонентов, подключения `SpriteViewer`, миграции legacy-конфигурации и диагностики ошибок. - -Не навязывай проекту конкретную архитектуру каталогов. Сначала изучи существующие `package.json`, конфигурацию спрайта, используемый фреймворк, роутер и сборщик. - -## Рабочий алгоритм - -1. Определи существующий режим и не смешивай его API с другим режимом. -2. Для React выбери `react@vite` или `react@webpack` и открой соответствующий reference. -3. Для Next.js определи App Router или Pages Router, затем Turbopack или Webpack, и открой соответствующий reference. -4. Для существующего `svg-sprites.config.ts` с несколькими спрайтами используй legacy-документацию. Не мигрируй такой проект без явного запроса. -5. Изучи локальные scripts и добавляй генерацию перед `dev`, `build` и `typecheck`, если generated-файлы не хранятся в Git. -6. После изменения конфигурации или SVG запусти генерацию, затем доступную проверку типов или сборку проекта. - -## Правила React и Next.js - -- Используй локальный `svg-sprite.config.ts` и подходящий config helper: `defineReactSpriteConfig` или `defineNextSpriteConfig`. -- Не редактируй `generated/`, `index.ts`, `manifest.ts` и созданный генератором `.gitignore` вручную. -- Имена исходных SVG становятся допустимыми значениями prop `icon`; используй generated-компонент и его публичные типы вместо deep imports. -- Объединяй локальную папку и `inputFiles`, когда общая иконка нужна нескольким спрайтам. Не создавай копии одного SVG без необходимости. -- В Next.js generated-компонент можно использовать в Server Components, SSR и SSG. Не добавляй `'use client'` только ради иконки. -- Спрайт должен оставаться внешним asset сборщика: не переносить SVG path-данные в JavaScript и не класть generated-файл вручную в `public`. - -## Цвета и трансформации - -- По умолчанию генератор удаляет `width` и `height`, заменяет поддерживаемые `fill` и `stroke` на CSS-переменные и добавляет transitions. -- Для монохромной иконки сначала управляй `color`; для многоцветной используй `--icon-color-N`. -- Не обещай автоматическую замену цветов внутри внешних stylesheets, gradients, patterns, filters и значений `url(#...)` без проверки результата. -- CSS-переменные страницы работают при ``, но не проникают внутрь `` и `background-image`. - -## Превью - -Для React и Next.js подключай `` отдельной debug-страницей приложения. Передай ему manifests или lazy loaders спрайтов. Viewer поддерживает поиск, светлую и тёмную темы, настройку цветов и примеры React, SVG, IMG и CSS. - -`SpriteViewer` является клиентским debug-инструментом и импортируется из `@gromlab/svg-sprites/react`; production-компоненты иконок от него не зависят. - -## Диагностика - -- Если имя иконки отсутствует в автодополнении, проверь входную папку и `inputFiles`, затем перезапусти генерацию. -- Если два файла имеют одинаковое имя иконки, устрани конфликт вместо выбора одного файла неявно. -- Если генератор отказывается перезаписывать файл, не удаляй защитный marker и не обходи writer: перенеси пользовательский файл или выбери другой каталог спрайта. -- Если asset не загружается, сначала проверь соответствие CLI mode реальному сборщику проекта и обработку generated SVG его asset pipeline. -- Если проект использует старый API, сверь установленную версию пакета и legacy reference перед изменениями. - -## References - -- [Основная документация и API](./references/README_RU.md) -- [React + Vite](./references/docs/ru/react-vite.md) -- [React + Webpack 5](./references/docs/ru/react-webpack.md) -- [Next.js App Router](./references/docs/ru/next-app.md) -- [Next.js Pages Router](./references/docs/ru/next-pages.md) -- [Legacy mode](./references/docs/ru/legacy.md) -- [Миграция с 0.1.x](./references/docs/ru/migration-1.md) -- [Программный API](./references/docs/ru/programmatic-api.md) diff --git a/skills/svg-sprites/src/en/SKILL.md b/skills/svg-sprites/src/en/SKILL.md new file mode 100644 index 0000000..81b117a --- /dev/null +++ b/skills/svg-sprites/src/en/SKILL.md @@ -0,0 +1,19 @@ +# @gromlab/svg-sprites + + + + + + + + + + +## References to open as needed + +- For React + Vite, open [React + Vite](./references/react-vite.md); for React with a custom Webpack 5 setup, open [React + Webpack](./references/react-webpack.md). +- For Next.js, open the guide for the [App Router](./references/next-app.md) or [Pages Router](./references/next-pages.md), then select the section for the bundler actually in use. +- If the project has `svg-sprites.config.ts`, open [legacy mode](./references/legacy.md); when upgrading from `0.1.x`, also open the [migration guide](./references/migration-1.md). +- To invoke the generator from Node.js, open the [programmatic API](./references/programmatic-api.md). +- For gradients, filters, `url(#...)`, unusual colors, and `viewBox` issues, open [complex SVGs](./references/complex-svg.md). +- Use the complete user documentation as a secondary source: [package README](./references/upstream/README.md). diff --git a/skills/svg-sprites/src/en/core/00-package-overview.md b/skills/svg-sprites/src/en/core/00-package-overview.md new file mode 100644 index 0000000..7d98aa2 --- /dev/null +++ b/skills/svg-sprites/src/en/core/00-package-overview.md @@ -0,0 +1,15 @@ +## What the package does + +`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles the project's SVGs into an external cacheable sprite asset and, for React/Next.js, creates a typed component, a list of valid names, and a debug manifest. + +The package supports multiple independent sprites in one project. Each selected directory containing `svg-sprite.config.ts` describes one sprite and gets its own: + +- SVG asset; +- icon name types; +- React component; +- production entry point `index.ts`; +- debug entry point `manifest.ts`. + +The project determines how many sprite directories exist and where they live. For example, `name: 'file-manager'` produces `FileManagerIcon`, while another directory with `name: 'navigation'` produces a separate `NavigationIcon`. The names `FileManagerIcon` and `fileManagerIconNames` used below are examples of the API for one possible sprite, not fixed package exports. + +Generated production components do not import `@gromlab/svg-sprites` at runtime. For routine generation, run the latest CLI through `npx`; install the package in the project only when `SpriteViewer`, the programmatic API, or a local config helper is required. diff --git a/skills/svg-sprites/src/en/core/10-mode-selection.md b/skills/svg-sprites/src/en/core/10-mode-selection.md new file mode 100644 index 0000000..b45030c --- /dev/null +++ b/skills/svg-sprites/src/en/core/10-mode-selection.md @@ -0,0 +1,29 @@ +## Selecting a mode + +Select exactly one supported mode key: + +| Project | Mode key | +|---|---| +| React + Vite | `react@vite` | +| React + Webpack 5 | `react@webpack` | +| Next.js App Router + Turbopack | `next@app/turbopack` | +| Next.js App Router + Webpack 5 | `next@app/webpack` | +| Next.js Pages Router + Turbopack | `next@pages/turbopack` | +| Next.js Pages Router + Webpack 5 | `next@pages/webpack` | +| Existing shared config | `legacy` | + +Do not use the incomplete `react`, `next@app`, or `next@pages` keys, the future `standalone` mode, or a mode for a different bundler. The CLI always requires a mode and exactly one path to a configuration directory: + +```bash +npx --yes @gromlab/svg-sprites@latest --mode +``` + +Do not pass multiple paths, a glob, or the path to the config file itself. For multiple modern sprites, create a separate command for each directory. + +Identify legacy mode by its contract, not by the number of sprites: + +- `svg-sprites.config.ts` in the supplied root, with top-level `output` and a non-empty `sprites` array, is a legacy config even if it contains only one entry; +- each legacy entry uses `name`, `input`, and optional `format: 'stack' | 'symbol'`; the `mode` field is deprecated; +- a local `svg-sprite.config.ts` in a single sprite directory, with `name`, `description`, `inputFolder`, `inputFiles`, `transform`, and `generatedNotice`, belongs to the React/Next API even when the application has many such sprites. + +Do not migrate a legacy config without an explicit request. Do not combine `defineLegacyConfig` or the `output`/`sprites` fields with `defineReactSpriteConfig` or `defineNextSpriteConfig`. diff --git a/skills/svg-sprites/src/en/core/20-project-inspection.md b/skills/svg-sprites/src/en/core/20-project-inspection.md new file mode 100644 index 0000000..b9c7cad --- /dev/null +++ b/skills/svg-sprites/src/en/core/20-project-inspection.md @@ -0,0 +1,22 @@ +## Inspecting the project + +Establish the project's actual contract before making changes: + +1. Read the root `package.json`, lockfile, and workspace configuration; identify the framework, bundler, and existing commands. +2. Find `svg-sprite.config.ts`, `svg-sprites.config.ts`, commands containing `svg-sprites`, and imports of generated components. +3. For React, determine whether the project uses Vite or Webpack 5 from its scripts and configuration. For Next.js, separately determine the App/Pages Router and the bundler used by the actual `dev`/`build` commands. +4. Check existing `predev`, `prebuild`, `pretypecheck`, and orchestration scripts. Do not overwrite them. +5. For a new sprite, choose a target directory without imposing a particular application layer or architecture. +6. Check TypeScript and alias settings. Package subpath exports require TypeScript 5+ with `moduleResolution: 'bundler'`, `'node16'`, or `'nodenext'`. + +All paths in a modern config are relative to the directory containing `svg-sprite.config.ts`: + +- `inputFolder` defaults to `./icons`; +- `inputFiles` contains additional relative paths to individual SVGs and is merged with `inputFolder`; +- the same absolute file is deduplicated, but different files with the same icon basename cause an error; +- scanning `inputFolder` is shallow: only immediate files ending in `.svg` are read, and nested directories are not traversed; +- an explicitly configured `inputFolder` that does not exist is an error; +- if `inputFolder` is omitted, `./icons` does not exist, and `inputFiles` is non-empty, generation uses only `inputFiles`; +- an empty final input set, a missing file, or a path that does not point to an `.svg` file is an error. + +Do not copy a shared SVG into several folders: add its relative path to `inputFiles` in every sprite that needs it. If a recursive structure is required, list the files explicitly or reorganize the sources; the generator does not perform recursive scanning. diff --git a/skills/svg-sprites/src/en/core/30-react-next-setup.md b/skills/svg-sprites/src/en/core/30-react-next-setup.md new file mode 100644 index 0000000..44e636e --- /dev/null +++ b/skills/svg-sprites/src/en/core/30-react-next-setup.md @@ -0,0 +1,51 @@ +## Setting up React or Next.js + +Choose a target directory for one sprite. It may live next to a feature, in a shared icons directory, or anywhere else that follows the project's conventions. The following structure is only an example: + +```text +src/ui/file-manager/svg-sprite/ +├── icons/ +│ ├── check.svg +│ └── folder.svg +└── svg-sprite.config.ts +``` + +One `svg-sprite.config.ts` creates one independent sprite. For multiple sets, choose multiple directories and assign each a unique `name`. + +When running through `npx`, the config may export a plain object without importing the package: + +```ts +export default { + name: 'file-manager', + description: 'File manager icons', + inputFolder: './icons', + inputFiles: ['../../shared/icons/close.svg'], +} +``` + +The object contract is the same for React and Next.js. If the package is already installed, the object may be wrapped in `defineReactSpriteConfig(...)` or `defineNextSpriteConfig(...)` for autocomplete. A helper is not required for normal CLI generation. + +`name` must begin with an ASCII letter and use kebab-case. The example `file-manager` produces `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames`. Another sprite gets its own names. If `name` is omitted, the generator derives it from the directory. + +Add a separate command with the selected mode key and one path: + +```json +{ + "scripts": { + "sprite:file-manager": "npx --yes @gromlab/svg-sprites@latest --mode react@vite src/ui/file-manager/svg-sprite", + "sprites": "npm run sprite:file-manager" + } +} +``` + +For Next.js, substitute the full key, for example `next@app/turbopack`. For multiple sprites, add one `sprite:` command per directory and invoke them sequentially from `sprites`. + +Generated files are excluded from Git by default, so run `sprites` before any process that needs `index.ts`, the types, or the asset. Add it to `predev`, `prebuild`, and, when a `typecheck` script exists, `pretypecheck`. + +If a lifecycle script is missing, create it. If it already exists, preserve its command and append generation with `&&`; for example, change `"prebuild": "npm run lint"` to `"prebuild": "npm run lint && npm run sprites"`. Never replace an existing `pre*` script with generation alone, and never create a duplicate JSON key. + +Run the first generation manually: + +```bash +npm run sprites +``` diff --git a/skills/svg-sprites/src/en/core/40-generated-contract.md b/skills/svg-sprites/src/en/core/40-generated-contract.md new file mode 100644 index 0000000..89c2165 --- /dev/null +++ b/skills/svg-sprites/src/en/core/40-generated-contract.md @@ -0,0 +1,34 @@ +## Generated directory contract + +After generation, the selected directory has this structure: + +```text +svg-sprite/ +├── icons/ # user-owned sources +├── svg-sprite.config.ts # user-owned config +├── .gitignore # managed by the generator +├── index.ts # public production entry point +├── manifest.ts # separate debug entry point +└── generated/ + ├── .svg-sprites.manifest.json # ownership registry + ├── react-component.tsx + ├── sprite.svg + ├── styles.module.css + └── types.ts +``` + +Edit only the source SVGs and `svg-sprite.config.ts`. Do not manually change `.gitignore`, `index.ts`, `manifest.ts`, or anything in `generated/`: the next generation will overwrite them. Import the production API from the root `index.ts`, not through a deep import from `generated/`. + +The generator owns only the listed root files and immediate files in `generated/`. The `.svg-sprites.manifest.json` registry allows stale generated files to be removed, but the writer refuses to overwrite or delete any file without a generated marker. Do not remove the marker, bypass the refusal, or replace generated paths with symlinks: move the user-owned file or choose a different directory. + +The public entry point exports the component, props/style types, a readonly array of names, and the icon-name union type. `manifest.ts` contains the URL, target, icon list, and icon metadata for debug tools and is not imported by the production component. + +The sprite remains a separate content-hashed asset; SVG path data is not embedded in JavaScript: + +- `react@vite` generates a static `sprite.svg?no-inline` import, preventing Vite from inlining it; +- React Webpack 5 and all Next modes generate `new URL('./sprite.svg', import.meta.url).href`, which must be processed by the selected bundler's Asset Modules; +- a custom Webpack SVG loader must not intercept the generated `sprite.svg`; +- in Next mode, the generated component does not contain `'use client'` and works in Server Components, SSR, and SSG; do not add a client boundary solely for an icon; +- the Next build command and mode key must agree: Turbopack with `.../turbopack`, Webpack with `.../webpack`. + +Do not move the generated sprite into `public` or rewrite its URL manually. When changing the router or bundler, regenerate the sprite with the new full mode key. diff --git a/skills/svg-sprites/src/en/core/50-usage-and-colors.md b/skills/svg-sprites/src/en/core/50-usage-and-colors.md new file mode 100644 index 0000000..c839e29 --- /dev/null +++ b/skills/svg-sprites/src/en/core/50-usage-and-colors.md @@ -0,0 +1,73 @@ +## Usage, accessibility, and colors + +The component name depends on the specific sprite's `name`. These examples use `name: 'file-manager'`, so the generated component is called `FileManagerIcon`. For `name: 'navigation'`, use the generated `NavigationIcon`. + +Import the component from the root of its sprite directory. `width` and `height` are optional: ordinary CSS classes can control the size. + +```tsx +import { FileManagerIcon } from './svg-sprite' + +export const OpenButton = () => ( + +) +``` + +```css +.icon { + width: 24px; + height: 24px; + color: #4b5563; +} +``` + +`icon` accepts exact source filenames without `.svg`; an unknown name is a TypeScript error. For names that are not safe SVG IDs, the generator preserves the public name but creates an internal stable hash ID, so do not construct a fragment URL from the name manually. + +By default, the component renders `` and accepts standard SVG attributes: optional `width`/`height`, `className`, `style`, `role`, `aria-*`, and event handlers. With `wrapped={true}`, the root becomes a ``, props apply to the span, and the inner SVG fills the wrapper. This is convenient when a class controls both size and colors: + +```tsx +