@quillmark/wasm 0.92.1 → 0.95.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +301 -4
- package/README.md +167 -66
- package/backends/pdfform/wasm.d.ts +1066 -0
- package/backends/pdfform/wasm.js +9 -0
- package/backends/pdfform/wasm_bg.js +2654 -0
- package/backends/pdfform/wasm_bg.wasm +0 -0
- package/backends/pdfform/wasm_bg.wasm.d.ts +98 -0
- package/backends/typst/wasm.d.ts +584 -167
- package/backends/typst/wasm.js +1 -1
- package/backends/typst/wasm_bg.js +1047 -397
- package/backends/typst/wasm_bg.wasm +0 -0
- package/backends/typst/wasm_bg.wasm.d.ts +45 -25
- package/core/wasm.d.ts +412 -110
- package/core/wasm.js +1 -1
- package/core/wasm_bg.js +713 -226
- package/core/wasm_bg.wasm +0 -0
- package/core/wasm_bg.wasm.d.ts +31 -17
- package/package.json +4 -3
- package/runtime/runtime.d.ts +459 -21
- package/runtime/runtime.js +536 -30
package/runtime/runtime.d.ts
CHANGED
|
@@ -14,6 +14,20 @@
|
|
|
14
14
|
// public entry point, so this is a structural fact. Replacing the re-export
|
|
15
15
|
// with a wrapper is a breaking design change, not a refactor. See runtime.js.
|
|
16
16
|
export { Quill, Document, init } from '../core/wasm.js';
|
|
17
|
+
// The document-free content codec, re-exported from the core build.
|
|
18
|
+
export { importMarkdown, exportMarkdown, rebase, mapPos } from '../core/wasm.js';
|
|
19
|
+
|
|
20
|
+
import type { CardAddr } from '../core/wasm.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The main card's address — the default target of the card-scoped verbs
|
|
24
|
+
* (`storeFields` / `storeExt` / `commitFields` / …). A named, {@link CardAddr}-typed
|
|
25
|
+
* alias for the empty address `{}`, so a main-card write names its target:
|
|
26
|
+
* `doc.storeFields(MAIN_CARD_ADDR, fields)`. It IS `{}` (frozen at runtime), a
|
|
27
|
+
* pure alias — `{}` and `undefined` stay equally valid. A card selector only,
|
|
28
|
+
* never a field address.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MAIN_CARD_ADDR: CardAddr;
|
|
17
31
|
|
|
18
32
|
// Core-build types consumers read off `Quill`/`Document`.
|
|
19
33
|
export type {
|
|
@@ -31,6 +45,31 @@ export type {
|
|
|
31
45
|
QuillMetadata
|
|
32
46
|
} from '../core/wasm.js';
|
|
33
47
|
|
|
48
|
+
// Content edit vocabulary — the op-grained content model `Document`'s methods
|
|
49
|
+
// speak (`applyChange(addr, bundle)`, `install(addr, rt)`, `revise(…) => Delta`).
|
|
50
|
+
// Declared in the core build; re-exported here so the single public entry point
|
|
51
|
+
// names every type its own re-exported surface already references — `Card.body`
|
|
52
|
+
// is a `Content`, `PayloadItem.nestedFills` a `PathStep[][]`, `CardInput.body` a
|
|
53
|
+
// `Content | string` — rather than forcing consumers to derive them structurally
|
|
54
|
+
// off the `Document` handle. The content write path (a ProseMirror↔content codec)
|
|
55
|
+
// must name all of them; they are its correctness core, not edge types.
|
|
56
|
+
export type {
|
|
57
|
+
Content,
|
|
58
|
+
ContentLine,
|
|
59
|
+
ContentContainer,
|
|
60
|
+
ContentMark,
|
|
61
|
+
ContentIsland,
|
|
62
|
+
CardInput,
|
|
63
|
+
PathStep,
|
|
64
|
+
Addr,
|
|
65
|
+
CardAddr,
|
|
66
|
+
Delta,
|
|
67
|
+
Assoc,
|
|
68
|
+
LineOp,
|
|
69
|
+
MarkOp,
|
|
70
|
+
ChangeBundle
|
|
71
|
+
} from '../core/wasm.js';
|
|
72
|
+
|
|
34
73
|
// ── Error contract ──────────────────────────────────────────────────────────
|
|
35
74
|
|
|
36
75
|
/**
|
|
@@ -68,7 +107,7 @@ export declare function isQuillmarkError(e: unknown): e is QuillmarkError;
|
|
|
68
107
|
// (run via `npm run typecheck`), so these and the generated
|
|
69
108
|
// `pkg/backends/typst/wasm.d.ts` cannot silently diverge.
|
|
70
109
|
|
|
71
|
-
import type { Quill, Document } from '../core/wasm.js';
|
|
110
|
+
import type { Quill, Document, Card } from '../core/wasm.js';
|
|
72
111
|
import type { Diagnostic } from '../core/wasm.js';
|
|
73
112
|
|
|
74
113
|
/** Canonical contract every backend build must satisfy. One emitted output. */
|
|
@@ -84,6 +123,92 @@ export interface RenderOptions {
|
|
|
84
123
|
ppi?: number;
|
|
85
124
|
pages?: number[];
|
|
86
125
|
producer?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Populate {@link RenderResult.regions} with the schema-field geometry
|
|
128
|
+
* sidecar (the same entries {@link LiveSession.regions} serves), for
|
|
129
|
+
* consumers without a live session — e.g. overlays over a one-shot SVG
|
|
130
|
+
* export. Defaults to `false`: exports pay no introspection cost.
|
|
131
|
+
*/
|
|
132
|
+
regions?: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* How precisely a {@link ContentHit.pos} resolved — the marker a caret UI reads
|
|
137
|
+
* to decide whether to trust the offset. Never sub-cluster: `'cluster'` is the
|
|
138
|
+
* finest, `'segment'` the floor it degrades to on origin-less ink.
|
|
139
|
+
*
|
|
140
|
+
* - `'cluster'` — `pos` is the first content char of the cluster under the point
|
|
141
|
+
* (an escaped/CJK/shaping cluster floors to its first char). Place the caret
|
|
142
|
+
* at `pos` directly.
|
|
143
|
+
* - `'segment'` — the point hit origin-less ink (list markers, numbering, a
|
|
144
|
+
* multi-line code fence's interior), so `pos` degraded to the containing
|
|
145
|
+
* segment's start. Treat `pos` as the selected segment, not a caret.
|
|
146
|
+
*/
|
|
147
|
+
export type HitGranularity = 'cluster' | 'segment';
|
|
148
|
+
|
|
149
|
+
/** A click resolved to a field and USV offset into its Content. */
|
|
150
|
+
export interface ContentHit {
|
|
151
|
+
field: string;
|
|
152
|
+
pos: number;
|
|
153
|
+
/**
|
|
154
|
+
* Whether {@link pos} is cluster-exact or floored to the segment start
|
|
155
|
+
* ({@link HitGranularity}). Absent when the backend does not report it.
|
|
156
|
+
*/
|
|
157
|
+
granularity?: HitGranularity;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* A rendered field region: the quill schema field address (`field`) plus its
|
|
162
|
+
* geometry (`rect`) on the page. Emitted by backends that place schema fields
|
|
163
|
+
* (`pdfform` AcroForm widgets; Typst form-fields and span-tracked content —
|
|
164
|
+
* richtext bodies, `richtext[]` elements, card content fields, direct scalar
|
|
165
|
+
* references). Only fields with a schema address produce a region — a
|
|
166
|
+
* backend-only widget produces none, and the backend widget name never
|
|
167
|
+
* appears.
|
|
168
|
+
*
|
|
169
|
+
* Use it to scroll to / highlight the focused field's rect; for the click
|
|
170
|
+
* direction use {@link LiveSession.fieldAt}, which resolves a point on *any*
|
|
171
|
+
* placement, not just the first one surfaced here. Geometry only —
|
|
172
|
+
* `LiveSession.paint` already bakes every value into the raster (see
|
|
173
|
+
* {@link LiveSession}), so a region is never a compositing input.
|
|
174
|
+
*
|
|
175
|
+
* COORDINATE TRANSFORM. `rect` is in PDF points with a **bottom-left** origin.
|
|
176
|
+
*
|
|
177
|
+
* For an **HTML/CSS overlay** on a `width:100%` canvas, position hotspots as
|
|
178
|
+
* percentages of the page — they track the displayed size across DPI and pane
|
|
179
|
+
* resize for free, and only the Y axis flips:
|
|
180
|
+
*
|
|
181
|
+
* ```js
|
|
182
|
+
* const [x0, y0, x1, y1] = region.rect; // PDF pt, bottom-left origin
|
|
183
|
+
* const left = (x0 / pageWidthPt) * 100; // % of page (from PageSize.widthPt)
|
|
184
|
+
* const top = (1 - y1 / pageHeightPt) * 100; // % — flip Y (from PageSize.heightPt)
|
|
185
|
+
* const width = ((x1 - x0) / pageWidthPt) * 100;
|
|
186
|
+
* const height = ((y1 - y0) / pageHeightPt) * 100;
|
|
187
|
+
* ```
|
|
188
|
+
*
|
|
189
|
+
* For painting **into a raster** at `renderScale` (= `layoutScale × densityScale`),
|
|
190
|
+
* use the device-pixel form instead:
|
|
191
|
+
*
|
|
192
|
+
* ```js
|
|
193
|
+
* const left = x0 * renderScale;
|
|
194
|
+
* const top = (pageHeightPt - y1) * renderScale; // flip Y
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export interface FieldRegion {
|
|
198
|
+
/** Quill schema field path (e.g. `"signature_block"`), not a backend widget name. */
|
|
199
|
+
field: string;
|
|
200
|
+
/** 0-based page index. */
|
|
201
|
+
page: number;
|
|
202
|
+
/** `[x0, y0, x1, y1]` in PDF points (1/72″), bottom-left origin. */
|
|
203
|
+
rect: [number, number, number, number];
|
|
204
|
+
/**
|
|
205
|
+
* The content slice this box covers — USV `[start, end)` into the field's
|
|
206
|
+
* `Content` for content ink (one segment), absent for a scalar reference
|
|
207
|
+
* site or widget. Consumers key segment highlights on it;
|
|
208
|
+
* {@link LiveSession.fieldBoxes} unions same-page segments for the
|
|
209
|
+
* whole-field box.
|
|
210
|
+
*/
|
|
211
|
+
span?: [number, number];
|
|
87
212
|
}
|
|
88
213
|
|
|
89
214
|
/** Canonical contract every backend build must satisfy. Result of one render. */
|
|
@@ -92,6 +217,14 @@ export interface RenderResult {
|
|
|
92
217
|
warnings: Diagnostic[];
|
|
93
218
|
outputFormat: OutputFormat;
|
|
94
219
|
renderTimeMs: number;
|
|
220
|
+
/**
|
|
221
|
+
* Schema-field geometry sidecar — populated only when
|
|
222
|
+
* {@link RenderOptions.regions} requested it; empty otherwise. The same
|
|
223
|
+
* entries {@link LiveSession.regions} serves, for consumers without a live
|
|
224
|
+
* session. Page indices are document-space even under a `pages` subset
|
|
225
|
+
* render.
|
|
226
|
+
*/
|
|
227
|
+
regions: FieldRegion[];
|
|
95
228
|
}
|
|
96
229
|
|
|
97
230
|
/** Canonical contract every backend build must satisfy. The emittable formats. */
|
|
@@ -99,7 +232,6 @@ export type OutputFormat = 'pdf' | 'svg' | 'txt' | 'png';
|
|
|
99
232
|
|
|
100
233
|
/**
|
|
101
234
|
* Canonical contract every backend build must satisfy. Page geometry in pt.
|
|
102
|
-
* @experimental Part of the iterative-session/canvas surface — see {@link RenderSession}.
|
|
103
235
|
*/
|
|
104
236
|
export interface PageSize {
|
|
105
237
|
widthPt: number;
|
|
@@ -108,7 +240,6 @@ export interface PageSize {
|
|
|
108
240
|
|
|
109
241
|
/**
|
|
110
242
|
* Canonical contract every backend build must satisfy. Inputs to `paint`.
|
|
111
|
-
* @experimental Part of the iterative-session/canvas surface — see {@link RenderSession}.
|
|
112
243
|
*/
|
|
113
244
|
export interface PaintOptions {
|
|
114
245
|
layoutScale?: number;
|
|
@@ -117,13 +248,36 @@ export interface PaintOptions {
|
|
|
117
248
|
|
|
118
249
|
/**
|
|
119
250
|
* Canonical contract every backend build must satisfy. Output of `paint`.
|
|
120
|
-
* @experimental Part of the iterative-session/canvas surface — see {@link RenderSession}.
|
|
121
251
|
*/
|
|
122
252
|
export interface PaintResult {
|
|
123
|
-
layoutWidth: number;
|
|
253
|
+
layoutWidth: number; // canvas.style.width target; independent of densityScale
|
|
124
254
|
layoutHeight: number;
|
|
125
|
-
pixelWidth: number;
|
|
255
|
+
pixelWidth: number; // canvas.width the painter wrote (clamped at 16384)
|
|
126
256
|
pixelHeight: number;
|
|
257
|
+
/**
|
|
258
|
+
* True when `MAX_BACKING_DIMENSION` forced `densityScale` down: the page is
|
|
259
|
+
* painted at fewer device pixels than requested and renders soft at the same
|
|
260
|
+
* `canvas.style` size. Reads the clamp off the return value instead of the
|
|
261
|
+
* `pixelWidth < round(layoutWidth × densityScale)` derivation.
|
|
262
|
+
*/
|
|
263
|
+
clamped: boolean;
|
|
264
|
+
/**
|
|
265
|
+
* The `densityScale` actually applied — equal to the requested value unless
|
|
266
|
+
* `clamped`, then reduced proportionally. `layoutScale × effectiveDensityScale`
|
|
267
|
+
* is the scale the backing store was rasterized at.
|
|
268
|
+
*/
|
|
269
|
+
effectiveDensityScale: number;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Canonical contract every backend build must satisfy. Output of
|
|
274
|
+
* {@link LiveSession.apply}: `dirtyPages` lists the pages whose rendered
|
|
275
|
+
* content differs from the previous compile, including added pages; removed
|
|
276
|
+
* pages are implied by `pageCount`. Repaint `dirty ∩ visible`.
|
|
277
|
+
*/
|
|
278
|
+
export interface ChangeSet {
|
|
279
|
+
pageCount: number;
|
|
280
|
+
dirtyPages: number[];
|
|
127
281
|
}
|
|
128
282
|
|
|
129
283
|
/**
|
|
@@ -161,16 +315,20 @@ export interface EngineOptions {
|
|
|
161
315
|
export declare class Engine {
|
|
162
316
|
constructor(options?: EngineOptions);
|
|
163
317
|
|
|
164
|
-
/**
|
|
318
|
+
/**
|
|
319
|
+
* Render `doc` against `quill` in one shot. Both handles are read
|
|
320
|
+
* synchronously before the first await, so the caller may `free()` them as
|
|
321
|
+
* soon as this call returns.
|
|
322
|
+
*/
|
|
165
323
|
render(quill: Quill, doc: Document, options?: RenderOptions): Promise<RenderResult>;
|
|
166
324
|
|
|
167
325
|
/**
|
|
168
|
-
* Open
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
326
|
+
* Open a live render session (canvas preview / per-page paint / `apply`).
|
|
327
|
+
* The `quill` and `doc` handles are read synchronously before the first
|
|
328
|
+
* await, so the caller may `free()` them as soon as this call returns; the
|
|
329
|
+
* caller owns the returned session and must `.free()` it.
|
|
172
330
|
*/
|
|
173
|
-
open(quill: Quill, doc: Document): Promise<
|
|
331
|
+
open(quill: Quill, doc: Document): Promise<LiveSession>;
|
|
174
332
|
|
|
175
333
|
/**
|
|
176
334
|
* Output formats `quill`'s backend can emit. An ALWAYS-free pre-render probe:
|
|
@@ -180,29 +338,126 @@ export declare class Engine {
|
|
|
180
338
|
supportedFormats(quill: Quill): Promise<OutputFormat[]>;
|
|
181
339
|
|
|
182
340
|
/**
|
|
183
|
-
* Whether `quill`'s
|
|
184
|
-
*
|
|
185
|
-
* `
|
|
186
|
-
*
|
|
341
|
+
* Whether `quill`'s BACKEND can paint sessions to a canvas — a pre-session
|
|
342
|
+
* ESTIMATE, not a fact about any particular compile. Same always-free probe
|
|
343
|
+
* as `supportedFormats`: answered from the descriptor's required `canvas`
|
|
344
|
+
* manifest, no binary load and no quill clone. Both the Typst and pdfform
|
|
345
|
+
* backends report `true` here unconditionally; each paints a complete page
|
|
346
|
+
* raster (see {@link LiveSession.paint}) — but a specific compile can still
|
|
347
|
+
* refuse to paint (e.g. a 0-page document), so this can answer `true` while
|
|
348
|
+
* the resulting {@link LiveSession.supportsCanvas} answers `false`. Gate
|
|
349
|
+
* mounting a canvas UI on this; gate the actual `paint` call on the session's
|
|
350
|
+
* getter once `open()` has run.
|
|
187
351
|
*/
|
|
188
352
|
supportsCanvas(quill: Quill): Promise<boolean>;
|
|
189
353
|
}
|
|
190
354
|
|
|
191
355
|
/**
|
|
192
356
|
* Iterative render session over a compiled snapshot. `free()` when done.
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
357
|
+
*
|
|
358
|
+
* CANVAS PAINT IS COMPLETE. {@link LiveSession.paint} writes a complete page
|
|
359
|
+
* raster — every piece of page content is already visible in the painted
|
|
360
|
+
* pixels, with NO compositing required by the caller. Both backends that
|
|
361
|
+
* support canvas satisfy this: Typst rasterizes its laid-out page natively;
|
|
362
|
+
* pdfform pre-flattens bound field values into the page content and rasterizes
|
|
363
|
+
* that, so field values appear in the raster on their own.
|
|
364
|
+
* {@link LiveSession.regions} carries schema-field geometry for interactive
|
|
365
|
+
* overlays / cross-navigation drawn on top of the raster; it is never needed to
|
|
366
|
+
* complete the picture.
|
|
197
367
|
*/
|
|
198
|
-
export declare class
|
|
368
|
+
export declare class LiveSession {
|
|
199
369
|
private constructor();
|
|
200
370
|
readonly pageCount: number;
|
|
201
371
|
readonly backendId: string;
|
|
372
|
+
/**
|
|
373
|
+
* `true` iff `paint`/`pageSize` will succeed for THIS compile — the
|
|
374
|
+
* authoritative answer, derived from the session's canvas seam, so it can
|
|
375
|
+
* never disagree with what `paint` actually does. This can be `false` even
|
|
376
|
+
* when {@link Engine.supportsCanvas} answered `true` for the same `quill`
|
|
377
|
+
* (that probe is a pre-session backend estimate; e.g. a canvas-capable
|
|
378
|
+
* backend compiled to a 0-page document has nothing to paint). Re-check
|
|
379
|
+
* this getter after `open()` rather than relying on the engine hint alone.
|
|
380
|
+
*/
|
|
202
381
|
readonly supportsCanvas: boolean;
|
|
203
382
|
readonly warnings: Diagnostic[];
|
|
383
|
+
/**
|
|
384
|
+
* Recompile the session against `doc` — the edit verb of a live preview.
|
|
385
|
+
* Transactional: on throw every read (`render`, `paint`, `pageSize`,
|
|
386
|
+
* `regions`) keeps serving the last-good compile, and the session recovers
|
|
387
|
+
* on the next successful `apply`. On success reads serve the new compile;
|
|
388
|
+
* repaint `dirtyPages ∩ visible`.
|
|
389
|
+
*/
|
|
390
|
+
apply(doc: Document): ChangeSet;
|
|
204
391
|
render(options?: RenderOptions): RenderResult;
|
|
392
|
+
/**
|
|
393
|
+
* Schema-field geometry for this compiled session, keyed on quill schema
|
|
394
|
+
* field path. A session-level query: no render, no byte artifact. Read it
|
|
395
|
+
* to scroll to / highlight the focused field over a `paint`-ed canvas;
|
|
396
|
+
* the click direction is {@link fieldAt}. Empty for backends that place
|
|
397
|
+
* no schema fields.
|
|
398
|
+
*
|
|
399
|
+
* `field` is **not** unique: a content field surfaces its **first
|
|
400
|
+
* placement** as one {@link FieldRegion} per page that placement touches
|
|
401
|
+
* (so a highlight covers continuation pages); a scalar referenced at
|
|
402
|
+
* several plate sites surfaces each site; tracked content plus a
|
|
403
|
+
* `field:`-bound widget yields both, widget ordered first. Group by
|
|
404
|
+
* `field` — every entry routes to that field. Later placements of one
|
|
405
|
+
* content value are not enumerated; {@link fieldAt} still resolves
|
|
406
|
+
* clicks on them.
|
|
407
|
+
*/
|
|
408
|
+
regions(): FieldRegion[];
|
|
409
|
+
/**
|
|
410
|
+
* The whole-field highlight boxes for `field` — one union rect per page,
|
|
411
|
+
* over the field's `span`-bearing content segments (the "highlight the
|
|
412
|
+
* focused field" quantity). Owns the union {@link regions} leaves derived
|
|
413
|
+
* (span-filter + per-page union), keeping `regions()` the low-level disjoint
|
|
414
|
+
* truth, so a consumer stops reimplementing it. **Content only** — a field
|
|
415
|
+
* placed solely as a scalar reference or a bound widget carries no `span`
|
|
416
|
+
* and returns `[]`; its box is a single {@link regions} rect. Reflects the
|
|
417
|
+
* current compile, like `regions()`.
|
|
418
|
+
*/
|
|
419
|
+
fieldBoxes(field: string): FieldRegion[];
|
|
420
|
+
/**
|
|
421
|
+
* The schema field whose content is under a point on `page` — the forward
|
|
422
|
+
* (click → field) direction: hit-test a click against the compiled
|
|
423
|
+
* document and get back the field address to focus in the editor, or
|
|
424
|
+
* `undefined` off any field's ink. `x`/`y` are PDF points with a
|
|
425
|
+
* **bottom-left** origin, the same space as {@link FieldRegion.rect} —
|
|
426
|
+
* from a canvas click, invert the overlay transform documented there:
|
|
427
|
+
* `x = clickPx.x / renderScale`,
|
|
428
|
+
* `y = pageHeightPt - clickPx.y / renderScale`. Unlike {@link regions},
|
|
429
|
+
* *every* placement answers, not just the first.
|
|
430
|
+
*/
|
|
431
|
+
fieldAt(page: number, x: number, y: number): string | undefined;
|
|
432
|
+
/**
|
|
433
|
+
* Fine-grained click → content position (caret placement). Same PDF-point
|
|
434
|
+
* space as {@link fieldAt}; `undefined` off all content ink.
|
|
435
|
+
*/
|
|
436
|
+
positionAt(page: number, x: number, y: number): ContentHit | undefined;
|
|
437
|
+
/** Content position → caret rect — reverse of {@link positionAt}. */
|
|
438
|
+
locate(field: string, pos: number): FieldRegion | undefined;
|
|
439
|
+
/** Page geometry in points (1/72″). Report-only; the painter sizes the canvas. */
|
|
205
440
|
pageSize(page: number): PageSize;
|
|
441
|
+
/**
|
|
442
|
+
* Paint `page` into a 2D canvas context, sizing the backing store itself
|
|
443
|
+
* (it owns `canvas.width`/`height`; the caller owns `canvas.style.*`). The
|
|
444
|
+
* painted raster is COMPLETE — all page content visible, no caller-side
|
|
445
|
+
* compositing (Typst rasterizes natively; pdfform rasterizes its
|
|
446
|
+
* pre-flattened page). Effective rasterization scale is
|
|
447
|
+
* `layoutScale × densityScale`, clamped so neither backing dimension exceeds
|
|
448
|
+
* 16384 px — {@link PaintResult.clamped} reports the clamp and
|
|
449
|
+
* {@link PaintResult.effectiveDensityScale} the density actually applied.
|
|
450
|
+
*
|
|
451
|
+
* The write is a whole-backing-store `putImageData`, which bypasses the 2D
|
|
452
|
+
* context transform, `globalAlpha`, and clip: the painter owns the entire
|
|
453
|
+
* canvas, so give each visible page its own `` element. You cannot
|
|
454
|
+
* paint two pages into one canvas, paint into a sub-rect, or apply a context
|
|
455
|
+
* transform through this call — the raster is complete precisely so you never
|
|
456
|
+
* need to. Keep the per-page canvases alive while their pages stay near the
|
|
457
|
+
* viewport: each `paint` re-rasterizes from scratch, so reusing (pooling) a
|
|
458
|
+
* canvas across pages on scroll re-runs a full render, whereas an idle canvas
|
|
459
|
+
* retains its pixels for free.
|
|
460
|
+
*/
|
|
206
461
|
paint(
|
|
207
462
|
ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
208
463
|
page: number,
|
|
@@ -210,3 +465,186 @@ export declare class RenderSession {
|
|
|
210
465
|
): PaintResult;
|
|
211
466
|
free(): void;
|
|
212
467
|
}
|
|
468
|
+
|
|
469
|
+
// ── Typed writer — the schema-bound front door ───────────────────────────────
|
|
470
|
+
|
|
471
|
+
// `quill.writer(doc)` is patched onto the re-exported `Quill` prototype (the
|
|
472
|
+
// class is re-exported verbatim, so the method is declared by merging into the
|
|
473
|
+
// core module's `Quill` rather than redeclaring the class).
|
|
474
|
+
declare module '../core/wasm.js' {
|
|
475
|
+
interface Quill {
|
|
476
|
+
/**
|
|
477
|
+
* Bind this quill's schema to `doc` for typed writes — the documented
|
|
478
|
+
* front door, mirroring core's `quill.writer(&mut doc)`. The schema grants
|
|
479
|
+
* the typing, so the quill is the factory. The returned writer holds both
|
|
480
|
+
* handles by reference and owns neither (nothing to `free()`); it is
|
|
481
|
+
* ephemeral by convention — bind, write, discard.
|
|
482
|
+
*/
|
|
483
|
+
writer(doc: Document): DocumentWriter;
|
|
484
|
+
/**
|
|
485
|
+
* Bind this quill's schema to `doc` for interpreted reads — the read twin of
|
|
486
|
+
* {@link Quill.writer}, mirroring core's `quill.view(&doc)`. Each field is
|
|
487
|
+
* read by its declared type (a richtext field to markdown, every other type
|
|
488
|
+
* verbatim) with schema authority, so a name the schema does not declare
|
|
489
|
+
* throws rather than reading back `undefined`. Holds both handles by
|
|
490
|
+
* reference and owns neither (nothing to `free()`); ephemeral by convention —
|
|
491
|
+
* bind, read, discard.
|
|
492
|
+
*/
|
|
493
|
+
view(doc: Document): DocumentView;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* A `Document` bound to its `Quill` for typed writes — the schema-bound writer,
|
|
499
|
+
* constructed via {@link Quill.writer}. Speaks names, values, and markdown. Bare
|
|
500
|
+
* `set` / `setAll` / `setBody` / `reviseField` / `addCard` / `card(i).set`
|
|
501
|
+
* instead of threading the `quill` handle through the underscored ABI. Holds both
|
|
502
|
+
* handles by reference and owns neither — nothing to `free()`.
|
|
503
|
+
*
|
|
504
|
+
* Typed commit is the default whenever a quill is in hand: it resolves each
|
|
505
|
+
* field's schema type and strict-commits it, throwing `UnknownField` for a name
|
|
506
|
+
* the schema does not declare — on the typed path an undeclared name is a typo,
|
|
507
|
+
* not a fallback. The raw `Document.storeField` / `storeFields` verbs remain the
|
|
508
|
+
* deliberate quill-free primitive (standalone data, storage/migration infra, or
|
|
509
|
+
* holding not-yet-conforming in-progress input).
|
|
510
|
+
*/
|
|
511
|
+
export declare class DocumentWriter {
|
|
512
|
+
constructor(quill: Quill, doc: Document);
|
|
513
|
+
/** The bound document — the instance passed in, mutated in place. */
|
|
514
|
+
readonly document: Document;
|
|
515
|
+
/**
|
|
516
|
+
* Typed-commit one main-card field (strict coerce, mismatch throws now).
|
|
517
|
+
* Throws `UnknownField` for a name the schema does not declare.
|
|
518
|
+
*/
|
|
519
|
+
set(name: string, value: unknown): void;
|
|
520
|
+
/**
|
|
521
|
+
* Typed-commit several main-card fields atomically — nothing is applied on
|
|
522
|
+
* error (throws a {@link QuillmarkError} carrying one diagnostic per
|
|
523
|
+
* offending field, including an `UnknownField` for each undeclared name).
|
|
524
|
+
*/
|
|
525
|
+
setAll(fields: Record<string, unknown>): void;
|
|
526
|
+
/**
|
|
527
|
+
* Set the main body from markdown (edit semantics: anchors rebase), discarding
|
|
528
|
+
* the delta — the receipt-free body write. Use `doc.revise({}, md)` for the
|
|
529
|
+
* `Delta` receipt.
|
|
530
|
+
*/
|
|
531
|
+
setBody(markdown: string): void;
|
|
532
|
+
/**
|
|
533
|
+
* Revise the richtext main-card field `name` from markdown — typed *and*
|
|
534
|
+
* anchor-preserving. Surviving anchors rebase, then the diffed result is
|
|
535
|
+
* schema-conformed (`richtext(inline)` rejects a multi-block result). Throws
|
|
536
|
+
* `UnknownField` for a name the schema does not declare. Returns the `Delta`.
|
|
537
|
+
*/
|
|
538
|
+
reviseField(name: string, markdown: string): Delta;
|
|
539
|
+
/**
|
|
540
|
+
* Build a composable card of `kind`, typed-commit `fields` onto it, set its
|
|
541
|
+
* body from optional markdown, and place it — the fused `makeCard` + typed
|
|
542
|
+
* commit + insertion. `at` picks the position: omitted appends, a number
|
|
543
|
+
* inserts at that index, so a positioned typed insert is one atomic call
|
|
544
|
+
* rather than `addCard` + `moveCard`. Transactional: a rejected field (throws
|
|
545
|
+
* a per-field diagnostic bundle) or an invalid kind/body/position leaves the
|
|
546
|
+
* document untouched.
|
|
547
|
+
*/
|
|
548
|
+
addCard(kind: string, fields?: Record<string, unknown>, body?: string, at?: number): void;
|
|
549
|
+
/** Remove the composable card at `index`, returning it (or `undefined`). */
|
|
550
|
+
removeCard(index: number): Card | undefined;
|
|
551
|
+
/**
|
|
552
|
+
* A {@link CardWriter} for the composable card at `index`. Index validity is
|
|
553
|
+
* checked lazily at commit time, so this never throws. The cursor is
|
|
554
|
+
* ephemeral — a `removeCard`/`addCard` between binding and writing silently
|
|
555
|
+
* retargets it; for durable addressing stamp `$id` and re-resolve at write.
|
|
556
|
+
*/
|
|
557
|
+
card(index: number): CardWriter;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* A composable card bound to its `Quill` for typed writes, from
|
|
562
|
+
* {@link DocumentWriter.card}. Same verbs as {@link DocumentWriter}, targeting
|
|
563
|
+
* the card at its bound index; each write throws `IndexOutOfRange` if that index
|
|
564
|
+
* is out of range.
|
|
565
|
+
*/
|
|
566
|
+
export declare class CardWriter {
|
|
567
|
+
constructor(quill: Quill, doc: Document, index: number);
|
|
568
|
+
/** The bound card index. */
|
|
569
|
+
readonly index: number;
|
|
570
|
+
/**
|
|
571
|
+
* The bound card's `$kind` (empty string when it carries none), read through
|
|
572
|
+
* the document — mirrors core `CardWriter::kind()`. Throws `IndexOutOfRange`
|
|
573
|
+
* if the bound index is out of range.
|
|
574
|
+
*/
|
|
575
|
+
readonly kind: string;
|
|
576
|
+
set(name: string, value: unknown): void;
|
|
577
|
+
setAll(fields: Record<string, unknown>): void;
|
|
578
|
+
/** Set this card's body from markdown (edit semantics), discarding the delta. */
|
|
579
|
+
setBody(markdown: string): void;
|
|
580
|
+
/**
|
|
581
|
+
* Revise the richtext field `name` on this card from markdown — typed *and*
|
|
582
|
+
* anchor-preserving; the card twin of {@link DocumentWriter.reviseField}.
|
|
583
|
+
* Throws `UnknownField` for an undeclared name and `IndexOutOfRange` if the
|
|
584
|
+
* bound index is out of range. Returns the `Delta`.
|
|
585
|
+
*/
|
|
586
|
+
reviseField(name: string, markdown: string): Delta;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* A `Document` bound to its `Quill` for interpreted reads — the schema-plane read
|
|
591
|
+
* view, constructed via {@link Quill.view} and the read twin of
|
|
592
|
+
* {@link DocumentWriter}. One `get` reads each field by its declared type: a
|
|
593
|
+
* richtext field to its markdown projection, a plaintext field to its literal
|
|
594
|
+
* text, every other type its canonical value verbatim. Holds both handles by
|
|
595
|
+
* reference and owns neither — nothing to `free()`.
|
|
596
|
+
*
|
|
597
|
+
* The schema authority is the point: unlike the quill-free transport `Document.get`,
|
|
598
|
+
* a name the schema does not declare throws `UnknownField` (a typo) rather than
|
|
599
|
+
* reading back `undefined`, and a content field holding a value that does not
|
|
600
|
+
* decode throws `FieldRichtextDecode`. A field's markdown lives here, not on the
|
|
601
|
+
* body-only `Document.getMarkdown`. The body read stays quill-free (a body's type
|
|
602
|
+
* is a format fact) and never throws.
|
|
603
|
+
*/
|
|
604
|
+
export declare class DocumentView {
|
|
605
|
+
constructor(quill: Quill, doc: Document);
|
|
606
|
+
/** The bound document — the instance passed in. */
|
|
607
|
+
readonly document: Document;
|
|
608
|
+
/**
|
|
609
|
+
* Read the value at `addr`, interpreted by its declared type: a richtext field
|
|
610
|
+
* to markdown, every other type verbatim. A bare string is `Addr` shorthand for
|
|
611
|
+
* `{ field }`; an absent `addr.field` reads the body markdown. `undefined` for
|
|
612
|
+
* an absent field; throws `UnknownField` for a name the schema does not declare,
|
|
613
|
+
* `FieldRichtextDecode` for a richtext field holding an undecodable value, and
|
|
614
|
+
* `IndexOutOfRange` for a bad `addr.card`.
|
|
615
|
+
*/
|
|
616
|
+
get(addr: Addr | string): unknown;
|
|
617
|
+
/** The main body's markdown — the quill-free body read. Equals `get({})`. */
|
|
618
|
+
getBody(): string;
|
|
619
|
+
/**
|
|
620
|
+
* A {@link CardView} for the composable card at `index`. Index validity is
|
|
621
|
+
* checked lazily at read time, so this never throws. The cursor is ephemeral —
|
|
622
|
+
* a `removeCard`/`addCard` between binding and reading silently retargets it.
|
|
623
|
+
*/
|
|
624
|
+
card(index: number): CardView;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* A composable card bound to its `Quill` for interpreted reads, from
|
|
629
|
+
* {@link DocumentView.card}. Same verbs as {@link DocumentView}, reading the card
|
|
630
|
+
* at its bound index; each read throws `IndexOutOfRange` if that index is out of
|
|
631
|
+
* range.
|
|
632
|
+
*/
|
|
633
|
+
export declare class CardView {
|
|
634
|
+
constructor(quill: Quill, doc: Document, index: number);
|
|
635
|
+
/** The bound card index. */
|
|
636
|
+
readonly index: number;
|
|
637
|
+
/**
|
|
638
|
+
* The bound card's `$kind` (empty string when it carries none). Throws
|
|
639
|
+
* `IndexOutOfRange` if the bound index is out of range.
|
|
640
|
+
*/
|
|
641
|
+
readonly kind: string;
|
|
642
|
+
/**
|
|
643
|
+
* Read the field `name` on this card, interpreted by its declared type.
|
|
644
|
+
* `undefined` when absent; throws `UnknownField` for an undeclared name and
|
|
645
|
+
* `IndexOutOfRange` for a bad index.
|
|
646
|
+
*/
|
|
647
|
+
get(name: string): unknown;
|
|
648
|
+
/** This card's body markdown — the card twin of {@link DocumentView.getBody}. */
|
|
649
|
+
getBody(): string;
|
|
650
|
+
}
|