@quillmark/svelte 0.0.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.
Files changed (143) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +58 -0
  3. package/README.md +241 -0
  4. package/THEMING.md +153 -0
  5. package/dist/core/address.d.ts +56 -0
  6. package/dist/core/address.js +95 -0
  7. package/dist/core/bloom.d.ts +37 -0
  8. package/dist/core/bloom.js +116 -0
  9. package/dist/core/codec/anchor.d.ts +19 -0
  10. package/dist/core/codec/anchor.js +36 -0
  11. package/dist/core/codec/code.d.ts +12 -0
  12. package/dist/core/codec/code.js +118 -0
  13. package/dist/core/codec/decode.d.ts +24 -0
  14. package/dist/core/codec/decode.js +270 -0
  15. package/dist/core/codec/encode.d.ts +71 -0
  16. package/dist/core/codec/encode.js +480 -0
  17. package/dist/core/codec/field.d.ts +171 -0
  18. package/dist/core/codec/field.js +493 -0
  19. package/dist/core/codec/index.d.ts +22 -0
  20. package/dist/core/codec/index.js +43 -0
  21. package/dist/core/codec/inputrules.d.ts +7 -0
  22. package/dist/core/codec/inputrules.js +168 -0
  23. package/dist/core/codec/islands.d.ts +36 -0
  24. package/dist/core/codec/islands.js +82 -0
  25. package/dist/core/codec/keymap.d.ts +13 -0
  26. package/dist/core/codec/keymap.js +29 -0
  27. package/dist/core/codec/lists.d.ts +22 -0
  28. package/dist/core/codec/lists.js +104 -0
  29. package/dist/core/codec/marks.d.ts +37 -0
  30. package/dist/core/codec/marks.js +74 -0
  31. package/dist/core/codec/positions.d.ts +17 -0
  32. package/dist/core/codec/positions.js +108 -0
  33. package/dist/core/codec/prose.css +458 -0
  34. package/dist/core/codec/reconcile.d.ts +26 -0
  35. package/dist/core/codec/reconcile.js +64 -0
  36. package/dist/core/codec/schema.d.ts +13 -0
  37. package/dist/core/codec/schema.js +155 -0
  38. package/dist/core/codec/slash.d.ts +79 -0
  39. package/dist/core/codec/slash.js +0 -0
  40. package/dist/core/codec/table-view.d.ts +50 -0
  41. package/dist/core/codec/table-view.js +928 -0
  42. package/dist/core/codec/table.d.ts +85 -0
  43. package/dist/core/codec/table.js +224 -0
  44. package/dist/core/errors.d.ts +73 -0
  45. package/dist/core/errors.js +24 -0
  46. package/dist/core/index.d.ts +5 -0
  47. package/dist/core/index.js +19 -0
  48. package/dist/core/lifecycle.d.ts +7 -0
  49. package/dist/core/lifecycle.js +21 -0
  50. package/dist/core/rebind.svelte.d.ts +18 -0
  51. package/dist/core/rebind.svelte.js +44 -0
  52. package/dist/core/teardown.d.ts +20 -0
  53. package/dist/core/teardown.js +37 -0
  54. package/dist/core/theme.css +276 -0
  55. package/dist/index.d.ts +1 -0
  56. package/dist/index.js +9 -0
  57. package/dist/preset/index.css +10 -0
  58. package/dist/preset/recipes.css +188 -0
  59. package/dist/preset/scale.css +97 -0
  60. package/dist/preview/Preview.svelte +118 -0
  61. package/dist/preview/Preview.svelte.d.ts +39 -0
  62. package/dist/preview/bridge.d.ts +13 -0
  63. package/dist/preview/bridge.js +88 -0
  64. package/dist/preview/controller.d.ts +40 -0
  65. package/dist/preview/controller.js +141 -0
  66. package/dist/preview/geometry.d.ts +37 -0
  67. package/dist/preview/geometry.js +45 -0
  68. package/dist/preview/index.d.ts +5 -0
  69. package/dist/preview/index.js +14 -0
  70. package/dist/preview/overlay.d.ts +10 -0
  71. package/dist/preview/overlay.js +82 -0
  72. package/dist/preview/paint.d.ts +17 -0
  73. package/dist/preview/paint.js +262 -0
  74. package/dist/preview/strings.d.ts +14 -0
  75. package/dist/preview/strings.js +22 -0
  76. package/dist/visual/ArrayField.svelte +362 -0
  77. package/dist/visual/ArrayField.svelte.d.ts +24 -0
  78. package/dist/visual/BooleanField.svelte +88 -0
  79. package/dist/visual/BooleanField.svelte.d.ts +20 -0
  80. package/dist/visual/Card.svelte +849 -0
  81. package/dist/visual/Card.svelte.d.ts +43 -0
  82. package/dist/visual/CardControls.svelte +92 -0
  83. package/dist/visual/CardControls.svelte.d.ts +11 -0
  84. package/dist/visual/DateField.svelte +203 -0
  85. package/dist/visual/DateField.svelte.d.ts +28 -0
  86. package/dist/visual/DiagnosticList.svelte +50 -0
  87. package/dist/visual/DiagnosticList.svelte.d.ts +7 -0
  88. package/dist/visual/EnumField.svelte +169 -0
  89. package/dist/visual/EnumField.svelte.d.ts +25 -0
  90. package/dist/visual/Field.svelte +270 -0
  91. package/dist/visual/Field.svelte.d.ts +37 -0
  92. package/dist/visual/FieldHint.svelte +185 -0
  93. package/dist/visual/FieldHint.svelte.d.ts +13 -0
  94. package/dist/visual/FieldLabel.svelte +141 -0
  95. package/dist/visual/FieldLabel.svelte.d.ts +19 -0
  96. package/dist/visual/FormatPopover.svelte +396 -0
  97. package/dist/visual/FormatPopover.svelte.d.ts +9 -0
  98. package/dist/visual/NumberField.svelte +72 -0
  99. package/dist/visual/NumberField.svelte.d.ts +19 -0
  100. package/dist/visual/ObjectField.svelte +131 -0
  101. package/dist/visual/ObjectField.svelte.d.ts +17 -0
  102. package/dist/visual/ProseArrayElement.svelte +98 -0
  103. package/dist/visual/ProseArrayElement.svelte.d.ts +19 -0
  104. package/dist/visual/ProseField.svelte +213 -0
  105. package/dist/visual/ProseField.svelte.d.ts +50 -0
  106. package/dist/visual/SlashMenu.svelte +126 -0
  107. package/dist/visual/SlashMenu.svelte.d.ts +14 -0
  108. package/dist/visual/TextField.svelte +63 -0
  109. package/dist/visual/TextField.svelte.d.ts +23 -0
  110. package/dist/visual/TipsCard.svelte +191 -0
  111. package/dist/visual/TipsCard.svelte.d.ts +13 -0
  112. package/dist/visual/VisualEditor.svelte +96 -0
  113. package/dist/visual/VisualEditor.svelte.d.ts +14 -0
  114. package/dist/visual/VisualEditorInner.svelte +1032 -0
  115. package/dist/visual/VisualEditorInner.svelte.d.ts +17 -0
  116. package/dist/visual/controls.css +272 -0
  117. package/dist/visual/diagnostics.d.ts +52 -0
  118. package/dist/visual/diagnostics.js +83 -0
  119. package/dist/visual/domid.d.ts +21 -0
  120. package/dist/visual/domid.js +36 -0
  121. package/dist/visual/ext.d.ts +19 -0
  122. package/dist/visual/ext.js +29 -0
  123. package/dist/visual/icons/Icon.svelte +39 -0
  124. package/dist/visual/icons/Icon.svelte.d.ts +12 -0
  125. package/dist/visual/icons/nodes.d.ts +103 -0
  126. package/dist/visual/icons/nodes.js +55 -0
  127. package/dist/visual/index.d.ts +9 -0
  128. package/dist/visual/index.js +24 -0
  129. package/dist/visual/motion.d.ts +18 -0
  130. package/dist/visual/motion.js +53 -0
  131. package/dist/visual/props.d.ts +77 -0
  132. package/dist/visual/props.js +1 -0
  133. package/dist/visual/signals.d.ts +59 -0
  134. package/dist/visual/signals.js +1 -0
  135. package/dist/visual/strings.d.ts +123 -0
  136. package/dist/visual/strings.js +80 -0
  137. package/dist/visual/structure.d.ts +224 -0
  138. package/dist/visual/structure.js +270 -0
  139. package/dist/visual/synced.svelte.d.ts +15 -0
  140. package/dist/visual/synced.svelte.js +34 -0
  141. package/dist/visual/tips.d.ts +24 -0
  142. package/dist/visual/tips.js +50 -0
  143. package/package.json +102 -0
