From 67871695835de5dd23bac6079fb618eddbf618cf Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:11:45 +0000 Subject: [PATCH] Handle composition input in PDF text editor (#5192) ## Summary - track IME composition state in the PDF text editor to avoid interrupting phonetic input methods - update text syncing to occur after composition completes and skip redundant updates mid-composition ## Testing - npm run lint -- --max-warnings 0 ------ [Codex Task](https://chatgpt.com/codex/tasks/task_b_693744be74148328bd3bda9150de6e56) --- build.gradle | 2 +- .../tools/pdfTextEditor/PdfTextEditorView.tsx | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 85f6a480d..64908ee4a 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ repositories { allprojects { group = 'stirling.software' - version = '2.1.1' + version = '2.1.2' configurations.configureEach { exclude group: 'commons-logging', module: 'commons-logging' diff --git a/frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx b/frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx index 4eeff845b..3c5b45e0e 100644 --- a/frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx +++ b/frontend/src/core/components/tools/pdfTextEditor/PdfTextEditorView.tsx @@ -334,6 +334,7 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { const containerRef = useRef(null); const editorRefs = useRef>(new Map()); const caretOffsetsRef = useRef>(new Map()); + const composingGroupsRef = useRef>(new Set()); const lastSelectedGroupIdRef = useRef(null); const widthOverridesRef = useRef>(widthOverrides); const resizingRef = useRef<{ @@ -614,6 +615,18 @@ const PdfTextEditorView = ({ data }: PdfTextEditorViewProps) => { [editingGroupId, onGroupEdit], ); + const handleCompositionStart = useCallback((groupId: string) => { + composingGroupsRef.current.add(groupId); + }, []); + + const handleCompositionEnd = useCallback( + (element: HTMLElement, pageIndex: number, groupId: string) => { + composingGroupsRef.current.delete(groupId); + syncEditorValue(element, pageIndex, groupId); + }, + [syncEditorValue], + ); + const handleMergeSelection = useCallback(() => { if (!canMergeSelection) { return; @@ -2279,6 +2292,10 @@ const selectionToolbarPosition = useMemo(() => { contentEditable suppressContentEditableWarning data-editor-group={group.id} + onCompositionStart={() => handleCompositionStart(group.id)} + onCompositionEnd={(event) => + handleCompositionEnd(event.currentTarget, group.pageIndex, group.id) + } onFocus={(event) => { const primaryFont = fontFamily.split(',')[0]?.replace(/['"]/g, '').trim(); if (primaryFont && typeof document !== 'undefined') { @@ -2300,6 +2317,7 @@ const selectionToolbarPosition = useMemo(() => { event.stopPropagation(); }} onBlur={(event) => { + composingGroupsRef.current.delete(group.id); syncEditorValue(event.currentTarget, group.pageIndex, group.id, { skipCaretRestore: true, }); @@ -2309,6 +2327,9 @@ const selectionToolbarPosition = useMemo(() => { setEditingGroupId(null); }} onInput={(event) => { + if (composingGroupsRef.current.has(group.id)) { + return; + } syncEditorValue(event.currentTarget, group.pageIndex, group.id); }} style={{