@ai-matrx/content-ir 0.11.7 → 0.13.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.
- package/CHANGELOG.md +94 -0
- package/README.md +2 -0
- package/dist/index.cjs +1682 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1659 -9
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +1678 -0
- package/dist/source.cjs.map +1 -0
- package/dist/source.d.cts +374 -0
- package/dist/source.d.ts +374 -0
- package/dist/source.js +1653 -0
- package/dist/source.js.map +1 -0
- package/package.json +14 -1
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE OFFSET-PRESERVING SOURCE TOKENIZER (rich-content PLAN decision 6).
|
|
3
|
+
*
|
|
4
|
+
* Cuts stored markdown into blocks that cover the text EXACTLY: the
|
|
5
|
+
* concatenation of every block's `raw` is the input, always, byte for byte.
|
|
6
|
+
* Nothing is trimmed, normalized, or re-serialized here — the tokenizer only
|
|
7
|
+
* draws boundaries. Every custom construct the platform stores (kinds and
|
|
8
|
+
* `__kind` JSON, typed JSON blocks, special fences, XML sections including
|
|
9
|
+
* attribute-bearing, mid-line and unknown containers, `{{variables}}`,
|
|
10
|
+
* `<matrxcite/>`, math, raw HTML, code fences, HTML comments and pinned
|
|
11
|
+
* anchors `<!--@a:…-->`) is an ISLAND: an atomic span no editor may parse
|
|
12
|
+
* into its own model. Islands that sit inside running prose (a variable, a
|
|
13
|
+
* citation, inline math) stay inside their paragraph as `inlines`, so a
|
|
14
|
+
* visual editor keeps the paragraph whole and holds the island as an atom.
|
|
15
|
+
*
|
|
16
|
+
* Block kinds:
|
|
17
|
+
* - `prose` — a markdown paragraph run (headings, lists, tables…): the only
|
|
18
|
+
* thing a visual editor may re-serialize, and only when edited.
|
|
19
|
+
* - `island` — a protected construct; `islandType` says which.
|
|
20
|
+
* - `gap` — the line terminator(s) and blank lines between blocks. Never
|
|
21
|
+
* edited; they keep the document's exact spacing.
|
|
22
|
+
*
|
|
23
|
+
* Streaming-tolerant: never throws on partial input. A construct whose
|
|
24
|
+
* closer has not arrived yet becomes an island running to the end of the text
|
|
25
|
+
* with `complete: false` — the same ownership rule the renderer's splitter
|
|
26
|
+
* applies, so a half-streamed fence or XML section is never reflowed.
|
|
27
|
+
*
|
|
28
|
+
* Boundaries follow matrx-frontend's renderer splitter (`content-splitter-v2.ts`)
|
|
29
|
+
* wherever it decides where a construct ends, so the editor and the renderer
|
|
30
|
+
* agree on what is locked. Offsets are UTF-16 (`start`/`end`) and code points
|
|
31
|
+
* (`startCp`/`endCp`).
|
|
32
|
+
*/
|
|
33
|
+
type SourceBlockKind = "prose" | "island" | "gap";
|
|
34
|
+
/** Island types that own whole blocks. */
|
|
35
|
+
type BlockIslandType =
|
|
36
|
+
/** ``` / ~~~ fenced code of any language (meta.lang, meta.special). */
|
|
37
|
+
"fence"
|
|
38
|
+
/** A bare JSON object block — typed JSON, `__kind` instances, directives. */
|
|
39
|
+
| "json"
|
|
40
|
+
/** A known attribute-less XML region (`<thinking>`, `<flashcards>`, …). */
|
|
41
|
+
| "xml_region"
|
|
42
|
+
/** An attribute-bearing XML element (`<artifact …>`, `<decision …>`, …). */
|
|
43
|
+
| "xml_attr"
|
|
44
|
+
/** Any other line-leading, balanced XML element. */
|
|
45
|
+
| "xml_container"
|
|
46
|
+
/** Allow-listed raw HTML block. */
|
|
47
|
+
| "html_block"
|
|
48
|
+
/** `<!-- … -->` comment. */
|
|
49
|
+
| "html_comment"
|
|
50
|
+
/** A pinned annotation anchor `<!--@a:id-->`. */
|
|
51
|
+
| "anchor"
|
|
52
|
+
/** `$$ … $$` display math on its own lines. */
|
|
53
|
+
| "math_block"
|
|
54
|
+
/** YAML front matter at the start of the text. */
|
|
55
|
+
| "front_matter"
|
|
56
|
+
/** A box-drawing tree diagram. */
|
|
57
|
+
| "tree";
|
|
58
|
+
/** Island types that live inside a prose block. */
|
|
59
|
+
type InlineIslandType = "variable" | "cite" | "math_inline" | "html_tag" | "xml_tag" | "xml_inline" | "html_comment" | "anchor" | "kind_json" | "media_ref";
|
|
60
|
+
type IslandType = BlockIslandType | InlineIslandType;
|
|
61
|
+
type IslandMeta = Readonly<Record<string, string | number | boolean>>;
|
|
62
|
+
interface SourceSpan {
|
|
63
|
+
/** UTF-16 offset of the first code unit. */
|
|
64
|
+
readonly start: number;
|
|
65
|
+
/** UTF-16 offset just past the last code unit. */
|
|
66
|
+
readonly end: number;
|
|
67
|
+
/** Code-point offset of `start`. */
|
|
68
|
+
readonly startCp: number;
|
|
69
|
+
/** Code-point offset of `end`. */
|
|
70
|
+
readonly endCp: number;
|
|
71
|
+
/** Exactly `text.slice(start, end)`. */
|
|
72
|
+
readonly raw: string;
|
|
73
|
+
}
|
|
74
|
+
interface SourceIsland extends SourceSpan {
|
|
75
|
+
readonly islandType: IslandType;
|
|
76
|
+
/** True when the island sits inside a prose block. */
|
|
77
|
+
readonly inline: boolean;
|
|
78
|
+
/** False when the construct's closer never arrived (streaming / malformed). */
|
|
79
|
+
readonly complete: boolean;
|
|
80
|
+
readonly meta: IslandMeta;
|
|
81
|
+
}
|
|
82
|
+
interface SourceBlock extends SourceSpan {
|
|
83
|
+
readonly kind: SourceBlockKind;
|
|
84
|
+
/** Set only on `island` blocks. */
|
|
85
|
+
readonly islandType: BlockIslandType | null;
|
|
86
|
+
readonly complete: boolean;
|
|
87
|
+
readonly meta: IslandMeta;
|
|
88
|
+
/** Inline islands of a `prose` block, in document order. Empty otherwise. */
|
|
89
|
+
readonly inlines: readonly SourceIsland[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Cut `text` into blocks whose raw concatenation is exactly `text`.
|
|
93
|
+
* Never throws; unclosed constructs are islands with `complete: false`.
|
|
94
|
+
*/
|
|
95
|
+
declare function tokenizeSource(text: string): SourceBlock[];
|
|
96
|
+
/** The exact source the blocks were cut from. */
|
|
97
|
+
declare function joinSource(blocks: readonly SourceBlock[]): string;
|
|
98
|
+
/** Every island — block-level and inline — in document order. */
|
|
99
|
+
declare function listIslands(blocks: readonly SourceBlock[]): SourceIsland[];
|
|
100
|
+
/** The block containing UTF-16 offset `offset` (the later block at a boundary). */
|
|
101
|
+
declare function blockAt(blocks: readonly SourceBlock[], offset: number): SourceBlock | null;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* SAVE = SPLICE (rich-content PLAN decision 6).
|
|
105
|
+
*
|
|
106
|
+
* An editor never hands back a whole re-serialized document. It hands back
|
|
107
|
+
* per-block replacements for exactly the blocks the person changed; every
|
|
108
|
+
* other byte of the stored text is written back as it was. This module
|
|
109
|
+
* applies those replacements and returns:
|
|
110
|
+
*
|
|
111
|
+
* - the new text,
|
|
112
|
+
* - a change set (old range → new range, in UTF-16 AND code points,
|
|
113
|
+
* narrowed to the bytes that actually differ) that the annotation layer
|
|
114
|
+
* uses to carry every live anchor through the save
|
|
115
|
+
* (STORE-DESIGN §3.19 — the ProseMirror / Google Docs position-mapping
|
|
116
|
+
* model), and
|
|
117
|
+
* - an integrity guarantee: every island not explicitly replaced must still
|
|
118
|
+
* be the same island, at its mapped position, in the new text. An edit
|
|
119
|
+
* that opens an unclosed fence and swallows a kind below it is REFUSED.
|
|
120
|
+
*
|
|
121
|
+
* ISLANDS CHANGE ONLY THROUGH `islandEdit`. A prose/range edit must hand back
|
|
122
|
+
* every island inside its range byte-identical and in order (a visual editor
|
|
123
|
+
* holds them as atoms); if one is altered or dropped the save throws
|
|
124
|
+
* `SourceSpliceError("island_edit")` naming the island. A kind editor or code
|
|
125
|
+
* view changes an island with `islandEdit(island, newRaw)`.
|
|
126
|
+
*
|
|
127
|
+
* A no-edit save (or edits whose text equals the original block) returns the
|
|
128
|
+
* original string itself — byte-identical, zero changes.
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Replace `[start, end)` (UTF-16) of the original with `text`. A plain edit
|
|
133
|
+
* starts and ends on block boundaries; an island edit (`island: true`, made by
|
|
134
|
+
* `islandEdit`) names exactly one island's span.
|
|
135
|
+
*/
|
|
136
|
+
interface SourceEdit {
|
|
137
|
+
readonly start: number;
|
|
138
|
+
readonly end: number;
|
|
139
|
+
readonly text: string;
|
|
140
|
+
readonly island?: boolean;
|
|
141
|
+
}
|
|
142
|
+
interface SourceChange {
|
|
143
|
+
/** The block-aligned range the edit named, in the ORIGINAL text. */
|
|
144
|
+
readonly blockStart: number;
|
|
145
|
+
readonly blockEnd: number;
|
|
146
|
+
/** The narrowed range that actually differs: old text … */
|
|
147
|
+
readonly oldStart: number;
|
|
148
|
+
readonly oldEnd: number;
|
|
149
|
+
/** … and where its replacement sits in the NEW text. */
|
|
150
|
+
readonly newStart: number;
|
|
151
|
+
readonly newEnd: number;
|
|
152
|
+
readonly oldStartCp: number;
|
|
153
|
+
readonly oldEndCp: number;
|
|
154
|
+
readonly newStartCp: number;
|
|
155
|
+
readonly newEndCp: number;
|
|
156
|
+
}
|
|
157
|
+
interface DisturbedIsland {
|
|
158
|
+
readonly islandType: IslandType;
|
|
159
|
+
readonly inline: boolean;
|
|
160
|
+
/** Where the island was in the original (UTF-16). */
|
|
161
|
+
readonly start: number;
|
|
162
|
+
readonly end: number;
|
|
163
|
+
/** Where it should have been in the new text. */
|
|
164
|
+
readonly mappedStart: number;
|
|
165
|
+
}
|
|
166
|
+
interface SpliceIntegrity {
|
|
167
|
+
/** True when every untouched island survived intact and outside bytes are identical. */
|
|
168
|
+
readonly ok: boolean;
|
|
169
|
+
/** Always true by construction; checked anyway because it is THE guarantee. */
|
|
170
|
+
readonly bytesOutsideEditsIdentical: boolean;
|
|
171
|
+
readonly disturbed: readonly DisturbedIsland[];
|
|
172
|
+
}
|
|
173
|
+
interface SpliceResult {
|
|
174
|
+
readonly text: string;
|
|
175
|
+
readonly changed: boolean;
|
|
176
|
+
readonly changes: readonly SourceChange[];
|
|
177
|
+
readonly integrity: SpliceIntegrity;
|
|
178
|
+
/** Tokenization of the new text. */
|
|
179
|
+
readonly blocks: readonly SourceBlock[];
|
|
180
|
+
}
|
|
181
|
+
type SourceSpliceErrorCode = "out_of_range" | "misaligned" | "overlap" | "stale_blocks" | "island_edit" | "integrity";
|
|
182
|
+
declare class SourceSpliceError extends Error {
|
|
183
|
+
readonly code: SourceSpliceErrorCode;
|
|
184
|
+
constructor(code: SourceSpliceErrorCode, message: string);
|
|
185
|
+
}
|
|
186
|
+
interface SpliceOptions {
|
|
187
|
+
/** The tokenization of `original`, when the caller already holds it. */
|
|
188
|
+
readonly blocks?: readonly SourceBlock[];
|
|
189
|
+
/**
|
|
190
|
+
* Default `true`: a save that disturbs any island it did not explicitly
|
|
191
|
+
* replace throws `SourceSpliceError("integrity")`. `false` is for TOOLING
|
|
192
|
+
* ONLY (diagnostic scripts that want the report instead of the refusal) —
|
|
193
|
+
* no editor or persistence path may pass it.
|
|
194
|
+
*/
|
|
195
|
+
readonly requireIntegrity?: boolean;
|
|
196
|
+
}
|
|
197
|
+
/** An edit that replaces one block's raw text. */
|
|
198
|
+
declare function blockEdit(block: SourceBlock, text: string): SourceEdit;
|
|
199
|
+
/** The one door that changes an island: replace exactly its span with `text`. */
|
|
200
|
+
declare function islandEdit(island: {
|
|
201
|
+
readonly start: number;
|
|
202
|
+
readonly end: number;
|
|
203
|
+
}, text: string): SourceEdit;
|
|
204
|
+
/** An edit that replaces a contiguous run of blocks `[first, last]`. */
|
|
205
|
+
declare function blockRangeEdit(first: SourceBlock, last: SourceBlock, text: string): SourceEdit;
|
|
206
|
+
/**
|
|
207
|
+
* Apply block-aligned replacements to `original`. Bytes outside the edits are
|
|
208
|
+
* copied, never rebuilt; an edit equal to its block changes nothing.
|
|
209
|
+
*/
|
|
210
|
+
declare function spliceSave(original: string, edits: readonly SourceEdit[], options?: SpliceOptions): SpliceResult;
|
|
211
|
+
interface MapOptions {
|
|
212
|
+
/** -1 keeps a position at an insertion point before the inserted text; 1 (default) moves it after. */
|
|
213
|
+
readonly assoc?: -1 | 1;
|
|
214
|
+
/** Offset unit of `pos` and of the result. Default `utf16`. */
|
|
215
|
+
readonly unit?: "utf16" | "codepoint";
|
|
216
|
+
}
|
|
217
|
+
interface MappedPosition {
|
|
218
|
+
readonly pos: number;
|
|
219
|
+
/** True when the position sat strictly inside replaced text. */
|
|
220
|
+
readonly deleted: boolean;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Carry a position in the old text through a change set (ProseMirror StepMap
|
|
224
|
+
* semantics): positions before a change stay, positions after shift by the
|
|
225
|
+
* size difference, a position strictly inside replaced text is `deleted` and
|
|
226
|
+
* lands on the replacement's edge chosen by `assoc`.
|
|
227
|
+
*/
|
|
228
|
+
declare function mapPosition(changes: readonly SourceChange[], pos: number, options?: MapOptions): MappedPosition;
|
|
229
|
+
interface MappedRange {
|
|
230
|
+
readonly start: number;
|
|
231
|
+
readonly end: number;
|
|
232
|
+
/** True when a change overlapped the range's interior. */
|
|
233
|
+
readonly touched: boolean;
|
|
234
|
+
/** True when the whole range was replaced away. */
|
|
235
|
+
readonly collapsed: boolean;
|
|
236
|
+
}
|
|
237
|
+
/** Carry an anchor range through a change set; it stays inside its text when edits happen around it. */
|
|
238
|
+
declare function mapRange(changes: readonly SourceChange[], start: number, end: number, unit?: "utf16" | "codepoint"): MappedRange;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* UTF-16 ⇄ code-point offset conversion.
|
|
242
|
+
*
|
|
243
|
+
* JavaScript strings are indexed in UTF-16 code units; Postgres `position()`,
|
|
244
|
+
* `substring()` and `length()` count code points. Every offset this module
|
|
245
|
+
* emits is given in BOTH units so an anchor captured on either side of the
|
|
246
|
+
* wire can be mapped without re-deriving the text.
|
|
247
|
+
*/
|
|
248
|
+
/** Indexes the surrogate pairs of one text so conversion is O(log pairs). */
|
|
249
|
+
interface CodePointIndex {
|
|
250
|
+
/** Code-point offset of a UTF-16 offset. */
|
|
251
|
+
toCodePoint(utf16: number): number;
|
|
252
|
+
/** UTF-16 offset of a code-point offset. */
|
|
253
|
+
toUtf16(codePoint: number): number;
|
|
254
|
+
/** Code-point length of the text. */
|
|
255
|
+
readonly lengthCp: number;
|
|
256
|
+
}
|
|
257
|
+
/** True when `offset` sits between the two halves of a surrogate pair. */
|
|
258
|
+
declare function splitsSurrogatePair(text: string, offset: number): boolean;
|
|
259
|
+
declare function buildCodePointIndex(text: string): CodePointIndex;
|
|
260
|
+
/** One-shot conversion; build an index when converting many offsets. */
|
|
261
|
+
declare function toCodePointOffset(text: string, utf16: number): number;
|
|
262
|
+
/** One-shot conversion; build an index when converting many offsets. */
|
|
263
|
+
declare function toUtf16Offset(text: string, codePoint: number): number;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* THE single-dollar math rule — the one predicate that decides whether
|
|
267
|
+
* `$…$` is inline math or currency.
|
|
268
|
+
*
|
|
269
|
+
* Moved here from matrx-frontend `components/markdown-core/math-normalizer.ts`
|
|
270
|
+
* (2026-09-23) so the renderer's normalizer and the source tokenizer can never
|
|
271
|
+
* disagree about which bytes are math: the normalizer imports this function.
|
|
272
|
+
*
|
|
273
|
+
* Pandoc's rule — the opening `$` is not followed by whitespace, the closing
|
|
274
|
+
* `$` is not preceded by whitespace and not followed by a digit — plus a math
|
|
275
|
+
* signal: a TeX command or operator (`\alpha`, `^`, `_`, `{`, `=`, …), or a
|
|
276
|
+
* lone variable (`$x$`, `$n$`, `$x'$`). Content that opens with a digit needs
|
|
277
|
+
* a real TeX signal, so a price range can never read as math.
|
|
278
|
+
*/
|
|
279
|
+
declare function isSingleDollarMath(content: string, after: string | undefined): boolean;
|
|
280
|
+
/**
|
|
281
|
+
* Where a single-dollar math span opened at `open` (a lone `$`) closes, or -1.
|
|
282
|
+
* Same scan the normalizer uses: same line, backslash-escapes skipped, the
|
|
283
|
+
* closer is a `$` not followed by another `$`, and the content passes
|
|
284
|
+
* {@link isSingleDollarMath}. Returns the offset just past the closing `$`.
|
|
285
|
+
*/
|
|
286
|
+
declare function singleDollarMathEnd(text: string, open: number, limit?: number): number;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* XML tag reading + the generic-container balance scanner.
|
|
290
|
+
*
|
|
291
|
+
* Ported VERBATIM (behaviour) from matrx-frontend
|
|
292
|
+
* `components/mardown-display/blocks/xml/readXmlTag.ts` and the
|
|
293
|
+
* `XmlContainerTracker` in
|
|
294
|
+
* `components/mardown-display/markdown-classification/processors/utils/content-splitter-v2.ts`
|
|
295
|
+
* so the source tokenizer ends a generic XML section exactly where the
|
|
296
|
+
* renderer does. The renderer copies stay for rendering (PLAN decision 6);
|
|
297
|
+
* a divergence between the two is a defect in whichever changed last.
|
|
298
|
+
*/
|
|
299
|
+
interface ReadXmlTag {
|
|
300
|
+
tagName: string;
|
|
301
|
+
raw: string;
|
|
302
|
+
isClosing: boolean;
|
|
303
|
+
isSelfClosing: boolean;
|
|
304
|
+
attributes: Array<{
|
|
305
|
+
name: string;
|
|
306
|
+
value: string;
|
|
307
|
+
}>;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* `find(needle, from)` must behave like `content.indexOf(needle, from)`; the
|
|
311
|
+
* tokenizer passes a cached occurrence index so a text full of unclosed
|
|
312
|
+
* attribute quotes stays linear. Behaviour is identical either way.
|
|
313
|
+
*/
|
|
314
|
+
declare function readXmlTag(content: string, start: number, find?: (needle: string, from: number) => number): ReadXmlTag | null;
|
|
315
|
+
/**
|
|
316
|
+
* Balances ONE unknown root tag line by line, ignoring lookalike tags inside
|
|
317
|
+
* fenced code, inline code spans, comments and CDATA. `consumeLine` returns
|
|
318
|
+
* the offset (within that line) just past the root's closing tag, or null.
|
|
319
|
+
*/
|
|
320
|
+
declare class XmlContainerTracker {
|
|
321
|
+
readonly rootTag: string;
|
|
322
|
+
private depth;
|
|
323
|
+
private inComment;
|
|
324
|
+
private inCdata;
|
|
325
|
+
private inlineTicks;
|
|
326
|
+
private fenceMarker;
|
|
327
|
+
constructor(rootTag: string);
|
|
328
|
+
consumeLine(line: string, startOffset?: number): number | null;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Fence languages whose body is itself a markdown document. */
|
|
332
|
+
declare const NESTING_FENCE_LANGUAGES: ReadonlySet<string>;
|
|
333
|
+
/** True when a fence opened with this info-string language nests inner fences. */
|
|
334
|
+
declare function fenceNestsInnerFences(language: string | undefined | null): boolean;
|
|
335
|
+
type InnerFenceLine =
|
|
336
|
+
/** Not a fence line — ordinary content of the outer fence. */
|
|
337
|
+
"content"
|
|
338
|
+
/** ```lang inside a markdown fence — content, and one level deeper. */
|
|
339
|
+
| "open-nested"
|
|
340
|
+
/** Bare ``` that closes the innermost nested fence — content, one level up. */
|
|
341
|
+
| "close-nested"
|
|
342
|
+
/** Bare ``` that closes the OUTER fence. */
|
|
343
|
+
| "close-outer";
|
|
344
|
+
/**
|
|
345
|
+
* Classify one TRIMMED line inside an open backtick fence.
|
|
346
|
+
*
|
|
347
|
+
* @param trimmed the line with surrounding whitespace removed
|
|
348
|
+
* @param openTicks backtick count of the outer fence's opener
|
|
349
|
+
* @param nests whether the outer fence follows the nesting rule
|
|
350
|
+
* @param nestedDepth how many nested fences are open right now
|
|
351
|
+
*/
|
|
352
|
+
declare function classifyInnerFenceLine(trimmed: string, openTicks: number, nests: boolean, nestedDepth: number): InnerFenceLine;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* `$$` pairing — the core's rule for which `$$` delimiters are real math.
|
|
356
|
+
*
|
|
357
|
+
* Ported from `@ai-matrx/kit/delimiter-guard` (`guardMathDelimiters`), which
|
|
358
|
+
* every math-capable renderer runs before remark-math. Its decisions:
|
|
359
|
+
* - `$$` tokens outside code (``` / ~~~ fences, `inline code`) pair in
|
|
360
|
+
* document order, open → close;
|
|
361
|
+
* - a pair whose inside reads as prose or carries markdown structure is NOT
|
|
362
|
+
* math: the renderer neutralizes its opener and the closer is retried as
|
|
363
|
+
* the next opener;
|
|
364
|
+
* - an unpaired `$$` is inert — it renders as literal text.
|
|
365
|
+
* So the tokenizer protects exactly the pairs the renderer shows as math, and
|
|
366
|
+
* a lone `$$` (a stray closer, or an opener that never closed) never locks
|
|
367
|
+
* anything. Linear time: one regex pass per code shape, one sweep.
|
|
368
|
+
*/
|
|
369
|
+
/** The guard's `looksLikeMath`, verbatim. */
|
|
370
|
+
declare function looksLikeDisplayMath(inner: string): boolean;
|
|
371
|
+
/** Map of `$$` opener offset → closer offset for every pair the renderer shows as math. */
|
|
372
|
+
declare function pairDisplayMath(text: string): Map<number, number>;
|
|
373
|
+
|
|
374
|
+
export { type BlockIslandType, type CodePointIndex, type DisturbedIsland, type InlineIslandType, type InnerFenceLine, type IslandMeta, type IslandType, type MapOptions, type MappedPosition, type MappedRange, NESTING_FENCE_LANGUAGES, type ReadXmlTag, type SourceBlock, type SourceBlockKind, type SourceChange, type SourceEdit, type SourceIsland, type SourceSpan, SourceSpliceError, type SourceSpliceErrorCode, type SpliceIntegrity, type SpliceOptions, type SpliceResult, XmlContainerTracker, blockAt, blockEdit, blockRangeEdit, buildCodePointIndex, classifyInnerFenceLine, fenceNestsInnerFences, isSingleDollarMath, islandEdit, joinSource, listIslands, looksLikeDisplayMath, mapPosition, mapRange, pairDisplayMath, readXmlTag, singleDollarMathEnd, spliceSave, splitsSurrogatePair, toCodePointOffset, toUtf16Offset, tokenizeSource };
|