fix: handle local edit prompt beforeinput
This commit is contained in:
parent
b5c7eeac5a
commit
15bc64984d
@ -235,6 +235,12 @@ type LocalEditRegionInteractionState = {
|
||||
startRect: NormalizedCropRect;
|
||||
};
|
||||
|
||||
type LocalEditPromptCompositionSnapshot = {
|
||||
end: number;
|
||||
start: number;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type AnglePreviewDragState = {
|
||||
pointerId: number;
|
||||
startX: number;
|
||||
@ -2060,6 +2066,9 @@ 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);
|
||||
|
||||
@ -2601,6 +2610,110 @@ 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) {
|
||||
@ -3699,6 +3812,7 @@ export function ImageNode({
|
||||
delete next[regionId];
|
||||
return next;
|
||||
});
|
||||
delete localEditPromptCompositionStartRefs.current[regionId];
|
||||
delete localEditPromptComposingRefs.current[regionId];
|
||||
commitLocalEditRegions(
|
||||
localEditRegionsRef.current.filter((region) => region.id !== regionId)
|
||||
@ -3801,6 +3915,7 @@ export function ImageNode({
|
||||
|
||||
const clearLocalEditRegions = useCallback(() => {
|
||||
localEditPromptDraftRefs.current = {};
|
||||
localEditPromptCompositionStartRefs.current = {};
|
||||
setLocalEditPromptDrafts({});
|
||||
commitLocalEditRegions([]);
|
||||
setEditingLocalEditRegionId(null);
|
||||
@ -8051,17 +8166,42 @@ 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;
|
||||
updateLocalEditPromptDraft(
|
||||
applyLocalEditPromptCompositionFallback(
|
||||
region.id,
|
||||
event.currentTarget.value
|
||||
event.currentTarget,
|
||||
event.data
|
||||
);
|
||||
}}
|
||||
onCompositionStart={() => {
|
||||
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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user