Something went wrong. Try again.
A local-first note taking app
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117import type { DocumentSlotState } from '../documents/useDocumentSlot';import { resolveDocumentLink } from '../documents/resolveDocumentLink';import type { OpenDocRequest } from '../workspaces/workspace';import { useWikiLinkDocument } from '../wikilinks/WikiLinkVaultContext';import { DocumentPane } from './DocumentPane';import type { EditorMode, EditorViewSnapshot } from './types';
const DEFAULT_EMPTY_HINT = 'Select a file from the sidebar to open it here.';
type DocumentSlotViewProps = { state: DocumentSlotState; className?: string; placeholder?: string; emptyHint?: string; /** Rendering mode for the open editor; defaults to `edit`. */ mode?: EditorMode; /** * Open a document (by provider + entry). When provided, internal-link clicks * in the editor are resolved against the open document and routed here — so a * workspace gets file-to-file navigation just by forwarding its open handler. */ onOpenEntry?: (request: OpenDocRequest) => void; /** Position to restore — where this slot's tab was when it was last shown. */ viewSnapshot?: EditorViewSnapshot | null; /** Called as the editor tears down, with the position to restore next time. */ onViewSnapshot?: (snapshot: EditorViewSnapshot) => void;};
function SlotPlaceholder({ message }: { message: string }) { return ( <div className="border-border text-foreground/60 flex h-full min-h-0 w-full items-center justify-center rounded-md border border-dashed p-5 text-center text-sm"> {message} </div> );}
/** * Renders a document slot's empty, loading, error, or open states. * Workspaces compose this inside their layout chrome. * * Both kinds of internal navigation attach here rather than in each workspace, * for the same reason: this is the one place that knows which document is open * *and* which provider it came from, which is exactly what resolving a link * target needs. Relative markdown links resolve through `resolveDocumentLink`; * `[[wikilinks]]` resolve vault-wide through `useWikiLinkDocument`. Every * workspace that renders a slot gets both without its own wiring. */export function DocumentSlotView({ state, className, placeholder = 'Start writing…', emptyHint = DEFAULT_EMPTY_HINT, mode, onOpenEntry, viewSnapshot, onViewSnapshot,}: DocumentSlotViewProps) { // Hooks must run before the early returns below (rules of hooks). Only the // `empty` state lacks a request — and it has no document to link from. const slotRequest = state.kind === 'empty' ? null : state.request; const { vault, headings, openWikiLink } = useWikiLinkDocument( slotRequest?.providerId ?? null, slotRequest?.entryId ?? null, );
if (state.kind === 'empty') { return <SlotPlaceholder message={emptyHint} />; } if (state.kind === 'loading') { return <SlotPlaceholder message={`Opening ${state.request.title}…`} />; } if (state.kind === 'error') { return ( <SlotPlaceholder message={`Could not open ${state.request.title}: ${state.message}`} /> ); } // Resolve internal-link targets against this document, then route to the // workspace's open handler. Kept here (not in each workspace) so every // workspace gets file-to-file navigation for free. // Resolve from `request.entryId`, NOT `handle.id`: the handle's id is the // provider's own document identity, which for a synced vault is an `at://` // URI rather than a path (`syncedVaultDocumentsProvider` sets `id: // session.uri`). `entryId` is always the absolute path in both providers. const { request, handle } = state; const onOpenLink = onOpenEntry ? (target: string) => onOpenEntry( resolveDocumentLink( { providerId: request.providerId, path: request.entryId }, target, ), ) : undefined; return ( // Key by document id so switching tabs to a different document remounts a // fresh editor instead of reusing one bound to the previous document. The // shared editor view binds its content on mount only, so without this a // tab switch can leave the previous file's content showing. <DocumentPane key={handle.id} handle={handle} placeholder={placeholder} className={className} mode={mode} onOpenLink={onOpenLink} wikiLinkVault={vault} wikiLinkHeadings={headings} onOpenWikiLink={openWikiLink} viewSnapshot={viewSnapshot} onViewSnapshot={onViewSnapshot} reveal={request.nav ?? null} /> );}