1- import { DiffEditor , type Monaco , type DiffOnMount , type MonacoDiffEditor } from '@monaco-editor/react'
1+ import '~/utils/monaco-setup'
2+ import * as monaco from 'monaco-editor'
23import { useShallow } from 'zustand/react/shallow'
3- import { useCallback , useEffect , useMemo , useRef , useState } from 'react'
4+ import { useEffect , useMemo , useRef , useState } from 'react'
45import { useTheme } from '~/hooks/use-theme'
56import { useGitStore } from '~/stores/git-store'
67import type { DiffTabData } from '~/stores/editor-tab-store'
78import type { GitHunk } from '~/types/git.types'
89import Checkbox from '~/components/inputs/checkbox'
910
10- type CodeEditor = ReturnType < MonacoDiffEditor [ 'getModifiedEditor' ] >
11+ type CodeEditor = monaco . editor . IStandaloneCodeEditor
1112
1213function getLanguage ( filePath : string ) : string {
1314 if ( filePath . endsWith ( '.xml' ) ) return 'xml'
@@ -17,7 +18,6 @@ function getLanguage(filePath: string): string {
1718
1819function applyHunkDecorations (
1920 modifiedEditor : CodeEditor ,
20- monaco : Monaco ,
2121 hunks : GitHunk [ ] ,
2222 selectedHunks : Set < number > ,
2323 prevDecorations : string [ ] ,
@@ -77,11 +77,12 @@ export default function DiffTabView({ diffData }: DiffTabViewProps) {
7777
7878 const filePath = diffData . filePath
7979 const hunks = diffData . hunks
80+ const language = getLanguage ( filePath )
8081 const hunkState = fileHunkStates [ filePath ]
8182 const selectedHunks = useMemo ( ( ) => hunkState ?. selectedHunks ?? new Set < number > ( ) , [ hunkState ?. selectedHunks ] )
8283
83- const diffEditorRef = useRef < MonacoDiffEditor | null > ( null )
84- const monacoRef = useRef < Monaco | null > ( null )
84+ const containerRef = useRef < HTMLDivElement > ( null )
85+ const diffEditorRef = useRef < monaco . editor . IStandaloneDiffEditor | null > ( null )
8586 const decorationsRef = useRef < string [ ] > ( [ ] )
8687 const [ editorReady , setEditorReady ] = useState ( false )
8788
@@ -93,27 +94,30 @@ export default function DiffTabView({ diffData }: DiffTabViewProps) {
9394 filePathRef . current = filePath
9495
9596 useEffect ( ( ) => {
96- if ( ! editorReady || ! diffEditorRef . current || ! monacoRef . current ) return
97+ if ( ! editorReady || ! diffEditorRef . current ) return
9798 const modifiedEditor = diffEditorRef . current . getModifiedEditor ( )
98- decorationsRef . current = applyHunkDecorations (
99- modifiedEditor ,
100- monacoRef . current ,
101- hunks ,
102- selectedHunks ,
103- decorationsRef . current ,
104- )
99+ decorationsRef . current = applyHunkDecorations ( modifiedEditor , hunks , selectedHunks , decorationsRef . current )
105100 } , [ hunks , selectedHunks , editorReady ] )
106101
107- const handleDiffMount : DiffOnMount = useCallback ( ( diffEditor : MonacoDiffEditor , monaco : Monaco ) => {
102+ useEffect ( ( ) => {
103+ if ( ! containerRef . current ) return
104+
105+ const diffEditor = monaco . editor . createDiffEditor ( containerRef . current , {
106+ readOnly : true ,
107+ automaticLayout : true ,
108+ renderSideBySide : true ,
109+ minimap : { enabled : false } ,
110+ scrollBeyondLastLine : false ,
111+ glyphMargin : true ,
112+ } )
113+
108114 diffEditorRef . current = diffEditor
109- monacoRef . current = monaco
110115
111116 const modifiedEditor = diffEditor . getModifiedEditor ( )
112117 modifiedEditor . updateOptions ( { glyphMargin : true } )
113118
114119 decorationsRef . current = applyHunkDecorations (
115120 modifiedEditor ,
116- monaco ,
117121 hunksRef . current ,
118122 useGitStore . getState ( ) . fileHunkStates [ filePathRef . current ] ?. selectedHunks ?? new Set ( ) ,
119123 decorationsRef . current ,
@@ -136,8 +140,33 @@ export default function DiffTabView({ diffData }: DiffTabViewProps) {
136140 } )
137141
138142 setEditorReady ( true )
143+
144+ return ( ) => {
145+ diffEditor . dispose ( )
146+ diffEditorRef . current = null
147+ decorationsRef . current = [ ]
148+ setEditorReady ( false )
149+ }
139150 } , [ ] )
140151
152+ useEffect ( ( ) => {
153+ const diffEditor = diffEditorRef . current
154+ if ( ! diffEditor || ! editorReady ) return
155+
156+ const originalModel = monaco . editor . createModel ( diffData . oldContent , language )
157+ const modifiedModel = monaco . editor . createModel ( diffData . newContent , language )
158+ diffEditor . setModel ( { original : originalModel , modified : modifiedModel } )
159+
160+ return ( ) => {
161+ originalModel . dispose ( )
162+ modifiedModel . dispose ( )
163+ }
164+ } , [ diffData , language , editorReady ] )
165+
166+ useEffect ( ( ) => {
167+ monaco . editor . setTheme ( theme === 'dark' ? 'vs-dark' : 'vs' )
168+ } , [ theme ] )
169+
141170 const selectableHunks = hunks . filter ( ( hunk ) => hunk . newCount > 0 )
142171 const allSelected = selectableHunks . length > 0 && selectableHunks . every ( ( hunk ) => selectedHunks . has ( hunk . index ) )
143172 const someSelected = selectableHunks . some ( ( hunk ) => selectedHunks . has ( hunk . index ) )
@@ -151,8 +180,6 @@ export default function DiffTabView({ diffData }: DiffTabViewProps) {
151180 }
152181 }
153182
154- const language = getLanguage ( filePath )
155-
156183 return (
157184 < >
158185 < div className = "border-b-border bg-background flex h-12 items-center gap-3 border-b px-4" >
@@ -165,21 +192,7 @@ export default function DiffTabView({ diffData }: DiffTabViewProps) {
165192 < span className = "text-sm font-medium" > { filePath } </ span >
166193 </ div >
167194 < div className = "min-h-0 flex-1" >
168- < DiffEditor
169- original = { diffData . oldContent }
170- modified = { diffData . newContent }
171- language = { language }
172- theme = { `vs-${ theme } ` }
173- onMount = { handleDiffMount }
174- options = { {
175- readOnly : true ,
176- automaticLayout : true ,
177- renderSideBySide : true ,
178- minimap : { enabled : false } ,
179- scrollBeyondLastLine : false ,
180- glyphMargin : true ,
181- } }
182- />
195+ < div ref = { containerRef } className = "h-full w-full" />
183196 </ div >
184197 </ >
185198 )
0 commit comments