diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index f7b14d2b7..44f6116b0 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -1984,6 +1984,7 @@ export function ImageNode({ const localEditPromptInputRefs = useRef< Record >({}); + const localEditPromptDraftRefs = useRef>({}); const updateNode = useCanvasStore((state) => state.updateNode); const deleteNode = useCanvasStore((state) => state.deleteNode); @@ -2459,6 +2460,8 @@ export function ImageNode({ const input = localEditPromptInputRefs.current[regionId]; if (!input) return; input.focus({ preventScroll: true }); + const cursorPosition = input.value.length; + input.setSelectionRange(cursorPosition, cursorPosition); }); }, []); @@ -2473,6 +2476,9 @@ export function ImageNode({ regions.slice(0, LOCAL_EDIT_MAX_REGIONS) ); setLocalEditRegions(nextRegions); + localEditPromptDraftRefs.current = Object.fromEntries( + nextRegions.map((region) => [region.id, region.prompt]) + ); const nextMetadata = data.metadata && typeof data.metadata === "object" @@ -3417,21 +3423,9 @@ export function ImageNode({ [localEditMode] ); - const updateLocalEditRegionPrompt = useCallback( - (regionId: string, prompt: string) => { - setLocalEditRegions((prevRegions) => - reindexLocalEditRegions( - prevRegions.map((region) => - region.id === regionId ? { ...region, prompt } : region - ) - ) - ); - }, - [] - ); - const persistLocalEditRegionPrompt = useCallback( (regionId: string, prompt: string) => { + localEditPromptDraftRefs.current[regionId] = prompt; commitLocalEditRegions( localEditRegions.map((region) => region.id === regionId ? { ...region, prompt } : region @@ -3443,6 +3437,7 @@ export function ImageNode({ const deleteLocalEditRegion = useCallback( (regionId: string) => { + delete localEditPromptDraftRefs.current[regionId]; commitLocalEditRegions( localEditRegions.filter((region) => region.id !== regionId) ); @@ -3454,6 +3449,7 @@ export function ImageNode({ ); const clearLocalEditRegions = useCallback(() => { + localEditPromptDraftRefs.current = {}; commitLocalEditRegions([]); setEditingLocalEditRegionId(null); }, [commitLocalEditRegions]); @@ -3475,7 +3471,9 @@ export function ImageNode({ const validRegions = reindexLocalEditRegions( localEditRegions.map((region) => ({ ...region, - prompt: region.prompt.trim(), + prompt: ( + localEditPromptDraftRefs.current[region.id] ?? region.prompt + ).trim(), })) ); @@ -8159,33 +8157,32 @@ export function ImageNode({ className="nodrag nopan nowheel h-9 min-w-0 flex-1 rounded-full border border-slate-300 bg-white/95 px-3 text-[12px] font-medium text-slate-950 caret-slate-950 shadow-[0_10px_24px_rgba(15,23,42,0.14)] outline-none ring-2 ring-transparent transition placeholder:text-slate-400 focus:border-slate-950 focus:ring-slate-950/15 dark:border-white/20 dark:bg-slate-950/88 dark:text-white dark:caret-white dark:placeholder:text-white/42 dark:focus:border-white/40 dark:focus:ring-white/18" data-canvas-no-drag="true" data-local-edit-control="true" - onBlur={(event) => { - const relatedTarget = - event.relatedTarget instanceof HTMLElement - ? event.relatedTarget - : null; - const nextPrompt = event.currentTarget.value; - if (nextPrompt.trim()) { - persistLocalEditRegionPrompt(region.id, nextPrompt); - setEditingLocalEditRegionId(null); - return; - } - if ( - editingLocalEditRegionId === region.id && - !relatedTarget?.closest("[data-local-edit-control='true']") - ) { - focusLocalEditPromptInput(region.id); - } - }} - onChange={(event) => - updateLocalEditRegionPrompt(region.id, event.target.value) + defaultValue={ + localEditPromptDraftRefs.current[region.id] ?? region.prompt } - onClick={(event) => { - event.stopPropagation(); - focusLocalEditPromptInput(region.id); + onBlur={(event) => { + const nextPrompt = event.currentTarget.value; + localEditPromptDraftRefs.current[region.id] = nextPrompt; + persistLocalEditRegionPrompt(region.id, nextPrompt); + setEditingLocalEditRegionId(null); + }} + onClick={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); + event.stopPropagation(); + event.currentTarget.focus({ preventScroll: true }); + }} + onFocus={(event) => { + event.stopPropagation(); + setEditingLocalEditRegionId((current) => + current === region.id ? current : region.id + ); + }} + onInput={(event) => { + localEditPromptDraftRefs.current[region.id] = + event.currentTarget.value; }} - onFocus={() => setEditingLocalEditRegionId(region.id)} onKeyDown={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); event.stopPropagation(); if (event.key === "Enter") { event.preventDefault(); @@ -8204,19 +8201,32 @@ export function ImageNode({ setEditingLocalEditRegionId(null); } }} - onKeyUp={(event) => event.stopPropagation()} - onMouseDown={(event) => event.stopPropagation()} - onMouseDownCapture={(event) => event.stopPropagation()} - onPointerDown={(event) => event.stopPropagation()} - onPointerDownCapture={(event) => { + onKeyUp={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); event.stopPropagation(); - focusLocalEditPromptInput(region.id); + }} + onMouseDown={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); + event.stopPropagation(); + }} + onMouseDownCapture={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); + event.stopPropagation(); + event.currentTarget.focus({ preventScroll: true }); + }} + onPointerDown={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); + event.stopPropagation(); + }} + onPointerDownCapture={(event) => { + event.nativeEvent.stopImmediatePropagation?.(); + event.stopPropagation(); + event.currentTarget.focus({ preventScroll: true }); }} placeholder={`区域 ${region.index} 提示词`} ref={(input) => { localEditPromptInputRefs.current[region.id] = input; }} - value={region.prompt} /> ) : (