@quillmark/wasm 0.92.1 → 0.95.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +301 -4
- package/README.md +167 -66
- package/backends/pdfform/wasm.d.ts +1066 -0
- package/backends/pdfform/wasm.js +9 -0
- package/backends/pdfform/wasm_bg.js +2654 -0
- package/backends/pdfform/wasm_bg.wasm +0 -0
- package/backends/pdfform/wasm_bg.wasm.d.ts +98 -0
- package/backends/typst/wasm.d.ts +584 -167
- package/backends/typst/wasm.js +1 -1
- package/backends/typst/wasm_bg.js +1047 -397
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +45 -25
- package/core/wasm.d.ts +412 -110
- package/core/wasm.js +1 -1
- package/core/wasm_bg.js +713 -226
- package/core/wasm_bg.wasm +0 -0
- package/core/wasm_bg.wasm.d.ts +31 -17
- package/package.json +4 -3
- package/runtime/runtime.d.ts +459 -21
- package/runtime/runtime.js +536 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,312 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.95.1 - 2026-07-19
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## v0.95.0 - 2026-07-19
|
|
8
|
+
|
|
9
|
+
- release: re-cut to finish binding dists after the first publish run cancelled mid-flight
|
|
10
|
+
- **breaking** typst: a present `date` / `datetime` field lowers to a click-to-edit value-object — `(value: datetime(..), display: (..args) => text(value.display(..args)))` — instead of a bare Typst `datetime`, so the rendered glyphs are born at a generated `text(..)` node carrying a region keyed on the field's schema path: a date placed by a vendored package, or a card's date riding a shared loop variable, is click-to-edit. A blank date stays `none`, so `!= none` guards hold. Plates migrate two shapes: `(data.f.display)("…")` (paren form — the stored `display` is a closure on a dict, not a method) and `data.f.value` for anything native (comparison, `.year()`-family components, datetime-consuming packages). The tonguetoquill flagship quills' `display-date` dispatches on `type(date)`, since their `datetime.today()` blank-date fallback stays a native datetime (#990)
|
|
11
|
+
- **breaking** core,typst,pdfform: split the `datetime` field type into strict `date` and `datetime` (#717/#799 resolved) — `date` accepts a bare `YYYY-MM-DD` and rejects any time component; `datetime` accepts offset-less wall-clock `YYYY-MM-DDThh:mm[:ss]` (seconds zero-filled) and rejects timezone offsets, the space separator, fractional seconds, and bare dates. Offsets are rejected, never dropped (the engine does no zone math); storage stays verbatim; no truncation in either direction. A `date` lowers to the three-component Typst `datetime(year:, month:, day:)` (unchanged emission) and a `datetime` to the six-component constructor, carrying the wall-clock time. The transform schema marks `date` as `format: "date"` (keeping `format: "date-time"` for `datetime`), and the blueprint reads `date<YYYY-MM-DD>` / `datetime<YYYY-MM-DDThh:mm[:ss]>`. Most deployed `datetime` fields hold a bare date and migrate to `type: date` with byte-identical data; the fixtures (usaf_memo, cmu_letter) do so. No deprecation alias — `datetime` rejecting a bare date is the decided end-state (#991)
|
|
12
|
+
- **breaking** python: the binding commits to the typed lanes — field I/O flows through `quill.writer(doc)` / `quill.view(doc)` exclusively and `Document` is quill-free data and structure. Removed (WASM-only by scope, not lag — their audience is not a Python audience): the opaque field store (`store_field` / `store_fields` / `store_fill` and card twins), the content lane (`install` / `revise` / `apply_change`), the quill-free field reads (`get` / `get_card_field`), and the module codec fns (`import_markdown` / `export_markdown` / `rebase` / `map_pos`). The composable `$ext` / field-remove card twins fold onto a trailing `card=None` selector and `push_card` folds into `insert_card(card, at=None)`. Mirrored additions: `writer.revise_field` (the `Delta` receipt is not surfaced), `writer.add_card(kind, fields=None, body=None, at=None)`, `writer.card(i).kind`, and the `Writer` / `CardWriter` / `View` / `CardView` handle classes exported from the package (#970)
|
|
13
|
+
- **breaking** core,wasm,python: complete the `RichText` → `Content` residual sweep (#976 folds into #982) — retire the last informal-`corpus` / model-`RichText` *identifiers* the mechanical rename missed: `CorpusHit` → `ContentHit`, `EditError::CorpusApply` → `EditError::ContentApply`, `RichtextDecodeError::NotCorpus` → `NotContent` (the codec-specific `RichtextDecodeError` type itself is kept), storage DTO `CanonicalRichText` → `CanonicalContent` (serde is unchanged — no wire migration), and the model-generic Typst emitter `emit_richtext` / `emit_richtext_inline` → `emit_content` / `emit_content_inline` (they lower any `Content`, richtext *or* plaintext). Also fixes the `ParseError` display strings `richtext json …` → `content json …`, the doubled-word find-replace debris (`content content model` → `content model`), and stale prose/comments across canon and rustdoc. Schema/codec names are untouched — `richtext` / `plaintext` tokens, `FieldType::{RichText,PlainText}`, `field_richtext` / `FieldRichtext*` / `apply_field_richtext_change`, `richtext(inline)` — they name codecs, not the model. "corpus" is purged from the tree entirely, including the ordinary-English test-set names that meant a *collection* of fixtures (`fixture_corpus` → `fixtures`, `synthetic_corpus` → `synthetic_inputs`) (#982)
|
|
14
|
+
- **breaking** core,wasm,python: a schema-bound read view — `Quill::view(&doc)` / `quill.view(doc)`, the read twin of `quill.writer(doc)`. `view.get(addr)` interprets each field by its declared type (a `richtext` field → markdown, a `plaintext` field → its literal text via the plaintext codec, every other type → its canonical value verbatim), returns absent as `undefined` / `None`, and — the authority the quill-free `getMarkdown` lacks — throws `UnknownField` for a name the schema does not declare and `FieldRichtextDecode` for a content field holding an undecodable value. Core `TypedReader::get` returns a `ReadValue` (`Markdown`/`Plaintext`/`Value`); `view.card(i)` is the card cursor; core adds `Card::field_plaintext` (the `to_plaintext` twin of `field_markdown`). **`getMarkdown`'s field half retires**: `getMarkdown` / `get_markdown` / `get_card_markdown` are now body-only (WASM `getMarkdown` takes a `CardAddr`, a present `field` throws; Python drops the `name` parameter) — a field's markdown is read through `view.get`. The quill-free body projection stays on `Document` (#978)
|
|
15
|
+
- **breaking** content: one delta-application contract — implicit trailing retain is `try_apply`'s semantics (a short delta retains the untouched remainder; the error is over-consumption only), `apply` panics on an over-long delta instead of clamping (clamping is silent corruption), and `extend_to_base` is removed. `split_line` / `join_line` rebase marks through their one-char `\n` splice with `map_pos` — the same mapping the text-delta channel uses — so marks no longer drift across line ops and `apply_field_change` canonicalizes once (a single terminal normalize instead of one per stage); line sync rebuilds in one forward pass instead of per-`\n` `Vec` splices. Mark ops are specified in final-text coordinates (post-delta, post-line-op — the frame they validate against) (#926, #987)
|
|
16
|
+
- **breaking** core: storage blobs tagged `@0.81.0` / `@0.82.0` fail as an unknown schema version — the read-only `V0_81_0` / `V0_82_0` DTO trees and their forward migrations are retired (nothing persisted on this lineage predates `@0.92.0`; `0.82.0` was yanked). `V0_92_0` stays the oldest shape read, and its payload types back the current write path. DOCUMENT_STORAGE.md records variant retirement as the policy when no stored population remains (#929)
|
|
17
|
+
- **breaking** core,wasm,python: the markdown projection stops appending a trailing newline — `to_markdown` projects a *value*, not a file, so `field_markdown` / `body_markdown` (WASM `getMarkdown` / `exportMarkdown`, Python `export_markdown` / `get_markdown`) no longer grow a `\n`; `writer.set("subject", "Hello")` reads back as `"Hello"`, not `"Hello\n"`. `.qmd` files still end in one newline (owned by `Document::to_markdown`, the file writer) and the content fixed point is unchanged (import is newline-insensitive) (#965)
|
|
18
|
+
- **breaking** all: rename the content genus off its codec's name — crate `quillmark-richtext` → `quillmark-content`, type `RichText` → `Content` (and `RichTextLine`/`RichTextContainer`/`RichTextMark`/`RichTextIsland` → `ContentLine`/…), const `RICHTEXT_MEDIA_TYPE` → `CONTENT_MEDIA_TYPE` and its wire string `application/quillmark-richtext+json` → `application/quillmark-content+json`, `#[serde(skip)]` companion caches `FieldSchema::{default,example}_corpus` → `_content`, `SegmentMap.corpus: Range<usize>` → `.content`, Typst-emitter `EmittedContent` → `Emission` (it is markup + source map, not a Typst `content` value). Schema tokens `richtext` / `plaintext`, `FieldType::{RichText,PlainText}` variants, and the codec-specific `field_richtext` / `FieldRichtext*` / `apply_field_richtext_change` / `richtext(inline)` surface are unchanged — those name codecs, not the model. Canonical body JSON is nameless, so stored documents don't migrate; `contentMediaType` consumers pin to the new spelling. Retires the informal "corpus" noun to end the code/prose split (#976)
|
|
19
|
+
- **breaking** core,wasm,python: `getMarkdown` / `get_markdown` / `get_card_markdown` stop conflating an absent field with a present-but-not-richtext one — a present field that does not decode as richtext (a scalar/array/object a `storeField` wrote) now throws `FieldRichtextDecode` instead of reading back `undefined` / `""`; absence still returns the absent shape. Core `Card::field_markdown` becomes `Option<Result<String, RichtextDecodeError>>` (the projection twin of `field_richtext`). Rule: absence returns, mismatch raises; read the raw value with `get` (#968)
|
|
20
|
+
- feat(core,wasm): typed, anchor-preserving field revise — `TypedWriter::revise_field` / `CardWriter::revise_field` and `writer.reviseField` / `writer.card(i).reviseField` wrap core `Card::revise_field_checked` (diff-rebase surviving anchors, then schema-conform the result); the schema-bound verb lives on the writer, where the schema is (#957, #966)
|
|
21
|
+
- **breaking** wasm: the quill-taking `Document` methods become the hidden ABI under the writer — `commitField` / `commitFields` / `addCard` → `_commitField` / `_commitFields` / `_addCard`, dropped from the `.d.ts`; remove `doc.reviseChecked` (no runtime consumer — use `writer.reviseField`). The visible `Document` class then carries zero quill-taking methods (#966)
|
|
22
|
+
- **breaking** core: rename `EditError::BodyImport` → `EditError::Import` (message `body import failed:` → `markdown import failed:`) — the variant also fires on field-path imports (`revise_field`), where "body" misnamed it (#966)
|
|
23
|
+
- **breaking** wasm: fold `pushCard` into `insertCard(card, at?)` — one insertion verb per lane, absent `at` appends; `insertCard`'s parameters reorder to `(card, at?)`. Delete the deprecated `replaceBody` alias (use `revise({}, md)` or `writer.setBody`) (#961)
|
|
24
|
+
- feat(core,wasm): positioned card insert — `TypedWriter::add_card` / `writer.addCard` and the `addCard` ABI take an `at` position, so a positioned typed insert is one atomic call instead of `addCard` + `moveCard`; add `TypedWriter::remove_card` (mirrors JS `writer.removeCard`) and a JS `CardWriter.kind` getter (mirrors core `CardWriter::kind()`) (#961)
|
|
25
|
+
- **breaking** core: `Payload::insert` / `insert_fill` now validate the field-name and value-depth invariant at the boundary and return `Result<_, FieldViolation>`, closing the `payload_mut().insert(...)` hole that let a direct caller build an invalid document; pre-validated internal callers use the new `pub(crate)` `insert_unchecked` / `insert_fill_unchecked` (#958)
|
|
26
|
+
- feat(wasm,core): single-card reads — `doc.card(i)` (throws out of range), `doc.cardIndexById(id)` (first match; `$id` is non-unique), and `doc.seedOverlay(kind)`, backed by core `Document::card(i)` / `find_card(id)`. Reading one card, resolving a `$id`, or fetching a `$seed` overlay no longer serializes the whole `cards` array or main card (#956)
|
|
27
|
+
- **breaking** core: parse warnings live only on `ParseOutput` — the redundant `Document::warnings` field + `warnings()` getter are dropped and `Document::from_main_and_cards` no longer takes a `warnings` param (`Document` `PartialEq` is now a plain derive) (#959)
|
|
28
|
+
- **breaking** core: collapse the two parse functions into one entry — `Document::from_markdown` and `Document::from_markdown_with_warnings` are removed in favor of `Document::parse(md) -> Result<Parsed, ParseError>`, and `ParseOutput` is renamed `Parsed`. A document-only caller writes `parse(md)?.document`. Bindings are unaffected: WASM `Document.fromMarkdown` / Python `Document.from_markdown` keep their names and their `doc.warnings` getter (#964)
|
|
29
|
+
- feat(wasm,python): keyed card reads `getCardField(index, name)` / `getCardMarkdown(index, name?)` (py `get_card_field` / `get_card_markdown`) — the card-indexed twins of `get` / `getMarkdown`, mirroring the `commitCardField` / `setCardField` write verbs so card reads no longer require a `payloadItems` walk (#953)
|
|
30
|
+
- feat(content,wasm,python): `LineOp::SetContinues { line, continues }` — hard breaks lower op-wise. Split, join, and a text-delta `\n` all mint `continues: false` lines, so a within-block hard break (a paragraph hard break, a code fence's interior line) had no op and fell back to a whole-install, losing that edit's identity anchors. Threaded through the wire codec into WASM `applyChange` (TS union updated) and Python; `continues: true` on line 0 is rejected with `ApplyError::FirstLineContinues` before the write, leaving the content untouched (#949)
|
|
31
|
+
- feat(wasm): the runtime root re-exports the edit vocabulary its own signatures reference — `Content` / `ContentLine` / `ContentContainer` / `ContentMark` / `ContentIsland`, `Addr` / `Delta` / `Assoc` / `LineOp` / `MarkOp` / `ChangeBundle`, `CardInput` / `PathStep` — as type-only exports (single entry point preserved; no `/core` subpath), with a presence guard so a dropped re-export fails `npm run typecheck` (#948)
|
|
32
|
+
|
|
33
|
+
<!-- seed: commits since v0.94.0 — confirm the entries above cover them, then delete this comment
|
|
34
|
+
- chore: prune redundant logic and duplicate tests (post-0.94.0 residue scan) (#996)
|
|
35
|
+
- release: unbreak the crates.io publish lane; fold curated notes into the seed (#995)
|
|
36
|
+
- Emit date fields as click-to-edit value-objects (#990) (#994)
|
|
37
|
+
- Split `datetime` into strict `date` and `datetime` types (#991) (#993)
|
|
38
|
+
- Python binding: commit to the Tier-1 surface (#970) (#992)
|
|
39
|
+
- audit #982: complete the Content-genus residual sweep (retire "corpus") (#989)
|
|
40
|
+
- feat: schema-bound read view — `quill.view(doc)` and `TypedReader` (#988)
|
|
41
|
+
- Rebase marks through line ops; collapse bundle normalize (#987)
|
|
42
|
+
- core: retire the V0_81_0 and V0_82_0 storage read shims (#929) (#986)
|
|
43
|
+
- Document binding build performance guidance in CLAUDE.md (#984)
|
|
44
|
+
- richtext: to_markdown projects a value, not a file — no trailing newline (#965) (#977)
|
|
45
|
+
- docs(markdown-spec): scope $body wire claim, fix lossless→lossy projection (#983)
|
|
46
|
+
- Delete prose/review directory
|
|
47
|
+
- rename: content genus off its codec's name — RichText → Content, crate → quillmark-content (#976) (#981)
|
|
48
|
+
- Add note to not run cargo fmt (#980)
|
|
49
|
+
- fix(core,wasm,python): getMarkdown surfaces present-but-not-richtext instead of blanking (#968) (#979)
|
|
50
|
+
- docs: purge rogue .qmd file-extension mentions (#975)
|
|
51
|
+
- Rewrite CLAUDE.md for density (#974)
|
|
52
|
+
- docs: fix mkdocs strict build — drop cross-tree link to prose/canon
|
|
53
|
+
- wasm: name the main-card address (MAIN_CARD_ADDR), reject unknown addr keys (#969)
|
|
54
|
+
- core: collapse the two parse functions into one `Document::parse` -> `Parsed` (#964)
|
|
55
|
+
- core,wasm: writer-level reviseField; hide the quill-taking Document ABI (#966)
|
|
56
|
+
- core,wasm,docs: dense-prose pass over the #963 write surface
|
|
57
|
+
- docs: document the write-surface reshape (#955, #957, #960)
|
|
58
|
+
- python: rename opaque store verbs set_* → store_* (#960)
|
|
59
|
+
- wasm: unify Document on Addr addressing; store_* verbs; reviseChecked (#955, #960, #957)
|
|
60
|
+
- core: rename opaque store verbs set_* → store_*; add revise_field_checked (#960, #957)
|
|
61
|
+
- core: collapse the Payload insert helpers; tighten prose
|
|
62
|
+
- core: use plain code spans for pub(crate) refs in Payload::insert docs
|
|
63
|
+
- docs: 0.94→0.95 migration guide and BINDINGS parity refresh (#956, #958, #959, #961)
|
|
64
|
+
- wasm,core: writer/card-surface parity cleanups (#961)
|
|
65
|
+
- wasm,core: single-card, $id, and seed-overlay reads (#956)
|
|
66
|
+
- core: enforce field invariants at the Payload::insert boundary (#958)
|
|
67
|
+
- core: make ParseOutput the single owner of parse warnings (#959)
|
|
68
|
+
- docs(bindings): densify the card-read doc comments
|
|
69
|
+
- feat(wasm,python): keyed card reads mirroring the card write verbs (#953)
|
|
70
|
+
- docs: remove prose/simplifications backlog for greenfield re-analysis
|
|
71
|
+
- Add setContinues line op so hard breaks lower op-wise (#949)
|
|
72
|
+
- Re-export corpus edit vocabulary from @quillmark/wasm root (#948)
|
|
73
|
+
-->
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## v0.94.0 - 2026-07-15
|
|
77
|
+
|
|
78
|
+
These notes cover everything since v0.92.1. No 0.93.x was separately
|
|
79
|
+
published — the 0.93 milestone folds into this release, so the upgrade path
|
|
80
|
+
from 0.92.1 is the `0.92-to-0.93` and `0.93-to-0.94` guides read in sequence.
|
|
81
|
+
|
|
82
|
+
- feat(wasm): the live-session / canvas-paint surface graduates from
|
|
83
|
+
`@experimental` to stable — `Engine.open`, `LiveSession`, `apply` /
|
|
84
|
+
`ChangeSet`, `paint` / `PaintOptions` / `PaintResult`, `PageSize`, and the
|
|
85
|
+
`supportsCanvas` probe are now the committed preview API. The tag is dropped
|
|
86
|
+
from the runtime `.d.ts` / `.js`, the wasm README, and `PREVIEW.md`; further
|
|
87
|
+
shape changes follow the normal deprecation path rather than landing in any
|
|
88
|
+
0.x. `Engine.render` / `supportedFormats` remain the one-shot path
|
|
89
|
+
- refactor(core)!: field ordering becomes fully structural — `ui.order` is
|
|
90
|
+
removed and an authored `order:` is a load error. Field and card-kind display
|
|
91
|
+
order is now the key order of the emitted schema (declaration order, backed by
|
|
92
|
+
an `IndexMap`), and the auto-stamped `order:` integer disappears from
|
|
93
|
+
`QuillConfig::schema()`; consumers walk the maps in key order instead of
|
|
94
|
+
sorting on a stamped index. Typed-dictionary / typed-table-row properties
|
|
95
|
+
render in declaration order, not alphabetically (#941). See
|
|
96
|
+
`docs/migrations/0.93-to-0.94.md`
|
|
97
|
+
- feat(core)!: a card-level `ui.groups` registry gives groups identity and
|
|
98
|
+
order. `ui.group` becomes a validated reference to a snake_case id
|
|
99
|
+
(`quill::unknown_group` for a dangling ref); the registry's declaration order
|
|
100
|
+
fixes group display order, labels derive from the id with a `title:` override,
|
|
101
|
+
and a bare label-as-identity group is deprecated (`quill::implicit_group`). A
|
|
102
|
+
nested `ui.group` is a load error (`quill::nested_group_not_supported`) (#941).
|
|
103
|
+
See `docs/migrations/0.93-to-0.94.md`
|
|
104
|
+
- feat(core,typst,wasm,python)!: `plaintext` and a first-class `enum` join the
|
|
105
|
+
schema. `plaintext` is navigable unformatted prose carried over the richtext
|
|
106
|
+
corpus (a literal codec, with a `plaintext(field)` helper on the Typst side);
|
|
107
|
+
`enum` is promoted to `type: enum` + `values:`, and the `enum:` modifier on
|
|
108
|
+
`string` is deprecated for one release. `string` narrows to open scalar data
|
|
109
|
+
(#938). See `docs/migrations/0.93-to-0.94.md`
|
|
110
|
+
- refactor(core)!: `type: richtext(inline)` retires — declare `type: richtext`
|
|
111
|
+
with `inline: true`. The old token is a hard `quill::field_parse_error`, and
|
|
112
|
+
`inline: true` on a non-richtext field is likewise rejected. Blueprint still
|
|
113
|
+
emits `richtext(inline)<markdown>` and `build_transform_schema` gains
|
|
114
|
+
`quillmark:inline: true`, both derived from the flag; documents and corpus
|
|
115
|
+
wire shapes are unaffected. See `docs/migrations/0.93-to-0.94.md`
|
|
116
|
+
- refactor(pdfform)!: `form.json` slims to a binding layer (`form@0.2.0`). Bound
|
|
117
|
+
`fields` drop `type` / `options` / `multiline` (derived from the schema
|
|
118
|
+
field's kind, `enum` values, and `ui.multiline`); unbound widgets move to a
|
|
119
|
+
`widgets` section; binding runs at load, so a bad `schema_field` fails with
|
|
120
|
+
`pdfform::dangling_binding` / `pdfform::unbindable_field` instead of a silent
|
|
121
|
+
blank. `form@0.1.0` is rejected and `$cards` absolute-index addressing is
|
|
122
|
+
removed. Widget geometry is placed once at bind, not per render (#940). See
|
|
123
|
+
`docs/migrations/0.93-to-0.94.md`
|
|
124
|
+
- refactor(core,richtext,wasm,python)!: the binding write surface settles into
|
|
125
|
+
two tiers over a document-free corpus codec. `quill.writer(doc)` (wasm and
|
|
126
|
+
Python alike) is the documented default — typed `set` / `set_all` / `setBody`
|
|
127
|
+
/ `addCard` / `card(i)` and quill-free `get` / `getMarkdown` reads — layered
|
|
128
|
+
over the corpus lane (`importMarkdown` / `exportMarkdown` / `rebase` / `mapPos`
|
|
129
|
+
plus the addressed `install` / `revise` / `applyChange` verbs) and the opaque
|
|
130
|
+
`setField` primitive. The eager `bodyMarkdown` / `fieldMarkdown` projections
|
|
131
|
+
and the per-address body writers retire pre-release; `replaceBody` /
|
|
132
|
+
`replace_body` / `update_card_body` alias for one cycle; richtext fields gain
|
|
133
|
+
the anchor-preserving `revise_field`; the addressed `commit(addr, …)` is
|
|
134
|
+
deleted (subsumed by the writer). A core-vs-bindings parity table governs
|
|
135
|
+
drift (#925, #932). See `docs/migrations/0.93-to-0.94.md`
|
|
136
|
+
- refactor(wasm)!: the `Card` shape splits by direction — a read `Card` always
|
|
137
|
+
carries `body: RichText`, while `pushCard` / `insertCard` take a `CardInput`
|
|
138
|
+
whose `body` still accepts a markdown string and whose non-`kind` fields are
|
|
139
|
+
optional (#917). The card-write verbs become mechanical twins of their
|
|
140
|
+
main-card names: `updateCardField` / `updateCardFields` rename to
|
|
141
|
+
`setCardField` / `setCardFields` (#895). See `docs/migrations/0.93-to-0.94.md`
|
|
142
|
+
- fix(typst/overlay): underline / strike decoration ink no longer truncates
|
|
143
|
+
`$body` field regions — the region geometry is taken before decoration strokes
|
|
144
|
+
extend the glyph ink box, so a highlighted body field's box matches the text
|
|
145
|
+
instead of the overrun (#937)
|
|
146
|
+
- chore: migrate org references `quillmark-org` → `borb-sh` across the tree
|
|
147
|
+
- fix(richtext): the markdown-export codec never leaks a delimiter into the
|
|
148
|
+
corpus. An editor `apply_mark_ops` mark can wrap a span markdown can't
|
|
149
|
+
represent (a `strong`/`emph`/`strike` edge on punctuation/symbols/whitespace,
|
|
150
|
+
or abutting a literal `*`) — the run would re-import as literal `**`/`*`/`~~`
|
|
151
|
+
text (bolding `a.` used to export `**a.**b`). `to_markdown` now verifies each
|
|
152
|
+
rendered line by re-parse and drops any mark whose emission would alter the
|
|
153
|
+
text, so the text always round-trips; only the unrepresentable formatting is
|
|
154
|
+
lost. Import-domain corpora are unaffected (still an exact fixed point).
|
|
155
|
+
- feat(core,wasm,python)!: typed field writes via schema-carried types. One
|
|
156
|
+
per-type write dispatch (`conform_value(value, schema, mode)`) unifies the
|
|
157
|
+
render floor's coercion with a strict-write mode behind a `Leniency` flag; one
|
|
158
|
+
typed writer per address, `Card::commit_field(name, value, &FieldSchema)`,
|
|
159
|
+
dispatches on the schema — the write surface stays O(1) in field types. Adds
|
|
160
|
+
`EditError::FieldConform` for non-richtext mismatches (richtext keeps
|
|
161
|
+
`FieldRichtextDecode` / `FieldRichtextNotInline`). A schema-bound
|
|
162
|
+
`TypedWriter` (`Quill::writer(&mut doc)`) is the front door: `set` / `set_all`
|
|
163
|
+
resolve field types and strict-commit; a name the schema does not declare is a
|
|
164
|
+
typo on the typed path, so it fails with `EditError::UnknownField` instead of
|
|
165
|
+
falling to the opaque store (#918) — opaque storage stays available through the
|
|
166
|
+
raw `set_field` / `setField` / `setCardField` verbs. Bindings gain
|
|
167
|
+
`commitField` / `commitCardField` (wasm) and `commit_field` /
|
|
168
|
+
`commit_card_field` (Python, net-new — Python had no richtext field writer).
|
|
169
|
+
The pre-release richtext-specific writers are removed in the same cycle:
|
|
170
|
+
`Card::set_field_richtext`, wasm `setRichtextField` / `updateCardRichtextField`
|
|
171
|
+
— use the typed writer, which carries the `inline` constraint in the schema.
|
|
172
|
+
Strict writes drop the render floor's cross-type `Boolean`↔`Number` coercions
|
|
173
|
+
and fail a shape mismatch at the write, not at a later render (#893)
|
|
174
|
+
- remove(core,richtext,wasm)!: delete the incremental-edit surface — the
|
|
175
|
+
per-field change log and everything layered on it: `richtext::ChangeLog` /
|
|
176
|
+
`FieldChange` / `StaleRevision`; `LiveSession::revision` /
|
|
177
|
+
`record_field_delta_at` / `record_field_change_at` / `ensure_base_revision` /
|
|
178
|
+
`map_field_pos` / `apply_for_field_delta`; the WASM `applyFieldDelta` /
|
|
179
|
+
`mapFieldPos` / `revision` and the `Delta` DTO; and the `revision` stamp on
|
|
180
|
+
`RenderedRegion` / `CorpusHit` (and `FieldRegion` / `CorpusHit` on the wire).
|
|
181
|
+
Anchoring a caret or selection across edits belongs to the editor's own
|
|
182
|
+
transaction mapping (a ProseMirror / CodeMirror `StepMap`), not a parallel
|
|
183
|
+
core-side position map: the bidirectional preview↔editor cursor bridge is
|
|
184
|
+
`positionAt` / `locate` over the current compile, exact inverses that never
|
|
185
|
+
consulted the change log. Whole-document `apply(doc)` stays the one edit verb.
|
|
186
|
+
This dissolves #886's anchor-stranding half outright and drops the
|
|
187
|
+
half-built delta path behind its per-keystroke-marshalling half; `Delta` /
|
|
188
|
+
`diff` / `diff_import` / the mark & line op channels remain as the corpus
|
|
189
|
+
writers' substrate (`replace_body`, `import_body_delta`, `apply_body_change`)
|
|
190
|
+
(#886)
|
|
191
|
+
- feat(core,wasm): `field_boxes(field)` / `LiveSession.fieldBoxes(field)` derive
|
|
192
|
+
the whole-field highlight — one union rect per page over the field's
|
|
193
|
+
`span`-bearing content segments — so a "highlight the focused field" consumer
|
|
194
|
+
stops reimplementing the span-filter + per-page union by hand. `regions()`
|
|
195
|
+
stays the low-level disjoint truth (#829); the helper owns the union, and is
|
|
196
|
+
content-only (a scalar-reference/widget-only field returns `[]`, its box being
|
|
197
|
+
a single `regions()` rect). Core `field_boxes(&[RenderedRegion], field)` is a
|
|
198
|
+
pure function so the one-shot `RenderResult.regions` sidecar gets it too (#884)
|
|
199
|
+
- feat(core,wasm): `CorpusHit.granularity` (`HitGranularity` = `cluster` |
|
|
200
|
+
`segment`) reports whether `positionAt`'s `pos` resolved cluster-exact or
|
|
201
|
+
floored to the containing segment's start (origin-less ink, a multi-line code
|
|
202
|
+
fence's interior), so a caret UI trusts a `cluster` offset for the caret and
|
|
203
|
+
treats a `segment` one as a segment selection instead of guessing. Additive-
|
|
204
|
+
optional, omitted from the wire when the backend does not report it (#884)
|
|
205
|
+
- fix(wasm): `Engine.supportsCanvas` and `LiveSession.supportsCanvas` gain doc
|
|
206
|
+
comments cross-referencing each other: the two are spelled identically but
|
|
207
|
+
answer different questions (a pre-session backend estimate vs. this compile's
|
|
208
|
+
authoritative answer, which can diverge — e.g. a 0-page document) — the
|
|
209
|
+
divergence is now visible where each is used instead of only discoverable at
|
|
210
|
+
runtime (#883)
|
|
211
|
+
- fix(core): drop two rustdoc intra-doc links from public items
|
|
212
|
+
(`RichtextDecodeError`, `Card::set_field_richtext`) to the private
|
|
213
|
+
`decode_richtext_value`, which `-D rustdoc::private-intra-doc-links` (part of
|
|
214
|
+
the lint gate) rejects since the link can never resolve for a doc reader;
|
|
215
|
+
reworded to a plain code span, matching the existing convention elsewhere in
|
|
216
|
+
the same file for referencing a private helper from public docs
|
|
217
|
+
- fix(wasm): drop the `revision?` field from the public `CorpusHit`/`FieldRegion`
|
|
218
|
+
types and the broken `{@link LiveSession.mapFieldPos}` / `.revision` references
|
|
219
|
+
in `runtime.d.ts`. The delta API (`applyFieldDelta`/`revision`/`mapFieldPos`) is
|
|
220
|
+
not forwarded through `runtime.js`, so no published consumer could reach the
|
|
221
|
+
methods those fields pointed at, and the stamped `revision` was always `0` on
|
|
222
|
+
the reachable read paths (whole-doc `apply` is revision-neutral). The public
|
|
223
|
+
types no longer advertise a capability the shipped `LiveSession` doesn't expose
|
|
224
|
+
(#850)
|
|
225
|
+
- refactor(core)!: `RenderSession` collapses into `LiveSession` — a persistent,
|
|
226
|
+
incremental compiler that owns preview (#778). Reads (`render`, the canvas
|
|
227
|
+
seam, `regions`) serve the session's current compile; the new transactional
|
|
228
|
+
`apply(json_data)` recompiles in place (on `Err` every read keeps serving the
|
|
229
|
+
last-good compile) and returns `ChangeSet { page_count, dirty_pages }` so a
|
|
230
|
+
preview repaints `dirty ∩ visible`. Typst applies incrementally: the session
|
|
231
|
+
persists its `QuillWorld` (fonts/packages/assets parsed once), swaps document
|
|
232
|
+
data via `Source::replace`, and fingerprints visible page content for the
|
|
233
|
+
dirty set; pdfform re-resolves + re-flattens (cheap by construction). New
|
|
234
|
+
`RenderError::ApplyUnsupported` is the seam default. The callerless
|
|
235
|
+
`typst_session_of` is removed. WASM: the `RenderSession` class is renamed
|
|
236
|
+
`LiveSession` and gains `apply(doc): ChangeSet`; don't re-open per edit. The
|
|
237
|
+
Typst backend now evicts `comemo`'s process-global cache after every compile,
|
|
238
|
+
bounding memory over long editing sessions. See
|
|
239
|
+
`docs/migrations/0.92-to-0.93.md`
|
|
240
|
+
- remove(dotnet)!: drop the .NET binding (`crates/bindings/dotnet`, the
|
|
241
|
+
`quillmark-dotnet` crate, its `csharp/` managed layer, CI job, and NuGet
|
|
242
|
+
publish workflow). Second-class and unmaintained relative to WASM/Python;
|
|
243
|
+
removed rather than carried as bloat. Python and WASM are unaffected.
|
|
244
|
+
- refactor(core)!: field regions move from `RenderResult` to a session-level
|
|
245
|
+
query, `RenderSession::regions()` (WASM `session.regions()`), and are keyed on
|
|
246
|
+
the quill schema field path, not the backend widget. Only the interactive
|
|
247
|
+
preview path wants region geometry; a one-shot byte render (PDF/PNG/SVG) does
|
|
248
|
+
not, so `RenderResult.regions` is removed and the geometry is read once off the
|
|
249
|
+
compiled session without a render. `RenderedRegion` (and the WASM
|
|
250
|
+
`FieldRegion`) drop `name`/`kind`/`fieldType`/`value` for a single `field`
|
|
251
|
+
carrying the schema address (e.g. `signature_block`); the pdfform AcroForm
|
|
252
|
+
widget name no longer leaks. A region is emitted only for a schema-bound field
|
|
253
|
+
— an unbound widget produces none. `RegionKind` is removed; the `quillmark-pdf`
|
|
254
|
+
`FieldSpec` gains `schema_field` and `stamp`/`flatten` return plain bytes
|
|
255
|
+
(`StampResult` is gone). Regions are geometry for overlays and canvas↔editor
|
|
256
|
+
cross-navigation, never a compositing input (#773). See
|
|
257
|
+
`docs/migrations/0.92-to-0.93.md`
|
|
258
|
+
- feat(pdfform)!: the `pdfform` backend now exports PNG and SVG as first-class
|
|
259
|
+
`render()` output formats (`SUPPORTED_FORMATS == [Pdf, Svg, Png]`); PNG
|
|
260
|
+
rasters at `RenderOptions::ppi` (default 144). The `preview` cargo feature is
|
|
261
|
+
removed — the hayro raster/SVG/PNG seam is always linked, so SVG/PNG/canvas
|
|
262
|
+
work out of the box rather than behind a flag. The `quillmark` crate's
|
|
263
|
+
`pdfform-preview` feature is folded into `pdfform`; in the wasm crate both the
|
|
264
|
+
`typst` and `pdfform` build variants link the `web-sys` canvas painter directly
|
|
265
|
+
- fix(quillmark-pdf): `find_dict_value` now walks the dict as strict
|
|
266
|
+
key→value pairs, so a Name in *value* position (e.g. `/Subtype /Producer`)
|
|
267
|
+
is no longer mis-matched as a key; the object/dict scanners also skip
|
|
268
|
+
`%`-comments, so `endobj` or a key token inside a comment can't derail
|
|
269
|
+
parsing of a base PDF. The `<<…>>`/`[…]` depth walkers (`extract_outer_dict`
|
|
270
|
+
and `read_value_end`'s nested-dict/array branches) skip `%`-comments and
|
|
271
|
+
literal `(…)` strings uniformly, so a `>>`/`]` carried inside a comment or
|
|
272
|
+
string no longer truncates a dict/array and drops the keys after it
|
|
273
|
+
- feat(pdfform): add the Typst-free `pdfform` backend + shared `quillmark-pdf`
|
|
274
|
+
AcroForm stamping spine; rewire Typst signatures onto the spine; thread a
|
|
275
|
+
`regions` sidecar through `RenderResult` and generalize the raster-preview
|
|
276
|
+
seam (#749, #750). See `prose/canon/ARCHITECTURE.md` and
|
|
277
|
+
`docs/quills/pdfform-backend.md` for the shipped design.
|
|
278
|
+
- refactor(pdfform): PDF output is always an interactive AcroForm (Technique A).
|
|
279
|
+
Value-flattening is internal machinery backing the SVG/PNG/canvas raster
|
|
280
|
+
outputs, never a PDF deliverable. The public `RenderOptions.flatten` knob is
|
|
281
|
+
removed across core and all four bindings (it was wired only in wasm, hardcoded
|
|
282
|
+
`false` in Python, and ignored in .NET)
|
|
283
|
+
- fix(pdfform): the flatten path transcodes values to WinAnsi (with a
|
|
284
|
+
`WinAnsiEncoding` font) so accented/Latin-1 text renders correctly in the
|
|
285
|
+
raster output, and clips each value to its field box so long values can't
|
|
286
|
+
overflow
|
|
287
|
+
- refactor(quillmark-pdf): hoist the shared PDF byte-serialization (object/text
|
|
288
|
+
writers, `/Info /Producer` stamp) into `quillmark_pdf::writer`, consumed by
|
|
289
|
+
both the stamp and flatten paths; `find_object_bytes` now matches any object
|
|
290
|
+
generation and returns the live (last) revision
|
|
291
|
+
- docs(canon): canonize `$ext.editor.title` as the slot for a per-card display name
|
|
292
|
+
- refactor(core)!: remove the hand-set `Backend::supports_canvas()`; derive
|
|
293
|
+
canvas capability from the one seam instead. `RenderSession::supports_canvas()`
|
|
294
|
+
(authoritative, from `page_size_pt`) and `formats_support_canvas()`
|
|
295
|
+
(pre-session hint, from output formats) replace it, so the capability can no
|
|
296
|
+
longer disagree with what `paint` does. The engine and WASM `supportsCanvas`
|
|
297
|
+
surfaces are unchanged in shape. See `docs/migrations/0.92-to-0.93.md`
|
|
298
|
+
- build(wasm)!: rename the WASM engine feature `render` → `typst` (now the
|
|
299
|
+
default) and add a `pdfform` build variant, so a Typst-free
|
|
300
|
+
PDF-form bundle can ship without Typst. From-source builders pass
|
|
301
|
+
`--features typst` where they used `--features render`; the published JS API is
|
|
302
|
+
unchanged. See `docs/migrations/0.92-to-0.93.md`
|
|
303
|
+
|
|
3
304
|
## v0.92.1 - 2026-06-22
|
|
4
305
|
|
|
5
306
|
- Accept uppercase field names; reserve only `$`-prefixed keys (#730)
|
|
6
307
|
- docs: canonize $ext.editor.title as per-card display name slot (#729)
|
|
7
308
|
|
|
8
309
|
|
|
9
|
-
## Unreleased
|
|
10
|
-
|
|
11
|
-
- docs(canon): canonize `$ext.editor.title` as the slot for a per-card display name
|
|
12
|
-
|
|
13
310
|
## v0.92.0 - 2026-06-22
|
|
14
311
|
|
|
15
312
|
- 0.92 technical-debt sweep: correctness, $seed hardening, de-duplication (#727)
|