@quillmark/wasm 0.114.0 → 0.115.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 +172 -0
- package/README.md +6 -4
- package/core/wasm.d.ts +36 -41
- package/core/wasm.js +12 -0
- package/core/wasm_bg.wasm +0 -0
- package/core/wasm_bg.wasm.d.ts +1 -0
- package/package.json +1 -1
- package/render/wasm.d.ts +36 -41
- package/render/wasm.js +12 -0
- package/render/wasm_bg.wasm +0 -0
- package/render/wasm_bg.wasm.d.ts +1 -0
- package/runtime/runtime.d.ts +3 -4
- package/runtime/runtime.js +10 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,177 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.115.0 - 2026-09-22
|
|
4
|
+
|
|
5
|
+
Upgrade path: [0.114 → 0.115](docs/migrations/0.114-to-0.115.md).
|
|
6
|
+
|
|
7
|
+
### The quill authoring contract
|
|
8
|
+
|
|
9
|
+
- feat(core)!: **a `matrix` member mapping takes the ordinary ladder.** Two
|
|
10
|
+
rules decided what a stored member spelling meant — a bare scalar is the tick,
|
|
11
|
+
and a mapping naming no `held` is held — and the second retires. A member is
|
|
12
|
+
an `object` of a synthesized `held: {type: boolean, default: false}` beside
|
|
13
|
+
its columns, so a mapping is the member object already and the tick it names
|
|
14
|
+
no key for resolves as every other absent cell does, to its `default:`. The
|
|
15
|
+
old rule overrode that ladder for one key in one type, and bought a spelling
|
|
16
|
+
convenience for it: two mappings one key apart meant opposite ticks, the
|
|
17
|
+
shorter one ticked, and any producer building the member object without
|
|
18
|
+
conforming — an editor writing a column before the tick, a serde-built
|
|
19
|
+
payload, `{}` — set a tick it never wrote. `{ detail: X }` is now unheld with
|
|
20
|
+
its detail retained, which is what `{ held: false, detail: X }` already meant;
|
|
21
|
+
the bare scalar and every mapping naming `held` are unchanged. What remains is
|
|
22
|
+
exactly `conform_variant`'s bare-scalar rule, so `SCHEMAS.md` §Matrix lists
|
|
23
|
+
three things as the type's own rather than four. The flip is silent — the
|
|
24
|
+
value is well-typed under both readings, and a defaultless column's
|
|
25
|
+
`validation::must_fill` leaves with the tick it was gated on — so the upgrade
|
|
26
|
+
guide leads with it. (#1860)
|
|
27
|
+
- feat(core): **a typed dictionary's property opens a variant world.**
|
|
28
|
+
`variants:` was card-level, so a conditional block had to sit at the top of a
|
|
29
|
+
card even where the fields it governs belong to a dictionary. The rule was
|
|
30
|
+
never about depth: what stays one level deep is the gap between a field's
|
|
31
|
+
*declared* shape and its *live* one, and a dictionary's own shape is the
|
|
32
|
+
schema's — so a world opened there deepens the address and leaves the gap
|
|
33
|
+
alone. `header.classification` reads, binds, branches and strands exactly as
|
|
34
|
+
`classification` does, one step down. Every other position would deepen the
|
|
35
|
+
gap and still refuses one: an array element, whose liveness would vary per
|
|
36
|
+
index against a form bound once; a matrix column, inside a grid the page
|
|
37
|
+
prints in full; and another variant's cell, which would make the plate's one
|
|
38
|
+
branch a tree. `quill::variant_placement` now names the position carrying the
|
|
39
|
+
ban rather than restating the old rule, and the ban is **sticky** — an object
|
|
40
|
+
inside any of them inherits it instead of laundering a world into a position
|
|
41
|
+
that cannot hold one. The surfaces that dispatch on the container before
|
|
42
|
+
walking the type needed nothing (coercion, validation, the render floor,
|
|
43
|
+
conditional `must_fill`, the transform schema, `acroform` binding); the three
|
|
44
|
+
that scanned card fields now descend, so `validation::out_of_variant` finds a
|
|
45
|
+
strand at `main.header.classification.controlled_by`, the blueprint seats a
|
|
46
|
+
world's cells — live or commented — at the dictionary's indent, and the seed
|
|
47
|
+
resolves a nested discriminant before walking its field set.
|
|
48
|
+
- feat(core)!: **`ui.layout: table` contracts its columns.** The key was checked
|
|
49
|
+
for its outer shape alone — `type: array` whose `items` is an `object` — so a
|
|
50
|
+
row holding a container loaded clean and no editor could draw it: the author
|
|
51
|
+
wrote a key, the loader took it, and a surface ignored it with no signal at
|
|
52
|
+
either end. A column that is not a leaf is now
|
|
53
|
+
`quill::table_column_not_flat`, naming the column (`appendices[].entries`)
|
|
54
|
+
rather than the field. This splits the key along a seam it always had: a
|
|
55
|
+
**contract** that every column is a leaf, settled at load, and a **request**
|
|
56
|
+
to draw a grid, which an editor still declines for a width or a cell renderer
|
|
57
|
+
it does not have. Shape leaves the decline list, since shape was never a
|
|
58
|
+
consumer's judgement to make. The boundary is containment, not height — prose
|
|
59
|
+
is a column whatever its `inline`, because how tall a cell renders is the
|
|
60
|
+
editor's call and what it contains is not. `FieldType::is_leaf` is the one
|
|
61
|
+
definition, an exhaustive match a new type cannot skip, and `acroform`'s
|
|
62
|
+
`is_scalar_or_prose` is gone in favor of it: the same question had two
|
|
63
|
+
answers in two crates. Part of #1859.
|
|
64
|
+
- feat(core,wasm,python): **the empty document has a constructor.**
|
|
65
|
+
`Quill::empty_document()` is `Document::new` under the quill's own reference,
|
|
66
|
+
so the three canonical documents are three constructors beside
|
|
67
|
+
`QuillConfig::blueprint()` and `Quill::seed_document()`, and no caller spells
|
|
68
|
+
`name@version` to reach the one a plate is bound to render. `quiver_test.rs`
|
|
69
|
+
reads it rather than formatting the two-line markdown itself. The bindings
|
|
70
|
+
carry it as `Quill.emptyDocument()` / `Quill.empty_document()`.
|
|
71
|
+
- feat(cli)!: **`quillmark validate` compiles the plate.** It renders the empty
|
|
72
|
+
document, the blueprint and the seed through the quill's backend at the
|
|
73
|
+
backend's first declared format, and reports each failure as a
|
|
74
|
+
`cli::canonical_document_failed` error naming which of the three it is,
|
|
75
|
+
followed by the backend's own diagnostics. The contract that a plate renders
|
|
76
|
+
the empty document was checked for the fixtures quiver alone, and `render`
|
|
77
|
+
with no input file renders the seed, the fullest of the three: a plate that
|
|
78
|
+
reads a blank badly passed both. A quill whose plate does not compile now
|
|
79
|
+
exits `1` where it exited `0`, and a backend that does not resolve is
|
|
80
|
+
`cli::backend_unresolved` followed by the engine's own diagnostic. A render's
|
|
81
|
+
warnings join the `-v` list, and `validate --no-render` is the configuration
|
|
82
|
+
read on its own. Closes #1844.
|
|
83
|
+
|
|
84
|
+
### The content model
|
|
85
|
+
|
|
86
|
+
- refactor(content)!: **an island stores its value, not its provenance.** `Loss`
|
|
87
|
+
had one minting rule — a table cell dropping an inline image's url — and no
|
|
88
|
+
reader: export dispatches on an island's `type`, the Typst emitter and the
|
|
89
|
+
acroform binder never looked, and an `islandOps` `set` stored whatever class
|
|
90
|
+
the op named. Two cells written `| a cat |` and `|  |` mint
|
|
91
|
+
equal `props` and differ only in the markdown behind them, which the
|
|
92
|
+
projection has no syntax to carry back out, so the class survived neither a
|
|
93
|
+
`to_markdown` → `from_markdown` hop nor re-derivation from the row holding it
|
|
94
|
+
— which is why the content fixed point had to be stated modulo it. An
|
|
95
|
+
island's canonical form is `{id, type, props}`, the fixed point is exact, and
|
|
96
|
+
the closed vocabularies cover the four discriminators a projection dispatches
|
|
97
|
+
on. `Loss`, `Island::loss`, `Island::with_loss`, `IslandType::default_loss`,
|
|
98
|
+
`ContentIsland.loss` and `ContentLossClass` are deleted; a stored row spelling
|
|
99
|
+
`loss` opens with the key ignored and absent from what comes back, and a
|
|
100
|
+
`loss` on an authored island or an `islandOps` entry is ignored rather than
|
|
101
|
+
refused. A host that surfaced the class surfaces the drop at the import
|
|
102
|
+
instead, where the markdown it is about is still in hand. Closes #1757.
|
|
103
|
+
- refactor(content,core,wasm,python)!: **a block island's line is a `para`, and
|
|
104
|
+
`LineKind::Island` is gone.** The kind was a second copy of a fact the island
|
|
105
|
+
beside it already carried: markdown writes a `table` as a block and an `image`
|
|
106
|
+
inline, so `IslandType::block_only` settled which line a lone slot sat on and
|
|
107
|
+
`normalize` wrote that answer back over whatever was stored. Every read site
|
|
108
|
+
now asks the island — export already dispatched on the type, the Typst emitter
|
|
109
|
+
already lowered `Island` as `Para`, and the one fact the kind alone still
|
|
110
|
+
answered, "this block renders one line", moves to the new public
|
|
111
|
+
`Content::block_island_at`, which `normalize` reads beside
|
|
112
|
+
`takes_continuations` when it clears a stranded `continues`. Landing a block
|
|
113
|
+
island is two channels rather than three: the delta opens the line, the island
|
|
114
|
+
op fills it, and the `setKind` goes. The TS `ContentLineKind` loses its
|
|
115
|
+
`{ kind: "island" }` arm, so a host branching on it typechecks red; a `setKind`
|
|
116
|
+
still naming `island` lands as the `para` it would have settled to.
|
|
117
|
+
- refactor(core)!: **the storage tag is `quillmark/document@0.115.0`.** The two
|
|
118
|
+
lines above move canonical bytes — the `loss` key for every document holding
|
|
119
|
+
an island, the line kind for every document holding a table — so content
|
|
120
|
+
hashes recompute once on those rows. `DocumentV0_112_0` freezes with a raw
|
|
121
|
+
`body` and the new `DocumentV0_115_0` carries the `CanonicalContent`; the hop
|
|
122
|
+
between them is that body's decode, and `V0_93_0 → V0_112_0` becomes a retag
|
|
123
|
+
now that both trees spell `body` raw. Stored rows need no migration: the
|
|
124
|
+
content decoder reads `island` as `para` wherever it meets it, permanently,
|
|
125
|
+
since the same spelling rests untagged inside every `richtext` field and
|
|
126
|
+
`$seed` overlay, where no hop reaches it. Closes #1647.
|
|
127
|
+
|
|
128
|
+
### Schema, validation and the resolved view
|
|
129
|
+
|
|
130
|
+
- refactor(core)!: **a schema is loaded, not deserialized.** `QuillConfig`,
|
|
131
|
+
`CardSchema` and `FieldSchema` serialize and no longer implement
|
|
132
|
+
`Deserialize`, and `FieldSchema::from_quill_value` is crate-internal. A field
|
|
133
|
+
arrives in two steps — the parse, which folds the sibling keys (`inline:`,
|
|
134
|
+
`values:`, `members:`, `max:`) into the type payload, and the shape walk,
|
|
135
|
+
which rejects an `object` without `properties`, an `array` without `items`,
|
|
136
|
+
an empty `properties` map, and the placements — and only `parse_fields` ran
|
|
137
|
+
both, so a config assembled through serde carried states no `Quill.yaml` can
|
|
138
|
+
reach, absorbed downstream without a diagnostic. Gating the serde path
|
|
139
|
+
instead would have duplicated the walk and collapsed its diagnostic codes
|
|
140
|
+
into `quill::field_parse_error` at that boundary. The way to a config is
|
|
141
|
+
`QuillConfig::from_yaml_with_warnings`, or a `Quill` read off a file tree;
|
|
142
|
+
serialization is untouched, so `schema()`, `schema_yaml()` and every emission
|
|
143
|
+
read the same loaded form they did. Closes #1836.
|
|
144
|
+
- refactor(core,wasm)!: **a matrix roster is one flat mapping; group headings
|
|
145
|
+
are gone.** A block's `group:` was declared in the schema and absent from the
|
|
146
|
+
value, the address and the vocabulary — the blueprint emits
|
|
147
|
+
`matrix<a | b | c>` off one flat key space, so the partition reached the
|
|
148
|
+
visual editor and nowhere else, buying one shared `properties` map for a
|
|
149
|
+
schema concept, its load rules and a reserved key. `members:` is now
|
|
150
|
+
`{id: Title}` directly, `MatrixGroup`, `MATRIX_GROUP_KEY` and the TypeScript
|
|
151
|
+
`QuillMatrixGroup` are deleted, and `FieldType::Matrix` carries an
|
|
152
|
+
`IndexMap<String, String>` read through the new `FieldType::matrix_roster`,
|
|
153
|
+
which replaces `matrix_members`. The projection stops writing `group` onto
|
|
154
|
+
every member, which returns `group` to the column names a quill may declare,
|
|
155
|
+
and `quill::duplicate_matrix_member` retires: the roster is a mapping, so it
|
|
156
|
+
has one slot per id exactly as the stored value does and a duplicate is
|
|
157
|
+
unspellable on both sides. Closes #1851.
|
|
158
|
+
|
|
159
|
+
### The blueprint
|
|
160
|
+
|
|
161
|
+
- feat(core)!: **a dormant variant world is its own cells, commented out.** A
|
|
162
|
+
variant-bearing enum's blueprint named every world it could not show, one
|
|
163
|
+
`# when CUI: controlled_by, poc, category` line each — the one place on the
|
|
164
|
+
surface where a cell was named rather than shown, dropping the position,
|
|
165
|
+
obligation, type, description and example a cell carries. Every world now sits
|
|
166
|
+
under a `# when <MEMBER>:` header in declaration order: the selected world's
|
|
167
|
+
cells live, every other world's the same cells with a `# ` in front, at the
|
|
168
|
+
slot they would occupy live. A reader activates a world by setting the
|
|
169
|
+
discriminant and deleting `# `. One builder and one emitter serve both, so a
|
|
170
|
+
dormant line is byte-for-byte the live line, and the block round-trips through
|
|
171
|
+
`Document::parse` as the comments it is, reaching neither the validator nor
|
|
172
|
+
the render floor. Closes #1846.
|
|
173
|
+
|
|
174
|
+
|
|
3
175
|
## v0.114.0 - 2026-09-18
|
|
4
176
|
|
|
5
177
|
### Schema, validation and the resolved view
|
package/README.md
CHANGED
|
@@ -175,9 +175,9 @@ const doc = Document.storageVersionOf(content)
|
|
|
175
175
|
: Document.fromMarkdown(content);
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
The `schema` value (`quillmark/document@0.
|
|
178
|
+
The `schema` value (`quillmark/document@0.115.0`) is the **model version**,
|
|
179
179
|
not the running crate version. It is a hand-set constant, bumped only when
|
|
180
|
-
the `Document` model itself changes, so every `0.
|
|
180
|
+
the `Document` model itself changes, so every `0.115.x` patch release reads
|
|
181
181
|
and writes that same value.
|
|
182
182
|
|
|
183
183
|
- **Upgrading is safe.** A newer build reads documents an older build's
|
|
@@ -187,8 +187,10 @@ and writes that same value.
|
|
|
187
187
|
canonical on-disk format keeps loading across crate upgrades: there is no
|
|
188
188
|
need to pin old wasm to read old data. The exception is a row a host
|
|
189
189
|
authored a content construct of its own into — a line `kind`, container,
|
|
190
|
-
mark `type
|
|
191
|
-
|
|
190
|
+
mark `type` or island `type` outside the vocabulary. Those are refused from
|
|
191
|
+
0.113 on; see that release's migration guide. An island `loss` is not one of
|
|
192
|
+
them: 0.115 drops the key, so a row spelling one opens whatever the class and
|
|
193
|
+
comes back without it.
|
|
192
194
|
- **Downgrading is not.** `fromStored` rejects an *unknown* (i.e. newer)
|
|
193
195
|
`schema` version rather than guessing at a format it predates. Don't feed
|
|
194
196
|
documents written by a newer build back into an older one.
|
package/core/wasm.d.ts
CHANGED
|
@@ -73,9 +73,9 @@ export interface Content {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/** One `\n`-separated segment of `Content.text`, in order. `kind` is a closed
|
|
76
|
-
* set:
|
|
77
|
-
* spells its payload in `attrs`, so
|
|
78
|
-
* `{ level: number }` with no guard. */
|
|
76
|
+
* set: `island` reads as `para`, and any other role outside it is refused
|
|
77
|
+
* wherever content is decoded. Every role spells its payload in `attrs`, so
|
|
78
|
+
* `kind === "heading"` narrows `attrs` to `{ level: number }` with no guard. */
|
|
79
79
|
export type ContentLine = {
|
|
80
80
|
containers: ContentContainer[];
|
|
81
81
|
/** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
|
|
@@ -87,7 +87,6 @@ export type ContentLineKind =
|
|
|
87
87
|
| { kind: "para" }
|
|
88
88
|
| { kind: "heading"; attrs: { level: number } }
|
|
89
89
|
| { kind: "code"; attrs?: { lang?: string } }
|
|
90
|
-
| { kind: "island" }
|
|
91
90
|
| { kind: "rule" };
|
|
92
91
|
|
|
93
92
|
/** An ancestor block a line nests inside, outermost first. Closed like
|
|
@@ -158,14 +157,10 @@ export interface ImageProps {
|
|
|
158
157
|
alt: string;
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
/** How faithfully the markdown projection can carry an island. */
|
|
162
|
-
export type ContentLossClass = "lossless" | "degraded" | "unrepresentable";
|
|
163
|
-
|
|
164
160
|
/** A structured object occupying one island slot in `Content.text`. `type` is a
|
|
165
161
|
* closed set, so `type === "table"` narrows `props` to `TableProps`. */
|
|
166
162
|
export type ContentIsland = {
|
|
167
163
|
id: string;
|
|
168
|
-
loss: ContentLossClass;
|
|
169
164
|
} & (
|
|
170
165
|
| { type: "table"; props: TableProps }
|
|
171
166
|
| { type: "image"; props: ImageProps }
|
|
@@ -237,10 +232,12 @@ export type MarkOp =
|
|
|
237
232
|
* metadata. `setContinues` sets or clears a line's within-block hard-break flag
|
|
238
233
|
* (`ContentLine.continues`); `continues: true` lands as `false` on line 0, which
|
|
239
234
|
* nothing precedes, on a line whose containers differ from the line above, and
|
|
240
|
-
* on one following a heading,
|
|
241
|
-
* `setKind` lands a kind the line's text contradicts — `
|
|
235
|
+
* on one following a heading, a rule or a block island's line, each a block of
|
|
236
|
+
* one line. `setKind` lands a kind the line's text contradicts — `rule` over
|
|
242
237
|
* prose, `code` over a slot — as `para`, which is what re-importing the line's
|
|
243
|
-
* own markdown yields
|
|
238
|
+
* own markdown yields, and reads an `island` the union does not offer as the
|
|
239
|
+
* `para` that spelling projects. Read the content back to see where an op
|
|
240
|
+
* settled.
|
|
244
241
|
*/
|
|
245
242
|
export type LineOp =
|
|
246
243
|
| { op: "split"; at: number }
|
|
@@ -264,22 +261,16 @@ export type LineOp =
|
|
|
264
261
|
* one `insert` per slot.
|
|
265
262
|
*
|
|
266
263
|
* Deleting an island needs no op: a `delta` that removes its slot drops the
|
|
267
|
-
* island whole
|
|
268
|
-
*
|
|
269
|
-
* island mints a fresh one.
|
|
270
|
-
*
|
|
271
|
-
* A `set` stores the `loss` it is given; nothing re-derives the class from the
|
|
272
|
-
* new `props`.
|
|
264
|
+
* island whole. Re-landing it is an `insert` of the full island under its
|
|
265
|
+
* original id; a pasted copy of a live island mints a fresh one.
|
|
273
266
|
*
|
|
274
267
|
* An island is *inline* (a slot inside a paragraph) or a **block** (that slot
|
|
275
|
-
* alone on
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* `{ op: "setKind", kind: "island" }`. `{ op: "split" }` cannot open that line,
|
|
282
|
-
* since line ops run after island ops.
|
|
268
|
+
* alone on its own line), and the type settles which: markdown writes a `table`
|
|
269
|
+
* as a block and an image inline. Both lines read `kind: "para"` — the block is
|
|
270
|
+
* the slot's markup, not a role the line carries — so landing a block island is
|
|
271
|
+
* two channels: `delta` inserts the `\n` that opens the line, `islandOps`
|
|
272
|
+
* inserts the slot. `{ op: "split" }` cannot open that line, since line ops run
|
|
273
|
+
* after island ops.
|
|
283
274
|
*
|
|
284
275
|
* A `table` has no inline placement: markdown writes it as a block, so an
|
|
285
276
|
* `insert` whose `at` is not an empty line throws, as does a `set` retyping an
|
|
@@ -398,9 +389,10 @@ export interface QuillFieldUi {
|
|
|
398
389
|
* conventional label of its own. */
|
|
399
390
|
blank_title?: string;
|
|
400
391
|
/** The control the field asks for, where the shape admits more than one.
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
392
|
+
* `"table"` is valid only on an `array` whose `items` is an `object`.
|
|
393
|
+
* Every column is a leaf, a contract refused at load as
|
|
394
|
+
* `quill::table_column_not_flat`. Drawing the grid is a request: a
|
|
395
|
+
* consumer that cannot falls back to its own choice for the type. */
|
|
404
396
|
layout?: "table";
|
|
405
397
|
}
|
|
406
398
|
|
|
@@ -460,16 +452,17 @@ export interface QuillFieldSchema {
|
|
|
460
452
|
/** The closed set of allowed values. Required on `type: "enum"`, and valid
|
|
461
453
|
* nowhere else. */
|
|
462
454
|
values?: string[];
|
|
463
|
-
/** Per-member field sets on a
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
* `{value: <member>, …that member's
|
|
455
|
+
/** Per-member field sets on a `type: "enum"` field that is a card's own
|
|
456
|
+
* field or a typed dictionary's property, keyed by member: the fields that
|
|
457
|
+
* exist only where the discriminant holds that member. Declaring it makes
|
|
458
|
+
* the field rest as a container, `{value: <member>, …that member's
|
|
459
|
+
* fields}`, rather than a bare string. */
|
|
467
460
|
variants?: Record<string, Record<string, QuillFieldSchema>>;
|
|
468
461
|
/** The roster of a `type: "matrix"` field, required there and valid
|
|
469
|
-
* nowhere else:
|
|
462
|
+
* nowhere else: member id to display title, key order the display order.
|
|
470
463
|
* Each member is an object of `held` plus the field's `properties`
|
|
471
464
|
* (the columns), addressed as `<field>.<member id>.held`. */
|
|
472
|
-
members?:
|
|
465
|
+
members?: Record<string, string>;
|
|
473
466
|
ui?: QuillFieldUi;
|
|
474
467
|
properties?: Record<string, QuillFieldSchema>;
|
|
475
468
|
items?: QuillFieldSchema;
|
|
@@ -482,13 +475,6 @@ export interface QuillFieldSchema {
|
|
|
482
475
|
inline?: boolean;
|
|
483
476
|
}
|
|
484
477
|
|
|
485
|
-
/** One block of a `type: "matrix"` roster: an optional display heading and the
|
|
486
|
-
* members under it, member id to display title. Key order is display order. */
|
|
487
|
-
export interface QuillMatrixGroup {
|
|
488
|
-
group?: string;
|
|
489
|
-
values: Record<string, string>;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
478
|
/** Schema entry for the main card or a named card kind. */
|
|
493
479
|
export interface QuillCardSchema {
|
|
494
480
|
description?: string;
|
|
@@ -853,6 +839,14 @@ export class Quill {
|
|
|
853
839
|
* mutation.
|
|
854
840
|
*/
|
|
855
841
|
conform(doc: Document): Diagnostic[];
|
|
842
|
+
/**
|
|
843
|
+
* The empty document: a main card carrying this quill's `$quill` and
|
|
844
|
+
* `$kind: main`, no field committed, no composable card, no body. The
|
|
845
|
+
* type-minimal valid input, and the document a plate is bound to render
|
|
846
|
+
* (the quill authoring contract). `new Document(ref)` is the same document
|
|
847
|
+
* reached by spelling the reference.
|
|
848
|
+
*/
|
|
849
|
+
emptyDocument(): Document;
|
|
856
850
|
/**
|
|
857
851
|
* Build a quill from a file tree. Pure: the declared backend is resolved
|
|
858
852
|
* later, at render time. Accepts a `Map<string, Uint8Array>` or a plain
|
|
@@ -1087,6 +1081,7 @@ export interface InitOutput {
|
|
|
1087
1081
|
readonly quill_backendId: (a: number, b: number) => void;
|
|
1088
1082
|
readonly quill_blueprint: (a: number, b: number) => void;
|
|
1089
1083
|
readonly quill_conform: (a: number, b: number, c: number) => void;
|
|
1084
|
+
readonly quill_emptyDocument: (a: number) => number;
|
|
1090
1085
|
readonly quill_fromTree: (a: number, b: number) => void;
|
|
1091
1086
|
readonly quill_metadata: (a: number, b: number) => void;
|
|
1092
1087
|
readonly quill_parse: (a: number, b: number, c: number, d: number) => void;
|
package/core/wasm.js
CHANGED
|
@@ -1297,6 +1297,18 @@ export class Quill {
|
|
|
1297
1297
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
1298
1298
|
}
|
|
1299
1299
|
}
|
|
1300
|
+
/**
|
|
1301
|
+
* The empty document: a main card carrying this quill's `$quill` and
|
|
1302
|
+
* `$kind: main`, no field committed, no composable card, no body. The
|
|
1303
|
+
* type-minimal valid input, and the document a plate is bound to render
|
|
1304
|
+
* (the quill authoring contract). `new Document(ref)` is the same document
|
|
1305
|
+
* reached by spelling the reference.
|
|
1306
|
+
* @returns {Document}
|
|
1307
|
+
*/
|
|
1308
|
+
emptyDocument() {
|
|
1309
|
+
const ret = wasm.quill_emptyDocument(this.__wbg_ptr);
|
|
1310
|
+
return Document.__wrap(ret);
|
|
1311
|
+
}
|
|
1300
1312
|
/**
|
|
1301
1313
|
* Build a quill from a file tree. Pure: the declared backend is resolved
|
|
1302
1314
|
* later, at render time. Accepts a `Map<string, Uint8Array>` or a plain
|
package/core/wasm_bg.wasm
CHANGED
|
Binary file
|
package/core/wasm_bg.wasm.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export const quill__resolve: (a: number, b: number, c: number) => void;
|
|
|
62
62
|
export const quill_backendId: (a: number, b: number) => void;
|
|
63
63
|
export const quill_blueprint: (a: number, b: number) => void;
|
|
64
64
|
export const quill_conform: (a: number, b: number, c: number) => void;
|
|
65
|
+
export const quill_emptyDocument: (a: number) => number;
|
|
65
66
|
export const quill_fromTree: (a: number, b: number) => void;
|
|
66
67
|
export const quill_metadata: (a: number, b: number) => void;
|
|
67
68
|
export const quill_parse: (a: number, b: number, c: number, d: number) => void;
|
package/package.json
CHANGED
package/render/wasm.d.ts
CHANGED
|
@@ -73,9 +73,9 @@ export interface Content {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/** One `\n`-separated segment of `Content.text`, in order. `kind` is a closed
|
|
76
|
-
* set:
|
|
77
|
-
* spells its payload in `attrs`, so
|
|
78
|
-
* `{ level: number }` with no guard. */
|
|
76
|
+
* set: `island` reads as `para`, and any other role outside it is refused
|
|
77
|
+
* wherever content is decoded. Every role spells its payload in `attrs`, so
|
|
78
|
+
* `kind === "heading"` narrows `attrs` to `{ level: number }` with no guard. */
|
|
79
79
|
export type ContentLine = {
|
|
80
80
|
containers: ContentContainer[];
|
|
81
81
|
/** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
|
|
@@ -87,7 +87,6 @@ export type ContentLineKind =
|
|
|
87
87
|
| { kind: "para" }
|
|
88
88
|
| { kind: "heading"; attrs: { level: number } }
|
|
89
89
|
| { kind: "code"; attrs?: { lang?: string } }
|
|
90
|
-
| { kind: "island" }
|
|
91
90
|
| { kind: "rule" };
|
|
92
91
|
|
|
93
92
|
/** An ancestor block a line nests inside, outermost first. Closed like
|
|
@@ -158,14 +157,10 @@ export interface ImageProps {
|
|
|
158
157
|
alt: string;
|
|
159
158
|
}
|
|
160
159
|
|
|
161
|
-
/** How faithfully the markdown projection can carry an island. */
|
|
162
|
-
export type ContentLossClass = "lossless" | "degraded" | "unrepresentable";
|
|
163
|
-
|
|
164
160
|
/** A structured object occupying one island slot in `Content.text`. `type` is a
|
|
165
161
|
* closed set, so `type === "table"` narrows `props` to `TableProps`. */
|
|
166
162
|
export type ContentIsland = {
|
|
167
163
|
id: string;
|
|
168
|
-
loss: ContentLossClass;
|
|
169
164
|
} & (
|
|
170
165
|
| { type: "table"; props: TableProps }
|
|
171
166
|
| { type: "image"; props: ImageProps }
|
|
@@ -237,10 +232,12 @@ export type MarkOp =
|
|
|
237
232
|
* metadata. `setContinues` sets or clears a line's within-block hard-break flag
|
|
238
233
|
* (`ContentLine.continues`); `continues: true` lands as `false` on line 0, which
|
|
239
234
|
* nothing precedes, on a line whose containers differ from the line above, and
|
|
240
|
-
* on one following a heading,
|
|
241
|
-
* `setKind` lands a kind the line's text contradicts — `
|
|
235
|
+
* on one following a heading, a rule or a block island's line, each a block of
|
|
236
|
+
* one line. `setKind` lands a kind the line's text contradicts — `rule` over
|
|
242
237
|
* prose, `code` over a slot — as `para`, which is what re-importing the line's
|
|
243
|
-
* own markdown yields
|
|
238
|
+
* own markdown yields, and reads an `island` the union does not offer as the
|
|
239
|
+
* `para` that spelling projects. Read the content back to see where an op
|
|
240
|
+
* settled.
|
|
244
241
|
*/
|
|
245
242
|
export type LineOp =
|
|
246
243
|
| { op: "split"; at: number }
|
|
@@ -264,22 +261,16 @@ export type LineOp =
|
|
|
264
261
|
* one `insert` per slot.
|
|
265
262
|
*
|
|
266
263
|
* Deleting an island needs no op: a `delta` that removes its slot drops the
|
|
267
|
-
* island whole
|
|
268
|
-
*
|
|
269
|
-
* island mints a fresh one.
|
|
270
|
-
*
|
|
271
|
-
* A `set` stores the `loss` it is given; nothing re-derives the class from the
|
|
272
|
-
* new `props`.
|
|
264
|
+
* island whole. Re-landing it is an `insert` of the full island under its
|
|
265
|
+
* original id; a pasted copy of a live island mints a fresh one.
|
|
273
266
|
*
|
|
274
267
|
* An island is *inline* (a slot inside a paragraph) or a **block** (that slot
|
|
275
|
-
* alone on
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* `{ op: "setKind", kind: "island" }`. `{ op: "split" }` cannot open that line,
|
|
282
|
-
* since line ops run after island ops.
|
|
268
|
+
* alone on its own line), and the type settles which: markdown writes a `table`
|
|
269
|
+
* as a block and an image inline. Both lines read `kind: "para"` — the block is
|
|
270
|
+
* the slot's markup, not a role the line carries — so landing a block island is
|
|
271
|
+
* two channels: `delta` inserts the `\n` that opens the line, `islandOps`
|
|
272
|
+
* inserts the slot. `{ op: "split" }` cannot open that line, since line ops run
|
|
273
|
+
* after island ops.
|
|
283
274
|
*
|
|
284
275
|
* A `table` has no inline placement: markdown writes it as a block, so an
|
|
285
276
|
* `insert` whose `at` is not an empty line throws, as does a `set` retyping an
|
|
@@ -453,9 +444,10 @@ export interface QuillFieldUi {
|
|
|
453
444
|
* conventional label of its own. */
|
|
454
445
|
blank_title?: string;
|
|
455
446
|
/** The control the field asks for, where the shape admits more than one.
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
447
|
+
* `"table"` is valid only on an `array` whose `items` is an `object`.
|
|
448
|
+
* Every column is a leaf, a contract refused at load as
|
|
449
|
+
* `quill::table_column_not_flat`. Drawing the grid is a request: a
|
|
450
|
+
* consumer that cannot falls back to its own choice for the type. */
|
|
459
451
|
layout?: "table";
|
|
460
452
|
}
|
|
461
453
|
|
|
@@ -515,16 +507,17 @@ export interface QuillFieldSchema {
|
|
|
515
507
|
/** The closed set of allowed values. Required on `type: "enum"`, and valid
|
|
516
508
|
* nowhere else. */
|
|
517
509
|
values?: string[];
|
|
518
|
-
/** Per-member field sets on a
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
* `{value: <member>, …that member's
|
|
510
|
+
/** Per-member field sets on a `type: "enum"` field that is a card's own
|
|
511
|
+
* field or a typed dictionary's property, keyed by member: the fields that
|
|
512
|
+
* exist only where the discriminant holds that member. Declaring it makes
|
|
513
|
+
* the field rest as a container, `{value: <member>, …that member's
|
|
514
|
+
* fields}`, rather than a bare string. */
|
|
522
515
|
variants?: Record<string, Record<string, QuillFieldSchema>>;
|
|
523
516
|
/** The roster of a `type: "matrix"` field, required there and valid
|
|
524
|
-
* nowhere else:
|
|
517
|
+
* nowhere else: member id to display title, key order the display order.
|
|
525
518
|
* Each member is an object of `held` plus the field's `properties`
|
|
526
519
|
* (the columns), addressed as `<field>.<member id>.held`. */
|
|
527
|
-
members?:
|
|
520
|
+
members?: Record<string, string>;
|
|
528
521
|
ui?: QuillFieldUi;
|
|
529
522
|
properties?: Record<string, QuillFieldSchema>;
|
|
530
523
|
items?: QuillFieldSchema;
|
|
@@ -537,13 +530,6 @@ export interface QuillFieldSchema {
|
|
|
537
530
|
inline?: boolean;
|
|
538
531
|
}
|
|
539
532
|
|
|
540
|
-
/** One block of a `type: "matrix"` roster: an optional display heading and the
|
|
541
|
-
* members under it, member id to display title. Key order is display order. */
|
|
542
|
-
export interface QuillMatrixGroup {
|
|
543
|
-
group?: string;
|
|
544
|
-
values: Record<string, string>;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
533
|
/** Schema entry for the main card or a named card kind. */
|
|
548
534
|
export interface QuillCardSchema {
|
|
549
535
|
description?: string;
|
|
@@ -1129,6 +1115,14 @@ export class Quill {
|
|
|
1129
1115
|
* mutation.
|
|
1130
1116
|
*/
|
|
1131
1117
|
conform(doc: Document): Diagnostic[];
|
|
1118
|
+
/**
|
|
1119
|
+
* The empty document: a main card carrying this quill's `$quill` and
|
|
1120
|
+
* `$kind: main`, no field committed, no composable card, no body. The
|
|
1121
|
+
* type-minimal valid input, and the document a plate is bound to render
|
|
1122
|
+
* (the quill authoring contract). `new Document(ref)` is the same document
|
|
1123
|
+
* reached by spelling the reference.
|
|
1124
|
+
*/
|
|
1125
|
+
emptyDocument(): Document;
|
|
1132
1126
|
/**
|
|
1133
1127
|
* Build a quill from a file tree. Pure: the declared backend is resolved
|
|
1134
1128
|
* later, at render time. Accepts a `Map<string, Uint8Array>` or a plain
|
|
@@ -1403,6 +1397,7 @@ export interface InitOutput {
|
|
|
1403
1397
|
readonly quill_backendId: (a: number, b: number) => void;
|
|
1404
1398
|
readonly quill_blueprint: (a: number, b: number) => void;
|
|
1405
1399
|
readonly quill_conform: (a: number, b: number, c: number) => void;
|
|
1400
|
+
readonly quill_emptyDocument: (a: number) => number;
|
|
1406
1401
|
readonly quill_fromTree: (a: number, b: number) => void;
|
|
1407
1402
|
readonly quill_metadata: (a: number, b: number) => void;
|
|
1408
1403
|
readonly quill_parse: (a: number, b: number, c: number, d: number) => void;
|
package/render/wasm.js
CHANGED
|
@@ -1620,6 +1620,18 @@ export class Quill {
|
|
|
1620
1620
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
1621
1621
|
}
|
|
1622
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* The empty document: a main card carrying this quill's `$quill` and
|
|
1625
|
+
* `$kind: main`, no field committed, no composable card, no body. The
|
|
1626
|
+
* type-minimal valid input, and the document a plate is bound to render
|
|
1627
|
+
* (the quill authoring contract). `new Document(ref)` is the same document
|
|
1628
|
+
* reached by spelling the reference.
|
|
1629
|
+
* @returns {Document}
|
|
1630
|
+
*/
|
|
1631
|
+
emptyDocument() {
|
|
1632
|
+
const ret = wasm.quill_emptyDocument(this.__wbg_ptr);
|
|
1633
|
+
return Document.__wrap(ret);
|
|
1634
|
+
}
|
|
1623
1635
|
/**
|
|
1624
1636
|
* Build a quill from a file tree. Pure: the declared backend is resolved
|
|
1625
1637
|
* later, at render time. Accepts a `Map<string, Uint8Array>` or a plain
|
package/render/wasm_bg.wasm
CHANGED
|
Binary file
|
package/render/wasm_bg.wasm.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ export const quill__resolve: (a: number, b: number, c: number) => void;
|
|
|
76
76
|
export const quill_backendId: (a: number, b: number) => void;
|
|
77
77
|
export const quill_blueprint: (a: number, b: number) => void;
|
|
78
78
|
export const quill_conform: (a: number, b: number, c: number) => void;
|
|
79
|
+
export const quill_emptyDocument: (a: number) => number;
|
|
79
80
|
export const quill_fromTree: (a: number, b: number) => void;
|
|
80
81
|
export const quill_metadata: (a: number, b: number) => void;
|
|
81
82
|
export const quill_parse: (a: number, b: number, c: number, d: number) => void;
|
package/runtime/runtime.d.ts
CHANGED
|
@@ -108,7 +108,6 @@ export type {
|
|
|
108
108
|
QuillFieldUi,
|
|
109
109
|
QuillCardUi,
|
|
110
110
|
QuillGroupUi,
|
|
111
|
-
QuillMatrixGroup,
|
|
112
111
|
QuillMetadata
|
|
113
112
|
} from '../core/wasm.js';
|
|
114
113
|
|
|
@@ -182,9 +181,9 @@ export declare function isQuillmarkError(e: unknown): e is QuillmarkError;
|
|
|
182
181
|
|
|
183
182
|
import type { ContentContainer } from '../core/wasm.js';
|
|
184
183
|
|
|
185
|
-
// `ContentIsland.type`, `ContentMark.type`, `ContentLine.kind
|
|
186
|
-
// `ContentContainer.container`
|
|
187
|
-
//
|
|
184
|
+
// `ContentIsland.type`, `ContentMark.type`, `ContentLine.kind` and
|
|
185
|
+
// `ContentContainer.container` are closed sets, so a bare discriminant check
|
|
186
|
+
// narrows the payload on its own:
|
|
188
187
|
// `line.kind === 'heading'` reaches `line.attrs.level`, with no guard to call.
|
|
189
188
|
|
|
190
189
|
/**
|
package/runtime/runtime.js
CHANGED
|
@@ -174,7 +174,7 @@ const HANDLE_KINDS = {
|
|
|
174
174
|
},
|
|
175
175
|
Document: {
|
|
176
176
|
code: 'runtime::not_a_document',
|
|
177
|
-
hint: 'Pass a Document built by Document.fromMarkdown / fromStored or quill.seedDocument. A Document from another copy of @quillmark/wasm is refused too — each copy is its own WASM linear memory and its own class — so run `npm ls @quillmark/wasm` and dedupe to one.'
|
|
177
|
+
hint: 'Pass a Document built by Document.fromMarkdown / fromStored or quill.seedDocument / emptyDocument. A Document from another copy of @quillmark/wasm is refused too — each copy is its own WASM linear memory and its own class — so run `npm ls @quillmark/wasm` and dedupe to one.'
|
|
178
178
|
}
|
|
179
179
|
};
|
|
180
180
|
|
|
@@ -219,9 +219,15 @@ function requireLocalQuill(quill, method) {
|
|
|
219
219
|
// WELD_KEYS is the rule `Container::same_weld` owns upstream: which `attrs`
|
|
220
220
|
// entries two adjacent runs must share for the markdown projection to read them
|
|
221
221
|
// as one. `start` is not among them, since CommonMark reads only a list's first
|
|
222
|
-
// number — a subset, which is why every container needs its own entry.
|
|
223
|
-
//
|
|
224
|
-
//
|
|
222
|
+
// number — a subset, which is why every container needs its own entry.
|
|
223
|
+
//
|
|
224
|
+
// Re-spelled here rather than called, because `assignInstances` answers before
|
|
225
|
+
// wasm initializes (`prose/canon/BINDINGS.md` § "WebAssembly"). What holds the
|
|
226
|
+
// two spellings equal is running the rule, not comparing their sources:
|
|
227
|
+
// `runtime.test.js` § "container run boundaries" stamps a pair and re-imports
|
|
228
|
+
// it, where `Content::normalize` re-mints against the Rust predicate. A tag the
|
|
229
|
+
// table omits welds with nothing, so `tests/known_names_drift.rs` pins that
|
|
230
|
+
// every container has an entry.
|
|
225
231
|
|
|
226
232
|
const WELD_KEYS = { list_item: ['ordered'], quote: [] };
|
|
227
233
|
|