feat(canvas): 添加画布图片信息显示功能

- 在画布项目状态中添加 showImageInfo 属性
- 实现图片信息栏组件显示图片尺寸和文件大小
- 添加画布工具栏开关控制图片信息显示
- 将图片信息状态集成到项目历史记录和持久化
- 更新画布节点组件支持图片信息显示功能
This commit is contained in:
HouYunFei
2026-05-25 15:51:14 +08:00
parent 2934b1d0bc
commit 030541b99d
6 changed files with 54 additions and 38 deletions
@@ -5,6 +5,7 @@ import type { ReactNode } from "react";
import { ChevronRight, Image as ImageIcon, RefreshCw, Star, Video } from "lucide-react";
import { canvasThemes } from "@/lib/canvas-theme";
import { formatBytes } from "@/lib/image-utils";
import { useThemeStore } from "@/stores/use-theme-store";
import { CanvasNodeType, type CanvasNodeData, type Position } from "../types";
@@ -21,6 +22,7 @@ type CanvasNodeProps = {
isConnecting: boolean;
editRequestNonce?: number;
showPanel: boolean;
showImageInfo: boolean;
renderPanel?: (node: CanvasNodeData) => ReactNode;
renderNodeContent?: (node: CanvasNodeData) => ReactNode;
batchCount?: number;
@@ -71,6 +73,7 @@ export const CanvasNode = React.memo(function CanvasNode({
isConnecting,
editRequestNonce = 0,
showPanel,
showImageInfo,
renderPanel,
renderNodeContent,
batchCount = 0,
@@ -296,6 +299,8 @@ export const CanvasNode = React.memo(function CanvasNode({
/>
</div>
{showImageInfo && hasImageContent ? <ImageInfoBar node={data} /> : null}
{!hasImageContent && !hasVideoContent ? <div className="pointer-events-none absolute inset-x-0 bottom-0 h-12" style={{ background: `linear-gradient(to top, ${theme.canvas.background}66, transparent)` }} /> : null}
<ResizeHandle corner="top-left" onMouseDown={handleResizeMouseDown} />
@@ -537,6 +542,20 @@ function ImageContent({
);
}
function ImageInfoBar({ node }: { node: CanvasNodeData }) {
const width = Math.round(node.metadata?.naturalWidth || node.width);
const height = Math.round(node.metadata?.naturalHeight || node.height);
const size = formatBytes(node.metadata?.bytes || 0);
return (
<div className="pointer-events-none absolute bottom-3 right-3 z-40 max-w-[calc(100%-24px)]">
<span className="max-w-full truncate rounded-md bg-black/55 px-2 py-1 text-[11px] font-medium leading-none text-white backdrop-blur-sm">
{width} x {height}
{size ? ` · ${size}` : ""}
</span>
</div>
);
}
function BatchFrame({ batchCount, batchExpanded, batchOpening, batchRecovering, onToggleBatch, children }: { batchCount: number; batchExpanded: boolean; batchOpening: boolean; batchRecovering: boolean; onToggleBatch?: () => void; children: ReactNode }) {
const theme = canvasThemes[useThemeStore((state) => state.theme)];
const isBatchRoot = batchCount > 1;
@@ -1,7 +1,7 @@
import type { CSSProperties, MouseEvent as ReactMouseEvent, ReactNode, RefObject } from "react";
import { useRef, useState } from "react";
import { Button, Segmented } from "antd";
import { CircleDot, Eraser, FolderOpen, Grid2x2, Hand, Image as ImageIcon, Library, Moon, Palette, Redo2, Settings2, Square, Sun, Trash2, Type, Undo2, Upload, Video } from "lucide-react";
import { Button, Segmented, Switch } from "antd";
import { CircleDot, Eraser, FolderOpen, Grid2x2, Hand, Image as ImageIcon, Info, Library, Moon, Palette, Redo2, Settings2, Square, Sun, Trash2, Type, Undo2, Upload, Video } from "lucide-react";
import { canvasThemes, type CanvasBackgroundMode, type CanvasColorTheme, type CanvasTheme } from "@/lib/canvas-theme";
import { useThemeStore } from "@/stores/use-theme-store";
@@ -12,6 +12,7 @@ export function CanvasToolbar({
canUndo,
canRedo,
backgroundMode,
showImageInfo,
onAddImage,
onAddVideo,
onAddText,
@@ -23,6 +24,7 @@ export function CanvasToolbar({
onClear,
onDeselect,
onBackgroundModeChange,
onShowImageInfoChange,
onOpenAssetLibrary,
onOpenMyAssets,
}: {
@@ -30,6 +32,7 @@ export function CanvasToolbar({
canUndo: boolean;
canRedo: boolean;
backgroundMode: CanvasBackgroundMode;
showImageInfo: boolean;
onAddImage: () => void;
onAddVideo: () => void;
onAddText: () => void;
@@ -41,6 +44,7 @@ export function CanvasToolbar({
onClear: () => void;
onDeselect: () => void;
onBackgroundModeChange: (mode: CanvasBackgroundMode) => void;
onShowImageInfoChange: (show: boolean) => void;
onOpenAssetLibrary: () => void;
onOpenMyAssets: () => void;
}) {
@@ -174,6 +178,13 @@ export function CanvasToolbar({
},
]}
/>
<div className="mt-3 flex items-center justify-between gap-3 rounded-lg px-1.5 py-1">
<span className="inline-flex min-w-0 items-center gap-1.5 text-[11px] font-medium opacity-65">
<Info className="size-3.5" />
</span>
<Switch size="small" checked={showImageInfo} onChange={onShowImageInfoChange} />
</div>
</div>
) : null}
</div>