diff --git a/src/renderer/src/components/editor/CombinedDiffViewer.tsx b/src/renderer/src/components/editor/CombinedDiffViewer.tsx index bbce4491602..0c557c13b44 100644 --- a/src/renderer/src/components/editor/CombinedDiffViewer.tsx +++ b/src/renderer/src/components/editor/CombinedDiffViewer.tsx @@ -441,11 +441,19 @@ export default function CombinedDiffViewer({ // find their place again. useEffect(() => { const cached = combinedDiffViewStateCache.get(viewStateKey) + // Why: the save-side signature is '' for branch/commit modes (only the + // uncommitted tree tracks git status), so the restore-side recompute must + // apply the same treeMode short-circuit. Otherwise '' can never equal the + // recomputed '[]'/'[...]', and branch/commit combined diffs would always + // discard their cached sections and re-enter Loading on every remount. + const cachedGitStatusSignature = + cached && treeMode === 'uncommitted' + ? buildCombinedGitStatusSignature(cached.sections, gitStatusEntries) + : '' const canRestoreCachedSections = cached && cached.entrySignature === entrySignature && - (cached.gitStatusSignature ?? '') === - buildCombinedGitStatusSignature(cached.sections, gitStatusEntries) && + (cached.gitStatusSignature ?? '') === cachedGitStatusSignature && (cached.sections.length > 0 || entries.length === 0) if (canRestoreCachedSections && cached) { const collapsedPreference = combinedDiffCollapsedPreference @@ -493,7 +501,7 @@ export default function CombinedDiffViewer({ loadSchedulerRef.current.reset() generationRef.current += 1 setGeneration((prev) => prev + 1) - }, [entries, entrySignature, file.diffSource, gitStatusEntries, viewStateKey]) + }, [entries, entrySignature, file.diffSource, gitStatusEntries, treeMode, viewStateKey]) const loadSectionNow = useCallback( async (index: number) => { diff --git a/src/renderer/src/components/editor/EditorContent.tsx b/src/renderer/src/components/editor/EditorContent.tsx index ceee3f2013d..3e226687b36 100644 --- a/src/renderer/src/components/editor/EditorContent.tsx +++ b/src/renderer/src/components/editor/EditorContent.tsx @@ -851,10 +851,24 @@ export function EditorContent({ // edit-buffer text, so editable unstaged diffs keep their undo stack. const diffReloadNonce = activeFile.diffContentReloadNonce ?? 0 const originalModelKey = `${diffViewStateKey}:original:${getDiffContentSignature(dc.originalContent)}` - const modifiedModelKey = `${diffViewStateKey}:modified:${getDiffContentSignature(dc.modifiedContent)}:${diffReloadNonce}` + // Why: for editable diffs the modified side is the working tree the user is + // editing. Saving rewrites dc.modifiedContent (via the FILE_SAVED handler), + // so embedding its signature in the modified-model identity / component key + // would remount Monaco on every save and wipe the undo stack the user just + // built. Rotate the modified side on the explicit reload nonce instead; a + // kept modified model still repaints refreshed git blobs through the + // `modified` prop's undo-preserving executeEdits. Non-editable diffs have no + // undo to lose, so they keep rotating on the content signature. + const modifiedContentSignature = getDiffContentSignature(dc.modifiedContent) + const modifiedModelKey = isEditable + ? `${diffViewStateKey}:modified:${diffReloadNonce}` + : `${diffViewStateKey}:modified:${modifiedContentSignature}:${diffReloadNonce}` + const diffViewerKey = isEditable + ? `${viewStateScopeId}:${diffReloadNonce}` + : `${viewStateScopeId}:${diffReloadNonce}:${modifiedContentSignature}` return (