Files
makelore/src/components/file-preview/MonacoDiffViewer.tsx
T

78 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Monaco split diff editor (VS Code–style). Replaces the custom table-based
* `SplitDiffViewer`: same Monaco bundle as `MonacoViewer`, with syntax
* highlighting, scroll sync, and virtualisation for large files.
*/
import { useMemo } from 'react';
import { DiffEditor, languageForPath } from '@/lib/monaco/loader';
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
import { cn } from '@/lib/utils';
export interface MonacoDiffViewerProps {
filePath: string;
/** Left pane; `null`/`undefined` treated as empty (e.g. new file → all additions). */
original: string | null | undefined;
modified: string;
className?: string;
}
function diffModelPath(filePath: string, side: 'orig' | 'mod'): string {
const key = encodeURIComponent(filePath.replace(/\\/g, '/'));
return `inmemory://niancode-diff/${side}/${key}`;
}
export default function MonacoDiffViewer({
filePath,
original,
modified,
className,
}: MonacoDiffViewerProps) {
const language = useMemo(() => languageForPath(filePath), [filePath]);
const originalPath = useMemo(() => diffModelPath(filePath, 'orig'), [filePath]);
const modifiedPath = useMemo(() => diffModelPath(filePath, 'mod'), [filePath]);
const left = original ?? '';
return (
<div
data-testid="monaco-diff-viewer"
className={cn('niancode-diff-editor h-full w-full min-h-0', className)}
>
<DiffEditor
height="100%"
language={language}
original={left}
modified={modified}
originalModelPath={originalPath}
modifiedModelPath={modifiedPath}
theme="vs"
loading={
<div className="flex h-full items-center justify-center">
<LoadingSpinner />
</div>
}
options={{
readOnly: true,
domReadOnly: true,
originalEditable: false,
renderSideBySide: true,
useInlineViewWhenSpaceIsLimited: false,
renderIndicators: true,
renderOverviewRuler: true,
renderMarginRevertIcon: false,
minimap: { enabled: false },
fontSize: 13,
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',
scrollBeyondLastLine: false,
smoothScrolling: true,
wordWrap: 'on',
diffWordWrap: 'on',
automaticLayout: true,
guides: { indentation: false },
stickyScroll: { enabled: false },
padding: { top: 8, bottom: 8 },
}}
/>
</div>
);
}