fix: prevent local edit prompt focus loss
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-05-19 16:43:10 +08:00
parent 7421e158d1
commit d370c68b43

View File

@ -1984,6 +1984,7 @@ export function ImageNode({
const localEditPromptInputRefs = useRef<
Record<string, HTMLInputElement | null>
>({});
const localEditPromptDraftRefs = useRef<Record<string, string>>({});
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}
/>
) : (
<button
@ -8286,8 +8296,7 @@ export function ImageNode({
className="mr-1 inline-flex h-10 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-xl bg-emerald-600 px-3 font-semibold text-[13px] text-white hover:bg-emerald-500 hover:text-white disabled:bg-slate-300 disabled:text-slate-500 dark:bg-emerald-500 dark:disabled:bg-slate-700 dark:disabled:text-slate-400 dark:hover:bg-emerald-400"
disabled={
isSubmittingLocalEdit ||
localEditRegions.length === 0 ||
localEditRegions.some((region) => !region.prompt.trim())
localEditRegions.length === 0
}
onClick={() => void handleSubmitLocalEdit()}
title="生成局部编辑结果"