From 2af67ea155baafb6eeb212b84346d46c57e38779 Mon Sep 17 00:00:00 2001 From: KKKK Date: Fri, 21 Aug 2026 12:28:44 +0800 Subject: [PATCH 1/2] feat: improve canvas media workflow --- messages/en.json | 10 +- messages/zh.json | 10 +- src/components/app/product-page-shell.test.ts | 7 ++ src/components/app/product-page-shell.tsx | 11 +++ ...tcanvas-composer-reference-picker.test.tsx | 41 +++++++++ .../beatcanvas-composer-reference-picker.tsx | 28 +++++- .../beatcanvas-media-preview-overlay.test.tsx | 19 ++++ .../beatcanvas-media-preview-overlay.tsx | 27 ++++-- .../beatcanvas-media-preview.test.ts | 4 +- .../beatcanvas/beatcanvas-media-preview.ts | 5 +- .../beatcanvas/beatcanvas-shell.tsx | 42 ++++++--- .../beatcanvas/beatcanvas-sidebar-panels.tsx | 73 --------------- .../beatcanvas-sidebar.lazy-panels.test.ts | 8 +- .../beatcanvas/beatcanvas-sidebar.tsx | 33 +++---- .../nodes/asset-card-node-preview.test.ts | 15 +++ .../beatcanvas/nodes/asset-card-node.tsx | 92 ++++++++++++++++--- .../beatcanvas/nodes/beatcanvas-node-copy.ts | 10 ++ .../use-beatcanvas-upload-actions.test.ts | 7 ++ .../use-beatcanvas-upload-actions.ts | 84 +++++++++++++---- .../effects/media-upload-detection.test.ts | 44 +++++++++ src/core/effects/validation.ts | 40 +++++++- 21 files changed, 451 insertions(+), 159 deletions(-) create mode 100644 src/core/effects/media-upload-detection.test.ts diff --git a/messages/en.json b/messages/en.json index 87b7b2b..2c5905f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -124,7 +124,7 @@ "toolbar": { "addNode": "Add node", "generationNode": "Generation node", - "uploadNode": "Upload node", + "uploadNode": "Upload media", "upload": "Upload", "assets": "Assets", "batchScenes": "Batch scenes" @@ -133,7 +133,7 @@ "templates": "Templates", "historyAssets": "History assets", "addNodeTitle": "Add node", - "uploadNodeTitle": "Upload node", + "uploadNodeTitle": "Upload media", "recentAssetsTitle": "Recent assets", "recentImages": "Recent images", "recentVideos": "Recent videos", @@ -190,6 +190,7 @@ "promptPlaceholder": "Describe the image or video you want to generate. When you select a canvas node, it becomes the current reference or branch anchor." }, "actions": { + "uploadMedia": "Upload media", "uploadImage": "Upload image", "uploadVideo": "Upload video", "combine": "Combine cards", @@ -275,6 +276,8 @@ "addNodeSuccess": "Prompt node added to the canvas.", "uploadImageInvalid": "Images must be JPG, PNG, or WebP and smaller than {maxSizeMb}MB.", "uploadVideoInvalid": "Videos must be MP4, WebM, or MOV and smaller than {maxSizeMb}MB.", + "uploadMediaInvalid": "{fileName} is not a supported image or video.", + "openingMediaPicker": "Opening media picker...", "openingImagePicker": "Opening image picker...", "openingVideoPicker": "Opening video picker...", "uploading": "Uploading asset...", @@ -402,6 +405,8 @@ "videoGeneration": "Video generation", "image": "Image", "video": "Video", + "previewLoading": "Loading preview...", + "previewUnavailable": "Preview unavailable", "processing": "Processing...", "pending": "Pending...", "generationFailed": "Generation failed", @@ -511,6 +516,7 @@ "workspaceNameLabel": "Workspace name", "projects": "Projects", "projectsLabel": "Open projects", + "githubRepository": "Open BeatAPI Workspace on GitHub", "projectAssets": { "triggerLabel": "Project assets", "title": "Project assets", diff --git a/messages/zh.json b/messages/zh.json index cf2d508..e3ec00a 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -125,7 +125,7 @@ "toolbar": { "addNode": "新增节点", "generationNode": "生成节点", - "uploadNode": "上传节点", + "uploadNode": "上传素材", "upload": "上传", "assets": "资产库", "batchScenes": "批量场景" @@ -134,7 +134,7 @@ "templates": "模板库", "historyAssets": "历史素材", "addNodeTitle": "新增节点", - "uploadNodeTitle": "上传节点", + "uploadNodeTitle": "上传素材", "recentAssetsTitle": "最近素材", "recentImages": "最近图片", "recentVideos": "最近视频", @@ -191,6 +191,7 @@ "promptPlaceholder": "描述你想生成的图片或视频。选中画布节点后,它会成为当前参考素材或分支锚点。" }, "actions": { + "uploadMedia": "上传素材", "uploadImage": "上传图片", "uploadVideo": "上传视频", "combine": "合并卡片", @@ -276,6 +277,8 @@ "addNodeSuccess": "提示节点已加入画布。", "uploadImageInvalid": "图片需为 JPG、PNG 或 WebP,且小于 {maxSizeMb}MB。", "uploadVideoInvalid": "视频需为 MP4、WebM 或 MOV,且小于 {maxSizeMb}MB。", + "uploadMediaInvalid": "{fileName} 不是支持的图片或视频格式。", + "openingMediaPicker": "正在打开素材选择器...", "openingImagePicker": "正在打开图片选择器...", "openingVideoPicker": "正在打开视频选择器...", "uploading": "上传素材中...", @@ -403,6 +406,8 @@ "videoGeneration": "视频生成", "image": "图片", "video": "视频", + "previewLoading": "正在加载预览...", + "previewUnavailable": "暂时无法预览", "processing": "处理中...", "pending": "等待中...", "generationFailed": "生成失败", @@ -512,6 +517,7 @@ "workspaceNameLabel": "空间名称", "projects": "项目", "projectsLabel": "打开项目列表", + "githubRepository": "在 GitHub 打开 BeatAPI Workspace", "projectAssets": { "triggerLabel": "项目素材", "title": "项目素材", diff --git a/src/components/app/product-page-shell.test.ts b/src/components/app/product-page-shell.test.ts index a0bd751..bc68aa7 100644 --- a/src/components/app/product-page-shell.test.ts +++ b/src/components/app/product-page-shell.test.ts @@ -19,6 +19,13 @@ const apiConfigSource = readFileSync( test('workspace header keeps mode switching, shared assets, and API configuration actions', () => { assert.match(shellSource, /WorkspaceApiConfigDialog/); assert.match(shellSource, /ProjectAssetsDialog/); + assert.match(shellSource, /GitHubIcon/); + assert.match( + shellSource, + /https:\/\/github\.com\/BeatAPI\/beatapi-workspace/ + ); + assert.match(shellSource, /target="_blank"/); + assert.match(shellSource, /rel="noreferrer"/); assert.match(shellSource, />Studio<\/span>/); assert.match(shellSource, />Canvas<\/span>/); assert.match(shellSource, /\{ workspaceMode \}/); diff --git a/src/components/app/product-page-shell.tsx b/src/components/app/product-page-shell.tsx index 40919ae..b635a6d 100644 --- a/src/components/app/product-page-shell.tsx +++ b/src/components/app/product-page-shell.tsx @@ -6,6 +6,7 @@ import { LayoutPanelTop, Workflow } from 'lucide-react'; import { WorkspaceApiConfigDialog } from '@/components/app/workspace-api-config-dialog'; import { ProjectAssetsDialog } from '@/components/app/project-assets-dialog'; +import { GitHubIcon } from '@/components/icons/github'; import { Link } from '@/core/i18n/navigation'; import { useTranslations } from '@/core/workspace-lib/shims/next-intl'; import { apiJsonPatch, apiJsonPost } from '@/lib/api-client'; @@ -159,6 +160,16 @@ export function ProductPageShell({ + + diff --git a/src/components/beatcanvas/beatcanvas-composer-reference-picker.test.tsx b/src/components/beatcanvas/beatcanvas-composer-reference-picker.test.tsx index a08e54b..30212bd 100644 --- a/src/components/beatcanvas/beatcanvas-composer-reference-picker.test.tsx +++ b/src/components/beatcanvas/beatcanvas-composer-reference-picker.test.tsx @@ -77,3 +77,44 @@ test('open picker lists attached and canvas reference cards', () => { assert.match(html, /Beach/); assert.match(html, /aria-label="Remove reference"/); }); + +test('open picker renders video references with video elements instead of images', () => { + const html = renderToStaticMarkup( + {}} + onOpenChange={() => {}} + onOpenReferencePicker={() => {}} + onRemoveCanvasReference={() => {}} + options={[{ intent: 'video', remaining: 2 }]} + primaryIntent="video" + /> + ); + + assert.equal((html.match(/