From a19d3db929f5d840900f1190c9efd10bb0595548 Mon Sep 17 00:00:00 2001 From: Ku6epXBOCTuK Date: Fri, 4 Sep 2026 17:37:55 +0500 Subject: [PATCH] feat: add new registry for preview design --- docs/plan-composite-params.md | 57 +++++++++++++++---- .../lib/components/kit/SchemaToolView.svelte | 2 +- web/src/lib/preview/catalog.ts | 2 +- web/src/lib/registry-new/alpha.ts | 25 ++++++++ web/src/lib/registry-new/geometry.ts | 33 +++++++++++ web/src/lib/registry-new/index.ts | 14 +++++ web/src/lib/registry-new/types.ts | 12 ++-- web/src/routes/preview/+page.svelte | 2 +- .../routes/preview/tools/[id]/+page.svelte | 7 +-- web/src/routes/preview/tools/[id]/+page.ts | 2 +- 10 files changed, 130 insertions(+), 26 deletions(-) create mode 100644 web/src/lib/registry-new/alpha.ts create mode 100644 web/src/lib/registry-new/geometry.ts create mode 100644 web/src/lib/registry-new/index.ts diff --git a/docs/plan-composite-params.md b/docs/plan-composite-params.md index 08bb127..7f54986 100644 --- a/docs/plan-composite-params.md +++ b/docs/plan-composite-params.md @@ -1,10 +1,14 @@ # План: составные типы параметров и полная типизация pipeline -> Статус: **в реализации.** Фаза 0 (фундамент) — сделана. Следующее: Фаза 1 — -> один полноценный рабочий инструмент в preview, затем масштабирование. +> Статус: **в реализации.** Фаза 0 (фундамент) ✔, Фаза 1 (рабочий инструмент +> в preview) ✔, Фаза 2 — начата: построен отдельный **новый registry** +> (`web/src/lib/registry-new/`) с первыми инструментами (`add-border-png`, +> `add-stroke-png`), preview переключён на него. Следующее: продолжить Фазу 2 — +> следующий инструмент `find-contour-png` в новый registry. > -> Файлы: `web/src/lib/registry.ts` (3918 строк), `web/src/lib/preview/tool-views.ts` (193 строки), -> `web/src/lib/registry-schema.ts`. +> Ключевые файлы нового registry: `web/src/lib/registry-new/{types,geometry,alpha,index}.ts`, +> `web/src/lib/registry-schema.ts`. Старый `web/src/lib/registry.ts` разбит по +> категориям в `web/src/lib/registry/` (см. `registry.ts` — тонкий баррель). ## Ключевая стратегия: параллельная сборка, старый UI не трогаем @@ -30,6 +34,31 @@ инструментов, старый UI ничего не замечает. - Удаление старого UI (после перехода) попутно удаляет и старый `ParamDef[]` +### Разделение registry по UI (новое решение, Фаза 2+) + +Помимо двух схем, registry тоже разделён по UI (по факту миграции): + +- **Старый registry** (`web/src/lib/registry.ts` + `registry/` + + `registry-helpers.ts`) — работает на старом UI, использует `ParamDef[]` и + `tool-views.ts`. **Не трогаем**; идёт под удаление вместе со старым UI. +- **Новый registry** (`web/src/lib/registry-new/`) — строится **с нуля «как надо»**: + `ToolEntry

