From f34344d53b576f7f373629917774ef9ec1d61229 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 4 Jun 2026 14:02:05 +0800 Subject: [PATCH] fix: compact canvas image tools --- components/canvas/AiWashWorkspace.tsx | 6 +- components/canvas/nodes/ImageNode.tsx | 438 +++++++++++--------------- 2 files changed, 185 insertions(+), 259 deletions(-) diff --git a/components/canvas/AiWashWorkspace.tsx b/components/canvas/AiWashWorkspace.tsx index d0dba9979..4af410e5f 100644 --- a/components/canvas/AiWashWorkspace.tsx +++ b/components/canvas/AiWashWorkspace.tsx @@ -1284,14 +1284,14 @@ export function AiWashWorkspace({ ) : (
-
+
选择本地图后即可开始 {config.modeLabel || "AI洗图"}
diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index 89049be1e..962825fd0 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -1201,6 +1201,14 @@ export function ImageNode({ imageNodeMoreEnabled && (hasHoverImageMorePrimaryActions || hasHoverImageMoreSecondaryActions); const hasSelectedImageMorePrimaryActions = + imageBoxCutoutEnabled || + imageLocalEditEnabled || + imageRemoveWatermarkEnabled || + imageResizeEnabled || + imageMultiAngleEnabled || + imageGenerate3dModelEnabled || + imageGenerate3dWorldEnabled || + imageLayerSplitEnabled || imageExtendEnabled || imageCropEnabled || imageAdjustEnabled || @@ -1208,22 +1216,14 @@ export function ImageNode({ imagePromptRegenerateEnabled || imageNoteEnabled; const hasSelectedImageMoreMenu = - imageNodeMoreEnabled && - (hasSelectedImageMorePrimaryActions || imageDeleteEnabled); + hasSelectedImageMorePrimaryActions || + (imageNodeMoreEnabled && imageDeleteEnabled); const hasSelectedImagePrimaryToolbarActions = imagePickObjectEnabled || imageEnhanceEnabled || imageRemoveBackgroundEnabled || - imageBoxCutoutEnabled || - imageLocalEditEnabled || - imageRemoveWatermarkEnabled || - imageResizeEnabled || imageTextEditEnabled || - imageMultiAngleEnabled || - imageGenerate3dModelEnabled || - imageGenerate3dWorldEnabled || - imageAiWashQuickEnabled || - imageLayerSplitEnabled; + imageAiWashQuickEnabled; const hasSelectedImageSecondaryToolbarActions = hasSelectedImageMoreMenu || imageDownloadEnabled; const contentScale = useMemo( @@ -4294,7 +4294,7 @@ export function ImageNode({ const rect = containerRef.current.getBoundingClientRect(); const toolbarWidth = Math.max( 280, - (toolbarSize.width || 980) * FLOATING_TOOLBAR_SCALE + (toolbarSize.width || 640) * FLOATING_TOOLBAR_SCALE ); const toolbarHeight = Math.max( 42, @@ -4302,12 +4302,12 @@ export function ImageNode({ ); const titleSafeHeight = 34; const gap = 12; - const { viewportPadding, maxTop, safeLeft, safeRight } = + const { viewportPadding, maxTop, safeBottom, safeLeft, safeRight } = getFloatingViewportBounds(toolbarHeight); - let top = rect.top - toolbarHeight - titleSafeHeight - gap; - if (top < viewportPadding) { - top = rect.bottom + gap; + let top = rect.bottom + gap; + if (top + toolbarHeight > safeBottom) { + top = rect.top - toolbarHeight - titleSafeHeight - gap; } top = Math.min(Math.max(top, viewportPadding), maxTop); @@ -4332,36 +4332,31 @@ export function ImageNode({ return { top: 0, left: 0, maxWidth: 480 }; }; - const getAiWashQuickPanelPosition = ( - anchor: { top: number; left: number; maxWidth: number } - ) => { - if (typeof window === "undefined") { - return { top: 0, left: 0, width: 360, maxHeight: 520 }; + const getAiWashQuickPanelPosition = () => { + if (!containerRef.current || typeof window === "undefined") { + return { top: 0, left: 0, width: 286, maxHeight: 430 }; } - const panelWidth = 360; - const panelHeight = 540; - const gap = 10; - const toolbarHeight = Math.max( - 42, - (toolbarSize.height || 60) * FLOATING_TOOLBAR_SCALE - ); + const rect = containerRef.current.getBoundingClientRect(); + const panelWidth = 286; + const panelHeight = 430; + const gap = 12; const { viewportPadding, safeBottom, safeLeft, safeRight, maxTop } = getFloatingViewportBounds(panelHeight); const width = Math.min(panelWidth, Math.max(260, safeRight - safeLeft)); - let top = anchor.top + toolbarHeight + gap; + let left = rect.right + gap; + if (left + width > safeRight) { + left = rect.left - width - gap; + } + left = Math.min(Math.max(left, safeLeft), Math.max(safeLeft, safeRight - width)); + + let top = rect.top; if (top + panelHeight > safeBottom) { - top = anchor.top - panelHeight - gap; + top = rect.bottom - panelHeight; } top = Math.min(Math.max(top, viewportPadding), maxTop); - const preferredLeft = anchor.left; - const left = Math.min( - Math.max(preferredLeft, safeLeft), - Math.max(safeLeft, safeRight - width) - ); - return { top, left, @@ -7457,7 +7452,7 @@ export function ImageNode({ const cropPanelPosition = getCropPanelPosition(); const imageAdjustPanelPosition = getImageAdjustPanelPosition(); const toolbarPosition = getToolbarPosition(); - const aiWashQuickPanelPosition = getAiWashQuickPanelPosition(toolbarPosition); + const aiWashQuickPanelPosition = getAiWashQuickPanelPosition(); const boxCutoutToolbarPosition = getBoxCutoutToolbarPosition(); const flipRotatePanelPosition = getFlipRotatePanelPosition(); const anglePanelPosition = getAnglePanelPosition(); @@ -8653,7 +8648,7 @@ export function ImageNode({ typeof document !== "undefined" && createPortal(
event.stopPropagation()} onMouseDown={(event) => event.stopPropagation()} @@ -8667,93 +8662,80 @@ export function ImageNode({ scrollbarWidth: "thin", }} > -
-
- +
+
+
-
+
{aiWashQuickModeLabel}
-
- 选中图片快捷洗图 -
{aiWashConfigLoading ? ( -
- +
+ 正在加载 AI洗图 配置...
) : null} -
-
-
-
- - 渲染目标 +
+
+
+
+ + 目标
- + {getAiWashStatusLabel(aiWashQuickStatus)}
-
- {aiWashQuickPresets.map((preset) => { - const isActive = selectedAiWashQuickPreset?.id === preset.id; - return ( - - ); - })} -
+ + {selectedAiWashQuickPreset?.description ? ( +
+ {selectedAiWashQuickPreset.description} +
+ ) : null}
-
-
参数设置
+
+
+ 参数 +
{shouldShowAiWashQuickModelSelector ? ( -
+
@@ -8761,7 +8743,7 @@ export function ImageNode({ onValueChange={setAiWashQuickModelId} value={selectedAiWashQuickModel?.id || aiWashQuickModelId} > - + @@ -8787,7 +8769,7 @@ export function ImageNode({
{selectedAiWashQuickModel - ? `当前模型:${selectedAiWashQuickModel.displayName} · ${ + ? `${selectedAiWashQuickModel.displayName} · ${ selectedAiWashQuickModel.unitCost > 0 ? `${selectedAiWashQuickModel.unitCost} 积分/张` : "未设置积分" @@ -8798,15 +8780,15 @@ export function ImageNode({
) : selectedAiWashQuickModel ? ( -
- 管理员已锁定模型:{selectedAiWashQuickModel.displayName} +
+ {selectedAiWashQuickModel.displayName} {selectedAiWashQuickModel.unitCost > 0 ? ` · ${selectedAiWashQuickModel.unitCost} 积分/张` : ""}
) : null} -
+
{aiWashQuickControls.map((control) => { const selectedIndex = aiWashQuickLevels[control.id] ?? control.defaultLevel; @@ -8816,14 +8798,14 @@ export function ImageNode({ null; return (
-
-
+
+
{control.label}
- + {activeLevel?.label || selectedIndex + 1}
@@ -8839,8 +8821,8 @@ export function ImageNode({ } rangeClassName="bg-slate-950 dark:bg-white" step={1} - thumbClassName="h-3.5 w-3.5 border border-slate-300 bg-white shadow-[0_0_0_3px_rgba(15,23,42,0.06)]" - trackClassName="h-1.5 bg-slate-200 dark:bg-white/14" + thumbClassName="h-3 w-3 border border-slate-300 bg-white shadow-[0_0_0_3px_rgba(15,23,42,0.06)]" + trackClassName="h-1 bg-slate-200 dark:bg-white/14" value={[selectedIndex]} />
@@ -8849,14 +8831,8 @@ export function ImageNode({
- {aiWashQuickPrompt ? ( -
- {aiWashQuickPrompt} -
- ) : null} - ) : null} - {imageAddToChatEnabled && hasSelectedImagePrimaryToolbarActions ? ( + {imageAddToChatEnabled && + (hasSelectedImagePrimaryToolbarActions || + hasSelectedImageSecondaryToolbarActions) ? (
) : null} @@ -8994,40 +8972,6 @@ export function ImageNode({ ) : null} - {imageBoxCutoutEnabled ? ( - - ) : null} - - {imageLocalEditEnabled ? ( - - ) : null} - {imageAiWashQuickEnabled ? ( - ) : null} - - {imageResizeEnabled ? ( - - ) : null} - {imageTextEditEnabled ? ( - ) : null} - - {imageGenerate3dModelEnabled ? ( - - ) : null} - - {imageGenerate3dWorldEnabled ? ( - - ) : null} - - {imageLayerSplitEnabled ? ( - - ) : null} - {hasSelectedImagePrimaryToolbarActions && hasSelectedImageSecondaryToolbarActions ? (
@@ -9201,6 +9043,90 @@ export function ImageNode({ className="bg-white opacity-100 backdrop-blur-none dark:bg-slate-900 dark:text-slate-100" side="bottom" > + {imageBoxCutoutEnabled ? ( + handleAction("box-cutout")}> + + {imageBoxCutoutItem.label} + + ) : null} + {imageLocalEditEnabled ? ( + handleAction("local-edit")}> + + {imageLocalEditItem.label} + + ) : null} + {imageRemoveWatermarkEnabled ? ( + handleAction("watermark")}> + + {imageRemoveWatermarkItem.label} + + ) : null} + {imageResizeEnabled ? ( + handleAction("resize")}> + + {imageResizeItem.label} + + ) : null} + {imageMultiAngleEnabled ? ( + handleAction("angle")}> + + {imageMultiAngleItem.label} + + ) : null} + {imageGenerate3dModelEnabled ? ( + handleAction("generate-3d-model")} + > + + {imageGenerate3dModelItem.label} + + ) : null} + {imageGenerate3dWorldEnabled ? ( + handleAction("generate-3d-world")} + > + + {imageGenerate3dWorldItem.label} + + ) : null} + {imageLayerSplitEnabled ? ( + handleAction("layer-split")}> + + {imageLayerSplitItem.label} + + ) : null} {imageExtendEnabled ? ( handleAction("extend")}>