@quillmark/wasm 0.92.0 → 0.94.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,551 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.94.0 - 2026-07-15
4
+
5
+ - Delete prose/plans directory
6
+ - feat(wasm): promote the live-session/canvas surface to stable
7
+ - docs(release): reconcile changelog and guides for 0.94.0 (skip 0.93)
8
+ - test(core,quillmark): remove redundant and low-value tests
9
+ - core: drop public→private rustdoc link in UiFieldSchema docs
10
+ - core/seed: collapse merge-then-sort into one declaration-order pass
11
+ - docs: declaration order is the field ordering contract; ui.order removed
12
+ - core, bindings: update tests, golden, and doc surfaces for structural order
13
+ - core: field declaration order becomes structural (IndexMap), ui.order retired
14
+ - Simplify group-order sort key and ui.order stamping
15
+ - Add card-level ui.groups registry for group identity and ordering
16
+ - pdfform: place widget geometry once at bind, not per render
17
+ - pdfform: gate form.json version before field-shape parse
18
+ - pdfform: derive widget intrinsics from the quill schema (form@0.2.0, #940)
19
+ - Reject nested ui.group; auto-order nested object properties
20
+ - docs: densify the plaintext/enum section of the 0.93->0.94 guide
21
+ - core, richtext: follow simplification cascades from plaintext
22
+ - docs: extend canon (BLUEPRINT, PLATE_DATA) for plaintext and enum
23
+ - docs: document plaintext and enum field types, narrow string
24
+ - quillmark: end-to-end render test for plaintext through typst
25
+ - core: test plaintext codec and enum promotion end-to-end
26
+ - core: add plaintext and enum first-class field types
27
+ - richtext: add from_plaintext literal codec and is_plain predicate
28
+ - fix(typst/overlay): stop underline/strike decoration ink truncating $body regions
29
+ - chore: migrate org references quillmark-org → borb-sh
30
+ - docs: fix cross-tree link that broke mkdocs --strict
31
+ - simplify: fix stale receiver refs and facade-prose misses after rename
32
+ - rename typed-write facade editor→writer; unify on quill.writer(doc)
33
+ - python: delete redundant commit_* verbs (subsumed by the editor)
34
+ - docs: two-tier binding surface — parity table + strata/ephemerality (#932)
35
+ - python: Document.editor(quill) front door + Editor/CardEditor + reads
36
+ - wasm: quill.editor(doc) front door + tier-1 gap verbs + reads
37
+ - wasm+python: delete addressed commit(addr) verb (subsumed by editor)
38
+ - core: TypedEditor set_body + add_card (tier-1 mirror verbs)
39
+ - simplify: follow the #925 cascades
40
+ - docs: finish migration index entry; tidy wasm doc link
41
+ - python: mirrored addressed write surface + corpus codec
42
+ - wasm: addressed write surface (install/revise/applyChange/commit) + codec
43
+ - richtext+core: pure Delta codec + install/revise body & field verbs
44
+ - test(wasm): update runtime.test.js editor sugar to the strict contract
45
+ - feat(core,wasm,python)!: typed field writes reject unknown names (#918)
46
+ - wasm: split Card read type from CardInput write type (#917)
47
+ - richtext: MarkOp::Remove subtracts the range instead of dropping the whole mark
48
+ - fix(quillmark-pdf): dict/array depth walkers skip comments and strings
49
+ - fix(richtext): markdown export never leaks a delimiter into the corpus
50
+ - fix(richtext): saturate ordered-list marker; doc/hint cleanups from final review
51
+ - #913: mirror the WASM typed batch on Python (commit_fields + set/commit reframe)
52
+ - #911: finish exposing the typed editor in WASM (commitFields batch + JS editor sugar + reframe)
53
+ - test(wasm): warm the lazy Typst backend load before timed Engine renders
54
+ - test: consolidate generator-arm round-trips and costume proptests (tier 3-4)
55
+ - test: remove orphan fixtures and redundant/low-value tests (tier 1-2)
56
+ - docs: audit of legacy/redundant/low-value tests, fixtures, logic
57
+ - richtext/core/bindings: address issues #902–#906
58
+ - richtext: escape image alt/URL and link URL on markdown export (#900)
59
+ - richtext: strip \r and bidi controls from apply_text_delta inserts
60
+ - Remove prose/proposals/: 893 proposal has landed
61
+ - Unify binding write-verb grammar: mechanical card-write twins (#895)
62
+ - Hard cutover to commit_field: remove legacy richtext writers
63
+ - wip: bindings + tests + docs for typed writes (pre-cutover)
64
+ - Typed field writes: conform dispatch, commit_field, TypedEditor (core)
65
+ - Review typed-field-writes proposal against codebase; resolve open questions
66
+ - Add proposal: typed field writes via schema-carried types (#893)
67
+ - feat(wasm): setCardBody — corpus writer for a non-main card body (#892)
68
+ - remove(core,richtext,wasm)!: delete the incremental-edit surface (#886)
69
+ - test(wasm): fix two applyFieldDelta tests left stale by the #881 un-gate
70
+ - feat(regions): field_boxes helper + CorpusHit granularity signal (#884)
71
+ - fix(core,wasm): resolve lint CI failure + re-sync applyFieldDelta type after merge
72
+ - wasm/canvas: report clamp on PaintResult; document paint compositing constraints
73
+ - fix(wasm): make applyFieldDelta and supportsCanvas types honest about their domain
74
+ - docs: migration guide for the richtext field write-surface unification (#881)
75
+ - richtext: un-gate applyFieldDelta to richtext fields (step 6)
76
+ - wasm: setRichtextField / updateCardRichtextField + per-field markdown getter (step 5)
77
+ - richtext: emit projects corpus fields to markdown; wire/DTO lossless (steps 2-4)
78
+ - richtext: Card::set_field_richtext writer + corpus read-back (step 1)
79
+ - fixtures: table_demo Quill + end-to-end table render test; document pdfform limit
80
+ - richtext: property-test structured tables through normalize + round-trip
81
+ - richtext: in-cell images degrade honestly instead of silent-Lossless
82
+ - richtext: canonical table shape — validate invariants, repair in normalize
83
+ - test: drop two redundant inline tests; dense-prose the runtime edit-surface note
84
+ - Prune landed simplification entries from the backlog
85
+ - Silence clippy nits in span_scan walk refactor
86
+ - test(wasm): keep runtime edit-surface guard focused on the #876 methods
87
+ - docs: fix rustdoc intra-doc links to satisfy the -Dwarnings lint gate
88
+ - Filter locate's glyph walk to the target segment
89
+ - Share frame-walk geometry between region scan and corpus walk
90
+ - feat(wasm): expose incremental editor edit surface through runtime (#876)
91
+ - Skip props re-clone in normalize when already key-sorted; document R2 hazard
92
+ - Carry the <u>/Strong distinction instead of re-sniffing source
93
+ - feat(typst): add plaintext(field) projection helper + document richtext migration hazard (#873)
94
+ - Lift island mark-dispatch out of model into serial
95
+ - Build canonical richtext tree once via move-based key sort
96
+ - feat(wasm): add setBody(corpus) mutator on Document (#874)
97
+ - Dedup emit_inline mark partition into wraps_and_codes
98
+ - fix(typst): lower richtext(inline) fields to inline content (#872); fix wasm TS types (#875)
99
+ - Remove unused ChangeLog/session forward surface
100
+ - Cut over-engineered simplification findings; refresh drifted citations
101
+ - docs(simplifications): remove the addressed findings
102
+ - docs(simplifications): record the sync-twins deferral rationale
103
+ - refactor(typst,richtext): dedup container dispatch and the #846 clip
104
+ - refactor(core): one shared decoder for the dual-shape richtext seam
105
+ - refactor(richtext): make apply_field_change all-or-nothing
106
+ - refactor(core): single carrier for the richtext inline flag
107
+ - refactor(richtext): drop unused usv UTF-16 helpers and content_key alias
108
+ - refactor: apply low-risk simplifications from prose/simplifications
109
+ - docs: record simplification-review findings in prose/simplifications/
110
+ - fix(richtext): apply a short field delta leniently (auto-append remainder)
111
+ - Trim unused test-only public methods from core
112
+ - docs(core,typst): dense-prose pass on comments
113
+ - docs(richtext): dense-prose pass on comments
114
+ - docs(core): update QuillValue fill-setter reference after set_fill removal
115
+ - Remove more dead/redundant logic
116
+ - docs: remove stale info across canon, docs, and comments
117
+ - test: remove redundant and low-value tests across the workspace
118
+ - Remove dead and redundant logic
119
+ - fix(docs): resolve broken intra-doc links (cargo doc -Dwarnings)
120
+ - typst/richtext: address #855 cleanup findings
121
+ - Fix #851 follow-up: transactional applyFieldDelta and fail-closed invalidation
122
+ - richtext: fix issue #854 cleanup findings
123
+ - fix(session): close the three delta-protocol lifecycle gaps (#851)
124
+ - Fix low-risk API-surface findings from #856
125
+ - fix(wasm): strip unreachable delta-API types from public runtime.d.ts (#850)
126
+ - chore: ignore dist/, .vite/, test-results/ to prevent build-artifact commits
127
+ - fix(richtext): cap single-line char diff to avoid quadratic blowup (#849)
128
+ - fix(richtext): balance markdown export for overlap, escape `&` and trailing heading `#` (#848)
129
+ - docs: fix richtext drift found in #853
130
+ - fix(richtext): keep islands in sync on text-channel edits (#847)
131
+ - fix(typst): balance markup for overlapping wrap+code marks (#846)
132
+ - Render thematic breaks (---/***/___) instead of dropping them
133
+ - fix(richtext): correct ChangeLog::map_pos staleness and future-revision handling
134
+ - chore(richtext): drop richtext-spikes from integration branch
135
+ - Move richtext inline shape to `inline: true` schema field.
136
+ - feat(richtext): PR-H findings — fixture, runtime nav, docs (PR-H)
137
+ - cargo fmt
138
+ - feat(richtext): preview revision stamp + WASM session delta API (PR-F/PR-G)
139
+ - feat(richtext): fallible document body mutators (PR-E)
140
+ - docs(richtext): drop .qmd and Quill-Delta from phase-3 plan
141
+ - docs(richtext): record PR-C/D landing in phase-3 plan
142
+ - feat(richtext): add mark/line op channels and apply bundle (PR-D)
143
+ - feat(richtext): add revision counter and bounded change log (PR-C)
144
+ - feat(richtext): replace coarse delta::diff with Myers/LCS (PR-B)
145
+ - Remove husky pre-commit hook
146
+ - cargo fmt
147
+ - docs(richtext): record phase-3 spike findings on integration branch
148
+ - docs(richtext): align canon + code comments to PR-G, wrap up phase 2
149
+ - fix(wasm): add span to canonical FieldRegion contract
150
+ - richtext PR-G: richtext(inline), load-time example cache, alias cutover
151
+ - Delete prose/review directory
152
+ - docs(richtext): trim phase plans to landed reality, fold spike findings
153
+ - test(richtext): wasm binding test for positionAt/locate round-trip (#829)
154
+ - PR-F: regions + navigation — two-tier segment scan, position_at/locate (#829)
155
+ - spike(richtext): PR-F de-risking — run-machine transparency + glyph.span.1 precision
156
+ - fix(richtext): accept bodyMarkdown in the wasm card field allowlist
157
+ - feat(richtext): phase-2 PR-E — bindings expose corpus body + bodyMarkdown
158
+ - docs(richtext): clarify hard vs soft break in the CONVERT lowering table
159
+ - docs(richtext): phase-2 PR-E canon rework for the corpus seam
160
+ - feat(richtext): phase-2 PR-E — pdfform lowers richtext to plaintext + fixture
161
+ - feat(richtext): phase-2 PR-E part 2 — delete the markdown oracle
162
+ - feat(richtext): phase-2 PR-E part 1 — seam flip + typst consumes corpus
163
+ - docs(richtext): reconcile Typst-emit section with structured cells
164
+ - docs(richtext): PR-E handover + phase-2 status refresh
165
+ - docs(richtext): remove the .qmd file-extension concept
166
+ - feat(richtext): structured table cells — markdown a pure projection
167
+ - feat(richtext): phase-2 PR-D — typst corpus emitter + segment source maps
168
+ - feat(richtext): phase-2 PR-C — storage cutover to quillmark/document@0.93.0
169
+ - fix(richtext): resolve broken RichText::validate intra-doc link
170
+ - fix(core): quote payload scalars that would re-parse as a non-string
171
+ - docs(richtext): PR-B landing log + PR-C handover on the phase-2 plan
172
+ - fix(richtext): clear rustdoc -Dwarnings lint on phase-2 PR-B docs
173
+ - test(bindings): update body expectations for corpus canonicalization
174
+ - feat(richtext): phase-2 PR-B — Card.body is RichText, markdown a projection
175
+ - docs(richtext): lock separate-crate topology + retire Quill-Delta framing in plan
176
+ - refactor(richtext): make quillmark-richtext a leaf crate core depends on
177
+ - docs(richtext): phase-2 plan — engine consumes RichText, delivers #829
178
+ - docs(richtext): note block-quote emit decision for phase-2 handover
179
+ - docs(richtext): phase-1 handover doc + integration HQ updates
180
+ - fix(richtext): address phase-1 review — freeze determinism, codecs, anchors
181
+ - feat(richtext): phase 1 — RichText corpus model, codecs, canonical serialization
182
+ - docs(richtext): land phase-0 spike findings on the integration HQ
183
+ - docs(richtext): make integration HQ canonical, self-contained
184
+ - docs(richtext): open integration HQ plan for the content-model rework
185
+ - Sync usaf_memo/0.2.0 fixture with airmark-quiver template
186
+ - #824: dedup bindings tests + add two coverage guards
187
+ - #823 items 2-9: dedup core + integration tests
188
+ - #821 follow-up: fix rustdoc private-intra-doc-link in winansi_encode
189
+ - #822: dedup typst backend + PDF crate tests (~280 lines)
190
+ - #825: cache only the wasm cargo build dir, always regenerate pkg/
191
+ - #806: resolve overlapping widget field_at by paint order, not name
192
+ - Dead-code sweep from minimize-0.92.1-main review (#817–#821, #823, #825)
193
+ - Ground #809/#810: memoize page checks, unify dict-splice + field-name filters (#816)
194
+ - Tidy deferred backlog: dedup error-summary, pdfform flatten-limit docs, cleanup (#804, #807, #810, #812) (#815)
195
+ - Fix silent-data-loss regression and blueprint/error/docs gaps (#803, #805, #808, #811) (#814)
196
+ - Land LiveSession.fieldAt delegation; harden page_hashes against source spans (#801) (#813)
197
+ - Helper codegen v2: markup blocks + generated data literal, retiring eval() and json() (#800)
198
+ - Disable clippy in lint CI job (#798)
199
+ - Satisfy clippy's manual_contains in the scalar-window walker
200
+ - Drop the #797 spike modules from the branch
201
+ - Harden the span scan against the review findings
202
+ - Delete .claude/skills/simplification-cascades directory
203
+ - Spike: generated data literal is Typst-equal to the json() blob
204
+ - Spike: content fields as markup blocks resolve with per-node spans
205
+ - Document the span-based region contract across canon, core, and the migration guide
206
+ - Expose fieldAt on the WASM session; align binding docs with the span contract
207
+ - Migrate usaf_memo off tagged(); rewrite region tests for the span contract
208
+ - Span-based region tracking: codegen'd eval windows + glyph-span scan
209
+ - Omit $body from the transform schema for body-disabled kinds
210
+ - feat(regions): any array is element-addressable; SchemaMeta drops its unreachable Option
211
+ - test(typst): own the widget-region test's schema instead of borrowing the fixture's
212
+ - refactor(typst): collapse SchemaMeta per-kind table boilerplate, test-owned widget width
213
+ - docs(migrations): document the compile-time region-binding validation in the working guide
214
+ - fix(ci): hash every pkg/ input in the wasm cache key, check key sets in the drift guard
215
+ - fix(wasm): sync canonical runtime.d.ts with the regions sidecar, wire typecheck into CI
216
+ - feat(regions): validate form-field paths, reject scalar index-tagging, cache schema meta
217
+ - docs(migrations): fix stale region-uniqueness line in 0.92→0.93 guide
218
+ - Reconcile canon and migration guide with the reworked error system
219
+ - Update binding surfaces to the current-compile warnings contract
220
+ - Thread Typst compile warnings through the session as current-compile state
221
+ - Collapse RenderError to a single diagnostics-carrying struct; drop Severity::Note
222
+ - Amend error-rework proposal per adversarial review
223
+ - Propose error system rework: one failure type, warnings on the compile seam
224
+ - dense-prose: drop issue reference and history narration from regions test
225
+ - feat(regions): tag the usaf_memo plate; canon + migration guide for the new region contract
226
+ - feat(regions): per-placement regions, public tagged() helper, one-shot sidecar
227
+ - docs: accuracy pass + dense-prose sweep across docs/, prose/canon/, comments, READMEs
228
+ - build-wasm.sh: dev builds stamp next-patch -dev.<sha>, releases stamp verbatim
229
+ - wasm runtime: snapshot caller handles before the first await
230
+ - ci: enable the Clippy lint gate
231
+ - Python: batched-error message embeds the first diagnostic
232
+ - docs(core): note apply's $quill-check boundary on LiveSession::apply (#778)
233
+ - perf(wasm): LiveSession retains QuillConfig, not the whole Quill (#778)
234
+ - docs: canonize LiveSession — live preview owns apply/ChangeSet (#778)
235
+ - feat(wasm): LiveSession.apply(doc) -> ChangeSet (#778)
236
+ - Canon: endorse programmatic document construction
237
+ - refactor!: collapse RenderSession into LiveSession (#778)
238
+ - Add blank-canvas Document constructor in core and all bindings
239
+ - feat: transactional apply() + ChangeSet on the render session (#778)
240
+ - Add scalar From impls for QuillValue; mutators take Into<QuillValue>
241
+ - Expose batched set_fields across Python, WASM, and .NET bindings
242
+ - feat(typst): persist the World in the session; evict comemo after each compile (#778)
243
+ - Add atomic batched Card::set_fields
244
+ - remove(dotnet)!: drop the .NET binding entirely
245
+ - Update README.md
246
+ - Update README.md
247
+ - Update README.md
248
+ - Update README.md
249
+ - Update README.md
250
+ - Update README.md
251
+ - Update README.md
252
+ - fix: update WASM region test for content auto-tag; fix doc links
253
+ - docs(region): tighten regions() doc per dense-prose
254
+ - feat(region): one region per logical schema field
255
+ - docs(region): fix grouping guidance and note blank/empty no-region case
256
+ - refactor(typst): expose a region only for schema-addressable fields
257
+ - feat(typst): auto-region tagging for content fields (#775)
258
+ - refactor(core)!: move field regions from RenderResult to RenderSession::regions()
259
+ - refactor(core)!: key regions on the quill schema field, not the backend widget
260
+ - docs: fold prune-evolutionary-info into dense-prose
261
+ - docs: canonize comment/doc style as the dense-prose skill
262
+ - refactor(test): drop dangling BACKLOG.md reference
263
+ - refactor(core): tighten doc-comments, prune evolutionary phrasing
264
+ - refactor(bindings): neutralize marketing and reframe legacy phrasing
265
+ - refactor(quillmark,cli,fuzz): tighten comments, drop bug-history narration
266
+ - refactor(typst): tighten module and item doc-comments
267
+ - docs(canon): prune evolutionary and marketing phrasing
268
+ - docs: neutralize marketing and reframe legacy aliases in consumer docs
269
+ - feat(pdfform): export PNG and SVG; remove the preview feature gate
270
+ - fix(ci): resolve lint and docs CI failures on pdfform branch
271
+ - fix(quillmark-pdf): make find_dict_value key/value-position aware
272
+ - Make the plate a Typst-backend concern, not a universal one
273
+ - Address branch-review findings: spine robustness, coercion parity, doc accuracy
274
+ - Prune resolved review items 01–06; trim 07 to remaining gaps
275
+ - Rework item 2: derive canvas capability, delete supports_canvas flag
276
+ - Address branch-review item 3: total FieldRegion round-trip
277
+ - Address branch-review items 1, 2, 4, 5, 6, 7
278
+ - docs(review): write up unaddressed branch-review items for discussion
279
+ - test(pdfform): restore flatten byte-level coverage at the unit level (preview-gated)
280
+ - refactor(pdfform): PDF output always AcroForm; remove the public flatten knob
281
+ - Update sample_form PDF title and field labels
282
+ - Make pdfform framing industry-neutral
283
+ - docs: add PDF Form backend page under quills section
284
+ - refactor(pdfform): remove the form.json -> Quill.yaml scaffold
285
+ - refactor(pdf): extract the incremental-update envelope into PdfUpdate
286
+ - pdfform: WinAnsi flatten encoding, value clipping, shared PDF writer
287
+ - review: resolve low-risk findings from pdfform branch review
288
+ - Update README.md
289
+ - Remove qualification layer and page composition: pdfform fills static forms only
290
+ - feat(quillmark-pdf): page-merge primitive + continuation foundation (#757)
291
+ - refactor(pdfform): centralize flatten value typography as one source of truth (#752)
292
+ - feat(wasm): ship pdfform canvas + make the per-backend canvas contract explicit (#755)
293
+ - feat(typst): generalize the plate API to arbitrary form-field(...) (#758)
294
+ - feat(pdfform): card-instance value addressing in the resolver (#757)
295
+ - feat(qualify): add quillmark-qualify — AcroForm PDF → form.pdf + form.json (#753)
296
+ - chore(fmt): apply rustfmt line-wrapping to pre-existing drift
297
+ - feat(pdfform,cli): scaffold Quill.yaml from a form.json field spec (#756)
298
+ - fix(pdfform): build the preview feature against hayro's vello_cpu re-export
299
+ - Expose form-field regions from stamped AcroForm backends (#759)
300
+ - Implement `pdfform` backend + shared `quillmark-pdf` stamping spine (V1) (#750)
301
+ - docs(proposal): design for pdfform backend + shared AcroForm spine (#744) (#748)
302
+ - fix(python): count container levels, not the scalar leaf, in depth bound (#742) (#746)
303
+ - Add generic hint for unresolvable Typst eval errors (issue #745) (#747)
304
+ - docs: fix prose/canon, docs, and code-comment consistency drift (#743)
305
+ - fix(core): count empty containers in json_depth_exceeds (#741)
306
+ - Update README.md
307
+ - docs: position Quillmark as a "schema-driven document engine" (#739) (#740)
308
+ - review(0.93.0): fix stale comments, document scalar coercion, cover card markers (#738)
309
+ - Unify blueprint emission on Document::to_markdown (#737)
310
+ - docs(blueprint): fix $-line comment inaccuracies in canon (#735)
311
+ - core: graciously coerce bare scalars into string fields (#733)
312
+ - docs(spike): propose unifying blueprint placeholder on the !must_fill tag (#732)
313
+
314
+
315
+ ## v0.92.1 - 2026-06-22
316
+
317
+ - Accept uppercase field names; reserve only `$`-prefixed keys (#730)
318
+ - docs: canonize $ext.editor.title as per-card display name slot (#729)
319
+
320
+
321
+ ## Unreleased
322
+
323
+ These notes cover everything since v0.92.1. No 0.93.x was separately
324
+ published — the 0.93 milestone folds into this release, so the upgrade path
325
+ from 0.92.1 is the `0.92-to-0.93` and `0.93-to-0.94` guides read in sequence.
326
+
327
+ - feat(wasm): the live-session / canvas-paint surface graduates from
328
+ `@experimental` to stable — `Engine.open`, `LiveSession`, `apply` /
329
+ `ChangeSet`, `paint` / `PaintOptions` / `PaintResult`, `PageSize`, and the
330
+ `supportsCanvas` probe are now the committed preview API. The tag is dropped
331
+ from the runtime `.d.ts` / `.js`, the wasm README, and `PREVIEW.md`; further
332
+ shape changes follow the normal deprecation path rather than landing in any
333
+ 0.x. `Engine.render` / `supportedFormats` remain the one-shot path
334
+ - refactor(core)!: field ordering becomes fully structural — `ui.order` is
335
+ removed and an authored `order:` is a load error. Field and card-kind display
336
+ order is now the key order of the emitted schema (declaration order, backed by
337
+ an `IndexMap`), and the auto-stamped `order:` integer disappears from
338
+ `QuillConfig::schema()`; consumers walk the maps in key order instead of
339
+ sorting on a stamped index. Typed-dictionary / typed-table-row properties
340
+ render in declaration order, not alphabetically (#941). See
341
+ `docs/migrations/0.93-to-0.94.md`
342
+ - feat(core)!: a card-level `ui.groups` registry gives groups identity and
343
+ order. `ui.group` becomes a validated reference to a snake_case id
344
+ (`quill::unknown_group` for a dangling ref); the registry's declaration order
345
+ fixes group display order, labels derive from the id with a `title:` override,
346
+ and a bare label-as-identity group is deprecated (`quill::implicit_group`). A
347
+ nested `ui.group` is a load error (`quill::nested_group_not_supported`) (#941).
348
+ See `docs/migrations/0.93-to-0.94.md`
349
+ - feat(core,typst,wasm,python)!: `plaintext` and a first-class `enum` join the
350
+ schema. `plaintext` is navigable unformatted prose carried over the richtext
351
+ corpus (a literal codec, with a `plaintext(field)` helper on the Typst side);
352
+ `enum` is promoted to `type: enum` + `values:`, and the `enum:` modifier on
353
+ `string` is deprecated for one release. `string` narrows to open scalar data
354
+ (#938). See `docs/migrations/0.93-to-0.94.md`
355
+ - refactor(core)!: `type: richtext(inline)` retires — declare `type: richtext`
356
+ with `inline: true`. The old token is a hard `quill::field_parse_error`, and
357
+ `inline: true` on a non-richtext field is likewise rejected. Blueprint still
358
+ emits `richtext(inline)<markdown>` and `build_transform_schema` gains
359
+ `quillmark:inline: true`, both derived from the flag; documents and corpus
360
+ wire shapes are unaffected. See `docs/migrations/0.93-to-0.94.md`
361
+ - refactor(pdfform)!: `form.json` slims to a binding layer (`form@0.2.0`). Bound
362
+ `fields` drop `type` / `options` / `multiline` (derived from the schema
363
+ field's kind, `enum` values, and `ui.multiline`); unbound widgets move to a
364
+ `widgets` section; binding runs at load, so a bad `schema_field` fails with
365
+ `pdfform::dangling_binding` / `pdfform::unbindable_field` instead of a silent
366
+ blank. `form@0.1.0` is rejected and `$cards` absolute-index addressing is
367
+ removed. Widget geometry is placed once at bind, not per render (#940). See
368
+ `docs/migrations/0.93-to-0.94.md`
369
+ - refactor(core,richtext,wasm,python)!: the binding write surface settles into
370
+ two tiers over a document-free corpus codec. `quill.writer(doc)` (wasm and
371
+ Python alike) is the documented default — typed `set` / `set_all` / `setBody`
372
+ / `addCard` / `card(i)` and quill-free `get` / `getMarkdown` reads — layered
373
+ over the corpus lane (`importMarkdown` / `exportMarkdown` / `rebase` / `mapPos`
374
+ plus the addressed `install` / `revise` / `applyChange` verbs) and the opaque
375
+ `setField` primitive. The eager `bodyMarkdown` / `fieldMarkdown` projections
376
+ and the per-address body writers retire pre-release; `replaceBody` /
377
+ `replace_body` / `update_card_body` alias for one cycle; richtext fields gain
378
+ the anchor-preserving `revise_field`; the addressed `commit(addr, …)` is
379
+ deleted (subsumed by the writer). A core-vs-bindings parity table governs
380
+ drift (#925, #932). See `docs/migrations/0.93-to-0.94.md`
381
+ - refactor(wasm)!: the `Card` shape splits by direction — a read `Card` always
382
+ carries `body: RichText`, while `pushCard` / `insertCard` take a `CardInput`
383
+ whose `body` still accepts a markdown string and whose non-`kind` fields are
384
+ optional (#917). The card-write verbs become mechanical twins of their
385
+ main-card names: `updateCardField` / `updateCardFields` rename to
386
+ `setCardField` / `setCardFields` (#895). See `docs/migrations/0.93-to-0.94.md`
387
+ - fix(typst/overlay): underline / strike decoration ink no longer truncates
388
+ `$body` field regions — the region geometry is taken before decoration strokes
389
+ extend the glyph ink box, so a highlighted body field's box matches the text
390
+ instead of the overrun (#937)
391
+ - chore: migrate org references `quillmark-org` → `borb-sh` across the tree
392
+ - fix(richtext): the markdown-export codec never leaks a delimiter into the
393
+ corpus. An editor `apply_mark_ops` mark can wrap a span markdown can't
394
+ represent (a `strong`/`emph`/`strike` edge on punctuation/symbols/whitespace,
395
+ or abutting a literal `*`) — the run would re-import as literal `**`/`*`/`~~`
396
+ text (bolding `a.` used to export `**a.**b`). `to_markdown` now verifies each
397
+ rendered line by re-parse and drops any mark whose emission would alter the
398
+ text, so the text always round-trips; only the unrepresentable formatting is
399
+ lost. Import-domain corpora are unaffected (still an exact fixed point).
400
+ - feat(core,wasm,python)!: typed field writes via schema-carried types. One
401
+ per-type write dispatch (`conform_value(value, schema, mode)`) unifies the
402
+ render floor's coercion with a strict-write mode behind a `Leniency` flag; one
403
+ typed writer per address, `Card::commit_field(name, value, &FieldSchema)`,
404
+ dispatches on the schema — the write surface stays O(1) in field types. Adds
405
+ `EditError::FieldConform` for non-richtext mismatches (richtext keeps
406
+ `FieldRichtextDecode` / `FieldRichtextNotInline`). A schema-bound
407
+ `TypedWriter` (`Quill::writer(&mut doc)`) is the front door: `set` / `set_all`
408
+ resolve field types and strict-commit; a name the schema does not declare is a
409
+ typo on the typed path, so it fails with `EditError::UnknownField` instead of
410
+ falling to the opaque store (#918) — opaque storage stays available through the
411
+ raw `set_field` / `setField` / `setCardField` verbs. Bindings gain
412
+ `commitField` / `commitCardField` (wasm) and `commit_field` /
413
+ `commit_card_field` (Python, net-new — Python had no richtext field writer).
414
+ The pre-release richtext-specific writers are removed in the same cycle:
415
+ `Card::set_field_richtext`, wasm `setRichtextField` / `updateCardRichtextField`
416
+ — use the typed writer, which carries the `inline` constraint in the schema.
417
+ Strict writes drop the render floor's cross-type `Boolean`↔`Number` coercions
418
+ and fail a shape mismatch at the write, not at a later render (#893)
419
+ - remove(core,richtext,wasm)!: delete the incremental-edit surface — the
420
+ per-field change log and everything layered on it: `richtext::ChangeLog` /
421
+ `FieldChange` / `StaleRevision`; `LiveSession::revision` /
422
+ `record_field_delta_at` / `record_field_change_at` / `ensure_base_revision` /
423
+ `map_field_pos` / `apply_for_field_delta`; the WASM `applyFieldDelta` /
424
+ `mapFieldPos` / `revision` and the `Delta` DTO; and the `revision` stamp on
425
+ `RenderedRegion` / `CorpusHit` (and `FieldRegion` / `CorpusHit` on the wire).
426
+ Anchoring a caret or selection across edits belongs to the editor's own
427
+ transaction mapping (a ProseMirror / CodeMirror `StepMap`), not a parallel
428
+ core-side position map: the bidirectional preview↔editor cursor bridge is
429
+ `positionAt` / `locate` over the current compile, exact inverses that never
430
+ consulted the change log. Whole-document `apply(doc)` stays the one edit verb.
431
+ This dissolves #886's anchor-stranding half outright and drops the
432
+ half-built delta path behind its per-keystroke-marshalling half; `Delta` /
433
+ `diff` / `diff_import` / the mark & line op channels remain as the corpus
434
+ writers' substrate (`replace_body`, `import_body_delta`, `apply_body_change`)
435
+ (#886)
436
+ - feat(core,wasm): `field_boxes(field)` / `LiveSession.fieldBoxes(field)` derive
437
+ the whole-field highlight — one union rect per page over the field's
438
+ `span`-bearing content segments — so a "highlight the focused field" consumer
439
+ stops reimplementing the span-filter + per-page union by hand. `regions()`
440
+ stays the low-level disjoint truth (#829); the helper owns the union, and is
441
+ content-only (a scalar-reference/widget-only field returns `[]`, its box being
442
+ a single `regions()` rect). Core `field_boxes(&[RenderedRegion], field)` is a
443
+ pure function so the one-shot `RenderResult.regions` sidecar gets it too (#884)
444
+ - feat(core,wasm): `CorpusHit.granularity` (`HitGranularity` = `cluster` |
445
+ `segment`) reports whether `positionAt`'s `pos` resolved cluster-exact or
446
+ floored to the containing segment's start (origin-less ink, a multi-line code
447
+ fence's interior), so a caret UI trusts a `cluster` offset for the caret and
448
+ treats a `segment` one as a segment selection instead of guessing. Additive-
449
+ optional, omitted from the wire when the backend does not report it (#884)
450
+ - fix(wasm): `Engine.supportsCanvas` and `LiveSession.supportsCanvas` gain doc
451
+ comments cross-referencing each other: the two are spelled identically but
452
+ answer different questions (a pre-session backend estimate vs. this compile's
453
+ authoritative answer, which can diverge — e.g. a 0-page document) — the
454
+ divergence is now visible where each is used instead of only discoverable at
455
+ runtime (#883)
456
+ - fix(core): drop two rustdoc intra-doc links from public items
457
+ (`RichtextDecodeError`, `Card::set_field_richtext`) to the private
458
+ `decode_richtext_value`, which `-D rustdoc::private-intra-doc-links` (part of
459
+ the lint gate) rejects since the link can never resolve for a doc reader;
460
+ reworded to a plain code span, matching the existing convention elsewhere in
461
+ the same file for referencing a private helper from public docs
462
+ - fix(wasm): drop the `revision?` field from the public `CorpusHit`/`FieldRegion`
463
+ types and the broken `{@link LiveSession.mapFieldPos}` / `.revision` references
464
+ in `runtime.d.ts`. The delta API (`applyFieldDelta`/`revision`/`mapFieldPos`) is
465
+ not forwarded through `runtime.js`, so no published consumer could reach the
466
+ methods those fields pointed at, and the stamped `revision` was always `0` on
467
+ the reachable read paths (whole-doc `apply` is revision-neutral). The public
468
+ types no longer advertise a capability the shipped `LiveSession` doesn't expose
469
+ (#850)
470
+ - refactor(core)!: `RenderSession` collapses into `LiveSession` — a persistent,
471
+ incremental compiler that owns preview (#778). Reads (`render`, the canvas
472
+ seam, `regions`) serve the session's current compile; the new transactional
473
+ `apply(json_data)` recompiles in place (on `Err` every read keeps serving the
474
+ last-good compile) and returns `ChangeSet { page_count, dirty_pages }` so a
475
+ preview repaints `dirty ∩ visible`. Typst applies incrementally: the session
476
+ persists its `QuillWorld` (fonts/packages/assets parsed once), swaps document
477
+ data via `Source::replace`, and fingerprints visible page content for the
478
+ dirty set; pdfform re-resolves + re-flattens (cheap by construction). New
479
+ `RenderError::ApplyUnsupported` is the seam default. The callerless
480
+ `typst_session_of` is removed. WASM: the `RenderSession` class is renamed
481
+ `LiveSession` and gains `apply(doc): ChangeSet`; don't re-open per edit. The
482
+ Typst backend now evicts `comemo`'s process-global cache after every compile,
483
+ bounding memory over long editing sessions. See
484
+ `docs/migrations/0.92-to-0.93.md`
485
+ - remove(dotnet)!: drop the .NET binding (`crates/bindings/dotnet`, the
486
+ `quillmark-dotnet` crate, its `csharp/` managed layer, CI job, and NuGet
487
+ publish workflow). Second-class and unmaintained relative to WASM/Python;
488
+ removed rather than carried as bloat. Python and WASM are unaffected.
489
+ - refactor(core)!: field regions move from `RenderResult` to a session-level
490
+ query, `RenderSession::regions()` (WASM `session.regions()`), and are keyed on
491
+ the quill schema field path, not the backend widget. Only the interactive
492
+ preview path wants region geometry; a one-shot byte render (PDF/PNG/SVG) does
493
+ not, so `RenderResult.regions` is removed and the geometry is read once off the
494
+ compiled session without a render. `RenderedRegion` (and the WASM
495
+ `FieldRegion`) drop `name`/`kind`/`fieldType`/`value` for a single `field`
496
+ carrying the schema address (e.g. `signature_block`); the pdfform AcroForm
497
+ widget name no longer leaks. A region is emitted only for a schema-bound field
498
+ — an unbound widget produces none. `RegionKind` is removed; the `quillmark-pdf`
499
+ `FieldSpec` gains `schema_field` and `stamp`/`flatten` return plain bytes
500
+ (`StampResult` is gone). Regions are geometry for overlays and canvas↔editor
501
+ cross-navigation, never a compositing input (#773). See
502
+ `docs/migrations/0.92-to-0.93.md`
503
+ - feat(pdfform)!: the `pdfform` backend now exports PNG and SVG as first-class
504
+ `render()` output formats (`SUPPORTED_FORMATS == [Pdf, Svg, Png]`); PNG
505
+ rasters at `RenderOptions::ppi` (default 144). The `preview` cargo feature is
506
+ removed — the hayro raster/SVG/PNG seam is always linked, so SVG/PNG/canvas
507
+ work out of the box rather than behind a flag. The `quillmark` crate's
508
+ `pdfform-preview` feature is folded into `pdfform`; in the wasm crate both the
509
+ `typst` and `pdfform` build variants link the `web-sys` canvas painter directly
510
+ - fix(quillmark-pdf): `find_dict_value` now walks the dict as strict
511
+ key→value pairs, so a Name in *value* position (e.g. `/Subtype /Producer`)
512
+ is no longer mis-matched as a key; the object/dict scanners also skip
513
+ `%`-comments, so `endobj` or a key token inside a comment can't derail
514
+ parsing of a base PDF. The `<<…>>`/`[…]` depth walkers (`extract_outer_dict`
515
+ and `read_value_end`'s nested-dict/array branches) skip `%`-comments and
516
+ literal `(…)` strings uniformly, so a `>>`/`]` carried inside a comment or
517
+ string no longer truncates a dict/array and drops the keys after it
518
+ - feat(pdfform): add the Typst-free `pdfform` backend + shared `quillmark-pdf`
519
+ AcroForm stamping spine; rewire Typst signatures onto the spine; thread a
520
+ `regions` sidecar through `RenderResult` and generalize the raster-preview
521
+ seam (#749, #750). See `prose/canon/ARCHITECTURE.md` and
522
+ `docs/quills/pdfform-backend.md` for the shipped design.
523
+ - refactor(pdfform): PDF output is always an interactive AcroForm (Technique A).
524
+ Value-flattening is internal machinery backing the SVG/PNG/canvas raster
525
+ outputs, never a PDF deliverable. The public `RenderOptions.flatten` knob is
526
+ removed across core and all four bindings (it was wired only in wasm, hardcoded
527
+ `false` in Python, and ignored in .NET)
528
+ - fix(pdfform): the flatten path transcodes values to WinAnsi (with a
529
+ `WinAnsiEncoding` font) so accented/Latin-1 text renders correctly in the
530
+ raster output, and clips each value to its field box so long values can't
531
+ overflow
532
+ - refactor(quillmark-pdf): hoist the shared PDF byte-serialization (object/text
533
+ writers, `/Info /Producer` stamp) into `quillmark_pdf::writer`, consumed by
534
+ both the stamp and flatten paths; `find_object_bytes` now matches any object
535
+ generation and returns the live (last) revision
536
+ - docs(canon): canonize `$ext.editor.title` as the slot for a per-card display name
537
+ - refactor(core)!: remove the hand-set `Backend::supports_canvas()`; derive
538
+ canvas capability from the one seam instead. `RenderSession::supports_canvas()`
539
+ (authoritative, from `page_size_pt`) and `formats_support_canvas()`
540
+ (pre-session hint, from output formats) replace it, so the capability can no
541
+ longer disagree with what `paint` does. The engine and WASM `supportsCanvas`
542
+ surfaces are unchanged in shape. See `docs/migrations/0.92-to-0.93.md`
543
+ - build(wasm)!: rename the WASM engine feature `render` → `typst` (now the
544
+ default) and add a `pdfform` build variant, so a Typst-free
545
+ PDF-form bundle can ship without Typst. From-source builders pass
546
+ `--features typst` where they used `--features render`; the published JS API is
547
+ unchanged. See `docs/migrations/0.92-to-0.93.md`
548
+
3
549
  ## v0.92.0 - 2026-06-22
4
550
 
5
551
  - 0.92 technical-debt sweep: correctness, $seed hardening, de-duplication (#727)