1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
//! `Content`: the canonical content model for Quillmark.
//!
//! One [`Content`] per content field: a single text sequence carrying line
//! attributes, anchored marks, and embedded islands, over one coordinate space
//! of Unicode scalar values. Markdown is demoted to a *projection* (import
//! ([`import::from_markdown`]) and export ([`export::to_markdown`]) codecs) so
//! every edit is a splice and all structure moves with it.
//!
//! `core`, `quillmark`, and both backends (`typst`, `pdfform`) consume this
//! crate: the seam carries content JSON, storage embeds it structurally (see
//! `prose/canon/DOCUMENT_STORAGE.md`), and the content edit surface
//! (`delta`, `ops`) drives per-field splices.
//!
//! ## Layout
//!
//! - [`model`]: the [`Content`] type, the mark set, normalization (the three
//! Spike-A rules), and invariants. The freeze.
//! - [`serial`]: canonical, byte-deterministic JSON. One encoding for the seam
//! and for storage.
//! - [`island`]: [`KnownIslandType`], the closed dispatch authority for island
//! types; adding a type is one variant and the compiler enforces every arm.
//! - [`import`]: markdown → content (normalize → pulldown → content).
//! - [`export`]: content → markdown, per island loss class.
//! - [`delta`], the per-field edit surface: a text-splice change set
//! (`retain`/`insert`/`delete`, CodeMirror `ChangeSet` semantics) plus the
//! cold-parse + content-diff stale-text writer with a block-move detector. The
//! text-splice channel is the positional core; mark and line-attribute edits
//! are separate op channels, not op attributes; see [`delta`].
//! - [`ops`], mark and line op channels:
//! [`Content::apply_text_delta`], [`apply_mark_ops`](Content::apply_mark_ops),
//! [`apply_line_ops`](Content::apply_line_ops).
//! - [`normalize`]: the markdown-string input primitive (line endings, bidi
//! strip, HTML-comment fence repair), applied at the import boundary.
//! - [`usv`]: USV → UTF-8 byte-offset conversion for slicing the content.
pub use ;
pub use ;
pub use ;
pub use KnownIslandType;
pub use ;
pub use normalize_markdown;
pub use ;
pub use ParseError;
/// Maximum container nesting depth the markdown codecs accept before erroring.
/// The import guard ([`import::from_markdown`]) and the typst backend's markup
/// converter share this one limit (the backend re-exports it via
/// `quillmark_core::error::MAX_NESTING_DEPTH`), so a document that imports also
/// renders.
pub const MAX_NESTING_DEPTH: usize = 100;
/// Maximum nesting depth of an opaque JSON payload (an island's `props`, an
/// unknown line/container/mark's `attrs`) measured in container levels from the
/// bag itself.
///
/// The payload axis of [`MAX_NESTING_DEPTH`]: `is_value_key_sorted`,
/// `sort_keys_owned`, and `serde_json::Value`'s own `Drop` each recurse one
/// frame per level, so an unbounded bag overflows the stack, on wasm32, an
/// unrecoverable trap rather than a catchable error. The bag is refused at the
/// decode boundary, before it is cloned out of the wire, and
/// [`Content::validate`](model::Content::validate) restates it as an invariant
/// for the hand-built content that never went through a decoder.
///
/// 128 is what `serde_json::from_str` already enforces, so nothing a stored blob
/// can carry is refused. The string lane counts from the document root rather
/// than the bag, so it admits ~3 levels fewer; both bound the recursion, and the
/// per-bag reading is the one the recursive consumers actually walk.
pub const MAX_JSON_DEPTH: usize = 128;