@quillmark/wasm 0.94.0 → 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.
@@ -58,12 +58,24 @@
58
58
  // re-export keeps the identity: the exported `Quill` IS the core class.
59
59
  import { Quill, Document, init } from '../core/wasm.js';
60
60
  export { Quill, Document, init };
61
- // The document-free corpus codec — re-exported verbatim from the core build so
61
+ // The document-free content codec — re-exported verbatim from the core build so
62
62
  // the runtime subpath exposes `exportMarkdown(body)` (the on-demand markdown
63
- // projection that replaces the eager `bodyMarkdown`), `importMarkdown`, and the
64
- // position-mapping pair (`rebase`, `mapPos`).
63
+ // projection), `importMarkdown`, and the position-mapping pair (`rebase`,
64
+ // `mapPos`).
65
65
  export { importMarkdown, exportMarkdown, rebase, mapPos } from '../core/wasm.js';
66
66
 
67
+ // ── The main-card address ───────────────────────────────────────────────────
68
+ /**
69
+ * The main card's address — the default target of the card-scoped verbs
70
+ * (`storeFields` / `storeExt` / `commitFields` / …). A named, `CardAddr`-typed
71
+ * alias for the empty address `{}`, so a main-card write names its target:
72
+ * `doc.storeFields(MAIN_CARD_ADDR, fields)`. It IS `{}` (frozen), a pure alias —
73
+ * `{}` and `undefined` stay equally valid. Card axis only: a card selector,
74
+ * never a field address.
75
+ * @type {import('../core/wasm.js').CardAddr}
76
+ */
77
+ export const MAIN_CARD_ADDR = Object.freeze({});
78
+
67
79
  /**
68
80
  * Narrow an unknown caught value to a `QuillmarkError` — the error every
69
81
  * fallible method in this package throws: a real `Error` with a non-empty
@@ -479,7 +491,7 @@ export class LiveSession {
479
491
  * @param {number} page
480
492
  * @param {number} x
481
493
  * @param {number} y
482
- * @returns {import('./runtime.d.ts').CorpusHit | undefined}
494
+ * @returns {import('./runtime.d.ts').ContentHit | undefined}
483
495
  */
