refactor: align canvas image toolbar with available features
This commit is contained in:
parent
435fbd10f7
commit
c80b46d43c
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import React, { useState, useRef, useEffect, useCallback } from "react";
|
import React, { useState, useRef, useEffect, useCallback } from "react";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import { ImagePlus, Loader2, AlertCircle, MoreVertical, Scissors, Eraser, Sparkles, Expand, Maximize2, Move3D, Layers, Download, Trash2, RefreshCw, Pencil, MessageSquarePlus, Crosshair, Check, X, Type, StickyNote } from "lucide-react";
|
import { ImagePlus, Loader2, AlertCircle, MoreVertical, MoreHorizontal, Scissors, Eraser, Sparkles, Expand, Maximize2, Move3D, Download, Trash2, RefreshCw, Pencil, MessageSquarePlus, Crosshair, Check, X, Type, StickyNote } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@ -1617,10 +1617,6 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
|||||||
<Move3D className="h-4 w-4 mr-2" />
|
<Move3D className="h-4 w-4 mr-2" />
|
||||||
角度编辑
|
角度编辑
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => handleAction('scene-fusion')}>
|
|
||||||
<Layers className="h-4 w-4 mr-2" />
|
|
||||||
详情替换
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => handleAction('prompt-regenerate')}>
|
<DropdownMenuItem onClick={() => handleAction('prompt-regenerate')}>
|
||||||
<RefreshCw className="h-4 w-4 mr-2" />
|
<RefreshCw className="h-4 w-4 mr-2" />
|
||||||
提示词重绘
|
提示词重绘
|
||||||
@ -1732,7 +1728,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
|||||||
{isSelected && !isMultiSelected && data.src && !isEditingTitle && typeof document !== 'undefined' && createPortal(
|
{isSelected && !isMultiSelected && data.src && !isEditingTitle && typeof document !== 'undefined' && createPortal(
|
||||||
<TooltipProvider delayDuration={200}>
|
<TooltipProvider delayDuration={200}>
|
||||||
<div
|
<div
|
||||||
className="fixed flex items-center gap-0.5 rounded-full shadow-xl px-2 py-1 z-[9999] bg-white/95 border border-gray-200 text-gray-800 dark:bg-gray-900/95 dark:border-gray-700 dark:text-gray-100"
|
className="fixed z-[9999] flex items-center gap-1 rounded-[22px] border border-slate-300 bg-slate-50/95 px-3 py-2 text-gray-800 shadow-[0_8px_24px_rgba(15,23,42,0.14)] backdrop-blur-sm dark:border-slate-600 dark:bg-slate-900/95 dark:text-gray-100"
|
||||||
style={{
|
style={{
|
||||||
top: getToolbarPosition().top,
|
top: getToolbarPosition().top,
|
||||||
left: getToolbarPosition().left,
|
left: getToolbarPosition().left,
|
||||||
@ -1743,226 +1739,127 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
|
|||||||
onPointerDown={(e) => e.stopPropagation()}
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{/* 添加到对话 */}
|
<Button
|
||||||
<Tooltip>
|
variant="ghost"
|
||||||
<TooltipTrigger asChild>
|
disabled={isProcessing || isAddingToChat}
|
||||||
<Button
|
className="h-9 rounded-xl px-3 text-[13px] font-medium text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
variant="ghost"
|
onClick={handleAddToChat}
|
||||||
size="icon"
|
>
|
||||||
disabled={isProcessing || isAddingToChat}
|
{isAddingToChat ? (
|
||||||
className="h-8 w-8 rounded-full hover:bg-blue-50 text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 dark:hover:bg-blue-900/30 disabled:opacity-50 disabled:pointer-events-none"
|
<Loader2 className="mr-1.5 h-4 w-4 animate-spin" />
|
||||||
onClick={handleAddToChat}
|
) : (
|
||||||
>
|
<MessageSquarePlus className="mr-1.5 h-4 w-4 text-blue-600" />
|
||||||
{isAddingToChat ? (
|
)}
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
添加到对话
|
||||||
) : (
|
</Button>
|
||||||
<MessageSquarePlus className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">添加到对话</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 🆕 物品拾取 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-orange-50 text-orange-600 hover:text-orange-700 dark:text-orange-400 dark:hover:text-orange-300 dark:hover:bg-orange-900/30 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => {
|
|
||||||
setPickerMode(true, node.id);
|
|
||||||
toast.info('点击图片中的物品进行拾取');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Crosshair className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">拾取物品</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 分隔线 */}
|
|
||||||
<div className="h-5 w-px bg-gray-200 dark:bg-gray-700 mx-0.5" />
|
|
||||||
|
|
||||||
{/* 放大/高清 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => handleAction('enhance')}
|
|
||||||
>
|
|
||||||
<span className="text-[10px] font-medium text-gray-500 border border-gray-300 rounded px-1 dark:text-gray-200 dark:border-gray-600">HD</span>
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">高清放大</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 移除背景/抠图 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => handleAction('rmbg')}
|
|
||||||
>
|
|
||||||
<Scissors className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">移除背景</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 去水印 */}
|
<div className="mx-1 h-5 w-px bg-slate-200 dark:bg-slate-700" />
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => handleAction('watermark')}
|
|
||||||
>
|
|
||||||
<Eraser className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">去水印</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 文字编辑 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing || isDetectingText || isApplyingTextEdit}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => handleAction('text-edit')}
|
|
||||||
>
|
|
||||||
<Type className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">编辑文字</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 图片备注 */}
|
<Button
|
||||||
<Tooltip>
|
variant="ghost"
|
||||||
<TooltipTrigger asChild>
|
disabled={isProcessing}
|
||||||
<Button
|
className="h-9 rounded-xl px-3 text-[13px] font-medium text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
variant="ghost"
|
onClick={() => handleAction('enhance')}
|
||||||
size="icon"
|
>
|
||||||
disabled={isProcessing}
|
<span className="mr-1.5 rounded-md border border-slate-300 px-1.5 text-[11px] leading-5 text-slate-600 dark:border-slate-500 dark:text-slate-100">HD</span>
|
||||||
className={cn(
|
放大
|
||||||
"h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none",
|
</Button>
|
||||||
data.note && "text-amber-600 dark:text-amber-300"
|
|
||||||
)}
|
|
||||||
onClick={() => handleAction('note')}
|
|
||||||
>
|
|
||||||
<StickyNote className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">图片备注</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 提示词重绘 */}
|
<Button
|
||||||
<Tooltip>
|
variant="ghost"
|
||||||
<TooltipTrigger asChild>
|
disabled={isProcessing}
|
||||||
|
className="h-9 rounded-xl px-3 text-[13px] font-medium text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
|
onClick={() => handleAction('rmbg')}
|
||||||
|
>
|
||||||
|
<Scissors className="mr-1.5 h-4 w-4" />
|
||||||
|
移除背景
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
disabled={isProcessing}
|
||||||
|
className="h-9 rounded-xl px-3 text-[13px] font-medium text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
|
onClick={() => handleAction('watermark')}
|
||||||
|
>
|
||||||
|
<Eraser className="mr-1.5 h-4 w-4" />
|
||||||
|
擦除
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
disabled={isProcessing}
|
||||||
|
className="h-9 rounded-xl px-3 text-[13px] font-medium text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
|
onClick={() => {
|
||||||
|
setPickerMode(true, node.id);
|
||||||
|
toast.info('点击图片中的物品进行拾取');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Crosshair className="mr-1.5 h-4 w-4 text-orange-600" />
|
||||||
|
拾取物品
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
disabled={isProcessing || isDetectingText || isApplyingTextEdit}
|
||||||
|
className="h-9 rounded-xl px-3 text-[13px] font-medium text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
|
onClick={() => handleAction('text-edit')}
|
||||||
|
>
|
||||||
|
<Type className="mr-1.5 h-4 w-4" />
|
||||||
|
编辑文字
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
className="h-9 w-9 rounded-xl text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
onClick={() => handleAction('prompt-regenerate')}
|
|
||||||
>
|
>
|
||||||
<RefreshCw className="h-4 w-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</TooltipTrigger>
|
</DropdownMenuTrigger>
|
||||||
<TooltipContent side="bottom" className="text-xs">提示词重绘</TooltipContent>
|
<DropdownMenuContent align="center" side="bottom">
|
||||||
</Tooltip>
|
<DropdownMenuItem onClick={() => handleAction('note')}>
|
||||||
|
<StickyNote className="mr-2 h-4 w-4" />
|
||||||
{/* 角度编辑 */}
|
图片备注
|
||||||
<Tooltip>
|
</DropdownMenuItem>
|
||||||
<TooltipTrigger asChild>
|
<DropdownMenuItem onClick={() => handleAction('prompt-regenerate')}>
|
||||||
<Button
|
<RefreshCw className="mr-2 h-4 w-4" />
|
||||||
variant="ghost"
|
提示词重绘
|
||||||
size="icon"
|
</DropdownMenuItem>
|
||||||
disabled={isProcessing}
|
<DropdownMenuItem onClick={() => handleAction('angle')}>
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
<Move3D className="mr-2 h-4 w-4" />
|
||||||
onClick={() => handleAction('angle')}
|
角度编辑
|
||||||
>
|
</DropdownMenuItem>
|
||||||
<Move3D className="h-4 w-4" />
|
<DropdownMenuItem onClick={() => handleAction('extend')}>
|
||||||
</Button>
|
<Expand className="mr-2 h-4 w-4" />
|
||||||
</TooltipTrigger>
|
图片扩展
|
||||||
<TooltipContent side="bottom" className="text-xs">角度编辑</TooltipContent>
|
</DropdownMenuItem>
|
||||||
</Tooltip>
|
<DropdownMenuItem onClick={() => handleAction('resize')}>
|
||||||
|
<Maximize2 className="mr-2 h-4 w-4" />
|
||||||
{/* 扩展 */}
|
尺寸调整
|
||||||
<Tooltip>
|
</DropdownMenuItem>
|
||||||
<TooltipTrigger asChild>
|
<DropdownMenuSeparator />
|
||||||
<Button
|
<DropdownMenuItem
|
||||||
variant="ghost"
|
className="text-red-600 focus:text-red-700"
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => handleAction('extend')}
|
|
||||||
>
|
|
||||||
<Expand className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">图片扩展</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 尺寸调整 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => handleAction('resize')}
|
|
||||||
>
|
|
||||||
<Maximize2 className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">尺寸调整</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 下载 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-gray-100 dark:hover:bg-white/10 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={handleDownload}
|
|
||||||
>
|
|
||||||
<Download className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="bottom" className="text-xs">下载图片</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
{/* 删除 */}
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="h-8 w-8 rounded-full hover:bg-red-50 text-red-500 hover:text-red-600 dark:text-red-400 dark:hover:text-red-300 dark:hover:bg-red-900/30 disabled:opacity-50 disabled:pointer-events-none"
|
|
||||||
onClick={() => deleteNode(node.id)}
|
onClick={() => deleteNode(node.id)}
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4" />
|
<Trash2 className="mr-2 h-4 w-4" />
|
||||||
</Button>
|
删除
|
||||||
</TooltipTrigger>
|
</DropdownMenuItem>
|
||||||
<TooltipContent side="bottom" className="text-xs">删除</TooltipContent>
|
</DropdownMenuContent>
|
||||||
</Tooltip>
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
disabled={isProcessing}
|
||||||
|
className="h-9 w-9 rounded-xl text-slate-700 hover:bg-white hover:text-slate-900 dark:text-slate-100 dark:hover:bg-white/10"
|
||||||
|
onClick={handleDownload}
|
||||||
|
>
|
||||||
|
<Download className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>,
|
</TooltipProvider>,
|
||||||
document.body
|
document.body
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user