/** * Shell filesystem abstraction. Future: readFile, watch, capabilities, etc. * * Listing and live documents are separate concerns: a source may implement * `listFiles` only (e.g. disk) or also expose a `documents` backend injected * at the composition root (`App.tsx`). */ import type { EditorKind } from '../editors/types'; import type { DocumentProvider } from '../documents/types'; /** Opaque to the shell UI; used as React `key` and selection id. */ export type FileSystemEntry = { id: string; name: string; }; /** Raw filesystem directory entry (name + type flags). */ export type FsDirent = { name: string; isDirectory: boolean; isFile: boolean; }; /** Recursive tree node returned by `getTree()`. */ export type FileSystemTreeNode = { id: string; name: string; type: 'file' | 'folder'; children?: FileSystemTreeNode[]; }; export type FileSystemProvider = { /** Stable key for React and selection state (e.g. `habitat-docs`). */ id: string; /** Shown as the sidebar section heading. */ displayName: string; /** Id of this source's root directory; the move target for drops at the root. */ readonly rootId: string; /** When `isReady()` is false, shell may show this instead of a generic idle line. */ idleHint?: string; /** When false, skip `listFiles` and show idle state. */ isReady(): boolean; /** * Optional: subscribe to `isReady()` transitions (e.g. user signs in). * The listener is called when readiness may have changed; return value * unsubscribes. Used by the sidebar to re-run `listFiles` after login. */ subscribeReady?(listener: () => void): () => void; /** List entries for this source; reject on transport errors. */ listFiles(): Promise; /** * Build a recursive tree of the source's contents. * Providers without directories return a flat list of files. */ getTree(): Promise; /** * Create a new persisted entry in this source (e.g. a new Habitat docs * record). The shell optimistically inserts it, then refreshes the listing. */ createFile( parentId?: string, name?: string, /** Initial file contents. Defaults to empty. */ content?: string, ): Promise; /** * Create a new directory in this source. * Providers without directory support may throw or no-op. */ createDirectory(name: string, parentId?: string): Promise; /** * Rename an entry (file or directory). * Returns the updated entry with the new id and name. */ renameFile(entryId: string, newName: string): Promise; /** * Move an entry into a different directory (`targetDirId`). Returns the * updated entry with its new id. No-ops when the target is the current parent. */ moveFile(entryId: string, targetDirId: string): Promise; /** * Delete an entry (file or directory) permanently. */ deleteFile(entryId: string): Promise; /** * Duplicate a file. Returns the new entry. */ copyFile(entryId: string): Promise; /** * Get the absolute filesystem path for an entry. * Used for "Copy Path" to clipboard. */ getPath(entryId: string): string; /** * When implemented, called when a listed entry's display name changes * (e.g. habitat doc heading edits). Used to keep the sidebar in sync. */ subscribeEntryTitles?( listener: (entryId: string, name: string) => void, ): () => void; /** * Backend kind used when creating new documents from this source. * TODO: Let users pick the default per source (or globally) in settings. */ /** Default editor/sync backend when creating documents from this source. */ defaultEditorKind: EditorKind; /** Live-document backend, injected at the composition root (`App.tsx`). */ documents: DocumentProvider; }; /** Low-level filesystem API used by providers that access the host disk. */ export interface FilesystemApi { readdir(dirPath: string): Promise; readFile(filePath: string): Promise; writeFile(filePath: string, content: string): Promise; fileExists(filePath: string): Promise; mkdir(dirPath: string): Promise; rename(oldPath: string, newPath: string): Promise; deletePath(filePath: string): Promise; copyFile(srcPath: string, destPath: string): Promise; }