` с обязательным `schema`, типизированный `run`, **без** `ParamDef[]` + и **без** связи со старым. Импортирует core-функции (`expandCanvas`, + `strokeImage`, …) напрямую. Наполняется **по-инструментно** по мере миграции + (не разовым переводом всех 130). Preview показывает только переведённые + инструменты. + +Preview (`catalog.ts`, маршруты `preview/**`, `SchemaToolView`, `SchemaFields`) +импортируют из `$lib/registry-new`. После удаления старого UI `registry-new/` +переименуется в `registry`. + +**Нюанс по worker:** `executor.worker.ts` резолвит инструменты по `id` в +**старом** registry. Для инструментов, существующих в обоих (сейчас +`add-border/add-stroke`), worker работает. Для новых инструментов, которых нет в +старом registry, worker не найдёт → `executeStep` откатится на `runDirect` +(fallback в `catch`) — это корректно, но стоит поправить worker на новый registry +при дальнейшей миграции. + ## Проблема (текущая) - `ParamDef` — плоский union из 6 примитивов (`number | slider | select | @@ -227,7 +256,11 @@ Typed field builders + `Field` + `toolSchema

()` + `ToolSchema

` — (лишний/неверный/отсутствующий ключ ловит TS), тесты, svelte-check без новых ошибок. -### Фаза 1 — ОДИН полноценный рабочий инструмент в preview (срез) +### Фаза 1 — ОДИН полноценный рабочий инструмент в preview (срез) ✔ (сделано) + +Инструмент переехал в `registry-new` и рендерится через `SchemaToolView` +(`SchemaFields`/`SchemaPreview`); применение реально работает end-to-end до +картинки. Паттерн подтверждён. `find-contour-png` — следующий в этой фазе-2. 5. **Инструмент «добавить рамку» (`add-border-png`)** — сделать полностью рабочим в preview: @@ -249,10 +282,13 @@ Typed field builders + `Field` + `toolSchema

()` + `ToolSchema

` — Одиночные/небольшие инструменты, где `interface Params` + `schema` не требуют составных типов. Каждый — отдельный маленький diff (~15-30 строк), тем самым -проверенным в Фазе 1 паттерном: +проверенным в Фазе 1 паттерном. **Переезжают в `registry-new`.** -6. add-stroke-png (color + slider) -7. find-contour-png (color + slider) +Переведено: `add-border-png` (Фаза 1), `add-stroke-png`. Следующий — +`find-contour-png`. + +6. ~~add-stroke-png (color + slider)~~ → переведён в `registry-new` ✔ +7. find-contour-png (color + slider) — **следующий** 8. convert-png-to-jpg (color + slider) 9. convert-png-to-webp (slider) 10. remove-color-from-png (color + slider) @@ -335,9 +371,8 @@ Typed field builders + `Field` + `toolSchema

()` + `ToolSchema

` — - **Старый UI** (`ParamForm.svelte`, группа `(old)/`) — **не трогаем.** Работает на `ParamDef[]` как раньше. -- **Новый UI** (kit/`ParamControl.svelte`, новый pipeline) — читает - `ToolSchema

`; поддержка составных типов и пер-инструмент layout - (`schema.layout`). +- **Новый UI** (kit/`SchemaToolView` + `SchemaFields` + `SchemaPreview`, читает + `ToolSchema

`) — сейчас рендерит поля по схеме (number/slider/color). - `tool-views.ts` — со временем поглощается registry (preview/lede/layout → meta инструмента). Отдельный шаг, НЕ блокирует типизацию params. diff --git a/web/src/lib/components/kit/SchemaToolView.svelte b/web/src/lib/components/kit/SchemaToolView.svelte index 50c2437..7c1a097 100644 --- a/web/src/lib/components/kit/SchemaToolView.svelte +++ b/web/src/lib/components/kit/SchemaToolView.svelte @@ -8,7 +8,7 @@ type ToolSchema, } from "$lib/registry-schema"; import { executeStep } from "$lib/tools/executor"; - import type { ToolEntry } from "$lib/registry"; + import type { ToolEntry } from "$lib/registry-new"; import SchemaFields from "$lib/components/kit/SchemaFields.svelte"; import SchemaPreview from "$lib/components/kit/SchemaPreview.svelte"; diff --git a/web/src/lib/preview/catalog.ts b/web/src/lib/preview/catalog.ts index 0440a1c..ffd3760 100644 --- a/web/src/lib/preview/catalog.ts +++ b/web/src/lib/preview/catalog.ts @@ -1,5 +1,5 @@ import { CATEGORIES } from "../categories"; -import { TOOLS, type ToolEntry } from "../registry"; +import { TOOLS, type ToolEntry } from "../registry-new"; export type PreviewGroup = { id: string; diff --git a/web/src/lib/registry-new/alpha.ts b/web/src/lib/registry-new/alpha.ts new file mode 100644 index 0000000..3637e75 --- /dev/null +++ b/web/src/lib/registry-new/alpha.ts @@ -0,0 +1,25 @@ +import type { ToolEntry } from "./types"; +import { field, toolSchema } from "../registry-schema"; +import { strokeImage } from "../core/morphology"; + +interface AddStrokeParams { + color: string; + thickness: number; +} + +export const addStrokeSchema = toolSchema({ + color: field.color({ default: "#ff0000" }), + thickness: field.slider({ min: 1, max: 10, step: 1, default: 3 }), +}); + +const addStroke: ToolEntry = { + id: "add-stroke-png", + title: "Outline PNG", + description: + "Adds a colored ring outline around the opaque content with the chosen thickness.", + category: "alpha", + schema: addStrokeSchema, + run: (img, p) => strokeImage(img, p.thickness, p.color), +}; + +export const alphaEntries = [addStroke]; diff --git a/web/src/lib/registry-new/geometry.ts b/web/src/lib/registry-new/geometry.ts new file mode 100644 index 0000000..0df4f5b --- /dev/null +++ b/web/src/lib/registry-new/geometry.ts @@ -0,0 +1,33 @@ +import type { ToolEntry } from "./types"; +import { field, toolSchema } from "../registry-schema"; +import { expandCanvas } from "../core/geometry"; + +interface AddBorderParams { + thickness: number; + color: string; +} + +export const addBorderSchema = toolSchema({ + thickness: field.number({ min: 1, max: 500, step: 1, default: 5 }), + color: field.color({ default: "#000000" }), +}); + +const addBorder: ToolEntry = { + id: "add-border-png", + title: "Add border to PNG", + description: + "Draws a colored frame of the chosen thickness around the image.", + category: "geometry", + schema: addBorderSchema, + run: (img, p) => + expandCanvas( + img, + p.thickness, + p.thickness, + p.thickness, + p.thickness, + p.color, + ), +}; + +export const geometryEntries = [addBorder]; diff --git a/web/src/lib/registry-new/index.ts b/web/src/lib/registry-new/index.ts new file mode 100644 index 0000000..1ee46eb --- /dev/null +++ b/web/src/lib/registry-new/index.ts @@ -0,0 +1,14 @@ +import type { ToolEntry } from "./types"; +import { geometryEntries } from "./geometry"; +import { alphaEntries } from "./alpha"; + +export type { ToolEntry } from "./types"; + +export const TOOLS: ToolEntry[] = [ + ...geometryEntries, + ...alphaEntries, +] as unknown as ToolEntry[]; + +export function getTool(id: string): ToolEntry | undefined { + return TOOLS.find((tool) => tool.id === id); +} diff --git a/web/src/lib/registry-new/types.ts b/web/src/lib/registry-new/types.ts index 6f7bfb8..a5299bf 100644 --- a/web/src/lib/registry-new/types.ts +++ b/web/src/lib/registry-new/types.ts @@ -13,11 +13,11 @@ export type ToolEntry

> = { description: string; category: CategoryId; schema: ToolSchema

; - run: (img: PixelImage, params: P) => Promise | PixelImage; - generate?: (params: P) => Promise | PixelImage; - toText?: (img: PixelImage, params: P) => Promise | string; - runFromText?: (text: string, params: P) => Promise | PixelImage; - textToText?: (text: string) => Promise | string; - preview?: (img: PixelImage, params: P) => Promise | PixelImage; + run(img: PixelImage, params: P): Promise | PixelImage; + generate?(params: P): Promise | PixelImage; + toText?(img: PixelImage, params: P): Promise | string; + runFromText?(text: string, params: P): Promise | PixelImage; + textToText?(text: string): Promise | string; + preview?(img: PixelImage, params: P): Promise | PixelImage; icon?: string; }; diff --git a/web/src/routes/preview/+page.svelte b/web/src/routes/preview/+page.svelte index 61cf116..092e6c1 100644 --- a/web/src/routes/preview/+page.svelte +++ b/web/src/routes/preview/+page.svelte @@ -4,7 +4,7 @@ import SettingsFooter from "$lib/components/kit/SettingsFooter.svelte"; import ToolCard from "$lib/components/kit/ToolCard.svelte"; import Button from "$lib/components/kit/ui/Button.svelte"; - import { TOOLS } from "$lib/registry"; + import { TOOLS } from "$lib/registry-new"; import { Search } from "@lucide/svelte"; let query = $state(""); diff --git a/web/src/routes/preview/tools/[id]/+page.svelte b/web/src/routes/preview/tools/[id]/+page.svelte index 8d3af18..15d8d33 100644 --- a/web/src/routes/preview/tools/[id]/+page.svelte +++ b/web/src/routes/preview/tools/[id]/+page.svelte @@ -1,6 +1,5 @@