import { type ComponentType, type ReactNode } from 'react'; import type { AnyExtension } from '@tiptap/core'; import { type Editor, type JSONContent } from '@tiptap/react'; import type { HandleBlock } from './BlockHandle'; import { type AttachmentViewProps, type ReferenceViewProps } from './BlockNodes'; import { type SelectionAnchor } from './CommentHighlights'; import { type MentionPerson, type ReferenceSection } from './ComposerTriggers'; import { type LinkChipsOptions } from './LinkChips'; /** * How the app's saved text and the editor's document turn into each other. The * package speaks only the editor's document; the translation is * `@estiva-app/protocol`'s, which the package does not depend on (the record * below): * * { read: (value, format) => toEditorDocument(documentForEditing(value, format), { links: true }), * write: (doc) => publishableFromEditor(doc) } */ export interface DocumentCodec { /** The saved text, in its format, as the editor's document. */ read: (value: string, format: string) => JSONContent; /** The editor's document as the text to save, and the format it is in. */ write: (doc: JSONContent) => { value: string; contentFormat?: string; }; } /** * Where text typed when the sign-in ends is kept through the passkey trip, and * put back, unsaved, after it. The app's own store, by key. */ export interface HeldTyping { /** What is held for `key` from the last ending, or `undefined`. */ text: (key: string) => string | undefined; /** Forget what is held for `key`: it was saved. */ release: (key: string) => void; /** While the editor is open, let an ending read its unsaved text. Returns what stops that, as the editor closes. */ register: (key: string, read: () => string | undefined) => () => void; /** Whether the sign-in has ended: nothing is saved then, and the text is held instead. */ ended: () => boolean; } /** What `[` lists, and how. Without it `[` is a bracket. */ export interface DocumentReferences { /** The sections for what was typed after `[`, already narrowed and in the app's order. */ sections: (query: string) => readonly ReferenceSection[]; /** Called with a listener while the list is open; call it when `sections` would answer differently (a search landed). */ subscribe?: (listener: () => void) => () => void; /** Names the list. */ ariaLabel: string; /** The heading of rows that bring no section of their own. */ sectionLabel: string; /** A row's 16px icon, unless the row brings its own. Default none. */ icon?: ReactNode; } /** Whom `@` offers. Without it `@` is an at sign. */ export interface DocumentMentions { /** Everyone `@` can offer, asked as the query changes. */ people: () => readonly MentionPerson[]; /** The `nostr:npub…` a person is written as; `null` for someone without one, who is not offered. */ uriOf: (person: MentionPerson) => string | null; } export interface DocumentEditorProps { /** The saved text. A new value while the editor is open is somebody else's save, or your own coming back. */ value: string; /** The format `value` is in, handed to `codec.read`. */ format: string; codec: DocumentCodec; /** * Save. Called when you leave, never while typing, with the text, its format, * and the saved text this edit started from — so the app can tell whom the * save names anew. Answer whether it was saved. */ onCommit: (value: string, contentFormat: string | undefined, editedFrom: string) => Promise | boolean; /** The editor's accessible name. */ label: string; /** Muted words over an empty editor. */ placeholder?: ReactNode; /** For reading: nothing is typed or saved; chips open, and comments on a selection work when on. */ readOnly?: boolean; /** Who saved the text last, when it was not you: named in "… saved a newer version". */ changedBy?: { name: string; } | null; /** A save refused after the editor closed, with nothing left on screen to say it. */ onLeftUnsaved?: () => void; /** Where text typed when the sign-in ends is held, under `holdKey`. */ hold?: HeldTyping; holdKey?: string; /** `[` and `/` → File. */ references?: DocumentReferences; /** `@` and `/` → Mention. */ mentions?: DocumentMentions; /** A pasted app URL drawn as the chip of what it names (`LinkChips`). */ linkChips?: LinkChipsOptions; /** `/` → Upload, and a pasted picture: upload one file and answer the attachment's attributes (its `imeta` fields). */ upload?: (file: File) => Promise>; /** Plain text pasted as the blocks it spells: the app's reader of markdown, as the editor's document. */ readMarkdown?: (text: string) => JSONContent | undefined; /** `/` → Table. Default on. */ table?: boolean; /** Comment on what is selected: the selection strip's Comment, and the comment highlights. Without it, neither. */ onCommentOnSelection?: (anchor: SelectionAnchor) => void; /** The app's rows in a block's handle menu, after the editing ones: Copy link, Comment. */ blockRows?: (block: HandleBlock) => ReactNode; /** * Draws a link: the app's own, which resolves what it names — its chip, its * row or its card in the row's place, as `place` says (§13.3). */ referenceView?: ComponentType; /** Draws a placed file: the app's own card. */ attachmentView?: ComponentType; /** Around everything the editor draws: the app's providers, which may read the editor. */ wrap?: (children: ReactNode, editor: Editor | null) => ReactNode; /** The editor, once it is there, and `null` as it goes. */ onEditor?: (editor: Editor | null) => void; /** More of the app's own extensions. */ extensions?: readonly AnyExtension[]; } /** * One document's text as one surface that is always an editor — Leaf's page, * Ship's description and Peek's panel, which each had their own until this. * Every app's document can do the same things, wherever it is written. * * - **Saves when you leave:** on blur, on unmount and on a checklist tick. * Never while typing. Escape leaves and keeps the text. A refused save keeps * the text and says so; one refused after the editor closed goes to * `onLeftUnsaved`. * - **Someone else's save arrives** while you are not in it: it is loaded. * While you are in it, it waits, and a line says who saved a newer version, * with "Load it"; leaving with a change saves yours over it — the later save * wins (Miky, LEAF-3's kickoff and 13 October). Your own save coming back is * never mistaken for someone else's. * - **The sign-in ending** holds what was typed (`hold`), and the editor opens * with it, unsaved, after the passkey tap. * * why: https://github.com/estiva-app/estiva-docs/blob/main/decisions/every-app-edits-a-document-with-one-shared-editor.md * why: https://github.com/estiva-app/estiva-docs/blob/main/decisions/the-block-editor-kit-lives-in-protocol-and-ui.md */ export declare function DocumentEditor({ value, format, codec, onCommit, label, placeholder, readOnly, changedBy, onLeftUnsaved, hold, holdKey, references, mentions, linkChips, upload, readMarkdown, table, onCommentOnSelection, blockRows, referenceView, attachmentView, wrap, onEditor, extensions, }: DocumentEditorProps): import("react").JSX.Element; //# sourceMappingURL=DocumentEditor.d.ts.map