@xenosystem/blocks 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,300 @@
1
+ import { XenoDocNode } from './format.js';
2
+ export { DOCUMENT_MARK_TYPES, DOCUMENT_NODE_TYPES, JSONContent, LIMITS, SAFE_IMAGE, SAFE_LINK, ValidateOptions, XDOC_EXTENSION, XDOC_FORMAT, XDOC_VERSION, XdocError, XenoDocFile, XenoDocMark, XenoDocProblem, documentToMarkdown, emptyContent, markdownToDocument, nodeTypesIn, pageLinksIn, parseXdoc, plainText, serializeXdoc, validateContent, wordCount } from './format.js';
3
+ import * as _tiptap_core from '@tiptap/core';
4
+ import { Node, Mark, AnyExtension, Editor } from '@tiptap/core';
5
+ import * as _tiptap_extension_image from '@tiptap/extension-image';
6
+
7
+ /**
8
+ * Callout block extension for TipTap
9
+ * Renders info/warning/error/success/tip callout blocks with colored left border
10
+ * Pattern: Notion-style callout blocks
11
+ */
12
+
13
+ type CalloutType = 'info' | 'warning' | 'error' | 'success' | 'tip';
14
+ declare module '@tiptap/core' {
15
+ interface Commands<ReturnType> {
16
+ callout: {
17
+ setCallout: (attrs?: {
18
+ type?: CalloutType;
19
+ }) => ReturnType;
20
+ toggleCallout: (attrs?: {
21
+ type?: CalloutType;
22
+ }) => ReturnType;
23
+ };
24
+ }
25
+ }
26
+ declare const Callout: Node<any, any>;
27
+
28
+ /**
29
+ * Toggle (collapsible) block extension for TipTap
30
+ * Renders a summary/details-style collapsible content block
31
+ * Pattern: Notion toggle blocks
32
+ */
33
+
34
+ declare module '@tiptap/core' {
35
+ interface Commands<ReturnType> {
36
+ toggleBlock: {
37
+ setToggleBlock: () => ReturnType;
38
+ };
39
+ }
40
+ }
41
+ declare const ToggleBlock: Node<any, any>;
42
+
43
+ /**
44
+ * Page break extension for TipTap
45
+ * Inserts a page break that translates to a real page break in PDF/DOCX export.
46
+ *
47
+ * Pattern: Google Docs page break (Ctrl+Enter)
48
+ *
49
+ * MOVED from xeno-docs `src/renderer/src/extensions/PageBreak.ts` (2026-10-23). The node is identical
50
+ * (`pageBreak`, `div[data-page-break]`); what changed is that it no longer paints itself — the old
51
+ * file carried its own dashed rule colours and label size inline, which a block may not
52
+ * (`test/no-visual-value.test.ts`). The host styles `.page-break` and its two `.page-break-rule`
53
+ * halves; `page-break-after: always` stays, because that is print behaviour, not chrome.
54
+ */
55
+
56
+ declare module '@tiptap/core' {
57
+ interface Commands<ReturnType> {
58
+ pageBreak: {
59
+ /** Insert a page break at the current cursor position */
60
+ setPageBreak: () => ReturnType;
61
+ };
62
+ }
63
+ }
64
+ declare const PageBreak: Node<any, any>;
65
+
66
+ /**
67
+ * TipTap extension for bi-directional [[wiki links]].
68
+ *
69
+ * Implements:
70
+ * - Inline mark that renders [[target]] or [[target|alias]] as clickable links
71
+ * - Input rule: typing [[ triggers the link creation
72
+ * - The mark stores pageId and pageTitle for resolution
73
+ *
74
+ * Approach: Custom TipTap Mark extension (similar to how Obsidian handles it).
75
+ * The mark wraps the display text and stores metadata in attrs.
76
+ */
77
+
78
+ interface WikiLinkOptions {
79
+ /** Callback when a wiki link is clicked */
80
+ onClickLink: (pageId: string, pageTitle: string) => void;
81
+ /** HTML attributes for the link element */
82
+ HTMLAttributes: Record<string, unknown>;
83
+ }
84
+ declare module '@tiptap/core' {
85
+ interface Commands<ReturnType> {
86
+ wikiLink: {
87
+ setWikiLink: (attrs: {
88
+ pageId: string;
89
+ pageTitle: string;
90
+ }) => ReturnType;
91
+ unsetWikiLink: () => ReturnType;
92
+ };
93
+ }
94
+ }
95
+ declare const WikiLinkMark: Mark<WikiLinkOptions, any>;
96
+
97
+ /**
98
+ * The image node, refusing at PARSE time any source the format refuses — so a pasted image whose
99
+ * source is a script URL or a tracking pixel over http never enters the document, instead of
100
+ * entering it and making the next save fail.
101
+ */
102
+ declare const SafeImage: _tiptap_core.Node<_tiptap_extension_image.ImageOptions, any>;
103
+ /** Options for {@link createDocumentExtensions}. */
104
+ interface DocumentExtensionOptions {
105
+ /** The empty-editor hint. */
106
+ placeholder?: string;
107
+ /** `false` when a collaboration extension owns undo (Y.js brings its own history). */
108
+ history?: boolean;
109
+ /**
110
+ * The code-block extension. Default: StarterKit's plain `codeBlock`. XENO Docs passes
111
+ * `CodeBlockLowlight` for highlighting — same node name, same JSON.
112
+ */
113
+ codeBlock?: AnyExtension;
114
+ /** Called when a page link (`wikiLink`) is clicked — the host navigates; the editor never does. */
115
+ onOpenPage?: (pageId: string, pageTitle: string) => void;
116
+ /** Table column resizing (on in XENO Docs). */
117
+ resizableTables?: boolean;
118
+ }
119
+ /**
120
+ * The shared extension list. Order matters only for priority, and it is the order XENO Docs has
121
+ * always used.
122
+ */
123
+ declare function createDocumentExtensions(options?: DocumentExtensionOptions): AnyExtension[];
124
+
125
+ /**
126
+ * The document block's CONTROLLER — framework-free state over one open document.
127
+ *
128
+ * It never saves, never navigates and never fetches: it emits INTENTS (`save`, `openPage`, `dirty`)
129
+ * and the host performs them, then reports back with {@link DocumentController.saved} or
130
+ * {@link DocumentController.setStatus}. The editing itself is TipTap's, bound through
131
+ * {@link DocumentEditorHandle} by `./surface`, so this file imports no editor and runs in a test.
132
+ *
133
+ * @module
134
+ */
135
+
136
+ /** What the host hands the block: one document, as the host stored it. */
137
+ interface XenoDocumentInput {
138
+ /** The host's stable id for the document (the platform: its Storage file id). */
139
+ id: string;
140
+ /** The host's id for THIS version — round-tripped as `expectedVersion` so a stale save is refused. */
141
+ versionId?: string;
142
+ title: string;
143
+ /** A `.xdoc` file (text or parsed) — or bare content, for a host that keeps the envelope itself. */
144
+ xdoc?: string | unknown;
145
+ content?: XenoDocNode;
146
+ readOnly?: boolean;
147
+ created?: number;
148
+ }
149
+ /** The `save` intent: everything the host needs to write the next version. */
150
+ interface XenoDocumentSave {
151
+ id: string;
152
+ expectedVersion?: string;
153
+ title: string;
154
+ /** The `.xdoc` text, validated — the host stores these bytes. */
155
+ xdoc: string;
156
+ content: XenoDocNode;
157
+ text: string;
158
+ pageLinks: string[];
159
+ }
160
+ /** The `openPage` intent: a page link was followed. */
161
+ interface XenoDocumentOpenPage {
162
+ pageId: string;
163
+ pageTitle: string;
164
+ }
165
+ /** Where the open document stands. */
166
+ type XenoDocumentStatus = 'empty' | 'ready' | 'saving' | 'saved' | 'conflict' | 'failed' | 'unsupported';
167
+ /** The controller's state, for a view. */
168
+ interface DocumentState {
169
+ id: string | null;
170
+ versionId: string | null;
171
+ title: string;
172
+ readOnly: boolean;
173
+ /** True when the document contains a block this schema cannot render — it opens read-only. */
174
+ unsupported: string[];
175
+ dirty: boolean;
176
+ status: XenoDocumentStatus;
177
+ message: string | null;
178
+ content: XenoDocNode;
179
+ revision: number;
180
+ }
181
+ /** The slice of a TipTap `Editor` the controller drives. `./surface` supplies the real one. */
182
+ interface DocumentEditorHandle {
183
+ getJSON(): XenoDocNode;
184
+ setContent(content: XenoDocNode): void;
185
+ setEditable(editable: boolean): void;
186
+ focus(): void;
187
+ run(command: DocumentFormatCommand, arg?: unknown): boolean;
188
+ isActive(command: DocumentFormatCommand): boolean;
189
+ insertText(text: string): void;
190
+ insertPageLink(pageId: string, pageTitle: string, label?: string): void;
191
+ }
192
+ /** Every format command the toolbar, a keyboard or an agent can ask for. */
193
+ declare const DOCUMENT_FORMAT_COMMANDS: readonly ["paragraph", "h1", "h2", "h3", "bold", "italic", "underline", "strike", "code", "highlight", "bulletList", "orderedList", "taskList", "blockquote", "codeBlock", "callout", "toggle", "pageBreak", "horizontalRule", "link", "unlink", "undo", "redo"];
194
+ type DocumentFormatCommand = (typeof DOCUMENT_FORMAT_COMMANDS)[number];
195
+ /** The host side the controller talks to. */
196
+ interface DocumentHostBridge {
197
+ emit(portId: 'save' | 'openPage' | 'dirty', value: unknown): void;
198
+ }
199
+ interface DocumentControllerOptions {
200
+ host: DocumentHostBridge;
201
+ }
202
+ /** The toolbar, in order: what XENO Docs' own toolbar leads with. */
203
+ declare const DOCUMENT_TOOLBAR: readonly {
204
+ command: DocumentFormatCommand;
205
+ label: string;
206
+ }[];
207
+ /** The controller. One per mounted document block. */
208
+ declare class DocumentController {
209
+ private readonly host;
210
+ private state;
211
+ private created;
212
+ /** XENO Docs' comment threads, carried through a save untouched (this block does not edit comments). */
213
+ private commentThreads;
214
+ private editor;
215
+ private listeners;
216
+ constructor(options: DocumentControllerOptions);
217
+ /** Subscribe to state changes (a view re-renders on each). */
218
+ subscribe(fn: () => void): () => void;
219
+ getState(): DocumentState;
220
+ private set;
221
+ /**
222
+ * Open a document. A file the shared schema cannot render is NOT coerced: it opens read-only with
223
+ * the unknown types listed, so nothing the user wrote elsewhere is lost on a save here.
224
+ */
225
+ load(input: XenoDocumentInput): void;
226
+ /** Bind the live editor (`./surface` does this). Returns the unbind. */
227
+ attach(editor: DocumentEditorHandle): () => void;
228
+ hasEditor(): boolean;
229
+ /** The editor reported an edit. */
230
+ contentChanged(): void;
231
+ /** The cursor moved: a view re-reads which format commands are active. No state changes. */
232
+ selectionChanged(): void;
233
+ setTitle(title: string): void;
234
+ /** The content as it stands — the live editor's when bound. */
235
+ currentContent(): XenoDocNode;
236
+ /** Run a format command against the live editor. */
237
+ format(command: DocumentFormatCommand, arg?: unknown): boolean;
238
+ isActive(command: DocumentFormatCommand): boolean;
239
+ insertText(text: string): boolean;
240
+ insertPageLink(pageId: string, pageTitle: string, label?: string): boolean;
241
+ /** A page link was clicked — the host navigates. */
242
+ openPage(pageId: string, pageTitle: string): void;
243
+ /** The `.xdoc` for the document as it stands. Throws {@link XdocError} if it would not validate. */
244
+ toXdoc(): string;
245
+ /** Ask the host to save. Refused (false) when read-only, when nothing is open, or mid-save. */
246
+ save(): boolean;
247
+ /** The host saved: adopt its new version id. */
248
+ saved(versionId?: string): void;
249
+ /** The host reports an outcome that is not success (`conflict`, `failed`) or a message. */
250
+ setStatus(status: XenoDocumentStatus, message?: string): void;
251
+ /** What an agent reads. The status travels with the words so "empty" is never a guess. */
252
+ describe(): {
253
+ id: string | null;
254
+ title: string;
255
+ status: XenoDocumentStatus;
256
+ readOnly: boolean;
257
+ dirty: boolean;
258
+ text: string;
259
+ wordCount: number;
260
+ pageLinks: string[];
261
+ unsupported: string[];
262
+ };
263
+ serialize(): unknown;
264
+ deserialize(_state: unknown): void;
265
+ dispose(): void;
266
+ }
267
+
268
+ /**
269
+ * The document SURFACE — XENO Docs' editor (TipTap over the shared schema) mounted into one element,
270
+ * framework-free. The React view mounts this; so does a host that draws its own chrome (the platform
271
+ * workspace), which is why it needs no React.
272
+ *
273
+ * The element is the editor's: ProseMirror owns its DOM, and builds it ONLY from the schema's
274
+ * `renderHTML` specs. Nothing a document carries is ever assigned as HTML.
275
+ *
276
+ * @module
277
+ */
278
+
279
+ /** Options for {@link mountDocumentSurface}. */
280
+ interface DocumentSurfaceOptions extends Omit<DocumentExtensionOptions, 'onOpenPage'> {
281
+ /** A product's own extensions, appended to the shared set (XENO Docs: math, mermaid…). */
282
+ extensions?: AnyExtension[];
283
+ /** Called for a link the user asks to set (`format('link')` with no href). Return the address. */
284
+ promptLink?: (current: string | null) => string | null;
285
+ /** Class names for the editable element — the host's typography, never the block's. */
286
+ editorClass?: string;
287
+ /** Accessible name for the text area. */
288
+ label?: string;
289
+ }
290
+ /** Run one format command on a TipTap editor. Exported so XENO Docs' toolbar uses the SAME map. */
291
+ declare function runFormat(editor: Editor, command: DocumentFormatCommand, arg?: unknown, promptLink?: DocumentSurfaceOptions['promptLink']): boolean;
292
+ /** A {@link DocumentEditorHandle} over a TipTap editor. */
293
+ declare function editorHandle(editor: Editor, promptLink?: DocumentSurfaceOptions['promptLink']): DocumentEditorHandle;
294
+ /**
295
+ * Mount the editor into `element` and bind it to `controller`. Returns the teardown, which destroys
296
+ * the editor — call it before the element is removed.
297
+ */
298
+ declare function mountDocumentSurface(element: HTMLElement, controller: DocumentController, options?: DocumentSurfaceOptions): () => void;
299
+
300
+ export { Callout, type CalloutType, DOCUMENT_FORMAT_COMMANDS, DOCUMENT_TOOLBAR, DocumentController, type DocumentControllerOptions, type DocumentEditorHandle, type DocumentExtensionOptions, type DocumentFormatCommand, type DocumentHostBridge, type DocumentState, type DocumentSurfaceOptions, PageBreak, SafeImage, ToggleBlock, WikiLinkMark, type WikiLinkOptions, XenoDocNode, type XenoDocumentInput, type XenoDocumentOpenPage, type XenoDocumentSave, type XenoDocumentStatus, createDocumentExtensions, editorHandle, mountDocumentSurface, runFormat };
@@ -0,0 +1,70 @@
1
+ import {
2
+ Callout,
3
+ DOCUMENT_FORMAT_COMMANDS,
4
+ DOCUMENT_TOOLBAR,
5
+ DocumentController,
6
+ PageBreak,
7
+ SafeImage,
8
+ ToggleBlock,
9
+ WikiLinkMark,
10
+ createDocumentExtensions,
11
+ editorHandle,
12
+ mountDocumentSurface,
13
+ runFormat
14
+ } from "../chunk-5VOA3OSG.js";
15
+ import "../chunk-HDP35LGH.js";
16
+ import {
17
+ DOCUMENT_MARK_TYPES,
18
+ DOCUMENT_NODE_TYPES,
19
+ LIMITS,
20
+ SAFE_IMAGE,
21
+ SAFE_LINK,
22
+ XDOC_EXTENSION,
23
+ XDOC_FORMAT,
24
+ XDOC_VERSION,
25
+ XdocError,
26
+ documentToMarkdown,
27
+ emptyContent,
28
+ markdownToDocument,
29
+ nodeTypesIn,
30
+ pageLinksIn,
31
+ parseXdoc,
32
+ plainText,
33
+ serializeXdoc,
34
+ validateContent,
35
+ wordCount
36
+ } from "../chunk-NFOBZJDF.js";
37
+ import "../chunk-2KG3PWR4.js";
38
+ export {
39
+ Callout,
40
+ DOCUMENT_FORMAT_COMMANDS,
41
+ DOCUMENT_MARK_TYPES,
42
+ DOCUMENT_NODE_TYPES,
43
+ DOCUMENT_TOOLBAR,
44
+ DocumentController,
45
+ LIMITS,
46
+ PageBreak,
47
+ SAFE_IMAGE,
48
+ SAFE_LINK,
49
+ SafeImage,
50
+ ToggleBlock,
51
+ WikiLinkMark,
52
+ XDOC_EXTENSION,
53
+ XDOC_FORMAT,
54
+ XDOC_VERSION,
55
+ XdocError,
56
+ createDocumentExtensions,
57
+ documentToMarkdown,
58
+ editorHandle,
59
+ emptyContent,
60
+ markdownToDocument,
61
+ mountDocumentSurface,
62
+ nodeTypesIn,
63
+ pageLinksIn,
64
+ parseXdoc,
65
+ plainText,
66
+ runFormat,
67
+ serializeXdoc,
68
+ validateContent,
69
+ wordCount
70
+ };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * The XENO document FORMAT — `.xdoc`, XENO Docs' native file, now the one format every surface that
3
+ * edits a document reads and writes (XENO Docs, XENO Notes' link mark, the platform workspace's
4
+ * Write and Notes).
5
+ *
6
+ * A `.xdoc` is JSON: an envelope around a ProseMirror document (`content`, the TipTap `getJSON()`
7
+ * shape). It is NOT HTML and is never rendered as HTML: a surface turns `content` into DOM through
8
+ * the schema in `./schema`, which only knows the node and mark types listed here. That is the whole
9
+ * XSS argument, and this module is what keeps it true at every boundary that accepts a file:
10
+ *
11
+ * - every node / mark type must be one of {@link DOCUMENT_NODE_TYPES} / {@link DOCUMENT_MARK_TYPES}
12
+ * (a product may widen the set for ITS OWN extensions with `extraNodes` / `extraMarks`);
13
+ * - every attribute is checked against the type's own rule — a link's `href` must be http(s) or
14
+ * mailto, an image's `src` https or a raster `data:` image, a page link's id a UUID;
15
+ * - depth and node count are bounded.
16
+ *
17
+ * {@link validateContent} reports what it refused instead of dropping it: a server refuses the file,
18
+ * a surface opens it read-only. Silently dropping an unknown block would lose the user's words on the
19
+ * next save — the same outcome as a bug, with a better excuse.
20
+ *
21
+ * This module imports nothing (no TipTap, no DOM), so a server, a migration script or a test can use
22
+ * it without an editor.
23
+ *
24
+ * @module
25
+ */
26
+ /** The `format` field every `.xdoc` written from now on carries. */
27
+ declare const XDOC_FORMAT = "xeno.xdoc";
28
+ /** The current envelope version. */
29
+ declare const XDOC_VERSION = 1;
30
+ /** The file extension XENO Docs ships (`DocumentEditor.tsx` — `${title}.xdoc`). */
31
+ declare const XDOC_EXTENSION = ".xdoc";
32
+ /** One ProseMirror node, as TipTap's `getJSON()` writes it. */
33
+ interface XenoDocNode {
34
+ type: string;
35
+ attrs?: Record<string, unknown>;
36
+ content?: XenoDocNode[];
37
+ marks?: XenoDocMark[];
38
+ text?: string;
39
+ }
40
+ /** One mark on a text node. */
41
+ interface XenoDocMark {
42
+ type: string;
43
+ attrs?: Record<string, unknown>;
44
+ }
45
+ /**
46
+ * The `.xdoc` envelope. Field names are exactly the ones XENO Docs' autosave has always written
47
+ * (`id, title, content, lastModified, created, wordCount`), so every existing file still parses;
48
+ * `format` and `version` are the additive part.
49
+ */
50
+ interface XenoDocFile {
51
+ format: typeof XDOC_FORMAT;
52
+ version: number;
53
+ id?: string;
54
+ title: string;
55
+ content: XenoDocNode;
56
+ created: number;
57
+ lastModified: number;
58
+ wordCount: number;
59
+ /**
60
+ * XENO Docs' comment threads (its `Comment` mark points into them; XENO Docs keeps them as an object keyed by thread id). Carried through untouched by every surface,
61
+ * so a document that round-trips through the workspace keeps its comments; only XENO Docs reads them.
62
+ */
63
+ commentThreads?: unknown;
64
+ }
65
+ /** Node types the shared schema renders. A file with any other type is refused, never coerced. */
66
+ declare const DOCUMENT_NODE_TYPES: readonly ["doc", "paragraph", "text", "heading", "blockquote", "bulletList", "orderedList", "listItem", "taskList", "taskItem", "codeBlock", "horizontalRule", "hardBreak", "table", "tableRow", "tableCell", "tableHeader", "image", "callout", "toggleBlock", "pageBreak"];
67
+ /** Mark types the shared schema renders. */
68
+ declare const DOCUMENT_MARK_TYPES: readonly ["bold", "italic", "strike", "code", "underline", "link", "subscript", "superscript", "highlight", "wikiLink"];
69
+ /** A link a reader can follow without running anything. */
70
+ declare const SAFE_LINK: RegExp;
71
+ /** An image a reader can load without running anything: https, or a raster image inline. */
72
+ declare const SAFE_IMAGE: RegExp;
73
+ /** Limits a file must stay inside. */
74
+ declare const LIMITS: Readonly<{
75
+ depth: 64;
76
+ nodes: 50000;
77
+ text: 2000000;
78
+ }>;
79
+ /** One reason a document was refused. `path` points at the offending node (`content[2].content[0]`). */
80
+ interface XenoDocProblem {
81
+ path: string;
82
+ code: 'unknown_node' | 'unknown_mark' | 'bad_attr' | 'bad_shape' | 'too_deep' | 'too_large';
83
+ detail: string;
84
+ }
85
+ /** Options for {@link validateContent}: a product's own extensions, allowed in ITS files only. */
86
+ interface ValidateOptions {
87
+ extraNodes?: Iterable<string>;
88
+ extraMarks?: Iterable<string>;
89
+ }
90
+ /**
91
+ * Check a ProseMirror document against the shared schema. Pure; never throws for bad input.
92
+ *
93
+ * Types in `extraNodes` / `extraMarks` are accepted with their attributes unchecked: they belong to the
94
+ * product that declared them, and only that product's surface renders them.
95
+ */
96
+ declare function validateContent(content: unknown, options?: ValidateOptions): XenoDocProblem[];
97
+ /** The error {@link parseXdoc} throws: the problems ride on it so a caller can say exactly what is wrong. */
98
+ declare class XdocError extends Error {
99
+ readonly problems: XenoDocProblem[];
100
+ constructor(message: string, problems?: XenoDocProblem[]);
101
+ }
102
+ /** An empty document: one empty paragraph, which is what TipTap writes for a blank editor. */
103
+ declare const emptyContent: () => XenoDocNode;
104
+ /**
105
+ * Parse a `.xdoc` file (text or an already-parsed object). Accepts the legacy XENO Docs envelope with
106
+ * no `format` field. Throws {@link XdocError} for anything the shared schema would not render.
107
+ */
108
+ declare function parseXdoc(input: string | unknown, options?: ValidateOptions): XenoDocFile;
109
+ /** Write a `.xdoc`. Validates first: a surface must never write a file the next surface refuses. */
110
+ declare function serializeXdoc(doc: Omit<XenoDocFile, 'format' | 'version' | 'wordCount'> & {
111
+ wordCount?: number;
112
+ }, options?: ValidateOptions & {
113
+ pretty?: boolean;
114
+ }): string;
115
+ /** The document's words without structure — what search indexes and an excerpt shows. */
116
+ declare function plainText(content: XenoDocNode | undefined): string;
117
+ /** Words, the way XENO Docs' status bar counts them. */
118
+ declare function wordCount(content: XenoDocNode | undefined): number;
119
+ /** The ids every page link (`wikiLink` mark) in the document points at, deduplicated, in order. */
120
+ declare function pageLinksIn(content: XenoDocNode | undefined): string[];
121
+ /** Every node type in a document — what a surface checks before it decides it can edit it. */
122
+ declare function nodeTypesIn(content: XenoDocNode | undefined): Set<string>;
123
+
124
+ /**
125
+ * Markdown import/export engine
126
+ * Converts between TipTap JSON document model and Markdown text
127
+ * Uses a hand-written converter for full control over formatting fidelity
128
+ * Pattern: Typora/Obsidian markdown handling
129
+ */
130
+
131
+ /** TipTap's JSONContent, restated so this file imports no editor (attrs are as loose as TipTap's). */
132
+ interface JSONContent {
133
+ type?: string;
134
+ attrs?: Record<string, any>;
135
+ content?: JSONContent[];
136
+ marks?: {
137
+ type: string;
138
+ attrs?: Record<string, any>;
139
+ }[];
140
+ text?: string;
141
+ }
142
+ declare function documentToMarkdown(doc: JSONContent): string;
143
+ declare function markdownToDocument(md: string): XenoDocNode;
144
+
145
+ export { DOCUMENT_MARK_TYPES, DOCUMENT_NODE_TYPES, type JSONContent, LIMITS, SAFE_IMAGE, SAFE_LINK, type ValidateOptions, XDOC_EXTENSION, XDOC_FORMAT, XDOC_VERSION, XdocError, type XenoDocFile, type XenoDocMark, type XenoDocNode, type XenoDocProblem, documentToMarkdown, emptyContent, markdownToDocument, nodeTypesIn, pageLinksIn, parseXdoc, plainText, serializeXdoc, validateContent, wordCount };
@@ -0,0 +1,44 @@
1
+ import "../chunk-HDP35LGH.js";
2
+ import {
3
+ DOCUMENT_MARK_TYPES,
4
+ DOCUMENT_NODE_TYPES,
5
+ LIMITS,
6
+ SAFE_IMAGE,
7
+ SAFE_LINK,
8
+ XDOC_EXTENSION,
9
+ XDOC_FORMAT,
10
+ XDOC_VERSION,
11
+ XdocError,
12
+ documentToMarkdown,
13
+ emptyContent,
14
+ markdownToDocument,
15
+ nodeTypesIn,
16
+ pageLinksIn,
17
+ parseXdoc,
18
+ plainText,
19
+ serializeXdoc,
20
+ validateContent,
21
+ wordCount
22
+ } from "../chunk-NFOBZJDF.js";
23
+ import "../chunk-2KG3PWR4.js";
24
+ export {
25
+ DOCUMENT_MARK_TYPES,
26
+ DOCUMENT_NODE_TYPES,
27
+ LIMITS,
28
+ SAFE_IMAGE,
29
+ SAFE_LINK,
30
+ XDOC_EXTENSION,
31
+ XDOC_FORMAT,
32
+ XDOC_VERSION,
33
+ XdocError,
34
+ documentToMarkdown,
35
+ emptyContent,
36
+ markdownToDocument,
37
+ nodeTypesIn,
38
+ pageLinksIn,
39
+ parseXdoc,
40
+ plainText,
41
+ serializeXdoc,
42
+ validateContent,
43
+ wordCount
44
+ };