fix: rely on native local edit textarea input
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:51:26 +08:00
parent 852498b6bb
commit 87a1514a0f

View File

@ -235,12 +235,6 @@ type LocalEditRegionInteractionState = {
startRect: NormalizedCropRect;
};
type LocalEditPromptCompositionSnapshot = {
end: number;
start: number;
value: string;
};
type AnglePreviewDragState = {
pointerId: number;
startX: number;
@ -2072,10 +2066,6 @@ export function ImageNode({
Record<string, HTMLTextAreaElement | null>
>({});
const localEditPromptDraftRefs = useRef<Record<string, string>>({});
const localEditPromptCompositionStartRefs = useRef<
Record<string, LocalEditPromptCompositionSnapshot | undefined>
>({});
const localEditPromptComposingRefs = useRef<Record<string, boolean>>({});
const localEditRegionsRef = useRef<LocalEditRegion[]>(localEditRegions);
const updateNode = useCanvasStore((state) => state.updateNode);
@ -2683,110 +2673,6 @@ export function ImageNode({
[]
);
const restoreLocalEditPromptSelection = useCallback(
(regionId: string, position: number) => {
if (typeof window === "undefined") return;
window.requestAnimationFrame(() => {
const input = localEditPromptInputRefs.current[regionId];
if (!input) return;
const safePosition = clampNumber(position, 0, input.value.length);
input.setSelectionRange(safePosition, safePosition);
});
},
[]
);
const applyLocalEditPromptTextEdit = useCallback(
(
regionId: string,
input: HTMLTextAreaElement,
inputEvent: InputEvent
) => {
const inputType = inputEvent.inputType || "";
if (!inputType || inputEvent.isComposing) {
return false;
}
if (inputType === "insertCompositionText") {
return false;
}
const currentValue = localEditPromptDraftRefs.current[regionId] ?? input.value;
const selectionStart = input.selectionStart ?? currentValue.length;
const selectionEnd = input.selectionEnd ?? selectionStart;
const start = clampNumber(selectionStart, 0, currentValue.length);
const end = clampNumber(selectionEnd, start, currentValue.length);
let nextValue: string | null = null;
let nextCursor = start;
if (inputType.startsWith("insert")) {
const insertedText = inputEvent.data ?? "";
if (!insertedText) {
return false;
}
nextValue = `${currentValue.slice(0, start)}${insertedText}${currentValue.slice(end)}`;
nextCursor = start + insertedText.length;
} else if (inputType === "deleteContentBackward") {
if (start !== end) {
nextValue = `${currentValue.slice(0, start)}${currentValue.slice(end)}`;
nextCursor = start;
} else if (start > 0) {
nextValue = `${currentValue.slice(0, start - 1)}${currentValue.slice(end)}`;
nextCursor = start - 1;
} else {
nextValue = currentValue;
nextCursor = start;
}
} else if (inputType === "deleteContentForward") {
if (start !== end) {
nextValue = `${currentValue.slice(0, start)}${currentValue.slice(end)}`;
} else if (start < currentValue.length) {
nextValue = `${currentValue.slice(0, start)}${currentValue.slice(start + 1)}`;
} else {
nextValue = currentValue;
}
nextCursor = start;
}
if (nextValue === null) {
return false;
}
updateLocalEditPromptDraft(regionId, nextValue);
restoreLocalEditPromptSelection(regionId, nextCursor);
return true;
},
[restoreLocalEditPromptSelection, updateLocalEditPromptDraft]
);
const applyLocalEditPromptCompositionFallback = useCallback(
(
regionId: string,
input: HTMLTextAreaElement,
compositionText: string
) => {
const snapshot = localEditPromptCompositionStartRefs.current[regionId];
delete localEditPromptCompositionStartRefs.current[regionId];
if (!snapshot || !compositionText) {
updateLocalEditPromptDraft(regionId, input.value);
return;
}
const draftValue = localEditPromptDraftRefs.current[regionId] ?? input.value;
if (input.value !== snapshot.value || draftValue !== snapshot.value) {
updateLocalEditPromptDraft(regionId, input.value);
return;
}
const nextValue = `${snapshot.value.slice(0, snapshot.start)}${compositionText}${snapshot.value.slice(snapshot.end)}`;
updateLocalEditPromptDraft(regionId, nextValue);
restoreLocalEditPromptSelection(
regionId,
snapshot.start + compositionText.length
);
},
[restoreLocalEditPromptSelection, updateLocalEditPromptDraft]
);
const refreshBoxCutoutMetrics = useCallback(() => {
const imageElement = displayImageRef.current;
if (!imageElement) {
@ -3885,8 +3771,6 @@ export function ImageNode({
delete next[regionId];
return next;
});
delete localEditPromptCompositionStartRefs.current[regionId];
delete localEditPromptComposingRefs.current[regionId];
commitLocalEditRegions(
localEditRegionsRef.current.filter((region) => region.id !== regionId)
);
@ -3988,7 +3872,6 @@ export function ImageNode({
const clearLocalEditRegions = useCallback(() => {
localEditPromptDraftRefs.current = {};
localEditPromptCompositionStartRefs.current = {};
setLocalEditPromptDrafts({});
commitLocalEditRegions([]);
setEditingLocalEditRegionId(null);
@ -8252,43 +8135,6 @@ export function ImageNode({
onClick={(event) => {
event.stopPropagation();
}}
onBeforeInput={(event) => {
if (
applyLocalEditPromptTextEdit(
region.id,
event.currentTarget,
event.nativeEvent as InputEvent
)
) {
event.preventDefault();
}
}}
onCompositionEnd={(event) => {
localEditPromptComposingRefs.current[region.id] =
false;
applyLocalEditPromptCompositionFallback(
region.id,
event.currentTarget,
event.data
);
}}
onCompositionStart={(event) => {
localEditPromptComposingRefs.current[region.id] =
true;
localEditPromptCompositionStartRefs.current[
region.id
] = {
end:
event.currentTarget.selectionEnd ??
event.currentTarget.value.length,
start:
event.currentTarget.selectionStart ??
event.currentTarget.value.length,
value:
localEditPromptDraftRefs.current[region.id] ??
event.currentTarget.value,
};
}}
onFocus={(event) => {
event.stopPropagation();
setEditingLocalEditRegionId((current) =>
@ -8304,8 +8150,7 @@ export function ImageNode({
onKeyDown={(event) => {
const isComposing =
event.nativeEvent.isComposing ||
event.key === "Process" ||
localEditPromptComposingRefs.current[region.id];
event.key === "Process";
if (isComposing) {
return;
}