@quillmark/wasm 0.132.0 → 0.134.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.
package/core/wasm.d.ts CHANGED
@@ -21,12 +21,14 @@ export type PayloadItem =
21
21
  * `CardInput`, so a card read from one document pushes straight into another.
22
22
  *
23
23
  * `$` system entries are hoisted to named fields: `kind` (the `$kind`),
24
- * `quill` (`$quill` `name@version`, main card only), `ext` (`$ext`), and
25
- * `seed` (the `$seed` per-kind overlay map, main card only).
26
- * `payloadItems` carries user fields and comments in order.
24
+ * `id` (the `$id`, composable cards only), `quill` (`$quill`
25
+ * `name@version`, main card only), `ext` (`$ext`), and `seed` (the `$seed`
26
+ * per-kind overlay map, main card only). `payloadItems` carries user fields
27
+ * and comments in order.
27
28
  */
28
29
  export interface Card {
29
30
  kind: string;
31
+ id?: string;
30
32
  quill?: string;
31
33
  ext?: Record<string, unknown>;
32
34
  seed?: Record<string, unknown>;
@@ -41,10 +43,13 @@ export interface Card {
41
43
  /**
42
44
  * A card written *into* a document, accepted by `Document.insertCard`. Like
43
45
  * `Card`, but `body` also takes a markdown `string`, and every field but `kind`
44
- * is optional (defaulting to no payload items and an empty body).
46
+ * is optional (defaulting to no payload items and an empty body). A card
47
+ * without an `id` is minted one where it is placed; one whose `id` is not a
48
+ * card id, or is held by another card of the document, is refused.
45
49
  */
46
50
  export interface CardInput {
47
51
  kind: string;
52
+ id?: string;
48
53
  quill?: string;
49
54
  ext?: Record<string, unknown>;
50
55
  seed?: Record<string, unknown>;
@@ -52,156 +57,20 @@ export interface CardInput {
52
57
  body?: Content | string;
53
58
  }
54
59
 
55
- /**
56
- * Canonical richtext content: the model behind a card body and richtext fields.
57
- * One text sequence over a single coordinate space (Unicode scalar values):
58
- * `text` plus line attributes, anchored `marks`, and embedded `islands`. Every
59
- * edit is a splice; markdown is a projection, not the model.
60
- */
61
- export interface Content {
62
- text: string;
63
- lines: ContentLine[];
64
- marks: ContentMark[];
65
- islands: ContentIsland[];
66
- }
67
-
68
- /** One `\n`-separated segment of `Content.text`, in order. `kind` is a closed
69
- * set: `island` reads as `para`, and any other role outside it is refused
70
- * wherever content is decoded. Every role spells its payload in `attrs`, so
71
- * `kind === "heading"` narrows `attrs` to `{ level: number }` with no guard. */
72
- export type ContentLine = {
73
- containers: ContentContainer[];
74
- /** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
75
- continues?: boolean;
76
- } & ContentLineKind;
77
-
78
- /** A line's block role, shared by `ContentLine` and the `setKind` op. */
79
- export type ContentLineKind =
80
- | { kind: "para" }
81
- | { kind: "heading"; attrs: { level: number } }
82
- | { kind: "code"; attrs?: { lang?: string } }
83
- | { kind: "rule" };
84
-
85
- /** An ancestor block a line nests inside, outermost first. Closed like
86
- * `ContentLine.kind`.
87
- *
88
- * Two adjacent lines sit in the same container iff their whole path matches, so
89
- * `instance` is what tells one container from an adjacent sibling of identical
90
- * shape — two consecutive quotes, two consecutive lists — which contiguity
91
- * alone reads as one.
92
- *
93
- * **A writer owes a distinct value per adjacent sibling run.** Runs of one shape
94
- * sharing one arrive as one: a second list's items come back as continuation
95
- * paragraphs of the first, markers gone. Nothing reports that — an omitted field
96
- * and a `0` stamped on both are the same write — so a codec flattening a tree
97
- * takes them from `assignInstances` in `@quillmark/wasm` rather than by hand.
98
- * Any distinct pair works; a write is canonicalized to `0`/`1`.
99
- *
100
- * Absent is `0`, and a read omits it there, so a container with nothing adjacent
101
- * to be told apart from carries no key. A read does spell one on pairs no writer
102
- * had to: `1.` beside a list starting at `3` differs by `start`, so those runs
103
- * arrive apart with nothing written, and the canonical form spends a
104
- * discriminator anyway because Markdown reads only a list's first number. */
105
- export type ContentContainer =
106
- | {
107
- container: "list_item";
108
- /** `checked` is present on a task item: `true` ticked, `false` open. */
109
- attrs: { ordered: boolean; start: number; ordinal: number; checked?: boolean };
110
- instance?: number;
111
- }
112
- | { container: "quote"; instance?: number };
113
-
114
- /** A mark over char range `[start, end)` into `Content.text`. `type` is a
115
- * closed set, so `type === "link"` narrows `attrs` to `{ url: string }` with no
116
- * guard. An `anchor`'s `id` is a caller-supplied opaque
117
- * handle, unique per `Content` and invariant while the mark lives (positions
118
- * rebase, the id never does); it has no markdown projection and survives only
119
- * through the edit lane. */
120
- export type ContentMark = { start: number; end: number } & ContentMarkKind;
121
-
122
- /** A mark's type with its payload, shared by `ContentMark` and a `MarkOp`'s
123
- * `add` / `remove`. */
124
- export type ContentMarkKind =
125
- | { type: "strong" | "emph" | "underline" | "strike" | "code" }
126
- | { type: "link"; attrs: { url: string } }
127
- | { type: "anchor"; attrs: { id: string } }
128
- | { type: "font_family"; attrs: { name: string } };
129
-
130
- /** A cell in a `TableProps`. `marks` rides the prose `ContentMark` shape, but
131
- * each mark's `start`/`end` are USV offsets into this cell's `text`, not into
132
- * `Content.text`. A `\n` in `text` is a line break. */
133
- export interface TableCell {
134
- text: string;
135
- marks: ContentMark[];
136
- /** The cell's horizontal alignment; absent is its column's. */
137
- align?: "left" | "center" | "right";
138
- /** The cell's vertical alignment; absent is the quill's. */
139
- valign?: "top" | "middle" | "bottom";
140
- /** The columns the cell spans, from 2; absent is one. Each cell it covers
141
- * holds its slot as an empty placeholder. */
142
- colspan?: number;
143
- /** The rows the cell spans, from 2; absent is one. */
144
- rowspan?: number;
145
- }
146
-
147
- /** `props` of a `type: "table"` island: a rectangular grid normalized to one
148
- * column count that `header`, every row of `rows`, `aligns` and `widths` all
149
- * share. A layout key at its default is absent; one holding a value outside
150
- * its type stays as written and reads as absent. */
151
- export interface TableProps {
152
- header: TableCell[];
153
- rows: TableCell[][];
154
- /** Per-column alignment, one entry per column. */
155
- aligns: ("none" | "left" | "center" | "right")[];
156
- /** Per-column relative weights; `null` is an auto-fit column. Absent when
157
- * every column is auto-fit. A weight is an integer from 1 to
158
- * `Number.MAX_SAFE_INTEGER`. */
159
- widths?: (number | null)[];
160
- /** The table's placement; absent is the quill's. */
161
- align?: "left" | "center" | "right";
162
- /** How many rows from the top of `header` then `rows` are header rows;
163
- * `0` draws `header` as the first body row, and absent is one. */
164
- head?: number;
165
- }
166
-
167
- /** `props` of a `type: "image"` island. Stores and round-trips; no backend
168
- * typesets one, and a render that holds one warns `backend::declined_construct`,
169
- * because no backend resolves `url`. */
170
- export interface ImageProps {
171
- url: string;
172
- alt: string;
173
- }
174
-
175
- /** `props` of a `type: "footnote"` island: the note, shaped as one `TableCell`
176
- * with no `\n` at either edge. The island's slot is the reference, inline in
177
- * its paragraph. */
178
- export type FootnoteProps = TableCell;
179
-
180
- /** `props` of a `type: "math"` island: the equation's TeX, as written between
181
- * its dollars, on one line with no edge whitespace. `display` is present and
182
- * `true` on a `$$…$$` equation. */
183
- export interface MathProps {
184
- tex: string;
185
- display?: true;
186
- }
187
-
188
- /** A structured object occupying one island slot in `Content.text`. `type` is a
189
- * closed set, so `type === "table"` narrows `props` to `TableProps`. */
190
- export type ContentIsland = {
191
- id: string;
192
- } & (
193
- | { type: "table"; props: TableProps }
194
- | { type: "image"; props: ImageProps }
195
- | { type: "footnote"; props: FootnoteProps }
196
- | { type: "math"; props: MathProps }
197
- );
198
-
199
60
  /**
200
61
  * A write address: one navigation concept for the whole `Document` surface. An
201
62
  * absent `field` targets the card body; an absent `card` targets the main card.
202
- * `{}` is the main-card body; `{ card: 2 }` the body of the composable card at
203
- * index 2; `{ field: "intro" }` the main card's `intro` field; `{ card: 2,
204
- * field: "intro" }` a card field.
63
+ * A composable card is named by its `$id` (`card.id`) or by its index: `{}` is
64
+ * the main-card body; `{ card: "c3xk7p" }` the body of the card holding that
65
+ * id, wherever it sits; `{ card: 2 }` the body of the card at index 2;
66
+ * `{ field: "intro" }` the main card's `intro` field; `{ card: "c3xk7p",
67
+ * field: "intro" }` a card field. An id no card holds throws
68
+ * `edit::unknown_card_id`.
69
+ *
70
+ * The content lane's writes (`apply`, `revise`, `replace`) take a
71
+ * card by `$id` only, and throw on an index: a card moved or inserted between
72
+ * a writer's read and its write shifts an index onto another card, and an
73
+ * equal body there would pass the `base` check.
205
74
  *
206
75
  * On the `Addr`-taking verbs a **bare string** is shorthand for `{ field: name }`
207
76
  * (`doc.storeField("qty", 3)`); a bare number is *not* an addr.
@@ -216,23 +85,23 @@ export type ContentIsland = {
216
85
  * inside takes the path as its own argument, `reader.getContentAt(addr, path)`.
217
86
  */
218
87
  export interface Addr {
219
- card?: number;
88
+ card?: string | number;
220
89
  field?: string;
221
90
  }
222
91
 
223
92
  /**
224
- * A card-only address, taken by the card-scoped verbs (`storeFields`,
225
- * `storeExt`, `getExt`, `commitFields`, …). An absent `card` targets the main
226
- * card; a present `field` throws.
93
+ * A card-only address, taken by the card-scoped verbs (`storeExt`, `getExt`, `commitFields`, …). An absent `card` targets the main
94
+ * card, a string the card holding that `$id`, a number the card at that
95
+ * index; a present `field` throws.
227
96
  */
228
97
  export interface CardAddr {
229
- card?: number;
98
+ card?: string | number;
230
99
  }
231
100
 
232
101
  /**
233
- * A text-splice change set over the USV content (CodeMirror `ChangeSet`
234
- * semantics), returned by `revise` and by the `rebase` codec. Map a stored
235
- * position through it with `mapPos`.
102
+ * A text-splice change set over the revised value's text, counted in UTF-16
103
+ * code units (CodeMirror `ChangeSet` semantics), returned in a revise's
104
+ * `Revised` receipt and taken back by `reattach`.
236
105
  *
237
106
  * Applying one admits an `insert` string rather than storing it verbatim: `\r`
238
107
  * and the Unicode bidi controls drop, and a line separator — VT, FF, NEL,
@@ -243,13 +112,34 @@ export interface Delta {
243
112
  }
244
113
 
245
114
  /**
246
- * The receipt of a markdown revise (`revise`, `writer.reviseBody`,
247
- * `writer.reviseField`): the text `delta`, and one `parse::dropped_construct`
248
- * warning per construct the import dropped, anchored at the revised address.
115
+ * The receipt of a typed revise (`writer.reviseBody`, `writer.reviseField`): the text `delta`, one `parse::dropped_construct`
116
+ * warning per construct a markdown import dropped, anchored at the revised
117
+ * address, and `droppedAnchors`, every anchor of the stored value, in its
118
+ * prose or a cell, whose id the revised value holds nowhere, each with the
119
+ * text of the line or cell it stood in. The revised
120
+ * address is the dropped anchors' path, so they carry none.
249
121
  */
250
122
  export interface Revised {
251
123
  delta: Delta;
252
124
  warnings: Diagnostic[];
125
+ droppedAnchors: { id: string; line: string }[];
126
+ }
127
+
128
+ /**
129
+ * What a revise's writer read, the optional last argument of the writer's
130
+ * `reviseBody` / `reviseField` / `reviseDocument`.
131
+ * Name one or neither; a key present with no string throws. `baseHash` is the
132
+ * hash the writer read, paired with the lane it writes on: `writer.hashAt` /
133
+ * `writer.hash`. A write onto a value that hashes otherwise
134
+ * throws `edit::stale_base`. `baseMarkdown` is the markdown the writer read, annotated
135
+ * or not (a `text` field's literal text): when the value has changed since,
136
+ * the writer's edit and the change since merge by line, so both land, and
137
+ * runs of lines both changed throw `edit::rebase_conflict`. Each refusal's
138
+ * `args.current` is the hash now, and leaves the value unchanged.
139
+ */
140
+ export interface BaseOptions {
141
+ baseHash?: string;
142
+ baseMarkdown?: string;
253
143
  }
254
144
 
255
145
  /**
@@ -258,20 +148,26 @@ export interface Revised {
258
148
  * cell's anchors drop unnamed. `warnings` are the parse's, then each revised
259
149
  * content field's `parse::dropped_construct`, then the writer's `conform::*`.
260
150
  * A field that lands rather than revises, on an inserted card or over no stored
261
- * content, reports no drop.
151
+ * content, reports no drop. `from` names, for each composable card of the
152
+ * revised document, the index of the stored card it revises, or `null` for an
153
+ * inserted card; a stored card no entry names was removed, and only cards of
154
+ * one `$kind` pair.
262
155
  */
263
156
  export interface DocumentRevised {
264
157
  droppedAnchors: DocumentAnchor[];
265
158
  warnings: Diagnostic[];
159
+ from: (number | null)[];
266
160
  }
267
161
 
268
162
  /**
269
163
  * The read `toAnnotatedMarkdown` returns: `toMarkdown`'s markdown with a
270
- * read-only `<qm-anchor ref="…"></qm-anchor>` at each prose anchor's
271
- * start its line can hold, and every prose anchor of every body and content
272
- * field, spelled or not; a table cell's anchors are neither. Each names the
273
- * `path` of its body or field and the text of the `line` its start sits on,
274
- * island slots removed.
164
+ * `<qm-anchor ref="…"></qm-anchor>` at each prose anchor's start its line
165
+ * can hold, and a `<qm-island ref="…"></qm-island>` token for each island
166
+ * whose markdown does not read back, which every revise reads back; and every
167
+ * anchor of every body and content field, spelled or not, a table cell's or
168
+ * a note's listed unspelled. Each names the `path` of its body or field and
169
+ * the text of the `line` its start sits on, island slots removed, or of its
170
+ * cell.
275
171
  */
276
172
  export interface AnnotatedMarkdown {
277
173
  markdown: string;
@@ -281,9 +177,40 @@ export interface AnnotatedMarkdown {
281
177
  export interface DocumentAnchor {
282
178
  id: string;
283
179
  path: string;
180
+ /** The `$id` of the card `path` names; absent on the main card. */
181
+ cardId?: string;
284
182
  line: string;
285
183
  }
286
184
 
185
+ /**
186
+ * An anchor's context at its address (`anchorContexts`, `anchorContext`,
187
+ * `reattach`): the text-quote selector a host stores beside a comment thread.
188
+ * `path` is the body or content value's `DocPath` string. `quote` is the
189
+ * anchored text as stored, an inline island its `U+FFFC` slot and a line break
190
+ * its `\n`, empty for a point; `prefix` and `suffix` are up to 32 scalar values
191
+ * of stored text on each side, cut at the value's edges or a cell's. `cardId` is the `$id`
192
+ * of the card `path` names, absent on the main card: `reattach` finds the card
193
+ * by it wherever the card now sits, and orphans the selector where no card of
194
+ * the path's kind holds it.
195
+ */
196
+ export interface AnchorContext {
197
+ id: string;
198
+ path: string;
199
+ cardId?: string;
200
+ quote: string;
201
+ prefix: string;
202
+ suffix: string;
203
+ }
204
+
205
+ /**
206
+ * The receipt of `reattach`: each selector given, in order, either reattached
207
+ * with its context as read after the write, or orphaned as given.
208
+ */
209
+ export interface Reattached {
210
+ reattached: AnchorContext[];
211
+ orphaned: AnchorContext[];
212
+ }
213
+
287
214
  /**
288
215
  * A markdown import (`importMarkdown`): the canonical `content`, and one
289
216
  * `parse::dropped_construct` warning per construct it dropped, with no `path`.
@@ -293,104 +220,201 @@ export interface Imported {
293
220
  warnings: Diagnostic[];
294
221
  }
295
222
 
296
- /** Which side of a same-position insertion `mapPos` lands a point on. */
297
- export type Assoc = "before" | "after";
223
+
224
+
298
225
 
299
226
  /**
300
- * A mark edit in final-text coordinates (post-delta, post-line-op). `add` /
301
- * `remove` are a `ContentMark` under an op, so a held mark spreads in whole;
302
- * `removeAnchor` drops one identity anchor by id. An `add` of a `link` or a
303
- * `font_family` replaces the one under its range, and a mark added across a
304
- * line break reads back as one mark per line. An `add` of an `anchor`
305
- * requires a non-empty `id` not already live in the field; a collision or the
306
- * empty id throws.
227
+ * One rich-text value: a card body or a `richtext` field. Flat `text` where a
228
+ * `\n` opens a block, `U+2028` is a hard break and `U+FFFC` is an island's
229
+ * slot; one block record per `\n`-separated segment, each `\n` carrying the
230
+ * record of the block it opens and `blocks[0]` the first block's; containers
231
+ * the blocks' paths name; formatting marks; comment anchors; and islands, a
232
+ * table's cells and a footnote's note each a nested `Content` in its `slots`.
233
+ * Every part carries an id. Offsets are UTF-16 code units, JavaScript string
234
+ * indices, into the `text` of the value holding them.
307
235
  */
308
- export type MarkOp =
309
- | ({ op: "add" | "remove" } & ContentMark)
310
- | { op: "removeAnchor"; id: string };
236
+ export interface Content {
237
+ text: string;
238
+ blocks: ContentBlock[];
239
+ containers?: ContentContainer[];
240
+ marks?: ContentMark[];
241
+ anchors?: ContentAnchor[];
242
+ islands?: ContentIsland[];
243
+ }
244
+
245
+ /** A block's record: its kind and attributes, and its container path,
246
+ * outermost first. A kind, mark type or island type spelled `x-` and more
247
+ * is one this build does not know, held as written: a block reads as a
248
+ * paragraph, a mark formats nothing, an island renders as nothing. */
249
+ export type ContentBlock = { id: string; path?: string[] } & ContentBlockKind;
250
+
251
+ export type ContentBlockKind =
252
+ | { kind: "para" }
253
+ | { kind: "heading"; attrs: { level: number } }
254
+ | { kind: "code"; attrs?: { lang?: string } }
255
+ | { kind: "rule" }
256
+ | { kind: `x-${string}`; attrs?: Record<string, unknown> };
257
+
258
+ /** A list, a list item or a quote. An item's number is computed, never stored. */
259
+ export type ContentContainer = { id: string } & (
260
+ | { kind: "list"; attrs: { ordered: boolean; start: number } }
261
+ | { kind: "item"; attrs?: { checked?: boolean } }
262
+ | { kind: "quote" }
263
+ );
264
+
265
+ /** Formatting over `[start, end)`. Same-kind marks union; `link` and
266
+ * `font_family` hold one value per character. */
267
+ export type ContentMark = { start: number; end: number } & ContentMarkKind;
268
+
269
+ export type ContentMarkKind =
270
+ | { type: "strong" | "emph" | "underline" | "strike" | "code" }
271
+ | { type: "link"; attrs: { url: string } }
272
+ | { type: "font_family"; attrs: { name: string } }
273
+ | { type: `x-${string}`; attrs?: Record<string, unknown> };
274
+
275
+ /** A comment handle over `[start, end)` with a host-minted id; a point when
276
+ * the two are equal. */
277
+ export interface ContentAnchor {
278
+ id: string;
279
+ start: number;
280
+ end: number;
281
+ }
282
+
283
+ /** `props` of a `table` island. `columns` are column ids; each row maps a
284
+ * column id to the slot holding its cell. `rows[0]` is the header row, and
285
+ * `head` counts header rows from the top, absent at 1. `aligns` and `widths`
286
+ * are keyed by column id, `cell_attrs` by slot id. */
287
+ export interface TableProps {
288
+ columns: string[];
289
+ rows: { id: string; cells: Record<string, string> }[];
290
+ head?: number;
291
+ aligns?: Record<string, "left" | "center" | "right">;
292
+ widths?: Record<string, number>;
293
+ align?: "left" | "center" | "right";
294
+ cell_attrs?: Record<string, {
295
+ align?: "left" | "center" | "right";
296
+ valign?: "top" | "middle" | "bottom";
297
+ colspan?: number;
298
+ rowspan?: number;
299
+ }>;
300
+ }
301
+
302
+ /** An island: the object one `U+FFFC` of `text` stands for. A `table`'s cells
303
+ * and a `footnote`'s `note` are its `slots`. */
304
+ export type ContentIsland = { id: string; slots?: Record<string, Content> } & (
305
+ | { type: "table"; props: TableProps }
306
+ | { type: "image"; props: { url: string; alt: string } }
307
+ | { type: "footnote"; props: Record<string, never> }
308
+ | { type: "math"; props: { tex: string; display?: true } }
309
+ | { type: `x-${string}`; props: Record<string, unknown> }
310
+ );
311
311
 
312
312
  /**
313
- * A line/block edit. `split`/`join` splice `\n` in post-`delta`,
314
- * post-`islandOps` coordinates; `setKind`/`setContainers`/`setContinues` touch
315
- * metadata. `setContinues` sets or clears a line's within-block hard-break flag
316
- * (`ContentLine.continues`); `continues: true` lands as `false` on line 0, which
317
- * nothing precedes, on a line whose containers differ from the line above, and
318
- * on one following a heading, a rule or a block island's line, each a block of
319
- * one line. `setKind` lands a kind the line's text contradicts — `rule` over
320
- * prose, `code` over a slot — as `para`, which is what re-importing the line's
321
- * own markdown yields, and reads an `island` the union does not offer as the
322
- * `para` that spelling projects. Read the content back to see where an op
323
- * settled.
313
+ * An edit: a list of steps, each reading the value the step before it left.
314
+ * `apply` runs them all or none.
324
315
  */
325
- export type LineOp =
326
- | { op: "split"; at: number }
327
- | { op: "join"; line: number }
328
- | ({ op: "setKind"; line: number } & ContentLineKind)
329
- | { op: "setContainers"; line: number; containers: ContentContainer[] }
330
- | { op: "setContinues"; line: number; continues: boolean };
316
+ export interface Change {
317
+ steps: Step[];
318
+ }
331
319
 
332
320
  /**
333
- * An island edit: the only channel that reaches an island's payload, a table's
334
- * cells or an image's url. Both ops leave the field's text and marks alone, so
335
- * an island edit keeps every identity anchor in the field.
321
+ * One step, an object of one key naming it.
336
322
  *
337
- * `set` addresses an existing island by `id`; an unknown `id` throws. `insert`
338
- * places a new island's slot at `at` together with its entry, so a slot never
339
- * exists without an island behind it; its `id` must be non-empty and unused.
340
- * `at` is a position in the text the `delta` and this bundle's earlier island
341
- * ops left, so slots after `a` and `b` of `abc` go in at 1 and 3. A stale frame
342
- * misplaces slots and never throws. A `delta` insert string may not carry a
343
- * slot, which would orphan: split such a splice into the slot-free `delta` plus
344
- * one `insert` per slot.
323
+ * - `splice` replaces `delete`, which must be the text at `at`, with `insert`.
324
+ * `marks` is the formatting the inserted text carries, offsets into
325
+ * `insert`, a mark without `start`/`end` covering all of it; `blocks` the
326
+ * record of each block an inserted `\n` opens; `islands` the island of each
327
+ * inserted `U+FFFC`. `removed` is what the deleted text held, filled in on
328
+ * the receipt's `applied`.
329
+ * - `setBlock` changes a block's kind, attributes or path.
330
+ * - `container` adds a container or sets an existing one's attributes, or
331
+ * removes one no path names.
332
+ * - `mark` adds or removes formatting over a range.
333
+ * - `anchor` places, moves or removes a comment anchor.
334
+ * - `island` replaces an island's props, or inserts an island's slot at `at`.
335
+ * - `nested` applies a change inside one slot of an island.
345
336
  *
346
- * Deleting an island needs no op: a `delta` that removes its slot drops the
347
- * island whole. Re-landing it is an `insert` of the full island under its
348
- * original id; a pasted copy of a live island mints a fresh one.
349
- *
350
- * An island is *inline* (a slot inside a paragraph) or a **block** (that slot
351
- * alone on its own line), and the type settles which: markdown writes a `table`
352
- * as a block and an image inline. Both lines read `kind: "para"` — the block is
353
- * the slot's markup, not a role the line carries — so landing a block island is
354
- * two channels: `delta` inserts the `\n` that opens the line, `islandOps`
355
- * inserts the slot. `{ op: "split" }` cannot open that line, since line ops run
356
- * after island ops.
357
- *
358
- * A `table` has no inline placement: markdown writes it as a block, so an
359
- * `insert` whose `at` is not an empty line throws, as does a `set` retyping an
360
- * inline island into one.
337
+ * A `mark` step and an `anchor` placement take `quote`, the text their range
338
+ * covered when the writer read it, and an island insert takes `block`, the id
339
+ * of the block the writer read `at` in. A step whose `quote` or `block` the
340
+ * value no longer holds is refused as stale (`edit::content_apply`), as a
341
+ * splice is whose `delete` the value no longer holds. The receipt's `applied`
342
+ * fills both in.
361
343
  */
362
- export type IslandOp =
363
- | ({ op: "set" } & ContentIsland)
364
- | ({ op: "insert"; at: number } & ContentIsland);
344
+ export type Step =
345
+ | { splice: { at: number; delete?: string; insert: string;
346
+ marks?: ({ start?: number; end?: number } & ContentMarkKind)[];
347
+ blocks?: ContentBlock[]; islands?: ContentIsland[];
348
+ removed?: { blocks?: ContentBlock[]; marks?: ContentMark[]; islands?: ContentIsland[] } } }
349
+ | { setBlock: { id: string; kind?: ContentBlockKind["kind"]; attrs?: Record<string, unknown>; path?: string[] } }
350
+ | { container: ContentContainer | { id: string; remove: true } }
351
+ | { mark: { start: number; end: number; add: boolean; quote?: string } & ContentMarkKind }
352
+ | { anchor: (ContentAnchor & { quote?: string }) | { id: string; remove: true } }
353
+ | { island: { id: string; props: Record<string, unknown>; slots?: Record<string, Content> } | { at: number; island: ContentIsland; block?: string } }
354
+ | { nested: { island: string; slot: string; change: Change } };
365
355
 
366
356
  /**
367
- * A committed content edit bundle for `applyChange`, applied in order: a text
368
- * `delta`, then `islandOps`, then `lineOps`, then `markOps` (mark ranges are in
369
- * final-text coordinates). Every field is optional.
370
- *
371
- * Within each channel ops apply in sequence against the state the earlier ones
372
- * left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
373
- * positions and indices renumber through earlier `split`/`join`.
374
- *
375
- * **Mark rebase.** `delta`, `islandOps` and `lineOps` each move text, and each
376
- * rebases the marks already in the field by one rule: a range mark's `start`
377
- * takes assoc `after` and its `end` `before`, so an insertion at either edge
378
- * grows text *outside* the span; a **zero-width** mark takes `before`, so an
379
- * insertion at its own position leaves it put. That last case is the one
380
- * position where the two assocs differ, and where an anchor most often sits.
381
- *
382
- * `markOps` name the result, so a caller emitting them predicts this rebase.
383
- * `mapMarks(content, bundle)` runs it instead: pass the bundle's text-moving
384
- * channels, diff the marks it returns against the ones you intend, and emit
385
- * only the difference. Reproducing the rule by hand is a second copy to drift.
357
+ * What a write did. `applied` is the change in its normalized form, which
358
+ * `invert(applied, before)` undoes; `hash` is the value's new hash, the next
359
+ * write's `base`. `droppedAnchors` names each anchor the value held and holds
360
+ * nowhere now, as it read before: every one a `revise` or `replace` lost, and
361
+ * for an `apply` every one but those its `anchor` steps removed.
362
+ * `movedAnchors` names each surviving one whose text the write rewrote.
363
+ * `warnings` holds a `parse::dropped_construct` per construct a revise's
364
+ * markdown dropped.
365
+ * `rebased` says the write's `base` was a value the field had moved on from,
366
+ * and the write was rebased over what changed since.
386
367
  */
387
- export interface ChangeBundle {
388
- delta?: Delta;
389
- islandOps?: IslandOp[];
390
- lineOps?: LineOp[];
391
- markOps?: MarkOp[];
368
+ export interface Receipt {
369
+ applied: Change;
370
+ hash: string;
371
+ droppedAnchors: DroppedAnchor[];
372
+ movedAnchors: { id: string; before: string; after: string }[];
373
+ warnings: Diagnostic[];
374
+ rebased: boolean;
375
+ }
376
+
377
+ /**
378
+ * A receipt's dropped anchor: its selector, which `reattach` takes as given,
379
+ * and the `island` and `slot` that held it, absent for an anchor in the
380
+ * value's own text.
381
+ */
382
+ export interface DroppedAnchor extends AnchorContext {
383
+ island?: string;
384
+ slot?: string;
385
+ }
386
+
387
+ /** The hash a writer read. A value that has changed since rebases the write
388
+ * over the change, refusing it with `edit::rebase_conflict` where the two
389
+ * touch the same text or record, and with `edit::stale_base` where the document no
390
+ * longer holds the change since. Pass it: a write without one lands over
391
+ * whatever the value now holds. */
392
+ export interface WriteOptions {
393
+ base?: string;
394
+ }
395
+
396
+ /** A position in a value: an offset into its text, or one inside an
397
+ * island's slot. */
398
+ export type Pos = number | { island: string; slot: string; at: Pos };
399
+
400
+ /**
401
+ * Where `mapPos` carried a position, and what around it the change deleted:
402
+ * the character on its `assoc` side (`deleted`), the one before, the one
403
+ * after, or both in one deletion (`deletedAcross`).
404
+ */
405
+ export interface MapResult {
406
+ pos: Pos;
407
+ deleted: boolean;
408
+ deletedBefore: boolean;
409
+ deletedAfter: boolean;
410
+ deletedAcross: boolean;
392
411
  }
393
412
 
413
+ /** What `wrap` opens around the blocks it wraps: a quote, or a list with an
414
+ * item per wrapped child, each `checked` as given. */
415
+ export type WrapKind = { kind: "quote" } | { kind: "list"; ordered: boolean; checked?: boolean };
416
+
417
+
394
418
 
395
419
 
396
420
  /**
@@ -500,6 +524,11 @@ export interface QuillCardUi {
500
524
  export interface QuillCardBody {
501
525
  /** When false, consumers must not accept or store body content for this card kind. Defaults to true. */
502
526
  enabled?: boolean;
527
+ /** What the body holds, for the writer. Never beside `enabled: false`. */
528
+ description?: string;
529
+ /** A made-up body in markdown, which `exampleDocument()` commits. Never
530
+ * beside `enabled: false`. */
531
+ example?: string;
503
532
  }
504
533
 
505
534
  export type QuillFieldType = "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "text" | "richtext" | "enum" | "matrix";
@@ -507,7 +536,8 @@ export type QuillFieldType = "number" | "integer" | "boolean" | "array" | "objec
507
536
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
508
537
  *
509
538
  * `default` is the value an unanswered cell renders. An absent field without a
510
- * `default` blank-fills.
539
+ * `default` blank-fills. `example` is a made-up value of the same type, which
540
+ * answers no cell: `exampleDocument()` commits it.
511
541
  */
512
542
  export interface QuillFieldSchema {
513
543
  /** A trailing `?` marks the cell optional: unanswered, it renders `none`
@@ -519,6 +549,7 @@ export interface QuillFieldSchema {
519
549
  title?: string;
520
550
  description?: string;
521
551
  default?: unknown;
552
+ example?: unknown;
522
553
  /** The closed set of allowed values. Required on `type: "enum"`, and valid
523
554
  * nowhere else. */
524
555
  values?: string[];
@@ -583,6 +614,26 @@ export interface QuillSchema {
583
614
  card_kinds?: Record<string, QuillCardSchema>;
584
615
  }
585
616
 
617
+ /**
618
+ * One card of an `AuthoringDocument`: its kind, its fields in the values form,
619
+ * and its body as Markdown. `null` anywhere a value goes is absent.
620
+ */
621
+ export interface AuthoringCard {
622
+ kind: string;
623
+ fields?: Record<string, unknown> | null;
624
+ body?: string | null;
625
+ }
626
+
627
+ /**
628
+ * The JSON a model reads and writes, described by `Quill.authoringSchema`:
629
+ * the main card's fields and body, and the composable cards in order.
630
+ */
631
+ export interface AuthoringDocument {
632
+ fields?: Record<string, unknown> | null;
633
+ body?: string | null;
634
+ cards?: AuthoringCard[] | null;
635
+ }
636
+
586
637
  /**
587
638
  * Identity snapshot mirroring the `quill:` section of `Quill.yaml`. The schema
588
639
  * lives on `Quill.schema`; output formats are a resolved-backend capability read
@@ -635,22 +686,40 @@ export class Document {
635
686
  free(): void;
636
687
  [Symbol.dispose](): void;
637
688
  /**
638
- * **Apply** a committed content edit `bundle` at `addr`, the editor splice:
639
- * text delta first, then island ops, then line ops, then mark ops (mark
640
- * ranges in final-text coordinates), all-or-nothing. An absent `addr.field`
641
- * targets the body, an absent `addr.card` the main card. The island channel
642
- * moves an island alone, so anchors elsewhere in the field survive an edit
643
- * `overwrite` would clear.
644
- *
645
- * Throws on an out-of-range card, a field that is not richtext, a malformed
646
- * bundle, or an op that applies out of bounds; the value is unchanged on a
647
- * failed apply.
648
- *
649
- * Each text-moving channel rebases the marks already in the field, by the
650
- * rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
651
- * building `markOps` need not predict it.
689
+ * The context of the anchor `id`, or `undefined` where no body or
690
+ * content value holds one.
691
+ */
692
+ anchorContext(id: string): AnchorContext | undefined;
693
+ /**
694
+ * Every anchor of every body and content value with its context, a table
695
+ * cell's or a note's included, in the order `toAnnotatedMarkdown` lists
696
+ * anchors: the selector a host stores beside a comment thread and hands
697
+ * back to `reattach`.
652
698
  */
653
- applyChange(addr: Addr | string, bundle: ChangeBundle): void;
699
+ anchorContexts(): AnchorContext[];
700
+ /**
701
+ * The annotated read of the body or richtext field at `addr` alone: the
702
+ * scoped twin of `toAnnotatedMarkdown`, what a revise at `addr` reads
703
+ * back and the `baseMarkdown` it merges against. Each anchor names `addr`'s
704
+ * path. An absent field reads as the empty content. Throws as `hashAt`.
705
+ */
706
+ annotatedAt(addr: Addr | string): AnnotatedMarkdown;
707
+ /**
708
+ * The markdown at `addr` with a `qm-*` tag for each anchor and island id
709
+ * its lines can hold: what `revise` reads back. Throws as `read`.
710
+ */
711
+ annotated(addr: Addr | string): string;
712
+ /**
713
+ * **Apply** `change` to the value at `addr`, all its steps or none, and
714
+ * answer the `Receipt`. `opts.base` is the hash the writer read
715
+ * (`hash(addr)`), and the change's offsets are into the value it names:
716
+ * one the value has moved on from rebases the change over the change
717
+ * since, throwing `edit::rebase_conflict` where they touch the same text or
718
+ * record and `edit::stale_base` where the document no longer holds the change
719
+ * since. A refused change throws `edit::content_apply` and writes
720
+ * nothing.
721
+ */
722
+ apply(addr: Addr | string, change: Change, opts?: WriteOptions): Receipt;
654
723
  /**
655
724
  * The **body** markdown projection: an on-demand, lossy export (content-only
656
725
  * marks do not survive markdown). A body's type is a format fact, not a
@@ -677,11 +746,10 @@ export class Document {
677
746
  clone(): Document;
678
747
  /**
679
748
  * The newest storage version this build writes via
680
- * [`toStored`](Document::to_stored): a document spelling a font mark, a
681
- * `table` element, a merged table cell or a table head 0.130 cannot draw
682
- * carries it, every other the tag before it, which a 0.130 build reads.
683
- * The tag advances only when the wire format changes, not on every
684
- * release.
749
+ * [`toStored`](Document::to_stored): a document holding a composable
750
+ * card, each carrying its `$id`, carries it. A document of the main card
751
+ * alone carries the oldest tag that holds its content. The tag advances
752
+ * only when the wire format changes, not on every release.
685
753
  */
686
754
  static currentStorageVersion(): string;
687
755
  /**
@@ -694,7 +762,7 @@ export class Document {
694
762
  static fromMarkdown(markdown: string): Document;
695
763
  /**
696
764
  * Reconstruct a `Document` from a versioned storage DTO string produced by
697
- * [`toStored`](Document::to_stored). The result carries no parse-time warnings.
765
+ * [`toStored`](Document::to_stored), with no `warnings`.
698
766
  * Throws if `json` is not a valid storage DTO (malformed JSON, unknown
699
767
  * `schema`, missing fields, or unparseable quill reference).
700
768
  */
@@ -726,6 +794,23 @@ export class Document {
726
794
  * unread, which is the contract and why it is `unknown`.
727
795
  */
728
796
  getStored(addr: Addr | string): unknown;
797
+ /**
798
+ * The hash of the body or richtext field at `addr`, the `baseHash` a
799
+ * revise at `addr` checks: the hash of its content, an absent field the
800
+ * empty content. An absent `addr.field` reads the body, an absent
801
+ * `addr.card` the main card. Richtext only; the
802
+ * typed `writer.hashAt` reads a field of any type through its schema.
803
+ * Throws on an out-of-range card, a malformed field name, or a present
804
+ * non-content field value.
805
+ */
806
+ hashAt(addr: Addr | string): string;
807
+ /**
808
+ * With no `addr`, the hash of the document's stored form, the quill-free
809
+ * twin of `writer.hash()`: equal documents hash alike, whatever order a
810
+ * store kept their keys in. With one, the hash of the `Content` at
811
+ * `addr`, the `base` a write to it names. Throws as `read`.
812
+ */
813
+ hash(addr?: Addr | string): string;
729
814
  /**
730
815
  * Insert a card: `at` absent appends, a number inserts at that index (in
731
816
  * `0..=cards.length`). Accepts any `CardInput`, including a card read back
@@ -734,19 +819,6 @@ export class Document {
734
819
  * 1,000 cards.
735
820
  */
736
821
  insertCard(card: CardInput, at?: number): void;
737
- /**
738
- * Replace this document's contents **in place** from a versioned storage DTO
739
- * string: the mutating twin of [`fromStored`](Document::from_stored).
740
- * Parse-time `warnings` are cleared. Throws on an invalid DTO, leaving the
741
- * document unchanged. A caller holding the document need not re-bind its
742
- * variable.
743
- */
744
- loadStored(json: string): void;
745
- /**
746
- * The Quillmark Markdown rules, re-exposed from core. Constant across
747
- * calls; read once and cache.
748
- */
749
- static markdownRules(): string;
750
822
  /**
751
823
  * Move the card at `from` to position `to`. `from == to` is a no-op.
752
824
  */
@@ -759,17 +831,6 @@ export class Document {
759
831
  * an invalid quill reference.
760
832
  */
761
833
  constructor(quill_ref: string);
762
- /**
763
- * **Overwrite** the content value at `addr` with exactly `rt`: value
764
- * semantics, so the identity anchors of any previous value are gone. By
765
- * anchor fate `overwrite` destroys, [`revise`](Document::revise) rebases,
766
- * and [`applyChange`](Document::apply_change) preserves. An absent
767
- * `addr.field` targets the body, an absent `addr.card` the main card.
768
- *
769
- * Throws on an out-of-range card, a malformed field name, or an `rt` that is
770
- * not a canonical content object.
771
- */
772
- overwrite(addr: Addr | string, rt: Content): void;
773
834
  /**
774
835
  * `addr`'s canonical `DocPath` string, the anchor `Diagnostic.path` carries:
775
836
  * `pathFor()` is `main.body`, `pathFor("intro")` `main.intro`,
@@ -783,7 +844,7 @@ export class Document {
783
844
  * **Total on the index axis**, unlike the `Addr` reads, which throw there:
784
845
  * an out-of-range `{card: 7, field: "from"}` renders `cards[7].from`, which
785
846
  * parses back and resolves to nothing rather than mis-targeting. Only a
786
- * malformed address throws.
847
+ * malformed address throws, or a card id no card holds.
787
848
  */
788
849
  pathFor(addr: Addr | string): string;
789
850
  /**
@@ -792,6 +853,32 @@ export class Document {
792
853
  * messages from this instead of re-stating the rule.
793
854
  */
794
855
  static quillRefHint(): string;
856
+ /**
857
+ * The `Content` at `addr`: an absent `addr.field` reads the body, an
858
+ * absent `addr.card` the main card, and an absent field the empty
859
+ * content. Throws on an out-of-range card, a malformed field name, or a
860
+ * present field value that is not content.
861
+ */
862
+ read(addr: Addr | string): Content;
863
+ /**
864
+ * Re-add anchors a write dropped, each at the `path` its selector names,
865
+ * where its `quote` is found exactly once with its context agreeing: four
866
+ * scalar values of quote or more, or a table's slot alone, eight of
867
+ * context on each side or the whole context where the value's edge cut
868
+ * it, and an empty quote a point where the two meet. Never a nearest match. Returns the `Reattached`
869
+ * receipt.
870
+ *
871
+ * A path naming no body or content value orphans its selector, and
872
+ * nothing remaps a card index. An id live anywhere in the document, a
873
+ * table cell's included, is answered as reattached and not written. Each
874
+ * id is answered once, by its first selector. `deltas` maps a path to the
875
+ * `delta` of the revise that dropped its anchors, and a match there must
876
+ * overlap text that revise inserted.
877
+ *
878
+ * Throws on a selector or delta that is not one, or a `path` that does
879
+ * not parse.
880
+ */
881
+ reattach(selectors: AnchorContext[], deltas?: Record<string, Delta>): Reattached;
795
882
  removeCard(index: number): Card | undefined;
796
883
  /**
797
884
  * Remove the `$ext` map on the card `addr` targets entirely, returning the
@@ -811,19 +898,20 @@ export class Document {
811
898
  */
812
899
  removeSeedOverlay(card_kind: string): any;
813
900
  /**
814
- * **Revise** the richtext value at `addr` from a markdown string: the
815
- * default write path. Imports the markdown, diffs it against the current
816
- * value, rebases surviving identity anchors, and returns the `Revised`
817
- * receipt: the text `delta` an editor bridge maps its own positions through
818
- * (`mapPos`), and a `parse::dropped_construct` warning per construct the
819
- * import dropped, its `path` the address written. An absent `addr.field`
820
- * targets the body, an absent `addr.card` the main card; an absent field
821
- * cold-imports from empty.
822
- *
823
- * Throws on an out-of-range card, a malformed field name, a present
824
- * non-content field value, or an over-nested markdown input.
901
+ * Write `content` over the value at `addr`, carrying each anchor it does
902
+ * not name where a diff keeps its text and reporting the rest in
903
+ * `droppedAnchors`. Throws as `apply`, and on a `content` that is not a
904
+ * `Content`.
905
+ */
906
+ replace(addr: Addr | string, content: Content, opts?: WriteOptions): Receipt;
907
+ /**
908
+ * Rewrite the value at `addr` as `markdown`: the anchors and islands
909
+ * whose `qm-*` tags it keeps (`annotated(addr)` spells them) stay put,
910
+ * and the rest keeps what a word-aligned diff keeps. Answers the
911
+ * `Receipt`, its `warnings` the import's. Throws as `apply`, and on an
912
+ * over-nested markdown input.
825
913
  */
826
- revise(addr: Addr | string, markdown: string): Revised;
914
+ revise(addr: Addr | string, markdown: string, opts?: WriteOptions): Receipt;
827
915
  /**
828
916
  * The main card's `$seed[kind]` overlay object, or `undefined`. Feeds
829
917
  * `quill.seedCard(kind, overlay)` without serializing the whole main card,
@@ -856,18 +944,10 @@ export class Document {
856
944
  * write is [`commitField`](Document::commit_field). A bare string is `Addr`
857
945
  * shorthand for `{ field }`; `{ card: 2, field: "qty" }` targets a
858
946
  * composable card. A body address throws:
859
- * write a body with `revise` / `overwrite`. Throws on an out-of-range card
947
+ * write a body with `writer.reviseBody` / `replace`. Throws on an out-of-range card
860
948
  * or a malformed name.
861
949
  */
862
950
  storeField(addr: Addr | string, value: any): void;
863
- /**
864
- * Store several fields verbatim and atomically on the card `addr` targets.
865
- * `addr` is a **card address** (`{ card }`, absent = main) and comes first
866
- * because `card` is itself a legal field name; a present `field` throws.
867
- * Nothing is applied on error, and the thrown error's `diagnostics` carry
868
- * one entry per offending field. Throws on an out-of-range card.
869
- */
870
- storeFields(addr: CardAddr, fields: Record<string, unknown>): void;
871
951
  /**
872
952
  * Merge a card-kind's seed `overlay` into the **main** card's `$seed` map
873
953
  * under `cardKind`, preserving sibling kinds; `$seed` is main-only, so this
@@ -877,10 +957,11 @@ export class Document {
877
957
  storeSeedOverlay(card_kind: string, overlay: any): void;
878
958
  /**
879
959
  * `toMarkdown` with each prose anchor of every body and content field
880
- * spelled read-only at its start where its line can hold the tag, and
881
- * listed with its field's path and line either way. An import drops every
882
- * tag, so `writer.reviseDocument` with the markdown keeps the anchors it keeps
883
- * with `toMarkdown`'s.
960
+ * spelled at its start where its line can hold the tag, every anchor,
961
+ * a cell's too, listed with its field's path and line, and each island whose markdown
962
+ * does not read back spelled as its token. `writer.reviseDocument` reads
963
+ * both back: a kept tag pins its anchor, and a token keeps its island as
964
+ * stored.
884
965
  */
885
966
  toAnnotatedMarkdown(): AnnotatedMarkdown;
886
967
  /**
@@ -911,7 +992,8 @@ export class Document {
911
992
  * The non-fatal diagnostics of the load that produced this document: parse
912
993
  * warnings, plus `conform::*` warnings when it came through `quill.parse`.
913
994
  * Session state, not document value: `equals` and the storage DTO exclude
914
- * it, and `fromStored` / `loadStored` / `writer.reviseDocument` clear it.
995
+ * it. `fromStored` sets it to the stored value's anchor id
996
+ * collisions, and `writer.reviseDocument` clears it.
915
997
  */
916
998
  readonly warnings: Diagnostic[];
917
999
  }
@@ -936,19 +1018,28 @@ export class Quill {
936
1018
  * The empty document: a main card carrying this quill's `$quill` and
937
1019
  * `$kind: main`, no field committed, no composable card, no body. The
938
1020
  * type-minimal valid input, and the document a plate is bound to render
939
- * (the quill authoring contract). `new Document(ref)` is the same document
1021
+ * (the plate contract). `new Document(ref)` is the same document
940
1022
  * reached by spelling the reference.
941
1023
  */
942
1024
  emptyDocument(): Document;
943
1025
  /**
944
- * The quill's example document, `undefined` when the quill has no
945
- * `example.md` at its root: a filled-in page whose values are made up to show the
946
- * quill in use, never kept. It comes back pinned to this quill's
947
- * `name@MAJOR.MINOR` and conformed as `parse` conforms, its parse and
948
- * `conform::*` warnings on `doc.warnings`. Throws when the file does not
949
- * parse or its `$quill` is not this quill's bare name.
1026
+ * The quill's example document, `undefined` when no slot or body declares
1027
+ * an example: the seeded document with every declared `example` committed
1028
+ * as its cell's value and each kind's `body.example` as its body. A
1029
+ * filled-in page whose values are made up to show the quill in use, never
1030
+ * kept.
950
1031
  */
951
1032
  exampleDocument(): Document | undefined;
1033
+ /**
1034
+ * A document from an authoring document, conformed against this quill:
1035
+ * the JSON twin of `parse`. A `null` is absent; the document carries this
1036
+ * quill's `$quill`. A value the schema refuses lands with a `conform::*`
1037
+ * warning on `doc.warnings`, and `validate` reports the rest at document
1038
+ * paths. Throws, with every error found, on a value that is not the
1039
+ * authoring document's shape (`authoring::invalid_shape`) or a name or
1040
+ * kind no card can store (`edit::*`).
1041
+ */
1042
+ fromAuthoring(value: AuthoringDocument): Document;
952
1043
  /**
953
1044
  * Read a `.quill` bundle, the zip archive holding one quill's file tree.
954
1045
  * Pure, as [`fromTree`](Self::from_tree) is. A bundle outside the format
@@ -993,10 +1084,12 @@ export class Quill {
993
1084
  */
994
1085
  seedDocument(): Document;
995
1086
  /**
996
- * Seed a starter main `Card` (carries `$quill`) from the schema: the
997
- * `$kind: main` card of [`seedDocument`](Self::seed_document) alone.
1087
+ * `doc` as an authoring document: every field in the values form (each
1088
+ * content leaf its codec's text), every body as Markdown, each card
1089
+ * `{kind, fields, body}`. Sparse: an absent or `null` field stays absent. A
1090
+ * content leaf that decodes under neither encoding exports as stored.
998
1091
  */
999
- seedMain(): Card;
1092
+ toAuthoring(doc: Document): AuthoringDocument;
1000
1093
  /**
1001
1094
  * This quill's file tree as a `.quill` bundle, the bytes
1002
1095
  * [`fromBundle`](Self::from_bundle) reads.
@@ -1019,12 +1112,18 @@ export class Quill {
1019
1112
  * backend's `backend::declined_construct`.
1020
1113
  */
1021
1114
  validate(doc: Document): Diagnostic[];
1115
+ /**
1116
+ * JSON Schema (draft 2020-12) of the quill's authoring document: the
1117
+ * shape `fromAuthoring` takes and `toAuthoring` returns, every content
1118
+ * leaf a Markdown or literal string, each card kind under `$defs`
1119
+ * selected by `kind`.
1120
+ */
1121
+ readonly authoringSchema: Record<string, unknown>;
1022
1122
  /**
1023
1123
  * The *declared* backend identifier (e.g. `"typst"`): intent, not a
1024
1124
  * resolved capability. Capability is read from the engine.
1025
1125
  */
1026
1126
  readonly backendId: string;
1027
- readonly blueprint: string;
1028
1127
  /**
1029
1128
  * Identity snapshot of the `quill:` section of `Quill.yaml`. Pure config:
1030
1129
  * output formats are a resolved-backend capability read from
@@ -1045,6 +1144,20 @@ export class Quill {
1045
1144
  readonly warnings: Diagnostic[];
1046
1145
  }
1047
1146
 
1147
+ /**
1148
+ * Deleting `[start, end)`: the splice, and where the range runs from a
1149
+ * block's start into a later block, the `setBlock` giving the block that
1150
+ * keeps the text the shape of the block the range ends in.
1151
+ */
1152
+ export function deleteRange(content: Content, start: number, end: number): Change;
1153
+
1154
+ /**
1155
+ * A change taking `before` to `after`, word-aligned, for a writer with no
1156
+ * steps of its own. Throws `edit::content_apply` where `after` is not a
1157
+ * valid value.
1158
+ */
1159
+ export function diff(before: Content, after: Content): Change;
1160
+
1048
1161
  /**
1049
1162
  * Export canonical `Content` to its markdown projection. Throws if `rt` is not
1050
1163
  * canonical content.
@@ -1072,14 +1185,35 @@ export function formatDiagnostic(diagnostic: Diagnostic): string;
1072
1185
  export function formatDocPath(segs: DocPathSeg[]): string;
1073
1186
 
1074
1187
  /**
1075
- * Import a markdown string: the pure, document-free codec. Returns the
1188
+ * Import a markdown string: the markdown codec's import, pure and document-free. Returns the
1076
1189
  * canonical `content` and a `parse::dropped_construct` warning per construct
1077
- * the import dropped, with no `path`. `overwrite(addr, importMarkdown(md).content)`
1078
- * spells the cold, anchor-losing write; prefer `revise` for edit semantics.
1190
+ * the import dropped, with no `path`. `replace(addr, importMarkdown(md).content)`
1191
+ * spells the cold write; prefer `revise` for edit semantics.
1079
1192
  * Throws on an over-nested input.
1080
1193
  */
1081
1194
  export function importMarkdown(markdown: string): Imported;
1082
1195
 
1196
+ /**
1197
+ * Tab over the blocks `first` to `last` (`first` alone when absent): the
1198
+ * change nesting their items under the item before them. Throws
1199
+ * `edit::content_apply` where `first` is in no item, the range leaves its
1200
+ * list, or its first item is the list's first.
1201
+ */
1202
+ export function indent(content: Content, first: string, last?: string | null): Change;
1203
+
1204
+ /**
1205
+ * Typing at `at`: the formatting the typed text carries, for a splice's
1206
+ * `marks`, in canonical order. Throws `edit::content_apply` where `at` is
1207
+ * past the text.
1208
+ */
1209
+ export function inheritedMarks(content: Content, at: number): ContentMarkKind[];
1210
+
1211
+ /**
1212
+ * The change that undoes `change` on `before`. Throws `edit::content_apply`
1213
+ * where `change` does not apply to `before`.
1214
+ */
1215
+ export function invert(change: Change, before: Content): Change;
1216
+
1083
1217
  /**
1084
1218
  * Whether `content` satisfies the one-line constraint of `text` and
1085
1219
  * `richtext`: one `Para` line, in no container, with no islands. Judged on
@@ -1097,32 +1231,35 @@ export function isInline(content: Content): boolean;
1097
1231
  export function isPlain(content: Content): boolean;
1098
1232
 
1099
1233
  /**
1100
- * Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
1101
- * `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
1102
- * `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
1103
- * The document-free read an editor diffs against to decide which `markOps` to
1104
- * emit, rather than reproducing that rule in its own language.
1105
- *
1106
- * `bundle.markOps` are ignored. The answer is normalized, as the store's is:
1107
- * marks a text move drops (out of range, zero-width formatting) are absent,
1108
- * and same-kind runs a move left adjacent arrive already unioned, so a bundle
1109
- * carrying no `markOps` names the marks the field will hold.
1110
- * Throws on a non-content `content`, a malformed bundle, or an op that applies
1111
- * out of bounds: `applyChange`'s errors on the same ops.
1234
+ * Backspace at the start of block `id`: the change joining it onto the block
1235
+ * before it, under that block's record. Throws `edit::content_apply` for the
1236
+ * first block or an id no block holds.
1112
1237
  */
1113
- export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
1238
+ export function joinBlocks(content: Content, id: string): Change;
1114
1239
 
1115
1240
  /**
1116
- * Map a base content position (a USV index into `Content.text`, not a UTF-16
1117
- * offset) through a `delta` to its new position, holding a caret stable across
1118
- * a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
1119
- * moves past it). Throws on a malformed `delta`.
1120
- *
1121
- * This maps a position the *caller* holds. For the marks already in a field,
1122
- * `mapMarks` applies the store's own assoc rule across every channel of a
1123
- * `ChangeBundle`.
1241
+ * Where `pos` lands after `change`, and what around it the change deleted;
1242
+ * `assoc` (`"after"` by default) picks the side of text inserted at it.
1243
+ * Throws `edit::content_apply` where a splice deletes an island and names
1244
+ * none in `removed`, as the receipt's `applied` always does.
1245
+ */
1246
+ export function mapPos(change: Change, pos: Pos, assoc?: "before" | "after"): MapResult;
1247
+
1248
+ /**
1249
+ * The canonical form of `rt`: the content `replace` stores from it, read
1250
+ * without a document. Normalizing rewrites structure, never the text, so the
1251
+ * answer's `text` is `rt.text`. Throws where `replace` would, with the same
1252
+ * error under this verb's name.
1253
+ */
1254
+ export function normalize(rt: Content): Content;
1255
+
1256
+ /**
1257
+ * Shift-Tab over the blocks `first` to `last` (`first` alone when absent):
1258
+ * the change lifting them out of the innermost item or quote holding
1259
+ * `first`. Throws `edit::content_apply` where `first` is in neither or the
1260
+ * range leaves it.
1124
1261
  */
1125
- export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1262
+ export function outdent(content: Content, first: string, last?: string | null): Change;
1126
1263
 
1127
1264
  /**
1128
1265
  * Parse a canonical document-model `Diagnostic.path` (`cards.<kind>[<i>].<field>`,
@@ -1133,13 +1270,11 @@ export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1133
1270
  export function parseDocPath(path: string): DocPathSeg[];
1134
1271
 
1135
1272
  /**
1136
- * Rebase `markdown` onto a `base` content: the document-free twin of `revise`,
1137
- * returning the new `content`, the text `delta` (offsets are USV indices into
1138
- * `Content.text`, surviving anchors rebased), and the import's
1139
- * `parse::dropped_construct` warnings, with no `path`. Throws on an
1140
- * over-nested markdown input or a non-content `base`.
1273
+ * Enter at `at`: the change splitting the block holding `at` there, the new
1274
+ * block `newId` and the text keeping its id. Throws `edit::content_apply`
1275
+ * where `at` is past the text.
1141
1276
  */
1142
- export function rebase(base: Content, markdown: string): { content: Content; delta: Delta; warnings: Diagnostic[] };
1277
+ export function splitBlock(content: Content, at: number, newId: string): Change;
1143
1278
 
1144
1279
  /**
1145
1280
  * Runs at instantiation, so a Rust panic reaches the console as a stack trace
@@ -1148,21 +1283,37 @@ export function rebase(base: Content, markdown: string): { content: Content; del
1148
1283
  */
1149
1284
  export function start(): void;
1150
1285
 
1286
+ /**
1287
+ * The change wrapping the blocks `first` to `last` in a quote or a list,
1288
+ * around the children of their innermost shared item or quote that the range
1289
+ * touches; a list the range sits in is wrapped whole.
1290
+ */
1291
+ export function wrap(content: Content, first: string, last: string, kind: WrapKind): Change;
1292
+
1151
1293
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
1152
1294
 
1153
1295
  export interface InitOutput {
1154
1296
  readonly memory: WebAssembly.Memory;
1155
1297
  readonly __wbg_document_free: (a: number, b: number) => void;
1156
1298
  readonly __wbg_quill_free: (a: number, b: number) => void;
1299
+ readonly deleteRange: (a: number, b: number, c: number, d: number) => void;
1300
+ readonly diff: (a: number, b: number, c: number) => void;
1157
1301
  readonly document__addCard: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
1158
1302
  readonly document__commitField: (a: number, b: number, c: number, d: number, e: number) => void;
1159
1303
  readonly document__commitFields: (a: number, b: number, c: number, d: number, e: number) => void;
1304
+ readonly document__hash: (a: number, b: number, c: number) => void;
1305
+ readonly document__hashAt: (a: number, b: number, c: number, d: number) => void;
1160
1306
  readonly document__readerGet: (a: number, b: number, c: number, d: number) => void;
1161
1307
  readonly document__readerGetContent: (a: number, b: number, c: number, d: number) => void;
1162
1308
  readonly document__readerGetContentAt: (a: number, b: number, c: number, d: number, e: number) => void;
1163
- readonly document__reviseDocument: (a: number, b: number, c: number, d: number, e: number) => void;
1164
- readonly document__reviseField: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1165
- readonly document_applyChange: (a: number, b: number, c: number, d: number) => void;
1309
+ readonly document__revise: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1310
+ readonly document__reviseDocument: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1311
+ readonly document__reviseField: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
1312
+ readonly document_anchorContext: (a: number, b: number, c: number, d: number) => void;
1313
+ readonly document_anchorContexts: (a: number, b: number) => void;
1314
+ readonly document_annotated: (a: number, b: number, c: number) => void;
1315
+ readonly document_annotatedAt: (a: number, b: number, c: number) => void;
1316
+ readonly document_apply: (a: number, b: number, c: number, d: number, e: number) => void;
1166
1317
  readonly document_bodyMarkdown: (a: number, b: number, c: number) => void;
1167
1318
  readonly document_card: (a: number, b: number, c: number) => void;
1168
1319
  readonly document_cardCount: (a: number) => number;
@@ -1175,27 +1326,28 @@ export interface InitOutput {
1175
1326
  readonly document_fromStored: (a: number, b: number, c: number) => void;
1176
1327
  readonly document_getExt: (a: number, b: number, c: number) => void;
1177
1328
  readonly document_getStored: (a: number, b: number, c: number) => void;
1329
+ readonly document_hash: (a: number, b: number, c: number) => void;
1330
+ readonly document_hashAt: (a: number, b: number, c: number) => void;
1178
1331
  readonly document_insertCard: (a: number, b: number, c: number, d: number) => void;
1179
- readonly document_loadStored: (a: number, b: number, c: number, d: number) => void;
1180
1332
  readonly document_main: (a: number, b: number) => void;
1181
- readonly document_markdownRules: (a: number) => void;
1182
1333
  readonly document_moveCard: (a: number, b: number, c: number, d: number) => void;
1183
1334
  readonly document_new: (a: number, b: number, c: number) => void;
1184
- readonly document_overwrite: (a: number, b: number, c: number, d: number) => void;
1185
1335
  readonly document_pathFor: (a: number, b: number, c: number) => void;
1186
1336
  readonly document_quillRef: (a: number, b: number) => void;
1187
1337
  readonly document_quillRefHint: (a: number) => void;
1338
+ readonly document_read: (a: number, b: number, c: number) => void;
1339
+ readonly document_reattach: (a: number, b: number, c: number, d: number) => void;
1188
1340
  readonly document_removeCard: (a: number, b: number, c: number) => void;
1189
1341
  readonly document_removeExt: (a: number, b: number, c: number) => void;
1190
1342
  readonly document_removeField: (a: number, b: number, c: number) => void;
1191
1343
  readonly document_removeSeedOverlay: (a: number, b: number, c: number, d: number) => void;
1192
- readonly document_revise: (a: number, b: number, c: number, d: number, e: number) => void;
1344
+ readonly document_replace: (a: number, b: number, c: number, d: number, e: number) => void;
1345
+ readonly document_revise: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1193
1346
  readonly document_seedOverlay: (a: number, b: number, c: number, d: number) => void;
1194
1347
  readonly document_setQuillRef: (a: number, b: number, c: number, d: number) => void;
1195
1348
  readonly document_storageVersionOf: (a: number, b: number, c: number) => void;
1196
1349
  readonly document_storeExt: (a: number, b: number, c: number, d: number) => void;
1197
1350
  readonly document_storeField: (a: number, b: number, c: number, d: number) => void;
1198
- readonly document_storeFields: (a: number, b: number, c: number, d: number) => void;
1199
1351
  readonly document_storeSeedOverlay: (a: number, b: number, c: number, d: number, e: number) => void;
1200
1352
  readonly document_toAnnotatedMarkdown: (a: number, b: number) => void;
1201
1353
  readonly document_toMarkdown: (a: number, b: number) => void;
@@ -1205,17 +1357,23 @@ export interface InitOutput {
1205
1357
  readonly formatDiagnostic: (a: number, b: number) => void;
1206
1358
  readonly formatDocPath: (a: number, b: number) => void;
1207
1359
  readonly importMarkdown: (a: number, b: number, c: number) => void;
1360
+ readonly indent: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1361
+ readonly inheritedMarks: (a: number, b: number, c: number) => void;
1362
+ readonly invert: (a: number, b: number, c: number) => void;
1208
1363
  readonly isInline: (a: number, b: number) => void;
1209
1364
  readonly isPlain: (a: number, b: number) => void;
1210
- readonly mapMarks: (a: number, b: number, c: number) => void;
1365
+ readonly joinBlocks: (a: number, b: number, c: number, d: number) => void;
1211
1366
  readonly mapPos: (a: number, b: number, c: number, d: number) => void;
1367
+ readonly normalize: (a: number, b: number) => void;
1368
+ readonly outdent: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1212
1369
  readonly parseDocPath: (a: number, b: number, c: number) => void;
1213
1370
  readonly quill__resolve: (a: number, b: number, c: number, d: number, e: number) => void;
1371
+ readonly quill_authoringSchema: (a: number, b: number) => void;
1214
1372
  readonly quill_backendId: (a: number, b: number) => void;
1215
- readonly quill_blueprint: (a: number, b: number) => void;
1216
1373
  readonly quill_conform: (a: number, b: number, c: number) => void;
1217
1374
  readonly quill_emptyDocument: (a: number) => number;
1218
- readonly quill_exampleDocument: (a: number, b: number) => void;
1375
+ readonly quill_exampleDocument: (a: number) => number;
1376
+ readonly quill_fromAuthoring: (a: number, b: number, c: number) => void;
1219
1377
  readonly quill_fromBundle: (a: number, b: number, c: number) => void;
1220
1378
  readonly quill_fromTree: (a: number, b: number) => void;
1221
1379
  readonly quill_metadata: (a: number, b: number) => void;
@@ -1223,13 +1381,14 @@ export interface InitOutput {
1223
1381
  readonly quill_schema: (a: number, b: number) => void;
1224
1382
  readonly quill_seedCard: (a: number, b: number, c: number, d: number, e: number) => void;
1225
1383
  readonly quill_seedDocument: (a: number) => number;
1226
- readonly quill_seedMain: (a: number, b: number) => void;
1384
+ readonly quill_toAuthoring: (a: number, b: number, c: number) => void;
1227
1385
  readonly quill_toBundle: (a: number, b: number) => void;
1228
1386
  readonly quill_toTree: (a: number) => number;
1229
1387
  readonly quill_validate: (a: number, b: number, c: number) => void;
1230
1388
  readonly quill_warnings: (a: number, b: number) => void;
1231
- readonly rebase: (a: number, b: number, c: number, d: number) => void;
1389
+ readonly splitBlock: (a: number, b: number, c: number, d: number, e: number) => void;
1232
1390
  readonly start: () => void;
1391
+ readonly wrap: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
1233
1392
  readonly __wbindgen_export: (a: number, b: number) => number;
1234
1393
  readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
1235
1394
  readonly __wbindgen_export3: (a: number) => void;