@quillmark/wasm 0.92.1 → 0.95.1

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.
@@ -17,17 +17,18 @@ export type PayloadItem =
17
17
  /**
18
18
  * Paths to `!must_fill` markers nested *inside* `value` (the `value`
19
19
  * projection itself is fill-free). Absent when the field has no nested
20
- * placeholders. Preserved across `pushCard` / `makeCard`.
20
+ * placeholders. Preserved across `insertCard` / `makeCard`.
21
21
  */
22
22
  nestedFills?: PathStep[][];
23
23
  }
24
24
  | { type: "comment"; text: string; inline?: boolean };
25
25
 
26
26
  /**
27
- * A single card block. The one shape exchanged in both directions: returned by
28
- * `Document.main` / `Document.cards` / `Document.removeCard` / `Quill.seedCard`,
29
- * and accepted by `Document.pushCard` / `Document.insertCard`. Build a fresh
30
- * one with `Document.makeCard`.
27
+ * A single card block, as read back from a document: returned by
28
+ * `Document.main` / `Document.cards` / `Document.removeCard` / `Quill.seedCard`
29
+ * / `Document.makeCard`. To feed a card *into* a document use `CardInput`
30
+ * (which `insertCard` accepts); every `Card` is a valid `CardInput`,
31
+ * so a card read from one document pushes straight into another.
31
32
  *
32
33
  * `$` system entries are hoisted to named fields: `kind` (the `$kind`, empty
33
34
  * string when none), optional `quill` (the `$quill` `name@version`, main card
@@ -42,13 +43,169 @@ export interface Card {
42
43
  ext?: Record<string, unknown>;
43
44
  seed?: Record<string, unknown>;
44
45
  payloadItems: PayloadItem[];
45
- body: string;
46
+ /**
47
+ * The card body as canonical `Content` — the source-of-truth content model.
48
+ * Always this content shape on read, never a markdown string. For the markdown
49
+ * projection call the codec `exportMarkdown(card.body)`. Write a body back
50
+ * with `doc.install(addr, rt)` / `doc.revise(addr, md)`, or via `CardInput.body`.
51
+ */
52
+ body: Content;
46
53
  }
47
54
 
55
+ /**
56
+ * A card written *into* a document — the input twin of `Card`, accepted by
57
+ * `Document.insertCard`. Like `Card` but `body` also
58
+ * takes a markdown `string` (imported to the content, so a markdown / LLM writer
59
+ * needn't build the `Content` shape), and every field but `kind` is optional —
60
+ * an absent field defaults (no payload items, an empty body). Write one inline
61
+ * (`{ kind, body }`) or build it with `Document.makeCard`.
62
+ */
63
+ export interface CardInput {
64
+ kind: string;
65
+ quill?: string;
66
+ id?: string;
67
+ ext?: Record<string, unknown>;
68
+ seed?: Record<string, unknown>;
69
+ payloadItems?: PayloadItem[];
70
+ body?: Content | string;
71
+ }
48
72
 
