Scale node content when video canvas modules shrink
This commit is contained in:
parent
5763ed1c94
commit
ca0fbc5fd4
@ -69,6 +69,10 @@ import {
|
||||
import type { DetailUiScreenOverlaySnapshot } from "@/lib/detail-ui-surface-adapter";
|
||||
import { buildImageProxyUrl } from "@/lib/image-delivery";
|
||||
import { inferOriginalImageUrl } from "@/lib/image-url";
|
||||
import {
|
||||
getNodeContentScale,
|
||||
getScaledNodeContentStyle,
|
||||
} from "@/lib/canvas/node-content-scale";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type {
|
||||
CanvasTextEditPayloadItem,
|
||||
@ -350,6 +354,18 @@ interface ImageNodeProps {
|
||||
|
||||
export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
||||
const data = node.data as ImageNodeData;
|
||||
const contentScale = useMemo(
|
||||
() =>
|
||||
getNodeContentScale(node.size, {
|
||||
width: 220,
|
||||
height: 220,
|
||||
}),
|
||||
[node.size.height, node.size.width]
|
||||
);
|
||||
const scaledContentStyle = useMemo(
|
||||
() => getScaledNodeContentStyle(contentScale),
|
||||
[contentScale]
|
||||
);
|
||||
const detailProgressLabel =
|
||||
typeof data.metadata?.detailProgressLabel === "string"
|
||||
? data.metadata.detailProgressLabel.trim()
|
||||
@ -2867,6 +2883,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
ref={containerRef}
|
||||
>
|
||||
<div className="relative h-full w-full" style={scaledContentStyle}>
|
||||
{/* 图片内容 - 使用 object-contain 保持原始比例 */}
|
||||
{data.src ? (
|
||||
<div className="relative h-full w-full">
|
||||
@ -3298,6 +3315,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
||||
快照 {shortAgentExecutionSnapshotId}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 角度编辑对话框 */}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useRef } from "react";
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import { Video, Loader2, AlertCircle, MoreVertical, Download, Trash2, Play, Pause, Volume2, VolumeX } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@ -13,6 +13,10 @@ import {
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import { AgentExecutionSnapshotDialog } from "@/components/agents/AgentExecutionSnapshotDialog";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
getNodeContentScale,
|
||||
getScaledNodeContentStyle,
|
||||
} from "@/lib/canvas/node-content-scale";
|
||||
import { useCanvasStore } from "@/store/canvas";
|
||||
import type { CanvasNode, VideoNodeData } from "@/types/canvas";
|
||||
import { cn } from "@/lib/utils";
|
||||
@ -24,6 +28,18 @@ interface VideoNodeProps {
|
||||
|
||||
export function VideoNode({ node, isSelected }: VideoNodeProps) {
|
||||
const data = node.data as VideoNodeData;
|
||||
const contentScale = useMemo(
|
||||
() =>
|
||||
getNodeContentScale(node.size, {
|
||||
width: 240,
|
||||
height: 220,
|
||||
}),
|
||||
[node.size.height, node.size.width]
|
||||
);
|
||||
const scaledContentStyle = useMemo(
|
||||
() => getScaledNodeContentStyle(contentScale),
|
||||
[contentScale]
|
||||
);
|
||||
const agentExecutionSnapshotId =
|
||||
typeof data.metadata?.agentExecutionSnapshotId === "string"
|
||||
? data.metadata.agentExecutionSnapshotId.trim()
|
||||
@ -146,6 +162,7 @@ export function VideoNode({ node, isSelected }: VideoNodeProps) {
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div className="relative h-full w-full" style={scaledContentStyle}>
|
||||
{/* 视频内容 */}
|
||||
{data.src ? (
|
||||
<div className="relative w-full h-full">
|
||||
@ -306,6 +323,7 @@ export function VideoNode({ node, isSelected }: VideoNodeProps) {
|
||||
<p className="line-clamp-2 text-xs leading-5 text-[#556072]">{data.prompt}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -13,6 +13,10 @@ import {
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { useCanvasStore } from "@/store/canvas";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
getNodeContentScale,
|
||||
getScaledNodeContentStyle,
|
||||
} from "@/lib/canvas/node-content-scale";
|
||||
import {
|
||||
getVideoWorkflowNodeDefinition,
|
||||
getVideoWorkflowQuickConnectOutputs,
|
||||
@ -131,6 +135,14 @@ export function WorkflowNode({ node, isSelected }: WorkflowNodeProps) {
|
||||
const deleteNode = useCanvasStore((state) => state.deleteNode);
|
||||
const data = node.data as WorkflowNodeData;
|
||||
const definition = getVideoWorkflowNodeDefinition(data.templateType);
|
||||
const contentScale = getNodeContentScale(
|
||||
node.size,
|
||||
definition?.size || {
|
||||
width: 300,
|
||||
height: 180,
|
||||
}
|
||||
);
|
||||
const scaledContentStyle = getScaledNodeContentStyle(contentScale);
|
||||
const metadata = (data.metadata || {}) as Record<string, any>;
|
||||
const quickConnectOutputs =
|
||||
data.templateType && definition
|
||||
@ -257,7 +269,8 @@ export function WorkflowNode({ node, isSelected }: WorkflowNodeProps) {
|
||||
style={{ backgroundColor: accentBarColor }}
|
||||
/>
|
||||
|
||||
<div className="flex h-full flex-col gap-3 p-3">
|
||||
<div className="h-full w-full" style={scaledContentStyle}>
|
||||
<div className="flex h-full flex-col gap-3 p-3">
|
||||
<div
|
||||
className="rounded-[20px] border px-3 py-3"
|
||||
style={{
|
||||
@ -385,12 +398,13 @@ export function WorkflowNode({ node, isSelected }: WorkflowNodeProps) {
|
||||
{item.label}
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
) : (
|
||||
<span className="rounded-full border border-dashed border-[#d8def0] px-2.5 py-1 text-[11px] text-[#6f7483] dark:border-zinc-800">
|
||||
结束节点
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
33
lib/canvas/node-content-scale.ts
Normal file
33
lib/canvas/node-content-scale.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import type { CSSProperties } from "react";
|
||||
import type { Size } from "@/types/canvas";
|
||||
|
||||
const DEFAULT_MIN_SCALE = 0.56;
|
||||
|
||||
export function getNodeContentScale(
|
||||
size: Size,
|
||||
baseSize: Size,
|
||||
minScale = DEFAULT_MIN_SCALE
|
||||
) {
|
||||
if (!size.width || !size.height || !baseSize.width || !baseSize.height) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const widthScale = size.width / baseSize.width;
|
||||
const heightScale = size.height / baseSize.height;
|
||||
return Math.min(1, Math.max(minScale, Math.min(widthScale, heightScale)));
|
||||
}
|
||||
|
||||
export function getScaledNodeContentStyle(
|
||||
scale: number
|
||||
): CSSProperties | undefined {
|
||||
if (scale >= 0.999) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
width: `${100 / scale}%`,
|
||||
height: `${100 / scale}%`,
|
||||
transform: `scale(${scale})`,
|
||||
transformOrigin: "top left",
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user