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 InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language }: CodeComparisonProps) => {
|
||||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
|
// Use state to hold the shared highlighter instance
|
||||||
const [highlighter, setHighlighter] = useState<any>(null);
|
const [highlighter, setHighlighter] = useState<any>(null);
|
||||||
const theme = useStore(themeStore);
|
const theme = useStore(themeStore);
|
||||||
|
|
||||||
@ -554,34 +599,32 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language }
|
|||||||
const { unifiedBlocks, hasChanges, isBinary, error } = useProcessChanges(beforeCode, afterCode);
|
const { unifiedBlocks, hasChanges, isBinary, error } = useProcessChanges(beforeCode, afterCode);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getHighlighter({
|
// Fetch the shared highlighter instance
|
||||||
themes: ['github-dark', 'github-light'],
|
getSharedHighlighter().then(setHighlighter);
|
||||||
langs: [
|
|
||||||
'typescript',
|
/*
|
||||||
'javascript',
|
* No cleanup needed here for the highlighter instance itself,
|
||||||
'json',
|
* as it's managed globally. Shiki instances don't typically
|
||||||
'html',
|
* need disposal unless you are dynamically loading/unloading themes/languages.
|
||||||
'css',
|
* If you were dynamically loading, you might need a more complex
|
||||||
'jsx',
|
* shared instance manager with reference counting or similar.
|
||||||
'tsx',
|
* For static themes/langs, a single instance is sufficient.
|
||||||
'python',
|
*/
|
||||||
'php',
|
}, []); // Empty dependency array ensures this runs only once on mount
|
||||||
'java',
|
|
||||||
'c',
|
|
||||||
'cpp',
|
|
||||||
'csharp',
|
|
||||||
'go',
|
|
||||||
'ruby',
|
|
||||||
'rust',
|
|
||||||
'plaintext',
|
|
||||||
],
|
|
||||||
}).then(setHighlighter);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (isBinary || error) {
|
if (isBinary || error) {
|
||||||
return renderContentWarning(isBinary ? 'binary' : '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 (
|
return (
|
||||||
<FullscreenOverlay isFullscreen={isFullscreen}>
|
<FullscreenOverlay isFullscreen={isFullscreen}>
|
||||||
<div className="w-full h-full flex flex-col">
|
<div className="w-full h-full flex flex-col">
|
||||||
@ -602,7 +645,7 @@ const InlineDiffComparison = memo(({ beforeCode, afterCode, filename, language }
|
|||||||
lineNumber={block.lineNumber}
|
lineNumber={block.lineNumber}
|
||||||
content={block.content}
|
content={block.content}
|
||||||
type={block.type}
|
type={block.type}
|
||||||
highlighter={highlighter}
|
highlighter={highlighter} // Pass the shared instance
|
||||||
language={language}
|
language={language}
|
||||||
block={block}
|
block={block}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -692,27 +692,9 @@ export class FilesStore {
|
|||||||
#processEventBuffer(events: Array<[events: PathWatcherEvent[]]>) {
|
#processEventBuffer(events: Array<[events: PathWatcherEvent[]]>) {
|
||||||
const watchEvents = events.flat(2);
|
const watchEvents = events.flat(2);
|
||||||
|
|
||||||
for (const { type, path: eventPath, buffer } of watchEvents) {
|
for (const { type, path, buffer } of watchEvents) {
|
||||||
// remove any trailing slashes
|
// remove any trailing slashes
|
||||||
const sanitizedPath = eventPath.replace(/\/+$/g, '');
|
const sanitizedPath = path.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'add_dir': {
|
case 'add_dir': {
|
||||||
@ -738,38 +720,21 @@ export class FilesStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let content = '';
|
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);
|
const isBinary = isBinaryFile(buffer);
|
||||||
|
|
||||||
if (isBinary && buffer) {
|
if (!isBinary) {
|
||||||
// For binary files, we need to preserve the content as base64
|
|
||||||
content = Buffer.from(buffer).toString('base64');
|
|
||||||
} else if (!isBinary) {
|
|
||||||
content = this.#decodeFileContent(buffer);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case 'remove_file': {
|
case 'remove_file': {
|
||||||
|
Loading…
Reference in New Issue
Block a user