From 1e47fdaf4a42097ef63ef813687b329ce055f40d Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Sat, 12 Sep 2026 14:41:17 +0500 Subject: [PATCH] docs: add split into parts plan --- docs/plan-split-into-parts.md | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 docs/plan-split-into-parts.md diff --git a/docs/plan-split-into-parts.md b/docs/plan-split-into-parts.md new file mode 100644 index 0000000..70c99ce --- /dev/null +++ b/docs/plan-split-into-parts.md @@ -0,0 +1,80 @@ +# План: инструмент «Разрезать PNG на части» (split-into-parts-png) + +> Статус: **в работе** (2026-09). Первый потребитель механизма «результат = +> набор файлов» (1 → many). Общий процесс таких инструментов — отдельная задача +> в `docs/backlog.md`. + +## Цель + +Пользователь загружает одну картинку, выбирает число столбцов и строк, картинка +разрезается на равномерные части. Результат — набор PNG-файлов, скачивается +ZIP-архивом. + +## Решения + +- **Неравномерное деление:** канвас дополняется прозрачным до кратного размера + (`pieceW = ceil(w / cols)`, `newW = pieceW * cols`), все части строго равные, + картинка покрывается целиком. +- **Дефолт схемы:** 2 × 2. +- **Zip-библиотека:** `fflate` (zero deps, малый размер), скачивание из UI-слоя + (`encode()` требует DOM). +- **Результат инструмента:** `result: "files"` — терминальный тип, в пайплайн + (следующий шаг) не передаётся. + +## Шаги + +1. **Зависимость:** `fflate` в `web/package.json`. +2. **`registry/types.ts`:** `RESULT_KINDS.files`, тип `ToolImageFile`, + `FileResult`, расширение `ToolResult = PixelImage | string | FileResult`. +3. **`core/geometry.ts`:** `splitToParts(img, columns, rows)` — padding + сетка + через `crop`. +4. **`web/src/lib/zip.ts` (новый):** `downloadZip(files, zipName)` через + `zipSync`. +5. **`core/errors.ts`:** ключ `errors.tooManyParts` (лимит cols·rows ≤ 1000). +6. **`registry/geometry.ts`:** схема (`columns`, `rows`, дефолт 2×2, min 1, + max 50) + - entry `split-into-parts-png` в `geometryEntries`. +7. **Executor:** сериализация/десериализация `FileResult` в `executor.worker.ts` + и тип ветки в `executor.ts`. +8. **UI:** `SchemaToolView.svelte` (`fileResult`, Download → zip), + `SchemaPreview.svelte` (canDownload для files), `SchemaResultTile.svelte` + (сетка-превью частей, мета «N parts / ZIP»). +9. **i18n:** записи `split-into-parts-png` в `ru.ts` и `en.ts` + поисковые + строки в `matching.ts` при необходимости. +10. **Тесты:** юнит `splitToParts`, registry (result: "files"), i18n coverage. +11. **Docs:** `docs/backlog.md` (задача «процесс 1 → many и many → 1», отметить + пункт 11 «Мультифайловый вывод»), `docs/tools-map.md` (перенос из идей). + +## Файлы + +| Файл | Изменение | +| ------------------------------------------------ | ----------------------------------------------------------------- | +| `web/package.json` | `+ fflate` (dependencies) | +| `web/src/lib/registry/types.ts` | `RESULT_KINDS.files`, `ToolImageFile`, `FileResult`, `ToolResult` | +| `web/src/lib/core/geometry.ts` | `splitToParts()` | +| `web/src/lib/zip.ts` | **новый** — `downloadZip()` | +| `web/src/lib/core/errors.ts` | `errors.tooManyParts` | +| `web/src/lib/registry/geometry.ts` | schema + `splitPartsTool` | +| `web/src/lib/executor/executor.worker.ts` | ветка `FileResult` | +| `web/src/lib/executor/executor.ts` | тип ветки `FileResult` | +| `web/src/lib/components/SchemaToolView.svelte` | `fileResult`, download → zip | +| `web/src/lib/components/SchemaPreview.svelte` | props `fileResult`, `canDownload` | +| `web/src/lib/components/SchemaResultTile.svelte` | сетка частей, «N parts» | +| `web/src/lib/i18n/ru.ts`, `en.ts` | записи инструмента | + +## Нейминг файлов в ZIP + +`part-01-01.png` … `part--.png` (row, col нумеруются с 1, ведущий ноль +для выравнивания по числу строк/столбцов). + +## Верификация + +```bash +pnpm --dir web exec svelte-check --tsconfig ./tsconfig.json +pnpm --dir web test +pnpm --dir web lint +``` + +Ручная проверка: PNG не кратного размера (например 101×77), 3×2 → 6 частей +одинакового размера, скачивается ZIP; на кратном размере (100×80, 3×2) padding += 0.