fix: stabilize local edit prompt typing
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-23 14:03:50 +08:00
parent 862c0f2937
commit 9cbbc7f065

View File

@ -994,6 +994,17 @@ const getLocalEditRegionsFromMetadata = (
metadata: Record<string, any> | undefined
) => normalizeLocalEditRegions(metadata?.[LOCAL_EDIT_METADATA_KEY]);
const createLocalEditPromptDraftMap = (
regions: LocalEditRegion[],
draftSource?: Record<string, string>
): Record<string, string> =>
Object.fromEntries(
regions.map((region) => [
region.id,
draftSource?.[region.id] ?? region.prompt,
])
);
const getNormalizedRectOverlayStyle = (
rect: NormalizedCropRect,
metrics: ImageDisplayMetrics
@ -1877,6 +1888,9 @@ export function ImageNode({
const [localEditRegions, setLocalEditRegions] = useState<LocalEditRegion[]>(
() => getLocalEditRegionsFromMetadata(data.metadata)
);
const [localEditPromptDrafts, setLocalEditPromptDrafts] = useState<
Record<string, string>
>({});
const [isLocalEditDrawing, setIsLocalEditDrawing] = useState(false);
const [localEditDragStart, setLocalEditDragStart] =
useState<BoxCutoutPoint | null>(null);
@ -2547,11 +2561,14 @@ export function ImageNode({
const nextRegions = reindexLocalEditRegions(
regions.slice(0, LOCAL_EDIT_MAX_REGIONS)
);
const nextDrafts = createLocalEditPromptDraftMap(
nextRegions,
localEditPromptDraftRefs.current
);
localEditRegionsRef.current = nextRegions;
setLocalEditRegions(nextRegions);
localEditPromptDraftRefs.current = Object.fromEntries(
nextRegions.map((region) => [region.id, region.prompt])
);
localEditPromptDraftRefs.current = nextDrafts;
setLocalEditPromptDrafts(nextDrafts);
const nextMetadata =
data.metadata && typeof data.metadata === "object"
@ -2574,6 +2591,21 @@ export function ImageNode({
[data, node.id, updateNode]
);
const updateLocalEditPromptDraft = useCallback(
(regionId: string, prompt: string) => {
localEditPromptDraftRefs.current[regionId] = prompt;
setLocalEditPromptDrafts((current) =>
current[regionId] === prompt
? current
: {
...current,
[regionId]: prompt,
}
);
},
[]
);
const refreshBoxCutoutMetrics = useCallback(() => {
const imageElement = displayImageRef.current;
if (!imageElement) {
@ -2718,7 +2750,11 @@ export function ImageNode({
useEffect(() => {
if (localEditMode) return;
setLocalEditRegions(getLocalEditRegionsFromMetadata(data.metadata));
const nextRegions = getLocalEditRegionsFromMetadata(data.metadata);
const nextDrafts = createLocalEditPromptDraftMap(nextRegions);
localEditPromptDraftRefs.current = nextDrafts;
setLocalEditPromptDrafts(nextDrafts);
setLocalEditRegions(nextRegions);
}, [data.metadata, data.src, localEditMode]);
useEffect(() => {
@ -3649,19 +3685,25 @@ export function ImageNode({
const persistLocalEditRegionPrompt = useCallback(
(regionId: string, prompt: string) => {
localEditPromptDraftRefs.current[regionId] = prompt;
updateLocalEditPromptDraft(regionId, prompt);
commitLocalEditRegions(
localEditRegions.map((region) =>
localEditRegionsRef.current.map((region) =>
region.id === regionId ? { ...region, prompt } : region
)
);
},
[commitLocalEditRegions, localEditRegions]
[commitLocalEditRegions, updateLocalEditPromptDraft]
);
const deleteLocalEditRegion = useCallback(
(regionId: string) => {
delete localEditPromptDraftRefs.current[regionId];
setLocalEditPromptDrafts((current) => {
if (!(regionId in current)) return current;
const next = { ...current };
delete next[regionId];
return next;
});
delete localEditPromptComposingRefs.current[regionId];
commitLocalEditRegions(
localEditRegionsRef.current.filter((region) => region.id !== regionId)
@ -3764,6 +3806,7 @@ export function ImageNode({
const clearLocalEditRegions = useCallback(() => {
localEditPromptDraftRefs.current = {};
setLocalEditPromptDrafts({});
commitLocalEditRegions([]);
setEditingLocalEditRegionId(null);
}, [commitLocalEditRegions]);
@ -7996,45 +8039,31 @@ export function ImageNode({
className="nodrag nopan nowheel h-9 min-w-0 flex-1 resize-none overflow-hidden rounded-full border border-slate-300 bg-white/95 px-3 py-2 text-[12px] font-medium leading-5 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"
defaultValue={
autoComplete="off"
enterKeyHint="done"
inputMode="text"
lang="zh-CN"
value={
localEditPromptDrafts[region.id] ??
localEditPromptDraftRefs.current[region.id] ??
region.prompt
}
autoComplete="off"
enterKeyHint="done"
lang="zh-CN"
onBlur={(event) => {
const nextPrompt = event.currentTarget.value;
localEditPromptDraftRefs.current[region.id] =
nextPrompt;
updateLocalEditPromptDraft(region.id, nextPrompt);
persistLocalEditRegionPrompt(region.id, nextPrompt);
if (isLocalEditControlTarget(event.relatedTarget)) {
return;
}
window.setTimeout(() => {
if (
isLocalEditControlTarget(document.activeElement)
) {
return;
}
setEditingLocalEditRegionId((current) => {
if (current === region.id) {
focusLocalEditPromptInput(region.id);
}
return current;
});
}, 80);
}}
onClick={(event) => {
event.stopPropagation();
event.currentTarget.focus({ preventScroll: true });
}}
onCompositionEnd={(event) => {
stopLocalEditPromptTextEvent(event);
localEditPromptComposingRefs.current[region.id] =
false;
localEditPromptDraftRefs.current[region.id] =
event.currentTarget.value;
updateLocalEditPromptDraft(
region.id,
event.currentTarget.value
);
}}
onCompositionStart={(event) => {
stopLocalEditPromptTextEvent(event);
@ -8052,13 +8081,17 @@ export function ImageNode({
}}
onInput={(event) => {
stopLocalEditPromptTextEvent(event);
localEditPromptDraftRefs.current[region.id] =
event.currentTarget.value;
updateLocalEditPromptDraft(
region.id,
event.currentTarget.value
);
}}
onChange={(event) => {
stopLocalEditPromptTextEvent(event);
localEditPromptDraftRefs.current[region.id] =
event.currentTarget.value;
updateLocalEditPromptDraft(
region.id,
event.currentTarget.value
);
}}
onKeyDown={(event) => {
stopLocalEditPromptTextEvent(event);
@ -8095,17 +8128,9 @@ export function ImageNode({
onMouseDown={(event) => {
event.stopPropagation();
}}
onMouseDownCapture={(event) => {
event.stopPropagation();
event.currentTarget.focus({ preventScroll: true });
}}
onPointerDown={(event) => {
event.stopPropagation();
}}
onPointerDownCapture={(event) => {
event.stopPropagation();
event.currentTarget.focus({ preventScroll: true });
}}
placeholder={`区域 ${region.index} 提示词`}
ref={(input) => {
localEditPromptInputRefs.current[region.id] = input;