Textile core abstractions #
Textile separates where files live, how document bytes sync, and how editing UI renders. Each layer has a narrow job; concrete backends (Habitat PDS, Yjs, Zen/Tiling) plug in at the edges.
FileSystemProvider #
Role: Talk to a source about the file tree and other top-level actions.
- Lists entries (sidebar): names and stable ids
createFile()for new persisted entries (sidebar “New document”)- Readiness and auth hints (
isReady,subscribeReady) - Does not open document contents or run merge/sync
- Pairs with a
DocumentProviderviadocuments+defaultEditorKind(injected inApp.tsx)
Examples: habitat-docs (PDS record listing), future disk roots.
Key types: FileSystemProvider, FileSystemEntry — src/filesystem/types.ts
DocumentProvider #
Role: Resolve a document from local or remote storage and keep its contents coherent.
openDocument(entryId)— load/sync for an entry fromlistFilescreateDocument()— new in-memory or persisted doc- Transport from the source (XRPC, filesystem reads, …)
- Merge / sync when needed (e.g. Yjs CRDT via
docSession, libp2p) - Exposes
DocumentHandle: title,loadPromise,release,getEditorBinding()(data only, no React)
Does not: render UI or own TipTap/Yjs editor widgets.
Examples: HabitatDocumentsProvider — src/documents/providers/
Key types: DocumentProvider, DocumentHandle — src/documents/types.ts
Editor #
Role: The UI that displays and edits file contents. Deeply tied to the sync method (Yjs + TipTap today).
- Registered in
src/editors/registry.ts(Editor:{ kind, Component }) - Shared chrome and typography:
src/editors/ui/ - Per-backend wiring:
src/editors/<kind>/(e.g.yjs/yjsDocumentEditor.tsx) - Consumes
EditorBindingfromgetEditorBinding(); only the matching editor readspayload
Does not: fetch from PDS, run libp2p, or list the file tree.
Entry point for workspaces: DocumentPane — src/editors/DocumentPane.tsx
Key types: Editor, EditorKind, EditorBinding, EditorProps — src/editors/types.ts
Workspace #
Role: How open files are arranged on screen (tiles, splits, empty states, workspace chrome).
- Subscribes to open requests from the shell sidebar
- Holds
DocumentHandle(s) viauseDocumentSlot, callsrelease()on switch/close - Renders
DocumentSlotViewinside layout — does not import Habitat or Yjs
Examples: ZenWorkspace, TilingWorkspace — src/workspaces/
Composition (App.tsx) #
const habitatDocuments = new HabitatDocumentsProvider();
new HabitatFilesystemProvider({
documents: habitatDocuments,
defaultEditorKind: 'yjs',
});
Workspaces receive resolveProvider → fs.documents.openDocument(entryId) → DocumentPane handle={…}.
Adding a new backend #
- New source only (same Yjs docs): new
FileSystemProvider+ reuseHabitatDocumentsProvideror a siblingDocumentProvider. - New sync/editor (e.g. plain markdown): new
DocumentProvider(or branch in existing) returning{ kind: 'markdown', payload }; addeditors/markdown/+ register ineditors/registry.ts. - New layout: new workspace under
src/workspaces/; reuseDocumentPaneandDocumentHandle.