mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
feat: add new registry for preview design
This commit is contained in:
@@ -1,10 +1,14 @@
|
|||||||
# План: составные типы параметров и полная типизация pipeline
|
# План: составные типы параметров и полная типизация pipeline
|
||||||
|
|
||||||
> Статус: **в реализации.** Фаза 0 (фундамент) — сделана. Следующее: Фаза 1 —
|
> Статус: **в реализации.** Фаза 0 (фундамент) ✔, Фаза 1 (рабочий инструмент
|
||||||
> один полноценный рабочий инструмент в preview, затем масштабирование.
|
> в 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 строки),
|
> Ключевые файлы нового registry: `web/src/lib/registry-new/{types,geometry,alpha,index}.ts`,
|
||||||
> `web/src/lib/registry-schema.ts`.
|
> `web/src/lib/registry-schema.ts`. Старый `web/src/lib/registry.ts` разбит по
|
||||||
|
> категориям в `web/src/lib/registry/` (см. `registry.ts` — тонкий баррель).
|
||||||
|
|
||||||
## Ключевая стратегия: параллельная сборка, старый UI не трогаем
|
## Ключевая стратегия: параллельная сборка, старый UI не трогаем
|
||||||
|
|
||||||
@@ -30,6 +34,31 @@
|
|||||||
инструментов, старый UI ничего не замечает.
|
инструментов, старый UI ничего не замечает.
|
||||||
- Удаление старого UI (после перехода) попутно удаляет и старый `ParamDef[]`
|
- Удаление старого 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<P>` с обязательным `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 |
|
- `ParamDef` — плоский union из 6 примитивов (`number | slider | select |
|
||||||
@@ -227,7 +256,11 @@ Typed field builders + `Field<T>` + `toolSchema<P>()` + `ToolSchema<P>` —
|
|||||||
(лишний/неверный/отсутствующий ключ ловит TS), тесты, svelte-check без новых
|
(лишний/неверный/отсутствующий ключ ловит TS), тесты, svelte-check без новых
|
||||||
ошибок.
|
ошибок.
|
||||||
|
|
||||||
### Фаза 1 — ОДИН полноценный рабочий инструмент в preview (срез)
|
### Фаза 1 — ОДИН полноценный рабочий инструмент в preview (срез) ✔ (сделано)
|
||||||
|
|
||||||
|
Инструмент переехал в `registry-new` и рендерится через `SchemaToolView`
|
||||||
|
(`SchemaFields`/`SchemaPreview`); применение реально работает end-to-end до
|
||||||
|
картинки. Паттерн подтверждён. `find-contour-png` — следующий в этой фазе-2.
|
||||||
|
|
||||||
5. **Инструмент «добавить рамку» (`add-border-png`)** — сделать полностью
|
5. **Инструмент «добавить рамку» (`add-border-png`)** — сделать полностью
|
||||||
рабочим в preview:
|
рабочим в preview:
|
||||||
@@ -249,10 +282,13 @@ Typed field builders + `Field<T>` + `toolSchema<P>()` + `ToolSchema<P>` —
|
|||||||
|
|
||||||
Одиночные/небольшие инструменты, где `interface Params` + `schema` не требуют
|
Одиночные/небольшие инструменты, где `interface Params` + `schema` не требуют
|
||||||
составных типов. Каждый — отдельный маленький diff (~15-30 строк), тем самым
|
составных типов. Каждый — отдельный маленький diff (~15-30 строк), тем самым
|
||||||
проверенным в Фазе 1 паттерном:
|
проверенным в Фазе 1 паттерном. **Переезжают в `registry-new`.**
|
||||||
|
|
||||||
6. add-stroke-png (color + slider)
|
Переведено: `add-border-png` (Фаза 1), `add-stroke-png`. Следующий —
|
||||||
7. find-contour-png (color + slider)
|
`find-contour-png`.
|
||||||
|
|
||||||
|
6. ~~add-stroke-png (color + slider)~~ → переведён в `registry-new` ✔
|
||||||
|
7. find-contour-png (color + slider) — **следующий**
|
||||||
8. convert-png-to-jpg (color + slider)
|
8. convert-png-to-jpg (color + slider)
|
||||||
9. convert-png-to-webp (slider)
|
9. convert-png-to-webp (slider)
|
||||||
10. remove-color-from-png (color + slider)
|
10. remove-color-from-png (color + slider)
|
||||||
@@ -335,9 +371,8 @@ Typed field builders + `Field<T>` + `toolSchema<P>()` + `ToolSchema<P>` —
|
|||||||
|
|
||||||
- **Старый UI** (`ParamForm.svelte`, группа `(old)/`) — **не трогаем.** Работает
|
- **Старый UI** (`ParamForm.svelte`, группа `(old)/`) — **не трогаем.** Работает
|
||||||
на `ParamDef[]` как раньше.
|
на `ParamDef[]` как раньше.
|
||||||
- **Новый UI** (kit/`ParamControl.svelte`, новый pipeline) — читает
|
- **Новый UI** (kit/`SchemaToolView` + `SchemaFields` + `SchemaPreview`, читает
|
||||||
`ToolSchema<P>`; поддержка составных типов и пер-инструмент layout
|
`ToolSchema<P>`) — сейчас рендерит поля по схеме (number/slider/color).
|
||||||
(`schema.layout`).
|
|
||||||
- `tool-views.ts` — со временем поглощается registry (preview/lede/layout →
|
- `tool-views.ts` — со временем поглощается registry (preview/lede/layout →
|
||||||
meta инструмента). Отдельный шаг, НЕ блокирует типизацию params.
|
meta инструмента). Отдельный шаг, НЕ блокирует типизацию params.
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
type ToolSchema,
|
type ToolSchema,
|
||||||
} from "$lib/registry-schema";
|
} from "$lib/registry-schema";
|
||||||
import { executeStep } from "$lib/tools/executor";
|
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 SchemaFields from "$lib/components/kit/SchemaFields.svelte";
|
||||||
import SchemaPreview from "$lib/components/kit/SchemaPreview.svelte";
|
import SchemaPreview from "$lib/components/kit/SchemaPreview.svelte";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CATEGORIES } from "../categories";
|
import { CATEGORIES } from "../categories";
|
||||||
import { TOOLS, type ToolEntry } from "../registry";
|
import { TOOLS, type ToolEntry } from "../registry-new";
|
||||||
|
|
||||||
export type PreviewGroup = {
|
export type PreviewGroup = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -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<AddStrokeParams>({
|
||||||
|
color: field.color({ default: "#ff0000" }),
|
||||||
|
thickness: field.slider({ min: 1, max: 10, step: 1, default: 3 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const addStroke: ToolEntry<AddStrokeParams> = {
|
||||||
|
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];
|
||||||
@@ -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<AddBorderParams>({
|
||||||
|
thickness: field.number({ min: 1, max: 500, step: 1, default: 5 }),
|
||||||
|
color: field.color({ default: "#000000" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const addBorder: ToolEntry<AddBorderParams> = {
|
||||||
|
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];
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -13,11 +13,11 @@ export type ToolEntry<P = Record<string, unknown>> = {
|
|||||||
description: string;
|
description: string;
|
||||||
category: CategoryId;
|
category: CategoryId;
|
||||||
schema: ToolSchema<P>;
|
schema: ToolSchema<P>;
|
||||||
run: (img: PixelImage, params: P) => Promise<PixelImage> | PixelImage;
|
run(img: PixelImage, params: P): Promise<PixelImage> | PixelImage;
|
||||||
generate?: (params: P) => Promise<PixelImage> | PixelImage;
|
generate?(params: P): Promise<PixelImage> | PixelImage;
|
||||||
toText?: (img: PixelImage, params: P) => Promise<string> | string;
|
toText?(img: PixelImage, params: P): Promise<string> | string;
|
||||||
runFromText?: (text: string, params: P) => Promise<PixelImage> | PixelImage;
|
runFromText?(text: string, params: P): Promise<PixelImage> | PixelImage;
|
||||||
textToText?: (text: string) => Promise<string> | string;
|
textToText?(text: string): Promise<string> | string;
|
||||||
preview?: (img: PixelImage, params: P) => Promise<PixelImage> | PixelImage;
|
preview?(img: PixelImage, params: P): Promise<PixelImage> | PixelImage;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
import SettingsFooter from "$lib/components/kit/SettingsFooter.svelte";
|
import SettingsFooter from "$lib/components/kit/SettingsFooter.svelte";
|
||||||
import ToolCard from "$lib/components/kit/ToolCard.svelte";
|
import ToolCard from "$lib/components/kit/ToolCard.svelte";
|
||||||
import Button from "$lib/components/kit/ui/Button.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";
|
import { Search } from "@lucide/svelte";
|
||||||
|
|
||||||
let query = $state("");
|
let query = $state("");
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getTool } from "$lib/registry";
|
import { getTool } from "$lib/registry-new";
|
||||||
import ToolView from "$lib/components/kit/ToolView.svelte";
|
|
||||||
import SchemaToolView from "$lib/components/kit/SchemaToolView.svelte";
|
import SchemaToolView from "$lib/components/kit/SchemaToolView.svelte";
|
||||||
import EmptyState from "$lib/components/kit/EmptyState.svelte";
|
import EmptyState from "$lib/components/kit/EmptyState.svelte";
|
||||||
import { SlidersHorizontal as ToolIcon } from "@lucide/svelte";
|
import { SlidersHorizontal as ToolIcon } from "@lucide/svelte";
|
||||||
@@ -26,10 +25,8 @@
|
|||||||
icon={ToolIcon}
|
icon={ToolIcon}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else if tool.schema}
|
|
||||||
<SchemaToolView {tool} />
|
|
||||||
{:else}
|
{:else}
|
||||||
<ToolView {tool} />
|
<SchemaToolView {tool} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TOOLS } from "$lib/registry";
|
import { TOOLS } from "$lib/registry-new";
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user