import type { Extension, Node, Mark } from "@tiptap/core"; import type { StarterKitOptions } from "@tiptap/starter-kit"; import { Markdown } from "tiptap-markdown"; import type { Awareness } from "y-protocols/awareness"; import type { Doc as YDoc } from "yjs"; import { type ImageUploadFn } from "./ImageExtension.js"; /** * Markdown dialect the editor parses/serializes. * * - `gfm` — GitHub-Flavored Markdown. No raw HTML passthrough. The byte-stable * serialization used by Plans (see RichMarkdownEditor.roundtrip.spec.ts). * - `nfm` — the Notion-Flavored Markdown superset used by the Content editor, * which opts into inline HTML so Notion-specific blocks round-trip. */ export type RichMarkdownDialect = "gfm" | "nfm"; /** * Editor preset. Schema-neutral today (both presets share the base schema), * but threaded through so an app can branch schema/behavior per preset without * a new factory. The collab/markdown wiring is preset-independent. */ export type RichMarkdownEditorPreset = "plan" | "content"; /** User info used to label this client's collaborative cursor. */ export interface RichMarkdownCollabUser { name: string; color: string; email?: string; } /** Optional collaborative-editing inputs for the shared editor. */ export interface SharedEditorCollab { /** * Yjs document for collaborative editing. When present the editor binds the * shared {@link Collaboration} (+ {@link CollaborationCaret} when awareness * is set) extensions and StarterKit's built-in undo/redo is disabled (Yjs * owns history). When absent the editor is a controlled `value`/`onChange` * editor. */ ydoc?: YDoc | null; /** Shared awareness instance for live multi-user cursors. */ awareness?: Awareness | null; /** Current user info for the collaborative cursor label. */ user?: RichMarkdownCollabUser | null; } /** Toggle the optional base extensions on/off per app. All default to `true`. */ export interface SharedEditorFeatures { /** GFM pipe tables (Table + TableRow + TableHeader + TableCell). */ tables?: boolean; /** Task / checklist lists (TaskList + TaskItem). */ tasks?: boolean; /** Inline links (the `Link` mark). When off, links fall back to plain text. */ link?: boolean; /** Fenced code blocks. Disabling lets an app inject its own code-block node. */ codeBlock?: boolean; /** * The built-in {@link Placeholder} extension. Default `true`. Apps that need a * bespoke placeholder resolver (per-node-type labels, ancestor-aware text) * disable this and supply their own Placeholder via `extraExtensions`. */ placeholder?: boolean; /** * The built-in dialect-keyed {@link Markdown} serializer. Default `true`. * Apps with a custom serializer (e.g. Content's NFM converter, which does NOT * round-trip through tiptap-markdown's storage) disable this and own the * serialize/parse pipeline themselves. The Markdown extension is still added * so paste/clipboard transforms work — disable it only when supplying your own * Markdown configuration via the {@link CreateSharedEditorExtensionsOptions.markdown} * option instead. */ markdown?: boolean; /** * The shared block-level image node (`@tiptap/extension-image`). Default * `false` so existing embedders are unchanged. When `true`, images * serialize to GFM `![alt](src)` (source-syncable) and — when an * {@link CreateSharedEditorExtensionsOptions.onImageUpload} function is * supplied — paste / drop of local image files uploads through it. Content * leaves this off and injects its own richer image node via * `extraExtensions`, so the two never collide. */ image?: boolean; } export interface CreateSharedEditorExtensionsOptions { /** Markdown dialect; selects the keyed {@link Markdown} config. */ dialect?: RichMarkdownDialect; /** Preset hook (schema-neutral today). */ preset?: RichMarkdownEditorPreset; /** Empty-block placeholder text (headings get their own labels). */ placeholder?: string; /** Toggle individual base extensions. */ features?: SharedEditorFeatures; /** * Extra StarterKit options merged over the shared defaults. Lets an app turn * off StarterKit nodes it replaces (Content swaps in its own paragraph / * blockquote / code block) or pass a custom dropcursor, while still sharing * the rest of the StarterKit base + the collab undo/redo gating. The shared * defaults (`heading` levels 1-4, `link: false`, the default dropcursor, and * `undoRedo: false` in collab mode) are applied first and can be overridden * key-by-key here. */ starterKit?: Partial; /** * Custom {@link Markdown} configuration. Replaces the dialect-keyed config from * {@link MARKDOWN_DIALECT_CONFIG} when provided. Only used when * `features.markdown !== false`; apps that own the whole markdown pipeline (no * tiptap-markdown serialization at all) should set `features.markdown: false` * and add their own configured Markdown extension via `extraExtensions`. */ markdown?: Parameters[0]; /** * App-specific extensions (Notion nodes, media, drag handles, comment * anchors, etc.) appended LAST so they bind over the shared base schema and * the optional Collaboration extensions still mount after them. */ extraExtensions?: Array; /** Optional collaborative-editing wiring. */ collab?: SharedEditorCollab | null; /** * Disable StarterKit's built-in undo/redo (prosemirror-history) for a * controlled (non-collab) editor whose host owns its own undo authority. * Default `false`. When a `collab.ydoc` is present, undo/redo is ALWAYS * disabled regardless of this flag (Yjs owns history); this flag is the * non-collab equivalent. The plan editor sets it so a single app-level undo * stack (over the authoritative `blocks[]` tree, which includes block data * the ProseMirror doc never stores) is the sole cmd+z authority — otherwise * PM history and the app stack would both fire, and PM history can't see * block-option edits at all. */ disableHistory?: boolean; /** * Injectable image uploader for the shared image block. Only used when * `features.image` is on. Turns a picked / pasted / dropped image File into a * hosted `{ src, alt? }`. Plans pass `uploadEditorImage` (the framework * `upload-image` action). When omitted, the image block still renders and * round-trips `![alt](src)` markdown but cannot ingest local files. */ onImageUpload?: ImageUploadFn | null; } /** * tiptap-markdown configuration, keyed by dialect. This is the single source of * truth for how each dialect parses/serializes markdown so the editor component * and the round-trip fidelity test can never drift apart. * * tiptap-markdown re-serializes the whole document on every edit, so the goal * for GFM is `serialize(parse(markdown)) === markdown` for the markdown plans * actually contain. We deliberately keep tiptap-markdown's own defaults * (`bulletListMarker: "-"`, `tightLists: true`, `linkify: false`, * `breaks: false`) because those produce the most byte-stable GFM. See * RichMarkdownEditor.roundtrip.spec.ts for the pinned corpus. * * NFM (Content) opts into inline HTML passthrough (`html: true`) so * Notion-specific blocks survive a markdown round-trip; the rest mirrors the * Content editor's existing `Markdown.configure` call. */ export declare const MARKDOWN_DIALECT_CONFIG: Record[0]>; /** * The ONE editor extension factory shared by every embedder (Plans today, * Content next). It assembles the base Tiptap schema (StarterKit + Placeholder * + Link + tasks + tables + code block), the dialect-keyed {@link Markdown} * serializer, the optional Collaboration stack, and finally any app-specific * `extraExtensions`. * * Ordering matters: * 1. Base schema (StarterKit first so its nodes/marks register; `starterKit` * overrides let an app disable replaced nodes / swap the dropcursor). * 2. dialect-keyed Markdown serializer (suppressible via `features.markdown` * for apps that own the whole serialize/parse pipeline, e.g. Content's NFM). * 3. `extraExtensions` (Notion/media/etc.) — appended before Collaboration so * apps can extend the schema and Collaboration still binds over the full * schema. * 4. Collaboration (+ CollaborationCaret) LAST so they bind over everything. * * Content (the NFM editor) drives this factory with `features.placeholder` and * `features.markdown` off, `features.tasks/tables/link` off where it ships its * own, a `starterKit` override disabling paragraph/blockquote/codeBlock, and all * Notion/media/fidelity nodes + its own Markdown(NFM)/Placeholder via * `extraExtensions` — so it shares the StarterKit base + the collab wiring while * owning its byte-identical NFM serializer. */ export declare function createSharedEditorExtensions({ dialect, preset: _preset, placeholder, features, starterKit, markdown, extraExtensions, collab, onImageUpload, disableHistory, }?: CreateSharedEditorExtensionsOptions): Array; //# sourceMappingURL=extensions.d.ts.map