From 38bba0828c2681590fc36bdc3bab2013bc976453 Mon Sep 17 00:00:00 2001 From: Jammy Date: Tue, 7 Jul 2026 09:39:54 +0800 Subject: [PATCH] Add FunClip timeline handoff source --- apps/web/src/workbench/WorkbenchShell.tsx | 39 ++++++++++++++++++- .../components/chat-drawer/ModelSelector.tsx | 13 ++++--- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/apps/web/src/workbench/WorkbenchShell.tsx b/apps/web/src/workbench/WorkbenchShell.tsx index ee2b57f..af268ef 100644 --- a/apps/web/src/workbench/WorkbenchShell.tsx +++ b/apps/web/src/workbench/WorkbenchShell.tsx @@ -9780,6 +9780,37 @@ function FunClipEditingPage({ | 'clip' | 'published'; + const timelineHandoffSource = useMemo(() => { + const asset = timelineHandoff?.asset; + const url = publishableAssetMediaUrl(asset); + if (!asset || (asset.type !== 'video' && asset.type !== 'audio') || !url) { + return null; + } + return { + id: `handoff-${asset.assetId || asset.id || 'media'}`, + assetId: asset.assetId || asset.id, + name: asset.name || '数字人成片', + type: asset.type, + source: 'asset-center', + section: 'work', + tags: Array.from( + new Set([ + ...(asset.sourceTags || []), + 'ai', + 'clip', + ]) + ), + provider: asset.provider || '数字人', + url, + thumbnailUrl: publishableAssetThumbnailUrl(asset), + localPath: asset.localPath, + durationMs: asset.durationMs, + width: asset.width, + height: asset.height, + createdAt: asset.createdAt || timelineHandoff.createdAt || Date.now(), + }; + }, [timelineHandoff]); + const sourceOptions = useMemo(() => { const materialSources = assets .filter((asset) => asset.type === 'video' || asset.type === 'audio') @@ -9830,7 +9861,11 @@ function FunClipEditingPage({ }; }); const seen = new Set(); - return [...finishedSources, ...materialSources] + return [ + ...(timelineHandoffSource ? [timelineHandoffSource] : []), + ...finishedSources, + ...materialSources, + ] .filter((item) => { const key = item.assetId || item.localPath || item.url || item.id; if (seen.has(key)) { @@ -9840,7 +9875,7 @@ function FunClipEditingPage({ return true; }) .sort((a, b) => b.createdAt - a.createdAt); - }, [assets, finishedAssets]); + }, [assets, finishedAssets, timelineHandoffSource]); const clipPresets: Array<{ id: ClipPresetId; diff --git a/packages/drawnix/src/components/chat-drawer/ModelSelector.tsx b/packages/drawnix/src/components/chat-drawer/ModelSelector.tsx index 1c7e9e6..0887de4 100644 --- a/packages/drawnix/src/components/chat-drawer/ModelSelector.tsx +++ b/packages/drawnix/src/components/chat-drawer/ModelSelector.tsx @@ -76,9 +76,7 @@ const ModelDescFallback: React.FC<{ model: ModelConfig }> = React.memo( ({ model }) => { const meta = useModelMeta(model.sourceProfileId, model.id); if (!meta?.description) return null; - return ( -
{meta.description}
- ); + return
{meta.description}
; } ); @@ -209,7 +207,9 @@ export const ModelSelector: React.FC = React.memo( selectedModel, selectedModelRef ); - setActiveVendor(selectedIsSelectable ? currentModel?.vendor || null : null); + setActiveVendor( + selectedIsSelectable ? currentModel?.vendor || null : null + ); } else { setSearchQuery(''); } @@ -298,7 +298,10 @@ export const ModelSelector: React.FC = React.memo( const alignedLeft = rect.left; const nextLeft = Math.min( Math.max(viewportGutter, alignedLeft), - Math.max(viewportGutter, windowWidth - preferredWidth - viewportGutter) + Math.max( + viewportGutter, + windowWidth - preferredWidth - viewportGutter + ) ); setPortalPosition({