73
+ /**
74
+ * Canonical richtext content — the content model for a card body (and richtext
75
+ * fields). One text sequence over a single coordinate space (Unicode scalar
76
+ * values): `text` plus line attributes, anchored `marks`, and embedded
77
+ * `islands`. Every edit is a splice; markdown is a projection, not the model.
78
+ * Mirrors `quillmark_content::serial`'s canonical JSON encoding.
79
+ */
80
+ export interface Content {
81
+ text: string;
82
+ lines: ContentLine[];
83
+ marks: ContentMark[];
84
+ islands: ContentIsland[];
85
+ }
86
+
87
+ /** One `\n`-separated segment of `Content.text`, in order. */
88
+ export type ContentLine = {
89
+ containers: ContentContainer[];
90
+ /** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
91
+ continues?: boolean;
92
+ } & (
93
+ | { kind: "para" }
94
+ | { kind: "heading"; level: number }
95
+ | { kind: "code"; lang?: string }
96
+ | { kind: "island" }
97
+ | { kind: "rule" }
98
+ );
99
+
100
+ /** An ancestor block a line nests inside, outermost first. */
101
+ export type ContentContainer =
102
+ | { container: "list_item"; ordered: boolean; start: number; ordinal: number }
103
+ | { container: "quote" };
104
+
105
+ /** A mark over char range `[start, end)` into `Content.text`. */
106
+ export type ContentMark = { start: number; end: number } & (
107
+ | { type: "strong" | "emph" | "underline" | "strike" | "code" }
108
+ | { type: "link"; url: string }
109
+ | { type: "anchor"; id: string }
110
+ | { type: string; attrs: unknown }
111
+ );
112
+
113
+ /** A structured object (table, figure, …) occupying one island slot in `Content.text`. */
114
+ export interface ContentIsland {
115
+ id: string;
116
+ type: string;
117
+ props: unknown;
118
+ /** How faithfully the markdown projection can carry this island. */
119
+ loss: "lossless" | "degraded" | "unrepresentable";
120
+ }
49
121
 
50
122
  /**
51
- * Page dimensions in Typst points (1 pt = 1/72 inch).
123
+ * A write address one navigation concept for the whole `Document` surface. An
124
+ * absent `field` targets the card body; an absent `card` targets the main card.
125
+ * `{}` is the main-card body; `{ card: 2 }` the body of the composable card at
126
+ * index 2; `{ field: "intro" }` the main card's `intro` field; `{ card: 2,
127
+ * field: "intro" }` a card field.
128
+ *
129
+ * On the `Addr`-taking verbs a **bare string** is shorthand for `{ field: name }`
130
+ * — `doc.storeField("qty", 3)`, `doc.revise("intro", md)` — the one coercion
131
+ * rule. A bare number is *not* an addr (`{ card: 2 }` is the self-documenting
132
+ * spelling), so no third navigation idiom re-fragments the surface.
133
+ */
134
+ export interface Addr {
135
+ card?: number;
136
+ field?: string;
137
+ }
138
+
139
+ /**
140
+ * A card-only address — the axis the card-scoped verbs (`storeFields`,
141
+ * `storeExt`, `getExt`, `commitFields`, …) take. An absent `card` targets the
142
+ * main card. A present `field` throws: a card address takes only `card`, and a
143
+ * would-be nested write is a bug the error names rather than silently ignores.
144
+ */
145
+ export interface CardAddr {
146
+ card?: number;
147
+ }
148
+
149
+ /**
150
+ * A text-splice change set over the USV content (CodeMirror `ChangeSet`
151
+ * semantics) — plain, structured-clone-able data. Returned by `revise` and by
152
+ * the `rebase` codec; map a stored position through it with `mapPos`.
153
+ */
154
+ export interface Delta {
155
+ ops: ({ retain: number } | { insert: string } | { delete: number })[];
156
+ }
157
+
158
+ /** Which side of a same-position insertion `mapPos` lands a point on. */
159
+ export type Assoc = "before" | "after";
160
+
161
+ /**
162
+ * A mark edit in final-text coordinates (post-delta, post-line-op). `add` /
163
+ * `remove` carry the `ContentMark` vocabulary (`{ type, … }`); `removeAnchor`
164
+ * drops one identity anchor by id.
165
+ */
166
+ export type MarkOp =
167
+ | ({ op: "add" | "remove"; start: number; end: number } & (
168
+ | { type: "strong" | "emph" | "underline" | "strike" | "code" }
169
+ | { type: "link"; url: string }
170
+ | { type: "anchor"; id: string }
171
+ | { type: string; attrs: unknown }
172
+ ))
173
+ | { op: "removeAnchor"; id: string };
174
+
175
+ /**
176
+ * A line/block edit. `split`/`join` splice `\n`; `setKind`/`setContainers`/
177
+ * `setContinues` touch metadata. `setContinues` sets/clears a line's within-block
178
+ * hard-break flag (`ContentLine.continues`) — the op-grained way to lower a
179
+ * Shift+Enter hard break or a new code-fence interior line; `continues: true` on
180
+ * line 0 is rejected (nothing precedes it to continue).
181
+ */
182
+ export type LineOp =
183
+ | { op: "split"; at: number }
184
+ | { op: "join"; line: number }
185
+ | ({ op: "setKind"; line: number } & (
186
+ | { kind: "para" | "island" | "rule" }
187
+ | { kind: "heading"; level: number }
188
+ | { kind: "code"; lang?: string }
189
+ ))
190
+ | { op: "setContainers"; line: number; containers: ContentContainer[] }
191
+ | { op: "setContinues"; line: number; continues: boolean };
192
+
193
+ /**
194
+ * A committed content edit bundle for `applyChange`: a text `delta` (default no
195
+ * text change), then `lineOps`, then `markOps` (mark ranges are in post-delta
196
+ * coordinates). Every field is optional.
197
+ */
198
+ export interface ChangeBundle {
199
+ delta?: Delta;
200
+ lineOps?: LineOp[];
201
+ markOps?: MarkOp[];
202
+ }
203
+
204
+
205
+
206
+ /**
207
+ * Page dimensions in points (1 pt = 1/72 inch). Typst measures in Typst
208
+ * points; pdfform measures in PDF points — the same unit.
52
209
  *
53
210
  * Report-only: the painter sizes the canvas itself based on
54
211
  * `PaintOptions`. `pageSize` is exposed for callers that need page
@@ -61,10 +218,11 @@ export interface PageSize {
61
218
  }
62
219
 
63
220
  /**
64
- * Inputs to `RenderSession.paint`. Both fields are optional and default
221
+ * Inputs to `LiveSession.paint`. Both fields are optional and default
65
222
  * to `1`.
66
223
  *
67
- * - `layoutScale` — layout-space pixels per Typst point. For on-screen
224
+ * - `layoutScale` — layout-space pixels per point (Typst point / PDF
225
+ * point — the same 1/72″ unit). For on-screen
68
226
  * canvases this is CSS pixels per pt; the page's layout-pixel size is
69
227
  * `widthPt * layoutScale × heightPt * layoutScale`. The painter
70
228
  * surfaces these dimensions as `layoutWidth` / `layoutHeight` so
@@ -86,7 +244,7 @@ export interface PaintOptions {
86
244
  }
87
245
 
88
246
  /**
89
- * Returned by `RenderSession.paint`.
247
+ * Returned by `LiveSession.paint`.
90
248
  *
91
249
  * - `layoutWidth` / `layoutHeight` — layout-pixel dimensions of the
92
250
  * canvas's display box. For on-screen canvases this is CSS pixels:
@@ -98,12 +256,22 @@ export interface PaintOptions {
98
256
  * Equal to `round(layoutWidth * densityScale)` ×
99
257
  * `round(layoutHeight * densityScale)` *unless* the requested backing
100
258
  * exceeded the painter's safe maximum (16384 px per side), in which
101
- * case `densityScale` was clamped to fit. Detect clamping via
102
- * `pixelWidth < round(layoutWidth * densityScale)`.
259
+ * case `densityScale` was clamped to fit.
260
+ * - `clamped` `true` when that 16384-px clamp fired, so the page is
261
+ * painted at fewer device pixels than requested and renders soft at the
262
+ * same `canvas.style` size. Reads the clamp off the return value instead
263
+ * of the `pixelWidth < round(layoutWidth * densityScale)` derivation.
264
+ * - `effectiveDensityScale` — the `densityScale` actually applied: the
265
+ * requested value unless `clamped`, then reduced proportionally.
266
+ * `layoutScale * effectiveDensityScale` is the scale the backing store
267
+ * was rasterized at.
103
268
  *
104
269
  * The painter owns `canvas.width` / `canvas.height`; consumers must not
105
270
  * write to them. The painter does **not** touch `canvas.style.*`;
106
- * consumers own layout.
271
+ * consumers own layout. The write is a whole-backing-store `putImageData`,
272
+ * which bypasses the 2D context transform, `globalAlpha`, and clip: give
273
+ * each visible page its own `` — you cannot composite two pages, a
274
+ * sub-rect, or a context transform through `paint`.
107
275
  *
108
276
  * For `OffscreenCanvasRenderingContext2D` (Worker rasterization, no
109
277
  * DOM), `layoutWidth` / `layoutHeight` are informational — there's no
@@ -114,14 +282,18 @@ export interface PaintResult {
114
282
  layoutHeight: number;
115
283
  pixelWidth: number;
116
284
  pixelHeight: number;
285
+ clamped: boolean;
286
+ effectiveDensityScale: number;
117
287
  }
118
288
 
119
289
 
120
290
 
121
- /** UI layout hints for a single field. */
291
+ /** UI layout hints for a single field. Field display order is not a hint:
292
+ * key order in the schema's `fields`/`properties` objects is declaration
293
+ * order, the ordering contract. */
122
294
  export interface QuillFieldUi {
295
+ title?: string;
123
296
  group?: string;
124
- order?: number;
125
297
  compact?: boolean;
126
298
  multiline?: boolean;
127
299
  }
@@ -144,20 +316,28 @@ export interface QuillCardBody {
144
316
  * A field's *cell* is determined by `default`: a field with a `default`
145
317
  * is **Endorsed** (the rendered value is shippable as-is), while a field
146
318
  * without a `default` is **Unendorsed** (the blueprint carries a
147
- * `<must-fill>` sentinel and validation reports
148
- * `validation::field_absent` if the field is absent at validate
149
- * time — a non-fatal signal, since the render path zero-fills an absent
150
- * field). There is no separate `required` axis.
319
+ * `!must_fill` marker; a marker left in the document yields the non-fatal
320
+ * `validation::must_fill` warning from validate, and the render path
321
+ * zero-fills the field). There is no separate `required` axis.
151
322
  */
152
323
  export interface QuillFieldSchema {
153
- type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "datetime" | "markdown";
324
+ type: "string" | "number" | "integer" | "boolean" | "array" | "object" | "date" | "datetime" | "richtext" | "plaintext" | "enum";
154
325
  description?: string;
155
326
  default?: unknown;
156
327
  example?: unknown;
328
+ /** Closed value domain. On `type: "enum"` declared as `values`; the
329
+ * deprecated `enum` modifier on `type: "string"` is accepted for one
330
+ * release. Both round-trip through this field. */
157
331
  enum?: string[];
332
+ /** Required on `type: "enum"`: the closed set of allowed string values. */
333
+ values?: string[];
158
334
  ui?: QuillFieldUi;
159
335
  properties?: Record<string, QuillFieldSchema>;
160
336
  items?: QuillFieldSchema;
337
+ /** Present (and `true`) on a `richtext` or `plaintext` field declared
338
+ * `inline` — the single-paragraph, container-free, island-free constraint.
339
+ * Core serializes `inline: true` into the schema JSON; absent otherwise. */
340
+ inline?: boolean;
161
341
  }
162
342
 
163
343
  /** Schema entry for the main card or a named card kind. */
@@ -202,6 +382,17 @@ export interface Artifact {
202
382
  mimeType: string;
203
383
  }
204
384
 
385
+ export interface ChangeSet {
386
+ pageCount: number;
387
+ dirtyPages: number[];
388
+ }
389
+
390
+ export interface ContentHit {
391
+ field: string;
392
+ pos: number;
393
+ granularity?: HitGranularity;
394
+ }
395
+
205
396
  export interface Diagnostic {
206
397
  severity: Severity;
207
398
  code?: string;
@@ -212,6 +403,13 @@ export interface Diagnostic {
212
403
  sourceChain?: string[];
213
404
  }
214
405
 
406
+ export interface FieldRegion {
407
+ field: string;
408
+ page: number;
409
+ rect: [number, number, number, number];
410
+ span?: [number, number];
411
+ }
412
+
215
413
  export interface Location {
216
414
  file: string;
217
415
  line: number;
@@ -223,6 +421,7 @@ export interface RenderOptions {
223
421
  ppi?: number;
224
422
  pages?: number[];
225
423
  producer?: string;
424
+ regions?: boolean;
226
425
  }
227
426
 
228
427
  export interface RenderResult {
@@ -230,20 +429,33 @@ export interface RenderResult {
230
429
  warnings: Diagnostic[];
231
430
  outputFormat: OutputFormat;
232
431
  renderTimeMs: number;
432
+ regions: FieldRegion[];
233
433
  }
234
434
 
435
+ export type HitGranularity = "cluster" | "segment";
436
+
235
437
  export type OutputFormat = "pdf" | "svg" | "txt" | "png";
236
438
 
237
- export type Severity = "error" | "warning" | "note";
439
+ export type Severity = "error" | "warning";
238
440
 
239
441
 
240
442
  /**
241
443
  * Typed in-memory Quillmark document.
242
444
  */
243
445
  export class Document {
244
- private constructor();
245
446
  free(): void;
246
447
  [Symbol.dispose](): void;
448
+ /**
449
+ * **Apply** a committed content edit `bundle` (`{ delta?, lineOps?, markOps? }`)
450
+ * at `addr` — the editor splice: text delta first, then line ops, then mark
451
+ * ops (mark ranges in final-text coordinates), each all-or-nothing. An absent
452
+ * `addr.field` targets the body, an absent `addr.card` the main card.
453
+ *
454
+ * Throws on an out-of-range card, a field that is not richtext, a malformed
455
+ * bundle, or an op that applies out of bounds (the value is unchanged on a
456
+ * failed apply).
457
+ */
458
+ applyChange(addr: Addr | string, bundle: ChangeBundle): void;
247
459
  /**
248
460
  * Authoring-ergonomics header introducing a blueprint to an LLM/MCP
249
461
  * consumer for the given `quillName`. Re-exposes core's canonical text for
@@ -251,6 +463,21 @@ export class Document {
251
463
  * uniform.
252
464
  */
253
465
  static blueprintInstruction(quill_name: string): string;
466
+ /**
467
+ * A single composable card by index — the whole `Card`, the card-indexed
468
+ * twin of the [`main`](Self::main) getter, so reading one card need not
469
+ * materialize every card via [`cards`](Self::cards). An out-of-range
470
+ * `index` throws `[EditError::IndexOutOfRange]`, matching the card write
471
+ * verbs.
472
+ */
473
+ card(index: number): Card;
474
+ /**
475
+ * The index of the first composable card whose `$id` equals `id`, or
476
+ * `undefined` when none carries it. Resolves the canonical durable address
477
+ * without a hand-rolled scan over [`cards`](Self::cards); `$id` is
478
+ * non-unique by design, so the first match wins.
479
+ */
480
+ cardIndexById(id: string): number | undefined;
254
481
  clone(): Document;
255
482
  /**
256
483
  * Schema version this build writes via [`toJson`](Document::to_json).
@@ -291,16 +518,89 @@ export class Document {
291
518
  */
292
519
  static fromMarkdown(markdown: string): Document;
293
520
  /**
294
- * Insert a card at `index` (must be in `0..=cards.length`). Accepts a
295
- * `Card` (see [`pushCard`](Self::push_card)).
521
+ * Read the value at `addr` the raw stored payload value of a field (a
522
+ * content object for a richtext field, a scalar/array/object otherwise), or
523
+ * the **body content** when `addr.field` is absent. A bare string is `Addr`
524
+ * shorthand for `{ field }`. Reads are total over the field axis: an absent
525
+ * field is `undefined`; only an out-of-range `addr.card` throws
526
+ * `[EditError::IndexOutOfRange]`. Reads need no schema, so they live on
527
+ * `Document`, not the typed writer; for the markdown projection of a
528
+ * richtext value use [`getMarkdown`](Self::get_markdown).
529
+ */
530
+ get(addr: Addr | string): unknown;
531
+ /**
532
+ * The whole `$ext` map at `addr` (a card address, absent `card` = main), or
533
+ * `undefined` when the card carries none. The fine-grained `$ext` read —
534
+ * your own state without serializing the whole card. Throws on a present
535
+ * `field` (a card address takes only `card`) or an out-of-range card.
536
+ */
537
+ getExt(addr?: CardAddr): Record<string, unknown> | undefined;
538
+ /**
539
+ * The value stored under `$ext[ns]` at `addr` (a card address, absent `card`
540
+ * = main), or `undefined`. The namespace-scoped `$ext` read — your own slot
541
+ * without a whole-card serialize, and non-destructive (unlike
542
+ * `removeExtNamespace`). Throws on a present `field` or an out-of-range card.
543
+ */
544
+ getExtNamespace(addr: CardAddr, ns: string): unknown;
545
+ /**
546
+ * The **body** markdown projection — the main body, or a composable card's
547
+ * body (`{ card }`) — the on-demand, lossy export (content-only marks do not
548
+ * survive markdown). A body's type is a format fact, not a schema fact, so
549
+ * this read stays quill-free; a body is never absent.
550
+ *
551
+ * `addr` is an optional **card address** (`{ card }`, absent = main). A
552
+ * present `field` throws — a field's markdown is read through the
553
+ * schema-plane `quill.view(doc).get(field)`, which interprets by declared
554
+ * type (#978). An out-of-range `addr.card` throws.
555
+ */
556
+ getMarkdown(addr?: CardAddr): string;
557
+ /**
558
+ * Insert a card — the single insertion verb: `at` absent appends, a number
559
+ * inserts at that index (must be in `0..=cards.length`). Accepts a
560
+ * `CardInput` — a card read back (`cards` / `removeCard` / `quill.seedCard`),
561
+ * a [`makeCard`](Document::make_card) result, or a bare `{ kind, body }`
562
+ * (every returned `Card` is a valid `CardInput`). Throws if `card.kind` is
563
+ * not a valid kind name, or if `at` is out of range.
564
+ */
565
+ insertCard(card: CardInput, at?: number): void;
566
+ /**
567
+ * **Install** a richtext value at `addr` — **value semantics**, content only.
568
+ * Stores exactly `rt` (a canonical `Content` content object); the identity
569
+ * anchors of any previous value are gone. An absent `addr.field` targets the
570
+ * body, an absent `addr.card` the main card. For "here's new markdown," use
571
+ * [`revise`](Document::revise); the cold-import path is spelled at the call
572
+ * site as `install(addr, importMarkdown(md))`, so anchor loss is visible in
573
+ * source.
574
+ *
575
+ * Throws on an out-of-range card, a malformed field name, or an `rt` that is
576
+ * not a canonical content object.
577
+ */
578
+ install(addr: Addr | string, rt: Content): void;
579
+ /**
580
+ * Whether the field at `addr` is marked `!must_fill`. A bare string is `Addr`
581
+ * shorthand for `{ field }`. `false` for an absent field (truthful — it isn't
582
+ * marked) and for a body address (a body is never a fill). Only an
583
+ * out-of-range `addr.card` throws.
296
584
  */
297
- insertCard(index: number, card: Card): void;
585
+ isFill(addr: Addr | string): boolean;
586
+ /**
587
+ * Replace this document's contents **in place** from a versioned storage
588
+ * DTO string — the mutating twin of the static
589
+ * [`fromJson`](Document::from_json) constructor. Parse-time `warnings` are
590
+ * cleared. Throws (leaving the document unchanged) on an invalid DTO.
591
+ *
592
+ * The cross-WASM-memory `Document` bridge: mutate a document on a
593
+ * backend-memory clone, then write the mutated state back into the caller's
594
+ * canonical document with this — the one way to update a live handle across
595
+ * the linear-memory seam without the caller re-binding its variable.
596
+ */
597
+ loadJson(json: string): void;
298
598
  /**
299
599
  * Build a fresh `Card` from a kind and a flat field map — the ergonomic
300
- * constructor for `pushCard` / `insertCard`. `fields` is an optional
600
+ * constructor for `insertCard`. `fields` is an optional
301
601
  * `Record<string, unknown>` (each entry becomes a card field, in
302
602
  * insertion order); `body` defaults to `""`. Kind validity is checked by
303
- * `pushCard` / `insertCard`, not here.
603
+ * `insertCard`, not here.
304
604
  */
305
605
  static makeCard(kind: string, fields?: Record<string, unknown>, body?: string): Card;
306
606
  /**
@@ -308,12 +608,14 @@ export class Document {
308
608
  */
309
609
  moveCard(from: number, to: number): void;
310
610
  /**
311
- * Append a card to the end of the card list. Accepts a `Card` (the shape
312
- * returned by `cards` / `removeCard` / `quill.seedCard`); build a fresh
313
- * one with [`Document.makeCard`](Document::make_card). Throws if
314
- * `card.kind` is not a valid kind name.
611
+ * `new Document(quillRef)` a blank document: a main card carrying only
612
+ * `$quill`, an empty body, and no composable cards. The programmatic
613
+ * blank canvas: absent fields resolve at render time (`default`, else
614
+ * type-empty zero), so nothing the caller did not set reaches the
615
+ * output. For an example-filled starter use `Quill.seedDocument()`.
616
+ * Throws on an invalid quill reference. Mirrors Python `Document(quill_ref)`.
315
617
  */
316
- pushCard(card: Card): void;
618
+ constructor(quill_ref: string);
317
619
  /**
318
620
  * The canonical `$quill` reference grammar as author-facing text. Core is
319
621
  * the single source of truth: drive schema `describe` and validation
@@ -324,48 +626,44 @@ export class Document {
324
626
  static quillRefHint(): string;
325
627
  removeCard(index: number): Card | undefined;
326
628
  /**
327
- * Remove the `$ext` map from the composable card at `index` *entirely*,
328
- * returning the previous map or `undefined`. Throws if out of range.
329
- * Prefer `removeCardExtNamespace` to clear only one consumer's slot.
330
- */
331
- removeCardExt(index: number): Record<string, unknown> | undefined;
332
- /**
333
- * Remove `namespace` from the composable card's `$ext` map, returning the
334
- * value stored there or `undefined`; clears `$ext` entirely once empty.
335
- * The card-indexed twin of `removeExtNamespace`. Throws if out of range.
336
- */
337
- removeCardExtNamespace(index: number, namespace: string): any;
338
- /**
339
- * Remove a field on the card at `index`. Returns the removed value or
340
- * `undefined`. Throws if `index` is out of range or `name` is invalid.
341
- */
342
- removeCardField(index: number, name: string): any;
343
- /**
344
- * Remove the `$ext` map from the main card *entirely*, returning the
345
- * previous map or `undefined`. This is a blunt escape hatch that discards
346
- * every namespace at once — prefer `removeExtNamespace` to clear only your
347
- * own slot while leaving sibling consumers' state intact.
629
+ * Remove the `$ext` map on the card `addr` targets *entirely*, returning the
630
+ * previous map or `undefined` a blunt escape hatch that discards every
631
+ * namespace at once (prefer `removeExtNamespace`). `addr` is a card address
632
+ * (absent = main). Throws on a present `field` or an out-of-range card.
348
633
  */
349
- removeExt(): Record<string, unknown> | undefined;
634
+ removeExt(addr?: CardAddr): Record<string, unknown> | undefined;
350
635
  /**
351
- * Remove `namespace` from the main card's `$ext` map, returning the value
352
- * stored there or `undefined`. This is the recommended way to clear `$ext`
353
- * state: sibling namespaces survive, and when the last namespace is removed
354
- * the `$ext` entry is dropped entirely (not left as `$ext: {}`).
636
+ * Remove `$ext[ns]` on the card `addr` targets, returning its value or
637
+ * `undefined`; drops `$ext` once empty. `addr` is a card address (absent =
638
+ * main). Preserves sibling namespaces. Throws on a present `field` or an
639
+ * out-of-range card.
355
640
  */
356
- removeExtNamespace(namespace: string): any;
641
+ removeExtNamespace(addr: CardAddr, ns: string): any;
357
642
  /**
358
- * Remove a payload field on the main card, returning the removed value or
359
- * `undefined`. Throws if `name` does not match `[A-Za-z_][A-Za-z0-9_]*`.
643
+ * Remove a field at `addr`, returning the removed value or `undefined`. A
644
+ * bare string is `Addr` shorthand for `{ field }`. One `remove` verb serves
645
+ * every write lane. A body address throws; throws on an out-of-range card or
646
+ * a malformed name.
360
647
  */
361
- removeField(name: string): any;
648
+ removeField(addr: Addr | string): any;
362
649
  /**
363
650
  * Remove `cardKind` from the main card's `$seed` map, returning its
364
- * overlay or `undefined`; drops `$seed` entirely once empty. Sibling
365
- * kinds survive.
651
+ * overlay or `undefined`; drops `$seed` entirely once empty. Sibling kinds
652
+ * survive. `$seed` is main-only, so this takes no address.
366
653
  */
367
654
  removeSeedNamespace(card_kind: string): any;
368
- replaceBody(body: string): void;
655
+ /**
656
+ * **Revise** the richtext value at `addr` from a markdown string — **edit
657
+ * semantics**, the default write path, returning the text [`Delta`]. Imports
658
+ * the markdown, diffs it against the current value, rebases surviving
659
+ * identity anchors, and returns the change an editor bridge maps its own
660
+ * positions through (`mapPos`). An absent `addr.field` targets the body, an
661
+ * absent `addr.card` the main card; an absent field cold-imports from empty.
662
+ *
663
+ * Throws on an out-of-range card, a malformed field name, a present
664
+ * non-content field value, or an over-nested markdown input.
665
+ */
666
+ revise(addr: Addr | string, markdown: string): Delta;
369
667
  /**
370
668
  * Read the `schema` version tag from a raw storage DTO string without a
371
669
  * full parse, or `undefined`. Returns unknown future versions as-is —
@@ -374,17 +672,13 @@ export class Document {
374
672
  */
375
673
  static schemaVersionOf(json: string): string | undefined;
376
674
  /**
377
- * Replace the `$ext` map on the composable card at `index`. Throws if out
378
- * of range or `value` is not a plain object. Named to mirror `setExt` on
379
- * the main card; `setCardExtNamespace` is the sibling-safe alternative.
675
+ * The main card's `$seed` overlay object for `kind` (the `$seed[kind]`
676
+ * entry), or `undefined` when absent. The cheap read that feeds
677
+ * `quill.seedCard(kind, overlay)` without serializing the whole main card
678
+ * via [`main`](Self::main) to fish out one key — and it keeps `seedCard`
679
+ * pure: the quill still never reads the document.
380
680
  */
381
- setCardExt(index: number, value: any): void;
382
- /**
383
- * Merge `value` into the composable card's `$ext` map under `namespace`,
384
- * preserving sibling namespaces. The card-indexed twin of `setExtNamespace`.
385
- * Throws if out of range or `value` cannot be serialized.
386
- */
387
- setCardExtNamespace(index: number, namespace: string, value: any): void;
681
+ seedOverlay(kind: string): Record<string, unknown> | undefined;
388
682
  /**
389
683
  * Replace the kind of the card at `index`. Payload and body are untouched;
390
684
  * schema-aware migration is the caller's responsibility.
@@ -392,41 +686,61 @@ export class Document {
392
686
  */
393
687
  setCardKind(index: number, new_kind: string): void;
394
688
  /**
395
- * Replace the opaque `$ext` map on the main card. `value` must be a plain
396
- * object; throws otherwise. `$ext` carries out-of-band consumer state and
397
- * never reaches the rendered output. Pass `{}` to record an explicit
398
- * empty `$ext`.
689
+ * Replace the QUILL reference string. Throws if `ref_str` is invalid.
399
690
  */
400
- setExt(value: any): void;
691
+ setQuillRef(ref_str: string): void;
401
692
  /**
402
- * Merge `value` into the main card's `$ext` map under `namespace`, creating
403
- * the map when absent and replacing any existing value at that key. Sibling
404
- * namespaces are preserved, so independent consumers (`$ext.editor`,
405
- * `$ext.agent`, …) don't clobber each other.
693
+ * Replace the opaque `$ext` map on the card `addr` targets (a card address,
694
+ * absent `card` = main). `value` must be a plain object. `$ext` carries
695
+ * out-of-band consumer state and never reaches the rendered output; pass
696
+ * `{}` for an explicit empty `$ext`. Quill-free and verbatim an opaque
697
+ * `store` verb. Throws on a present `field` or an out-of-range card.
406
698
  */
407
- setExtNamespace(namespace: string, value: any): void;
699
+ storeExt(addr: CardAddr, value: any): void;
408
700
  /**
409
- * Update a payload field on the main card. Clears any existing `!must_fill` marker.
410
- *
411
- * Throws if `name` does not match `[A-Za-z_][A-Za-z0-9_]*`.
701
+ * Merge `value` into `$ext[ns]` on the card `addr` targets, preserving
702
+ * sibling namespaces — the recommended `$ext` write. `addr` is a card
703
+ * address (absent = main). Quill-free and verbatim — an opaque `store` verb.
704
+ * Throws on a present `field` or an out-of-range card.
412
705
  */
413
- setField(name: string, value: any): void;
706
+ storeExtNamespace(addr: CardAddr, ns: string, value: any): void;
414
707
  /**
415
- * Update a payload field on the main card and mark it as `!must_fill`.
416
- * Throws on invalid name (see [`setField`](Document::set_field)).
708
+ * Store a field verbatim at `addr` the opaque store (**store** = verbatim,
709
+ * coercion deferred to render; the typed write is
710
+ * [`commitField`](Document::commit_field)). A bare string is `Addr`
711
+ * shorthand for `{ field }`, so `doc.storeField("qty", 3)` reads as written;
712
+ * `{ card: 2, field: "qty" }` targets a composable card. Clears any
713
+ * `!must_fill` marker. A body address (no `field`) throws — a body is never
714
+ * opaque; write it with `revise` / `install` / `writer.setBody`. Throws on
715
+ * an out-of-range card or a malformed name.
417
716
  */
418
- setFill(name: string, value: any): void;
717
+ storeField(addr: Addr | string, value: any): void;
419
718
  /**
420
- * Replace the QUILL reference string. Throws if `ref_str` is invalid.
719
+ * Store several fields verbatim and atomically on the card `addr` targets
720
+ * the opaque store's batch. `addr` is a **card address** (`{ card }`, absent
721
+ * = main); a present `field` throws. The batch verb takes the address first
722
+ * and is never shape-overloaded, because `card` is a legal field name:
723
+ * `storeFields({}, fields)` is the main card, `storeFields({ card: 2 },
724
+ * fields)` a composable one — never ambiguous with "set field `card`".
725
+ * Nothing is applied on error; the thrown error's `diagnostics` carry one
726
+ * entry per offending field. Throws on an out-of-range card.
421
727
  */
422
- setQuillRef(ref_str: string): void;
728
+ storeFields(addr: CardAddr, fields: Record<string, unknown>): void;
729
+ /**
730
+ * Store a field verbatim at `addr` and mark it `!must_fill` — the opaque
731
+ * store's fill variant, card-capable (a bare string or `{ field }` for main,
732
+ * `{ card, field }` for a composable card). A body address throws. Same
733
+ * validation as [`storeField`](Document::store_field).
734
+ */
735
+ storeFill(addr: Addr | string, value: any): void;
423
736
  /**
424
- * Merge a card-kind's seed `overlay` into the main card's `$seed` map
425
- * under `cardKind`, preserving sibling kinds. Sets the starting values
426
- * new cards of that kind spawn with. Throws if `overlay` cannot be
427
- * serialized or nests too deep.
737
+ * Merge a card-kind's seed `overlay` into the **main** card's `$seed` map
738
+ * under `cardKind`, preserving sibling kinds `$seed` lives on the main
739
+ * card by model, so this takes no address. Sets the starting values new
740
+ * cards of that kind spawn with. Quill-free and verbatim — an opaque `store`
741
+ * verb. Throws if `overlay` cannot be serialized or nests too deep.
428
742
  */
429
- setSeedNamespace(card_kind: string, overlay: any): void;
743
+ storeSeedNamespace(card_kind: string, overlay: any): void;
430
744
  /**
431
745
  * Serialize this document to a versioned storage DTO string.
432
746
  *
@@ -451,15 +765,6 @@ export class Document {
451
765
  * genuinely malformed markdown.
452
766
  */
453
767
  static tryFromJson(json: string): Document | undefined;
454
- /**
455
- * Replace the body of the card at `index`. Throws if out of range.
456
- */
457
- updateCardBody(index: number, body: string): void;
458
- /**
459
- * Update a field on the card at `index`.
460
- * Throws if `index` is out of range, `name` is reserved or invalid.
461
- */
462
- updateCardField(index: number, name: string, value: any): void;
463
768
  /**
464
769
  * Number of composable cards (excludes the main card). O(1).
465
770
  */
@@ -474,6 +779,129 @@ export class Document {
474
779
  readonly warnings: Diagnostic[];
475
780
  }
476
781
 
782
+ /**
783
+ * Live render session: reads (`render`, `paint`, `pageSize`, `regions`,
784
+ * `fieldAt`, `positionAt`, `locate`) serve the current compile. `apply(doc)`
785
+ * recompiles a whole document in place, transactionally (on throw every read
786
+ * keeps serving the last-good compile). Geometry reads reflect the current
787
+ * compile; anchoring a caret across edits is the editor's job — re-read
788
+ * geometry after each committed `apply`.
789
+ *
790
+ * **Empty documents.** A zero-page document yields a valid session
791
+ * (`pageCount === 0`); `paint(ctx, 0)` or `pageSize(0)` throws with
792
+ * `"page index 0 out of range (pageCount=0)"`. Branch on `pageCount === 0`
793
+ * rather than catching the error.
794
+ */
795
+ export class LiveSession {
796
+ private constructor();
797
+ free(): void;
798
+ [Symbol.dispose](): void;
799
+ /**
800
+ * Recompile the session against `doc` — the edit verb of a live preview.
801
+ * The document is compiled through the same schema pipeline as `open`
802
+ * (same quill), then applied transactionally: on throw every read
803
+ * (`render`, `paint`, `pageSize`, `regions`, `fieldAt`) keeps serving the last-good
804
+ * compile, and the session recovers on the next successful `apply`. On
805
+ * success reads serve the new compile; repaint `dirtyPages ∩ visible`.
806
+ */
807
+ apply(doc: Document): ChangeSet;
808
+ /**
809
+ * The schema field whose content is under a point on `page` — the
810
+ * forward (click → field) direction: hit-test a click against the
811
+ * compiled document and get back the field address to focus in the
812
+ * editor, or `undefined` off any field's ink. `x`/`y` are PDF points
813
+ * with a **bottom-left** origin, the same space as `FieldRegion.rect` —
814
+ * from a canvas click, invert the overlay transform documented on
815
+ * `FieldRegion`: `x = clickPx.x / renderScale`,
816
+ * `y = pageHeightPt - clickPx.y / renderScale`. Unlike `regions()`,
817
+ * *every* placement answers, not just the first.
818
+ */
819
+ fieldAt(page: number, x: number, y: number): string | undefined;
820
+ /**
821
+ * The whole-field highlight boxes for `field` — one union rect per page,
822
+ * over the field's `span`-bearing content segments. The convenience that
823
+ * owns the union `regions()` leaves derived: it keeps `regions()` the
824
+ * low-level disjoint truth (#829) and folds the span-filter + per-page
825
+ * union here, so a "highlight the focused field" consumer stops
826
+ * reimplementing it. **Content only** — a field placed solely as a scalar
827
+ * reference or a bound widget carries no `span` and returns `[]`; its box
828
+ * is a single `regions()` rect. Reflects the current compile, like
829
+ * `regions()`.
830
+ */
831
+ fieldBoxes(field: string): FieldRegion[];
832
+ /**
833
+ * A content position → **caret rect** — the reverse of `positionAt`: given
834
+ * a field and a USV offset into its `Content`, return the box (in the
835
+ * same bottom-left PDF-point space as `FieldRegion.rect`) to draw a caret
836
+ * at, its `span` collapsed to `[pos, pos]`; `undefined` when the field
837
+ * places no tracked content or the offset maps to no drawn glyph.
838
+ */
839
+ locate(field: string, pos: number): FieldRegion | undefined;
840
+ /**
841
+ * Page dimensions in points (1 pt = 1/72 inch).
842
+ * Throws if the backend has no canvas painter or `page` is out of range.
843
+ */
844
+ pageSize(page: number): PageSize;
845
+ /**
846
+ * Paint `page` into a `CanvasRenderingContext2D` or
847
+ * `OffscreenCanvasRenderingContext2D`. The painter owns
848
+ * `canvas.width`/`height` (no `clearRect` needed); consumers own
849
+ * `canvas.style.*`. If `layoutScale * densityScale` exceeds 16384 px
850
+ * per side, `densityScale` is clamped — `PaintResult.clamped` reports it and
851
+ * `PaintResult.effectiveDensityScale` carries the density actually applied.
852
+ *
853
+ * `put_image_data` writes the whole backing store, bypassing the 2D
854
+ * context's transform, `globalAlpha`, and clip: the painter owns the entire
855
+ * canvas, so each visible page needs its own `` — you cannot composite
856
+ * two pages, a sub-rect, or a context transform through this call.
857
+ *
858
+ * Throws if the backend has no canvas painter, `page` is out of range,
859
+ * `ctx` is the wrong type, or either scale is non-finite or `<= 0`.
860
+ */
861
+ paint(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, page: number, opts: PaintOptions | undefined): PaintResult;
862
+ /**
863
+ * A point → **content position** — the fine-grained click direction:
864
+ * hit-test a point and get back the field *and* a USV offset into its
865
+ * `Content` (for placing a caret or mapping a selection into the content
866
+ * model), or `undefined` off all content ink. `x`/`y` are PDF points,
867
+ * bottom-left origin — the same space as `fieldAt`. The offset is
868
+ * cluster-exact and degrades to the containing segment's start on
869
+ * origin-less ink (list markers, a code fence's interior). See
870
+ * `ContentHit`.
871
+ */
872
+ positionAt(page: number, x: number, y: number): ContentHit | undefined;
873
+ /**
874
+ * Schema-field geometry for this compiled session — each content field's
875
+ * **first placement** (one region per page it touches) plus widget and
876
+ * scalar-reference-site regions, keyed on the quill schema field path; a
877
+ * field may still appear more than once (group by `field`, see
878
+ * `FieldRegion`). A session-level query: no render, no byte artifact. An
879
+ * interactive preview reads it to scroll to / highlight the focused
880
+ * field over a `paint`-ed canvas; the click direction is `fieldAt`.
881
+ * Empty for backends that place no schema fields.
882
+ */
883
+ regions(): FieldRegion[];
884
+ render(opts?: RenderOptions | null): RenderResult;
885
+ /**
886
+ * The backend that produced this session (e.g. `"typst"`).
887
+ */
888
+ readonly backendId: string;
889
+ readonly pageCount: number;
890
+ /**
891
+ * `true` iff `paint` and `pageSize` will succeed for this session. Derived
892
+ * from the session's canvas seam, so it reflects exactly what `paint` will
893
+ * do — no separately captured flag.
894
+ */
895
+ readonly supportsCanvas: boolean;
896
+ /**
897
+ * Non-fatal diagnostics of the session's **current compile** (e.g. Typst
898
+ * font fallback) — set at open and refreshed by each committed `apply`;
899
+ * a failed apply keeps the last-good compile's warnings. Also appended
900
+ * to `RenderResult.warnings` on each `render()` call.
901
+ */
902
+ readonly warnings: Diagnostic[];
903
+ }
904
+
477
905
  export class Quill {
478
906
  private constructor();
479
907
  free(): void;
@@ -493,9 +921,9 @@ export class Quill {
493
921
  * layering an optional per-kind seed `overlay` over the schema-example
494
922
  * base (`overlay › example › absent`). Returns `undefined` if `cardKind`
495
923
  * is not declared in this quill's schema, else a `Card` that feeds
496
- * straight into `Document.pushCard` / `insertCard`.
924
+ * straight into `Document.insertCard`.
497
925
  *
498
- * Pass `document.main.seed?.[cardKind]` as `overlay` so a card added to a
926
+ * Pass `document.seedOverlay(cardKind)` as `overlay` so a card added to a
499
927
  * template-derived document inherits its curated starting values; omit it
500
928
  * (or pass `undefined` / `null`) for the bare schema seed. `overlay` is a
501
929
  * plain object — this reads the document, it does not mutate it.
@@ -536,10 +964,10 @@ export class Quill {
536
964
  *
537
965
  * Forwards the canonical `validation::*` diagnostics — same `code`,
538
966
  * `path`, and `hint` the engine emits — including the non-fatal
539
- * `validation::field_absent` completeness signal that `render` demotes.
540
- * Field values, defaults, and order are not part of this surface: read
541
- * them from the `Document` payload and `Quill.schema` (fields carry
542
- * `ui.order`).
967
+ * `validation::must_fill` warning for each `!must_fill` marker left in
968
+ * the document. Field values, defaults, and order are not part of this
969
+ * surface: read them from the `Document` payload and `Quill.schema`
970
+ * (schema key order is display order).
543
971
  */
544
972
  validate(doc: Document): Diagnostic[];
545
973
  /**
@@ -558,9 +986,10 @@ export class Quill {
558
986
  readonly metadata: QuillMetadata;
559
987
  /**
560
988
  * Document schema for the quill: the user-fillable fields plus their
561
- * `ui` hints (group / order / showWhen). The single field-metadata
562
- * surface — drives form editors and LLM/MCP consumers alike. Returns the
563
- * `QuillSchema` shape.
989
+ * `ui` hints (title / group / compact / multiline). The single
990
+ * field-metadata surface — drives form editors and LLM/MCP consumers
991
+ * alike. Key order in `fields`/`properties` is declaration order — the
992
+ * ordering contract. Returns the `QuillSchema` shape.
564
993
  */
565
994
  readonly schema: QuillSchema;
566
995
  }
@@ -574,76 +1003,64 @@ export class Quillmark {
574
1003
  [Symbol.dispose](): void;
575
1004
  constructor();
576
1005
  /**
577
- * Open an iterative render session for `doc` against `quill`'s backend.
1006
+ * Open a live render session for `doc` against `quill`'s backend.
578
1007
  */
579
- open(quill: Quill, doc: Document): RenderSession;
1008
+ open(quill: Quill, doc: Document): LiveSession;
580
1009
  /**
581
1010
  * Render `doc` against `quill` in one shot. Convenience over `open` +
582
- * `RenderSession.render`: an unset `output_format` falls back to the
1011
+ * `LiveSession.render`: an unset `output_format` falls back to the
583
1012
  * backend's first supported format.
584
1013
  */
585
1014
  render(quill: Quill, doc: Document, opts?: RenderOptions | null): RenderResult;
586
1015
  /**
587
1016
  * The output formats `quill`'s backend can emit. Static capability —
588
- * resolves the backend but compiles nothing. Throws `UnsupportedBackend`
1017
+ * resolves the backend but compiles nothing. Throws `engine::backend_not_found`
589
1018
  * if no registered backend matches the quill's declared backend.
590
1019
  */
591
1020
  supportedFormats(quill: Quill): OutputFormat[];
592
1021
  /**
593
- * `true` iff `quill`'s backend can paint sessions to a canvas. Asked of
594
- * the real backend; `false` when the backend is unsupported or non-canvas.
595
- * Use as a precondition probe before mounting a canvas-based preview UI.
1022
+ * Pre-session hint: `true` iff `quill`'s backend can paint sessions to a
1023
+ * canvas, derived from the backend's output formats; `false` when the
1024
+ * backend is unsupported. Use as a cheap precondition probe before mounting
1025
+ * a canvas-based preview UI; the authoritative answer is the session's
1026
+ * `supportsCanvas` getter once `open()` has been called.
596
1027
  */
597
1028
  supportsCanvas(quill: Quill): boolean;
598
1029
  }
599
1030
 
600
1031
  /**
601
- * Iterative render handle backed by an immutable compiled snapshot.
602
- *
603
- * **Empty documents.** A zero-page document yields a valid session
604
- * (`pageCount === 0`); `paint(ctx, 0)` or `pageSize(0)` throws with
605
- * `"page index 0 out of range (pageCount=0)"`. Branch on `pageCount === 0`
606
- * rather than catching the error.
1032
+ * Export a canonical `Content` content to its markdown projection — the pure
1033
+ * on-demand codec behind `exportMarkdown(card.body)`. Throws if `rt` is not a
1034
+ * canonical content.
607
1035
  */
608
- export class RenderSession {
609
- private constructor();
610
- free(): void;
611
- [Symbol.dispose](): void;
612
- /**
613
- * Page dimensions in Typst points (1 pt = 1/72 inch).
614
- * Throws if the backend has no canvas painter or `page` is out of range.
615
- */
616
- pageSize(page: number): PageSize;
617
- /**
618
- * Paint `page` into a `CanvasRenderingContext2D` or
619
- * `OffscreenCanvasRenderingContext2D`. The painter owns
620
- * `canvas.width`/`height` (no `clearRect` needed); consumers own
621
- * `canvas.style.*`. If `layoutScale * densityScale` exceeds 16384 px
622
- * per side, `densityScale` is clamped — detect via `PaintResult.pixelWidth`.
623
- *
624
- * Throws if the backend has no canvas painter, `page` is out of range,
625
- * `ctx` is the wrong type, or either scale is non-finite or `<= 0`.
626
- */
627
- paint(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, page: number, opts: PaintOptions | undefined): PaintResult;
628
- render(opts?: RenderOptions | null): RenderResult;
629
- /**
630
- * The backend that produced this session (e.g. `"typst"`).
631
- */
632
- readonly backendId: string;
633
- readonly pageCount: number;
634
- /**
635
- * `true` iff `paint` and `pageSize` will succeed for this session. The
636
- * backend's canvas capability, captured at open time.
637
- */
638
- readonly supportsCanvas: boolean;
639
- /**
640
- * Non-fatal diagnostics emitted when opening the session. Also appended
641
- * to `RenderResult.warnings` on each `render()` call.
642
- */
643
- readonly warnings: Diagnostic[];
644
- }
1036
+ export function exportMarkdown(rt: Content): string;
1037
+
1038
+ /**
1039
+ * Import a markdown string to a canonical `Content` content — the pure,
1040
+ * document-free codec. Pair with `install(addr, importMarkdown(md))` to spell
1041
+ * the cold (anchor-losing) write at the call site; prefer `revise` for edit
1042
+ * semantics. Throws on an over-nested input.
1043
+ */
1044
+ export function importMarkdown(markdown: string): Content;
645
1045
 
646
1046
  /**
647
1047
  * Initialize the WASM module with panic hooks for better error messages
648
1048
  */
649
1049
  export function init(): void;
1050
+
1051
+ /**
1052
+ * Map a base content position through a `delta` to its new position — the pure
1053
+ * position-mapping codec an editor bridge composes to hold a caret stable
1054
+ * across a `revise`. `assoc` decides the side of a same-position insertion
1055
+ * (`"after"` moves past it). Throws on a malformed `delta`.
1056
+ */
1057
+ export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
1058
+
1059
+ /**
1060
+ * Rebase `markdown` onto a `base` content — the pure, document-free twin of
1061
+ * `revise`: cold-import + `diff_import`, returning the new `content` and the
1062
+ * text `delta` (surviving anchors rebased). Use it to compute a revise without
1063
+ * a document in hand; `revise(addr, md)` fuses this with the store for
1064
+ * atomicity. Throws on an over-nested markdown input or a non-content `base`.
1065
+ */
1066
+ export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };