@quillmark/wasm 0.95.1 → 0.97.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.
@@ -16,6 +16,9 @@
16
16
  export { Quill, Document, init } from '../core/wasm.js';
17
17
  // The document-free content codec, re-exported from the core build.
18
18
  export { importMarkdown, exportMarkdown, rebase, mapPos } from '../core/wasm.js';
19
+ // The document-model path parser/serializer — route on `Diagnostic.path`
20
+ // segments instead of regexing the string.
21
+ export { parseDocPath, formatDocPath } from '../core/wasm.js';
19
22
 
20
23
  import type { CardAddr } from '../core/wasm.js';
21
24
 
@@ -42,6 +45,7 @@ export type {
42
45
  QuillCardBody,
43
46
  QuillFieldUi,
44
47
  QuillCardUi,
48
+ QuillGroupUi,
45
49
  QuillMetadata
46
50
  } from '../core/wasm.js';
47
51
 
@@ -59,6 +63,9 @@ export type {
59
63
  ContentContainer,
60
64
  ContentMark,
61
65
  ContentIsland,
66
+ TableProps,
67
+ ImageProps,
68
+ TableCell,
62
69
  CardInput,
63
70
  PathStep,
64
71
  Addr,
@@ -67,7 +74,23 @@ export type {
67
74
  Assoc,
68
75
  LineOp,
69
76
  MarkOp,
70
- ChangeBundle
77
+ ChangeBundle,
78
+ DocPathSeg
79
+ } from '../core/wasm.js';
80
+
81
+ // The resolved-value view — the return shape of `quill.resolve(doc)`. Value
82
+ // + source rung per declared field (the body is a `body` sibling on its card,
83
+ // never a row in `fields`); diagnostics stay `quill.validate`, guidance stays
84
+ // `quill.schema`.
85
+ // Declared in the core build's generated `.d.ts` via a
86
+ // `typescript_custom_section`; re-exported here so the single public entry
87
+ // point names them.
88
+ export type {
89
+ FieldSource,
90
+ ResolvedField,
91
+ ResolvedMain,
92
+ ResolvedCard,
93
+ Resolved
71
94
  } from '../core/wasm.js';
72
95
 
73
96
  // ── Error contract ──────────────────────────────────────────────────────────
@@ -98,6 +121,37 @@ export interface QuillmarkError extends Error {
98
121
  */
99
122
  export declare function isQuillmarkError(e: unknown): e is QuillmarkError;
100
123
 
124
+ // ── Open-set discriminant guards ────────────────────────────────────────────
125
+ // `ContentIsland.type` / `ContentMark.type` are open sets — each union has a
126
+ // residual `{ type: string; … }` arm, so a bare discriminant check never narrows
127
+ // the payload (TS keeps the residual arm live, since a `string` can equal the
128
+ // literal). These guards are the checked narrowing path for the pinned arms; an
129
+ // unrecognized `type` fails every guard and keeps its opaque payload. Only the
130
+ // payload-carrying arms get a guard — the bare marks
131
+ // (`strong`/`emph`/`underline`/`strike`/`code`) narrow to nothing.
132
+
133
+ import type { ContentIsland, TableProps, ImageProps, ContentMark } from '../core/wasm.js';
134
+
135
+ /** Narrow a {@link ContentIsland} to the pinned `table` arm (`props: TableProps`). */
136
+ export declare function isTableIsland(
137
+ island: ContentIsland
138
+ ): island is ContentIsland & { type: 'table'; props: TableProps };
139
+
140
+ /** Narrow a {@link ContentIsland} to the pinned `image` arm (`props: ImageProps`). */
141
+ export declare function isImageIsland(
142
+ island: ContentIsland
143
+ ): island is ContentIsland & { type: 'image'; props: ImageProps };
144
+
145
+ /** Narrow a {@link ContentMark} to the `link` arm (carries `url`). */
146
+ export declare function isLinkMark(
147
+ mark: ContentMark
148
+ ): mark is ContentMark & { type: 'link'; url: string };
149
+
150
+ /** Narrow a {@link ContentMark} to the `anchor` arm (carries `id`). */
151
+ export declare function isAnchorMark(
152
+ mark: ContentMark
153
+ ): mark is ContentMark & { type: 'anchor'; id: string };
154
+
101
155
  // ── Canonical render-side types ─────────────────────────────────────────────
102
156
  // These are the BACKEND-NEUTRAL render contract of the plural-backend API. They
103
157
  // are defined HERE (not re-exported from one private backend) because no single
@@ -148,6 +202,10 @@ export type HitGranularity = 'cluster' | 'segment';
148
202
 
149
203
  /** A click resolved to a field and USV offset into its Content. */
150
204
  export interface ContentHit {
205
+ /**
206
+ * The field's canonical `DocPath` address (`parseDocPath`-routable) — the same
207
+ * address {@link LiveSession.fieldAt} returns for that point.
208
+ */
151
209
  field: string;
152
210
  pos: number;
153
211
  /**
@@ -158,8 +216,8 @@ export interface ContentHit {
158
216
  }
159
217
 
160
218
  /**
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
219
+ * A rendered field region: the canonical `DocPath` field address (`field`) plus
220
+ * its geometry (`rect`) on the page. Emitted by backends that place schema fields
163
221
  * (`pdfform` AcroForm widgets; Typst form-fields and span-tracked content —
164
222
  * richtext bodies, `richtext[]` elements, card content fields, direct scalar
165
223
  * references). Only fields with a schema address produce a region — a
@@ -195,7 +253,12 @@ export interface ContentHit {
195
253
  * ```
196
254
  */
197
255
  export interface FieldRegion {
198
- /** Quill schema field path (e.g. `"signature_block"`), not a backend widget name. */
256
+ /**
257
+ * The field's canonical `DocPath` address (`parseDocPath`-routable), not a
258
+ * backend widget name: `main.signature_block` for a main field,
259
+ * `cards.<kind>[<i>].signature_block` for a card field (`cards[<i>].…` when the
260
+ * card's kind is unknown).
261
+ */
199
262
  field: string;
200
263
  /** 0-based page index. */
201
264
  page: number;
@@ -390,11 +453,11 @@ export declare class LiveSession {
390
453
  apply(doc: Document): ChangeSet;
391
454
  render(options?: RenderOptions): RenderResult;
392
455
  /**
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.
456
+ * Schema-field geometry for this compiled session, keyed on the canonical
457
+ * `DocPath` address (`parseDocPath`-routable). A session-level query: no
458
+ * render, no byte artifact. Read it to scroll to / highlight the focused
459
+ * field over a `paint`-ed canvas; the click direction is {@link fieldAt}.
460
+ * Empty for backends that place no schema fields.
398
461
  *
399
462
  * `field` is **not** unique: a content field surfaces its **first
400
463
  * placement** as one {@link FieldRegion} per page that placement touches
@@ -407,9 +470,10 @@ export declare class LiveSession {
407
470
  */
408
471
  regions(): FieldRegion[];
409
472
  /**
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
473
+ * The whole-field highlight boxes for `field` (a canonical `DocPath` address,
474
+ * as {@link regions} keys) one union rect per page, over the field's
475
+ * `span`-bearing content segments (the "highlight the focused field"
476
+ * quantity). Owns the union {@link regions} leaves derived
413
477
  * (span-filter + per-page union), keeping `regions()` the low-level disjoint
414
478
  * truth, so a consumer stops reimplementing it. **Content only** — a field
415
479
  * placed solely as a scalar reference or a bound widget carries no `span`
@@ -420,10 +484,11 @@ export declare class LiveSession {
420
484
  /**
421
485
  * The schema field whose content is under a point on `page` — the forward
422
486
  * (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:
487
+ * document and get back the canonical `DocPath` field address
488
+ * (`parseDocPath`-routable) to focus in the editor, or `undefined` off any
489
+ * field's ink. `x`/`y` are PDF points with a **bottom-left** origin, the
490
+ * same space as {@link FieldRegion.rect} — from a canvas click, invert the
491
+ * overlay transform documented there:
427
492
  * `x = clickPx.x / renderScale`,
428
493
  * `y = pageHeightPt - clickPx.y / renderScale`. Unlike {@link regions},
429
494
  * *every* placement answers, not just the first.
@@ -434,7 +499,10 @@ export declare class LiveSession {
434
499
  * space as {@link fieldAt}; `undefined` off all content ink.
435
500
  */
436
501
  positionAt(page: number, x: number, y: number): ContentHit | undefined;
437
- /** Content position → caret rect — reverse of {@link positionAt}. */
502
+ /**
503
+ * Content position → caret rect — reverse of {@link positionAt}. `field` is a
504
+ * canonical `DocPath` address (`parseDocPath`-routable), as {@link regions} keys.
505
+ */
438
506
  locate(field: string, pos: number): FieldRegion | undefined;
439
507
  /** Page geometry in points (1/72″). Report-only; the painter sizes the canvas. */
440
508
  pageSize(page: number): PageSize;
@@ -483,14 +551,14 @@ declare module '../core/wasm.js' {
483
551
  writer(doc: Document): DocumentWriter;
484
552
  /**
485
553
  * 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
554
+ * {@link Quill.writer}, mirroring core's `quill.reader(&doc)`. Each field is
487
555
  * read by its declared type (a richtext field to markdown, every other type
488
556
  * verbatim) with schema authority, so a name the schema does not declare
489
557
  * throws rather than reading back `undefined`. Holds both handles by
490
558
  * reference and owns neither (nothing to `free()`); ephemeral by convention —
491
559
  * bind, read, discard.
492
560
  */
493
- view(doc: Document): DocumentView;
561
+ reader(doc: Document): DocumentReader;
494
562
  }
495
563
  }
496
564
 
@@ -588,20 +656,20 @@ export declare class CardWriter {
588
656
 
589
657
  /**
590
658
  * 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
659
+ * surface, constructed via {@link Quill.reader} and the read twin of
592
660
  * {@link DocumentWriter}. One `get` reads each field by its declared type: a
593
661
  * richtext field to its markdown projection, a plaintext field to its literal
594
662
  * text, every other type its canonical value verbatim. Holds both handles by
595
663
  * reference and owns neither — nothing to `free()`.
596
664
  *
597
- * The schema authority is the point: unlike the quill-free transport `Document.get`,
665
+ * The schema authority is the point: unlike the quill-free transport `Document.getStored`,
598
666
  * a name the schema does not declare throws `UnknownField` (a typo) rather than
599
667
  * reading back `undefined`, and a content field holding a value that does not
600
668
  * decode throws `FieldRichtextDecode`. A field's markdown lives here, not on the
601
669
  * body-only `Document.getMarkdown`. The body read stays quill-free (a body's type
602
670
  * is a format fact) and never throws.
603
671
  */
604
- export declare class DocumentView {
672
+ export declare class DocumentReader {
605
673
  constructor(quill: Quill, doc: Document);
606
674
  /** The bound document — the instance passed in. */
607
675
  readonly document: Document;
@@ -617,20 +685,20 @@ export declare class DocumentView {
617
685
  /** The main body's markdown — the quill-free body read. Equals `get({})`. */
618
686
  getBody(): string;
619
687
  /**
620
- * A {@link CardView} for the composable card at `index`. Index validity is
688
+ * A {@link CardReader} for the composable card at `index`. Index validity is
621
689
  * checked lazily at read time, so this never throws. The cursor is ephemeral —
622
690
  * a `removeCard`/`addCard` between binding and reading silently retargets it.
623
691
  */
624
- card(index: number): CardView;
692
+ card(index: number): CardReader;
625
693
  }
626
694
 
627
695
  /**
628
696
  * A composable card bound to its `Quill` for interpreted reads, from
629
- * {@link DocumentView.card}. Same verbs as {@link DocumentView}, reading the card
697
+ * {@link DocumentReader.card}. Same verbs as {@link DocumentReader}, reading the card
630
698
  * at its bound index; each read throws `IndexOutOfRange` if that index is out of
631
699
  * range.
632
700
  */
633
- export declare class CardView {
701
+ export declare class CardReader {
634
702
  constructor(quill: Quill, doc: Document, index: number);
635
703
  /** The bound card index. */
636
704
  readonly index: number;
@@ -645,6 +713,6 @@ export declare class CardView {
645
713
  * `IndexOutOfRange` for a bad index.
646
714
  */
647
715
  get(name: string): unknown;
648
- /** This card's body markdown — the card twin of {@link DocumentView.getBody}. */
716
+ /** This card's body markdown — the card twin of {@link DocumentReader.getBody}. */
649
717
  getBody(): string;
650
718
  }
@@ -63,6 +63,10 @@ export { Quill, Document, init };
63
63
  // projection), `importMarkdown`, and the position-mapping pair (`rebase`,
64
64
  // `mapPos`).
65
65
  export { importMarkdown, exportMarkdown, rebase, mapPos } from '../core/wasm.js';
66
+ // The document-model path parser/serializer: `parseDocPath(str) => DocPathSeg[]`
67
+ // and its inverse `formatDocPath`, so a consumer routes on `Diagnostic.path`
68
+ // segments instead of reverse-engineering the grammar.
69
+ export { parseDocPath, formatDocPath } from '../core/wasm.js';
66
70
 
67
71
  // ── The main-card address ───────────────────────────────────────────────────
68
72
  /**
@@ -94,6 +98,49 @@ export function isQuillmarkError(e) {
94
98
  return e instanceof Error && Array.isArray(/** @type {any} */ (e).diagnostics);
95
99
  }
96
100
 
101
+ // ── Open-set discriminant guards ────────────────────────────────────────────
102
+ // `ContentIsland.type` and `ContentMark.type` are OPEN sets: each union carries
103
+ // a residual `{ type: string; … }` arm, so a bare `x.type === 'table'` check
104
+ // never narrows the payload — TS keeps the residual arm live (a `string` can be
105
+ // `'table'`), leaving `props` / the mark payload opaque at every consumer. These
106
+ // are the checked narrowing path: on the true branch the payload's pinned shape
107
+ // is asserted. Only the payload-carrying arms get a guard — an island always
108
+ // carries `props`, a `link` mark carries `url`, an `anchor` mark carries `id`;
109
+ // the bare marks (`strong`/`emph`/`underline`/`strike`/`code`) narrow to
110
+ // nothing. An unrecognized `type` fails every guard and keeps its opaque payload.
111
+
112
+ /**
113
+ * @param {import('../core/wasm.js').ContentIsland} island
114
+ * @returns {island is import('../core/wasm.js').ContentIsland & { type: 'table'; props: import('../core/wasm.js').TableProps }}
115
+ */
116
+ export function isTableIsland(island) {
117
+ return island.type === 'table';
118
+ }
119
+
120
+ /**
121
+ * @param {import('../core/wasm.js').ContentIsland} island
122
+ * @returns {island is import('../core/wasm.js').ContentIsland & { type: 'image'; props: import('../core/wasm.js').ImageProps }}
123
+ */
124
+ export function isImageIsland(island) {
125
+ return island.type === 'image';
126
+ }
127
+
128
+ /**
129
+ * @param {import('../core/wasm.js').ContentMark} mark
130
+ * @returns {mark is import('../core/wasm.js').ContentMark & { type: 'link'; url: string }}
131
+ */
132
+ export function isLinkMark(mark) {
133
+ return mark.type === 'link';
134
+ }
135
+
136
+ /**
137
+ * @param {import('../core/wasm.js').ContentMark} mark
138
+ * @returns {mark is import('../core/wasm.js').ContentMark & { type: 'anchor'; id: string }}
139
+ */
140
+ export function isAnchorMark(mark) {
141
+ return mark.type === 'anchor';
142
+ }
143
+
97
144
  // Backend builds are NEVER statically imported here — that would pull a
98
145
  // multi-MB binary into the eager graph and defeat lazy loading. Each entry is a
99
146
  // DESCRIPTOR: `load` is a thunk returning a dynamic `import()` (a backend's
@@ -749,11 +796,11 @@ Quill.prototype.writer = function writer(doc) {
749
796
  return new DocumentWriter(this, doc);
750
797
  };
751
798
 
752
- // ── Typed-reader sugar: the schema-plane read view ──────────────────────────
753
- // The read twin of the writer above. The transport `Document.get` is schema-free
799
+ // ── Typed-reader sugar: the schema-plane read surface ──────────────────────────
800
+ // The read twin of the writer above. The transport `Document.getStored` is schema-free
754
801
  // — a `Document` cannot say which fields are richtext, so an unknown field name
755
802
  // reads back `undefined` rather than as the typo it is. Binding the quill's
756
- // schema (`_viewGet` takes the handle, like the `commit*` verbs) lets one `get`
803
+ // schema (`_readerGet` takes the handle, like the `commit*` verbs) lets one `get`
757
804
  // interpret by declared type: a richtext field to markdown, a plaintext field to
758
805
  // its literal text, every other type verbatim, and an unknown name throws
759
806
  // `UnknownField`. A field's markdown lives here, not on the body-only
@@ -762,11 +809,11 @@ Quill.prototype.writer = function writer(doc) {
762
809
 
763
810
  /**
764
811
  * A {@link Document} bound to its {@link Quill} for typed reads — the JS twin of
765
- * Rust's `quill.view(&doc)` and the read counterpart of {@link DocumentWriter}.
812
+ * Rust's `quill.reader(&doc)` and the read counterpart of {@link DocumentWriter}.
766
813
  * Reads target the main card; use {@link card} for a composable card. Holds both
767
814
  * handles by reference and owns neither, so there is nothing to `free()`.
768
815
  */
769
- export class DocumentView {
816
+ export class DocumentReader {
770
817
  #quill;
771
818
  #doc;
772
819
  /**
@@ -792,7 +839,7 @@ export class DocumentView {
792
839
  * @returns {unknown}
793
840
  */
794
841
  get(addr) {
795
- return this.#doc._viewGet(this.#quill, addr);
842
+ return this.#doc._readerGet(this.#quill, addr);
796
843
  }
797
844
  /**
798
845
  * The main body's markdown — the quill-free body read (a body's type is a
@@ -800,28 +847,28 @@ export class DocumentView {
800
847
  * @returns {string}
801
848
  */
802
849
  getBody() {
803
- return this.#doc._viewGet(this.#quill, {});
850
+ return this.#doc._readerGet(this.#quill, {});
804
851
  }
805
852
  /**
806
- * A {@link CardView} bound to the composable card at `index`. Index validity
853
+ * A {@link CardReader} bound to the composable card at `index`. Index validity
807
854
  * is checked lazily by the underlying read (it throws `IndexOutOfRange` at read
808
855
  * time), so constructing one never throws. Ephemeral like the writer cursor —
809
856
  * it holds `index`, not the card, so a `removeCard`/`addCard` between binding
810
857
  * and reading silently retargets it.
811
858
  * @param {number} index
812
- * @returns {CardView}
859
+ * @returns {CardReader}
813
860
  */
814
861
  card(index) {
815
- return new CardView(this.#quill, this.#doc, index);
862
+ return new CardReader(this.#quill, this.#doc, index);
816
863
  }
817
864
  }
818
865
 
819
866
  /**
820
867
  * A single composable card bound to its {@link Quill} for typed reads, from
821
- * {@link DocumentView.card}. Same `get` / `getBody` verbs as
822
- * {@link DocumentView}, reading the card at its bound index.
868
+ * {@link DocumentReader.card}. Same `get` / `getBody` verbs as
869
+ * {@link DocumentReader}, reading the card at its bound index.
823
870
  */
824
- export class CardView {
871
+ export class CardReader {
825
872
  #quill;
826
873
  #doc;
827
874
  #index;
@@ -855,30 +902,30 @@ export class CardView {
855
902
  * @returns {unknown}
856
903
  */
857
904
  get(name) {
858
- return this.#doc._viewGet(this.#quill, { card: this.#index, field: name });
905
+ return this.#doc._readerGet(this.#quill, { card: this.#index, field: name });
859
906
  }
860
907
  /**
861
- * This card's body markdown — the card twin of {@link DocumentView.getBody}.
908
+ * This card's body markdown — the card twin of {@link DocumentReader.getBody}.
862
909
  * @returns {string}
863
910
  */
864
911
  getBody() {
865
- return this.#doc._viewGet(this.#quill, { card: this.#index });
912
+ return this.#doc._readerGet(this.#quill, { card: this.#index });
866
913
  }
867
914
  }
868
915
 
869
- // ── `quill.view(doc)` — the schema-plane read front door ─────────────────────
916
+ // ── `quill.reader(doc)` — the schema-plane read front door ─────────────────────
870
917
  // The read twin of `quill.writer(doc)`, patched onto the same re-exported `Quill`
871
918
  // prototype (the `Quill === CoreQuill` identity invariant holds — this only adds
872
- // a method constructing the pure-JS view, which owns no WASM handle).
919
+ // a method constructing the pure-JS reader, which owns no WASM handle).
873
920
  /**
874
- * A {@link DocumentView} binding this quill's schema to `doc` for interpreted
875
- * reads — the read front door, mirroring core's `quill.view(&doc)`. The returned
876
- * view holds both handles by reference and owns neither, so there is nothing to
921
+ * A {@link DocumentReader} binding this quill's schema to `doc` for interpreted
922
+ * reads — the read front door, mirroring core's `quill.reader(&doc)`. The returned
923
+ * reader holds both handles by reference and owns neither, so there is nothing to
877
924
  * `free()`. Ephemeral by convention: bind, read, discard.
878
925
  * @this {Quill}
879
926
  * @param {Document} doc the document to read, held by reference (not owned)
880
- * @returns {DocumentView}
927
+ * @returns {DocumentReader}
881
928
  */
882
- Quill.prototype.view = function view(doc) {
883
- return new DocumentView(this, doc);
929
+ Quill.prototype.reader = function reader(doc) {
930
+ return new DocumentReader(this, doc);
884
931
  };