feat: update tools types

This commit is contained in:
2026-09-10 08:21:20 +05:00
parent e9bc59f818
commit 292e53c9c7
3 changed files with 80 additions and 13 deletions
+2
View File
@@ -112,6 +112,8 @@ export const en: Dict = {
},
errors: {
noImageRun: "This tool does not process images",
sourceRequired: "Upload an image first",
textRequired: "Enter text first",
workerFailed: "Worker execution failed",
workerUnavailable: "Worker is unavailable",
notFound: "Tool not found",
+2
View File
@@ -113,6 +113,8 @@ export const ru: Dict = {
},
errors: {
noImageRun: "Этот инструмент не обрабатывает изображения",
sourceRequired: "Сначала загрузите изображение",
textRequired: "Сначала введите текст",
workerFailed: "Ошибка исполнения в воркере",
workerUnavailable: "Воркер недоступен",
notFound: "Инструмент не найден",
+76 -13
View File
@@ -2,6 +2,7 @@ import type { CategoryId } from "../preview/categories";
import type { ToolSchema } from "../registry-schema";
import type { PixelImage } from "../core/types";
import type { OutputMime } from "../core/io";
import { ToolError } from "../core/errors";
/** Формат скачивания, отличный от PNG (bmp/jpeg/webp). */
export interface OutputFormat {
@@ -10,10 +11,37 @@ export interface OutputFormat {
qualityParamId?: string;
}
/** Чем управляется инструмент. `image` — требуется входное изображение. */
export const INPUT_MODES = {
image: "image",
text: "text",
none: "none",
} as const;
export type InputMode = (typeof INPUT_MODES)[keyof typeof INPUT_MODES];
/** Тип результата: картинка (по умолчанию), большой текст или короткий вердикт. */
export const RESULT_KINDS = {
image: "image",
text: "text",
verdict: "verdict",
} as const;
export type ResultKind = (typeof RESULT_KINDS)[keyof typeof RESULT_KINDS];
/** Единый вход инструмента. `source`/`text` присутствуют строго по `input`. */
export interface ToolContext<P> {
params: P;
source?: PixelImage;
text?: string;
}
export type ToolResult = PixelImage | string;
/**
* Инструмент нового registry: полностью типизирован на `Params`, схема —
* обязательна (единый источник дефолтов/валидации/UI). Старого `ParamDef[]`
* нет — это отдельная система от старого UI.
* обязательна (единый источник дефолтов/валидации/UI). У каждого инструмента
* ровно один метод `run(ctx)`; контракт входа декларируется через `input`
* (обязательное поле), а результат — через `result`. Отдельных методов
* `generate`/`runFromText`/`toText`/`textToText` нет.
*/
export type ToolEntry<P = Record<string, unknown>> = {
id: string;
@@ -21,20 +49,55 @@ export type ToolEntry<P = Record<string, unknown>> = {
description: string;
category: CategoryId;
schema: ToolSchema<P>;
/** Чем управляется инструмент: входным изображением, текстом или ничем. */
input: InputMode;
/** Тип результата: картинка (по умолчанию), большой текст или короткий вердикт. */
result?: ResultKind;
/** Единственный метод исполнения — и для генераторов, и для трансформаторов. */
run(ctx: ToolContext<P>): Promise<ToolResult> | ToolResult;
/** Требует DOM (canvas/document); превью-executor запускает напрямую, не в worker. */
domOnly?: boolean;
/** Чем управляется инструмент: файлом (по умолчанию), текстом или ничем (генератор). */
input?: "file" | "text" | "none";
/** Тип результата: картинка (по умолчанию), большой текст или короткий вердикт. */
result?: "image" | "text" | "verdict";
/** Применение к входному изображению. Для чистых генераторов отсутствует. */
run?(img: PixelImage, params: P): Promise<PixelImage> | PixelImage;
generate?(params: P): Promise<PixelImage> | PixelImage;
toText?(img: PixelImage, params: P): Promise<string> | string;
runFromText?(text: string, params: P): Promise<PixelImage> | PixelImage;
textToText?(text: string): Promise<string> | string;
preview?(img: PixelImage, params: P): Promise<PixelImage> | PixelImage;
/** Формат/качество скачивания результата; по умолчанию — PNG. */
output?: OutputFormat;
icon?: string;
};
/** Гарантирует наличие входного изображения для трансформаторов. */
export function requireSource<P>(ctx: ToolContext<P>): PixelImage {
if (!ctx.source) {
throw new ToolError("errors.sourceRequired");
}
return ctx.source;
}
/** Гарантирует наличие текстового входа для text-инструментов. */
export function requireText<P>(ctx: ToolContext<P>): string {
if (!ctx.text?.trim()) {
throw new ToolError("errors.textRequired");
}
return ctx.text;
}
/**
* Обёртка для image-to-image инструментов: подставляет `src` и `params`
* из контекста. Позволяет оставить тело инструмента в виде `(img, p) => …`.
*/
export function imgTool<P>(
fn: (img: PixelImage, params: P) => ToolResult,
): (ctx: ToolContext<P>) => ToolResult {
return (ctx) => fn(requireSource(ctx), ctx.params);
}
/** Обёртка для генераторов: инструменту нужны только параметры. */
export function genTool<P>(
fn: (params: P) => ToolResult,
): (ctx: ToolContext<P>) => ToolResult {
return (ctx) => fn(ctx.params);
}
/** Обёртка для text-to-image инструментов: подставляет `text` и `params`. */
export function textGen<P>(
fn: (text: string, params: P) => ToolResult,
): (ctx: ToolContext<P>) => ToolResult {
return (ctx) => fn(requireText(ctx), ctx.params);
}