mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-05 17:04:27 +08:00
feat(canvas): 添加视频节点支持功能
- 在后端添加 AI 视频相关接口处理函数 - 扩展前端画布组件支持视频节点类型 - 实现视频上传、生成和播放功能 - 更新画布数据结构文档以包含视频节点 - 添加视频节点的操作工具栏和配置面板 - 集成视频存储和检索服务 - 优化视频节点的渲染和交互体验
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
||||
import { Modal, Segmented, Tooltip } from "antd";
|
||||
import { Camera, Download, FolderPlus, Image as ImageIcon, Info, Lock, LockOpen, Maximize2, MessageSquare, Minus, Pencil, Plus, RefreshCw, Scissors, Settings2, Trash2, Upload } from "lucide-react";
|
||||
import { Camera, Download, FolderPlus, Image as ImageIcon, Info, Lock, LockOpen, Maximize2, MessageSquare, Minus, Pencil, Plus, RefreshCw, Scissors, Settings2, Trash2, Upload, Video } from "lucide-react";
|
||||
|
||||
import { canvasThemes } from "@/lib/canvas-theme";
|
||||
import { formatBytes, getDataUrlByteSize } from "@/lib/image-utils";
|
||||
@@ -57,12 +57,14 @@ export function CanvasNodeHoverToolbar({
|
||||
const left = viewport.x + (node.position.x + node.width / 2) * viewport.k;
|
||||
const top = viewport.y + node.position.y * viewport.k - 14;
|
||||
const isImage = node.type === CanvasNodeType.Image;
|
||||
const isVideo = node.type === CanvasNodeType.Video;
|
||||
const hasImage = isImage && Boolean(node.metadata?.content);
|
||||
const hasVideo = isVideo && Boolean(node.metadata?.content);
|
||||
const isText = node.type === CanvasNodeType.Text;
|
||||
const isConfig = node.type === CanvasNodeType.Config;
|
||||
const canOpenDialog = isText || hasImage;
|
||||
const canOpenDialog = isText || hasImage || isVideo;
|
||||
const canRetry = node.metadata?.status === "error";
|
||||
const hasSpecificTools = canRetry || isText || isImage || isConfig;
|
||||
const hasSpecificTools = canRetry || isText || isImage || isVideo || isConfig;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -77,8 +79,8 @@ export function CanvasNodeHoverToolbar({
|
||||
<ToolbarAction title="移除节点" label="删除" icon={<Trash2 className="size-4" />} onClick={() => onDelete(node)} danger />
|
||||
{hasSpecificTools ? <ToolbarDivider /> : null}
|
||||
{canRetry ? <ToolbarAction title="重新生成" label="重试" icon={<RefreshCw className="size-4" />} onClick={() => onRetry(node)} /> : null}
|
||||
{hasImage || isText ? <ToolbarAction title="加入我的素材" label="存素材" icon={<FolderPlus className="size-4" />} onClick={() => onSaveAsset(node)} /> : null}
|
||||
{hasImage ? <IconAction title="下载图片" icon={<Download className="size-5" />} onClick={() => onDownload(node)} /> : null}
|
||||
{hasImage || hasVideo || isText ? <ToolbarAction title="加入我的素材" label="存素材" icon={<FolderPlus className="size-4" />} onClick={() => onSaveAsset(node)} /> : null}
|
||||
{hasImage || hasVideo ? <IconAction title={hasVideo ? "下载视频" : "下载图片"} icon={<Download className="size-5" />} onClick={() => onDownload(node)} /> : null}
|
||||
{canOpenDialog ? <ToolbarAction title="编辑" label="编辑" icon={<MessageSquare className="size-4" />} onClick={() => onToggleDialog(node)} /> : null}
|
||||
{isText ? <ToolbarAction title="编辑文本" label="编辑文字" icon={<Pencil className="size-4" />} onClick={() => onEditText(node)} /> : null}
|
||||
{isText ? <ToolbarAction title="用文本生图" label="生图" icon={<ImageIcon className="size-4" />} onClick={() => onGenerateImage(node)} /> : null}
|
||||
@@ -86,6 +88,7 @@ export function CanvasNodeHoverToolbar({
|
||||
{isText ? <ToolbarAction title="减小字号" label="缩小" icon={<Minus className="size-4" />} onClick={() => onDecreaseFont(node)} /> : null}
|
||||
{isText ? <ToolbarAction title="增大字号" label="放大" icon={<Plus className="size-4" />} onClick={() => onIncreaseFont(node)} /> : null}
|
||||
{isImage ? <ToolbarAction title={hasImage ? "替换图片" : "上传图片"} label={hasImage ? "替换图片" : "上传图片"} icon={<Upload className="size-4" />} onClick={() => onUpload(node)} /> : null}
|
||||
{isVideo ? <ToolbarAction title={hasVideo ? "替换视频" : "上传视频"} label={hasVideo ? "替换视频" : "上传视频"} icon={<Video className="size-4" />} onClick={() => onUpload(node)} /> : null}
|
||||
{hasImage ? (
|
||||
<ToolbarAction
|
||||
title={node.metadata?.freeResize ? "切换为等比缩放" : "切换为自由比例"}
|
||||
@@ -148,7 +151,7 @@ export function CanvasNodeInfoModal({ node, open, onClose }: { node: CanvasNodeD
|
||||
{view === "info" ? (
|
||||
<div className="thin-scrollbar h-full space-y-3 overflow-auto pr-1">
|
||||
<InfoRow label="ID" value={node.id} />
|
||||
<InfoRow label="类型" value={node.type === CanvasNodeType.Text ? "文本" : node.type === CanvasNodeType.Image ? "图片" : "生成配置"} />
|
||||
<InfoRow label="类型" value={node.type === CanvasNodeType.Text ? "文本" : node.type === CanvasNodeType.Image ? "图片" : node.type === CanvasNodeType.Video ? "视频" : "生成配置"} />
|
||||
<InfoRow label="尺寸" value={`${Math.round(node.width)} x ${Math.round(node.height)}`} />
|
||||
<InfoRow label="位置" value={`${Math.round(node.position.x)}, ${Math.round(node.position.y)}`} />
|
||||
<InfoRow label="状态" value={node.metadata?.status || "idle"} />
|
||||
|
||||
Reference in New Issue
Block a user