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'; export type EditorDocuments = Record; type SelectedFile = WritableAtom; export class EditorStore { #filesStore: FilesStore; selectedFile: SelectedFile = import.meta.hot?.data.selectedFile ?? atom(); documents: MapStore = 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( Object.entries(files) .map(([filePath, dirent]) => { if (dirent === undefined || dirent.type === 'folder') { return undefined; } const previousDocument = previousDocuments?.[filePath]; return [ filePath, { value: dirent.content, filePath, 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; } const currentContent = documentState.value; const contentChanged = currentContent !== newContent; if (contentChanged) { this.documents.setKey(filePath, { ...documentState, value: newContent, }); } } }