486 lines
17 KiB
TypeScript

import { z } from "zod";
import type {
CanvasCapabilityClientAction,
CanvasCapabilityDefinition,
CanvasCapabilityId,
CanvasCapabilityManifestEntry,
SelectedImageToolbarActionKey,
} from "./types";
const emptyArgsSchema = z.object({}).passthrough();
const promptArgsSchema = z
.object({
prompt: z.string().trim().min(1).max(8_000).optional(),
})
.passthrough();
const resizeArgsSchema = z
.object({
width: z.number().int().positive().max(16_384).optional(),
height: z.number().int().positive().max(16_384).optional(),
aspectRatio: z.string().trim().max(32).optional(),
})
.passthrough();
const extendArgsSchema = z
.object({
prompt: z.string().trim().min(1).max(8_000).optional(),
xScale: z.number().min(1.1).max(4).optional(),
yScale: z.number().min(1.1).max(4).optional(),
})
.passthrough();
const angleArgsSchema = z
.object({
horizontalAngle: z.number().min(0).max(360).optional(),
verticalAngle: z.number().min(-30).max(90).optional(),
zoom: z.number().min(0).max(10).optional(),
})
.passthrough();
const cropArgsSchema = z
.object({
x: z.number().min(0).optional(),
y: z.number().min(0).optional(),
width: z.number().positive().optional(),
height: z.number().positive().optional(),
})
.passthrough();
const transformArgsSchema = z
.object({
rotate: z.number().min(-360).max(360).optional(),
flipHorizontal: z.boolean().optional(),
flipVertical: z.boolean().optional(),
})
.passthrough();
type DefinitionInput = Omit<
CanvasCapabilityDefinition,
"selection" | "requiresRemoteImage" | "confirmation" | "agent" | "mcp"
> & {
selection?: Partial<CanvasCapabilityDefinition["selection"]>;
requiresRemoteImage?: boolean;
confirmation?: CanvasCapabilityDefinition["confirmation"];
agent?: Partial<CanvasCapabilityDefinition["agent"]>;
mcp?: Partial<CanvasCapabilityDefinition["mcp"]>;
};
function defineCapability(input: DefinitionInput): CanvasCapabilityDefinition {
const mcpToolName = input.id.replaceAll(".", "_");
return {
...input,
requiresRemoteImage: input.requiresRemoteImage ?? true,
selection: {
nodeTypes: ["image"],
min: input.selection?.min ?? 1,
...(input.selection?.max ? { max: input.selection.max } : {}),
},
confirmation: input.confirmation ?? "never",
agent: {
exposed: input.agent?.exposed ?? true,
aliases: input.agent?.aliases ?? [],
toolName: input.agent?.toolName ?? mcpToolName,
},
mcp: {
exposed: input.mcp?.exposed ?? true,
toolName: input.mcp?.toolName ?? mcpToolName,
},
};
}
export const CANVAS_CAPABILITY_REGISTRY = [
defineCapability({
id: "image.add_to_chat",
title: "添加到对话",
description: "将选中图片加入当前 Agent 对话上下文。",
clientAction: "add-to-chat",
toolbarSettingKey: "imageAddToChat",
executionMode: "local",
risk: "read",
modelPolicy: "none",
requiresRemoteImage: false,
inputSchema: emptyArgsSchema,
agent: { exposed: false },
mcp: { exposed: false },
}),
defineCapability({
id: "image.pick_object",
title: "识物",
description: "进入图片点选识物模式,并把识别结果加入对话上下文。",
clientAction: "pick-object",
toolbarActionKey: "pickObject",
toolbarSettingKey: "imagePickObject",
executionMode: "interactive",
risk: "read",
modelPolicy: "feature-setting",
confirmation: "interactive",
inputSchema: z
.object({ x: z.number().min(0).max(1).optional(), y: z.number().min(0).max(1).optional() })
.passthrough(),
agent: { aliases: ["识物", "识别物品", "选择物体"] },
}),
defineCapability({
id: "image.enhance",
title: "高清增强",
description: "使用系统配置的增强服务提升图片清晰度。",
clientAction: "enhance",
toolbarActionKey: "enhance",
toolbarSettingKey: "imageEnhance",
executionMode: "headless",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.imageEnhance",
inputSchema: z.object({ scaleFactor: z.number().min(1).max(8).optional() }).passthrough(),
agent: { toolName: "enhanceImage", aliases: ["高清", "增强", "变清晰"] },
}),
defineCapability({
id: "image.remove_background",
title: "抠图去背",
description: "调用系统抠图能力移除图片背景。",
clientAction: "rmbg",
toolbarActionKey: "removeBackground",
toolbarSettingKey: "imageRemoveBackground",
executionMode: "headless",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.removeBackground",
inputSchema: promptArgsSchema,
agent: { toolName: "removeBackground", aliases: ["抠图", "去背景", "透明背景"] },
}),
defineCapability({
id: "image.ai_wash",
title: "AI洗图",
description: "按管理员配置的预设、档位和模型执行 AI 洗图。",
clientAction: "ai-wash",
toolbarActionKey: "aiWashQuick",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.aiWash",
confirmation: "interactive",
inputSchema: z
.object({
prompt: z.string().trim().min(1).max(8_000).optional(),
presetId: z.string().trim().min(1).optional(),
modelId: z.string().trim().min(1).optional(),
levels: z.record(z.number().int().nonnegative()).optional(),
})
.passthrough(),
agent: { aliases: ["洗图", "AI洗图", "重新洗图"] },
}),
defineCapability({
id: "image.text_edit",
title: "图片改字",
description: "识别图片文字并打开可编辑文字面板。",
clientAction: "text-edit",
toolbarActionKey: "textEdit",
toolbarSettingKey: "imageTextEdit",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.textEdit",
confirmation: "interactive",
inputSchema: promptArgsSchema,
agent: { aliases: ["改字", "修改图片文字", "替换文案"] },
}),
defineCapability({
id: "image.box_cutout",
title: "框选抠图",
description: "框选图片区域后提取指定主体。",
clientAction: "box-cutout",
toolbarActionKey: "boxCutout",
toolbarSettingKey: "imageBoxCutout",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
confirmation: "interactive",
inputSchema: cropArgsSchema,
agent: { toolName: "boxCutout", aliases: ["框选抠图", "抠出这部分"] },
}),
defineCapability({
id: "image.local_edit",
title: "局部编辑",
description: "按框选区域与区域提示词执行局部重绘。",
clientAction: "local-edit",
toolbarActionKey: "localEdit",
toolbarSettingKey: "imageLocalEdit",
executionMode: "hybrid",
risk: "external",
modelPolicy: "agent-selected",
confirmation: "interactive",
inputSchema: promptArgsSchema,
agent: { toolName: "localEditImage", aliases: ["局部编辑", "局部重绘"] },
}),
defineCapability({
id: "image.scale_adjust",
title: "比例调整",
description: "按目标框等比缩放和移动选中主体。",
clientAction: "scale-adjust",
toolbarSettingKey: "imageLocalEdit",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
confirmation: "interactive",
inputSchema: cropArgsSchema,
agent: { aliases: ["比例调整", "缩放主体", "调整主体位置"] },
}),
defineCapability({
id: "image.remove_watermark",
title: "去水印",
description: "使用系统配置的服务移除水印和画面瑕疵。",
clientAction: "watermark",
toolbarActionKey: "removeWatermark",
toolbarSettingKey: "imageRemoveWatermark",
executionMode: "headless",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.removeWatermark",
inputSchema: promptArgsSchema,
agent: { toolName: "removeWatermark", aliases: ["去水印", "去印", "清理水印"] },
}),
defineCapability({
id: "image.resize",
title: "尺寸调整",
description: "调整图片尺寸、比例和主体位置。",
clientAction: "resize",
toolbarActionKey: "resize",
toolbarSettingKey: "imageResize",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
confirmation: "interactive",
inputSchema: resizeArgsSchema,
agent: { toolName: "resizeImage", aliases: ["调整尺寸", "改比例", "改分辨率"] },
}),
defineCapability({
id: "image.multi_angle",
title: "多视角",
description: "按水平角、俯仰角和景别生成主体新视角。",
clientAction: "angle",
toolbarActionKey: "multiAngle",
toolbarSettingKey: "imageMultiAngle",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
confirmation: "interactive",
inputSchema: angleArgsSchema,
agent: { toolName: "angleEditImage", aliases: ["多视角", "换角度", "旋转视角"] },
}),
defineCapability({
id: "image.generate_3d_model",
title: "生成3D模型",
description: "使用系统配置的 Tripo 模型从图片生成 3D 模型。",
clientAction: "generate-3d-model",
toolbarActionKey: "generate3dModel",
toolbarSettingKey: "imageGenerate3dModel",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.tripo3d",
confirmation: "interactive",
inputSchema: z
.object({
modelId: z.string().trim().min(1).optional(),
prompt: z.string().trim().min(1).max(8_000).optional(),
})
.passthrough(),
agent: { aliases: ["生成3D模型", "转成3D模型", "Tripo"] },
}),
defineCapability({
id: "image.generate_3d_world",
title: "生成3D世界",
description: "使用系统配置的 World Labs 模型生成可漫游 3D 世界。",
clientAction: "generate-3d-world",
toolbarActionKey: "generate3dWorld",
toolbarSettingKey: "imageGenerate3dWorld",
executionMode: "hybrid",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.worldLabs3d",
confirmation: "interactive",
inputSchema: z
.object({
modelId: z.string().trim().min(1).optional(),
prompt: z.string().trim().min(1).max(8_000).optional(),
})
.passthrough(),
agent: { aliases: ["生成3D世界", "生成场景", "World Labs"] },
}),
defineCapability({
id: "image.layer_split",
title: "图层拆分",
description: "将图片拆分为可独立编辑的图层节点。",
clientAction: "layer-split",
toolbarActionKey: "layerSplit",
toolbarSettingKey: "imageLayerSplit",
executionMode: "headless",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.layerSplit",
inputSchema: emptyArgsSchema,
agent: { aliases: ["图层拆分", "拆图层", "分层"] },
}),
defineCapability({
id: "image.extend",
title: "扩图",
description: "向指定方向扩展画面并补全内容。",
clientAction: "extend",
toolbarActionKey: "extend",
toolbarSettingKey: "imageExtend",
executionMode: "headless",
risk: "external",
modelPolicy: "feature-setting",
modelSettingKey: "canvas.extend",
inputSchema: extendArgsSchema,
agent: { toolName: "extendImage", aliases: ["扩图", "补边", "扩展画面"] },
}),
defineCapability({
id: "image.crop",
title: "裁剪",
description: "打开裁剪面板,或按完整坐标直接裁剪。",
clientAction: "crop",
toolbarActionKey: "crop",
toolbarSettingKey: "imageCrop",
executionMode: "hybrid",
risk: "write",
modelPolicy: "none",
requiresRemoteImage: false,
confirmation: "interactive",
inputSchema: cropArgsSchema,
agent: { aliases: ["裁剪", "剪裁", "截取画面"] },
}),
defineCapability({
id: "image.adjust",
title: "画面调整",
description: "打开亮度、对比度、饱和度等图片调整面板。",
clientAction: "adjust",
toolbarActionKey: "adjust",
toolbarSettingKey: "imageAdjust",
executionMode: "interactive",
risk: "write",
modelPolicy: "none",
requiresRemoteImage: false,
confirmation: "interactive",
inputSchema: z.record(z.number()).optional().default({}),
agent: { aliases: ["调色", "调整亮度", "调整对比度"] },
}),
defineCapability({
id: "image.flip_rotate",
title: "翻转与旋转",
description: "翻转或旋转选中图片。",
clientAction: "flip-rotate",
toolbarActionKey: "flipRotate",
toolbarSettingKey: "imageFlipRotate",
executionMode: "hybrid",
risk: "write",
modelPolicy: "none",
requiresRemoteImage: false,
confirmation: "interactive",
inputSchema: transformArgsSchema,
agent: { aliases: ["翻转", "旋转图片", "镜像"] },
}),
defineCapability({
id: "image.prompt_regenerate",
title: "提示词重绘",
description: "按新的提示词和系统生图模型重绘选中图片。",
clientAction: "prompt-regenerate",
toolbarActionKey: "promptRegenerate",
toolbarSettingKey: "imagePromptRegenerate",
executionMode: "hybrid",
risk: "external",
modelPolicy: "agent-selected",
confirmation: "interactive",
inputSchema: promptArgsSchema,
agent: { toolName: "editTargetImage", aliases: ["重绘", "按提示词重做"] },
}),
defineCapability({
id: "image.note",
title: "添加批注",
description: "给图片添加可作为 Agent 上下文的批注。",
clientAction: "note",
toolbarActionKey: "note",
toolbarSettingKey: "imageNote",
executionMode: "hybrid",
risk: "write",
modelPolicy: "none",
requiresRemoteImage: false,
confirmation: "interactive",
inputSchema: z.object({ text: z.string().trim().max(1_000).optional() }).passthrough(),
agent: { aliases: ["添加批注", "备注", "标注"] },
}),
defineCapability({
id: "image.rename",
title: "重命名",
description: "修改选中图片节点标题。",
clientAction: "rename",
toolbarSettingKey: "imageRename",
executionMode: "hybrid",
risk: "write",
modelPolicy: "none",
requiresRemoteImage: false,
inputSchema: z.object({ title: z.string().trim().min(1).max(120).optional() }).passthrough(),
agent: { aliases: ["重命名", "改名", "修改标题"] },
}),
defineCapability({
id: "image.download",
title: "下载图片",
description: "下载选中图片资源。",
clientAction: "download",
toolbarSettingKey: "imageDownload",
executionMode: "local",
risk: "read",
modelPolicy: "none",
requiresRemoteImage: false,
inputSchema: emptyArgsSchema,
agent: { exposed: false },
mcp: { exposed: false },
}),
defineCapability({
id: "image.delete",
title: "删除图片",
description: "从当前画布删除选中图片节点。",
clientAction: "delete",
toolbarSettingKey: "imageDelete",
executionMode: "local",
risk: "destructive",
modelPolicy: "none",
requiresRemoteImage: false,
confirmation: "always",
inputSchema: z.object({ confirmed: z.literal(true).optional() }).passthrough(),
agent: { aliases: ["删除图片", "移除图片"] },
}),
] as const satisfies readonly CanvasCapabilityDefinition[];
const capabilityById = new Map<CanvasCapabilityId, CanvasCapabilityDefinition>(
CANVAS_CAPABILITY_REGISTRY.map((capability) => [capability.id, capability]),
);
const capabilityByClientAction = new Map<CanvasCapabilityClientAction, CanvasCapabilityDefinition>(
CANVAS_CAPABILITY_REGISTRY.map((capability) => [capability.clientAction, capability]),
);
const capabilityByToolbarAction = new Map<
SelectedImageToolbarActionKey,
CanvasCapabilityDefinition
>(
CANVAS_CAPABILITY_REGISTRY.flatMap((capability) =>
capability.toolbarActionKey ? [[capability.toolbarActionKey, capability]] : [],
),
);
export function getCanvasCapability(id: CanvasCapabilityId) {
return capabilityById.get(id) ?? null;
}
export function getCanvasCapabilityByClientAction(action: CanvasCapabilityClientAction) {
return capabilityByClientAction.get(action) ?? null;
}
export function getCanvasCapabilityByToolbarAction(action: SelectedImageToolbarActionKey) {
return capabilityByToolbarAction.get(action) ?? null;
}
export function parseCanvasCapabilityArgs(id: CanvasCapabilityId, value: unknown) {
const capability = getCanvasCapability(id);
if (!capability) throw new Error(`Unknown canvas capability: ${id}`);
return capability.inputSchema.parse(value ?? {});
}
export function getCanvasCapabilityManifest(): CanvasCapabilityManifestEntry[] {
return CANVAS_CAPABILITY_REGISTRY.map(({ inputSchema: _inputSchema, ...capability }) => capability);
}