Files
CodeNomad/packages/ui/src/components/file-viewer/monaco-diff-viewer.tsx
VooDisss d36e568ed0 fix: Use legacy diff algorithm for better large file performance
- Set diffAlgorithm to 'legacy' for Monaco DiffEditor
- Add maxComputationTime of 10s to avoid UI freeze on huge files

This addresses the issue where sessions with large JSON files (50k-100k+ lines)
would cause the UI to freeze. The 'legacy' algorithm is faster than 'advanced'
for large files, similar to VSCode's workaround for the same issue.

See: https://github.com/microsoft/vscode/issues/184037
2026-02-23 02:30:44 +02:00

137 lines
4.0 KiB
TypeScript

import { createEffect, createSignal, onCleanup, onMount } from "solid-js"
import { loadMonaco } from "../../lib/monaco/setup"
import { getOrCreateTextModel } from "../../lib/monaco/model-cache"
import { inferMonacoLanguageId } from "../../lib/monaco/language"
import { ensureMonacoLanguageLoaded } from "../../lib/monaco/setup"
import { useTheme } from "../../lib/theme"
interface MonacoDiffViewerProps {
scopeKey: string
path: string
before: string
after: string
viewMode?: "split" | "unified"
contextMode?: "expanded" | "collapsed"
wordWrap?: "on" | "off"
}
export function MonacoDiffViewer(props: MonacoDiffViewerProps) {
const { isDark } = useTheme()
let host: HTMLDivElement | undefined
let diffEditor: any = null
let monaco: any = null
const [ready, setReady] = createSignal(false)
const disposeEditor = () => {
try {
diffEditor?.setModel(null as any)
} catch {
// ignore
}
try {
diffEditor?.dispose()
} catch {
// ignore
}
diffEditor = null
}
onMount(() => {
let cancelled = false
void (async () => {
monaco = await loadMonaco()
if (cancelled) return
if (!host || !monaco) return
monaco.editor.setTheme(isDark() ? "vs-dark" : "vs")
diffEditor = monaco.editor.createDiffEditor(host, {
readOnly: true,
automaticLayout: true,
renderSideBySide: true,
renderSideBySideInlineBreakpoint: 0,
renderMarginRevertIcon: false,
minimap: { enabled: false },
scrollBeyondLastLine: false,
renderWhitespace: "selection",
fontSize: 13,
wordWrap: props.wordWrap === "on" ? "on" : "off",
glyphMargin: false,
folding: false,
// Keep enough gutter space so unified diffs don't overlap `+`/`-` markers.
lineNumbersMinChars: 4,
lineDecorationsWidth: 12,
// Use legacy diff algorithm for better performance with large files
// See: https://github.com/microsoft/vscode/issues/184037
diffAlgorithm: "legacy",
// Limit computation time to avoid freezing on large files
maxComputationTime: 10000,
})
setReady(true)
})()
onCleanup(() => {
cancelled = true
setReady(false)
disposeEditor()
})
})
createEffect(() => {
if (!ready() || !monaco || !diffEditor) return
monaco.editor.setTheme(isDark() ? "vs-dark" : "vs")
})
createEffect(() => {
if (!ready() || !monaco || !diffEditor) return
const viewMode = props.viewMode === "unified" ? "unified" : "split"
const contextMode = props.contextMode === "collapsed" ? "collapsed" : "expanded"
const wordWrap = props.wordWrap === "on" ? "on" : "off"
diffEditor.updateOptions({
renderSideBySide: viewMode === "split",
renderSideBySideInlineBreakpoint: 0,
hideUnchangedRegions:
contextMode === "collapsed"
? { enabled: true }
: { enabled: false },
wordWrap,
})
try {
diffEditor.getOriginalEditor?.()?.updateOptions?.({ wordWrap })
} catch {
// ignore
}
try {
diffEditor.getModifiedEditor?.()?.updateOptions?.({ wordWrap })
} catch {
// ignore
}
})
createEffect(() => {
if (!ready() || !monaco || !diffEditor) return
const languageId = inferMonacoLanguageId(monaco, props.path)
const beforeKey = `${props.scopeKey}:diff:${props.path}:before`
const afterKey = `${props.scopeKey}:diff:${props.path}:after`
const original = getOrCreateTextModel({ monaco, cacheKey: beforeKey, value: props.before, languageId })
const modified = getOrCreateTextModel({ monaco, cacheKey: afterKey, value: props.after, languageId })
diffEditor.setModel({ original, modified })
void ensureMonacoLanguageLoaded(languageId).then(() => {
try {
monaco.editor.setModelLanguage(original, languageId)
monaco.editor.setModelLanguage(modified, languageId)
} catch {
// ignore
}
})
})
return <div class="monaco-viewer" ref={host} />
}