@quillmark/wasm 0.111.0 → 0.112.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 +255 -0
- package/README.md +2 -2
- package/backends/pdfform/wasm.d.ts +38 -23
- package/backends/pdfform/wasm.js +19 -7
- package/backends/pdfform/wasm_bg.wasm +0 -0
- package/backends/pdfform/wasm_bg.wasm.d.ts +2 -2
- package/backends/typst/wasm.d.ts +38 -23
- package/backends/typst/wasm.js +19 -7
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +2 -2
- package/core/wasm.d.ts +17 -12
- package/core/wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/runtime/runtime.d.ts +26 -16
- package/runtime/runtime.js +25 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,260 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.112.0 - 2026-09-01
|
|
4
|
+
|
|
5
|
+
- feat(content)!: **every vocabulary member spells its payload in `attrs`.** The
|
|
6
|
+
canonical content had two spellings for one thing: a built-in put its payload
|
|
7
|
+
in named siblings (`{"kind":"heading","level":1}`), an unknown put it in one
|
|
8
|
+
opaque bag, and which a document used depended on whether the build that wrote
|
|
9
|
+
it happened to know the name. Promoting a name from unknown to built-in was
|
|
10
|
+
therefore an encoding change, and five mechanisms existed to bridge the split.
|
|
11
|
+
Now `Heading{level}` ⇄ `attrs.level`, `Code{lang}` ⇄ `attrs.lang`, `Link{url}`
|
|
12
|
+
⇄ `attrs.url`, `Anchor{id}` ⇄ `attrs.id`, and `ListItem`'s
|
|
13
|
+
`ordered`/`start`/`ordinal` ⇄ the same three under `attrs`, with the envelope
|
|
14
|
+
keys (`kind`/`type`/`container`, `containers`, `continues`, `start`/`end`,
|
|
15
|
+
`instance`) staying siblings and an empty bag omitted. The Rust types are
|
|
16
|
+
unchanged — typed in memory, uniform on the wire. `fold_legacy_attrs`, the
|
|
17
|
+
`reject_*_attrs` family, `MarkKind::ord` and its placement rule, and
|
|
18
|
+
`RESERVED_*`'s wire role are all deleted; the canonical mark tie-break becomes
|
|
19
|
+
`MarkKind::sort_key`, the `(type, attrs)` pair the wire carries, which two
|
|
20
|
+
builds compute identically whether or not either knows the member. **Stored
|
|
21
|
+
documents need no migration**: the decoder reads the old spelling wherever the
|
|
22
|
+
`attrs` bag is absent. That is the load-bearing half — a `richtext` field
|
|
23
|
+
rests as a content object inside an opaque payload value carrying no schema
|
|
24
|
+
tag, so most stored content reaches no migration and a walk cannot safely find
|
|
25
|
+
it (`$ext` is arbitrary host data by contract). The fallback is frozen: unlike
|
|
26
|
+
the fold, a promotion neither grows it nor inherits it. The **break is at the
|
|
27
|
+
seam**: a host reading `line.level` / `line.lang` / `mark.url` / `mark.id` or a
|
|
28
|
+
list item's shape reads them under `attrs` (the TS narrowing types move too, so
|
|
29
|
+
`typecheck` reports it), and the authored lane — `overwrite`, `install`,
|
|
30
|
+
`CardInput.body`, and the op wire — *rejects* the old spelling rather than
|
|
31
|
+
reading it, so a stale write throws instead of landing somewhere it did not
|
|
32
|
+
aim. A foreign bag beside a built-in becomes legal in exchange, dropping unread
|
|
33
|
+
as it always did in effect. Canonical bytes move (`attrs` on built-ins, and
|
|
34
|
+
coincident marks reorder on the new tie-break), so **content hashes recompute
|
|
35
|
+
once**; generated Typst nests coincident wraps the other way for identical
|
|
36
|
+
glyphs, and `exportMarkdown` nests them the same way (`**~~x~~**` becomes
|
|
37
|
+
`~~**x**~~`, parsing back to the same content), so goldens and any hash over
|
|
38
|
+
either projection move too. A consumer holding bare seam JSON outside a
|
|
39
|
+
stored document gets a hard break, having no tag to dispatch on. The storage
|
|
40
|
+
tag becomes `quillmark/document@0.112.0`; `@0.93.0` rows migrate forward on
|
|
41
|
+
read. See [0.111 → 0.112](docs/migrations/0.111-to-0.112.md).
|
|
42
|
+
- feat(core)!: **`fieldAt` and `positionAt` take a pointer tolerance, and
|
|
43
|
+
resolve to the nearest ink rather than the first containing it.** A glyph's
|
|
44
|
+
box is its run's ink height by its own advance, so a text column answers over
|
|
45
|
+
a fraction of the area it occupies: the boxes of one line abut, but the
|
|
46
|
+
leading between two lines is inside the paragraph and on no glyph. An 11pt
|
|
47
|
+
paragraph is live over about two thirds of its own height at default leading
|
|
48
|
+
and under half of it double-spaced, which is the whole of why clicking the
|
|
49
|
+
preview to place a caret misses. Both queries now take `tol` in PDF points
|
|
50
|
+
(`tolPt`, optional, on the WASM seam) and answer with the nearest placement
|
|
51
|
+
within it, a radius in both axes. The caller derives it from the scale it drew
|
|
52
|
+
the page at, slack being a property of the pointer rather than of the
|
|
53
|
+
document: a tolerance fixed in points shrinks under the cursor exactly as the
|
|
54
|
+
target does. Ranking by distance rather than growing each rect is what keeps
|
|
55
|
+
the answer the nearer item's — outset boxes overlap, and a first match over
|
|
56
|
+
them decides by paint order — and makes the tolerance a pure widening:
|
|
57
|
+
containment is distance zero, so no point that resolves exactly changes answer
|
|
58
|
+
however high `tol` goes, and later-painted still wins a tie.
|
|
59
|
+
`RenderedRegion::distance` is the measure, and `contains` is now stated in
|
|
60
|
+
terms of it. The break is the added argument on
|
|
61
|
+
`SessionHandle::{field_at,position_at}` and their `LiveSession` forwarders,
|
|
62
|
+
which a type checker reports; `0.0` is the previous behaviour exactly. The
|
|
63
|
+
WASM argument is optional and defaults to `0`, so no JS caller changes.
|
|
64
|
+
- fix(content): **markdown export escapes what markdown strips at a line's
|
|
65
|
+
edges.** A line's leading and trailing space/tab crossed verbatim, so
|
|
66
|
+
`from_markdown(to_markdown(rt))` dropped it — and where the leading run
|
|
67
|
+
opened an indented code block, or hid a `- ` / `# ` / `> ` / `N. ` marker
|
|
68
|
+
from the position-0 escapes, the re-import rewrote the line's kind and
|
|
69
|
+
containers with it: `" foo"` returned as `"foo"` under `Code`, and
|
|
70
|
+
`" - item"` as `"item"` inside a `ListItem` nobody wrote. Neither takes
|
|
71
|
+
adversarial input, since `apply_text_delta` and `from_plaintext` both mint
|
|
72
|
+
edge whitespace: a `Codec::Plaintext` field holding an indented sample was
|
|
73
|
+
corrupted by `Document::to_markdown`. The verify-and-drop net could not catch
|
|
74
|
+
it, being gated on flanking marks — a mark-free line never reaches it, and
|
|
75
|
+
the `,…,` probe wrapping the lines that do blocks every leading-block
|
|
76
|
+
construct by design, so it reads such a line as safe while the `**` ships
|
|
77
|
+
into the text. Space and tab at an edge now cross as character references
|
|
78
|
+
(` ` / `	`), markdown having no backslash escape for whitespace. A
|
|
79
|
+
literal `&` still escapes, so text authored as ` ` returns as itself. An
|
|
80
|
+
image `alt` is the one edge run a reference cannot carry — the parser trims
|
|
81
|
+
alt after decoding it — and joins the module's documented codec limits.
|
|
82
|
+
Emitted markdown moves for content holding an edge run.
|
|
83
|
+
- fix(content): **a `=` run on a continuation line stays text.** It underlined
|
|
84
|
+
the paragraph line above it into a setext heading, taking the hard break's
|
|
85
|
+
`\` into the text with it: `"abc\n==="` returned as an H1 reading `"abc\"`.
|
|
86
|
+
`\=` joins the block starters escaped at position 0.
|
|
87
|
+
- fix(content): **an unknown island's type cannot leave its placeholder.**
|
|
88
|
+
`emit_island` wrote `island_type` — an open wire string no lane constrains —
|
|
89
|
+
raw into `<!-- island:… -->`, where a `-->` closes the comment early and a
|
|
90
|
+
line break ends the HTML block, either way leaking the rest as content text.
|
|
91
|
+
At column zero that opens whatever the rest spells, a `~~~` card fence
|
|
92
|
+
included, which the document layer reads as another card. The type crosses
|
|
93
|
+
with `<` / `>` as entities and control characters as spaces; nothing reads it
|
|
94
|
+
back out of markdown.
|
|
95
|
+
- fix(content): **an inline unknown island keeps its line whole.**
|
|
96
|
+
`fix_html_comment_fences` inserted a newline after any `-->` carrying text
|
|
97
|
+
behind it, but only a comment opening a line — at most three spaces of indent
|
|
98
|
+
— starts an HTML block. One reached mid-line is inline HTML and swallows
|
|
99
|
+
nothing, so an inline island turned `ab` into `"a b"`. The repair fires for
|
|
100
|
+
the block-opening case alone.
|
|
101
|
+
- fix(core): **the payload ingresses refuse what no parse can produce.** The
|
|
102
|
+
wire `TryFrom`, the storage DTO and `push_card` / `insert_card` each admitted
|
|
103
|
+
a payload whose markdown does not read back: a duplicate field key, more
|
|
104
|
+
fields than `MAX_FIELD_COUNT`, a repeated `$` entry, a comment whose text
|
|
105
|
+
spans lines, and a composable card carrying `$quill` or `$seed`. The comment
|
|
106
|
+
is the one that fails quietly — `#` opens a single line, so a text of
|
|
107
|
+
`"hi\ninjected: pwned"` emits bare YAML after the first line and re-reads as
|
|
108
|
+
a field nobody wrote. The rest emit markdown the parser rejects, and a
|
|
109
|
+
composable `$quill` also trips the `debug_assert` in `from_main_and_cards`,
|
|
110
|
+
which the rebuild behind `compile_data` runs: a render panicked on a debug
|
|
111
|
+
build where it owed an error. `PayloadViolation` carries the verdict —
|
|
112
|
+
`FieldViolation`'s seam one level up, reading the item list rather than one
|
|
113
|
+
item — which the wire maps to `WireError::InvalidPayload` and the DTO to
|
|
114
|
+
`StorageError::Malformed`, so both boundaries share one message. The
|
|
115
|
+
`$quill` / `$seed` half is positional, since a `CardWire` is equally how the
|
|
116
|
+
main card is read back, so it sits at placement beside the `$kind` gate as
|
|
117
|
+
`EditError::RootOnlyEntry` (`edit::root_only_entry`). The DTO refuses a root
|
|
118
|
+
`$kind` other than `main` and synthesises an absent one, as the parser does.
|
|
119
|
+
Additive: `validate_payload`, `PayloadViolation`, `MetaKey::ALL`. Both error
|
|
120
|
+
enums are `#[non_exhaustive]`, so no compiling caller changes, and no
|
|
121
|
+
document the format calls readable is refused.
|
|
122
|
+
- fix(cli): **`render -f svg` / `-f png` writes every page.** The Typst backend
|
|
123
|
+
emits one artifact per page and the command wrote `artifacts.first()`,
|
|
124
|
+
discarding the rest with no warning, so a multi-page document produced a
|
|
125
|
+
single-page file that looked complete. A multi-page render now writes one
|
|
126
|
+
numbered file per page: `out.svg` becomes `out-1.svg`, `out-2.svg`, …. Page
|
|
127
|
+
one is numbered too, so no unnumbered file claims to be the whole document.
|
|
128
|
+
`--stdout` carries one artifact and refuses a multi-page render.
|
|
129
|
+
- fix(core): **a `!must_fill` marker on a mapping is refused at every ingress,
|
|
130
|
+
not just at parse.** `Card::store_fill`, the `CardWire` boundary and the
|
|
131
|
+
`@0.92.0` storage DTO took `fill: true` against an object and emitted
|
|
132
|
+
`x: a: 1`, which `Document::parse` then refuses — breaking the emit round
|
|
133
|
+
trip. The rule the parser enforces now sits beside the other field invariants
|
|
134
|
+
as `edit::validate_fill_targets`, raising `edit::fill_on_mapping`. A canonical
|
|
135
|
+
content object stays legal: emit projects it to its markdown scalar first,
|
|
136
|
+
which is the shape `!must_fill` emits against.
|
|
137
|
+
- fix(core): **a comment after a nested key that needs quoting keeps its
|
|
138
|
+
position.** A comment's position is its index among its mapping's children,
|
|
139
|
+
and the prescan matched only a bare `[A-Za-z_][A-Za-z0-9_]*` nested key. So
|
|
140
|
+
`"a b": 1`, which the emitter writes itself, was not counted, and every comment
|
|
141
|
+
after it in that mapping round-tripped one slot early. The prescan reads a
|
|
142
|
+
nested key in both spellings the emitter writes at depth: quoted, and plain
|
|
143
|
+
with characters the bare form excludes.
|
|
144
|
+
- fix(pdfform): **an array-bound text widget is multiline.** `resolve::coerce_text`
|
|
145
|
+
joins an array's elements with newlines unconditionally while the widget took
|
|
146
|
+
`multiline` from `ui`, defaulting to false: a viewer collapsed the `/V` at its
|
|
147
|
+
first line while the flattened SVG/PNG stacked every line, so the interactive
|
|
148
|
+
PDF and the raster disagreed.
|
|
149
|
+
- fix(core): **a version segment is plain digits, and a `$quill` `@` carries a
|
|
150
|
+
selector.** `u32::from_str` accepts a leading `+`, so `memo@+2.+1` parsed as
|
|
151
|
+
`Minor(2, 1)` and `version: "+1.0"` loaded, though `quill_ref_hint` promises
|
|
152
|
+
digits and neither spelling re-`Display`s to itself; and `memo@` yielded an
|
|
153
|
+
empty selector silently read as `latest`. An absent selector still means
|
|
154
|
+
latest.
|
|
155
|
+
- fix(core): **an unquoted numeric `version:` keeps its fraction.** The loader
|
|
156
|
+
accepts a YAML number by intent but converted it through `f64::to_string`,
|
|
157
|
+
which drops the fraction: `version: 1.0` became `"1"` and failed validation
|
|
158
|
+
with a hint naming the `'1.0'` the author had written. An unquoted `1.10` is
|
|
159
|
+
the YAML number `1.1` before the loader sees it, so VERSIONING.md's quote-it
|
|
160
|
+
rule now names that rather than the `x.0` case.
|
|
161
|
+
- fix(python): **a value with no JSON form raises instead of storing its
|
|
162
|
+
`repr`.** `py_to_json_at` fell through to `str()`, so a tuple stored
|
|
163
|
+
`"('a', 'b')"`, a `bytes` stored `"b'...'"` and a `set` stored `"{'x'}"` —
|
|
164
|
+
silently, surfacing as garbage at render or read-back. `datetime.date`,
|
|
165
|
+
`datetime.datetime` and `datetime.time` keep the stringified form the
|
|
166
|
+
fallback existed for; everything else is a `ValueError`, which is what the
|
|
167
|
+
WASM lane already refuses.
|
|
168
|
+
- fix(python): **the type stub declares `Diagnostic.args`**, the localization
|
|
169
|
+
channel the class exposes. Under a type checker `diag.args` was an attribute
|
|
170
|
+
error on a `@final` class.
|
|
171
|
+
- docs(python): **the error contract names both classes the binding raises.**
|
|
172
|
+
`errors.rs` claimed every raised exception is `QuillmarkError` carrying
|
|
173
|
+
diagnostics, while an argument the binding cannot convert raises `ValueError`
|
|
174
|
+
— the behavior the binding's own tests pin. Engine refusals carry diagnostics;
|
|
175
|
+
an unconvertible argument raises `ValueError` before the engine is called.
|
|
176
|
+
Stated in the module doc and in the error-handling guide.
|
|
177
|
+
- fix(wasm): **`new Document(ref)` refuses an invalid reference with the code
|
|
178
|
+
and hint `setQuillRef` attaches.** The same input was classified two ways by
|
|
179
|
+
one binding: `parse::invalid_quill_reference` plus the canonical grammar hint,
|
|
180
|
+
or a bare message. Both doors mint it through one helper now.
|
|
181
|
+
- fix(typst): **a literal `;` after emitted inline markup survives to the
|
|
182
|
+
page.** Typst's markup parser reads a semicolon directly after an embedded
|
|
183
|
+
code expression as that expression's terminator and renders nothing, so
|
|
184
|
+
``Use `--force`; otherwise`` lowered to `#raw("--force"); otherwise` and
|
|
185
|
+
dropped the character. `continues_expr` guarded `(` and `.ident` for the same
|
|
186
|
+
reason; it guards `;` now, and the emitter writes the same `\` before it.
|
|
187
|
+
- fix(typst): **document text reaches the page as the characters it holds, not
|
|
188
|
+
Typst's substitutions for them.** `escape_markup` escaped `~`, whose lexer
|
|
189
|
+
shorthand is a non-breaking space, and left the rest of that class active:
|
|
190
|
+
`pages 3--5` rendered an en dash, `wait...` an ellipsis, `-5` a minus sign,
|
|
191
|
+
and `-?` an invisible soft hyphen, taking both authored characters off the
|
|
192
|
+
page. A mark decided it too, since Typst reads the text behind one as a fresh
|
|
193
|
+
token: `x-5` stayed literal but `**x**-5` lowered to `#strong[x]-5`, a minus
|
|
194
|
+
sign the content never held. Each shorthand's head is escaped now, which is
|
|
195
|
+
enough — what one leaves behind is too short to re-form it. **Smart quotes
|
|
196
|
+
stay.** `'` and `"` are an element with a set rule, not a lexer shorthand, so
|
|
197
|
+
a quill picks its own typography with `#set smartquote(enabled: false)`; the
|
|
198
|
+
emitter escaping them would settle that for every quill with no way back.
|
|
199
|
+
Documents holding a dash pair, an ellipsis or a signed number render
|
|
200
|
+
differently.
|
|
201
|
+
- fix(typst): **an island that renders as nothing no longer joins the text
|
|
202
|
+
either side of it.** An island type this build does not know, and an empty
|
|
203
|
+
table island, emit no markup, so the two text runs their slot separated abut
|
|
204
|
+
in the output — where the escapers, which run per text run, see one side of
|
|
205
|
+
the join at a time. `a/`, such an island, `/b` wrote `a//b`: a Typst comment
|
|
206
|
+
that swallowed the rest of the paragraph. The emitter guards that seam with
|
|
207
|
+
the same `\` it writes at a line anchor and an expression tail.
|
|
208
|
+
- fix(typst): **the caret one past a field's last character resolves to the last
|
|
209
|
+
glyph, not the paragraph's first.** `Scan::locate` admits the end position but
|
|
210
|
+
`forward_pos` matched runs half-open, so the most common caret position while
|
|
211
|
+
typing fell through to the segment's generated start. A position no run
|
|
212
|
+
contains now resolves against the nearest preceding run.
|
|
213
|
+
- fix(typst): **a vendored package whose manifest declares a non-semver
|
|
214
|
+
`version` or an unusable `entrypoint` path warns instead of failing the
|
|
215
|
+
session.** Both aborted `QuillWorld::new` even when the plate never imports
|
|
216
|
+
that package, where an unparseable manifest, an unusable asset path, and a
|
|
217
|
+
missing entrypoint already warn and carry on. A non-semver `version` skips the
|
|
218
|
+
package; an unusable `entrypoint` path keeps the files already loaded and
|
|
219
|
+
skips only the check that the entrypoint is among them.
|
|
220
|
+
- fix(wasm): **`RenderResult` crosses a diagnostic's `args` as the
|
|
221
|
+
`Record<string, unknown>` it declares.** It returns through tsify's ABI, whose
|
|
222
|
+
default serializer emits a `Map` for a map-typed field, where every
|
|
223
|
+
hand-serialized diagnostic path passes `serialize_maps_as_objects(true)`.
|
|
224
|
+
`RenderResult.warnings` puts the document's parse warnings ahead of the
|
|
225
|
+
render's own, and a `plate::unsupported_construct` parse warning carries
|
|
226
|
+
`args`: a quill declining a construct its body still holds renders
|
|
227
|
+
successfully, and `args.construct` read back `undefined`. `RenderResult` and
|
|
228
|
+
`Diagnostic` declare `hashmap_as_object`, and a `const` assertion holds each
|
|
229
|
+
to it.
|
|
230
|
+
- fix(content): **a table island whose `aligns` or a row is not an array
|
|
231
|
+
normalizes to one the store can reload.** `normalize_table_props` repaired a
|
|
232
|
+
non-array `header` but fell through for the other two, while `table_shape_error`
|
|
233
|
+
read them as width 0 — so an island op carrying `{"aligns": "bogus"}` was
|
|
234
|
+
accepted and serialized, and the exact bytes then failed to reopen with
|
|
235
|
+
`TableAlignsMismatch`. Normalizing a table now makes its shape check pass, as
|
|
236
|
+
`KnownIslandType::normalize_props` promises.
|
|
237
|
+
- fix(content): **a code block's `lang` is reduced to an identifier on the
|
|
238
|
+
storage lane and refused on the authored one.** `sanitize_lang` ran on import
|
|
239
|
+
only, so the storage decode and the `setKind` op wire took any string and
|
|
240
|
+
`emit_code` wrote it onto the fence header verbatim: a `lang` of
|
|
241
|
+
`"rust\ninjected line"` exported a content line the document never had, and a
|
|
242
|
+
backtick made the fence illegal. The storage decode sanitizes, so a blob
|
|
243
|
+
written that way still opens; the authored wire raises a shape error, since
|
|
244
|
+
the host is writing now and the repair would be silent.
|
|
245
|
+
- fix(core): **a variant-bearing enum refuses a non-string `default:` at load,
|
|
246
|
+
as a plain enum already does.** The scalar branch applied the render floor's
|
|
247
|
+
leniency to a schema literal, so `default: 1` against `values: ["1"]` loaded
|
|
248
|
+
clean and then selected nothing: `selected_member`, `resolve_variant_sourced`
|
|
249
|
+
and the absent-discriminant fallback all read the default through `as_str`, so
|
|
250
|
+
the field compiled to the blank world instead.
|
|
251
|
+
- fix(core): **a seeded variant container commits at its resting form.**
|
|
252
|
+
`seed_parts` runs every other field through the strict write, but
|
|
253
|
+
`seed_variant` pushed its assembled container straight through, so a `$seed`
|
|
254
|
+
overlay for a richtext cell rested as raw markdown and the next `conform`
|
|
255
|
+
rewrote bytes on a document nobody edited — the divergence the shared write
|
|
256
|
+
exists to prevent.
|
|
257
|
+
|
|
3
258
|
## v0.111.0 - 2026-08-30
|
|
4
259
|
|
|
5
260
|
- feat(wasm): **`mapMarks(content, bundle)` answers where a `ChangeBundle`'s
|
package/README.md
CHANGED
|
@@ -173,9 +173,9 @@ discriminates the two formats without exceptions as control flow:
|
|
|
173
173
|
const doc = Document.tryFromJson(content) ?? Document.fromMarkdown(content);
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
The `schema` value (`quillmark/document@0.
|
|
176
|
+
The `schema` value (`quillmark/document@0.112.0`) is the **model version**,
|
|
177
177
|
not the running crate version. It is a hand-set constant, bumped only when
|
|
178
|
-
the `Document` model itself changes, so every `0.
|
|
178
|
+
the `Document` model itself changes, so every `0.112.x` patch release reads
|
|
179
179
|
and writes that same value.
|
|
180
180
|
|
|
181
181
|
- **Upgrading is safe.** A newer build always reads documents written by an
|
|
@@ -74,8 +74,9 @@ export interface Content {
|
|
|
74
74
|
|
|
75
75
|
/** One `\n`-separated segment of `Content.text`, in order. `kind` is an open set:
|
|
76
76
|
* an unknown role round-trips with opaque `attrs` and renders as a paragraph.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
77
|
+
* Every role spells its payload in `attrs`, known or not, so promoting one moves
|
|
78
|
+
* no bytes. The open arm blocks discriminant narrowing, so read
|
|
79
|
+
* `attrs.level`/`attrs.lang` behind a check of the arm you want. */
|
|
79
80
|
export type ContentLine = {
|
|
80
81
|
containers: ContentContainer[];
|
|
81
82
|
/** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
|
|
@@ -85,11 +86,11 @@ export type ContentLine = {
|
|
|
85
86
|
/** A line's block role, shared by `ContentLine` and the `setKind` op. */
|
|
86
87
|
export type ContentLineKind =
|
|
87
88
|
| { kind: "para" }
|
|
88
|
-
| { kind: "heading"; level: number }
|
|
89
|
-
| { kind: "code"; lang?: string }
|
|
89
|
+
| { kind: "heading"; attrs: { level: number } }
|
|
90
|
+
| { kind: "code"; attrs?: { lang?: string } }
|
|
90
91
|
| { kind: "island" }
|
|
91
92
|
| { kind: "rule" }
|
|
92
|
-
| { kind: string; attrs
|
|
93
|
+
| { kind: string; attrs?: unknown };
|
|
93
94
|
|
|
94
95
|
/** An ancestor block a line nests inside, outermost first. Open like
|
|
95
96
|
* `ContentLine.kind`: an unrecognized container round-trips with opaque `attrs`
|
|
@@ -118,22 +119,26 @@ export type ContentLineKind =
|
|
|
118
119
|
* Content parsed from a stored document is the one shape that arrives without
|
|
119
120
|
* it — storage omits a zero — and needs a cast. */
|
|
120
121
|
export type ContentContainer =
|
|
121
|
-
| {
|
|
122
|
+
| {
|
|
123
|
+
container: "list_item";
|
|
124
|
+
attrs: { ordered: boolean; start: number; ordinal: number };
|
|
125
|
+
instance: number;
|
|
126
|
+
}
|
|
122
127
|
| { container: "quote"; instance: number }
|
|
123
|
-
| { container: string; attrs
|
|
128
|
+
| { container: string; attrs?: unknown; instance: number };
|
|
124
129
|
|
|
125
130
|
/** A mark over char range `[start, end)` into `Content.text`. The open `type`
|
|
126
131
|
* arm blocks discriminant narrowing, so read a payload-carrying arm behind its
|
|
127
|
-
* guard: `isLinkMark` (`url`) / `isAnchorMark` (`id`), from
|
|
132
|
+
* guard: `isLinkMark` (`attrs.url`) / `isAnchorMark` (`attrs.id`), from
|
|
128
133
|
* `@quillmark/wasm/runtime`. An `anchor`'s `id` is a caller-supplied opaque
|
|
129
134
|
* handle, unique per `Content` and invariant while the mark lives (positions
|
|
130
135
|
* rebase, the id never does); it has no markdown projection and survives only
|
|
131
136
|
* through the edit lane. */
|
|
132
137
|
export type ContentMark = { start: number; end: number } & (
|
|
133
138
|
| { type: "strong" | "emph" | "underline" | "strike" | "code" }
|
|
134
|
-
| { type: "link"; url: string }
|
|
135
|
-
| { type: "anchor"; id: string }
|
|
136
|
-
| { type: string; attrs
|
|
139
|
+
| { type: "link"; attrs: { url: string } }
|
|
140
|
+
| { type: "anchor"; attrs: { id: string } }
|
|
141
|
+
| { type: string; attrs?: unknown }
|
|
137
142
|
);
|
|
138
143
|
|
|
139
144
|
/** A cell in a `TableProps`. `marks` rides the prose `ContentMark` shape, but
|
|
@@ -560,7 +565,7 @@ export interface ContentHit {
|
|
|
560
565
|
*/
|
|
561
566
|
field: string;
|
|
562
567
|
/**
|
|
563
|
-
* USV offset into the field
|
|
568
|
+
* USV offset into the field's `Content`.
|
|
564
569
|
*/
|
|
565
570
|
pos: number;
|
|
566
571
|
/**
|
|
@@ -577,12 +582,12 @@ export interface ContentHit {
|
|
|
577
582
|
* (paragraph, heading, whole code fence) and per page each touches, a scalar
|
|
578
583
|
* referenced at several plate sites surfaces each site, and tracked content
|
|
579
584
|
* plus a `field:`-bound widget yields both. Group by `field`. The whole-field
|
|
580
|
-
* highlight is the union of a page
|
|
585
|
+
* highlight is the union of a page's `span`-bearing rects, so inter-paragraph
|
|
581
586
|
* whitespace stays uncovered; `LiveSession.fieldBoxes(field)` owns that union.
|
|
582
587
|
*/
|
|
583
588
|
export interface FieldRegion {
|
|
584
589
|
/**
|
|
585
|
-
* Canonical `DocPath` field address (e.g.
|
|
590
|
+
* Canonical `DocPath` field address (e.g. `"cards.indorsement[1].from"`):
|
|
586
591
|
* the grammar `parseDocPath` reads and `Diagnostic.path` carries. Feed it
|
|
587
592
|
* back to `fieldBoxes` / `locate`.
|
|
588
593
|
*/
|
|
@@ -596,7 +601,7 @@ export interface FieldRegion {
|
|
|
596
601
|
*/
|
|
597
602
|
rect: [number, number, number, number];
|
|
598
603
|
/**
|
|
599
|
-
* The slice this box covers: USV `[start, end)` into the field
|
|
604
|
+
* The slice this box covers: USV `[start, end)` into the field's `Content`
|
|
600
605
|
* for one content segment, `undefined` for a scalar site or widget.
|
|
601
606
|
*/
|
|
602
607
|
span?: [number, number];
|
|
@@ -611,7 +616,7 @@ export interface Diagnostic {
|
|
|
611
616
|
message: string;
|
|
612
617
|
location?: Location;
|
|
613
618
|
/**
|
|
614
|
-
* Document-model path anchor (e.g.
|
|
619
|
+
* Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`),
|
|
615
620
|
* set on schema validation diagnostics and `undefined` otherwise.
|
|
616
621
|
*/
|
|
617
622
|
path?: string;
|
|
@@ -1072,8 +1077,14 @@ export class LiveSession {
|
|
|
1072
1077
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1073
1078
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1074
1079
|
* Unlike `regions()`, *every* placement answers, not just the first.
|
|
1080
|
+
*
|
|
1081
|
+
* `tolPt` is how far off the ink a click still counts, in the same points,
|
|
1082
|
+
* and defaults to `0` — exact. Convert the pointer slack a surface wants
|
|
1083
|
+
* from CSS pixels at the scale it drew the page (`slackPx / renderScale`),
|
|
1084
|
+
* so it stays the same size under the cursor as the page zooms. The
|
|
1085
|
+
* nearest placement answers, so raising it only fills a miss.
|
|
1075
1086
|
*/
|
|
1076
|
-
fieldAt(page: number, x: number, y: number): string | undefined;
|
|
1087
|
+
fieldAt(page: number, x: number, y: number, tol_pt?: number | null): string | undefined;
|
|
1077
1088
|
/**
|
|
1078
1089
|
* The whole-field highlight boxes for `field`: one union rect per page over
|
|
1079
1090
|
* the field's `span`-bearing content segments, the union `regions()` leaves
|
|
@@ -1113,11 +1124,15 @@ export class LiveSession {
|
|
|
1113
1124
|
/**
|
|
1114
1125
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1115
1126
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1116
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1117
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1118
|
-
* degrades to the containing segment's start on origin-less ink.
|
|
1127
|
+
* model, or `undefined` off all content ink. `x`/`y`/`tolPt` are PDF
|
|
1128
|
+
* points, bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1129
|
+
* and degrades to the containing segment's start on origin-less ink.
|
|
1130
|
+
*
|
|
1131
|
+
* `tolPt` earns the most here: the leading between two lines lies inside a
|
|
1132
|
+
* paragraph and on no glyph, and under `tolPt` such a point takes the
|
|
1133
|
+
* nearer line.
|
|
1119
1134
|
*/
|
|
1120
|
-
positionAt(page: number, x: number, y: number): ContentHit | undefined;
|
|
1135
|
+
positionAt(page: number, x: number, y: number, tol_pt?: number | null): ContentHit | undefined;
|
|
1121
1136
|
/**
|
|
1122
1137
|
* Schema-field geometry for this compiled session: each content field's
|
|
1123
1138
|
* **first placement** (one region per page it touches) plus widget and
|
|
@@ -1430,13 +1445,13 @@ export interface InitOutput {
|
|
|
1430
1445
|
readonly formatDocPath: (a: number, b: number) => void;
|
|
1431
1446
|
readonly importMarkdown: (a: number, b: number, c: number) => void;
|
|
1432
1447
|
readonly livesession_backendId: (a: number, b: number) => void;
|
|
1433
|
-
readonly livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
1448
|
+
readonly livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
1434
1449
|
readonly livesession_fieldBoxes: (a: number, b: number, c: number, d: number) => void;
|
|
1435
1450
|
readonly livesession_locate: (a: number, b: number, c: number, d: number) => number;
|
|
1436
1451
|
readonly livesession_pageCount: (a: number) => number;
|
|
1437
1452
|
readonly livesession_pageSize: (a: number, b: number, c: number) => void;
|
|
1438
1453
|
readonly livesession_paint: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
1439
|
-
readonly livesession_positionAt: (a: number, b: number, c: number, d: number) => number;
|
|
1454
|
+
readonly livesession_positionAt: (a: number, b: number, c: number, d: number, e: number) => number;
|
|
1440
1455
|
readonly livesession_regions: (a: number, b: number) => void;
|
|
1441
1456
|
readonly livesession_render: (a: number, b: number, c: number) => void;
|
|
1442
1457
|
readonly livesession_supportsCanvas: (a: number) => number;
|
package/backends/pdfform/wasm.js
CHANGED
|
@@ -1410,15 +1410,22 @@ export class LiveSession {
|
|
|
1410
1410
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1411
1411
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1412
1412
|
* Unlike `regions()`, *every* placement answers, not just the first.
|
|
1413
|
+
*
|
|
1414
|
+
* `tolPt` is how far off the ink a click still counts, in the same points,
|
|
1415
|
+
* and defaults to `0` — exact. Convert the pointer slack a surface wants
|
|
1416
|
+
* from CSS pixels at the scale it drew the page (`slackPx / renderScale`),
|
|
1417
|
+
* so it stays the same size under the cursor as the page zooms. The
|
|
1418
|
+
* nearest placement answers, so raising it only fills a miss.
|
|
1413
1419
|
* @param {number} page
|
|
1414
1420
|
* @param {number} x
|
|
1415
1421
|
* @param {number} y
|
|
1422
|
+
* @param {number | null} [tol_pt]
|
|
1416
1423
|
* @returns {string | undefined}
|
|
1417
1424
|
*/
|
|
1418
|
-
fieldAt(page, x, y) {
|
|
1425
|
+
fieldAt(page, x, y, tol_pt) {
|
|
1419
1426
|
try {
|
|
1420
1427
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
1421
|
-
wasm.livesession_fieldAt(retptr, this.__wbg_ptr, page, x, y);
|
|
1428
|
+
wasm.livesession_fieldAt(retptr, this.__wbg_ptr, page, x, y, isLikeNone(tol_pt) ? 0x100000001 : Math.fround(tol_pt));
|
|
1422
1429
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
1423
1430
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
1424
1431
|
let v1;
|
|
@@ -1537,16 +1544,21 @@ export class LiveSession {
|
|
|
1537
1544
|
/**
|
|
1538
1545
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1539
1546
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1540
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1541
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1542
|
-
* degrades to the containing segment's start on origin-less ink.
|
|
1547
|
+
* model, or `undefined` off all content ink. `x`/`y`/`tolPt` are PDF
|
|
1548
|
+
* points, bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1549
|
+
* and degrades to the containing segment's start on origin-less ink.
|
|
1550
|
+
*
|
|
1551
|
+
* `tolPt` earns the most here: the leading between two lines lies inside a
|
|
1552
|
+
* paragraph and on no glyph, and under `tolPt` such a point takes the
|
|
1553
|
+
* nearer line.
|
|
1543
1554
|
* @param {number} page
|
|
1544
1555
|
* @param {number} x
|
|
1545
1556
|
* @param {number} y
|
|
1557
|
+
* @param {number | null} [tol_pt]
|
|
1546
1558
|
* @returns {ContentHit | undefined}
|
|
1547
1559
|
*/
|
|
1548
|
-
positionAt(page, x, y) {
|
|
1549
|
-
const ret = wasm.livesession_positionAt(this.__wbg_ptr, page, x, y);
|
|
1560
|
+
positionAt(page, x, y, tol_pt) {
|
|
1561
|
+
const ret = wasm.livesession_positionAt(this.__wbg_ptr, page, x, y, isLikeNone(tol_pt) ? 0x100000001 : Math.fround(tol_pt));
|
|
1550
1562
|
return takeObject(ret);
|
|
1551
1563
|
}
|
|
1552
1564
|
/**
|
|
Binary file
|
|
@@ -64,13 +64,13 @@ export const exportMarkdown: (a: number, b: number) => void;
|
|
|
64
64
|
export const formatDocPath: (a: number, b: number) => void;
|
|
65
65
|
export const importMarkdown: (a: number, b: number, c: number) => void;
|
|
66
66
|
export const livesession_backendId: (a: number, b: number) => void;
|
|
67
|
-
export const livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
67
|
+
export const livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
68
68
|
export const livesession_fieldBoxes: (a: number, b: number, c: number, d: number) => void;
|
|
69
69
|
export const livesession_locate: (a: number, b: number, c: number, d: number) => number;
|
|
70
70
|
export const livesession_pageCount: (a: number) => number;
|
|
71
71
|
export const livesession_pageSize: (a: number, b: number, c: number) => void;
|
|
72
72
|
export const livesession_paint: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
73
|
-
export const livesession_positionAt: (a: number, b: number, c: number, d: number) => number;
|
|
73
|
+
export const livesession_positionAt: (a: number, b: number, c: number, d: number, e: number) => number;
|
|
74
74
|
export const livesession_regions: (a: number, b: number) => void;
|
|
75
75
|
export const livesession_render: (a: number, b: number, c: number) => void;
|
|
76
76
|
export const livesession_supportsCanvas: (a: number) => number;
|
package/backends/typst/wasm.d.ts
CHANGED
|
@@ -74,8 +74,9 @@ export interface Content {
|
|
|
74
74
|
|
|
75
75
|
/** One `\n`-separated segment of `Content.text`, in order. `kind` is an open set:
|
|
76
76
|
* an unknown role round-trips with opaque `attrs` and renders as a paragraph.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
77
|
+
* Every role spells its payload in `attrs`, known or not, so promoting one moves
|
|
78
|
+
* no bytes. The open arm blocks discriminant narrowing, so read
|
|
79
|
+
* `attrs.level`/`attrs.lang` behind a check of the arm you want. */
|
|
79
80
|
export type ContentLine = {
|
|
80
81
|
containers: ContentContainer[];
|
|
81
82
|
/** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
|
|
@@ -85,11 +86,11 @@ export type ContentLine = {
|
|
|
85
86
|
/** A line's block role, shared by `ContentLine` and the `setKind` op. */
|
|
86
87
|
export type ContentLineKind =
|
|
87
88
|
| { kind: "para" }
|
|
88
|
-
| { kind: "heading"; level: number }
|
|
89
|
-
| { kind: "code"; lang?: string }
|
|
89
|
+
| { kind: "heading"; attrs: { level: number } }
|
|
90
|
+
| { kind: "code"; attrs?: { lang?: string } }
|
|
90
91
|
| { kind: "island" }
|
|
91
92
|
| { kind: "rule" }
|
|
92
|
-
| { kind: string; attrs
|
|
93
|
+
| { kind: string; attrs?: unknown };
|
|
93
94
|
|
|
94
95
|
/** An ancestor block a line nests inside, outermost first. Open like
|
|
95
96
|
* `ContentLine.kind`: an unrecognized container round-trips with opaque `attrs`
|
|
@@ -118,22 +119,26 @@ export type ContentLineKind =
|
|
|
118
119
|
* Content parsed from a stored document is the one shape that arrives without
|
|
119
120
|
* it — storage omits a zero — and needs a cast. */
|
|
120
121
|
export type ContentContainer =
|
|
121
|
-
| {
|
|
122
|
+
| {
|
|
123
|
+
container: "list_item";
|
|
124
|
+
attrs: { ordered: boolean; start: number; ordinal: number };
|
|
125
|
+
instance: number;
|
|
126
|
+
}
|
|
122
127
|
| { container: "quote"; instance: number }
|
|
123
|
-
| { container: string; attrs
|
|
128
|
+
| { container: string; attrs?: unknown; instance: number };
|
|
124
129
|
|
|
125
130
|
/** A mark over char range `[start, end)` into `Content.text`. The open `type`
|
|
126
131
|
* arm blocks discriminant narrowing, so read a payload-carrying arm behind its
|
|
127
|
-
* guard: `isLinkMark` (`url`) / `isAnchorMark` (`id`), from
|
|
132
|
+
* guard: `isLinkMark` (`attrs.url`) / `isAnchorMark` (`attrs.id`), from
|
|
128
133
|
* `@quillmark/wasm/runtime`. An `anchor`'s `id` is a caller-supplied opaque
|
|
129
134
|
* handle, unique per `Content` and invariant while the mark lives (positions
|
|
130
135
|
* rebase, the id never does); it has no markdown projection and survives only
|
|
131
136
|
* through the edit lane. */
|
|
132
137
|
export type ContentMark = { start: number; end: number } & (
|
|
133
138
|
| { type: "strong" | "emph" | "underline" | "strike" | "code" }
|
|
134
|
-
| { type: "link"; url: string }
|
|
135
|
-
| { type: "anchor"; id: string }
|
|
136
|
-
| { type: string; attrs
|
|
139
|
+
| { type: "link"; attrs: { url: string } }
|
|
140
|
+
| { type: "anchor"; attrs: { id: string } }
|
|
141
|
+
| { type: string; attrs?: unknown }
|
|
137
142
|
);
|
|
138
143
|
|
|
139
144
|
/** A cell in a `TableProps`. `marks` rides the prose `ContentMark` shape, but
|
|
@@ -560,7 +565,7 @@ export interface ContentHit {
|
|
|
560
565
|
*/
|
|
561
566
|
field: string;
|
|
562
567
|
/**
|
|
563
|
-
* USV offset into the field
|
|
568
|
+
* USV offset into the field's `Content`.
|
|
564
569
|
*/
|
|
565
570
|
pos: number;
|
|
566
571
|
/**
|
|
@@ -577,12 +582,12 @@ export interface ContentHit {
|
|
|
577
582
|
* (paragraph, heading, whole code fence) and per page each touches, a scalar
|
|
578
583
|
* referenced at several plate sites surfaces each site, and tracked content
|
|
579
584
|
* plus a `field:`-bound widget yields both. Group by `field`. The whole-field
|
|
580
|
-
* highlight is the union of a page
|
|
585
|
+
* highlight is the union of a page's `span`-bearing rects, so inter-paragraph
|
|
581
586
|
* whitespace stays uncovered; `LiveSession.fieldBoxes(field)` owns that union.
|
|
582
587
|
*/
|
|
583
588
|
export interface FieldRegion {
|
|
584
589
|
/**
|
|
585
|
-
* Canonical `DocPath` field address (e.g.
|
|
590
|
+
* Canonical `DocPath` field address (e.g. `"cards.indorsement[1].from"`):
|
|
586
591
|
* the grammar `parseDocPath` reads and `Diagnostic.path` carries. Feed it
|
|
587
592
|
* back to `fieldBoxes` / `locate`.
|
|
588
593
|
*/
|
|
@@ -596,7 +601,7 @@ export interface FieldRegion {
|
|
|
596
601
|
*/
|
|
597
602
|
rect: [number, number, number, number];
|
|
598
603
|
/**
|
|
599
|
-
* The slice this box covers: USV `[start, end)` into the field
|
|
604
|
+
* The slice this box covers: USV `[start, end)` into the field's `Content`
|
|
600
605
|
* for one content segment, `undefined` for a scalar site or widget.
|
|
601
606
|
*/
|
|
602
607
|
span?: [number, number];
|
|
@@ -611,7 +616,7 @@ export interface Diagnostic {
|
|
|
611
616
|
message: string;
|
|
612
617
|
location?: Location;
|
|
613
618
|
/**
|
|
614
|
-
* Document-model path anchor (e.g.
|
|
619
|
+
* Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`),
|
|
615
620
|
* set on schema validation diagnostics and `undefined` otherwise.
|
|
616
621
|
*/
|
|
617
622
|
path?: string;
|
|
@@ -1072,8 +1077,14 @@ export class LiveSession {
|
|
|
1072
1077
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1073
1078
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1074
1079
|
* Unlike `regions()`, *every* placement answers, not just the first.
|
|
1080
|
+
*
|
|
1081
|
+
* `tolPt` is how far off the ink a click still counts, in the same points,
|
|
1082
|
+
* and defaults to `0` — exact. Convert the pointer slack a surface wants
|
|
1083
|
+
* from CSS pixels at the scale it drew the page (`slackPx / renderScale`),
|
|
1084
|
+
* so it stays the same size under the cursor as the page zooms. The
|
|
1085
|
+
* nearest placement answers, so raising it only fills a miss.
|
|
1075
1086
|
*/
|
|
1076
|
-
fieldAt(page: number, x: number, y: number): string | undefined;
|
|
1087
|
+
fieldAt(page: number, x: number, y: number, tol_pt?: number | null): string | undefined;
|
|
1077
1088
|
/**
|
|
1078
1089
|
* The whole-field highlight boxes for `field`: one union rect per page over
|
|
1079
1090
|
* the field's `span`-bearing content segments, the union `regions()` leaves
|
|
@@ -1113,11 +1124,15 @@ export class LiveSession {
|
|
|
1113
1124
|
/**
|
|
1114
1125
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1115
1126
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1116
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1117
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1118
|
-
* degrades to the containing segment's start on origin-less ink.
|
|
1127
|
+
* model, or `undefined` off all content ink. `x`/`y`/`tolPt` are PDF
|
|
1128
|
+
* points, bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1129
|
+
* and degrades to the containing segment's start on origin-less ink.
|
|
1130
|
+
*
|
|
1131
|
+
* `tolPt` earns the most here: the leading between two lines lies inside a
|
|
1132
|
+
* paragraph and on no glyph, and under `tolPt` such a point takes the
|
|
1133
|
+
* nearer line.
|
|
1119
1134
|
*/
|
|
1120
|
-
positionAt(page: number, x: number, y: number): ContentHit | undefined;
|
|
1135
|
+
positionAt(page: number, x: number, y: number, tol_pt?: number | null): ContentHit | undefined;
|
|
1121
1136
|
/**
|
|
1122
1137
|
* Schema-field geometry for this compiled session: each content field's
|
|
1123
1138
|
* **first placement** (one region per page it touches) plus widget and
|
|
@@ -1430,13 +1445,13 @@ export interface InitOutput {
|
|
|
1430
1445
|
readonly formatDocPath: (a: number, b: number) => void;
|
|
1431
1446
|
readonly importMarkdown: (a: number, b: number, c: number) => void;
|
|
1432
1447
|
readonly livesession_backendId: (a: number, b: number) => void;
|
|
1433
|
-
readonly livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
1448
|
+
readonly livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
1434
1449
|
readonly livesession_fieldBoxes: (a: number, b: number, c: number, d: number) => void;
|
|
1435
1450
|
readonly livesession_locate: (a: number, b: number, c: number, d: number) => number;
|
|
1436
1451
|
readonly livesession_pageCount: (a: number) => number;
|
|
1437
1452
|
readonly livesession_pageSize: (a: number, b: number, c: number) => void;
|
|
1438
1453
|
readonly livesession_paint: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
1439
|
-
readonly livesession_positionAt: (a: number, b: number, c: number, d: number) => number;
|
|
1454
|
+
readonly livesession_positionAt: (a: number, b: number, c: number, d: number, e: number) => number;
|
|
1440
1455
|
readonly livesession_regions: (a: number, b: number) => void;
|
|
1441
1456
|
readonly livesession_render: (a: number, b: number, c: number) => void;
|
|
1442
1457
|
readonly livesession_supportsCanvas: (a: number) => number;
|
package/backends/typst/wasm.js
CHANGED
|
@@ -1410,15 +1410,22 @@ export class LiveSession {
|
|
|
1410
1410
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1411
1411
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1412
1412
|
* Unlike `regions()`, *every* placement answers, not just the first.
|
|
1413
|
+
*
|
|
1414
|
+
* `tolPt` is how far off the ink a click still counts, in the same points,
|
|
1415
|
+
* and defaults to `0` — exact. Convert the pointer slack a surface wants
|
|
1416
|
+
* from CSS pixels at the scale it drew the page (`slackPx / renderScale`),
|
|
1417
|
+
* so it stays the same size under the cursor as the page zooms. The
|
|
1418
|
+
* nearest placement answers, so raising it only fills a miss.
|
|
1413
1419
|
* @param {number} page
|
|
1414
1420
|
* @param {number} x
|
|
1415
1421
|
* @param {number} y
|
|
1422
|
+
* @param {number | null} [tol_pt]
|
|
1416
1423
|
* @returns {string | undefined}
|
|
1417
1424
|
*/
|
|
1418
|
-
fieldAt(page, x, y) {
|
|
1425
|
+
fieldAt(page, x, y, tol_pt) {
|
|
1419
1426
|
try {
|
|
1420
1427
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
1421
|
-
wasm.livesession_fieldAt(retptr, this.__wbg_ptr, page, x, y);
|
|
1428
|
+
wasm.livesession_fieldAt(retptr, this.__wbg_ptr, page, x, y, isLikeNone(tol_pt) ? 0x100000001 : Math.fround(tol_pt));
|
|
1422
1429
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
1423
1430
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
1424
1431
|
let v1;
|
|
@@ -1537,16 +1544,21 @@ export class LiveSession {
|
|
|
1537
1544
|
/**
|
|
1538
1545
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1539
1546
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1540
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1541
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1542
|
-
* degrades to the containing segment's start on origin-less ink.
|
|
1547
|
+
* model, or `undefined` off all content ink. `x`/`y`/`tolPt` are PDF
|
|
1548
|
+
* points, bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1549
|
+
* and degrades to the containing segment's start on origin-less ink.
|
|
1550
|
+
*
|
|
1551
|
+
* `tolPt` earns the most here: the leading between two lines lies inside a
|
|
1552
|
+
* paragraph and on no glyph, and under `tolPt` such a point takes the
|
|
1553
|
+
* nearer line.
|
|
1543
1554
|
* @param {number} page
|
|
1544
1555
|
* @param {number} x
|
|
1545
1556
|
* @param {number} y
|
|
1557
|
+
* @param {number | null} [tol_pt]
|
|
1546
1558
|
* @returns {ContentHit | undefined}
|
|
1547
1559
|
*/
|
|
1548
|
-
positionAt(page, x, y) {
|
|
1549
|
-
const ret = wasm.livesession_positionAt(this.__wbg_ptr, page, x, y);
|
|
1560
|
+
positionAt(page, x, y, tol_pt) {
|
|
1561
|
+
const ret = wasm.livesession_positionAt(this.__wbg_ptr, page, x, y, isLikeNone(tol_pt) ? 0x100000001 : Math.fround(tol_pt));
|
|
1550
1562
|
return takeObject(ret);
|
|
1551
1563
|
}
|
|
1552
1564
|
/**
|
|
Binary file
|
|
@@ -64,13 +64,13 @@ export const exportMarkdown: (a: number, b: number) => void;
|
|
|
64
64
|
export const formatDocPath: (a: number, b: number) => void;
|
|
65
65
|
export const importMarkdown: (a: number, b: number, c: number) => void;
|
|
66
66
|
export const livesession_backendId: (a: number, b: number) => void;
|
|
67
|
-
export const livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
67
|
+
export const livesession_fieldAt: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
|
|
68
68
|
export const livesession_fieldBoxes: (a: number, b: number, c: number, d: number) => void;
|
|
69
69
|
export const livesession_locate: (a: number, b: number, c: number, d: number) => number;
|
|
70
70
|
export const livesession_pageCount: (a: number) => number;
|
|
71
71
|
export const livesession_pageSize: (a: number, b: number, c: number) => void;
|
|
72
72
|
export const livesession_paint: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
73
|
-
export const livesession_positionAt: (a: number, b: number, c: number, d: number) => number;
|
|
73
|
+
export const livesession_positionAt: (a: number, b: number, c: number, d: number, e: number) => number;
|
|
74
74
|
export const livesession_regions: (a: number, b: number) => void;
|
|
75
75
|
export const livesession_render: (a: number, b: number, c: number) => void;
|
|
76
76
|
export const livesession_supportsCanvas: (a: number) => number;
|
package/core/wasm.d.ts
CHANGED
|
@@ -74,8 +74,9 @@ export interface Content {
|
|
|
74
74
|
|
|
75
75
|
/** One `\n`-separated segment of `Content.text`, in order. `kind` is an open set:
|
|
76
76
|
* an unknown role round-trips with opaque `attrs` and renders as a paragraph.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
77
|
+
* Every role spells its payload in `attrs`, known or not, so promoting one moves
|
|
78
|
+
* no bytes. The open arm blocks discriminant narrowing, so read
|
|
79
|
+
* `attrs.level`/`attrs.lang` behind a check of the arm you want. */
|
|
79
80
|
export type ContentLine = {
|
|
80
81
|
containers: ContentContainer[];
|
|
81
82
|
/** A within-block hard line break rather than a new block. Omitted (false) in the common case. */
|
|
@@ -85,11 +86,11 @@ export type ContentLine = {
|
|
|
85
86
|
/** A line's block role, shared by `ContentLine` and the `setKind` op. */
|
|
86
87
|
export type ContentLineKind =
|
|
87
88
|
| { kind: "para" }
|
|
88
|
-
| { kind: "heading"; level: number }
|
|
89
|
-
| { kind: "code"; lang?: string }
|
|
89
|
+
| { kind: "heading"; attrs: { level: number } }
|
|
90
|
+
| { kind: "code"; attrs?: { lang?: string } }
|
|
90
91
|
| { kind: "island" }
|
|
91
92
|
| { kind: "rule" }
|
|
92
|
-
| { kind: string; attrs
|
|
93
|
+
| { kind: string; attrs?: unknown };
|
|
93
94
|
|
|
94
95
|
/** An ancestor block a line nests inside, outermost first. Open like
|
|
95
96
|
* `ContentLine.kind`: an unrecognized container round-trips with opaque `attrs`
|
|
@@ -118,22 +119,26 @@ export type ContentLineKind =
|
|
|
118
119
|
* Content parsed from a stored document is the one shape that arrives without
|
|
119
120
|
* it — storage omits a zero — and needs a cast. */
|
|
120
121
|
export type ContentContainer =
|
|
121
|
-
| {
|
|
122
|
+
| {
|
|
123
|
+
container: "list_item";
|
|
124
|
+
attrs: { ordered: boolean; start: number; ordinal: number };
|
|
125
|
+
instance: number;
|
|
126
|
+
}
|
|
122
127
|
| { container: "quote"; instance: number }
|
|
123
|
-
| { container: string; attrs
|
|
128
|
+
| { container: string; attrs?: unknown; instance: number };
|
|
124
129
|
|
|
125
130
|
/** A mark over char range `[start, end)` into `Content.text`. The open `type`
|
|
126
131
|
* arm blocks discriminant narrowing, so read a payload-carrying arm behind its
|
|
127
|
-
* guard: `isLinkMark` (`url`) / `isAnchorMark` (`id`), from
|
|
132
|
+
* guard: `isLinkMark` (`attrs.url`) / `isAnchorMark` (`attrs.id`), from
|
|
128
133
|
* `@quillmark/wasm/runtime`. An `anchor`'s `id` is a caller-supplied opaque
|
|
129
134
|
* handle, unique per `Content` and invariant while the mark lives (positions
|
|
130
135
|
* rebase, the id never does); it has no markdown projection and survives only
|
|
131
136
|
* through the edit lane. */
|
|
132
137
|
export type ContentMark = { start: number; end: number } & (
|
|
133
138
|
| { type: "strong" | "emph" | "underline" | "strike" | "code" }
|
|
134
|
-
| { type: "link"; url: string }
|
|
135
|
-
| { type: "anchor"; id: string }
|
|
136
|
-
| { type: string; attrs
|
|
139
|
+
| { type: "link"; attrs: { url: string } }
|
|
140
|
+
| { type: "anchor"; attrs: { id: string } }
|
|
141
|
+
| { type: string; attrs?: unknown }
|
|
137
142
|
);
|
|
138
143
|
|
|
139
144
|
/** A cell in a `TableProps`. `marks` rides the prose `ContentMark` shape, but
|
|
@@ -503,7 +508,7 @@ export interface Diagnostic {
|
|
|
503
508
|
message: string;
|
|
504
509
|
location?: Location;
|
|
505
510
|
/**
|
|
506
|
-
* Document-model path anchor (e.g.
|
|
511
|
+
* Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`),
|
|
507
512
|
* set on schema validation diagnostics and `undefined` otherwise.
|
|
508
513
|
*/
|
|
509
514
|
path?: string;
|
package/core/wasm_bg.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/runtime/runtime.d.ts
CHANGED
|
@@ -219,34 +219,32 @@ export declare function isImageIsland(
|
|
|
219
219
|
island: ContentIsland
|
|
220
220
|
): island is ContentIsland & { type: 'image'; props: ImageProps };
|
|
221
221
|
|
|
222
|
-
/** Narrow a {@link ContentMark} to the `link` arm (carries `url`). */
|
|
222
|
+
/** Narrow a {@link ContentMark} to the `link` arm (carries `attrs.url`). */
|
|
223
223
|
export declare function isLinkMark(
|
|
224
224
|
mark: ContentMark
|
|
225
|
-
): mark is ContentMark & { type: 'link'; url: string };
|
|
225
|
+
): mark is ContentMark & { type: 'link'; attrs: { url: string } };
|
|
226
226
|
|
|
227
|
-
/** Narrow a {@link ContentMark} to the `anchor` arm (carries `id`). */
|
|
227
|
+
/** Narrow a {@link ContentMark} to the `anchor` arm (carries `attrs.id`). */
|
|
228
228
|
export declare function isAnchorMark(
|
|
229
229
|
mark: ContentMark
|
|
230
|
-
): mark is ContentMark & { type: 'anchor'; id: string };
|
|
230
|
+
): mark is ContentMark & { type: 'anchor'; attrs: { id: string } };
|
|
231
231
|
|
|
232
|
-
/** Narrow a {@link ContentLine} to the `heading` arm (carries `level`). */
|
|
232
|
+
/** Narrow a {@link ContentLine} to the `heading` arm (carries `attrs.level`). */
|
|
233
233
|
export declare function isHeadingLine(
|
|
234
234
|
line: ContentLine
|
|
235
|
-
): line is ContentLine & { kind: 'heading'; level: number };
|
|
235
|
+
): line is ContentLine & { kind: 'heading'; attrs: { level: number } };
|
|
236
236
|
|
|
237
|
-
/** Narrow a {@link ContentLine} to the `code` arm (carries `lang`). */
|
|
237
|
+
/** Narrow a {@link ContentLine} to the `code` arm (carries `attrs.lang`). */
|
|
238
238
|
export declare function isCodeLine(
|
|
239
239
|
line: ContentLine
|
|
240
|
-
): line is ContentLine & { kind: 'code'; lang?: string };
|
|
240
|
+
): line is ContentLine & { kind: 'code'; attrs?: { lang?: string } };
|
|
241
241
|
|
|
242
242
|
/** Narrow a {@link ContentContainer} to the `list_item` arm (carries its shape). */
|
|
243
243
|
export declare function isListItemContainer(
|
|
244
244
|
container: ContentContainer
|
|
245
245
|
): container is ContentContainer & {
|
|
246
246
|
container: 'list_item';
|
|
247
|
-
ordered: boolean;
|
|
248
|
-
start: number;
|
|
249
|
-
ordinal: number;
|
|
247
|
+
attrs: { ordered: boolean; start: number; ordinal: number };
|
|
250
248
|
instance: number;
|
|
251
249
|
};
|
|
252
250
|
|
|
@@ -257,8 +255,9 @@ export declare function isListItemContainer(
|
|
|
257
255
|
// built-in names by hand re-couples to a closed set.
|
|
258
256
|
//
|
|
259
257
|
// They classify unknown TAGS, not unknown payloads on known tags: a future
|
|
260
|
-
// `kind: "footnote"` carrying
|
|
261
|
-
// predates it, with or without these.
|
|
258
|
+
// `kind: "footnote"` carrying an `attrs.ref` loses `ref` at any consumer that
|
|
259
|
+
// predates it, with or without these. The spelling needs no classifying: a
|
|
260
|
+
// payload rides `attrs` whether or not this build knows the name.
|
|
262
261
|
|
|
263
262
|
/** True when this build does not know `line.kind`: the open arm, carrying opaque `attrs`. */
|
|
264
263
|
export declare function isUnknownLine(
|
|
@@ -612,13 +611,24 @@ export declare class LiveSession {
|
|
|
612
611
|
* transform documented there: `x = clickPx.x / renderScale`,
|
|
613
612
|
* `y = pageHeightPt - clickPx.y / renderScale`. Unlike {@link regions},
|
|
614
613
|
* *every* placement answers, not just the first.
|
|
614
|
+
*
|
|
615
|
+
* `tolPt` is how far off the ink a click still counts, in the same points,
|
|
616
|
+
* and defaults to `0` — exact. It is pointer slack, so derive it from the
|
|
617
|
+
* scale the page was drawn at (`slackPx / renderScale`) rather than fixing a
|
|
618
|
+
* value in points, which shrinks under the cursor as the page zooms out. The
|
|
619
|
+
* nearest placement answers and containment is distance zero, so raising
|
|
620
|
+
* `tolPt` only ever fills a miss.
|
|
615
621
|
*/
|
|
616
|
-
fieldAt(page: number, x: number, y: number): string | undefined;
|
|
622
|
+
fieldAt(page: number, x: number, y: number, tolPt?: number): string | undefined;
|
|
617
623
|
/**
|
|
618
624
|
* Fine-grained click → content position (caret placement). Same PDF-point
|
|
619
|
-
* space as {@link fieldAt}; `undefined`
|
|
625
|
+
* space as {@link fieldAt}; `undefined` past `tolPt` from all content ink.
|
|
626
|
+
*
|
|
627
|
+
* `tolPt` buys the most here: the leading between two lines lies inside a
|
|
628
|
+
* paragraph and on no glyph, and under `tolPt` a point there takes the line
|
|
629
|
+
* it is nearer.
|
|
620
630
|
*/
|
|
621
|
-
positionAt(page: number, x: number, y: number): ContentHit | undefined;
|
|
631
|
+
positionAt(page: number, x: number, y: number, tolPt?: number): ContentHit | undefined;
|
|
622
632
|
/**
|
|
623
633
|
* Content position → caret rect: reverse of {@link positionAt}. `field` is a
|
|
624
634
|
* canonical `DocPath` address (`parseDocPath`-routable), as {@link regions} keys.
|
package/runtime/runtime.js
CHANGED
|
@@ -398,12 +398,12 @@ for (const name of /** @type {const} */ (['validate', 'resolve', 'conform'])) {
|
|
|
398
398
|
// leaving `props` / the mark payload / `level` opaque at every consumer. These
|
|
399
399
|
// are the checked narrowing path: on the true branch the payload's pinned shape
|
|
400
400
|
// is asserted. Only the payload-carrying arms get a guard: an island always
|
|
401
|
-
// carries `props`, a `link` mark
|
|
402
|
-
// `
|
|
403
|
-
//
|
|
404
|
-
// `
|
|
405
|
-
//
|
|
406
|
-
//
|
|
401
|
+
// carries `props`, and a `link`/`anchor` mark, a `heading`/`code` line and a
|
|
402
|
+
// `list_item` container each carry their payload in `attrs`; the payload-free
|
|
403
|
+
// arms (`strong`/`emph`/`underline`/`strike`/`code` marks, `para`/`island`/
|
|
404
|
+
// `rule` lines, `quote`) omit `attrs` and narrow to nothing. An unrecognized
|
|
405
|
+
// discriminant fails every guard and carries the same `attrs` a known one
|
|
406
|
+
// would.
|
|
407
407
|
|
|
408
408
|
/**
|
|
409
409
|
* @param {import('../core/wasm.js').ContentIsland} island
|
|
@@ -423,7 +423,7 @@ export function isImageIsland(island) {
|
|
|
423
423
|
|
|
424
424
|
/**
|
|
425
425
|
* @param {import('../core/wasm.js').ContentMark} mark
|
|
426
|
-
* @returns {mark is import('../core/wasm.js').ContentMark & { type: 'link'; url: string }}
|
|
426
|
+
* @returns {mark is import('../core/wasm.js').ContentMark & { type: 'link'; attrs: { url: string } }}
|
|
427
427
|
*/
|
|
428
428
|
export function isLinkMark(mark) {
|
|
429
429
|
return mark.type === 'link';
|
|
@@ -431,7 +431,7 @@ export function isLinkMark(mark) {
|
|
|
431
431
|
|
|
432
432
|
/**
|
|
433
433
|
* @param {import('../core/wasm.js').ContentMark} mark
|
|
434
|
-
* @returns {mark is import('../core/wasm.js').ContentMark & { type: 'anchor'; id: string }}
|
|
434
|
+
* @returns {mark is import('../core/wasm.js').ContentMark & { type: 'anchor'; attrs: { id: string } }}
|
|
435
435
|
*/
|
|
436
436
|
export function isAnchorMark(mark) {
|
|
437
437
|
return mark.type === 'anchor';
|
|
@@ -439,7 +439,7 @@ export function isAnchorMark(mark) {
|
|
|
439
439
|
|
|
440
440
|
/**
|
|
441
441
|
* @param {import('../core/wasm.js').ContentLine} line
|
|
442
|
-
* @returns {line is import('../core/wasm.js').ContentLine & { kind: 'heading'; level: number }}
|
|
442
|
+
* @returns {line is import('../core/wasm.js').ContentLine & { kind: 'heading'; attrs: { level: number } }}
|
|
443
443
|
*/
|
|
444
444
|
export function isHeadingLine(line) {
|
|
445
445
|
return line.kind === 'heading';
|
|
@@ -447,7 +447,7 @@ export function isHeadingLine(line) {
|
|
|
447
447
|
|
|
448
448
|
/**
|
|
449
449
|
* @param {import('../core/wasm.js').ContentLine} line
|
|
450
|
-
* @returns {line is import('../core/wasm.js').ContentLine & { kind: 'code'; lang?: string }}
|
|
450
|
+
* @returns {line is import('../core/wasm.js').ContentLine & { kind: 'code'; attrs?: { lang?: string } }}
|
|
451
451
|
*/
|
|
452
452
|
export function isCodeLine(line) {
|
|
453
453
|
return line.kind === 'code';
|
|
@@ -455,7 +455,7 @@ export function isCodeLine(line) {
|
|
|
455
455
|
|
|
456
456
|
/**
|
|
457
457
|
* @param {import('../core/wasm.js').ContentContainer} container
|
|
458
|
-
* @returns {container is import('../core/wasm.js').ContentContainer & { container: 'list_item'; ordered: boolean; start: number; ordinal: number; instance?: number }}
|
|
458
|
+
* @returns {container is import('../core/wasm.js').ContentContainer & { container: 'list_item'; attrs: { ordered: boolean; start: number; ordinal: number }; instance?: number }}
|
|
459
459
|
*/
|
|
460
460
|
export function isListItemContainer(container) {
|
|
461
461
|
return container.container === 'list_item';
|
|
@@ -517,12 +517,13 @@ export function isUnknownIsland(island) {
|
|
|
517
517
|
// form cannot tell a list ending beside another from one list of two items, so
|
|
518
518
|
// an omitted discriminator welds them and nothing reports it.
|
|
519
519
|
//
|
|
520
|
-
// WELD_KEYS is the rule `Container::same_weld` owns upstream: which
|
|
521
|
-
// adjacent runs must share for the markdown projection to read them
|
|
522
|
-
// therefore for the canonical form to have to spend a
|
|
523
|
-
// not among them, since CommonMark reads only a
|
|
524
|
-
//
|
|
525
|
-
//
|
|
520
|
+
// WELD_KEYS is the rule `Container::same_weld` owns upstream: which `attrs`
|
|
521
|
+
// entries two adjacent runs must share for the markdown projection to read them
|
|
522
|
+
// as one, and therefore for the canonical form to have to spend a
|
|
523
|
+
// discriminator. `start` is not among them, since CommonMark reads only a
|
|
524
|
+
// list's first number — a subset, which is why a built-in needs an entry rather
|
|
525
|
+
// than the unknown branch's whole-bag compare. A table rather than a switch, so
|
|
526
|
+
// `tests/known_names_drift.rs` can pin it against the Rust predicate.
|
|
526
527
|
|
|
527
528
|
const WELD_KEYS = { list_item: ['ordered'], quote: [] };
|
|
528
529
|
|
|
@@ -549,7 +550,7 @@ function weldsWith(a, b) {
|
|
|
549
550
|
// `hasOwn`, so a tag colliding with an `Object.prototype` member reaches the
|
|
550
551
|
// unknown branch rather than a function.
|
|
551
552
|
if (!Object.hasOwn(WELD_KEYS, a.container)) return sameJson(a.attrs, b.attrs);
|
|
552
|
-
return WELD_KEYS[a.container].every((k) => a[k] === b[k]);
|
|
553
|
+
return WELD_KEYS[a.container].every((k) => a.attrs?.[k] === b.attrs?.[k]);
|
|
553
554
|
}
|
|
554
555
|
|
|
555
556
|
/**
|
|
@@ -962,20 +963,22 @@ export class LiveSession {
|
|
|
962
963
|
* @param {number} page
|
|
963
964
|
* @param {number} x
|
|
964
965
|
* @param {number} y
|
|
966
|
+
* @param {number} [tolPt]
|
|
965
967
|
* @returns {string | undefined}
|
|
966
968
|
*/
|
|
967
|
-
fieldAt(page, x, y) {
|
|
968
|
-
return this.#inner.fieldAt(page, x, y);
|
|
969
|
+
fieldAt(page, x, y, tolPt) {
|
|
970
|
+
return this.#inner.fieldAt(page, x, y, tolPt);
|
|
969
971
|
}
|
|
970
972
|
|
|
971
973
|
/**
|
|
972
974
|
* @param {number} page
|
|
973
975
|
* @param {number} x
|
|
974
976
|
* @param {number} y
|
|
977
|
+
* @param {number} [tolPt]
|
|
975
978
|
* @returns {import('./runtime.d.ts').ContentHit | undefined}
|
|
976
979
|
*/
|
|
977
|
-
positionAt(page, x, y) {
|
|
978
|
-
return this.#inner.positionAt(page, x, y);
|
|
980
|
+
positionAt(page, x, y, tolPt) {
|
|
981
|
+
return this.#inner.positionAt(page, x, y, tolPt);
|
|
979
982
|
}
|
|
980
983
|
|
|
981
984
|
/**
|