@quillmark/wasm 0.110.0 → 0.111.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 +67 -0
- package/backends/pdfform/wasm.d.ts +37 -0
- package/backends/pdfform/wasm.js +41 -0
- package/backends/pdfform/wasm_bg.wasm +0 -0
- package/backends/pdfform/wasm_bg.wasm.d.ts +1 -0
- package/backends/typst/wasm.d.ts +37 -0
- package/backends/typst/wasm.js +41 -0
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +1 -0
- package/core/wasm.d.ts +37 -0
- package/core/wasm.js +41 -0
- package/core/wasm_bg.wasm +0 -0
- package/core/wasm_bg.wasm.d.ts +1 -0
- package/package.json +1 -1
- package/runtime/runtime.d.ts +2 -0
- package/runtime/runtime.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.111.0 - 2026-08-30
|
|
4
|
+
|
|
5
|
+
- feat(wasm): **`mapMarks(content, bundle)` answers where a `ChangeBundle`'s
|
|
6
|
+
text-moving channels leave a field's marks**, the coordinates its `markOps`
|
|
7
|
+
are written in. Each of `delta`, `islandOps` and `lineOps` rebases the marks
|
|
8
|
+
already in the field — a range's `start` takes assoc `after` and its `end`
|
|
9
|
+
`before`, a zero-width mark takes `before` — and that rule reached the
|
|
10
|
+
boundary only as a comment on a private method, so an editor deciding which
|
|
11
|
+
`markOps` to emit had to reimplement it, and one that read the range rule as
|
|
12
|
+
the whole rule drifted an anchor a character on text typed at the anchor's own
|
|
13
|
+
position. `Content::map_marks` and `Content::apply_field_change` walk one
|
|
14
|
+
channel list, so the prediction and the store cannot answer a position
|
|
15
|
+
differently, and the answer is normalized as the store's is, so a bundle
|
|
16
|
+
carrying no `markOps` names the marks the field will hold. The rule is stated
|
|
17
|
+
on `ChangeBundle` and in `BINDINGS.md`.
|
|
18
|
+
|
|
19
|
+
- fix(core): **a `plaintext` field declared `inline: true` keeps the flag on the
|
|
20
|
+
declaration wire.** `FieldSchema::serialize` projected the flag back out of
|
|
21
|
+
the type enum with a `RichText { inline: true }` match only, so
|
|
22
|
+
`type: plaintext, inline: true` serialized as `{"type":"plaintext"}` — WASM
|
|
23
|
+
`quill.schema()`, the Python binding, and the CLI `schema` command all lost
|
|
24
|
+
the single-line constraint, and a serde round-trip degraded the field to
|
|
25
|
+
`inline: false`.
|
|
26
|
+
- fix(core): **a comment on a CRLF line no longer carries its `\r` into the
|
|
27
|
+
emitted document.** The prescan splits on `\n`, so a trailing- or own-line
|
|
28
|
+
comment slice ran to end-of-line including the `\r`; it rode through the DTO
|
|
29
|
+
and wire and was written back verbatim, against `to_markdown`'s "line endings:
|
|
30
|
+
`\n` only". Field values were never affected. A `\r` *inside* a comment still
|
|
31
|
+
reaches emit.
|
|
32
|
+
- fix(facade): **loading a path that names no directory says so.** The walk
|
|
33
|
+
answered a missing root with an empty tree, so `quill_from_path("/typo")`
|
|
34
|
+
failed later with `Quill.yaml not found in file tree`, pointing at the
|
|
35
|
+
bundle's contents instead of the path. Python's `Quill.from_path` surfaced
|
|
36
|
+
that directly; the CLI's pre-checks are gone but for `validate`'s
|
|
37
|
+
missing-`Quill.yaml` one, gated on the directory existing so it names the
|
|
38
|
+
bundle a real directory lacks without shadowing the loader's answer for a
|
|
39
|
+
typo. `validate`'s load-failure summary names no file, that branch covering a
|
|
40
|
+
missing directory too.
|
|
41
|
+
- fix(content): **`LineOp::SetKind` refuses a heading level outside `1..=6`.**
|
|
42
|
+
The arm checked kind/text agreement but not the level, so a Rust caller could
|
|
43
|
+
apply `Heading { level: 9 }` and leave a content whose `validate()` fails and
|
|
44
|
+
whose export emits `#########` — read back as a literal-hash paragraph on the
|
|
45
|
+
next import. The JSON wires already range-checked it. New
|
|
46
|
+
`ApplyError::BadHeadingLevel`.
|
|
47
|
+
- fix(typst): **diagnostic columns count characters, not bytes.** Any multi-byte
|
|
48
|
+
character earlier on the source line inflated the reported column, which an
|
|
49
|
+
editor reads as a jump target.
|
|
50
|
+
- fix(pdf): **a non-finite widget `/Rect` is refused (`pdf::bad_rect`) rather
|
|
51
|
+
than written.** `form.json` rect values deserialize as plain `f32` and
|
|
52
|
+
saturate to `inf`, and `flip_rect` arithmetic can reach `NaN`; pdf-writer
|
|
53
|
+
prints a non-finite float verbatim, so `stamp` returned `Ok` with `inf`/`NaN`
|
|
54
|
+
tokens in the output — no PDF number grammar admits them. `flatten` guards the
|
|
55
|
+
geometry it draws. Matches the posture `font_size` already took. `regions_of`
|
|
56
|
+
is still unguarded.
|
|
57
|
+
- fix(pdf): **a base PDF that already carries an `/AcroForm` is refused
|
|
58
|
+
(`pdf::existing_acroform`).** The catalog rewrite appended a second
|
|
59
|
+
`/AcroForm` key without looking, leaving a dict the spec does not define and
|
|
60
|
+
the old form's widgets live in the preserved page `/Annots`. Stripping was
|
|
61
|
+
already the documented authoring rule; it is now checked.
|
|
62
|
+
- fix(pdf): **`fonts_used` registers only the faces a `/DA` names.** Only `Text`
|
|
63
|
+
and `Choice` widgets write one, so a checkbox or signature spec carrying
|
|
64
|
+
`Times`/`Courier` emitted an unreferenced Type1 object and a dead `/DR /Font`
|
|
65
|
+
entry into every stamped PDF.
|
|
66
|
+
- refactor(core): the unreachable null arm in the `Date`/`DateTime` coercion is
|
|
67
|
+
deleted (`conform_value` returns on any null before the type match), and
|
|
68
|
+
`Version` derives the ordering its field order already spells.
|
|
69
|
+
|
|
3
70
|
## v0.110.0 - 2026-08-26
|
|
4
71
|
|
|
5
72
|
- **breaking** wasm: **the seam spells a container's `instance`, so the read
|
|
@@ -290,6 +290,18 @@ export type IslandOp =
|
|
|
290
290
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
291
291
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
292
292
|
* positions and indices renumber through earlier `split`/`join`.
|
|
293
|
+
*
|
|
294
|
+
* **Mark rebase.** `delta`, `islandOps` and `lineOps` each move text, and each
|
|
295
|
+
* rebases the marks already in the field by one rule: a range mark's `start`
|
|
296
|
+
* takes assoc `after` and its `end` `before`, so an insertion at either edge
|
|
297
|
+
* grows text *outside* the span; a **zero-width** mark takes `before`, so an
|
|
298
|
+
* insertion at its own position leaves it put. That last case is the one
|
|
299
|
+
* position where the two assocs differ, and where an anchor most often sits.
|
|
300
|
+
*
|
|
301
|
+
* `markOps` name the result, so a caller emitting them predicts this rebase.
|
|
302
|
+
* `mapMarks(content, bundle)` runs it instead: pass the bundle's text-moving
|
|
303
|
+
* channels, diff the marks it returns against the ones you intend, and emit
|
|
304
|
+
* only the difference. Reproducing the rule by hand is a second copy to drift.
|
|
293
305
|
*/
|
|
294
306
|
export interface ChangeBundle {
|
|
295
307
|
delta?: Delta;
|
|
@@ -720,6 +732,10 @@ export class Document {
|
|
|
720
732
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
721
733
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
722
734
|
* failed apply.
|
|
735
|
+
*
|
|
736
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
737
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
738
|
+
* building `markOps` need not predict it.
|
|
723
739
|
*/
|
|
724
740
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
725
741
|
/**
|
|
@@ -1296,11 +1312,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
1296
1312
|
*/
|
|
1297
1313
|
export function importMarkdown(markdown: string): Content;
|
|
1298
1314
|
|
|
1315
|
+
/**
|
|
1316
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
1317
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
1318
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
1319
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
1320
|
+
* emit, rather than reproducing that rule in its own language.
|
|
1321
|
+
*
|
|
1322
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
1323
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
1324
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
1325
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
1326
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
1327
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
1328
|
+
*/
|
|
1329
|
+
export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
|
|
1330
|
+
|
|
1299
1331
|
/**
|
|
1300
1332
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1301
1333
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1302
1334
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1303
1335
|
* moves past it). Throws on a malformed `delta`.
|
|
1336
|
+
*
|
|
1337
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
1338
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
1339
|
+
* `ChangeBundle`.
|
|
1304
1340
|
*/
|
|
1305
1341
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
1306
1342
|
|
|
@@ -1406,6 +1442,7 @@ export interface InitOutput {
|
|
|
1406
1442
|
readonly livesession_supportsCanvas: (a: number) => number;
|
|
1407
1443
|
readonly livesession_update: (a: number, b: number, c: number) => void;
|
|
1408
1444
|
readonly livesession_warnings: (a: number, b: number) => void;
|
|
1445
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1409
1446
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1410
1447
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1411
1448
|
readonly quill_backendId: (a: number, b: number) => void;
|
package/backends/pdfform/wasm.js
CHANGED
|
@@ -279,6 +279,10 @@ export class Document {
|
|
|
279
279
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
280
280
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
281
281
|
* failed apply.
|
|
282
|
+
*
|
|
283
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
284
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
285
|
+
* building `markOps` need not predict it.
|
|
282
286
|
* @param {Addr | string} addr
|
|
283
287
|
* @param {ChangeBundle} bundle
|
|
284
288
|
*/
|
|
@@ -2154,11 +2158,48 @@ export function importMarkdown(markdown) {
|
|
|
2154
2158
|
}
|
|
2155
2159
|
}
|
|
2156
2160
|
|
|
2161
|
+
/**
|
|
2162
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
2163
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
2164
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
2165
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
2166
|
+
* emit, rather than reproducing that rule in its own language.
|
|
2167
|
+
*
|
|
2168
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
2169
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
2170
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
2171
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
2172
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
2173
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
2174
|
+
* @param {Content} content
|
|
2175
|
+
* @param {ChangeBundle} bundle
|
|
2176
|
+
* @returns {ContentMark[]}
|
|
2177
|
+
*/
|
|
2178
|
+
export function mapMarks(content, bundle) {
|
|
2179
|
+
try {
|
|
2180
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
2181
|
+
wasm.mapMarks(retptr, addHeapObject(content), addHeapObject(bundle));
|
|
2182
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
2183
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
2184
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
2185
|
+
if (r2) {
|
|
2186
|
+
throw takeObject(r1);
|
|
2187
|
+
}
|
|
2188
|
+
return takeObject(r0);
|
|
2189
|
+
} finally {
|
|
2190
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2157
2194
|
/**
|
|
2158
2195
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
2159
2196
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
2160
2197
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
2161
2198
|
* moves past it). Throws on a malformed `delta`.
|
|
2199
|
+
*
|
|
2200
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
2201
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
2202
|
+
* `ChangeBundle`.
|
|
2162
2203
|
* @param {Delta} delta
|
|
2163
2204
|
* @param {number} pos
|
|
2164
2205
|
* @param {Assoc} assoc
|
|
Binary file
|
|
@@ -76,6 +76,7 @@ export const livesession_render: (a: number, b: number, c: number) => void;
|
|
|
76
76
|
export const livesession_supportsCanvas: (a: number) => number;
|
|
77
77
|
export const livesession_update: (a: number, b: number, c: number) => void;
|
|
78
78
|
export const livesession_warnings: (a: number, b: number) => void;
|
|
79
|
+
export const mapMarks: (a: number, b: number, c: number) => void;
|
|
79
80
|
export const mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
80
81
|
export const parseDocPath: (a: number, b: number, c: number) => void;
|
|
81
82
|
export const quill_backendId: (a: number, b: number) => void;
|
package/backends/typst/wasm.d.ts
CHANGED
|
@@ -290,6 +290,18 @@ export type IslandOp =
|
|
|
290
290
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
291
291
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
292
292
|
* positions and indices renumber through earlier `split`/`join`.
|
|
293
|
+
*
|
|
294
|
+
* **Mark rebase.** `delta`, `islandOps` and `lineOps` each move text, and each
|
|
295
|
+
* rebases the marks already in the field by one rule: a range mark's `start`
|
|
296
|
+
* takes assoc `after` and its `end` `before`, so an insertion at either edge
|
|
297
|
+
* grows text *outside* the span; a **zero-width** mark takes `before`, so an
|
|
298
|
+
* insertion at its own position leaves it put. That last case is the one
|
|
299
|
+
* position where the two assocs differ, and where an anchor most often sits.
|
|
300
|
+
*
|
|
301
|
+
* `markOps` name the result, so a caller emitting them predicts this rebase.
|
|
302
|
+
* `mapMarks(content, bundle)` runs it instead: pass the bundle's text-moving
|
|
303
|
+
* channels, diff the marks it returns against the ones you intend, and emit
|
|
304
|
+
* only the difference. Reproducing the rule by hand is a second copy to drift.
|
|
293
305
|
*/
|
|
294
306
|
export interface ChangeBundle {
|
|
295
307
|
delta?: Delta;
|
|
@@ -720,6 +732,10 @@ export class Document {
|
|
|
720
732
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
721
733
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
722
734
|
* failed apply.
|
|
735
|
+
*
|
|
736
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
737
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
738
|
+
* building `markOps` need not predict it.
|
|
723
739
|
*/
|
|
724
740
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
725
741
|
/**
|
|
@@ -1296,11 +1312,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
1296
1312
|
*/
|
|
1297
1313
|
export function importMarkdown(markdown: string): Content;
|
|
1298
1314
|
|
|
1315
|
+
/**
|
|
1316
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
1317
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
1318
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
1319
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
1320
|
+
* emit, rather than reproducing that rule in its own language.
|
|
1321
|
+
*
|
|
1322
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
1323
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
1324
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
1325
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
1326
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
1327
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
1328
|
+
*/
|
|
1329
|
+
export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
|
|
1330
|
+
|
|
1299
1331
|
/**
|
|
1300
1332
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1301
1333
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1302
1334
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1303
1335
|
* moves past it). Throws on a malformed `delta`.
|
|
1336
|
+
*
|
|
1337
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
1338
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
1339
|
+
* `ChangeBundle`.
|
|
1304
1340
|
*/
|
|
1305
1341
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
1306
1342
|
|
|
@@ -1406,6 +1442,7 @@ export interface InitOutput {
|
|
|
1406
1442
|
readonly livesession_supportsCanvas: (a: number) => number;
|
|
1407
1443
|
readonly livesession_update: (a: number, b: number, c: number) => void;
|
|
1408
1444
|
readonly livesession_warnings: (a: number, b: number) => void;
|
|
1445
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1409
1446
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1410
1447
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1411
1448
|
readonly quill_backendId: (a: number, b: number) => void;
|
package/backends/typst/wasm.js
CHANGED
|
@@ -279,6 +279,10 @@ export class Document {
|
|
|
279
279
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
280
280
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
281
281
|
* failed apply.
|
|
282
|
+
*
|
|
283
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
284
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
285
|
+
* building `markOps` need not predict it.
|
|
282
286
|
* @param {Addr | string} addr
|
|
283
287
|
* @param {ChangeBundle} bundle
|
|
284
288
|
*/
|
|
@@ -2154,11 +2158,48 @@ export function importMarkdown(markdown) {
|
|
|
2154
2158
|
}
|
|
2155
2159
|
}
|
|
2156
2160
|
|
|
2161
|
+
/**
|
|
2162
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
2163
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
2164
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
2165
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
2166
|
+
* emit, rather than reproducing that rule in its own language.
|
|
2167
|
+
*
|
|
2168
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
2169
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
2170
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
2171
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
2172
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
2173
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
2174
|
+
* @param {Content} content
|
|
2175
|
+
* @param {ChangeBundle} bundle
|
|
2176
|
+
* @returns {ContentMark[]}
|
|
2177
|
+
*/
|
|
2178
|
+
export function mapMarks(content, bundle) {
|
|
2179
|
+
try {
|
|
2180
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
2181
|
+
wasm.mapMarks(retptr, addHeapObject(content), addHeapObject(bundle));
|
|
2182
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
2183
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
2184
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
2185
|
+
if (r2) {
|
|
2186
|
+
throw takeObject(r1);
|
|
2187
|
+
}
|
|
2188
|
+
return takeObject(r0);
|
|
2189
|
+
} finally {
|
|
2190
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2157
2194
|
/**
|
|
2158
2195
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
2159
2196
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
2160
2197
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
2161
2198
|
* moves past it). Throws on a malformed `delta`.
|
|
2199
|
+
*
|
|
2200
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
2201
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
2202
|
+
* `ChangeBundle`.
|
|
2162
2203
|
* @param {Delta} delta
|
|
2163
2204
|
* @param {number} pos
|
|
2164
2205
|
* @param {Assoc} assoc
|
|
Binary file
|
|
@@ -76,6 +76,7 @@ export const livesession_render: (a: number, b: number, c: number) => void;
|
|
|
76
76
|
export const livesession_supportsCanvas: (a: number) => number;
|
|
77
77
|
export const livesession_update: (a: number, b: number, c: number) => void;
|
|
78
78
|
export const livesession_warnings: (a: number, b: number) => void;
|
|
79
|
+
export const mapMarks: (a: number, b: number, c: number) => void;
|
|
79
80
|
export const mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
80
81
|
export const parseDocPath: (a: number, b: number, c: number) => void;
|
|
81
82
|
export const quill_backendId: (a: number, b: number) => void;
|
package/core/wasm.d.ts
CHANGED
|
@@ -290,6 +290,18 @@ export type IslandOp =
|
|
|
290
290
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
291
291
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
292
292
|
* positions and indices renumber through earlier `split`/`join`.
|
|
293
|
+
*
|
|
294
|
+
* **Mark rebase.** `delta`, `islandOps` and `lineOps` each move text, and each
|
|
295
|
+
* rebases the marks already in the field by one rule: a range mark's `start`
|
|
296
|
+
* takes assoc `after` and its `end` `before`, so an insertion at either edge
|
|
297
|
+
* grows text *outside* the span; a **zero-width** mark takes `before`, so an
|
|
298
|
+
* insertion at its own position leaves it put. That last case is the one
|
|
299
|
+
* position where the two assocs differ, and where an anchor most often sits.
|
|
300
|
+
*
|
|
301
|
+
* `markOps` name the result, so a caller emitting them predicts this rebase.
|
|
302
|
+
* `mapMarks(content, bundle)` runs it instead: pass the bundle's text-moving
|
|
303
|
+
* channels, diff the marks it returns against the ones you intend, and emit
|
|
304
|
+
* only the difference. Reproducing the rule by hand is a second copy to drift.
|
|
293
305
|
*/
|
|
294
306
|
export interface ChangeBundle {
|
|
295
307
|
delta?: Delta;
|
|
@@ -536,6 +548,10 @@ export class Document {
|
|
|
536
548
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
537
549
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
538
550
|
* failed apply.
|
|
551
|
+
*
|
|
552
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
553
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
554
|
+
* building `markOps` need not predict it.
|
|
539
555
|
*/
|
|
540
556
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
541
557
|
/**
|
|
@@ -975,11 +991,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
975
991
|
*/
|
|
976
992
|
export function importMarkdown(markdown: string): Content;
|
|
977
993
|
|
|
994
|
+
/**
|
|
995
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
996
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
997
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
998
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
999
|
+
* emit, rather than reproducing that rule in its own language.
|
|
1000
|
+
*
|
|
1001
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
1002
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
1003
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
1004
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
1005
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
1006
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
1007
|
+
*/
|
|
1008
|
+
export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
|
|
1009
|
+
|
|
978
1010
|
/**
|
|
979
1011
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
980
1012
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
981
1013
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
982
1014
|
* moves past it). Throws on a malformed `delta`.
|
|
1015
|
+
*
|
|
1016
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
1017
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
1018
|
+
* `ChangeBundle`.
|
|
983
1019
|
*/
|
|
984
1020
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
985
1021
|
|
|
@@ -1070,6 +1106,7 @@ export interface InitOutput {
|
|
|
1070
1106
|
readonly exportMarkdown: (a: number, b: number) => void;
|
|
1071
1107
|
readonly formatDocPath: (a: number, b: number) => void;
|
|
1072
1108
|
readonly importMarkdown: (a: number, b: number, c: number) => void;
|
|
1109
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1073
1110
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1074
1111
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1075
1112
|
readonly quill_backendId: (a: number, b: number) => void;
|
package/core/wasm.js
CHANGED
|
@@ -279,6 +279,10 @@ export class Document {
|
|
|
279
279
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
280
280
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
281
281
|
* failed apply.
|
|
282
|
+
*
|
|
283
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
284
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
285
|
+
* building `markOps` need not predict it.
|
|
282
286
|
* @param {Addr | string} addr
|
|
283
287
|
* @param {ChangeBundle} bundle
|
|
284
288
|
*/
|
|
@@ -1749,11 +1753,48 @@ export function importMarkdown(markdown) {
|
|
|
1749
1753
|
}
|
|
1750
1754
|
}
|
|
1751
1755
|
|
|
1756
|
+
/**
|
|
1757
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
1758
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
1759
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
1760
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
1761
|
+
* emit, rather than reproducing that rule in its own language.
|
|
1762
|
+
*
|
|
1763
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
1764
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
1765
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
1766
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
1767
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
1768
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
1769
|
+
* @param {Content} content
|
|
1770
|
+
* @param {ChangeBundle} bundle
|
|
1771
|
+
* @returns {ContentMark[]}
|
|
1772
|
+
*/
|
|
1773
|
+
export function mapMarks(content, bundle) {
|
|
1774
|
+
try {
|
|
1775
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
1776
|
+
wasm.mapMarks(retptr, addHeapObject(content), addHeapObject(bundle));
|
|
1777
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
1778
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
1779
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
1780
|
+
if (r2) {
|
|
1781
|
+
throw takeObject(r1);
|
|
1782
|
+
}
|
|
1783
|
+
return takeObject(r0);
|
|
1784
|
+
} finally {
|
|
1785
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1752
1789
|
/**
|
|
1753
1790
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1754
1791
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1755
1792
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1756
1793
|
* moves past it). Throws on a malformed `delta`.
|
|
1794
|
+
*
|
|
1795
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
1796
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
1797
|
+
* `ChangeBundle`.
|
|
1757
1798
|
* @param {Delta} delta
|
|
1758
1799
|
* @param {number} pos
|
|
1759
1800
|
* @param {Assoc} assoc
|
package/core/wasm_bg.wasm
CHANGED
|
Binary file
|
package/core/wasm_bg.wasm.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export const document_warnings: (a: number, b: number) => void;
|
|
|
61
61
|
export const exportMarkdown: (a: number, b: number) => void;
|
|
62
62
|
export const formatDocPath: (a: number, b: number) => void;
|
|
63
63
|
export const importMarkdown: (a: number, b: number, c: number) => void;
|
|
64
|
+
export const mapMarks: (a: number, b: number, c: number) => void;
|
|
64
65
|
export const mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
65
66
|
export const parseDocPath: (a: number, b: number, c: number) => void;
|
|
66
67
|
export const quill_backendId: (a: number, b: number) => void;
|
package/package.json
CHANGED
package/runtime/runtime.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
exportMarkdown,
|
|
25
25
|
rebase,
|
|
26
26
|
mapPos,
|
|
27
|
+
mapMarks,
|
|
27
28
|
parseDocPath,
|
|
28
29
|
formatDocPath
|
|
29
30
|
} from '../core/wasm.js';
|
|
@@ -42,6 +43,7 @@ export interface CoreSurface {
|
|
|
42
43
|
exportMarkdown: typeof exportMarkdown;
|
|
43
44
|
rebase: typeof rebase;
|
|
44
45
|
mapPos: typeof mapPos;
|
|
46
|
+
mapMarks: typeof mapMarks;
|
|
45
47
|
parseDocPath: typeof parseDocPath;
|
|
46
48
|
formatDocPath: typeof formatDocPath;
|
|
47
49
|
}
|
package/runtime/runtime.js
CHANGED
|
@@ -40,7 +40,7 @@ import initCore, { Quill, Document } from '../core/wasm.js';
|
|
|
40
40
|
// itself), a `node:fs` read under Node, whose `fetch` rejects `file:` URLs.
|
|
41
41
|
// Resolution-time, so `node:fs` never enters a browser graph.
|
|
42
42
|
import { toModuleSource } from '#quillmark-env';
|
|
43
|
-
import { importMarkdown, exportMarkdown, rebase, mapPos } from '../core/wasm.js';
|
|
43
|
+
import { importMarkdown, exportMarkdown, rebase, mapPos, mapMarks } from '../core/wasm.js';
|
|
44
44
|
import { parseDocPath, formatDocPath } from '../core/wasm.js';
|
|
45
45
|
|
|
46
46
|
// ── Initialization ──────────────────────────────────────────────────────────
|
|
@@ -90,6 +90,7 @@ const CORE_SURFACE = Object.freeze({
|
|
|
90
90
|
exportMarkdown,
|
|
91
91
|
rebase,
|
|
92
92
|
mapPos,
|
|
93
|
+
mapMarks,
|
|
93
94
|
parseDocPath,
|
|
94
95
|
formatDocPath
|
|
95
96
|
});
|