484
496
  positionAt(page, x, y) {
485
497
  return this.#inner.positionAt(page, x, y);
@@ -531,7 +543,7 @@ export class LiveSession {
531
543
  // typed-committed (coerced to canonical form, mismatch throws now), and a name
532
544
  // the schema does not declare throws `UnknownField` rather than falling to the
533
545
  // opaque store — on the typed path an undeclared name is a typo. Opaque storage
534
- // stays available through the raw `Document.setField` / `setCardField` verbs.
546
+ // stays available through the raw addressed `Document.storeField` verb.
535
547
 
536
548
  /**
537
549
  * A {@link Document} bound to its {@link Quill} for typed writes — the JS twin
@@ -556,54 +568,68 @@ export class DocumentWriter {
556
568
  }
557
569
  /**
558
570
  * Typed-commit one main-card field (strict coerce, mismatch throws now).
559
- * Throws `UnknownField` for a name the schema does not declare. See
560
- * `Document.commitField`.
571
+ * Throws `UnknownField` for a name the schema does not declare.
561
572
  * @param {string} name
562
573
  * @param {unknown} value
563
574
  * @returns {void}
564
575
  */
565
576
  set(name, value) {
566
- return this.#doc.commitField(this.#quill, name, value);
577
+ return this.#doc._commitField(this.#quill, name, value);
567
578
  }
568
579
  /**
569
580
  * Typed-commit several main-card fields atomically — nothing is applied on
570
581
  * error (throws a per-field diagnostic bundle, including an `UnknownField`
571
- * for each undeclared name). See `Document.commitFields`.
582
+ * for each undeclared name).
572
583
  * @param {Record<string, unknown>} fields
573
584
  * @returns {void}
574
585
  */
575
586
  setAll(fields) {
576
- return this.#doc.commitFields(this.#quill, fields);
587
+ return this.#doc._commitFields(this.#quill, MAIN_CARD_ADDR, fields);
577
588
  }
578
589
  /**
579
590
  * Set the main body from markdown (edit semantics: surviving anchors rebase),
580
591
  * discarding the text delta — the receipt-free body write. Call
581
- * `doc.revise({}, md)` for the {@link Delta} receipt (the corpus-lane
582
- * spelling). Markdown in, no corpus or receipt in sight.
592
+ * `doc.revise({}, md)` for the {@link Delta} receipt.
583
593
  * @param {string} markdown
584
594
  * @returns {void}
585
595
  */
586
596
  setBody(markdown) {
587
597
  this.#doc.revise({}, markdown);
588
598
  }
599
+ /**
600
+ * Revise the richtext main-card field `name` from markdown — typed *and*
601
+ * anchor-preserving. Surviving anchors rebase, then the diffed result is
602
+ * schema-conformed (`richtext(inline)` rejects a multi-block result). Throws
603
+ * `UnknownField` for a name the schema does not declare. Returns the text
604
+ * {@link Delta}.
605
+ * @param {string} name
606
+ * @param {string} markdown
607
+ * @returns {import('../core/wasm.js').Delta}
608
+ */
609
+ reviseField(name, markdown) {
610
+ return this.#doc._reviseField(this.#quill, name, markdown);
611
+ }
589
612
  /**
590
613
  * Build a composable card of `kind`, typed-commit `fields` onto it, set its
591
- * body from optional markdown, and append it — the fused `makeCard` + typed
592
- * commit + `pushCard`. Transactional: the card is committed in full before it
593
- * joins the document, so a rejected field (throws a per-field diagnostic
594
- * bundle, `UnknownField` per undeclared name) or an invalid kind/body leaves
595
- * the document untouched. See `Document.addCard`.
614
+ * body from optional markdown, and place it — the fused `makeCard` + typed
615
+ * commit + insertion. `at` picks the position: omitted appends, a number
616
+ * inserts at that index (`0..=cardCount`), so a positioned typed insert is one
617
+ * atomic call rather than `addCard` + `moveCard`. Transactional: the card is
618
+ * committed in full before it joins the document, so a rejected field (throws
619
+ * a per-field diagnostic bundle, `UnknownField` per undeclared name) or an
620
+ * invalid kind/body/position leaves the document untouched.
596
621
  * @param {string} kind
597
622
  * @param {Record<string, unknown>} [fields]
598
623
  * @param {string} [body]
624
+ * @param {number} [at] insertion index; appends when omitted
599
625
  * @returns {void}
600
626
  */
601
- addCard(kind, fields, body) {
602
- return this.#doc.addCard(this.#quill, kind, fields, body);
627
+ addCard(kind, fields, body, at) {
628
+ return this.#doc._addCard(this.#quill, kind, fields, body, at);
603
629
  }
604
630
  /**
605
631
  * Remove the composable card at `index`, returning it (or `undefined` if the
606
- * index is out of range) — the tier-1 spelling of `Document.removeCard`.
632
+ * index is out of range) — the writer spelling of `Document.removeCard`.
607
633
  * @param {number} index
608
634
  * @returns {import('../core/wasm.js').Card | undefined}
609
635
  */
@@ -651,25 +677,34 @@ export class CardWriter {
651
677
  return this.#index;
652
678
  }
653
679
  /**
654
- * Typed-commit one field on this card, per `Document.commitCardField`.
655
- * Throws `UnknownField` for an undeclared name and `IndexOutOfRange` if the
656
- * bound index is out of range.
680
+ * The bound card's `$kind` (empty string when it carries none), read through
681
+ * the document mirrors core `CardWriter::kind()`. Ephemeral like the cursor
682
+ * itself: throws `IndexOutOfRange` if the bound index is out of range.
683
+ * @returns {string}
684
+ */
685
+ get kind() {
686
+ return this.#doc.card(this.#index).kind;
687
+ }
688
+ /**
689
+ * Typed-commit one field on this card, addressed at `{ card, field }`. Throws
690
+ * `UnknownField` for an undeclared name and `IndexOutOfRange` if the bound
691
+ * index is out of range.
657
692
  * @param {string} name
658
693
  * @param {unknown} value
659
694
  * @returns {void}
660
695
  */
661
696
  set(name, value) {
662
- return this.#doc.commitCardField(this.#quill, this.#index, name, value);
697
+ return this.#doc._commitField(this.#quill, { card: this.#index, field: name }, value);
663
698
  }
664
699
  /**
665
- * Typed-commit several fields on this card atomically, per
666
- * `Document.commitCardFields`. Throws a per-field diagnostic bundle on error
667
- * and `IndexOutOfRange` if the bound index is out of range.
700
+ * Typed-commit several fields on this card atomically, addressed at
701
+ * `{ card }`. Throws a per-field diagnostic bundle on error and
702
+ * `IndexOutOfRange` if the bound index is out of range.
668
703
  * @param {Record<string, unknown>} fields
669
704
  * @returns {void}
670
705
  */
671
706
  setAll(fields) {
672
- return this.#doc.commitCardFields(this.#quill, this.#index, fields);
707
+ return this.#doc._commitFields(this.#quill, { card: this.#index }, fields);
673
708
  }
674
709
  /**
675
710
  * Set this card's body from markdown (edit semantics), discarding the delta —
@@ -680,10 +715,22 @@ export class CardWriter {
680
715
  setBody(markdown) {
681
716
  this.#doc.revise({ card: this.#index }, markdown);
682
717
  }
718
+ /**
719
+ * Revise the richtext field `name` on this card from markdown — typed *and*
720
+ * anchor-preserving; the card twin of {@link DocumentWriter.reviseField}.
721
+ * Throws `UnknownField` for an undeclared name and `IndexOutOfRange` if the
722
+ * bound index is out of range. Returns the text {@link Delta}.
723
+ * @param {string} name
724
+ * @param {string} markdown
725
+ * @returns {import('../core/wasm.js').Delta}
726
+ */
727
+ reviseField(name, markdown) {
728
+ return this.#doc._reviseField(this.#quill, { card: this.#index, field: name }, markdown);
729
+ }
683
730
  }
684
731
 
685
732
  // ── `quill.writer(doc)` — the typed front door ──────────────────────────────
686
- // The tier-1 default: bind the quill's schema to a document and issue bare
733
+ // The schema-bound writer: bind the quill's schema to a document and issue bare
687
734
  // typed writes. Mirrors core's `quill.writer(&mut doc)` — the schema grants the
688
735
  // typing, so the quill (not the document) is the factory. Patched onto the
689
736
  // re-exported `Quill` prototype rather than wrapped: `Quill === CoreQuill`
@@ -701,3 +748,137 @@ export class CardWriter {
701
748
  Quill.prototype.writer = function writer(doc) {
702
749
  return new DocumentWriter(this, doc);
703
750
  };
751
+
752
+ // ── Typed-reader sugar: the schema-plane read view ──────────────────────────
753
+ // The read twin of the writer above. The transport `Document.get` is schema-free
754
+ // — a `Document` cannot say which fields are richtext, so an unknown field name
755
+ // 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`
757
+ // interpret by declared type: a richtext field to markdown, a plaintext field to
758
+ // its literal text, every other type verbatim, and an unknown name throws
759
+ // `UnknownField`. A field's markdown lives here, not on the body-only
760
+ // `getMarkdown`. Like the writer classes these hold the caller's handles by
761
+ // reference, own no WASM object, and have nothing to `free()`.
762
+
763
+ /**
764
+ * 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}.
766
+ * Reads target the main card; use {@link card} for a composable card. Holds both
767
+ * handles by reference and owns neither, so there is nothing to `free()`.
768
+ */
769
+ export class DocumentView {
770
+ #quill;
771
+ #doc;
772
+ /**
773
+ * @param {Quill} quill the schema source for interpreted reads
774
+ * @param {Document} doc the document to read, held by reference (not owned)
775
+ */
776
+ constructor(quill, doc) {
777
+ this.#quill = quill;
778
+ this.#doc = doc;
779
+ }
780
+ /** The bound document — the same instance passed in. */
781
+ get document() {
782
+ return this.#doc;
783
+ }
784
+ /**
785
+ * Read the value at `addr`, interpreted by its declared type: a richtext field
786
+ * to markdown, every other type verbatim. A bare string is `Addr` shorthand
787
+ * for `{ field }`; an absent `addr.field` reads the body markdown. `undefined`
788
+ * for an absent field; throws `UnknownField` for a name the schema does not
789
+ * declare, `FieldRichtextDecode` for a richtext field holding an undecodable
790
+ * value, and `IndexOutOfRange` for a bad `addr.card`.
791
+ * @param {import('../core/wasm.js').Addr | string} addr
792
+ * @returns {unknown}
793
+ */
794
+ get(addr) {
795
+ return this.#doc._viewGet(this.#quill, addr);
796
+ }
797
+ /**
798
+ * The main body's markdown — the quill-free body read (a body's type is a
799
+ * format fact, not a schema fact). Equivalent to `get({})`.
800
+ * @returns {string}
801
+ */
802
+ getBody() {
803
+ return this.#doc._viewGet(this.#quill, {});
804
+ }
805
+ /**
806
+ * A {@link CardView} bound to the composable card at `index`. Index validity
807
+ * is checked lazily by the underlying read (it throws `IndexOutOfRange` at read
808
+ * time), so constructing one never throws. Ephemeral like the writer cursor —
809
+ * it holds `index`, not the card, so a `removeCard`/`addCard` between binding
810
+ * and reading silently retargets it.
811
+ * @param {number} index
812
+ * @returns {CardView}
813
+ */
814
+ card(index) {
815
+ return new CardView(this.#quill, this.#doc, index);
816
+ }
817
+ }
818
+
819
+ /**
820
+ * 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.
823
+ */
824
+ export class CardView {
825
+ #quill;
826
+ #doc;
827
+ #index;
828
+ /**
829
+ * @param {Quill} quill the schema source
830
+ * @param {Document} doc the document to read, held by reference (not owned)
831
+ * @param {number} index the composable card's index
832
+ */
833
+ constructor(quill, doc, index) {
834
+ this.#quill = quill;
835
+ this.#doc = doc;
836
+ this.#index = index;
837
+ }
838
+ /** The bound card index. */
839
+ get index() {
840
+ return this.#index;
841
+ }
842
+ /**
843
+ * The bound card's `$kind` (empty string when it carries none), read through
844
+ * the document. Throws `IndexOutOfRange` if the bound index is out of range.
845
+ * @returns {string}
846
+ */
847
+ get kind() {
848
+ return this.#doc.card(this.#index).kind;
849
+ }
850
+ /**
851
+ * Read the field `name` on this card, interpreted by its declared type,
852
+ * addressed at `{ card, field }`. `undefined` when absent; throws
853
+ * `UnknownField` for an undeclared name and `IndexOutOfRange` for a bad index.
854
+ * @param {string} name
855
+ * @returns {unknown}
856
+ */
857
+ get(name) {
858
+ return this.#doc._viewGet(this.#quill, { card: this.#index, field: name });
859
+ }
860
+ /**
861
+ * This card's body markdown — the card twin of {@link DocumentView.getBody}.
862
+ * @returns {string}
863
+ */
864
+ getBody() {
865
+ return this.#doc._viewGet(this.#quill, { card: this.#index });
866
+ }
867
+ }
868
+
869
+ // ── `quill.view(doc)` — the schema-plane read front door ─────────────────────
870
+ // The read twin of `quill.writer(doc)`, patched onto the same re-exported `Quill`
871
+ // prototype (the `Quill === CoreQuill` identity invariant holds — this only adds
872
+ // a method constructing the pure-JS view, which owns no WASM handle).
873
+ /**
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
877
+ * `free()`. Ephemeral by convention: bind, read, discard.
878
+ * @this {Quill}
879
+ * @param {Document} doc the document to read, held by reference (not owned)
880
+ * @returns {DocumentView}
881
+ */
882
+ Quill.prototype.view = function view(doc) {
883
+ return new DocumentView(this, doc);
884
+ };