@quillmark/wasm 0.114.0 → 0.116.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.
@@ -21,6 +21,8 @@ import type {
21
21
  rebase,
22
22
  mapPos,
23
23
  mapMarks,
24
+ isInline,
25
+ isPlain,
24
26
  parseDocPath,
25
27
  formatDocPath,
26
28
  formatDiagnostic
@@ -41,6 +43,8 @@ export interface CoreSurface {
41
43
  rebase: typeof rebase;
42
44
  mapPos: typeof mapPos;
43
45
  mapMarks: typeof mapMarks;
46
+ isInline: typeof isInline;
47
+ isPlain: typeof isPlain;
44
48
  parseDocPath: typeof parseDocPath;
45
49
  formatDocPath: typeof formatDocPath;
46
50
  formatDiagnostic: typeof formatDiagnostic;
@@ -103,12 +107,12 @@ export type {
103
107
  Severity,
104
108
  QuillSchema,
105
109
  QuillFieldSchema,
110
+ QuillFieldType,
106
111
  QuillCardSchema,
107
112
  QuillCardBody,
108
113
  QuillFieldUi,
109
114
  QuillCardUi,
110
115
  QuillGroupUi,
111
- QuillMatrixGroup,
112
116
  QuillMetadata
113
117
  } from '../core/wasm.js';
114
118
 
@@ -182,9 +186,9 @@ export declare function isQuillmarkError(e: unknown): e is QuillmarkError;
182
186
 
183
187
  import type { ContentContainer } from '../core/wasm.js';
184
188
 
185
- // `ContentIsland.type`, `ContentMark.type`, `ContentLine.kind`,
186
- // `ContentContainer.container` and an island's `loss` are closed sets, so a
187
- // bare discriminant check narrows the payload on its own:
189
+ // `ContentIsland.type`, `ContentMark.type`, `ContentLine.kind` and
190
+ // `ContentContainer.container` are closed sets, so a bare discriminant check
191
+ // narrows the payload on its own:
188
192
  // `line.kind === 'heading'` reaches `line.attrs.level`, with no guard to call.
189
193
 
190
194
  /**
@@ -293,8 +297,9 @@ export interface ContentHit {
293
297
  *
294
298
  * ```js
295
299
  * const [x0, y0, x1, y1] = region.rect;
296
- * // Into a raster painted at renderScale (= layoutScale × densityScale):
297
- * const left = x0 * renderScale, top = (pageHeightPt - y1) * renderScale;
300
+ * // Into a painted raster, at the scale it was painted at:
301
+ * const scale = canvas.width / pageWidthPt;
302
+ * const left = x0 * scale, top = (pageHeightPt - y1) * scale;
298
303
  * // Or, for an HTML overlay on a width:100% canvas, as % of the page, which
299
304
  * // tracks the displayed size across DPI and pane resize with no scale to thread:
300
305
  * const leftPct = (x0 / pageWidthPt) * 100, topPct = (1 - y1 / pageHeightPt) * 100;
@@ -304,9 +309,9 @@ export interface FieldRegion {
304
309
  /**
305
310
  * The field's canonical `DocPath` address (`parseDocPath`-routable), not a
306
311
  * backend widget name: `main.signature_block`,
307
- * `cards.<kind>[<i>].signature_block` (`cards[<i>].…` when the card's kind is
308
- * unknown), an array element bracketed and a key dotted. The same spelling
309
- * `Diagnostic.path` uses, so the two join on string equality.
312
+ * `cards.<kind>[<i>].signature_block`, an array element bracketed and a key
313
+ * dotted. The same spelling `Diagnostic.path` uses, so the two join on
314
+ * string equality.
310
315
  */
311
316
  field: string;
312
317
  /** 0-based page index. */
@@ -344,36 +349,6 @@ export interface PageSize {
344
349
  heightPt: number;
345
350
  }
346
351
 
347
- /** Inputs to `paint`. */
348
- export interface PaintOptions {
349
- /** How big the page is on screen, in CSS px per point. Default 1. */
350
- layoutScale?: number;
351
- /**
352
- * How sharp it is: `window.devicePixelRatio`, in-app zoom and
353
- * `visualViewport.scale` folded into one number. Default 1, because the
354
- * painter cannot see any of them (SSR, tests, off-screen).
355
- */
356
- densityScale?: number;
357
- }
358
-
359
- /** Output of `paint`. */
360
- export interface PaintResult {
361
- layoutWidth: number; // canvas.style.width target; independent of densityScale
362
- layoutHeight: number;
363
- pixelWidth: number; // canvas.width the painter wrote (clamped at 16384)
364
- pixelHeight: number;
365
- /**
366
- * True when the backing-store clamp forced `densityScale` down: the page
367
- * renders soft at the same `canvas.style` size.
368
- */
369
- clamped: boolean;
370
- /**
371
- * The `densityScale` actually applied, reduced proportionally when
372
- * `clamped`. `layoutScale × effectiveDensityScale` is the rasterized scale.
373
- */
374
- effectiveDensityScale: number;
375
- }
376
-
377
352
  /**
378
353
  * Output of {@link LiveSession.update}: `dirtyPages` lists the pages whose
379
354
  * content differs from the previous compile, including added pages; removed
@@ -426,21 +401,27 @@ export declare class Engine {
426
401
  * synchronously before the first await, so the caller may `free()` them as
427
402
  * soon as this call returns.
428
403
  *
429
- * This is the surface that merges the two warning halves:
430
- * {@link RenderResult.warnings} carries `doc.warnings` (parse, `conform::*`,
431
- * `plate::unsupported_construct`) ahead of the compile's own. A
432
- * {@link LiveSession} outlives the document it opened from, so
433
- * {@link LiveSession.render} carries the compile half alone.
404
+ * {@link RenderResult.warnings} carries the whole pipeline's, in order:
405
+ * `doc.warnings` (parse and `conform::*`), every `quill.validate(doc)`
406
+ * warning, then the compile's own. A {@link LiveSession} outlives the
407
+ * document it opened from, so {@link LiveSession.render} carries the
408
+ * compile's alone: read `doc.warnings` and `quill.validate(doc)` beside it.
409
+ *
410
+ * `today` reads as on {@link open}.
434
411
  */
435
- render(quill: Quill, doc: Document, options?: RenderOptions): Promise<RenderResult>;
412
+ render(quill: Quill, doc: Document, options?: RenderOptions, today?: string): Promise<RenderResult>;
436
413
 
437
414
  /**
438
415
  * Open a live render session (canvas preview / per-page paint / `update`).
439
416
  * The `quill` and `doc` handles are read synchronously before the first
440
417
  * await, so the caller may `free()` them as soon as this call returns; the
441
418
  * caller owns the returned session and must `.free()` it.
419
+ *
420
+ * `today` (`YYYY-MM-DD`, default the local date) is the render date: a
421
+ * `today` date field renders as it, and so does a plate's
422
+ * `datetime.today()`. The session keeps it for every `update`.
442
423
  */
443
- open(quill: Quill, doc: Document): Promise<LiveSession>;
424
+ open(quill: Quill, doc: Document, today?: string): Promise<LiveSession>;
444
425
 
445
426
  /**
446
427
  * Output formats `quill`'s backend can emit. An always-free pre-render probe:
@@ -505,15 +486,17 @@ export declare class LiveSession {
505
486
  * The schema field whose content is under a point on `page`: the canonical
506
487
  * `DocPath` address to focus in the editor, or `undefined` off any field's
507
488
  * ink. `x`/`y` are PDF points with a **bottom-left** origin, the same space as
508
- * {@link FieldRegion.rect}, so from a canvas click invert the overlay
509
- * transform documented there: `x = clickPx.x / renderScale`,
510
- * `y = pageHeightPt - clickPx.y / renderScale`. Unlike {@link regions},
489
+ * {@link FieldRegion.rect}. A pointer event reports CSS pixels, so divide by
490
+ * the CSS px per point the page is shown at,
491
+ * `k = canvas.clientWidth / pageWidthPt`, not by {@link paint}'s `scale`,
492
+ * which carries `devicePixelRatio`: `x = e.offsetX / k`,
493
+ * `y = pageHeightPt - e.offsetY / k`. Unlike {@link regions},
511
494
  * *every* placement answers, not just the first.
512
495
  *
513
496
  * `tolPt` is how far off the ink a click still counts, in the same points,
514
497
  * and defaults to `0` — exact. It is pointer slack, so derive it from the
515
- * scale the page was drawn at (`slackPx / renderScale`) rather than fixing a
516
- * value in points, which shrinks under the cursor as the page zooms out. The
498
+ * same `k` (`slackPx / k`) rather than fixing a value in points, which
499
+ * shrinks under the cursor as the page zooms out. The
517
500
  * nearest placement answers and containment is distance zero, so raising
518
501
  * `tolPt` only ever fills a miss.
519
502
  */
@@ -532,14 +515,16 @@ export declare class LiveSession {
532
515
  * canonical `DocPath` address (`parseDocPath`-routable), as {@link regions} keys.
533
516
  */
534
517
  locate(field: string, pos: number): FieldRegion | undefined;
535
- /** Page geometry in points (1/72″). Report-only; the painter sizes the canvas. */
518
+ /** Page geometry in points (1/72″). */
536
519
  pageSize(page: number): PageSize;
537
520
  /**
538
- * Paint `page` into a 2D canvas context, sizing the backing store itself (it
539
- * owns `canvas.width`/`height`; the caller owns `canvas.style.*`). The
540
- * rasterization scale is `layoutScale × densityScale`, clamped so neither
541
- * backing dimension exceeds 16384 px; {@link PaintResult.clamped} reports the
542
- * clamp and {@link PaintResult.effectiveDensityScale} the density applied.
521
+ * Paint `page` into a 2D canvas context at `scale` backing-store pixels per
522
+ * point: `devicePixelRatio` times the CSS px per point the page is shown at.
523
+ * The painter owns `canvas.width`/`height`, reducing `scale` where it must so
524
+ * neither exceeds 16384 px, so `canvas.width / widthPt` is the scale painted
525
+ * at; the caller owns `canvas.style.*`, and a canvas styled `width: 100%`
526
+ * needs nothing back from the paint. Throws `backend::invalid_raster_scale`
527
+ * on a `scale` that is not finite and positive.
543
528
  *
544
529
  * The write is a whole-backing-store `putImageData`, which bypasses the 2D
545
530
  * context transform, `globalAlpha`, and clip, so give each visible page its
@@ -552,8 +537,8 @@ export declare class LiveSession {
552
537
  paint(
553
538
  ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
554
539
  page: number,
555
- options?: PaintOptions
556
- ): PaintResult;
540
+ scale: number
541
+ ): void;
557
542
  free(): void;
558
543
  }
559
544
 
@@ -657,8 +642,8 @@ export declare class CardWriter {
657
642
  /** The bound card index. */
658
643
  readonly index: number;
659
644
  /**
660
- * The bound card's `$kind`, empty string when it carries none. Throws
661
- * `IndexOutOfRange` for a bad bound index.
645
+ * The bound card's `$kind`. Throws `IndexOutOfRange` for a bad bound
646
+ * index.
662
647
  */
663
648
  readonly kind: string;
664
649
  set(name: string, value: unknown): void;
@@ -739,10 +724,11 @@ export declare class DocumentReader {
739
724
  * The resolved-value view: for every declared field, the value the render
740
725
  * projection would use and the rung it came from (`authored` / `default` /
741
726
  * `blank`). The one read that blank-fills and coerces; {@link get} reports
742
- * what the document carries. Value and provenance only; completeness stays
743
- * `quill.validate`'s.
727
+ * what the document carries. Value and provenance only; diagnostics stay
728
+ * `quill.validate`'s. `today` reads as on {@link Engine.open}; an editor
729
+ * pairing it with a session passes the session's opening date.
744
730
  */
745
- resolve(): Resolved;
731
+ resolve(today?: string): Resolved;
746
732
  /**
747
733
  * A {@link CardReader} for the composable card at `index`. Index validity is
748
734
  * checked lazily at read time, so an out-of-range index does not throw here.
@@ -763,8 +749,8 @@ export declare class CardReader {
763
749
  /** The bound card index. */
764
750
  readonly index: number;
765
751
  /**
766
- * The bound card's `$kind` (empty string when it carries none). Throws
767
- * `IndexOutOfRange` if the bound index is out of range.
752
+ * The bound card's `$kind`. Throws `IndexOutOfRange` if the bound index is
753
+ * out of range.
768
754
  */
769
755
  readonly kind: string;
770
756
  /**
@@ -20,6 +20,7 @@ import initCore, { Quill, Document } from '../core/wasm.js';
20
20
  // Resolution-time, so `node:fs` never enters a browser graph.
21
21
  import { toModuleSource } from '#quillmark-env';
22
22
  import { importMarkdown, exportMarkdown, rebase, mapPos, mapMarks } from '../core/wasm.js';
23
+ import { isInline, isPlain } from '../core/wasm.js';
23
24
  import { parseDocPath, formatDocPath, formatDiagnostic } from '../core/wasm.js';
24
25
 
25
26
  // A `--target web` build exports its classes synchronously but carries no wasm
@@ -49,6 +50,8 @@ const CORE_SURFACE = Object.freeze({
49
50
  rebase,
50
51
  mapPos,
51
52
  mapMarks,
53
+ isInline,
54
+ isPlain,
52
55
  parseDocPath,
53
56
  formatDocPath,
54
57
  formatDiagnostic
@@ -174,7 +177,7 @@ const HANDLE_KINDS = {
174
177
  },
175
178
  Document: {
176
179
  code: 'runtime::not_a_document',
177
- hint: 'Pass a Document built by Document.fromMarkdown / fromStored or quill.seedDocument. A Document from another copy of @quillmark/wasm is refused too — each copy is its own WASM linear memory and its own class — so run `npm ls @quillmark/wasm` and dedupe to one.'
180
+ hint: 'Pass a Document built by Document.fromMarkdown / fromStored or quill.seedDocument / emptyDocument. A Document from another copy of @quillmark/wasm is refused too — each copy is its own WASM linear memory and its own class — so run `npm ls @quillmark/wasm` and dedupe to one.'
178
181
  }
179
182
  };
180
183
 
@@ -219,9 +222,15 @@ function requireLocalQuill(quill, method) {
219
222
  // WELD_KEYS is the rule `Container::same_weld` owns upstream: which `attrs`
220
223
  // entries two adjacent runs must share for the markdown projection to read them
221
224
  // as one. `start` is not among them, since CommonMark reads only a list's first
222
- // number — a subset, which is why every container needs its own entry. A table
223
- // rather than a switch, so `tests/known_names_drift.rs` can pin it against the
224
- // Rust predicate.
225
+ // number — a subset, which is why every container needs its own entry.
226
+ //
227
+ // Re-spelled here rather than called, because `assignInstances` answers before
228
+ // wasm initializes (`prose/canon/BINDINGS.md` § "WebAssembly"). What holds the
229
+ // two spellings equal is running the rule, not comparing their sources:
230
+ // `runtime.test.js` § "container run boundaries" stamps a pair and re-imports
231
+ // it, where `Content::normalize` re-mints against the Rust predicate. A tag the
232
+ // table omits welds with nothing, so `tests/known_names_drift.rs` pins that
233
+ // every container has an entry.
225
234
 
226
235
  const WELD_KEYS = { list_item: ['ordered'], quote: [] };
227
236
 
@@ -513,15 +522,16 @@ export class Engine {
513
522
  * @param {Quill} quill
514
523
  * @param {Document} doc
515
524
  * @param {object} [options] render options (`{ format, ppi, pages, regions }`)
525
+ * @param {string} [today] the render date, `YYYY-MM-DD`
516
526
  * @returns {Promise<import('./runtime.js').RenderResult>}
517
527
  */
518
- async render(quill, doc, options) {
528
+ async render(quill, doc, options, today) {
519
529
  return this.#withClones(
520
530
  'engine.render(quill, doc)',
521
531
  quill,
522
532
  doc,
523
533
  ({ engine, quill: q, doc: d, docWarnings }) => {
524
- const result = engine.render(q, d, options ?? undefined);
534
+ const result = engine.render(q, d, options ?? undefined, today);
525
535
  result.warnings = docWarnings.concat(result.warnings);
526
536
  return result;
527
537
  }
@@ -531,14 +541,15 @@ export class Engine {
531
541
  /**
532
542
  * @param {Quill} quill
533
543
  * @param {Document} doc
544
+ * @param {string} [today] the render date, `YYYY-MM-DD`
534
545
  * @returns {Promise<LiveSession>}
535
546
  */
536
- async open(quill, doc) {
547
+ async open(quill, doc, today) {
537
548
  return this.#withClones(
538
549
  'engine.open(quill, doc)',
539
550
  quill,
540
551
  doc,
541
- ({ mod, engine, quill: q, doc: d }) => new LiveSession(engine.open(q, d), mod)
552
+ ({ mod, engine, quill: q, doc: d }) => new LiveSession(engine.open(q, d, today), mod)
542
553
  );
543
554
  }
544
555
 
@@ -654,10 +665,10 @@ export class LiveSession {
654
665
  /**
655
666
  * @param {CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D} ctx
656
667
  * @param {number} page
657
- * @param {object} [options]
668
+ * @param {number} scale
658
669
  */
659
- paint(ctx, page, options) {
660
- return this.#inner.paint(ctx, page, options);
670
+ paint(ctx, page, scale) {
671
+ this.#inner.paint(ctx, page, scale);
661
672
  }
662
673
 
663
674
  free() {
@@ -860,10 +871,11 @@ export class DocumentReader {
860
871
  return this.#doc._readerGet(this.#quill, {});
861
872
  }
862
873
  /**
874
+ * @param {string} [today] the render date, `YYYY-MM-DD`
863
875
  * @returns {Resolved}
864
876
  */
865
- resolve() {
866
- return this.#quill._resolve(this.#doc);
877
+ resolve(today) {
878
+ return this.#quill._resolve(this.#doc, today);
867
879
  }
868
880
  /**
869
881
  * @param {number} index