@quillmark/wasm 0.109.1 → 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 +115 -11
- package/backends/pdfform/wasm.d.ts +58 -26
- package/backends/pdfform/wasm.js +45 -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 +58 -26
- package/backends/typst/wasm.js +45 -0
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +1 -0
- package/core/wasm.d.ts +58 -26
- package/core/wasm.js +45 -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 +10 -10
- package/runtime/runtime.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,120 @@
|
|
|
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
|
+
|
|
70
|
+
## v0.110.0 - 2026-08-26
|
|
71
|
+
|
|
72
|
+
- **breaking** wasm: **the seam spells a container's `instance`, so the read
|
|
73
|
+
type can require it and every write lane reports an omission.** 0.109 gave
|
|
74
|
+
`Container` the discriminator that tells one container from an adjacent
|
|
75
|
+
sibling of identical shape, and left it an obligation no checker asked for;
|
|
76
|
+
the op lane got a requirement, the whole-`Content` lane — `overwrite`,
|
|
77
|
+
`CardInput.body`, the one a codec flattening a tree writes through — did not.
|
|
78
|
+
It could not: one encoder served storage and the bindings, writing the key
|
|
79
|
+
only where it was non-zero, and a field a read may omit is a field a write
|
|
80
|
+
cannot require. `serial::to_seam_value` is that encoder with every `instance`
|
|
81
|
+
spelled, and the bindings take it on every lane typed `Content`
|
|
82
|
+
(`reader.getContent{,At}`, `getStored` on a body, `importMarkdown`, `rebase`,
|
|
83
|
+
and the card wire behind `document.main` / `cards` / `card(i)` / `removeCard`
|
|
84
|
+
/ `makeCard` / `seedMain` / `seedCard`). `ContentContainer.instance` becomes
|
|
85
|
+
required, `ContentContainerInput` is deleted, and `LineOp.setContainers` takes
|
|
86
|
+
`ContentContainer` — a net-smaller surface than 0.109's. The break is every
|
|
87
|
+
hand-built container literal, which is the one a type checker does report.
|
|
88
|
+
A checker still cannot report a `0` stamped on every run, which is the write
|
|
89
|
+
that welds them: `assignInstances` is the rule, not the type. Storage keeps
|
|
90
|
+
the omission and stored blobs re-encode byte for byte; so do the render lanes,
|
|
91
|
+
and so does what the seam types `unknown` (`getStored` on a field,
|
|
92
|
+
`PayloadItem.value`), which answers with the stored bytes verbatim. Content
|
|
93
|
+
parsed from a stored document is the one shape that needs a cast.
|
|
94
|
+
[Guide](docs/migrations/0.109-to-0.110.md)
|
|
95
|
+
- fix(core): **`Quill::validate` refuses only what the render floor refuses.**
|
|
96
|
+
Validation ran its own read-side type dispatch over the authored value while
|
|
97
|
+
the render door validated the *coerced* one, so five of seven types had values
|
|
98
|
+
that rendered and were fatally `validation::type_mismatch` at once — a bare
|
|
99
|
+
scalar for an `array`, `"3"` for an `integer`, `1` for a `boolean`, a
|
|
100
|
+
length-1 array for a `string` or `date`. `airmark`'s `usaf_memo@0.2` declares
|
|
101
|
+
`letterhead_caption` as an `array`, and the bare scalar a starter template
|
|
102
|
+
spells it with — a valid spelling of a one-element list — audited as fatally
|
|
103
|
+
invalid across every document seeded from it, each rendering correctly.
|
|
104
|
+
|
|
105
|
+
`validate_value` now conforms each document value through `conform_value` at
|
|
106
|
+
`Leniency::Render` before judging it, so a type has one predicate and **a
|
|
107
|
+
fatal `validation::*` diagnostic means the document does not render**.
|
|
108
|
+
Conforming runs per node, so one refused element no longer mistypes its
|
|
109
|
+
siblings: `counts: [true, "abc"]` under `integer` items is one mismatch, at
|
|
110
|
+
`counts[1]`. Two consequences for a consumer routing on codes: a value the
|
|
111
|
+
floor adopts raises nothing where it previously raised
|
|
112
|
+
`validation::type_mismatch`, and a bare scalar the floor stringifies into an
|
|
113
|
+
`enum` field is now domain-checked on that string, so `grade: 5` against
|
|
114
|
+
`values: [alpha, beta]` is `validation::enum_violation` where it was
|
|
115
|
+
previously silent — the diagnostic the render door already raised. Schema
|
|
116
|
+
literals (`example:`, `default:`) stay strict.
|
|
117
|
+
|
|
3
118
|
## v0.109.1 - 2026-08-24
|
|
4
119
|
|
|
5
120
|
- fix(typst): **a `field-region` claim around inline content no longer widens
|
|
@@ -11,17 +126,6 @@
|
|
|
11
126
|
`form-field`'s marker carries the same shape and drops the space too, rather
|
|
12
127
|
than keep depending on the `box` that follows it.
|
|
13
128
|
|
|
14
|
-
<!-- seed: commits since v0.109.0, confirm the entries above cover them, then delete this comment
|
|
15
|
-
- docs: dense-prose pass over the branch
|
|
16
|
-
- fix(typst): a marker's label is not a space in the inline flow
|
|
17
|
-
- refactor(wasm): keep the weld rule internal, and pin what states it
|
|
18
|
-
- docs: dense-prose pass over the branch
|
|
19
|
-
- docs: classify 0.109's container break by consumer role
|
|
20
|
-
- feat(wasm)!: make a written container path spell its instance
|
|
21
|
-
- feat(content): name the rule a container's discriminator is minted against
|
|
22
|
-
-->
|
|
23
|
-
|
|
24
|
-
|
|
25
129
|
## v0.109.0 - 2026-08-24
|
|
26
130
|
|
|
27
131
|
- **breaking** content: **`Normalized` is the precondition the projections
|
|
@@ -100,33 +100,24 @@ export type ContentLineKind =
|
|
|
100
100
|
* shape — two consecutive quotes, two consecutive lists — which contiguity
|
|
101
101
|
* alone reads as one.
|
|
102
102
|
*
|
|
103
|
-
* **A writer owes
|
|
104
|
-
*
|
|
105
|
-
* continuation paragraphs of the first, markers gone.
|
|
106
|
-
*
|
|
107
|
-
* codec flattening a tree
|
|
108
|
-
* `@quillmark/wasm/runtime` rather than by hand. Any
|
|
109
|
-
* works; a write is canonicalized to `0`/`1`.
|
|
103
|
+
* **A writer owes a distinct value per adjacent sibling run**, not merely a
|
|
104
|
+
* value. Runs of one shape sharing one arrive as one: a second list's items come
|
|
105
|
+
* back as continuation paragraphs of the first, markers gone. The field is
|
|
106
|
+
* required, so a checker reports the omission; it cannot report a `0` stamped on
|
|
107
|
+
* both, which is the same write. A codec flattening a tree takes them from
|
|
108
|
+
* `assignInstances` in `@quillmark/wasm/runtime` rather than by hand. Any
|
|
109
|
+
* distinct pair works; a write is canonicalized to `0`/`1`.
|
|
110
110
|
*
|
|
111
|
-
* Reading is not the mirror of writing.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* only a list's
|
|
116
|
-
|
|
117
|
-
| { container: "list_item"; ordered: boolean; start: number; ordinal: number; instance?: number }
|
|
118
|
-
| { container: "quote"; instance?: number }
|
|
119
|
-
| { container: string; attrs: unknown; instance?: number };
|
|
120
|
-
|
|
121
|
-
/** A container path on a lane that only ever carries host-built values:
|
|
122
|
-
* `ContentContainer` with `instance` spelled out rather than defaulted. The
|
|
123
|
-
* field that decides whether two adjacent runs weld cannot be omitted by
|
|
124
|
-
* accident here. `assignInstances` returns this shape, and a path copied off a
|
|
125
|
-
* line carries its own instance through — `{ ...c, instance: c.instance ?? 0 }`.
|
|
111
|
+
* Reading is not the mirror of writing. Every read spells the field, the `0` on
|
|
112
|
+
* a container with nothing to be told apart from included. A read also carries a
|
|
113
|
+
* discriminator on pairs no writer had to spell: `1.` beside a list starting at
|
|
114
|
+
* `3` differs by `start`, so those runs arrive apart with nothing written, and
|
|
115
|
+
* the canonical form spends one anyway because Markdown reads only a list's
|
|
116
|
+
* first number.
|
|
126
117
|
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
export type
|
|
118
|
+
* Content parsed from a stored document is the one shape that arrives without
|
|
119
|
+
* it — storage omits a zero — and needs a cast. */
|
|
120
|
+
export type ContentContainer =
|
|
130
121
|
| { container: "list_item"; ordered: boolean; start: number; ordinal: number; instance: number }
|
|
131
122
|
| { container: "quote"; instance: number }
|
|
132
123
|
| { container: string; attrs: unknown; instance: number };
|
|
@@ -256,7 +247,7 @@ export type LineOp =
|
|
|
256
247
|
| { op: "split"; at: number }
|
|
257
248
|
| { op: "join"; line: number }
|
|
258
249
|
| ({ op: "setKind"; line: number } & ContentLineKind)
|
|
259
|
-
| { op: "setContainers"; line: number; containers:
|
|
250
|
+
| { op: "setContainers"; line: number; containers: ContentContainer[] }
|
|
260
251
|
| { op: "setContinues"; line: number; continues: boolean };
|
|
261
252
|
|
|
262
253
|
/**
|
|
@@ -299,6 +290,18 @@ export type IslandOp =
|
|
|
299
290
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
300
291
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
301
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.
|
|
302
305
|
*/
|
|
303
306
|
export interface ChangeBundle {
|
|
304
307
|
delta?: Delta;
|
|
@@ -729,6 +732,10 @@ export class Document {
|
|
|
729
732
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
730
733
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
731
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.
|
|
732
739
|
*/
|
|
733
740
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
734
741
|
/**
|
|
@@ -818,6 +825,10 @@ export class Document {
|
|
|
818
825
|
* is at rest; one from the transport door may rest as authored until it is
|
|
819
826
|
* conformed, and this read reports what is there. For the `Content` either
|
|
820
827
|
* way use `reader.getContent`.
|
|
828
|
+
*
|
|
829
|
+
* The body arm is typed `Content` and answers in the seam form, spelling
|
|
830
|
+
* every `ContentContainer.instance`. A field arm echoes the stored bytes,
|
|
831
|
+
* which omit a zero: verbatim is the contract, and is why it is `unknown`.
|
|
821
832
|
*/
|
|
822
833
|
getStored(addr: Addr | string): unknown;
|
|
823
834
|
/**
|
|
@@ -1301,11 +1312,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
1301
1312
|
*/
|
|
1302
1313
|
export function importMarkdown(markdown: string): Content;
|
|
1303
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
|
+
|
|
1304
1331
|
/**
|
|
1305
1332
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1306
1333
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1307
1334
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1308
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`.
|
|
1309
1340
|
*/
|
|
1310
1341
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
1311
1342
|
|
|
@@ -1411,6 +1442,7 @@ export interface InitOutput {
|
|
|
1411
1442
|
readonly livesession_supportsCanvas: (a: number) => number;
|
|
1412
1443
|
readonly livesession_update: (a: number, b: number, c: number) => void;
|
|
1413
1444
|
readonly livesession_warnings: (a: number, b: number) => void;
|
|
1445
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1414
1446
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1415
1447
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1416
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
|
*/
|
|
@@ -608,6 +612,10 @@ export class Document {
|
|
|
608
612
|
* is at rest; one from the transport door may rest as authored until it is
|
|
609
613
|
* conformed, and this read reports what is there. For the `Content` either
|
|
610
614
|
* way use `reader.getContent`.
|
|
615
|
+
*
|
|
616
|
+
* The body arm is typed `Content` and answers in the seam form, spelling
|
|
617
|
+
* every `ContentContainer.instance`. A field arm echoes the stored bytes,
|
|
618
|
+
* which omit a zero: verbatim is the contract, and is why it is `unknown`.
|
|
611
619
|
* @param {Addr | string} addr
|
|
612
620
|
* @returns {unknown}
|
|
613
621
|
*/
|
|
@@ -2150,11 +2158,48 @@ export function importMarkdown(markdown) {
|
|
|
2150
2158
|
}
|
|
2151
2159
|
}
|
|
2152
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
|
+
|
|
2153
2194
|
/**
|
|
2154
2195
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
2155
2196
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
2156
2197
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
2157
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`.
|
|
2158
2203
|
* @param {Delta} delta
|
|
2159
2204
|
* @param {number} pos
|
|
2160
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
|
@@ -100,33 +100,24 @@ export type ContentLineKind =
|
|
|
100
100
|
* shape — two consecutive quotes, two consecutive lists — which contiguity
|
|
101
101
|
* alone reads as one.
|
|
102
102
|
*
|
|
103
|
-
* **A writer owes
|
|
104
|
-
*
|
|
105
|
-
* continuation paragraphs of the first, markers gone.
|
|
106
|
-
*
|
|
107
|
-
* codec flattening a tree
|
|
108
|
-
* `@quillmark/wasm/runtime` rather than by hand. Any
|
|
109
|
-
* works; a write is canonicalized to `0`/`1`.
|
|
103
|
+
* **A writer owes a distinct value per adjacent sibling run**, not merely a
|
|
104
|
+
* value. Runs of one shape sharing one arrive as one: a second list's items come
|
|
105
|
+
* back as continuation paragraphs of the first, markers gone. The field is
|
|
106
|
+
* required, so a checker reports the omission; it cannot report a `0` stamped on
|
|
107
|
+
* both, which is the same write. A codec flattening a tree takes them from
|
|
108
|
+
* `assignInstances` in `@quillmark/wasm/runtime` rather than by hand. Any
|
|
109
|
+
* distinct pair works; a write is canonicalized to `0`/`1`.
|
|
110
110
|
*
|
|
111
|
-
* Reading is not the mirror of writing.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* only a list's
|
|
116
|
-
|
|
117
|
-
| { container: "list_item"; ordered: boolean; start: number; ordinal: number; instance?: number }
|
|
118
|
-
| { container: "quote"; instance?: number }
|
|
119
|
-
| { container: string; attrs: unknown; instance?: number };
|
|
120
|
-
|
|
121
|
-
/** A container path on a lane that only ever carries host-built values:
|
|
122
|
-
* `ContentContainer` with `instance` spelled out rather than defaulted. The
|
|
123
|
-
* field that decides whether two adjacent runs weld cannot be omitted by
|
|
124
|
-
* accident here. `assignInstances` returns this shape, and a path copied off a
|
|
125
|
-
* line carries its own instance through — `{ ...c, instance: c.instance ?? 0 }`.
|
|
111
|
+
* Reading is not the mirror of writing. Every read spells the field, the `0` on
|
|
112
|
+
* a container with nothing to be told apart from included. A read also carries a
|
|
113
|
+
* discriminator on pairs no writer had to spell: `1.` beside a list starting at
|
|
114
|
+
* `3` differs by `start`, so those runs arrive apart with nothing written, and
|
|
115
|
+
* the canonical form spends one anyway because Markdown reads only a list's
|
|
116
|
+
* first number.
|
|
126
117
|
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
export type
|
|
118
|
+
* Content parsed from a stored document is the one shape that arrives without
|
|
119
|
+
* it — storage omits a zero — and needs a cast. */
|
|
120
|
+
export type ContentContainer =
|
|
130
121
|
| { container: "list_item"; ordered: boolean; start: number; ordinal: number; instance: number }
|
|
131
122
|
| { container: "quote"; instance: number }
|
|
132
123
|
| { container: string; attrs: unknown; instance: number };
|
|
@@ -256,7 +247,7 @@ export type LineOp =
|
|
|
256
247
|
| { op: "split"; at: number }
|
|
257
248
|
| { op: "join"; line: number }
|
|
258
249
|
| ({ op: "setKind"; line: number } & ContentLineKind)
|
|
259
|
-
| { op: "setContainers"; line: number; containers:
|
|
250
|
+
| { op: "setContainers"; line: number; containers: ContentContainer[] }
|
|
260
251
|
| { op: "setContinues"; line: number; continues: boolean };
|
|
261
252
|
|
|
262
253
|
/**
|
|
@@ -299,6 +290,18 @@ export type IslandOp =
|
|
|
299
290
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
300
291
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
301
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.
|
|
302
305
|
*/
|
|
303
306
|
export interface ChangeBundle {
|
|
304
307
|
delta?: Delta;
|
|
@@ -729,6 +732,10 @@ export class Document {
|
|
|
729
732
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
730
733
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
731
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.
|
|
732
739
|
*/
|
|
733
740
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
734
741
|
/**
|
|
@@ -818,6 +825,10 @@ export class Document {
|
|
|
818
825
|
* is at rest; one from the transport door may rest as authored until it is
|
|
819
826
|
* conformed, and this read reports what is there. For the `Content` either
|
|
820
827
|
* way use `reader.getContent`.
|
|
828
|
+
*
|
|
829
|
+
* The body arm is typed `Content` and answers in the seam form, spelling
|
|
830
|
+
* every `ContentContainer.instance`. A field arm echoes the stored bytes,
|
|
831
|
+
* which omit a zero: verbatim is the contract, and is why it is `unknown`.
|
|
821
832
|
*/
|
|
822
833
|
getStored(addr: Addr | string): unknown;
|
|
823
834
|
/**
|
|
@@ -1301,11 +1312,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
1301
1312
|
*/
|
|
1302
1313
|
export function importMarkdown(markdown: string): Content;
|
|
1303
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
|
+
|
|
1304
1331
|
/**
|
|
1305
1332
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1306
1333
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1307
1334
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1308
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`.
|
|
1309
1340
|
*/
|
|
1310
1341
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
1311
1342
|
|
|
@@ -1411,6 +1442,7 @@ export interface InitOutput {
|
|
|
1411
1442
|
readonly livesession_supportsCanvas: (a: number) => number;
|
|
1412
1443
|
readonly livesession_update: (a: number, b: number, c: number) => void;
|
|
1413
1444
|
readonly livesession_warnings: (a: number, b: number) => void;
|
|
1445
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1414
1446
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1415
1447
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1416
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
|
*/
|
|
@@ -608,6 +612,10 @@ export class Document {
|
|
|
608
612
|
* is at rest; one from the transport door may rest as authored until it is
|
|
609
613
|
* conformed, and this read reports what is there. For the `Content` either
|
|
610
614
|
* way use `reader.getContent`.
|
|
615
|
+
*
|
|
616
|
+
* The body arm is typed `Content` and answers in the seam form, spelling
|
|
617
|
+
* every `ContentContainer.instance`. A field arm echoes the stored bytes,
|
|
618
|
+
* which omit a zero: verbatim is the contract, and is why it is `unknown`.
|
|
611
619
|
* @param {Addr | string} addr
|
|
612
620
|
* @returns {unknown}
|
|
613
621
|
*/
|
|
@@ -2150,11 +2158,48 @@ export function importMarkdown(markdown) {
|
|
|
2150
2158
|
}
|
|
2151
2159
|
}
|
|
2152
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
|
+
|
|
2153
2194
|
/**
|
|
2154
2195
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
2155
2196
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
2156
2197
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
2157
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`.
|
|
2158
2203
|
* @param {Delta} delta
|
|
2159
2204
|
* @param {number} pos
|
|
2160
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
|
@@ -100,33 +100,24 @@ export type ContentLineKind =
|
|
|
100
100
|
* shape — two consecutive quotes, two consecutive lists — which contiguity
|
|
101
101
|
* alone reads as one.
|
|
102
102
|
*
|
|
103
|
-
* **A writer owes
|
|
104
|
-
*
|
|
105
|
-
* continuation paragraphs of the first, markers gone.
|
|
106
|
-
*
|
|
107
|
-
* codec flattening a tree
|
|
108
|
-
* `@quillmark/wasm/runtime` rather than by hand. Any
|
|
109
|
-
* works; a write is canonicalized to `0`/`1`.
|
|
103
|
+
* **A writer owes a distinct value per adjacent sibling run**, not merely a
|
|
104
|
+
* value. Runs of one shape sharing one arrive as one: a second list's items come
|
|
105
|
+
* back as continuation paragraphs of the first, markers gone. The field is
|
|
106
|
+
* required, so a checker reports the omission; it cannot report a `0` stamped on
|
|
107
|
+
* both, which is the same write. A codec flattening a tree takes them from
|
|
108
|
+
* `assignInstances` in `@quillmark/wasm/runtime` rather than by hand. Any
|
|
109
|
+
* distinct pair works; a write is canonicalized to `0`/`1`.
|
|
110
110
|
*
|
|
111
|
-
* Reading is not the mirror of writing.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* only a list's
|
|
116
|
-
|
|
117
|
-
| { container: "list_item"; ordered: boolean; start: number; ordinal: number; instance?: number }
|
|
118
|
-
| { container: "quote"; instance?: number }
|
|
119
|
-
| { container: string; attrs: unknown; instance?: number };
|
|
120
|
-
|
|
121
|
-
/** A container path on a lane that only ever carries host-built values:
|
|
122
|
-
* `ContentContainer` with `instance` spelled out rather than defaulted. The
|
|
123
|
-
* field that decides whether two adjacent runs weld cannot be omitted by
|
|
124
|
-
* accident here. `assignInstances` returns this shape, and a path copied off a
|
|
125
|
-
* line carries its own instance through — `{ ...c, instance: c.instance ?? 0 }`.
|
|
111
|
+
* Reading is not the mirror of writing. Every read spells the field, the `0` on
|
|
112
|
+
* a container with nothing to be told apart from included. A read also carries a
|
|
113
|
+
* discriminator on pairs no writer had to spell: `1.` beside a list starting at
|
|
114
|
+
* `3` differs by `start`, so those runs arrive apart with nothing written, and
|
|
115
|
+
* the canonical form spends one anyway because Markdown reads only a list's
|
|
116
|
+
* first number.
|
|
126
117
|
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
export type
|
|
118
|
+
* Content parsed from a stored document is the one shape that arrives without
|
|
119
|
+
* it — storage omits a zero — and needs a cast. */
|
|
120
|
+
export type ContentContainer =
|
|
130
121
|
| { container: "list_item"; ordered: boolean; start: number; ordinal: number; instance: number }
|
|
131
122
|
| { container: "quote"; instance: number }
|
|
132
123
|
| { container: string; attrs: unknown; instance: number };
|
|
@@ -256,7 +247,7 @@ export type LineOp =
|
|
|
256
247
|
| { op: "split"; at: number }
|
|
257
248
|
| { op: "join"; line: number }
|
|
258
249
|
| ({ op: "setKind"; line: number } & ContentLineKind)
|
|
259
|
-
| { op: "setContainers"; line: number; containers:
|
|
250
|
+
| { op: "setContainers"; line: number; containers: ContentContainer[] }
|
|
260
251
|
| { op: "setContinues"; line: number; continues: boolean };
|
|
261
252
|
|
|
262
253
|
/**
|
|
@@ -299,6 +290,18 @@ export type IslandOp =
|
|
|
299
290
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
300
291
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
301
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.
|
|
302
305
|
*/
|
|
303
306
|
export interface ChangeBundle {
|
|
304
307
|
delta?: Delta;
|
|
@@ -545,6 +548,10 @@ export class Document {
|
|
|
545
548
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
546
549
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
547
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.
|
|
548
555
|
*/
|
|
549
556
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
550
557
|
/**
|
|
@@ -634,6 +641,10 @@ export class Document {
|
|
|
634
641
|
* is at rest; one from the transport door may rest as authored until it is
|
|
635
642
|
* conformed, and this read reports what is there. For the `Content` either
|
|
636
643
|
* way use `reader.getContent`.
|
|
644
|
+
*
|
|
645
|
+
* The body arm is typed `Content` and answers in the seam form, spelling
|
|
646
|
+
* every `ContentContainer.instance`. A field arm echoes the stored bytes,
|
|
647
|
+
* which omit a zero: verbatim is the contract, and is why it is `unknown`.
|
|
637
648
|
*/
|
|
638
649
|
getStored(addr: Addr | string): unknown;
|
|
639
650
|
/**
|
|
@@ -980,11 +991,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
980
991
|
*/
|
|
981
992
|
export function importMarkdown(markdown: string): Content;
|
|
982
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
|
+
|
|
983
1010
|
/**
|
|
984
1011
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
985
1012
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
986
1013
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
987
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`.
|
|
988
1019
|
*/
|
|
989
1020
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
990
1021
|
|
|
@@ -1075,6 +1106,7 @@ export interface InitOutput {
|
|
|
1075
1106
|
readonly exportMarkdown: (a: number, b: number) => void;
|
|
1076
1107
|
readonly formatDocPath: (a: number, b: number) => void;
|
|
1077
1108
|
readonly importMarkdown: (a: number, b: number, c: number) => void;
|
|
1109
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1078
1110
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1079
1111
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1080
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
|
*/
|
|
@@ -608,6 +612,10 @@ export class Document {
|
|
|
608
612
|
* is at rest; one from the transport door may rest as authored until it is
|
|
609
613
|
* conformed, and this read reports what is there. For the `Content` either
|
|
610
614
|
* way use `reader.getContent`.
|
|
615
|
+
*
|
|
616
|
+
* The body arm is typed `Content` and answers in the seam form, spelling
|
|
617
|
+
* every `ContentContainer.instance`. A field arm echoes the stored bytes,
|
|
618
|
+
* which omit a zero: verbatim is the contract, and is why it is `unknown`.
|
|
611
619
|
* @param {Addr | string} addr
|
|
612
620
|
* @returns {unknown}
|
|
613
621
|
*/
|
|
@@ -1745,11 +1753,48 @@ export function importMarkdown(markdown) {
|
|
|
1745
1753
|
}
|
|
1746
1754
|
}
|
|
1747
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
|
+
|
|
1748
1789
|
/**
|
|
1749
1790
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1750
1791
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1751
1792
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1752
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`.
|
|
1753
1798
|
* @param {Delta} delta
|
|
1754
1799
|
* @param {number} pos
|
|
1755
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
|
}
|
|
@@ -130,7 +132,6 @@ export type {
|
|
|
130
132
|
ContentLine,
|
|
131
133
|
ContentLineKind,
|
|
132
134
|
ContentContainer,
|
|
133
|
-
ContentContainerInput,
|
|
134
135
|
ContentMark,
|
|
135
136
|
ContentIsland,
|
|
136
137
|
TableProps,
|
|
@@ -205,8 +206,7 @@ import type {
|
|
|
205
206
|
ImageProps,
|
|
206
207
|
ContentMark,
|
|
207
208
|
ContentLine,
|
|
208
|
-
ContentContainer
|
|
209
|
-
ContentContainerInput
|
|
209
|
+
ContentContainer
|
|
210
210
|
} from '../core/wasm.js';
|
|
211
211
|
|
|
212
212
|
/** Narrow a {@link ContentIsland} to the pinned `table` arm (`props: TableProps`). */
|
|
@@ -247,7 +247,7 @@ export declare function isListItemContainer(
|
|
|
247
247
|
ordered: boolean;
|
|
248
248
|
start: number;
|
|
249
249
|
ordinal: number;
|
|
250
|
-
instance
|
|
250
|
+
instance: number;
|
|
251
251
|
};
|
|
252
252
|
|
|
253
253
|
// The guards above answer "is this arm X". These four answer "is this a value
|
|
@@ -280,11 +280,11 @@ export declare function isUnknownIsland(
|
|
|
280
280
|
island: ContentIsland
|
|
281
281
|
): island is ContentIsland & { type: string; props: unknown };
|
|
282
282
|
|
|
283
|
-
// `ContentContainer.instance` is
|
|
284
|
-
// `
|
|
285
|
-
//
|
|
286
|
-
// cannot tell it from one container spanning
|
|
287
|
-
// rule a codec would otherwise re-derive.
|
|
283
|
+
// `ContentContainer.instance` is required, so a checker reports an omission; it
|
|
284
|
+
// cannot report a `0` stamped on every run, which is the same write. Adjacent
|
|
285
|
+
// runs of one shape sharing a value arrive welded, and nothing reports that
|
|
286
|
+
// either: the flat `containers` form cannot tell it from one container spanning
|
|
287
|
+
// two paragraphs. This carries the rule a codec would otherwise re-derive.
|
|
288
288
|
|
|
289
289
|
/**
|
|
290
290
|
* Stamp `instance` across one parent's blocks at one depth, in document order,
|
|
@@ -313,7 +313,7 @@ export declare function isUnknownIsland(
|
|
|
313
313
|
*/
|
|
314
314
|
export declare function assignInstances(
|
|
315
315
|
runs: (ContentContainer | null)[]
|
|
316
|
-
): (
|
|
316
|
+
): (ContentContainer | null)[];
|
|
317
317
|
|
|
318
318
|
// The backend-neutral render contract, defined here rather than re-exported from
|
|
319
319
|
// one private backend because no single backend owns the canonical API's types.
|
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
|
});
|
|
@@ -553,7 +554,7 @@ function weldsWith(a, b) {
|
|
|
553
554
|
|
|
554
555
|
/**
|
|
555
556
|
* @param {(import('../core/wasm.js').ContentContainer | null)[]} runs
|
|
556
|
-
* @returns {(import('../core/wasm.js').
|
|
557
|
+
* @returns {(import('../core/wasm.js').ContentContainer | null)[]}
|
|
557
558
|
*/
|
|
558
559
|
export function assignInstances(runs) {
|
|
559
560
|
let prev = null;
|