@@ -0,0 +1,270 @@
1
+ // Decode: content → PM, a pure function (CODEC §Decode). Fold the flat lines into
2
+ // the tree: group a `continues` run into one block (para hard breaks → `hard_break`
3
+ // nodes; a code fence's lines → one `code_block`), nest by shared `containers`
4
+ // prefix (`list_item`/`quote` → lists/blockquote), select the block node by
5
+ // `kind`, apply marks over their `[start,end)` USV ranges (PM splits inline nodes
6
+ // at mark boundaries), and lower island slots to leaf nodes (inline in a `para`
7
+ // line, block on an `island` line). Anchors are NOT applied here; they are
8
+ // decorations (field.ts). Positions throughout are USV; `Array.from` iterates by
9
+ // code point so an astral char is one unit, never a surrogate half.
10
+ import { DOMSerializer } from 'prosemirror-model';
11
+ import { ISLAND_SLOT } from './islands.js';
12
+ import { descriptorOf, markKey, pmMarkFromContent } from './marks.js';
13
+ import { isInlineSchema } from './schema.js';
14
+ import { isAnchorMark, isCodeLine, isHeadingLine, isListItemContainer } from '@quillmark/wasm';
15
+ /** Code points of `s` (USV units): the iteration granularity the content speaks. */
16
+ export function codePoints(s) {
17
+ return Array.from(s);
18
+ }
19
+ /** USV length of `s` (code points, not UTF-16 units). */
20
+ export function usvLength(s) {
21
+ let n = 0;
22
+ for (const _ of s)
23
+ n++;
24
+ return n;
25
+ }
26
+ /**
27
+ * A `Content` as READ-ONLY DOM: decode under `schema`, then the nodes' own `toDOM`.
28
+ * The rendering half of the codec's job with no editing attached (no PM view, no
29
+ * plugins, no `contenteditable`) for chrome that must show content in the same
30
+ * mark vocabulary a leaf edits it in (the tips card). A second renderer over the
31
+ * same content would drift from `decode`; this cannot.
32
+ *
33
+ * `DOMSerializer.fromSchema` memoizes on the schema, so repeat calls build no
34
+ * serializer.
35
+ */
36
+ export function renderContent(rt, schema, opts = {}) {
37
+ return DOMSerializer.fromSchema(schema).serializeFragment(decode(rt, schema, opts).content);
38
+ }
39
+ /** Decode a `Content` to a PM document under `schema`. */
40
+ export function decode(rt, schema, opts = {}) {
41
+ const lineTexts = rt.text.split('\n');
42
+ // Per-line USV start: line i begins after all earlier lines and their `\n`s.
43
+ const starts = [];
44
+ let acc = 0;
45
+ for (let i = 0; i < lineTexts.length; i++) {
46
+ starts.push(acc);
47
+ acc += usvLength(lineTexts[i]) + 1; // +1 for the `\n` boundary
48
+ }
49
+ const marks = opts.plaintext ? [] : rt.marks.filter((m) => !isAnchorMark(m));
50
+ const cursor = { i: 0, rt };
51
+ if (isInlineSchema(schema)) {
52
+ return decodeInline(rt, schema, lineTexts, starts, marks, cursor);
53
+ }
54
+ // Build leaves (fold `continues` runs), then nest by container prefix.
55
+ const leaves = [];
56
+ for (let i = 0; i < rt.lines.length; i++) {
57
+ const line = rt.lines[i];
58
+ const seg = { text: lineTexts[i] ?? '', startUSV: starts[i] };
59
+ if (line.continues && leaves.length > 0) {
60
+ leaves[leaves.length - 1].segments.push(seg);
61
+ }
62
+ else {
63
+ leaves.push({ line, containers: line.containers, segments: [seg] });
64
+ }
65
+ }
66
+ const blocks = groupBlocks(schema, leaves, 0, marks, cursor);
67
+ return schema.nodes.doc.create(null, blocks.length ? blocks : schema.nodes.paragraph.create());
68
+ }
69
+ /** Inline / plaintext decode: one paragraph, containers and islands stripped. */
70
+ function decodeInline(rt, schema, lineTexts, starts, marks, cursor) {
71
+ // An inline field is single-line; join any stray lines with a space (no
72
+ // hard_break node exists in this schema). Islands are not representable inline:
73
+ // the slot char is dropped and its entry skipped.
74
+ const inline = [];
75
+ for (let i = 0; i < lineTexts.length; i++) {
76
+ if (i > 0)
77
+ inline.push(schema.text(' '));
78
+ inline.push(...buildInline(schema, lineTexts[i], starts[i], marks, cursor, true));
79
+ }
80
+ const para = schema.nodes.paragraph.create(null, inline);
81
+ return schema.nodes.doc.create(null, para);
82
+ }
83
+ /** Nest a run of leaves that share a `depth`-length container prefix into blocks. */
84
+ function groupBlocks(schema, leaves, depth, marks, cursor) {
85
+ const out = [];
86
+ let i = 0;
87
+ while (i < leaves.length) {
88
+ const path = leaves[i].containers;
89
+ if (path.length <= depth) {
90
+ out.push(makeLeaf(schema, leaves[i], marks, cursor));
91
+ i++;
92
+ continue;
93
+ }
94
+ const here = path[depth];
95
+ // `ContentContainer` is an OPEN set: a bare `here.container === 'x'` does not
96
+ // narrow, so the two arms this schema builds are claimed by the boundary's
97
+ // guard and a literal check, and everything else is an unknown the inert
98
+ // wrapper carries (CODEC §Open sets); no branch degrades silently.
99
+ if (isListItemContainer(here)) {
100
+ // Gather the maximal run of sibling `list_item` leaves at this depth
101
+ // (same ordered/start), then split it into items by `ordinal`.
102
+ const { ordered, start } = here;
103
+ let j = i + 1;
104
+ let prevOrd = here.ordinal;
105
+ while (j < leaves.length) {
106
+ const c = atDepth(leaves[j], depth);
107
+ if (!c || !isListItemContainer(c) || c.ordered !== ordered || c.start !== start)
108
+ break;
109
+ // An ordinal reset is an ADJACENT SIBLING list (content preserves the
110
+ // shape): merging it here would re-encode `[0,1,0]` as `[0,1,2]`,
111
+ // breaking the decode round-trip on the first edit.
112
+ if (c.ordinal < prevOrd)
113
+ break;
114
+ prevOrd = c.ordinal;
115
+ j++;
116
+ }
117
+ const run = leaves.slice(i, j);
118
+ const items = [];
119
+ let k = 0;
120
+ while (k < run.length) {
121
+ const ord = ordinalAt(run[k], depth);
122
+ let l = k + 1;
123
+ while (l < run.length && ordinalAt(run[l], depth) === ord)
124
+ l++;
125
+ items.push(schema.nodes.list_item.create(null, groupBlocks(schema, run.slice(k, l), depth + 1, marks, cursor)));
126
+ k = l;
127
+ }
128
+ const listType = ordered ? schema.nodes.ordered_list : schema.nodes.bullet_list;
129
+ out.push(listType.create(ordered ? { start } : null, items));
130
+ i = j;
131
+ continue;
132
+ }
133
+ // quote and every unknown container share one shape: a wrapper over the run
134
+ // of leaves carrying the IDENTICAL container here (identity by `containerKey`,
135
+ // so two adjacent unknowns differing only in `attrs` stay two wrappers).
136
+ const key = containerKey(here);
137
+ let j = i + 1;
138
+ while (j < leaves.length) {
139
+ const c = atDepth(leaves[j], depth);
140
+ if (!c || containerKey(c) !== key)
141
+ break;
142
+ j++;
143
+ }
144
+ const inner = groupBlocks(schema, leaves.slice(i, j), depth + 1, marks, cursor);
145
+ out.push(here.container === 'quote'
146
+ ? schema.nodes.blockquote.create(null, inner)
147
+ : schema.nodes.unknown_container.create({ container: here.container, attrs: 'attrs' in here ? here.attrs : null }, inner));
148
+ i = j;
149
+ }
150
+ return out;
151
+ }
152
+ function atDepth(leaf, depth) {
153
+ return leaf.containers[depth];
154
+ }
155
+ /** The `ordinal` of a leaf's `list_item` at `depth`: only called inside a run the
156
+ * guard has already established, so a miss is unreachable. */
157
+ function ordinalAt(leaf, depth) {
158
+ const c = atDepth(leaf, depth);
159
+ return c && isListItemContainer(c) ? c.ordinal : -1;
160
+ }
161
+ /** Identity of a container for run gathering: its name plus its payload, NUL-joined
162
+ * as `markKey` joins a mark's, so no `attrs` content can forge a name boundary.
163
+ * Every arm but `list_item` (which has its own run rule) keys here. */
164
+ function containerKey(c) {
165
+ return 'attrs' in c ? `${c.container}\u0000${JSON.stringify(c.attrs)}` : c.container;
166
+ }
167
+ /** A single leaf block node (para/heading/code/rule/island, or an unknown kind
168
+ * carried on a paragraph) from its segments. `kind` is an OPEN set, so the two
169
+ * payload-carrying arms read through the boundary's guards; the payload-free arms
170
+ * compare literally, and anything left is unknown. */
171
+ function makeLeaf(schema, leaf, marks, cursor) {
172
+ const line = leaf.line;
173
+ if (line.kind === 'rule')
174
+ return schema.nodes.horizontal_rule.create();
175
+ if (line.kind === 'island') {
176
+ return schema.nodes.island_block.create(islandAttrs(cursor));
177
+ }
178
+ if (isCodeLine(line)) {
179
+ // One code_block: the segments' texts joined by literal `\n`, no marks.
180
+ const text = leaf.segments.map((s) => s.text).join('\n');
181
+ const content = text.length ? [schema.text(text)] : [];
182
+ return schema.nodes.code_block.create({ lang: line.lang ?? null }, content);
183
+ }
184
+ // para / heading / unknown: inline content, `hard_break` between continued segments.
185
+ const inline = [];
186
+ leaf.segments.forEach((seg, idx) => {
187
+ if (idx > 0)
188
+ inline.push(schema.nodes.hard_break.create());
189
+ inline.push(...buildInline(schema, seg.text, seg.startUSV, marks, cursor, false));
190
+ });
191
+ if (isHeadingLine(line)) {
192
+ return schema.nodes.heading.create({ level: line.level }, inline);
193
+ }
194
+ // An unknown kind renders as a paragraph AND rides on it, so it re-encodes
195
+ // verbatim rather than flattening to `para` on the field's first edit.
196
+ const unknown = line.kind === 'para' ? null : { kind: line.kind, attrs: 'attrs' in line ? line.attrs : null };
197
+ return schema.nodes.paragraph.create({ unknown }, inline);
198
+ }
199
+ /** Consume the next island entry as PM node attrs (text-order matched to slots):
200
+ * the WHOLE entry, `loss` included, which an island edit has to write back
201
+ * (`islands.ts`). A slot with no entry behind it is a malformed content; the empty
202
+ * attrs keep the decode total rather than throwing on a read. */
203
+ function islandAttrs(cursor) {
204
+ const isl = cursor.rt.islands[cursor.i++];
205
+ if (!isl)
206
+ return { id: '', islandType: '', props: null, loss: 'unrepresentable' };
207
+ return { id: isl.id, islandType: isl.type, props: isl.props, loss: isl.loss };
208
+ }
209
+ /**
210
+ * Inline nodes for one segment: split text into maximal runs of a constant mark
211
+ * set, lower each `U+FFFC` to an inline island node. `stripIslands` drops slots
212
+ * (inline-field mode). Offsets are USV; `pos = segStartUSV + k` indexes into the
213
+ * global mark ranges.
214
+ */
215
+ function buildInline(schema, segText, segStartUSV, marks, cursor, stripIslands) {
216
+ const cps = codePoints(segText);
217
+ const out = [];
218
+ let runText = '';
219
+ let runKey = '';
220
+ let runMarks = [];
221
+ const flush = () => {
222
+ if (runText.length)
223
+ out.push(schema.text(runText, runMarks));
224
+ runText = '';
225
+ };
226
+ for (let k = 0; k < cps.length; k++) {
227
+ const cp = cps[k];
228
+ const pos = segStartUSV + k;
229
+ if (cp === ISLAND_SLOT) {
230
+ flush();
231
+ const attrs = islandAttrs(cursor);
232
+ if (!stripIslands && schema.nodes.island_inline) {
233
+ out.push(schema.nodes.island_inline.create(attrs));
234
+ }
235
+ runKey = '\0slot'; // force a fresh run after a slot
236
+ continue;
237
+ }
238
+ const active = marks.filter((m) => m.start <= pos && pos < m.end);
239
+ const key = markSetKey(active);
240
+ if (key !== runKey && runText.length)
241
+ flush();
242
+ if (key !== runKey) {
243
+ runKey = key;
244
+ runMarks = buildMarkSet(schema, active);
245
+ }
246
+ runText += cp;
247
+ }
248
+ flush();
249
+ return out;
250
+ }
251
+ /** A stable key for a mark set (order-independent) to detect run boundaries. */
252
+ function markSetKey(active) {
253
+ if (!active.length)
254
+ return '';
255
+ // Per-mark keys via the shared descriptor + NUL-delimited `markKey`: the same
256
+ // pair the mark diff groups families by, so a run boundary and a mark family are
257
+ // one rule. The set is JSON-joined so no url/attrs content collides with a
258
+ // delimiter: a `link:a|strong` url stays distinct from `{link:a} + {strong}`.
259
+ return JSON.stringify(active.map((m) => markKey(descriptorOf(m))).sort());
260
+ }
261
+ /** The PM mark array for an active content mark set (anchors already excluded). */
262
+ function buildMarkSet(schema, active) {
263
+ let set = [];
264
+ for (const m of active) {
265
+ const pm = pmMarkFromContent(schema, m);
266
+ if (pm)
267
+ set = pm.addToSet(set);
268
+ }
269
+ return set;
270
+ }
@@ -0,0 +1,71 @@
1
+ import type { Node as PMNode } from 'prosemirror-model';
2
+ import type { ChangeBundle, Delta, Content, ContentIsland, ContentLine, ContentMark } from '@quillmark/wasm';
3
+ export type PosRun = {
4
+ kind: 'text';
5
+ pmStart: number;
6
+ usvStart: number;
7
+ s: string;
8
+ } | {
9
+ kind: 'nl';
10
+ pmStart: number;
11
+ pmEnd: number;
12
+ usvStart: number;
13
+ } | {
14
+ kind: 'atom';
15
+ pmStart: number;
16
+ usvStart: number;
17
+ };
18
+ export interface Scan {
19
+ text: string;
20
+ lines: ContentLine[];
21
+ marks: ContentMark[];
22
+ islands: ContentIsland[];
23
+ runs: PosRun[];
24
+ /** Total PM position at the end of the document content (doc.content.size). */
25
+ pmEnd: number;
26
+ /** Total USV length of `text`. */
27
+ usvEnd: number;
28
+ }
29
+ /**
30
+ * Walk a PM document once, producing the content projection AND the position-map
31
+ * runs (they share the traversal so they can never disagree). This is the single
32
+ * source both `pmToContent` and the position map draw from.
33
+ */
34
+ export declare function scanDoc(doc: PMNode): Scan;
35
+ /** `pmToContent`: the pure inverse of decode (drops the position runs). */
36
+ export declare function pmToContent(doc: PMNode): Content;
37
+ interface LowerOpts {
38
+ /** Anchor decoration positions before the edit (post-nothing); see field.ts. */
39
+ oldAnchors?: {
40
+ id: string;
41
+ pos: number;
42
+ }[];
43
+ /** Anchor decoration positions after the edit, in FINAL (new) USV coords. */
44
+ newAnchors?: {
45
+ id: string;
46
+ pos: number;
47
+ }[];
48
+ }
49
+ /**
50
+ * One old→new content edit with the text splice it implies. BOTH sides are content,
51
+ * never a PM doc: the projection of the new doc (`pmToContent`) is the caller's to
52
+ * hold, because a doc-taking overload re-walks the whole tree, once per keystroke,
53
+ * for a value the caller has in hand.
54
+ *
55
+ * The splice rides along because `diffText` allocates a code-point array per side
56
+ * and the commit path holds the edit across the lowering. `contentEdit` being its
57
+ * only constructor is what keeps a `delta` from disagreeing with the contents it
58
+ * was diffed from; `lower` is where a slot inside that splice moves to the island
59
+ * channel, so this `delta` is the RAW splice, not always the one committed.
60
+ */
61
+ export interface ContentEdit {
62
+ readonly oldRt: Content;
63
+ readonly newRt: Content;
64
+ /** The minimal single splice, or absent when the text is unchanged. */
65
+ readonly delta?: Delta;
66
+ }
67
+ /** The edit `oldRt` → `newRt`: diffs the text once. */
68
+ export declare function contentEdit(oldRt: Content, newRt: Content): ContentEdit;
69
+ /** Lower an edit to a `ChangeBundle`: the old→new content diff, as ops. */
70
+ export declare function lower(edit: ContentEdit, opts?: LowerOpts): ChangeBundle;
71
+ export {};