@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/render/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.
336
- *
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.
321
+ * One step, an object of one key naming it.
345
322
  *
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.
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.
349
336
  *
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.
367
+ */
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`).
386
404
  */
387
- export interface ChangeBundle {
388
- delta?: Delta;
389
- islandOps?: IslandOp[];
390
- lineOps?: LineOp[];
391
- markOps?: MarkOp[];
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
  /**
@@ -524,6 +548,11 @@ export interface QuillCardUi {
524
548
  export interface QuillCardBody {
525
549
  /** When false, consumers must not accept or store body content for this card kind. Defaults to true. */
526
550
  enabled?: boolean;
551
+ /** What the body holds, for the writer. Never beside `enabled: false`. */
552
+ description?: string;
553
+ /** A made-up body in markdown, which `exampleDocument()` commits. Never
554
+ * beside `enabled: false`. */
555
+ example?: string;
527
556
  }
528
557
 
529
558
  export type QuillFieldType = "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "text" | "richtext" | "enum" | "matrix";
@@ -531,7 +560,8 @@ export type QuillFieldType = "number" | "integer" | "boolean" | "array" | "objec
531
560
  /** Schema entry for a single field declared in a quill's `Quill.yaml`.
532
561
  *
533
562
  * `default` is the value an unanswered cell renders. An absent field without a
534
- * `default` blank-fills.
563
+ * `default` blank-fills. `example` is a made-up value of the same type, which
564
+ * answers no cell: `exampleDocument()` commits it.
535
565
  */
536
566
  export interface QuillFieldSchema {
537
567
  /** A trailing `?` marks the cell optional: unanswered, it renders `none`
@@ -543,6 +573,7 @@ export interface QuillFieldSchema {
543
573
  title?: string;
544
574
  description?: string;
545
575
  default?: unknown;
576
+ example?: unknown;
546
577
  /** The closed set of allowed values. Required on `type: "enum"`, and valid
547
578
  * nowhere else. */
548
579
  values?: string[];
@@ -607,6 +638,26 @@ export interface QuillSchema {
607
638
  card_kinds?: Record<string, QuillCardSchema>;
608
639
  }
609
640
 
641
+ /**
642
+ * One card of an `AuthoringDocument`: its kind, its fields in the values form,
643
+ * and its body as Markdown. `null` anywhere a value goes is absent.
644
+ */
645
+ export interface AuthoringCard {
646
+ kind: string;
647
+ fields?: Record<string, unknown> | null;
648
+ body?: string | null;
649
+ }
650
+
651
+ /**
652
+ * The JSON a model reads and writes, described by `Quill.authoringSchema`:
653
+ * the main card's fields and body, and the composable cards in order.
654
+ */
655
+ export interface AuthoringDocument {
656
+ fields?: Record<string, unknown> | null;
657
+ body?: string | null;
658
+ cards?: AuthoringCard[] | null;
659
+ }
660
+
610
661
  /**
611
662
  * Identity snapshot mirroring the `quill:` section of `Quill.yaml`. The schema
612
663
  * lives on `Quill.schema`; output formats are a resolved-backend capability read
@@ -626,8 +677,8 @@ export interface QuillMetadata {
626
677
 
627
678
 
628
679
  /**
629
- * A resolved point → content position: the field a click landed in and the USV
630
- * offset into its `Content`. The `LiveSession.positionAt` result, inverse of
680
+ * A resolved point → content position: the field a click landed in and the
681
+ * UTF-16 offset into its `Content`. The `LiveSession.positionAt` result, inverse of
631
682
  * `locate`.
632
683
  */
633
684
  export interface ContentHit {
@@ -636,7 +687,7 @@ export interface ContentHit {
636
687
  */
637
688
  field: string;
638
689
  /**
639
- * USV offset into the field's `Content`.
690
+ * UTF-16 offset into the field's `Content`.
640
691
  */
641
692
  pos: number;
642
693
  /**
@@ -666,8 +717,8 @@ export interface FieldRegion {
666
717
  */
667
718
  rect: [number, number, number, number];
668
719
  /**
669
- * The slice this box covers: USV `[start, end)` into the field's `Content`
670
- * for one content segment, `undefined` for a scalar's ink, a claim or a
720
+ * The slice this box covers: UTF-16 `[start, end)` into the field's
721
+ * `Content` for one content segment, `undefined` for a scalar's ink, a claim or a
671
722
  * widget.
672
723
  */
673
724
  span?: [number, number];
@@ -791,22 +842,40 @@ export class Document {
791
842
  free(): void;
792
843
  [Symbol.dispose](): void;
793
844
  /**
794
- * **Apply** a committed content edit `bundle` at `addr`, the editor splice:
795
- * text delta first, then island ops, then line ops, then mark ops (mark
796
- * ranges in final-text coordinates), all-or-nothing. An absent `addr.field`
797
- * targets the body, an absent `addr.card` the main card. The island channel
798
- * moves an island alone, so anchors elsewhere in the field survive an edit
799
- * `overwrite` would clear.
800
- *
801
- * Throws on an out-of-range card, a field that is not richtext, a malformed
802
- * bundle, or an op that applies out of bounds; the value is unchanged on a
803
- * failed apply.
804
- *
805
- * Each text-moving channel rebases the marks already in the field, by the
806
- * rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
807
- * building `markOps` need not predict it.
845
+ * The context of the anchor `id`, or `undefined` where no body or
846
+ * content value holds one.
847
+ */
848
+ anchorContext(id: string): AnchorContext | undefined;
849
+ /**
850
+ * Every anchor of every body and content value with its context, a table
851
+ * cell's or a note's included, in the order `toAnnotatedMarkdown` lists
852
+ * anchors: the selector a host stores beside a comment thread and hands
853
+ * back to `reattach`.
854
+ */
855
+ anchorContexts(): AnchorContext[];
856
+ /**
857
+ * The annotated read of the body or richtext field at `addr` alone: the
858
+ * scoped twin of `toAnnotatedMarkdown`, what a revise at `addr` reads
859
+ * back and the `baseMarkdown` it merges against. Each anchor names `addr`'s
860
+ * path. An absent field reads as the empty content. Throws as `hashAt`.
861
+ */
862
+ annotatedAt(addr: Addr | string): AnnotatedMarkdown;
863
+ /**
864
+ * The markdown at `addr` with a `qm-*` tag for each anchor and island id
865
+ * its lines can hold: what `revise` reads back. Throws as `read`.
866
+ */
867
+ annotated(addr: Addr | string): string;
868
+ /**
869
+ * **Apply** `change` to the value at `addr`, all its steps or none, and
870
+ * answer the `Receipt`. `opts.base` is the hash the writer read
871
+ * (`hash(addr)`), and the change's offsets are into the value it names:
872
+ * one the value has moved on from rebases the change over the change
873
+ * since, throwing `edit::rebase_conflict` where they touch the same text or
874
+ * record and `edit::stale_base` where the document no longer holds the change
875
+ * since. A refused change throws `edit::content_apply` and writes
876
+ * nothing.
808
877
  */
809
- applyChange(addr: Addr | string, bundle: ChangeBundle): void;
878
+ apply(addr: Addr | string, change: Change, opts?: WriteOptions): Receipt;
810
879
  /**
811
880
  * The **body** markdown projection: an on-demand, lossy export (content-only
812
881
  * marks do not survive markdown). A body's type is a format fact, not a
@@ -833,11 +902,10 @@ export class Document {
833
902
  clone(): Document;
834
903
  /**
835
904
  * The newest storage version this build writes via
836
- * [`toStored`](Document::to_stored): a document spelling a font mark, a
837
- * `table` element, a merged table cell or a table head 0.130 cannot draw
838
- * carries it, every other the tag before it, which a 0.130 build reads.
839
- * The tag advances only when the wire format changes, not on every
840
- * release.
905
+ * [`toStored`](Document::to_stored): a document holding a composable
906
+ * card, each carrying its `$id`, carries it. A document of the main card
907
+ * alone carries the oldest tag that holds its content. The tag advances
908
+ * only when the wire format changes, not on every release.
841
909
  */
842
910
  static currentStorageVersion(): string;
843
911
  /**
@@ -850,7 +918,7 @@ export class Document {
850
918
  static fromMarkdown(markdown: string): Document;
851
919
  /**
852
920
  * Reconstruct a `Document` from a versioned storage DTO string produced by
853
- * [`toStored`](Document::to_stored). The result carries no parse-time warnings.
921
+ * [`toStored`](Document::to_stored), with no `warnings`.
854
922
  * Throws if `json` is not a valid storage DTO (malformed JSON, unknown
855
923
  * `schema`, missing fields, or unparseable quill reference).
856
924
  */
@@ -882,6 +950,23 @@ export class Document {
882
950
  * unread, which is the contract and why it is `unknown`.
883
951
  */
884
952
  getStored(addr: Addr | string): unknown;
953
+ /**
954
+ * The hash of the body or richtext field at `addr`, the `baseHash` a
955
+ * revise at `addr` checks: the hash of its content, an absent field the
956
+ * empty content. An absent `addr.field` reads the body, an absent
957
+ * `addr.card` the main card. Richtext only; the
958
+ * typed `writer.hashAt` reads a field of any type through its schema.
959
+ * Throws on an out-of-range card, a malformed field name, or a present
960
+ * non-content field value.
961
+ */
962
+ hashAt(addr: Addr | string): string;
963
+ /**
964
+ * With no `addr`, the hash of the document's stored form, the quill-free
965
+ * twin of `writer.hash()`: equal documents hash alike, whatever order a
966
+ * store kept their keys in. With one, the hash of the `Content` at
967
+ * `addr`, the `base` a write to it names. Throws as `read`.
968
+ */
969
+ hash(addr?: Addr | string): string;
885
970
  /**
886
971
  * Insert a card: `at` absent appends, a number inserts at that index (in
887
972
  * `0..=cards.length`). Accepts any `CardInput`, including a card read back
@@ -890,19 +975,6 @@ export class Document {
890
975
  * 1,000 cards.
891
976
  */
892
977
  insertCard(card: CardInput, at?: number): void;
893
- /**
894
- * Replace this document's contents **in place** from a versioned storage DTO
895
- * string: the mutating twin of [`fromStored`](Document::from_stored).
896
- * Parse-time `warnings` are cleared. Throws on an invalid DTO, leaving the
897
- * document unchanged. A caller holding the document need not re-bind its
898
- * variable.
899
- */
900
- loadStored(json: string): void;
901
- /**
902
- * The Quillmark Markdown rules, re-exposed from core. Constant across
903
- * calls; read once and cache.
904
- */
905
- static markdownRules(): string;
906
978
  /**
907
979
  * Move the card at `from` to position `to`. `from == to` is a no-op.
908
980
  */
@@ -915,17 +987,6 @@ export class Document {
915
987
  * an invalid quill reference.
916
988
  */
917
989
  constructor(quill_ref: string);
918
- /**
919
- * **Overwrite** the content value at `addr` with exactly `rt`: value
920
- * semantics, so the identity anchors of any previous value are gone. By
921
- * anchor fate `overwrite` destroys, [`revise`](Document::revise) rebases,
922
- * and [`applyChange`](Document::apply_change) preserves. An absent
923
- * `addr.field` targets the body, an absent `addr.card` the main card.
924
- *
925
- * Throws on an out-of-range card, a malformed field name, or an `rt` that is
926
- * not a canonical content object.
927
- */
928
- overwrite(addr: Addr | string, rt: Content): void;
929
990
  /**
930
991
  * `addr`'s canonical `DocPath` string, the anchor `Diagnostic.path` carries:
931
992
  * `pathFor()` is `main.body`, `pathFor("intro")` `main.intro`,
@@ -939,7 +1000,7 @@ export class Document {
939
1000
  * **Total on the index axis**, unlike the `Addr` reads, which throw there:
940
1001
  * an out-of-range `{card: 7, field: "from"}` renders `cards[7].from`, which
941
1002
  * parses back and resolves to nothing rather than mis-targeting. Only a
942
- * malformed address throws.
1003
+ * malformed address throws, or a card id no card holds.
943
1004
  */
944
1005
  pathFor(addr: Addr | string): string;
945
1006
  /**
@@ -948,6 +1009,32 @@ export class Document {
948
1009
  * messages from this instead of re-stating the rule.
949
1010
  */
950
1011
  static quillRefHint(): string;
1012
+ /**
1013
+ * The `Content` at `addr`: an absent `addr.field` reads the body, an
1014
+ * absent `addr.card` the main card, and an absent field the empty
1015
+ * content. Throws on an out-of-range card, a malformed field name, or a
1016
+ * present field value that is not content.
1017
+ */
1018
+ read(addr: Addr | string): Content;
1019
+ /**
1020
+ * Re-add anchors a write dropped, each at the `path` its selector names,
1021
+ * where its `quote` is found exactly once with its context agreeing: four
1022
+ * scalar values of quote or more, or a table's slot alone, eight of
1023
+ * context on each side or the whole context where the value's edge cut
1024
+ * it, and an empty quote a point where the two meet. Never a nearest match. Returns the `Reattached`
1025
+ * receipt.
1026
+ *
1027
+ * A path naming no body or content value orphans its selector, and
1028
+ * nothing remaps a card index. An id live anywhere in the document, a
1029
+ * table cell's included, is answered as reattached and not written. Each
1030
+ * id is answered once, by its first selector. `deltas` maps a path to the
1031
+ * `delta` of the revise that dropped its anchors, and a match there must
1032
+ * overlap text that revise inserted.
1033
+ *
1034
+ * Throws on a selector or delta that is not one, or a `path` that does
1035
+ * not parse.
1036
+ */
1037
+ reattach(selectors: AnchorContext[], deltas?: Record<string, Delta>): Reattached;
951
1038
  removeCard(index: number): Card | undefined;
952
1039
  /**
953
1040
  * Remove the `$ext` map on the card `addr` targets entirely, returning the
@@ -967,19 +1054,20 @@ export class Document {
967
1054
  */
968
1055
  removeSeedOverlay(card_kind: string): any;
969
1056
  /**
970
- * **Revise** the richtext value at `addr` from a markdown string: the
971
- * default write path. Imports the markdown, diffs it against the current
972
- * value, rebases surviving identity anchors, and returns the `Revised`
973
- * receipt: the text `delta` an editor bridge maps its own positions through
974
- * (`mapPos`), and a `parse::dropped_construct` warning per construct the
975
- * import dropped, its `path` the address written. An absent `addr.field`
976
- * targets the body, an absent `addr.card` the main card; an absent field
977
- * cold-imports from empty.
978
- *
979
- * Throws on an out-of-range card, a malformed field name, a present
980
- * non-content field value, or an over-nested markdown input.
1057
+ * Write `content` over the value at `addr`, carrying each anchor it does
1058
+ * not name where a diff keeps its text and reporting the rest in
1059
+ * `droppedAnchors`. Throws as `apply`, and on a `content` that is not a
1060
+ * `Content`.
1061
+ */
1062
+ replace(addr: Addr | string, content: Content, opts?: WriteOptions): Receipt;
1063
+ /**
1064
+ * Rewrite the value at `addr` as `markdown`: the anchors and islands
1065
+ * whose `qm-*` tags it keeps (`annotated(addr)` spells them) stay put,
1066
+ * and the rest keeps what a word-aligned diff keeps. Answers the
1067
+ * `Receipt`, its `warnings` the import's. Throws as `apply`, and on an
1068
+ * over-nested markdown input.
981
1069
  */
982
- revise(addr: Addr | string, markdown: string): Revised;
1070
+ revise(addr: Addr | string, markdown: string, opts?: WriteOptions): Receipt;
983
1071
  /**
984
1072
  * The main card's `$seed[kind]` overlay object, or `undefined`. Feeds
985
1073
  * `quill.seedCard(kind, overlay)` without serializing the whole main card,
@@ -1012,18 +1100,10 @@ export class Document {
1012
1100
  * write is [`commitField`](Document::commit_field). A bare string is `Addr`
1013
1101
  * shorthand for `{ field }`; `{ card: 2, field: "qty" }` targets a
1014
1102
  * composable card. A body address throws:
1015
- * write a body with `revise` / `overwrite`. Throws on an out-of-range card
1103
+ * write a body with `writer.reviseBody` / `replace`. Throws on an out-of-range card
1016
1104
  * or a malformed name.
1017
1105
  */
1018
1106
  storeField(addr: Addr | string, value: any): void;
1019
- /**
1020
- * Store several fields verbatim and atomically on the card `addr` targets.
1021
- * `addr` is a **card address** (`{ card }`, absent = main) and comes first
1022
- * because `card` is itself a legal field name; a present `field` throws.
1023
- * Nothing is applied on error, and the thrown error's `diagnostics` carry
1024
- * one entry per offending field. Throws on an out-of-range card.
1025
- */
1026
- storeFields(addr: CardAddr, fields: Record<string, unknown>): void;
1027
1107
  /**
1028
1108
  * Merge a card-kind's seed `overlay` into the **main** card's `$seed` map
1029
1109
  * under `cardKind`, preserving sibling kinds; `$seed` is main-only, so this
@@ -1033,10 +1113,11 @@ export class Document {
1033
1113
  storeSeedOverlay(card_kind: string, overlay: any): void;
1034
1114
  /**
1035
1115
  * `toMarkdown` with each prose anchor of every body and content field
1036
- * spelled read-only at its start where its line can hold the tag, and
1037
- * listed with its field's path and line either way. An import drops every
1038
- * tag, so `writer.reviseDocument` with the markdown keeps the anchors it keeps
1039
- * with `toMarkdown`'s.
1116
+ * spelled at its start where its line can hold the tag, every anchor,
1117
+ * a cell's too, listed with its field's path and line, and each island whose markdown
1118
+ * does not read back spelled as its token. `writer.reviseDocument` reads
1119
+ * both back: a kept tag pins its anchor, and a token keeps its island as
1120
+ * stored.
1040
1121
  */
1041
1122
  toAnnotatedMarkdown(): AnnotatedMarkdown;
1042
1123
  /**
@@ -1067,7 +1148,8 @@ export class Document {
1067
1148
  * The non-fatal diagnostics of the load that produced this document: parse
1068
1149
  * warnings, plus `conform::*` warnings when it came through `quill.parse`.
1069
1150
  * Session state, not document value: `equals` and the storage DTO exclude
1070
- * it, and `fromStored` / `loadStored` / `writer.reviseDocument` clear it.
1151
+ * it. `fromStored` sets it to the stored value's anchor id
1152
+ * collisions, and `writer.reviseDocument` clears it.
1071
1153
  */
1072
1154
  readonly warnings: Diagnostic[];
1073
1155
  }
@@ -1161,8 +1243,10 @@ export class LiveSession {
1161
1243
  /**
1162
1244
  * A content position → **caret rect**, the reverse of `positionAt`: the box
1163
1245
  * to draw a caret at, in the same bottom-left PDF-point space as
1164
- * `FieldRegion.rect`, its `span` collapsed to `[pos, pos]`. `undefined` when
1165
- * the field places no tracked content or the offset maps to no drawn glyph.
1246
+ * `FieldRegion.rect`, its `span` collapsed to `[pos, pos]`. `pos` is a
1247
+ * UTF-16 offset into the field's `Content`. `undefined` when the field
1248
+ * places no tracked content, the offset falls inside a character, or it
1249
+ * maps to no drawn glyph.
1166
1250
  */
1167
1251
  locate(field: string, pos: number): FieldRegion | undefined;
1168
1252
  /**
@@ -1188,8 +1272,8 @@ export class LiveSession {
1188
1272
  */
1189
1273
  paint(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, page: number, scale: number): void;
1190
1274
  /**
1191
- * A point → **content position**: the field *and* a USV offset into its
1192
- * `Content`, for placing a caret or mapping a selection into the content
1275
+ * A point → **content position**: the field *and* a UTF-16 offset into
1276
+ * its `Content`, for placing a caret or mapping a selection into the content
1193
1277
  * model, or `undefined` off all content ink. `x`/`y`/`tolPt` are PDF
1194
1278
  * points, bottom-left origin, as in `fieldAt`. The offset is cluster-exact
1195
1279
  * and degrades to the containing segment's start on origin-less ink.
@@ -1216,14 +1300,10 @@ export class LiveSession {
1216
1300
  * and the session recovers on the next successful `update`. On success,
1217
1301
  * repaint `dirtyPages ∩ visible`.
1218
1302
  *
1219
- * Distinct from [`applyChange`](Document::apply_change), which splices ops
1220
- * into a document; this recompiles a document the caller already mutated.
1303
+ * Distinct from [`apply`](Document::apply), which edits a document's
1304
+ * content; this recompiles a document the caller already mutated.
1221
1305
  */
1222
1306
  update(doc: Document): ChangeSet;
1223
- /**
1224
- * The backend that produced this session (e.g. `"typst"`).
1225
- */
1226
- readonly backendId: string;
1227
1307
  readonly pageCount: number;
1228
1308
  /**
1229
1309
  * Non-fatal diagnostics of the session's **current compile**, refreshed by
@@ -1253,19 +1333,28 @@ export class Quill {
1253
1333
  * The empty document: a main card carrying this quill's `$quill` and
1254
1334
  * `$kind: main`, no field committed, no composable card, no body. The
1255
1335
  * type-minimal valid input, and the document a plate is bound to render
1256
- * (the quill authoring contract). `new Document(ref)` is the same document
1336
+ * (the plate contract). `new Document(ref)` is the same document
1257
1337
  * reached by spelling the reference.
1258
1338
  */
1259
1339
  emptyDocument(): Document;
1260
1340
  /**
1261
- * The quill's example document, `undefined` when the quill has no
1262
- * `example.md` at its root: a filled-in page whose values are made up to show the
1263
- * quill in use, never kept. It comes back pinned to this quill's
1264
- * `name@MAJOR.MINOR` and conformed as `parse` conforms, its parse and
1265
- * `conform::*` warnings on `doc.warnings`. Throws when the file does not
1266
- * parse or its `$quill` is not this quill's bare name.
1341
+ * The quill's example document, `undefined` when no slot or body declares
1342
+ * an example: the seeded document with every declared `example` committed
1343
+ * as its cell's value and each kind's `body.example` as its body. A
1344
+ * filled-in page whose values are made up to show the quill in use, never
1345
+ * kept.
1267
1346
  */
1268
1347
  exampleDocument(): Document | undefined;
1348
+ /**
1349
+ * A document from an authoring document, conformed against this quill:
1350
+ * the JSON twin of `parse`. A `null` is absent; the document carries this
1351
+ * quill's `$quill`. A value the schema refuses lands with a `conform::*`
1352
+ * warning on `doc.warnings`, and `validate` reports the rest at document
1353
+ * paths. Throws, with every error found, on a value that is not the
1354
+ * authoring document's shape (`authoring::invalid_shape`) or a name or
1355
+ * kind no card can store (`edit::*`).
1356
+ */
1357
+ fromAuthoring(value: AuthoringDocument): Document;
1269
1358
  /**
1270
1359
  * Read a `.quill` bundle, the zip archive holding one quill's file tree.
1271
1360
  * Pure, as [`fromTree`](Self::from_tree) is. A bundle outside the format
@@ -1310,10 +1399,12 @@ export class Quill {
1310
1399
  */
1311
1400
  seedDocument(): Document;
1312
1401
  /**
1313
- * Seed a starter main `Card` (carries `$quill`) from the schema: the
1314
- * `$kind: main` card of [`seedDocument`](Self::seed_document) alone.
1402
+ * `doc` as an authoring document: every field in the values form (each
1403
+ * content leaf its codec's text), every body as Markdown, each card
1404
+ * `{kind, fields, body}`. Sparse: an absent or `null` field stays absent. A
1405
+ * content leaf that decodes under neither encoding exports as stored.
1315
1406
  */
1316
- seedMain(): Card;
1407
+ toAuthoring(doc: Document): AuthoringDocument;
1317
1408
  /**
1318
1409
  * This quill's file tree as a `.quill` bundle, the bytes
1319
1410
  * [`fromBundle`](Self::from_bundle) reads.
@@ -1336,12 +1427,18 @@ export class Quill {
1336
1427
  * backend's `backend::declined_construct`.
1337
1428
  */
1338
1429
  validate(doc: Document): Diagnostic[];
1430
+ /**
1431
+ * JSON Schema (draft 2020-12) of the quill's authoring document: the
1432
+ * shape `fromAuthoring` takes and `toAuthoring` returns, every content
1433
+ * leaf a Markdown or literal string, each card kind under `$defs`
1434
+ * selected by `kind`.
1435
+ */
1436
+ readonly authoringSchema: Record<string, unknown>;
1339
1437
  /**
1340
1438
  * The *declared* backend identifier (e.g. `"typst"`): intent, not a
1341
1439
  * resolved capability. Capability is read from the engine.
1342
1440
  */
1343
1441
  readonly backendId: string;
1344
- readonly blueprint: string;
1345
1442
  /**
1346
1443
  * Identity snapshot of the `quill:` section of `Quill.yaml`. Pure config:
1347
1444
  * output formats are a resolved-backend capability read from
@@ -1362,6 +1459,20 @@ export class Quill {
1362
1459
  readonly warnings: Diagnostic[];
1363
1460
  }
1364
1461
 
1462
+ /**
1463
+ * Deleting `[start, end)`: the splice, and where the range runs from a
1464
+ * block's start into a later block, the `setBlock` giving the block that
1465
+ * keeps the text the shape of the block the range ends in.
1466
+ */
1467
+ export function deleteRange(content: Content, start: number, end: number): Change;
1468
+
1469
+ /**
1470
+ * A change taking `before` to `after`, word-aligned, for a writer with no
1471
+ * steps of its own. Throws `edit::content_apply` where `after` is not a
1472
+ * valid value.
1473
+ */
1474
+ export function diff(before: Content, after: Content): Change;
1475
+
1365
1476
  /**
1366
1477
  * Export canonical `Content` to its markdown projection. Throws if `rt` is not
1367
1478
  * canonical content.
@@ -1389,14 +1500,35 @@ export function formatDiagnostic(diagnostic: Diagnostic): string;
1389
1500
  export function formatDocPath(segs: DocPathSeg[]): string;
1390
1501
 
1391
1502
  /**
1392
- * Import a markdown string: the pure, document-free codec. Returns the
1503
+ * Import a markdown string: the markdown codec's import, pure and document-free. Returns the
1393
1504
  * canonical `content` and a `parse::dropped_construct` warning per construct
1394
- * the import dropped, with no `path`. `overwrite(addr, importMarkdown(md).content)`
1395
- * spells the cold, anchor-losing write; prefer `revise` for edit semantics.
1505
+ * the import dropped, with no `path`. `replace(addr, importMarkdown(md).content)`
1506
+ * spells the cold write; prefer `revise` for edit semantics.
1396
1507
  * Throws on an over-nested input.
1397
1508
  */
1398
1509
  export function importMarkdown(markdown: string): Imported;
1399
1510
 
1511
+ /**
1512
+ * Tab over the blocks `first` to `last` (`first` alone when absent): the
1513
+ * change nesting their items under the item before them. Throws
1514
+ * `edit::content_apply` where `first` is in no item, the range leaves its
1515
+ * list, or its first item is the list's first.
1516
+ */
1517
+ export function indent(content: Content, first: string, last?: string | null): Change;
1518
+
1519
+ /**
1520
+ * Typing at `at`: the formatting the typed text carries, for a splice's
1521
+ * `marks`, in canonical order. Throws `edit::content_apply` where `at` is
1522
+ * past the text.
1523
+ */
1524
+ export function inheritedMarks(content: Content, at: number): ContentMarkKind[];
1525
+
1526
+ /**
1527
+ * The change that undoes `change` on `before`. Throws `edit::content_apply`
1528
+ * where `change` does not apply to `before`.
1529
+ */
1530
+ export function invert(change: Change, before: Content): Change;
1531
+
1400
1532
  /**
1401
1533
  * Whether `content` satisfies the one-line constraint of `text` and
1402
1534
  * `richtext`: one `Para` line, in no container, with no islands. Judged on
@@ -1414,32 +1546,35 @@ export function isInline(content: Content): boolean;
1414
1546
  export function isPlain(content: Content): boolean;
1415
1547
 
1416
1548
  /**
1417
- * Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
1418
- * `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
1419
- * `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
1420
- * The document-free read an editor diffs against to decide which `markOps` to
1421
- * emit, rather than reproducing that rule in its own language.
1422
- *
1423
- * `bundle.markOps` are ignored. The answer is normalized, as the store's is:
1424
- * marks a text move drops (out of range, zero-width formatting) are absent,
1425
- * and same-kind runs a move left adjacent arrive already unioned, so a bundle
1426
- * carrying no `markOps` names the marks the field will hold.
1427
- * Throws on a non-content `content`, a malformed bundle, or an op that applies
1428
- * out of bounds: `applyChange`'s errors on the same ops.
1549
+ * Backspace at the start of block `id`: the change joining it onto the block
1550
+ * before it, under that block's record. Throws `edit::content_apply` for the
1551
+ * first block or an id no block holds.
1429
1552
  */
1430
- export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
1553
+ export function joinBlocks(content: Content, id: string): Change;
1431
1554
 
1432
1555
  /**
1433
- * Map a base content position (a USV index into `Content.text`, not a UTF-16
1434
- * offset) through a `delta` to its new position, holding a caret stable across
1435
- * a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
1436
- * moves past it). Throws on a malformed `delta`.
1437
- *
1438
- * This maps a position the *caller* holds. For the marks already in a field,
1439
- * `mapMarks` applies the store's own assoc rule across every channel of a
1440
- * `ChangeBundle`.
1556
+ * Where `pos` lands after `change`, and what around it the change deleted;
1557
+ * `assoc` (`"after"` by default) picks the side of text inserted at it.
1558
+ * Throws `edit::content_apply` where a splice deletes an island and names
1559
+ * none in `removed`, as the receipt's `applied` always does.
1560
+ */
1561
+ export function mapPos(change: Change, pos: Pos, assoc?: "before" | "after"): MapResult;
1562
+
1563
+ /**
1564
+ * The canonical form of `rt`: the content `replace` stores from it, read
1565
+ * without a document. Normalizing rewrites structure, never the text, so the
1566
+ * answer's `text` is `rt.text`. Throws where `replace` would, with the same
1567
+ * error under this verb's name.
1441
1568
  */
1442
- export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1569
+ export function normalize(rt: Content): Content;
1570
+
1571
+ /**
1572
+ * Shift-Tab over the blocks `first` to `last` (`first` alone when absent):
1573
+ * the change lifting them out of the innermost item or quote holding
1574
+ * `first`. Throws `edit::content_apply` where `first` is in neither or the
1575
+ * range leaves it.
1576
+ */
1577
+ export function outdent(content: Content, first: string, last?: string | null): Change;
1443
1578
 
1444
1579
  /**
1445
1580
  * Parse a canonical document-model `Diagnostic.path` (`cards.<kind>[<i>].<field>`,
@@ -1450,13 +1585,11 @@ export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1450
1585
  export function parseDocPath(path: string): DocPathSeg[];
1451
1586
 
1452
1587
  /**
1453
- * Rebase `markdown` onto a `base` content: the document-free twin of `revise`,
1454
- * returning the new `content`, the text `delta` (offsets are USV indices into
1455
- * `Content.text`, surviving anchors rebased), and the import's
1456
- * `parse::dropped_construct` warnings, with no `path`. Throws on an
1457
- * over-nested markdown input or a non-content `base`.
1588
+ * Enter at `at`: the change splitting the block holding `at` there, the new
1589
+ * block `newId` and the text keeping its id. Throws `edit::content_apply`
1590
+ * where `at` is past the text.
1458
1591
  */
1459
- export function rebase(base: Content, markdown: string): { content: Content; delta: Delta; warnings: Diagnostic[] };
1592
+ export function splitBlock(content: Content, at: number, newId: string): Change;
1460
1593
 
1461
1594
  /**
1462
1595
  * Runs at instantiation, so a Rust panic reaches the console as a stack trace
@@ -1465,6 +1598,13 @@ export function rebase(base: Content, markdown: string): { content: Content; del
1465
1598
  */
1466
1599
  export function start(): void;
1467
1600
 
1601
+ /**
1602
+ * The change wrapping the blocks `first` to `last` in a quote or a list,
1603
+ * around the children of their innermost shared item or quote that the range
1604
+ * touches; a list the range sits in is wrapped whole.
1605
+ */
1606
+ export function wrap(content: Content, first: string, last: string, kind: WrapKind): Change;
1607
+
1468
1608
  export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
1469
1609
 
1470
1610
  export interface InitOutput {
@@ -1473,15 +1613,24 @@ export interface InitOutput {
1473
1613
  readonly __wbg_engine_free: (a: number, b: number) => void;
1474
1614
  readonly __wbg_livesession_free: (a: number, b: number) => void;
1475
1615
  readonly __wbg_quill_free: (a: number, b: number) => void;
1616
+ readonly deleteRange: (a: number, b: number, c: number, d: number) => void;
1617
+ readonly diff: (a: number, b: number, c: number) => void;
1476
1618
  readonly document__addCard: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number, i: number) => void;
1477
1619
  readonly document__commitField: (a: number, b: number, c: number, d: number, e: number) => void;
1478
1620
  readonly document__commitFields: (a: number, b: number, c: number, d: number, e: number) => void;
1621
+ readonly document__hash: (a: number, b: number, c: number) => void;
1622
+ readonly document__hashAt: (a: number, b: number, c: number, d: number) => void;
1479
1623
  readonly document__readerGet: (a: number, b: number, c: number, d: number) => void;
1480
1624
  readonly document__readerGetContent: (a: number, b: number, c: number, d: number) => void;
1481
1625
  readonly document__readerGetContentAt: (a: number, b: number, c: number, d: number, e: number) => void;
1482
- readonly document__reviseDocument: (a: number, b: number, c: number, d: number, e: number) => void;
1483
- readonly document__reviseField: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1484
- readonly document_applyChange: (a: number, b: number, c: number, d: number) => void;
1626
+ readonly document__revise: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1627
+ readonly document__reviseDocument: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1628
+ readonly document__reviseField: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
1629
+ readonly document_anchorContext: (a: number, b: number, c: number, d: number) => void;
1630
+ readonly document_anchorContexts: (a: number, b: number) => void;
1631
+ readonly document_annotated: (a: number, b: number, c: number) => void;
1632
+ readonly document_annotatedAt: (a: number, b: number, c: number) => void;
1633
+ readonly document_apply: (a: number, b: number, c: number, d: number, e: number) => void;
1485
1634
  readonly document_bodyMarkdown: (a: number, b: number, c: number) => void;
1486
1635
  readonly document_card: (a: number, b: number, c: number) => void;
1487
1636
  readonly document_cardCount: (a: number) => number;
@@ -1494,27 +1643,28 @@ export interface InitOutput {
1494
1643
  readonly document_fromStored: (a: number, b: number, c: number) => void;
1495
1644
  readonly document_getExt: (a: number, b: number, c: number) => void;
1496
1645
  readonly document_getStored: (a: number, b: number, c: number) => void;
1646
+ readonly document_hash: (a: number, b: number, c: number) => void;
1647
+ readonly document_hashAt: (a: number, b: number, c: number) => void;
1497
1648
  readonly document_insertCard: (a: number, b: number, c: number, d: number) => void;
1498
- readonly document_loadStored: (a: number, b: number, c: number, d: number) => void;
1499
1649
  readonly document_main: (a: number, b: number) => void;
1500
- readonly document_markdownRules: (a: number) => void;
1501
1650
  readonly document_moveCard: (a: number, b: number, c: number, d: number) => void;
1502
1651
  readonly document_new: (a: number, b: number, c: number) => void;
1503
- readonly document_overwrite: (a: number, b: number, c: number, d: number) => void;
1504
1652
  readonly document_pathFor: (a: number, b: number, c: number) => void;
1505
1653
  readonly document_quillRef: (a: number, b: number) => void;
1506
1654
  readonly document_quillRefHint: (a: number) => void;
1655
+ readonly document_read: (a: number, b: number, c: number) => void;
1656
+ readonly document_reattach: (a: number, b: number, c: number, d: number) => void;
1507
1657
  readonly document_removeCard: (a: number, b: number, c: number) => void;
1508
1658
  readonly document_removeExt: (a: number, b: number, c: number) => void;
1509
1659
  readonly document_removeField: (a: number, b: number, c: number) => void;
1510
1660
  readonly document_removeSeedOverlay: (a: number, b: number, c: number, d: number) => void;
1511
- readonly document_revise: (a: number, b: number, c: number, d: number, e: number) => void;
1661
+ readonly document_replace: (a: number, b: number, c: number, d: number, e: number) => void;
1662
+ readonly document_revise: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1512
1663
  readonly document_seedOverlay: (a: number, b: number, c: number, d: number) => void;
1513
1664
  readonly document_setQuillRef: (a: number, b: number, c: number, d: number) => void;
1514
1665
  readonly document_storageVersionOf: (a: number, b: number, c: number) => void;
1515
1666
  readonly document_storeExt: (a: number, b: number, c: number, d: number) => void;
1516
1667
  readonly document_storeField: (a: number, b: number, c: number, d: number) => void;
1517
- readonly document_storeFields: (a: number, b: number, c: number, d: number) => void;
1518
1668
  readonly document_storeSeedOverlay: (a: number, b: number, c: number, d: number, e: number) => void;
1519
1669
  readonly document_toAnnotatedMarkdown: (a: number, b: number) => void;
1520
1670
  readonly document_toMarkdown: (a: number, b: number) => void;
@@ -1529,9 +1679,12 @@ export interface InitOutput {
1529
1679
  readonly formatDiagnostic: (a: number, b: number) => void;
1530
1680
  readonly formatDocPath: (a: number, b: number) => void;
1531
1681
  readonly importMarkdown: (a: number, b: number, c: number) => void;
1682
+ readonly indent: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1683
+ readonly inheritedMarks: (a: number, b: number, c: number) => void;
1684
+ readonly invert: (a: number, b: number, c: number) => void;
1532
1685
  readonly isInline: (a: number, b: number) => void;
1533
1686
  readonly isPlain: (a: number, b: number) => void;
1534
- readonly livesession_backendId: (a: number, b: number) => void;
1687
+ readonly joinBlocks: (a: number, b: number, c: number, d: number) => void;
1535
1688
  readonly livesession_coverage: (a: number, b: number) => void;
1536
1689
  readonly livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1537
1690
  readonly livesession_fieldBoxes: (a: number, b: number, c: number, d: number) => void;
@@ -1544,15 +1697,17 @@ export interface InitOutput {
1544
1697
  readonly livesession_render: (a: number, b: number, c: number) => void;
1545
1698
  readonly livesession_update: (a: number, b: number, c: number) => void;
1546
1699
  readonly livesession_warnings: (a: number, b: number) => void;
1547
- readonly mapMarks: (a: number, b: number, c: number) => void;
1548
1700
  readonly mapPos: (a: number, b: number, c: number, d: number) => void;
1701
+ readonly normalize: (a: number, b: number) => void;
1702
+ readonly outdent: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1549
1703
  readonly parseDocPath: (a: number, b: number, c: number) => void;
1550
1704
  readonly quill__resolve: (a: number, b: number, c: number, d: number, e: number) => void;
1705
+ readonly quill_authoringSchema: (a: number, b: number) => void;
1551
1706
  readonly quill_backendId: (a: number, b: number) => void;
1552
- readonly quill_blueprint: (a: number, b: number) => void;
1553
1707
  readonly quill_conform: (a: number, b: number, c: number) => void;
1554
1708
  readonly quill_emptyDocument: (a: number) => number;
1555
- readonly quill_exampleDocument: (a: number, b: number) => void;
1709
+ readonly quill_exampleDocument: (a: number) => number;
1710
+ readonly quill_fromAuthoring: (a: number, b: number, c: number) => void;
1556
1711
  readonly quill_fromBundle: (a: number, b: number, c: number) => void;
1557
1712
  readonly quill_fromTree: (a: number, b: number) => void;
1558
1713
  readonly quill_metadata: (a: number, b: number) => void;
@@ -1560,13 +1715,14 @@ export interface InitOutput {
1560
1715
  readonly quill_schema: (a: number, b: number) => void;
1561
1716
  readonly quill_seedCard: (a: number, b: number, c: number, d: number, e: number) => void;
1562
1717
  readonly quill_seedDocument: (a: number) => number;
1563
- readonly quill_seedMain: (a: number, b: number) => void;
1718
+ readonly quill_toAuthoring: (a: number, b: number, c: number) => void;
1564
1719
  readonly quill_toBundle: (a: number, b: number) => void;
1565
1720
  readonly quill_toTree: (a: number) => number;
1566
1721
  readonly quill_validate: (a: number, b: number, c: number) => void;
1567
1722
  readonly quill_warnings: (a: number, b: number) => void;
1568
- readonly rebase: (a: number, b: number, c: number, d: number) => void;
1723
+ readonly splitBlock: (a: number, b: number, c: number, d: number, e: number) => void;
1569
1724
  readonly start: () => void;
1725
+ readonly wrap: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
1570
1726
  readonly __wbindgen_export: (a: number, b: number) => number;
1571
1727
  readonly __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
1572
1728
  readonly __wbindgen_export3: (a: number) => void;