)}
Modified{new Date().toLocaleTimeString()}
>
) : (
No Changes
)}
);
},
);
// Create and manage a single highlighter instance at the module level
let highlighterInstance: any = null;
let highlighterPromise: Promise | null = null;
const getSharedHighlighter = async () => {
if (highlighterInstance) {
return highlighterInstance;
}
if (highlighterPromise) {
return highlighterPromise;
}
highlighterPromise = getHighlighter({
themes: ['github-dark', 'github-light'],
langs: [
'typescript',
'javascript',
'json',
'html',
'css',
'jsx',
'tsx',
'python',
'php',
'java',
'c',
'cpp',
'csharp',
'go',
'ruby',
'rust',
'plaintext',
],
});
highlighterInstance = await highlighterPromise;
highlighterPromise = null;
// Clear the promise once resolved
return highlighterInstance;
};
const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language }: CodeComparisonProps) => {
const [isFullscreen, setIsFullscreen] = useState(false);
// Use state to hold the shared highlighter instance
const [highlighter, setHighlighter] = useState(null);
const theme = useStore(themeStore);
const toggleFullscreen = useCallback(() => {
setIsFullscreen((prev) => !prev);
}, []);
const { unifiedBlocks, hasChanges, isBinary, error } = useProcessChanges(beforeCode, afterCode);
useEffect(() => {
// Fetch the shared highlighter instance
getSharedHighlighter().then(setHighlighter);
/*
* No cleanup needed here for the highlighter instance itself,
* as it's managed globally. Shiki instances don't typically
* need disposal unless you are dynamically loading/unloading themes/languages.
* If you were dynamically loading, you might need a more complex
* shared instance manager with reference counting or similar.
* For static themes/langs, a single instance is sufficient.
*/
}, []); // Empty dependency array ensures this runs only once on mount
if (isBinary || error) {
return renderContentWarning(isBinary ? 'binary' : 'error');
}
// Render a loading state or null while highlighter is not ready
if (!highlighter) {
return (
Loading diff...
);
}
return (
{hasChanges ? (
{unifiedBlocks.map((block, index) => (
))}
) : (
)}
);
});
interface DiffViewProps {
fileHistory: Record;
setFileHistory: React.Dispatch>>;
}
export const DiffView = memo(({ fileHistory, setFileHistory }: DiffViewProps) => {
const files = useStore(workbenchStore.files) as FileMap;
const selectedFile = useStore(workbenchStore.selectedFile);
const currentDocument = useStore(workbenchStore.currentDocument) as EditorDocument;
const unsavedFiles = useStore(workbenchStore.unsavedFiles);
useEffect(() => {
if (selectedFile && currentDocument) {
const file = files[selectedFile];
if (!file || !('content' in file)) {
return;
}
const existingHistory = fileHistory[selectedFile];
const currentContent = currentDocument.value;
// Normalizar o conteúdo para comparação
const normalizedCurrentContent = currentContent.replace(/\r\n/g, '\n').trim();
const normalizedOriginalContent = (existingHistory?.originalContent || file.content)
.replace(/\r\n/g, '\n')
.trim();
// Se não há histórico existente, criar um novo apenas se houver diferenças
if (!existingHistory) {
if (normalizedCurrentContent !== normalizedOriginalContent) {
const newChanges = diffLines(file.content, currentContent);
setFileHistory((prev) => ({
...prev,
[selectedFile]: {
originalContent: file.content,
lastModified: Date.now(),
changes: newChanges,
versions: [
{
timestamp: Date.now(),
content: currentContent,
},
],
changeSource: 'auto-save',
},
}));
}
return;
}
// Se já existe histórico, verificar se há mudanças reais desde a última versão
const lastVersion = existingHistory.versions[existingHistory.versions.length - 1];
const normalizedLastContent = lastVersion?.content.replace(/\r\n/g, '\n').trim();
if (normalizedCurrentContent === normalizedLastContent) {
return; // Não criar novo histórico se o conteúdo é o mesmo
}
// Verificar se há mudanças significativas usando diffFiles
const relativePath = extractRelativePath(selectedFile);
const unifiedDiff = diffFiles(relativePath, existingHistory.originalContent, currentContent);
if (unifiedDiff) {
const newChanges = diffLines(existingHistory.originalContent, currentContent);
// Verificar se as mudanças são significativas
const hasSignificantChanges = newChanges.some(
(change) => (change.added || change.removed) && change.value.trim().length > 0,
);
if (hasSignificantChanges) {
const newHistory: FileHistory = {
originalContent: existingHistory.originalContent,
lastModified: Date.now(),
changes: [...existingHistory.changes, ...newChanges].slice(-100), // Limitar histórico de mudanças
versions: [
...existingHistory.versions,
{
timestamp: Date.now(),
content: currentContent,
},
].slice(-10), // Manter apenas as 10 últimas versões
changeSource: 'auto-save',
};
setFileHistory((prev) => ({ ...prev, [selectedFile]: newHistory }));
}
}
}
}, [selectedFile, currentDocument?.value, files, setFileHistory, unsavedFiles]);
if (!selectedFile || !currentDocument) {
return (