fix: unify canvas scale edit entry
This commit is contained in:
parent
d64d3c9deb
commit
12b21d6f3b
@ -1463,16 +1463,20 @@ function DetailResultLocalEditDialog({
|
||||
draggable={false}
|
||||
src={buildImageProxyUrl(target.imageUrl, { width: 1200, quality: 88 })}
|
||||
/>
|
||||
{regions.map((region) => {
|
||||
const isActive = region.id === activeRegionId;
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"absolute rounded-sm border-2 bg-black/0 text-left transition",
|
||||
regionInteraction?.regionId === region.id
|
||||
? "cursor-grabbing"
|
||||
: "cursor-grab"
|
||||
)}
|
||||
{regions.map((region) => {
|
||||
const isActive = region.id === activeRegionId;
|
||||
const isAwaitingScaleTarget =
|
||||
isScaleAdjustMode && !region.targetRect;
|
||||
return (
|
||||
<button
|
||||
className={cn(
|
||||
"absolute rounded-sm border-2 bg-black/0 text-left transition",
|
||||
isAwaitingScaleTarget
|
||||
? "pointer-events-none cursor-crosshair"
|
||||
: regionInteraction?.regionId === region.id
|
||||
? "cursor-grabbing"
|
||||
: "cursor-grab"
|
||||
)}
|
||||
key={region.id}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
@ -1510,8 +1514,8 @@ function DetailResultLocalEditDialog({
|
||||
>
|
||||
{region.index}
|
||||
</span>
|
||||
{isActive
|
||||
? (
|
||||
{isActive && !isAwaitingScaleTarget
|
||||
? (
|
||||
[
|
||||
{
|
||||
handle: "nw",
|
||||
@ -1705,7 +1709,7 @@ function DetailResultLocalEditDialog({
|
||||
? "在左侧图片上先框主体,再框青色目标大小和位置。"
|
||||
: "在左侧图片上拖拽框选后,这里会显示区域提示词和参考图。"}
|
||||
</div>
|
||||
) : (
|
||||
) : isScaleAdjustMode ? null : (
|
||||
<div className="max-h-[52vh] space-y-3 overflow-y-auto pr-1">
|
||||
{regions.map((region) => {
|
||||
const isActive = region.id === activeRegionId;
|
||||
|
||||
@ -22,7 +22,6 @@ import {
|
||||
Link2,
|
||||
Maximize2,
|
||||
MessageSquarePlus,
|
||||
Minimize2,
|
||||
MoreHorizontal,
|
||||
MoreVertical,
|
||||
Pencil,
|
||||
@ -1401,10 +1400,11 @@ export function ImageNode({
|
||||
[node.size.height, node.size.width]
|
||||
);
|
||||
const viewportZoom = useCanvasStore((state) => state.viewport.zoom);
|
||||
const nodeContentScale = contentScale * viewportZoom;
|
||||
const scaledContentStyle = useMemo(
|
||||
() => getScaledNodeContentStyle(nodeContentScale, { allowUpscale: true }),
|
||||
[nodeContentScale]
|
||||
const nodeContentScale = contentScale * viewportZoom;
|
||||
const localEditPromptVisualScale = clampNumber(nodeContentScale, 0.5, 1);
|
||||
const scaledContentStyle = useMemo(
|
||||
() => getScaledNodeContentStyle(nodeContentScale, { allowUpscale: true }),
|
||||
[nodeContentScale]
|
||||
);
|
||||
const selectedTitleStyle = useMemo<React.CSSProperties>(
|
||||
() => ({
|
||||
@ -2387,20 +2387,35 @@ export function ImageNode({
|
||||
localEditMode,
|
||||
localEditRegionOverlays,
|
||||
]);
|
||||
const localEditPromptViewportOverlays = useMemo(
|
||||
() =>
|
||||
localEditPromptInlineOverlays.map(({ region, isEditing, style }) => ({
|
||||
region,
|
||||
isEditing,
|
||||
style: {
|
||||
...style,
|
||||
left: Number(style.left || 0) * nodeContentScale,
|
||||
top: Number(style.top || 0) * nodeContentScale,
|
||||
width: Number(style.width || 0) * nodeContentScale,
|
||||
} satisfies React.CSSProperties,
|
||||
})),
|
||||
[localEditPromptInlineOverlays, nodeContentScale]
|
||||
);
|
||||
const localEditPromptViewportOverlays = useMemo(
|
||||
() =>
|
||||
localEditPromptInlineOverlays.map(({ region, isEditing, style }) => {
|
||||
const viewportWidth = Number(style.width || 0) * nodeContentScale;
|
||||
return {
|
||||
region,
|
||||
isEditing,
|
||||
isAwaitingScaleTarget:
|
||||
localEditOperationMode === "scale_adjust" && !region.targetRect,
|
||||
style: {
|
||||
...style,
|
||||
left: Number(style.left || 0) * nodeContentScale,
|
||||
top: Number(style.top || 0) * nodeContentScale,
|
||||
transform: `scale(${localEditPromptVisualScale})`,
|
||||
transformOrigin: "top left",
|
||||
width:
|
||||
localEditPromptVisualScale > 0
|
||||
? viewportWidth / localEditPromptVisualScale
|
||||
: viewportWidth,
|
||||
} satisfies React.CSSProperties,
|
||||
};
|
||||
}),
|
||||
[
|
||||
localEditOperationMode,
|
||||
localEditPromptInlineOverlays,
|
||||
localEditPromptVisualScale,
|
||||
nodeContentScale,
|
||||
]
|
||||
);
|
||||
useEffect(() => {
|
||||
if (
|
||||
!localEditMode ||
|
||||
@ -8396,21 +8411,26 @@ export function ImageNode({
|
||||
);
|
||||
}
|
||||
)}
|
||||
{localEditRegionOverlays.map(({ region, style }) => {
|
||||
const left = Number(style.left || 0);
|
||||
const top = Number(style.top || 0);
|
||||
const width = Number(style.width || 0);
|
||||
const height = Number(style.height || 0);
|
||||
const active =
|
||||
localEditRegionInteraction?.regionId === region.id &&
|
||||
localEditRegionInteraction.target === "source";
|
||||
{localEditRegionOverlays.map(({ region, style }) => {
|
||||
const left = Number(style.left || 0);
|
||||
const top = Number(style.top || 0);
|
||||
const width = Number(style.width || 0);
|
||||
const height = Number(style.height || 0);
|
||||
const isAwaitingScaleTarget =
|
||||
localEditOperationMode === "scale_adjust" && !region.targetRect;
|
||||
const active =
|
||||
localEditRegionInteraction?.regionId === region.id &&
|
||||
localEditRegionInteraction.target === "source";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"nodrag nopan nowheel pointer-events-auto absolute z-[160] rounded-none border-[1.5px] bg-transparent shadow-none",
|
||||
active ? "cursor-grabbing" : "cursor-grab"
|
||||
)}
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"nodrag nopan nowheel absolute z-[160] rounded-none border-[1.5px] bg-transparent shadow-none",
|
||||
isAwaitingScaleTarget
|
||||
? "pointer-events-none cursor-crosshair"
|
||||
: "pointer-events-auto",
|
||||
active ? "cursor-grabbing" : "cursor-grab"
|
||||
)}
|
||||
data-canvas-no-drag="true"
|
||||
data-local-edit-control="true"
|
||||
key={region.id}
|
||||
@ -8438,10 +8458,10 @@ export function ImageNode({
|
||||
borderColor: region.color,
|
||||
}}
|
||||
>
|
||||
{(
|
||||
[
|
||||
{
|
||||
handle: "nw",
|
||||
{!isAwaitingScaleTarget ? (
|
||||
[
|
||||
{
|
||||
handle: "nw",
|
||||
className: "-left-[5px] -top-[5px] cursor-nwse-resize",
|
||||
},
|
||||
{
|
||||
@ -8457,8 +8477,8 @@ export function ImageNode({
|
||||
className: "-right-[5px] -bottom-[5px] cursor-nwse-resize",
|
||||
},
|
||||
] as const
|
||||
).map((item) => (
|
||||
<button
|
||||
).map((item) => (
|
||||
<button
|
||||
aria-label={`调整区域 ${region.index}`}
|
||||
className={cn(
|
||||
"nodrag nopan nowheel absolute h-[10px] w-[10px] rounded-none border-[1.5px] bg-white shadow-none",
|
||||
@ -8469,12 +8489,12 @@ export function ImageNode({
|
||||
data-local-edit-region-handle={item.handle}
|
||||
key={item.handle}
|
||||
style={{ borderColor: region.color }}
|
||||
type="button"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
type="button"
|
||||
/>
|
||||
)) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{typeof document !== "undefined" && localEditPromptPortalFrame
|
||||
? createPortal(
|
||||
<div
|
||||
@ -8487,10 +8507,13 @@ export function ImageNode({
|
||||
height: localEditPromptPortalFrame.height,
|
||||
}}
|
||||
>
|
||||
{localEditPromptViewportOverlays.map(
|
||||
({ region, isEditing, style }) => (
|
||||
<div
|
||||
className="nodrag nopan nowheel pointer-events-auto absolute z-[170] flex flex-col items-stretch gap-1.5"
|
||||
{localEditPromptViewportOverlays.map(
|
||||
({ region, isEditing, isAwaitingScaleTarget, style }) => (
|
||||
<div
|
||||
className={cn(
|
||||
"nodrag nopan nowheel absolute z-[170] flex flex-col items-stretch gap-1.5",
|
||||
isAwaitingScaleTarget ? "pointer-events-none" : "pointer-events-auto"
|
||||
)}
|
||||
data-canvas-no-drag="true"
|
||||
data-local-edit-control="true"
|
||||
key={`local-edit-prompt-${region.id}`}
|
||||
@ -9298,17 +9321,8 @@ export function ImageNode({
|
||||
|
||||
<div className="mx-1 h-6 w-px bg-slate-200 dark:bg-slate-700" />
|
||||
|
||||
{localEditOperationMode === "scale_adjust" ? (
|
||||
<div className="inline-flex h-10 shrink-0 items-center gap-2 rounded-xl border border-cyan-300/35 bg-cyan-50 px-3 text-[12px] font-semibold text-cyan-800 dark:border-cyan-300/25 dark:bg-cyan-400/10 dark:text-cyan-100">
|
||||
<Maximize2 className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="whitespace-nowrap">
|
||||
先框主体,再框目标大小和位置
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Button
|
||||
className="mr-1 inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-xl bg-slate-950 px-3 font-semibold text-[13px] text-white hover:bg-slate-800 hover:text-white disabled:bg-slate-300 disabled:text-slate-500 dark:border dark:border-white/14 dark:bg-black dark:text-white dark:disabled:border-transparent dark:disabled:bg-slate-700 dark:disabled:text-slate-400 dark:hover:bg-zinc-900"
|
||||
<Button
|
||||
className="mr-1 inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-xl bg-slate-950 px-3 font-semibold text-[13px] text-white hover:bg-slate-800 hover:text-white disabled:bg-slate-300 disabled:text-slate-500 dark:border dark:border-white/14 dark:bg-black dark:text-white dark:disabled:border-transparent dark:disabled:bg-slate-700 dark:disabled:text-slate-400 dark:hover:bg-zinc-900"
|
||||
disabled={
|
||||
isSubmittingLocalEdit ||
|
||||
localEditRegions.length === 0 ||
|
||||
@ -9789,23 +9803,6 @@ export function ImageNode({
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{isSelectedImageActionInline("localEdit") ? (
|
||||
<Button
|
||||
className="group relative h-10 rounded-xl px-3 font-medium text-[13px] text-slate-700 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-100 dark:hover:bg-zinc-800"
|
||||
disabled={isProcessing || isSubmittingLocalEdit}
|
||||
onClick={() => handleAction("scale-adjust")}
|
||||
title="比例调整"
|
||||
variant="ghost"
|
||||
>
|
||||
<Minimize2 className="mr-1.5 h-4 w-4" />
|
||||
比例调整
|
||||
<ImageActionCreditBadge
|
||||
compact
|
||||
label={localEditCostShortLabel}
|
||||
/>
|
||||
</Button>
|
||||
) : null}
|
||||
|
||||
{isSelectedImageActionInline("removeWatermark") ? (
|
||||
<Button
|
||||
className="group relative h-10 rounded-xl px-3 font-medium text-[13px] text-slate-700 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-100 dark:hover:bg-zinc-800"
|
||||
@ -10077,13 +10074,6 @@ export function ImageNode({
|
||||
item={imageLocalEditItem}
|
||||
/>
|
||||
<span>{imageLocalEditItem.label}</span>
|
||||
<ImageActionCreditBadge label={localEditCostShortLabel} />
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
{isSelectedImageActionInMore("localEdit") ? (
|
||||
<DropdownMenuItem onClick={() => handleAction("scale-adjust")}>
|
||||
<Minimize2 className="mr-2 h-4 w-4" />
|
||||
<span>比例调整</span>
|
||||
<ImageActionCreditBadge label={localEditCostShortLabel} />
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user