From b01248cc803c268f2ea3a46293de369c7a378a97 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:04:07 -0700 Subject: [PATCH] fix(editor): sync content refs after commit --- .../editor/useEditorPanelContentState.ts | 14 +++++++++----- .../useEditorPanelExternalContentEvents.test.tsx | 11 +++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/components/editor/useEditorPanelContentState.ts b/src/renderer/src/components/editor/useEditorPanelContentState.ts index 8b428f79b0b..7d76617b708 100644 --- a/src/renderer/src/components/editor/useEditorPanelContentState.ts +++ b/src/renderer/src/components/editor/useEditorPanelContentState.ts @@ -116,7 +116,6 @@ export function useEditorPanelContentState({ const [fileContents, setFileContents] = useState>({}) const [diffContents, setDiffContents] = useState>({}) const diffContentsRef = useRef(diffContents) - diffContentsRef.current = diffContents const fileLoadRetryAttemptsRef = useRef>({}) // Why: per-tab read generations let a forced/external reload supersede an // older in-flight read so a slower stale promise cannot overwrite fresh state. @@ -125,18 +124,23 @@ export function useEditorPanelContentState({ const fileReadGenerationCounterRef = useRef(0) const diffReadGenerationCounterRef = useRef(0) const openFilesRef = useRef(openFiles) - openFilesRef.current = openFiles const editorViewModeRef = useRef(editorViewMode) - editorViewModeRef.current = editorViewMode const isVisibleRef = useRef(isVisible) - isVisibleRef.current = isVisible const selectedConflictReviewFile = activeFile?.mode === 'conflict-review' && activeFile.conflictReview?.selectedFileId ? (openFiles.find((file) => file.id === activeFile.conflictReview?.selectedFileId) ?? null) : null const activeContentFileId = selectedConflictReviewFile?.id ?? activeFile?.id ?? null const activeContentFileIdRef = useRef(activeContentFileId) - activeContentFileIdRef.current = activeContentFileId + + useEffect(() => { + // Why: event-driven readers must only observe state from committed renders. + diffContentsRef.current = diffContents + openFilesRef.current = openFiles + editorViewModeRef.current = editorViewMode + isVisibleRef.current = isVisible + activeContentFileIdRef.current = activeContentFileId + }, [activeContentFileId, diffContents, editorViewMode, isVisible, openFiles]) const invalidateFileContent = useCallback((fileIds: string[]): void => { const uniqueIds = new Set(fileIds) diff --git a/src/renderer/src/components/editor/useEditorPanelExternalContentEvents.test.tsx b/src/renderer/src/components/editor/useEditorPanelExternalContentEvents.test.tsx index aa31a65a30f..a7d9db6b968 100644 --- a/src/renderer/src/components/editor/useEditorPanelExternalContentEvents.test.tsx +++ b/src/renderer/src/components/editor/useEditorPanelExternalContentEvents.test.tsx @@ -1,6 +1,6 @@ // @vitest-environment happy-dom -import { act, useRef, useState } from 'react' +import { act, useEffect, useRef, useState } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { OpenFile } from '@/store/slices/editor' @@ -24,15 +24,18 @@ type ProbeProps = { function ExternalContentProbe({ activeFileId, calls, isVisible, openFiles }: ProbeProps): null { const activeContentFileIdRef = useRef(activeFileId) - activeContentFileIdRef.current = activeFileId const isVisibleRef = useRef(isVisible) - isVisibleRef.current = isVisible const openFilesRef = useRef(openFiles) - openFilesRef.current = openFiles const editorViewModeRef = useRef({}) const [, setFileContents] = useState>({}) const [, setDiffContents] = useState>({}) + useEffect(() => { + activeContentFileIdRef.current = activeFileId + isVisibleRef.current = isVisible + openFilesRef.current = openFiles + }, [activeFileId, isVisible, openFiles]) + useEditorPanelExternalContentEvents({ activeContentFileIdRef, editorViewModeRef,