fix(canvas-agent): block video-only controls in shared image menus

This commit is contained in:
134 2026-10-20 17:50:34 +08:00
parent 311d2eed8f
commit df7e6ee0e1
5 changed files with 74 additions and 60 deletions

File diff suppressed because one or more lines are too long

View File

@ -312,23 +312,23 @@
},
{
"id": "canvas.ui.markerPanel",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 标记",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.workflowConnection",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 工作流连线",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.styleMode",
@ -382,13 +382,13 @@
},
{
"id": "canvas.ui.addImageInput",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 添加图片输入",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.addText",
@ -402,33 +402,33 @@
},
{
"id": "canvas.ui.addScriptNode",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 添加脚本节点",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.addVideoInput",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 添加视频输入",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.workflowNodeLibrary",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 视频工作流节点库",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.realtimeCanvas",
@ -462,23 +462,23 @@
},
{
"id": "canvas.ui.exportVideoCanvas",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 导出视频画布",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.importVideoCanvas",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 导入视频画布",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.newCanvas",
@ -492,13 +492,13 @@
},
{
"id": "canvas.ui.newVideoCanvas",
"workspace": "canvas",
"workspace": "video",
"title": "画布主工具栏 · 新建视频画布",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.undo",
@ -872,83 +872,83 @@
},
{
"id": "canvas.ui.videoUpload",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 上传视频",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoPlay",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 播放",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoPause",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 暂停",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoMute",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 静音",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoUnmute",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 取消静音",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoMore",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 更多操作",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoDownload",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 下载",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.videoDelete",
"workspace": "canvas",
"workspace": "video",
"title": "节点工具栏 · 删除",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.workflowDelete",
@ -1382,23 +1382,23 @@
},
{
"id": "canvas.ui.chatModeVideoActive",
"workspace": "canvas",
"workspace": "video",
"title": "对话框工具栏 · 视频",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.chatModeVideoMenu",
"workspace": "canvas",
"workspace": "video",
"title": "对话框工具栏 · 视频生成",
"authorized": true,
"authorized": false,
"previouslyExposed": false,
"execution": "interactive",
"entry": "openCanvasFeature",
"note": "操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
"note": "用户暂未授权视频画布能力;操作当前可见的真实入口;隐藏入口保持隐藏,文件选择、支付、登录、发布和发送消息由用户完成"
},
{
"id": "canvas.ui.chatAttach",

View File

@ -3,3 +3,10 @@ export const ZCODE_VIDEO_CANVAS_AUTHORIZED = false;
export function isZcodeWorkspaceAuthorized(workspace: string) {
return workspace !== "video" || ZCODE_VIDEO_CANVAS_AUTHORIZED;
}
/** Video-only controls can also appear in the image workspace's mode menus. */
export const VIDEO_ONLY_AGENT_TOOLBAR_KEYS = new Set([
"markerPanel", "workflowConnection", "addImageInput", "addScriptNode", "addVideoInput", "workflowNodeLibrary",
"exportVideoCanvas", "importVideoCanvas", "newVideoCanvas", "videoUpload", "videoPlay", "videoPause", "videoMute",
"videoUnmute", "videoMore", "videoDownload", "videoDelete", "chatModeVideoActive", "chatModeVideoMenu",
]);

View File

@ -1,4 +1,4 @@
import { isZcodeWorkspaceAuthorized } from "./authorization";
import { isZcodeWorkspaceAuthorized, VIDEO_ONLY_AGENT_TOOLBAR_KEYS } from "./authorization";
import { CANVAS_CAPABILITY_REGISTRY } from "@/lib/canvas/capabilities/registry";
import { DEFAULT_CANVAS_TOOLBAR_ITEMS } from "@/lib/canvas-toolbar-config";
import {
@ -156,7 +156,7 @@ export function getCanvasAgentAccessManifest(
...DEFAULT_CANVAS_TOOLBAR_ITEMS.map(
(item): AgentCapabilityAccess => ({
id: `canvas.ui.${item.key}`,
workspace: "canvas",
workspace: VIDEO_ONLY_AGENT_TOOLBAR_KEYS.has(item.key) ? "video" : "canvas",
title: `${item.group} · ${item.label}`,
authorized: true,
previouslyExposed: false,

View File

@ -38,3 +38,10 @@ test("every legacy planner tool has a concrete native counterpart or explicit vi
for (const name of names) expect(LEGACY_AGENT_TOOL_MAP[name],name).toBeTruthy();
expect(LEGACY_AGENT_TOOL_MAP.generateVideo).toBe('paused:video-authorization');
});
test("image workspace mode menus cannot bypass the paused video authorization", () => {
const manifest=getCanvasAgentAccessManifest();
for (const key of ["newVideoCanvas","workflowNodeLibrary","addVideoInput","chatModeVideoMenu","exportVideoCanvas"])
expect(manifest.find(item=>item.id===`canvas.ui.${key}`)).toMatchObject({workspace:'video',authorized:false});
expect(manifest.find(item=>item.id==='canvas.ui.saveCanvas')).toMatchObject({workspace:'canvas',authorized:true});
});