@quillmark/wasm 0.123.1 → 0.125.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/CHANGELOG.md CHANGED
@@ -1,5 +1,213 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.125.0 - 2026-10-12
4
+
5
+ Upgrade path: [0.124 → 0.125](docs/migrations/0.124-to-0.125.md). Every Typst
6
+ plate migrates: the helper is version 0.2.0, imported `as qm`. Stored documents
7
+ load as before, under the same storage tag.
8
+
9
+ ### Regions and the helper
10
+
11
+ - feat(typst)!: **the helper is a module imported as `qm`, at version 0.2.0**,
12
+ and every other import fails the render as `typst::helper_import`.
13
+ `field-region` is `qm.region`, `ink(x).f` and `display(..)` are
14
+ `qm.ink(x, "f", ..)`, and every other member reads under `qm.`. No
15
+ dictionary carries `$ink`; `data`, every card and every typed dictionary
16
+ carry `$path`. (#2157)
17
+ - feat(typst)!: **regions come from three declared producers**: ink the helper
18
+ generates, a `qm.region` claim and a form widget. A value the plate prints
19
+ itself, `#data.subject` included, surfaces no region. (#2157)
20
+ - feat(typst)!: **a claim takes the text between its markers that has no
21
+ source position of its own**, list bullets included, and no header, footer,
22
+ background, page number, footnote or float it crosses; a claim opened
23
+ inside one of those takes its ink. `typst::unclosed_field_region` is
24
+ `typst::unclosed_region`. (#2157, #2163)
25
+ - feat(core,cli,wasm): **`LiveSession::coverage` and `quillmark check
26
+ --regions` list the filled fields no click reaches**, a printed `0` or
27
+ `false` included. (#2157, #2160)
28
+
29
+ ### Sections
30
+
31
+ - feat(core)!: **a card frames its fields in titled panels under
32
+ `sections:`**, and `ui.group` / `ui.groups` warn `quill::deprecated_groups`.
33
+ `schema()` still emits `ui.groups`, lists a grouped card's fields in display
34
+ order, and gives an object alone in its group `ui.layout: flat`. An empty
35
+ section fails the load. (#2156)
36
+
37
+ ### Validation and typed writes
38
+
39
+ - feat(core)!: **`Quill::validate` warns `backend::declined_construct`, the
40
+ render's own diagnostic**, where it warned `validation::declined_construct`,
41
+ which is gone. (#2147)
42
+ - feat(core,wasm,python)!: **a typed `set` / `setAll` returns the
43
+ `parse::dropped_construct` of each `richtext` string it writes**, at the
44
+ string's path, and `addCard` its fields' ahead of its body's. (#2147, #2160)
45
+ - fix(acroform): **a `form@0.1.0` file gets the version message**, not a
46
+ field-shape error. (#2160)
47
+
48
+ ### Markdown
49
+
50
+ - fix(core)!: **a backtick line whose info string holds a backtick opens no
51
+ code fence**, as CommonMark reads it, so a card block under it opens its
52
+ card. (#2147)
53
+ - fix(content)!: **a newline beside a hard break imports as no space, a line
54
+ whose content all drops imports as no line, and U+0000 reads as U+FFFD.**
55
+ Content stored by 0.124 keeps its space. (#2164)
56
+ - fix(content)!: **a raw tag inside a footnote definition warns nothing beside
57
+ the definition's `footnote`.** (#2160)
58
+ - fix(content): **a replaced line pairs by its word diff whatever its word
59
+ count.** (#2147)
60
+
61
+ ### Tables
62
+
63
+ - fix(typst)!: **a placed table aligns its cells as an unplaced one does**
64
+ under the plate's `table.align` and a vertical `table.cell` rule. (#2147)
65
+ - fix(content)!: **a table's `align`, `widths` or `headless` outside its set
66
+ rests as written and reads as absent**, where normalization removed it.
67
+ (#2147)
68
+ - fix(typst): **`keep` holds its run whatever the element registry holds.**
69
+ (#2147)
70
+
71
+ ### Revise
72
+
73
+ - refactor(core,wasm)!: **`droppedAnchors` holds `DocumentAnchor`s, `{ id,
74
+ path, line }`**, matched at the exact address, and an anchor dropped from
75
+ content stored out of canonical order is named. `DocumentAnchor` exports
76
+ from the WASM package root. (#2147)
77
+
78
+ ### Rust
79
+
80
+ - refactor(content)!: **`ImportWarning` is a struct, and
81
+ `Container::Element` holds a validated `carrier::Element`.** The typed
82
+ writer's `set` / `set_all` return `Vec<Diagnostic>`, and
83
+ `SessionHandle::update` takes the session's `QuillConfig`. (#2147)
84
+
85
+ ### CLI and packaging
86
+
87
+ - feat(typst,cli): **`quillmark --version` names the linked Typst**, 0.15.
88
+ (#2162)
89
+ - **`@quillmark/wasm` ships a CycloneDX SBOM of its crate graph**, and every
90
+ CI action is pinned by commit SHA. (#2132)
91
+
92
+ ## v0.124.0 - 2026-10-10
93
+
94
+ Upgrade path: [0.123 → 0.124](docs/migrations/0.123-to-0.124.md).
95
+
96
+ ### Elements
97
+
98
+ - feat(content,typst)!: **a `qm-<name>` element is stored content**, where its
99
+ tags dropped. A pair of tag lines wraps the blocks between them and keeps its
100
+ attributes as strings, and a line holding only `qm-*` tags is a tag line
101
+ wherever it stands outside a heading, a mark or a link. Export writes each
102
+ tag on its own line, and an element around nothing as its pair on one line.
103
+ An element left open, self-closing or inside a line of text drops and warns
104
+ `parse::dropped_construct` as `qm-<name>`. (#2096, #2141)
105
+ - feat(typst)!: **a Typst quill renders an element through the renderer its
106
+ plate registers in the helper's `elements` state**, called `(attrs, body)`,
107
+ and the built-in `keep` holds its run on one page. An element no renderer
108
+ takes, `keep` aside, warns `typst::unregistered_element` at its field, and
109
+ `quillmark validate` fails an `example.md` holding one. (#2096, #2140)
110
+ - feat(core,wasm)!: **the content types gain an element member**:
111
+ `Container::Element` and `Invariant::BadElement` in Rust, and `{ container:
112
+ "element" }` in WASM. (#2096)
113
+
114
+ ### Tables
115
+
116
+ - feat(typst)!: **a `qm-table` wrapper lays out its table**: `widths` weights
117
+ the columns, `align` places the table, and a bare `headless` draws the first
118
+ row as a body row, with no `table.header`. (#2096, #2142)
119
+ - feat(content,typst)!: **a `qm-cell` pair around a table cell's whole content
120
+ aligns that cell** through `align` and `valign`, and a stored cell's `align`
121
+ or `valign` renders. (#2134, #2139)
122
+ - fix(content)!: **a `|` in a table cell's code span or link destination stays
123
+ in its cell**; export escapes it, where it split the cell on re-import.
124
+ (#2134)
125
+
126
+ ### Task lists and footnotes
127
+
128
+ - feat(content,typst)!: **`- [ ]` and `- [x]` render a box**, ticked for
129
+ `[x]`, and import as the list item's `checked`, where the brackets were text.
130
+ A plate restyles the box through the helper's `tasks` state; `\[ ]` keeps the
131
+ text. (#2135)
132
+ - feat(content)!: **a footnote definition drops with its note and warns
133
+ `parse::dropped_construct` (`footnote`)**, where it silently defined a link.
134
+ (#2135)
135
+
136
+ ### Raw HTML
137
+
138
+ - feat(content)!: **a markdown import reports what it drops**: a raw tag, the
139
+ block CommonMark runs it to, and a `<u>` left open each warn
140
+ `parse::dropped_construct`, at the body or at a `richtext` field, and
141
+ `quillmark validate` fails an `example.md` holding one. (#2096)
142
+ - fix(content)!: **an inline `<u>` pairs as HTML pairs it**, closing the
143
+ innermost open `<u>` and never a `**`. (#2096)
144
+ - fix(content)!: **a fenced block holding `<!-- a --> b` keeps its line, text
145
+ after a block nested in a tight list item opens its own line, and a line of
146
+ tags after a table's rows ends the table.** (#2096)
147
+
148
+ ### Whitespace
149
+
150
+ - fix(typst): **typed spaces keep their width on the page**: a line's leading
151
+ spaces indent it, and a run of spaces inside a line, a mark's close between
152
+ them included, is as wide as the spaces in it. (#2143, #2144)
153
+ - fix(content)!: **a paragraph whose first line holds only tags that drop opens
154
+ without a leading space.** (#2141)
155
+
156
+ ### Revise
157
+
158
+ - feat(core,wasm,python): **`reviseDocument` / `revise_document` replace a
159
+ whole document with markdown and keep its comment anchors, island ids and
160
+ `$ext` where they match**, and `toAnnotatedMarkdown` /
161
+ `to_markdown_annotated` show where each anchor sits. (#2096)
162
+ - fix(content)!: **every revise keeps the island ids it can match**, and mints
163
+ past the field's highest, where it re-minted each by position. (#2096, #2144)
164
+ - feat(core,wasm,python)!: **an import and a revise return their warnings**:
165
+ `from_markdown` returns `Imported`, a revise `Revised { delta, warnings }`,
166
+ and `addCard` / `add_card` a list of diagnostics. (#2096)
167
+ - fix(core): **a revise keeps the YAML comments nested in a field it
168
+ revises.** (#2144)
169
+
170
+ ### Validation and rendering
171
+
172
+ - feat(core)!: **`Quill::validate` warns `validation::declined_construct` at a
173
+ field holding a construct the backend will not draw**, and the acroform
174
+ backend warns `backend::declined_construct` at render. (#2096)
175
+ - fix(wasm,python)!: **a one-shot render carries the render's warnings
176
+ alone**, not the document's parse and conform warnings. (#2096)
177
+ - fix(typst,cli)!: **`quillmark validate` reads `typst.plate_file` as a render
178
+ does**, refusing it with the render's own `typst::plate_*` diagnostic. (#2096)
179
+
180
+ ### Storage
181
+
182
+ - feat(core)!: **a save writes `quillmark/document@0.124.0`**, the first tag
183
+ that holds an element or a task item's `checked`. Every older row loads as
184
+ before; a 0.123 build refuses a 0.124 row. (#2135)
185
+
186
+ ### Card YAML
187
+
188
+ - fix(core)!: **text after a card-yaml block's root value, and a mapping
189
+ holding two keys of one text, fail the parse** at the offending line, where
190
+ they dropped or overwrote. A tag inside a merge's value warns at the value's
191
+ path. (#2096)
192
+ - fix(core): **the colon hint names the field whose `:` opens a mapping and
193
+ quotes its suggested value.** (#2136, #2144)
194
+
195
+ ### WASM
196
+
197
+ - fix(wasm)!: **a JSON `null` inside a returned value crosses as `null`**, where
198
+ it crossed as `undefined`. (#2096)
199
+ - fix(wasm)!: **an options object inheriting a key its verb does not read from
200
+ a null-prototype object throws `unknown key`**, and one whose read throws is
201
+ refused with the handle left usable. (#2096)
202
+
203
+ ### Base PDF reading
204
+
205
+ - fix(pdf)!: **a hybrid-xref base fails as `pdf::xref_stream`**, a `/Kids` that
206
+ is no array of references and an unreadable annotation fail to parse, and a
207
+ name compares through its `#xx` escapes. An `/Info` reached through a
208
+ reference chain stamps. (#2096)
209
+
210
+
3
211
  ## v0.123.1 - 2026-10-06
4
212
 
5
213
  Upgrade path: [0.123.0 → 0.123.1](docs/migrations/0.123.0-to-0.123.1.md).
package/README.md CHANGED
@@ -190,9 +190,9 @@ const doc = Document.storageVersionOf(content)
190
190
  : Document.fromMarkdown(content);
191
191
  ```
192
192
 
193
- The `schema` value (`quillmark/document@0.116.0`) is the **model version**,
193
+ The `schema` value (`quillmark/document@0.124.0`) is the **model version**,
194
194
  not the running crate version. It is a hand-set constant, bumped only when
195
- the `Document` model itself changes, so every `0.116.x` patch release reads
195
+ the `Document` model itself changes, so every `0.124.x` patch release reads
196
196
  and writes that same value.
197
197
 
198
198
  - **Upgrading is safe.** A newer build reads documents an older build's
@@ -332,7 +332,7 @@ the per-call `_commit*` ABI):
332
332
  const ed = quill.writer(doc); // Rust `quill.writer(doc)` twin; new DocumentWriter(quill, doc) also works
333
333
  ed.set("subject", "Q3 results"); // strict-committed to the schema type
334
334
  ed.setAll({ qty: "3", subject: "Q3" }); // all-or-nothing batch
335
- ed.reviseField("subject", "Q3 **results**"); // typed AND anchor-preserving; returns a Delta
335
+ ed.reviseField("subject", "Q3 **results**"); // typed AND anchor-preserving; returns { delta, warnings }
336
336
  ed.set("titel", "x"); // throws UnknownField: a typo, not a fallback
337
337
  ed.card(2).set("body", "**note**"); // composable card, resolved by its $kind
338
338
  ```
@@ -393,11 +393,12 @@ A document that compiles to zero pages still produces a valid session
393
393
  `pageCount === 0` to render a "no pages to preview" UI rather than relying on
394
394
  the throw.
395
395
 
396
- Their `warnings` differ in reach. `engine.render` returns one list for the
397
- whole pipeline: `doc.warnings` (parse and `conform::*`), then every
398
- `quill.validate(doc)` warning, then the compile's own. A session outlives
399
- the document it opened from, so `session.render` and `session.warnings` carry
400
- the compile's alone — read `doc.warnings` and `quill.validate(doc)` beside them.
396
+ Their `warnings` differ in reach. `engine.render` returns every
397
+ `quill.validate(doc)` warning but `backend::declined_construct`, then the
398
+ compile's own, a decline among them;
399
+ `session.render` and `session.warnings` carry the compile's alone, so read
400
+ `quill.validate(doc)` beside them. Neither carries the load's: those stay on
401
+ `doc.warnings`, and a revise's on its receipt.
401
402
 
402
403
  ### Canvas Preview
403
404
 
@@ -505,8 +506,12 @@ same shape applies to every throw site:
505
506
  - An object argument carrying a key its verb does not read: the render
506
507
  options, `new Engine` options, an `Addr`, a `CardInput`. Every own string
507
508
  key counts, a non-enumerable one and one holding `undefined` included. The
508
- argument must be a plain object, its prototype `null` or `Object.prototype`
509
- of any realm, so a `Map`, a class instance or `Object.create({ … })` throws.
509
+ argument must be a plain object, its prototype `null`, `Object.prototype`
510
+ of any realm, or a null-prototype object whose keys count too, so a `Map`, a
511
+ class instance or `Object.create({ … })` throws. A payload item, the fields
512
+ object `storeFields` takes, and a `$ext` or `$seed` value read their own
513
+ enumerable keys alone: a non-enumerable or inherited key there is neither
514
+ read nor refused.
510
515
  The diagnostic names the key or what was passed, and carries no `code`: it
511
516
  is a call site to fix, not a condition to route on.
512
517
  - `engine.render(quill, parsed)` against a quill whose *name* differs
package/core/wasm.d.ts CHANGED
@@ -105,10 +105,17 @@ export type ContentLineKind =
105
105
  export type ContentContainer =
106
106
  | {
107
107
  container: "list_item";
108
- attrs: { ordered: boolean; start: number; ordinal: number };
108
+ /** `checked` is present on a task item: `true` ticked, `false` open. */
109
+ attrs: { ordered: boolean; start: number; ordinal: number; checked?: boolean };
109
110
  instance?: number;
110
111
  }
111
- | { container: "quote"; instance?: number };
112
+ | { container: "quote"; instance?: number }
113
+ | { container: "element"; attrs: ElementAttrs; instance?: number };
114
+
115
+ /** A carrier element's name and attributes, one bag: `$name` is the part of
116
+ * its `qm-<name>` tag after the prefix, and every other key an attribute as
117
+ * written. */
118
+ export type ElementAttrs = { $name: string; [attr: string]: string };
112
119
 
113
120
  /** A mark over char range `[start, end)` into `Content.text`. `type` is a
114
121
  * closed set, so `type === "link"` narrows `attrs` to `{ url: string }` with no
@@ -131,20 +138,34 @@ export type ContentMarkKind =
131
138
  export interface TableCell {
132
139
  text: string;
133
140
  marks: ContentMark[];
141
+ /** The cell's horizontal alignment; absent is its column's. */
142
+ align?: "left" | "center" | "right";
143
+ /** The cell's vertical alignment; absent is the quill's. */
144
+ valign?: "top" | "middle" | "bottom";
134
145
  }
135
146
 
136
147
  /** `props` of a `type: "table"` island: a pipe table normalized to one column
137
- * count that `header`, every row of `rows`, and `aligns` all share. */
148
+ * count that `header`, every row of `rows`, `aligns` and `widths` all share.
149
+ * A layout key at its default is absent; one holding a value outside its type
150
+ * stays as written and reads as absent. */
138
151
  export interface TableProps {
139
152
  header: TableCell[];
140
153
  rows: TableCell[][];
141
154
  /** Per-column alignment, one entry per column. */
142
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
+ /** `true` draws `header` as the first body row; absent is a header row. */
163
+ headless?: true;
143
164
  }
144
165
 
145
166
  /** `props` of a `type: "image"` island. Stores and round-trips; no backend
146
167
  * typesets one, and a render that holds one warns `backend::declined_construct`,
147
- * because what `url` names is undecided. */
168
+ * because no backend resolves `url`. */
148
169
  export interface ImageProps {
149
170
  url: string;
150
171
  alt: string;
@@ -205,6 +226,57 @@ export interface Delta {
205
226
  ops: ({ retain: number } | { insert: string } | { delete: number })[];
206
227
  }
207
228
 
229
+ /**
230
+ * The receipt of a markdown revise (`revise`, `writer.reviseBody`,
231
+ * `writer.reviseField`): the text `delta`, and one `parse::dropped_construct`
232
+ * warning per construct the import dropped, anchored at the revised address.
233
+ */
234
+ export interface Revised {
235
+ delta: Delta;
236
+ warnings: Diagnostic[];
237
+ }
238
+
239
+ /**
240
+ * The receipt of `writer.reviseDocument`. `droppedAnchors` names every prose
241
+ * anchor the write did not carry, at its path and line in the stored document; a table
242
+ * cell's anchors drop unnamed. `warnings` are the parse's, then each revised
243
+ * content field's `parse::dropped_construct`, then the writer's `conform::*`.
244
+ * A field that lands rather than revises, on an inserted card or over no stored
245
+ * content, reports no drop.
246
+ */
247
+ export interface DocumentRevised {
248
+ droppedAnchors: DocumentAnchor[];
249
+ warnings: Diagnostic[];
250
+ }
251
+
252
+ /**
253
+ * The read `toAnnotatedMarkdown` returns: `toMarkdown`'s markdown with a
254
+ * read-only `<qm-anchor ref="…"></qm-anchor>` at each prose anchor's
255
+ * start its line can hold, and every prose anchor of every body and content
256
+ * field, spelled or not; a table cell's anchors are neither. Each names the
257
+ * `path` of its body or field and the text of the `line` its start sits on,
258
+ * island slots removed.
259
+ */
260
+ export interface AnnotatedMarkdown {
261
+ markdown: string;
262
+ anchors: DocumentAnchor[];
263
+ }
264
+
265
+ export interface DocumentAnchor {
266
+ id: string;
267
+ path: string;
268
+ line: string;
269
+ }
270
+
271
+ /**
272
+ * A markdown import (`importMarkdown`): the canonical `content`, and one
273
+ * `parse::dropped_construct` warning per construct it dropped, with no `path`.
274
+ */
275
+ export interface Imported {
276
+ content: Content;
277
+ warnings: Diagnostic[];
278
+ }
279
+
208
280
  /** Which side of a same-position insertion `mapPos` lands a point on. */
209
281
  export type Assoc = "before" | "after";
210
282
 
@@ -374,6 +446,8 @@ export interface Resolved {
374
446
  /** UI layout hints for a single field. Display order is not a hint: key order
375
447
  * in the schema's `fields`/`properties` objects is the ordering contract. */
376
448
  export interface QuillFieldUi {
449
+ /** The id of the card section holding the field, a key of the card's
450
+ * `ui.groups`. */
377
451
  group?: string;
378
452
  compact?: boolean;
379
453
  multiline?: boolean;
@@ -390,18 +464,18 @@ export interface QuillFieldUi {
390
464
  layout?: "table" | "flat";
391
465
  }
392
466
 
393
- /** One entry in a card's `ui.groups` registry: a display-label override for the
394
- * group id (the map key). An empty object carries no override, and the consumer
395
- * derives the label from the id (`memo_for` → "Memo For"). */
467
+ /** One section in a card's `ui.groups`: a display-label override for the
468
+ * section id (the map key). An empty object carries no override, and the
469
+ * consumer derives the label from the id (`memo_for` → "Memo For"). */
396
470
  export interface QuillGroupUi {
397
471
  title?: string;
398
472
  }
399
473
 
400
474
  /** UI layout hints for a card (main or named card kind). */
401
475
  export interface QuillCardUi {
402
- /** The groups a field's `ui.group` may reference, keyed by group id. Key
403
- * order is the display-order contract, as with `fields`. Absent when the
404
- * card declares no groups. */
476
+ /** The card's sections keyed by id, each holding at least one field, which
477
+ * names it in its `ui.group`. Key order is the display-order contract, as
478
+ * with `fields`. Absent when the card declares no sections. */
405
479
  groups?: Record<string, QuillGroupUi>;
406
480
  }
407
481
 
@@ -507,8 +581,8 @@ export interface Diagnostic {
507
581
  message: string;
508
582
  location?: Location;
509
583
  /**
510
- * Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`),
511
- * set on schema validation diagnostics and `undefined` otherwise.
584
+ * Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`)
585
+ * where the diagnostic has a document address, `undefined` otherwise.
512
586
  */
513
587
  path?: string;
514
588
  hint?: string;
@@ -708,15 +782,17 @@ export class Document {
708
782
  /**
709
783
  * **Revise** the richtext value at `addr` from a markdown string: the
710
784
  * default write path. Imports the markdown, diffs it against the current
711
- * value, rebases surviving identity anchors, and returns the text `Delta` an
712
- * editor bridge maps its own positions through (`mapPos`). An absent
713
- * `addr.field` targets the body, an absent `addr.card` the main card; an
714
- * absent field cold-imports from empty.
785
+ * value, rebases surviving identity anchors, and returns the `Revised`
786
+ * receipt: the text `delta` an editor bridge maps its own positions through
787
+ * (`mapPos`), and a `parse::dropped_construct` warning per construct the
788
+ * import dropped, its `path` the address written. An absent `addr.field`
789
+ * targets the body, an absent `addr.card` the main card; an absent field
790
+ * cold-imports from empty.
715
791
  *
716
792
  * Throws on an out-of-range card, a malformed field name, a present
717
793
  * non-content field value, or an over-nested markdown input.
718
794
  */
719
- revise(addr: Addr | string, markdown: string): Delta;
795
+ revise(addr: Addr | string, markdown: string): Revised;
720
796
  /**
721
797
  * The main card's `$seed[kind]` overlay object, or `undefined`. Feeds
722
798
  * `quill.seedCard(kind, overlay)` without serializing the whole main card,
@@ -769,8 +845,18 @@ export class Document {
769
845
  */
770
846
  storeSeedOverlay(card_kind: string, overlay: any): void;
771
847
  /**
772
- * Emit canonical Quillmark Markdown. Round-trip safe: re-parsing the
773
- * result produces a `Document` equal to `self` by value and by type.
848
+ * `toMarkdown` with each prose anchor of every body and content field
849
+ * spelled read-only at its start where its line can hold the tag, and
850
+ * listed with its field's path and line either way. An import drops every
851
+ * tag, so `writer.reviseDocument` with the markdown keeps the anchors it keeps
852
+ * with `toMarkdown`'s.
853
+ */
854
+ toAnnotatedMarkdown(): AnnotatedMarkdown;
855
+ /**
856
+ * Emit canonical Quillmark Markdown. Re-parsing the result produces a
857
+ * `Document` equal to `self` by value and by type, but for content: a body
858
+ * or content field holds what its markdown spells, so its anchors drop and
859
+ * its island ids re-mint. `toStored` keeps both.
774
860
  */
775
861
  toMarkdown(): string;
776
862
  /**
@@ -794,7 +880,7 @@ export class Document {
794
880
  * The non-fatal diagnostics of the load that produced this document: parse
795
881
  * warnings, plus `conform::*` warnings when it came through `quill.parse`.
796
882
  * Session state, not document value: `equals` and the storage DTO exclude
797
- * it, and `fromStored` / `loadStored` clear it.
883
+ * it, and `fromStored` / `loadStored` / `writer.reviseDocument` clear it.
798
884
  */
799
885
  readonly warnings: Diagnostic[];
800
886
  }
@@ -885,9 +971,10 @@ export class Quill {
885
971
  */
886
972
  toTree(): Map<string, Uint8Array>;
887
973
  /**
888
- * Validate `doc` against this quill's schema, returning every diagnostic
889
- * (empty when the document is valid). Forwards the canonical
890
- * `validation::*` diagnostics the engine emits.
974
+ * Validate `doc` against this quill's schema, returning every diagnostic:
975
+ * an error blocks a render, a warning does not. Forwards the engine's
976
+ * `validation::*`, a markdown string's `parse::dropped_construct` and the
977
+ * backend's `backend::declined_construct`.
891
978
  */
892
979
  validate(doc: Document): Diagnostic[];
893
980
  /**
@@ -936,18 +1023,20 @@ export function exportMarkdown(rt: Content): string;
936
1023
  export function formatDiagnostic(diagnostic: Diagnostic): string;
937
1024
 
938
1025
  /**
939
- * Serialize structured [`DocPathSeg`] segments back to the canonical path
1026
+ * Serialize structured `DocPathSeg` segments back to the canonical path
940
1027
  * string: the inverse of `parseDocPath`. Throws on a segment array the
941
1028
  * deserializer rejects, and on an empty one.
942
1029
  */
943
1030
  export function formatDocPath(segs: DocPathSeg[]): string;
944
1031
 
945
1032
  /**
946
- * Import a markdown string to canonical `Content`: the pure, document-free
947
- * codec. `overwrite(addr, importMarkdown(md))` spells the cold, anchor-losing
948
- * write; prefer `revise` for edit semantics. Throws on an over-nested input.
1033
+ * Import a markdown string: the pure, document-free codec. Returns the
1034
+ * canonical `content` and a `parse::dropped_construct` warning per construct
1035
+ * the import dropped, with no `path`. `overwrite(addr, importMarkdown(md).content)`
1036
+ * spells the cold, anchor-losing write; prefer `revise` for edit semantics.
1037
+ * Throws on an over-nested input.
949
1038
  */
950
- export function importMarkdown(markdown: string): Content;
1039
+ export function importMarkdown(markdown: string): Imported;
951
1040
 
952
1041
  /**
953
1042
  * Whether `content` satisfies the `inline` constraint of `richtext` and
@@ -995,7 +1084,7 @@ export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
995
1084
 
996
1085
  /**
997
1086
  * Parse a canonical document-model `Diagnostic.path` (`cards.<kind>[<i>].<field>`,
998
- * `main.body`, `recipients[0].name`) into structured [`DocPathSeg`] segments, so
1087
+ * `main.body`, `recipients[0].name`) into structured `DocPathSeg` segments, so
999
1088
  * a consumer routes on segments instead of regexing the string. Throws on a
1000
1089
  * malformed path.
1001
1090
  */
@@ -1003,11 +1092,12 @@ export function parseDocPath(path: string): DocPathSeg[];
1003
1092
 
1004
1093
  /**
1005
1094
  * Rebase `markdown` onto a `base` content: the document-free twin of `revise`,
1006
- * returning the new `content` and the text `delta` (offsets are USV indices into
1007
- * `Content.text`, surviving anchors rebased). Throws on an over-nested markdown
1008
- * input or a non-content `base`.
1095
+ * returning the new `content`, the text `delta` (offsets are USV indices into
1096
+ * `Content.text`, surviving anchors rebased), and the import's
1097
+ * `parse::dropped_construct` warnings, with no `path`. Throws on an
1098
+ * over-nested markdown input or a non-content `base`.
1009
1099
  */
1010
- export function rebase(base: Content, markdown: string): { content: Content; delta: Delta };
1100
+ export function rebase(base: Content, markdown: string): { content: Content; delta: Delta; warnings: Diagnostic[] };
1011
1101
 
1012
1102
  /**
1013
1103
  * Runs at instantiation, so a Rust panic reaches the console as a stack trace
@@ -1028,6 +1118,7 @@ export interface InitOutput {
1028
1118
  readonly document__readerGet: (a: number, b: number, c: number, d: number) => void;
1029
1119
  readonly document__readerGetContent: (a: number, b: number, c: number, d: number) => void;
1030
1120
  readonly document__readerGetContentAt: (a: number, b: number, c: number, d: number, e: number) => void;
1121
+ readonly document__reviseDocument: (a: number, b: number, c: number, d: number, e: number) => void;
1031
1122
  readonly document__reviseField: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
1032
1123
  readonly document_applyChange: (a: number, b: number, c: number, d: number) => void;
1033
1124
  readonly document_bodyMarkdown: (a: number, b: number, c: number) => void;
@@ -1064,6 +1155,7 @@ export interface InitOutput {
1064
1155
  readonly document_storeField: (a: number, b: number, c: number, d: number) => void;
1065
1156
  readonly document_storeFields: (a: number, b: number, c: number, d: number) => void;
1066
1157
  readonly document_storeSeedOverlay: (a: number, b: number, c: number, d: number, e: number) => void;
1158
+ readonly document_toAnnotatedMarkdown: (a: number, b: number) => void;
1067
1159
  readonly document_toMarkdown: (a: number, b: number) => void;
1068
1160
  readonly document_toStored: (a: number, b: number) => void;
1069
1161
  readonly document_warnings: (a: number, b: number) => void;