@quillmark/wasm 0.110.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 +322 -0
- package/README.md +2 -2
- package/backends/pdfform/wasm.d.ts +75 -23
- package/backends/pdfform/wasm.js +60 -7
- package/backends/pdfform/wasm_bg.wasm +0 -0
- package/backends/pdfform/wasm_bg.wasm.d.ts +3 -2
- package/backends/typst/wasm.d.ts +75 -23
- package/backends/typst/wasm.js +60 -7
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +3 -2
- package/core/wasm.d.ts +54 -12
- 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 +28 -16
- package/runtime/runtime.js +27 -23
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
|
*/
|
|
@@ -1406,15 +1410,22 @@ export class LiveSession {
|
|
|
1406
1410
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1407
1411
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1408
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.
|
|
1409
1419
|
* @param {number} page
|
|
1410
1420
|
* @param {number} x
|
|
1411
1421
|
* @param {number} y
|
|
1422
|
+
* @param {number | null} [tol_pt]
|
|
1412
1423
|
* @returns {string | undefined}
|
|
1413
1424
|
*/
|
|
1414
|
-
fieldAt(page, x, y) {
|
|
1425
|
+
fieldAt(page, x, y, tol_pt) {
|
|
1415
1426
|
try {
|
|
1416
1427
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
1417
|
-
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));
|
|
1418
1429
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
1419
1430
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
1420
1431
|
let v1;
|
|
@@ -1533,16 +1544,21 @@ export class LiveSession {
|
|
|
1533
1544
|
/**
|
|
1534
1545
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1535
1546
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1536
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1537
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1538
|
-
* 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.
|
|
1539
1554
|
* @param {number} page
|
|
1540
1555
|
* @param {number} x
|
|
1541
1556
|
* @param {number} y
|
|
1557
|
+
* @param {number | null} [tol_pt]
|
|
1542
1558
|
* @returns {ContentHit | undefined}
|
|
1543
1559
|
*/
|
|
1544
|
-
positionAt(page, x, y) {
|
|
1545
|
-
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));
|
|
1546
1562
|
return takeObject(ret);
|
|
1547
1563
|
}
|
|
1548
1564
|
/**
|
|
@@ -2154,11 +2170,48 @@ export function importMarkdown(markdown) {
|
|
|
2154
2170
|
}
|
|
2155
2171
|
}
|
|
2156
2172
|
|
|
2173
|
+
/**
|
|
2174
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
2175
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
2176
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
2177
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
2178
|
+
* emit, rather than reproducing that rule in its own language.
|
|
2179
|
+
*
|
|
2180
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
2181
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
2182
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
2183
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
2184
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
2185
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
2186
|
+
* @param {Content} content
|
|
2187
|
+
* @param {ChangeBundle} bundle
|
|
2188
|
+
* @returns {ContentMark[]}
|
|
2189
|
+
*/
|
|
2190
|
+
export function mapMarks(content, bundle) {
|
|
2191
|
+
try {
|
|
2192
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
2193
|
+
wasm.mapMarks(retptr, addHeapObject(content), addHeapObject(bundle));
|
|
2194
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
2195
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
2196
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
2197
|
+
if (r2) {
|
|
2198
|
+
throw takeObject(r1);
|
|
2199
|
+
}
|
|
2200
|
+
return takeObject(r0);
|
|
2201
|
+
} finally {
|
|
2202
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2157
2206
|
/**
|
|
2158
2207
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
2159
2208
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
2160
2209
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
2161
2210
|
* moves past it). Throws on a malformed `delta`.
|
|
2211
|
+
*
|
|
2212
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
2213
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
2214
|
+
* `ChangeBundle`.
|
|
2162
2215
|
* @param {Delta} delta
|
|
2163
2216
|
* @param {number} pos
|
|
2164
2217
|
* @param {Assoc} assoc
|
|
Binary file
|
|
@@ -64,18 +64,19 @@ 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;
|
|
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
|
@@ -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
|
|
@@ -290,6 +295,18 @@ export type IslandOp =
|
|
|
290
295
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
291
296
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
292
297
|
* positions and indices renumber through earlier `split`/`join`.
|
|
298
|
+
*
|
|
299
|
+
* **Mark rebase.** `delta`, `islandOps` and `lineOps` each move text, and each
|
|
300
|
+
* rebases the marks already in the field by one rule: a range mark's `start`
|
|
301
|
+
* takes assoc `after` and its `end` `before`, so an insertion at either edge
|
|
302
|
+
* grows text *outside* the span; a **zero-width** mark takes `before`, so an
|
|
303
|
+
* insertion at its own position leaves it put. That last case is the one
|
|
304
|
+
* position where the two assocs differ, and where an anchor most often sits.
|
|
305
|
+
*
|
|
306
|
+
* `markOps` name the result, so a caller emitting them predicts this rebase.
|
|
307
|
+
* `mapMarks(content, bundle)` runs it instead: pass the bundle's text-moving
|
|
308
|
+
* channels, diff the marks it returns against the ones you intend, and emit
|
|
309
|
+
* only the difference. Reproducing the rule by hand is a second copy to drift.
|
|
293
310
|
*/
|
|
294
311
|
export interface ChangeBundle {
|
|
295
312
|
delta?: Delta;
|
|
@@ -548,7 +565,7 @@ export interface ContentHit {
|
|
|
548
565
|
*/
|
|
549
566
|
field: string;
|
|
550
567
|
/**
|
|
551
|
-
* USV offset into the field
|
|
568
|
+
* USV offset into the field's `Content`.
|
|
552
569
|
*/
|
|
553
570
|
pos: number;
|
|
554
571
|
/**
|
|
@@ -565,12 +582,12 @@ export interface ContentHit {
|
|
|
565
582
|
* (paragraph, heading, whole code fence) and per page each touches, a scalar
|
|
566
583
|
* referenced at several plate sites surfaces each site, and tracked content
|
|
567
584
|
* plus a `field:`-bound widget yields both. Group by `field`. The whole-field
|
|
568
|
-
* highlight is the union of a page
|
|
585
|
+
* highlight is the union of a page's `span`-bearing rects, so inter-paragraph
|
|
569
586
|
* whitespace stays uncovered; `LiveSession.fieldBoxes(field)` owns that union.
|
|
570
587
|
*/
|
|
571
588
|
export interface FieldRegion {
|
|
572
589
|
/**
|
|
573
|
-
* Canonical `DocPath` field address (e.g.
|
|
590
|
+
* Canonical `DocPath` field address (e.g. `"cards.indorsement[1].from"`):
|
|
574
591
|
* the grammar `parseDocPath` reads and `Diagnostic.path` carries. Feed it
|
|
575
592
|
* back to `fieldBoxes` / `locate`.
|
|
576
593
|
*/
|
|
@@ -584,7 +601,7 @@ export interface FieldRegion {
|
|
|
584
601
|
*/
|
|
585
602
|
rect: [number, number, number, number];
|
|
586
603
|
/**
|
|
587
|
-
* 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`
|
|
588
605
|
* for one content segment, `undefined` for a scalar site or widget.
|
|
589
606
|
*/
|
|
590
607
|
span?: [number, number];
|
|
@@ -599,7 +616,7 @@ export interface Diagnostic {
|
|
|
599
616
|
message: string;
|
|
600
617
|
location?: Location;
|
|
601
618
|
/**
|
|
602
|
-
* Document-model path anchor (e.g.
|
|
619
|
+
* Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`),
|
|
603
620
|
* set on schema validation diagnostics and `undefined` otherwise.
|
|
604
621
|
*/
|
|
605
622
|
path?: string;
|
|
@@ -720,6 +737,10 @@ export class Document {
|
|
|
720
737
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
721
738
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
722
739
|
* failed apply.
|
|
740
|
+
*
|
|
741
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
742
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
743
|
+
* building `markOps` need not predict it.
|
|
723
744
|
*/
|
|
724
745
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
725
746
|
/**
|
|
@@ -1056,8 +1077,14 @@ export class LiveSession {
|
|
|
1056
1077
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1057
1078
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1058
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.
|
|
1059
1086
|
*/
|
|
1060
|
-
fieldAt(page: number, x: number, y: number): string | undefined;
|
|
1087
|
+
fieldAt(page: number, x: number, y: number, tol_pt?: number | null): string | undefined;
|
|
1061
1088
|
/**
|
|
1062
1089
|
* The whole-field highlight boxes for `field`: one union rect per page over
|
|
1063
1090
|
* the field's `span`-bearing content segments, the union `regions()` leaves
|
|
@@ -1097,11 +1124,15 @@ export class LiveSession {
|
|
|
1097
1124
|
/**
|
|
1098
1125
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1099
1126
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1100
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1101
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1102
|
-
* 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.
|
|
1103
1134
|
*/
|
|
1104
|
-
positionAt(page: number, x: number, y: number): ContentHit | undefined;
|
|
1135
|
+
positionAt(page: number, x: number, y: number, tol_pt?: number | null): ContentHit | undefined;
|
|
1105
1136
|
/**
|
|
1106
1137
|
* Schema-field geometry for this compiled session: each content field's
|
|
1107
1138
|
* **first placement** (one region per page it touches) plus widget and
|
|
@@ -1296,11 +1327,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
1296
1327
|
*/
|
|
1297
1328
|
export function importMarkdown(markdown: string): Content;
|
|
1298
1329
|
|
|
1330
|
+
/**
|
|
1331
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
1332
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
1333
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
1334
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
1335
|
+
* emit, rather than reproducing that rule in its own language.
|
|
1336
|
+
*
|
|
1337
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
1338
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
1339
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
1340
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
1341
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
1342
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
1343
|
+
*/
|
|
1344
|
+
export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
|
|
1345
|
+
|
|
1299
1346
|
/**
|
|
1300
1347
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
1301
1348
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
1302
1349
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
1303
1350
|
* moves past it). Throws on a malformed `delta`.
|
|
1351
|
+
*
|
|
1352
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
1353
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
1354
|
+
* `ChangeBundle`.
|
|
1304
1355
|
*/
|
|
1305
1356
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
1306
1357
|
|
|
@@ -1394,18 +1445,19 @@ export interface InitOutput {
|
|
|
1394
1445
|
readonly formatDocPath: (a: number, b: number) => void;
|
|
1395
1446
|
readonly importMarkdown: (a: number, b: number, c: number) => void;
|
|
1396
1447
|
readonly livesession_backendId: (a: number, b: number) => void;
|
|
1397
|
-
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;
|
|
1398
1449
|
readonly livesession_fieldBoxes: (a: number, b: number, c: number, d: number) => void;
|
|
1399
1450
|
readonly livesession_locate: (a: number, b: number, c: number, d: number) => number;
|
|
1400
1451
|
readonly livesession_pageCount: (a: number) => number;
|
|
1401
1452
|
readonly livesession_pageSize: (a: number, b: number, c: number) => void;
|
|
1402
1453
|
readonly livesession_paint: (a: number, b: number, c: number, d: number, e: number) => void;
|
|
1403
|
-
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;
|
|
1404
1455
|
readonly livesession_regions: (a: number, b: number) => void;
|
|
1405
1456
|
readonly livesession_render: (a: number, b: number, c: number) => void;
|
|
1406
1457
|
readonly livesession_supportsCanvas: (a: number) => number;
|
|
1407
1458
|
readonly livesession_update: (a: number, b: number, c: number) => void;
|
|
1408
1459
|
readonly livesession_warnings: (a: number, b: number) => void;
|
|
1460
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1409
1461
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1410
1462
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1411
1463
|
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
|
*/
|
|
@@ -1406,15 +1410,22 @@ export class LiveSession {
|
|
|
1406
1410
|
* `FieldRegion.rect`, so from a canvas click use
|
|
1407
1411
|
* `x = clickPx.x / renderScale`, `y = pageHeightPt - clickPx.y / renderScale`.
|
|
1408
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.
|
|
1409
1419
|
* @param {number} page
|
|
1410
1420
|
* @param {number} x
|
|
1411
1421
|
* @param {number} y
|
|
1422
|
+
* @param {number | null} [tol_pt]
|
|
1412
1423
|
* @returns {string | undefined}
|
|
1413
1424
|
*/
|
|
1414
|
-
fieldAt(page, x, y) {
|
|
1425
|
+
fieldAt(page, x, y, tol_pt) {
|
|
1415
1426
|
try {
|
|
1416
1427
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
1417
|
-
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));
|
|
1418
1429
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
1419
1430
|
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
1420
1431
|
let v1;
|
|
@@ -1533,16 +1544,21 @@ export class LiveSession {
|
|
|
1533
1544
|
/**
|
|
1534
1545
|
* A point → **content position**: the field *and* a USV offset into its
|
|
1535
1546
|
* `Content`, for placing a caret or mapping a selection into the content
|
|
1536
|
-
* model, or `undefined` off all content ink. `x`/`y` are PDF
|
|
1537
|
-
* bottom-left origin, as in `fieldAt`. The offset is cluster-exact
|
|
1538
|
-
* 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.
|
|
1539
1554
|
* @param {number} page
|
|
1540
1555
|
* @param {number} x
|
|
1541
1556
|
* @param {number} y
|
|
1557
|
+
* @param {number | null} [tol_pt]
|
|
1542
1558
|
* @returns {ContentHit | undefined}
|
|
1543
1559
|
*/
|
|
1544
|
-
positionAt(page, x, y) {
|
|
1545
|
-
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));
|
|
1546
1562
|
return takeObject(ret);
|
|
1547
1563
|
}
|
|
1548
1564
|
/**
|
|
@@ -2154,11 +2170,48 @@ export function importMarkdown(markdown) {
|
|
|
2154
2170
|
}
|
|
2155
2171
|
}
|
|
2156
2172
|
|
|
2173
|
+
/**
|
|
2174
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
2175
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
2176
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
2177
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
2178
|
+
* emit, rather than reproducing that rule in its own language.
|
|
2179
|
+
*
|
|
2180
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
2181
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
2182
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
2183
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
2184
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
2185
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
2186
|
+
* @param {Content} content
|
|
2187
|
+
* @param {ChangeBundle} bundle
|
|
2188
|
+
* @returns {ContentMark[]}
|
|
2189
|
+
*/
|
|
2190
|
+
export function mapMarks(content, bundle) {
|
|
2191
|
+
try {
|
|
2192
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
2193
|
+
wasm.mapMarks(retptr, addHeapObject(content), addHeapObject(bundle));
|
|
2194
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
2195
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
2196
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
2197
|
+
if (r2) {
|
|
2198
|
+
throw takeObject(r1);
|
|
2199
|
+
}
|
|
2200
|
+
return takeObject(r0);
|
|
2201
|
+
} finally {
|
|
2202
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2157
2206
|
/**
|
|
2158
2207
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
2159
2208
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
2160
2209
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
2161
2210
|
* moves past it). Throws on a malformed `delta`.
|
|
2211
|
+
*
|
|
2212
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
2213
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
2214
|
+
* `ChangeBundle`.
|
|
2162
2215
|
* @param {Delta} delta
|
|
2163
2216
|
* @param {number} pos
|
|
2164
2217
|
* @param {Assoc} assoc
|
|
Binary file
|
|
@@ -64,18 +64,19 @@ 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;
|
|
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
|
@@ -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
|
|
@@ -290,6 +295,18 @@ export type IslandOp =
|
|
|
290
295
|
* Within each channel ops apply in sequence against the state the earlier ones
|
|
291
296
|
* left: an island `insert`'s `at` counts earlier ops' slots, and `lineOps`
|
|
292
297
|
* positions and indices renumber through earlier `split`/`join`.
|
|
298
|
+
*
|
|
299
|
+
* **Mark rebase.** `delta`, `islandOps` and `lineOps` each move text, and each
|
|
300
|
+
* rebases the marks already in the field by one rule: a range mark's `start`
|
|
301
|
+
* takes assoc `after` and its `end` `before`, so an insertion at either edge
|
|
302
|
+
* grows text *outside* the span; a **zero-width** mark takes `before`, so an
|
|
303
|
+
* insertion at its own position leaves it put. That last case is the one
|
|
304
|
+
* position where the two assocs differ, and where an anchor most often sits.
|
|
305
|
+
*
|
|
306
|
+
* `markOps` name the result, so a caller emitting them predicts this rebase.
|
|
307
|
+
* `mapMarks(content, bundle)` runs it instead: pass the bundle's text-moving
|
|
308
|
+
* channels, diff the marks it returns against the ones you intend, and emit
|
|
309
|
+
* only the difference. Reproducing the rule by hand is a second copy to drift.
|
|
293
310
|
*/
|
|
294
311
|
export interface ChangeBundle {
|
|
295
312
|
delta?: Delta;
|
|
@@ -491,7 +508,7 @@ export interface Diagnostic {
|
|
|
491
508
|
message: string;
|
|
492
509
|
location?: Location;
|
|
493
510
|
/**
|
|
494
|
-
* Document-model path anchor (e.g.
|
|
511
|
+
* Document-model path anchor (e.g. `"cards.indorsement[0].signature_block"`),
|
|
495
512
|
* set on schema validation diagnostics and `undefined` otherwise.
|
|
496
513
|
*/
|
|
497
514
|
path?: string;
|
|
@@ -536,6 +553,10 @@ export class Document {
|
|
|
536
553
|
* Throws on an out-of-range card, a field that is not richtext, a malformed
|
|
537
554
|
* bundle, or an op that applies out of bounds; the value is unchanged on a
|
|
538
555
|
* failed apply.
|
|
556
|
+
*
|
|
557
|
+
* Each text-moving channel rebases the marks already in the field, by the
|
|
558
|
+
* rule on `ChangeBundle`; `mapMarks` answers where they land, so a caller
|
|
559
|
+
* building `markOps` need not predict it.
|
|
539
560
|
*/
|
|
540
561
|
applyChange(addr: Addr | string, bundle: ChangeBundle): void;
|
|
541
562
|
/**
|
|
@@ -975,11 +996,31 @@ export function formatDocPath(segs: DocPathSeg[]): string;
|
|
|
975
996
|
*/
|
|
976
997
|
export function importMarkdown(markdown: string): Content;
|
|
977
998
|
|
|
999
|
+
/**
|
|
1000
|
+
* Where `bundle`'s text-moving channels (`delta`, then `islandOps`, then
|
|
1001
|
+
* `lineOps`) leave `content`'s marks: the final-text coordinates the bundle's
|
|
1002
|
+
* `markOps` are written in, under the rebase rule stated on `ChangeBundle`.
|
|
1003
|
+
* The document-free read an editor diffs against to decide which `markOps` to
|
|
1004
|
+
* emit, rather than reproducing that rule in its own language.
|
|
1005
|
+
*
|
|
1006
|
+
* `bundle.markOps` are ignored. The answer is normalized, as the store's is:
|
|
1007
|
+
* marks a text move drops (out of range, zero-width formatting) are absent,
|
|
1008
|
+
* and same-kind runs a move left adjacent arrive already unioned, so a bundle
|
|
1009
|
+
* carrying no `markOps` names the marks the field will hold.
|
|
1010
|
+
* Throws on a non-content `content`, a malformed bundle, or an op that applies
|
|
1011
|
+
* out of bounds: `applyChange`'s errors on the same ops.
|
|
1012
|
+
*/
|
|
1013
|
+
export function mapMarks(content: Content, bundle: ChangeBundle): ContentMark[];
|
|
1014
|
+
|
|
978
1015
|
/**
|
|
979
1016
|
* Map a base content position (a USV index into `Content.text`, not a UTF-16
|
|
980
1017
|
* offset) through a `delta` to its new position, holding a caret stable across
|
|
981
1018
|
* a `revise`. `assoc` decides the side of a same-position insertion (`"after"`
|
|
982
1019
|
* moves past it). Throws on a malformed `delta`.
|
|
1020
|
+
*
|
|
1021
|
+
* This maps a position the *caller* holds. For the marks already in a field,
|
|
1022
|
+
* `mapMarks` applies the store's own assoc rule across every channel of a
|
|
1023
|
+
* `ChangeBundle`.
|
|
983
1024
|
*/
|
|
984
1025
|
export function mapPos(delta: Delta, pos: number, assoc: Assoc): number;
|
|
985
1026
|
|
|
@@ -1070,6 +1111,7 @@ export interface InitOutput {
|
|
|
1070
1111
|
readonly exportMarkdown: (a: number, b: number) => void;
|
|
1071
1112
|
readonly formatDocPath: (a: number, b: number) => void;
|
|
1072
1113
|
readonly importMarkdown: (a: number, b: number, c: number) => void;
|
|
1114
|
+
readonly mapMarks: (a: number, b: number, c: number) => void;
|
|
1073
1115
|
readonly mapPos: (a: number, b: number, c: number, d: number) => void;
|
|
1074
1116
|
readonly parseDocPath: (a: number, b: number, c: number) => void;
|
|
1075
1117
|
readonly quill_backendId: (a: number, b: number) => void;
|