bolt.diy/app/lib/stores/editor.ts
KevIsDev 18f1b251ab fix: add binary file detection support
Add isBinary flag to editor documents to handle binary files appropriately
Update BinaryContent component styling to display properly in the editor
2025-06-09 13:10:21 +01:00

114 lines
3.2 KiB
TypeScript

import { atom, computed, map, type MapStore, type WritableAtom } from 'nanostores';
import type { EditorDocument, ScrollPosition } from '~/components/editor/codemirror/CodeMirrorEditor';
import type { FileMap, FilesStore } from './files';
import { createScopedLogger } from '~/utils/logger';
export type EditorDocuments = Record<string, EditorDocument>;
type SelectedFile = WritableAtom<string | undefined>;
const logger = createScopedLogger('EditorStore');
export class EditorStore {
#filesStore: FilesStore;
selectedFile: SelectedFile = import.meta.hot?.data.selectedFile ?? atom<string | undefined>();
documents: MapStore<EditorDocuments> = import.meta.hot?.data.documents ?? map({});
currentDocument = computed([this.documents, this.selectedFile], (documents, selectedFile) => {
if (!selectedFile) {
return undefined;
}
return documents[selectedFile];
});
constructor(filesStore: FilesStore) {
this.#filesStore = filesStore;
if (import.meta.hot) {
import.meta.hot.data.documents = this.documents;
import.meta.hot.data.selectedFile = this.selectedFile;
}
}
setDocuments(files: FileMap) {
const previousDocuments = this.documents.value;
this.documents.set(
Object.fromEntries<EditorDocument>(
Object.entries(files)
.map(([filePath, dirent]) => {
if (dirent === undefined || dirent.type !== 'file') {
return undefined;
}
const previousDocument = previousDocuments?.[filePath];
return [
filePath,
{
value: dirent.content,
filePath,
isBinary: dirent.isBinary, // Add this line
scroll: previousDocument?.scroll,
},
] as [string, EditorDocument];
})
.filter(Boolean) as Array<[string, EditorDocument]>,
),
);
}
setSelectedFile(filePath: string | undefined) {
this.selectedFile.set(filePath);
}
updateScrollPosition(filePath: string, position: ScrollPosition) {
const documents = this.documents.get();
const documentState = documents[filePath];
if (!documentState) {
return;
}
this.documents.setKey(filePath, {
...documentState,
scroll: position,
});
}
updateFile(filePath: string, newContent: string) {
const documents = this.documents.get();
const documentState = documents[filePath];
if (!documentState) {
return;
}
// Check if the file is locked by getting the file from the filesStore
const file = this.#filesStore.getFile(filePath);
if (file?.isLocked) {
logger.warn(`Attempted to update locked file: ${filePath}`);
return;
}
/*
* For scoped locks, we would need to implement diff checking here
* to determine if the edit is modifying existing code or just adding new code
* This is a more complex feature that would be implemented in a future update
*/
const currentContent = documentState.value;
const contentChanged = currentContent !== newContent;
if (contentChanged) {
this.documents.setKey(filePath, {
...documentState,
value: newContent,
});
}
}
}