mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
refactor(files): simplify file event processing logic
Remove redundant checks for deleted paths and streamline binary file handling. This fixes the browser using excessive memory and freezing. Improve DiffView to use a singleton instance of Shiki
This commit is contained in:
parent
0ec30e2358
commit
cfc2fc75d8
@ -542,8 +542,53 @@ const FileInfo = memo(
|
||||
},
|
||||
);
|
||||
|
||||
// Create and manage a single highlighter instance at the module level
|
||||
let highlighterInstance: any = null;
|
||||
let highlighterPromise: Promise<any> | 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<any>(null);
|
||||
const theme = useStore(themeStore);
|
||||
|
||||
@ -554,34 +599,32 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language }
|
||||
const { unifiedBlocks, hasChanges, isBinary, error } = useProcessChanges(beforeCode, afterCode);
|
||||
|
||||
useEffect(() => {
|
||||
getHighlighter({
|
||||
themes: ['github-dark', 'github-light'],
|
||||
langs: [
|
||||
'typescript',
|
||||
'javascript',
|
||||
'json',
|
||||
'html',
|
||||
'css',
|
||||
'jsx',
|
||||
'tsx',
|
||||
'python',
|
||||
'php',
|
||||
'java',
|
||||
'c',
|
||||
'cpp',
|
||||
'csharp',
|
||||
'go',
|
||||
'ruby',
|
||||
'rust',
|
||||
'plaintext',
|
||||
],
|
||||
}).then(setHighlighter);
|
||||
}, []);
|
||||
// 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 (
|
||||
<div className="h-full flex items-center justify-center">
|
||||
<div className="text-bolt-elements-textTertiary">Loading diff...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FullscreenOverlay isFullscreen={isFullscreen}>
|
||||
<div className="w-full h-full flex flex-col">
|
||||
@ -602,7 +645,7 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language }
|
||||
lineNumber={block.lineNumber}
|
||||
content={block.content}
|
||||
type={block.type}
|
||||
highlighter={highlighter}
|
||||
highlighter={highlighter} // Pass the shared instance
|
||||
language={language}
|
||||
block={block}
|
||||
theme={theme}
|
||||
|
@ -692,27 +692,9 @@ export class FilesStore {
|
||||
#processEventBuffer(events: Array<[events: PathWatcherEvent[]]>) {
|
||||
const watchEvents = events.flat(2);
|
||||
|
||||
for (const { type, path: eventPath, buffer } of watchEvents) {
|
||||
for (const { type, path, buffer } of watchEvents) {
|
||||
// remove any trailing slashes
|
||||
const sanitizedPath = eventPath.replace(/\/+$/g, '');
|
||||
|
||||
// Skip processing if this file/folder was explicitly deleted
|
||||
if (this.#deletedPaths.has(sanitizedPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let isInDeletedFolder = false;
|
||||
|
||||
for (const deletedPath of this.#deletedPaths) {
|
||||
if (sanitizedPath.startsWith(deletedPath + '/')) {
|
||||
isInDeletedFolder = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInDeletedFolder) {
|
||||
continue;
|
||||
}
|
||||
const sanitizedPath = path.replace(/\/+$/g, '');
|
||||
|
||||
switch (type) {
|
||||
case 'add_dir': {
|
||||
@ -738,38 +720,21 @@ export class FilesStore {
|
||||
}
|
||||
|
||||
let content = '';
|
||||
|
||||
/**
|
||||
* @note This check is purely for the editor. The way we detect this is not
|
||||
* bullet-proof and it's a best guess so there might be false-positives.
|
||||
* The reason we do this is because we don't want to display binary files
|
||||
* in the editor nor allow to edit them.
|
||||
*/
|
||||
const isBinary = isBinaryFile(buffer);
|
||||
|
||||
if (isBinary && buffer) {
|
||||
// For binary files, we need to preserve the content as base64
|
||||
content = Buffer.from(buffer).toString('base64');
|
||||
} else if (!isBinary) {
|
||||
if (!isBinary) {
|
||||
content = this.#decodeFileContent(buffer);
|
||||
|
||||
/*
|
||||
* If the content is a single space and this is from our empty file workaround,
|
||||
* convert it back to an actual empty string
|
||||
*/
|
||||
if (content === ' ' && type === 'add_file') {
|
||||
content = '';
|
||||
}
|
||||
}
|
||||
|
||||
const existingFile = this.files.get()[sanitizedPath];
|
||||
this.files.setKey(sanitizedPath, { type: 'file', content, isBinary });
|
||||
|
||||
if (existingFile?.type === 'file' && existingFile.isBinary && existingFile.content && !content) {
|
||||
content = existingFile.content;
|
||||
}
|
||||
|
||||
// Preserve lock state if the file already exists
|
||||
const isLocked = existingFile?.type === 'file' ? existingFile.isLocked : false;
|
||||
|
||||
this.files.setKey(sanitizedPath, {
|
||||
type: 'file',
|
||||
content,
|
||||
isBinary,
|
||||
isLocked,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'remove_file': {
|
||||
|
Loading…
Reference in New Issue
Block a user