@ai-matrx/content-ir 0.11.7 → 0.12.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 +46 -0
- package/README.md +2 -0
- package/dist/index.cjs +1441 -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 +1424 -9
- package/dist/index.js.map +1 -1
- package/dist/source.cjs +1437 -0
- package/dist/source.cjs.map +1 -0
- package/dist/source.d.cts +306 -0
- package/dist/source.d.ts +306 -0
- package/dist/source.js +1418 -0
- package/dist/source.js.map +1 -0
- package/package.json +14 -1
package/dist/index.cjs
CHANGED
|
@@ -1266,18 +1266,18 @@ var KindStreamParser = class {
|
|
|
1266
1266
|
completeTypedObject(path, objectValue, at) {
|
|
1267
1267
|
const pathKey = this.pathKey(path);
|
|
1268
1268
|
if (this.rawObjectPaths.has(pathKey)) return;
|
|
1269
|
-
const
|
|
1269
|
+
const declaredKind2 = readObjectKind(objectValue);
|
|
1270
1270
|
const committedKind = this.objectKinds.get(pathKey);
|
|
1271
1271
|
if (committedKind && this.pendingSchemaPaths.get(committedKind)?.has(pathKey)) {
|
|
1272
1272
|
this.closedPendingPaths.add(pathKey);
|
|
1273
1273
|
return;
|
|
1274
1274
|
}
|
|
1275
|
-
if (!
|
|
1275
|
+
if (!declaredKind2 && committedKind && this.speculativeKinds.has(pathKey)) {
|
|
1276
1276
|
this.speculativeKinds.delete(pathKey);
|
|
1277
1277
|
this.finalizeSpeculatedObject(path, objectValue, committedKind, at);
|
|
1278
1278
|
return;
|
|
1279
1279
|
}
|
|
1280
|
-
if (path.length === 0 && !
|
|
1280
|
+
if (path.length === 0 && !declaredKind2 && !committedKind && this.rootSurfaceKind) {
|
|
1281
1281
|
const surfaceKind = this.rootSurfaceKind;
|
|
1282
1282
|
this.rootSurfaceKind = null;
|
|
1283
1283
|
this.objectKinds.set(pathKey, surfaceKind);
|
|
@@ -1293,7 +1293,7 @@ var KindStreamParser = class {
|
|
|
1293
1293
|
this.finalizeSpeculatedObject(path, objectValue, surfaceKind, at);
|
|
1294
1294
|
return;
|
|
1295
1295
|
}
|
|
1296
|
-
if (!
|
|
1296
|
+
if (!declaredKind2) {
|
|
1297
1297
|
this.emitRawObject(
|
|
1298
1298
|
path,
|
|
1299
1299
|
objectValue,
|
|
@@ -3209,8 +3209,8 @@ function refToObjectField(resolved, path, ctx) {
|
|
|
3209
3209
|
function isAnyValueSchema(node) {
|
|
3210
3210
|
return node.type === void 0 && node.enum === void 0 && node.const === void 0 && node.properties === void 0 && node.items === void 0 && node.additionalProperties === void 0 && node.anyOf === void 0 && node.oneOf === void 0 && node.allOf === void 0 && node.$ref === void 0;
|
|
3211
3211
|
}
|
|
3212
|
-
function registerDeclaredKindDraft(
|
|
3213
|
-
if (ctx.blockSchemas.some((draft) => draft.slug ===
|
|
3212
|
+
function registerDeclaredKindDraft(declaredKind2, objectNode, path, ctx) {
|
|
3213
|
+
if (ctx.blockSchemas.some((draft) => draft.slug === declaredKind2)) return;
|
|
3214
3214
|
if (!isRecord4(objectNode.properties)) return;
|
|
3215
3215
|
const memberRequired = new Set(
|
|
3216
3216
|
Array.isArray(objectNode.required) ? objectNode.required.filter((k) => typeof k === "string") : []
|
|
@@ -3228,8 +3228,8 @@ function registerDeclaredKindDraft(declaredKind, objectNode, path, ctx) {
|
|
|
3228
3228
|
);
|
|
3229
3229
|
}
|
|
3230
3230
|
ctx.blockSchemas.push({
|
|
3231
|
-
slug:
|
|
3232
|
-
label: formatBlockLabel(
|
|
3231
|
+
slug: declaredKind2,
|
|
3232
|
+
label: formatBlockLabel(declaredKind2),
|
|
3233
3233
|
fields
|
|
3234
3234
|
});
|
|
3235
3235
|
}
|
|
@@ -4973,6 +4973,1421 @@ function asKindInstance(item, kind) {
|
|
|
4973
4973
|
return { [KIND_KEY]: resolved, ...item };
|
|
4974
4974
|
}
|
|
4975
4975
|
|
|
4976
|
+
// source/offsets.ts
|
|
4977
|
+
function isHigh(code) {
|
|
4978
|
+
return code >= 55296 && code <= 56319;
|
|
4979
|
+
}
|
|
4980
|
+
function isLow(code) {
|
|
4981
|
+
return code >= 56320 && code <= 57343;
|
|
4982
|
+
}
|
|
4983
|
+
function splitsSurrogatePair(text, offset) {
|
|
4984
|
+
if (offset <= 0 || offset >= text.length) return false;
|
|
4985
|
+
return isHigh(text.charCodeAt(offset - 1)) && isLow(text.charCodeAt(offset));
|
|
4986
|
+
}
|
|
4987
|
+
function buildCodePointIndex(text) {
|
|
4988
|
+
const pairStarts = [];
|
|
4989
|
+
for (let i = 0; i < text.length - 1; i++) {
|
|
4990
|
+
if (isHigh(text.charCodeAt(i)) && isLow(text.charCodeAt(i + 1))) {
|
|
4991
|
+
pairStarts.push(i);
|
|
4992
|
+
i++;
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
const countBelow = (limit) => {
|
|
4996
|
+
let lo = 0;
|
|
4997
|
+
let hi = pairStarts.length;
|
|
4998
|
+
while (lo < hi) {
|
|
4999
|
+
const mid = lo + hi >>> 1;
|
|
5000
|
+
if (pairStarts[mid] < limit) lo = mid + 1;
|
|
5001
|
+
else hi = mid;
|
|
5002
|
+
}
|
|
5003
|
+
return lo;
|
|
5004
|
+
};
|
|
5005
|
+
const toCodePoint = (utf16) => {
|
|
5006
|
+
return utf16 - countBelow(utf16 - 1);
|
|
5007
|
+
};
|
|
5008
|
+
const toUtf16 = (codePoint) => {
|
|
5009
|
+
let lo = 0;
|
|
5010
|
+
let hi = pairStarts.length;
|
|
5011
|
+
while (lo < hi) {
|
|
5012
|
+
const mid = lo + hi >>> 1;
|
|
5013
|
+
const start = pairStarts[mid];
|
|
5014
|
+
if (start - mid < codePoint) lo = mid + 1;
|
|
5015
|
+
else hi = mid;
|
|
5016
|
+
}
|
|
5017
|
+
return codePoint + lo;
|
|
5018
|
+
};
|
|
5019
|
+
return {
|
|
5020
|
+
toCodePoint,
|
|
5021
|
+
toUtf16,
|
|
5022
|
+
lengthCp: text.length - pairStarts.length
|
|
5023
|
+
};
|
|
5024
|
+
}
|
|
5025
|
+
function toCodePointOffset(text, utf16) {
|
|
5026
|
+
return buildCodePointIndex(text).toCodePoint(utf16);
|
|
5027
|
+
}
|
|
5028
|
+
function toUtf16Offset(text, codePoint) {
|
|
5029
|
+
return buildCodePointIndex(text).toUtf16(codePoint);
|
|
5030
|
+
}
|
|
5031
|
+
|
|
5032
|
+
// source/constants.ts
|
|
5033
|
+
var XML_TAG_BLOCK_NAMES = [
|
|
5034
|
+
"thinking",
|
|
5035
|
+
"think",
|
|
5036
|
+
"reasoning",
|
|
5037
|
+
"info",
|
|
5038
|
+
"task",
|
|
5039
|
+
"database",
|
|
5040
|
+
"private",
|
|
5041
|
+
"plan",
|
|
5042
|
+
"event",
|
|
5043
|
+
"tool",
|
|
5044
|
+
"questionnaire",
|
|
5045
|
+
"flashcards",
|
|
5046
|
+
"cooking_recipe",
|
|
5047
|
+
"timeline",
|
|
5048
|
+
"progress_tracker",
|
|
5049
|
+
"troubleshooting",
|
|
5050
|
+
"resources",
|
|
5051
|
+
"research"
|
|
5052
|
+
];
|
|
5053
|
+
var THINKING_FAMILY = ["thinking", "think", "reasoning"];
|
|
5054
|
+
var ATTRIBUTE_XML_NAMES = [
|
|
5055
|
+
"decision",
|
|
5056
|
+
"artifact",
|
|
5057
|
+
"editor_error",
|
|
5058
|
+
"editor_code_snippet",
|
|
5059
|
+
"audiocite"
|
|
5060
|
+
];
|
|
5061
|
+
var KNOWN_XML_TAG_NAMES = /* @__PURE__ */ new Set([
|
|
5062
|
+
...XML_TAG_BLOCK_NAMES,
|
|
5063
|
+
...ATTRIBUTE_XML_NAMES
|
|
5064
|
+
]);
|
|
5065
|
+
var ALLOWED_RAW_HTML_TAGS = /* @__PURE__ */ new Set([
|
|
5066
|
+
"img",
|
|
5067
|
+
"a",
|
|
5068
|
+
"br",
|
|
5069
|
+
"hr",
|
|
5070
|
+
"span",
|
|
5071
|
+
"p",
|
|
5072
|
+
"strong",
|
|
5073
|
+
"b",
|
|
5074
|
+
"em",
|
|
5075
|
+
"i",
|
|
5076
|
+
"u",
|
|
5077
|
+
"s",
|
|
5078
|
+
"del",
|
|
5079
|
+
"ins",
|
|
5080
|
+
"sup",
|
|
5081
|
+
"sub",
|
|
5082
|
+
"mark",
|
|
5083
|
+
"kbd",
|
|
5084
|
+
"abbr",
|
|
5085
|
+
"small",
|
|
5086
|
+
"code",
|
|
5087
|
+
"pre",
|
|
5088
|
+
"ul",
|
|
5089
|
+
"ol",
|
|
5090
|
+
"li",
|
|
5091
|
+
"blockquote",
|
|
5092
|
+
"h1",
|
|
5093
|
+
"h2",
|
|
5094
|
+
"h3",
|
|
5095
|
+
"h4",
|
|
5096
|
+
"h5",
|
|
5097
|
+
"h6",
|
|
5098
|
+
"table",
|
|
5099
|
+
"thead",
|
|
5100
|
+
"tbody",
|
|
5101
|
+
"tfoot",
|
|
5102
|
+
"tr",
|
|
5103
|
+
"th",
|
|
5104
|
+
"td",
|
|
5105
|
+
"caption",
|
|
5106
|
+
"colgroup",
|
|
5107
|
+
"col",
|
|
5108
|
+
"figure",
|
|
5109
|
+
"figcaption"
|
|
5110
|
+
]);
|
|
5111
|
+
var HTML_BLOCK_LEVEL_TAGS = /* @__PURE__ */ new Set([
|
|
5112
|
+
"p",
|
|
5113
|
+
"hr",
|
|
5114
|
+
"ul",
|
|
5115
|
+
"ol",
|
|
5116
|
+
"li",
|
|
5117
|
+
"blockquote",
|
|
5118
|
+
"h1",
|
|
5119
|
+
"h2",
|
|
5120
|
+
"h3",
|
|
5121
|
+
"h4",
|
|
5122
|
+
"h5",
|
|
5123
|
+
"h6",
|
|
5124
|
+
"table",
|
|
5125
|
+
"thead",
|
|
5126
|
+
"tbody",
|
|
5127
|
+
"tfoot",
|
|
5128
|
+
"tr",
|
|
5129
|
+
"th",
|
|
5130
|
+
"td",
|
|
5131
|
+
"caption",
|
|
5132
|
+
"colgroup",
|
|
5133
|
+
"col",
|
|
5134
|
+
"figure",
|
|
5135
|
+
"figcaption"
|
|
5136
|
+
]);
|
|
5137
|
+
var SPECIAL_CODE_LANGUAGES = /* @__PURE__ */ new Set([
|
|
5138
|
+
"transcript",
|
|
5139
|
+
"tasks",
|
|
5140
|
+
"structured_info",
|
|
5141
|
+
"questionnaire",
|
|
5142
|
+
"flashcards",
|
|
5143
|
+
"cooking_recipe",
|
|
5144
|
+
"mermaid",
|
|
5145
|
+
"svg",
|
|
5146
|
+
"chart",
|
|
5147
|
+
"map",
|
|
5148
|
+
"stats",
|
|
5149
|
+
"diff",
|
|
5150
|
+
"html",
|
|
5151
|
+
"react",
|
|
5152
|
+
"matrx"
|
|
5153
|
+
]);
|
|
5154
|
+
var CODE_LANGUAGE_ALIASES = {
|
|
5155
|
+
mmd: "mermaid",
|
|
5156
|
+
jsx: "react",
|
|
5157
|
+
tsx: "react"
|
|
5158
|
+
};
|
|
5159
|
+
var TYPED_JSON_ROOT_KEYS = {
|
|
5160
|
+
quiz_title: "quiz",
|
|
5161
|
+
presentation: "presentation",
|
|
5162
|
+
decision_tree: "decision_tree",
|
|
5163
|
+
comparison: "comparison_table",
|
|
5164
|
+
diagram: "diagram",
|
|
5165
|
+
math_problem: "math_problem",
|
|
5166
|
+
item_presentation: "item_presentation",
|
|
5167
|
+
name: "schema_proposal"
|
|
5168
|
+
};
|
|
5169
|
+
var TREE_GLYPHS = /[├└│┌┐┘┬┴┤┼─]/;
|
|
5170
|
+
var PINNED_ANCHOR_RE = /^<!--@a:([A-Za-z0-9_-]+)-->$/;
|
|
5171
|
+
var STRICT_VARIABLE_RE = /^\{\{([a-zA-Z_][a-zA-Z0-9_.]*)\}\}$/;
|
|
5172
|
+
var CITE_RE = /^<matrxcite\s+n="(\d+)"\s*\/>/;
|
|
5173
|
+
function normalizeFenceLanguage(language) {
|
|
5174
|
+
const lower = language.toLowerCase();
|
|
5175
|
+
return CODE_LANGUAGE_ALIASES[lower] ?? lower;
|
|
5176
|
+
}
|
|
5177
|
+
|
|
5178
|
+
// source/math.ts
|
|
5179
|
+
var TEX_SIGNAL = /\\[A-Za-z]+|[\\^_{}=<>+]/;
|
|
5180
|
+
var LONE_VARIABLE = /^[A-Za-z](?:'|[0-9])?$/;
|
|
5181
|
+
function isSingleDollarMath(content, after) {
|
|
5182
|
+
if (!content || content.length > 400) return false;
|
|
5183
|
+
if (/^\s/.test(content) || /\s$/.test(content)) return false;
|
|
5184
|
+
if (after !== void 0 && /[0-9]/.test(after)) return false;
|
|
5185
|
+
if (LONE_VARIABLE.test(content)) return true;
|
|
5186
|
+
if (/^[0-9]/.test(content)) return /\\[A-Za-z]+|[\\^_{}]/.test(content);
|
|
5187
|
+
return TEX_SIGNAL.test(content);
|
|
5188
|
+
}
|
|
5189
|
+
function singleDollarMathEnd(text, open, limit = text.length) {
|
|
5190
|
+
let close = -1;
|
|
5191
|
+
for (let k = open + 1; k < limit; k += 1) {
|
|
5192
|
+
const c = text[k];
|
|
5193
|
+
if (c === "\n") break;
|
|
5194
|
+
if (c === "\\") {
|
|
5195
|
+
k += 1;
|
|
5196
|
+
continue;
|
|
5197
|
+
}
|
|
5198
|
+
if (c === "$") {
|
|
5199
|
+
close = k;
|
|
5200
|
+
break;
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
5203
|
+
if (close === -1 || text[close + 1] === "$") return -1;
|
|
5204
|
+
const content = text.slice(open + 1, close);
|
|
5205
|
+
return isSingleDollarMath(content, text[close + 1]) ? close + 1 : -1;
|
|
5206
|
+
}
|
|
5207
|
+
|
|
5208
|
+
// source/xml-tag.ts
|
|
5209
|
+
var XML_NAME_START = /[A-Za-z_]/;
|
|
5210
|
+
var XML_NAME_CHARACTER = /[\w.:-]/;
|
|
5211
|
+
var WHITESPACE = /\s/;
|
|
5212
|
+
function readXmlTag(content, start) {
|
|
5213
|
+
let cursor = start + 1;
|
|
5214
|
+
const isClosing = content[cursor] === "/";
|
|
5215
|
+
if (isClosing) cursor++;
|
|
5216
|
+
if (!XML_NAME_START.test(content[cursor] ?? "")) return null;
|
|
5217
|
+
const nameStart = cursor;
|
|
5218
|
+
while (XML_NAME_CHARACTER.test(content[cursor] ?? "")) cursor++;
|
|
5219
|
+
const tagName = content.slice(nameStart, cursor);
|
|
5220
|
+
const attributes = [];
|
|
5221
|
+
if (isClosing) {
|
|
5222
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
5223
|
+
if (content[cursor] !== ">") return null;
|
|
5224
|
+
return {
|
|
5225
|
+
tagName,
|
|
5226
|
+
raw: content.slice(start, cursor + 1),
|
|
5227
|
+
isClosing,
|
|
5228
|
+
isSelfClosing: false,
|
|
5229
|
+
attributes
|
|
5230
|
+
};
|
|
5231
|
+
}
|
|
5232
|
+
while (cursor < content.length) {
|
|
5233
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
5234
|
+
if (content[cursor] === ">")
|
|
5235
|
+
return {
|
|
5236
|
+
tagName,
|
|
5237
|
+
raw: content.slice(start, cursor + 1),
|
|
5238
|
+
isClosing,
|
|
5239
|
+
isSelfClosing: false,
|
|
5240
|
+
attributes
|
|
5241
|
+
};
|
|
5242
|
+
if (content[cursor] === "/") {
|
|
5243
|
+
cursor++;
|
|
5244
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
5245
|
+
if (content[cursor] !== ">") return null;
|
|
5246
|
+
return {
|
|
5247
|
+
tagName,
|
|
5248
|
+
raw: content.slice(start, cursor + 1),
|
|
5249
|
+
isClosing,
|
|
5250
|
+
isSelfClosing: true,
|
|
5251
|
+
attributes
|
|
5252
|
+
};
|
|
5253
|
+
}
|
|
5254
|
+
if (!XML_NAME_START.test(content[cursor] ?? "")) return null;
|
|
5255
|
+
const attributeStart = cursor;
|
|
5256
|
+
while (XML_NAME_CHARACTER.test(content[cursor] ?? "")) cursor++;
|
|
5257
|
+
const name = content.slice(attributeStart, cursor);
|
|
5258
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
5259
|
+
if (content[cursor] !== "=") return null;
|
|
5260
|
+
cursor++;
|
|
5261
|
+
while (WHITESPACE.test(content[cursor] ?? "")) cursor++;
|
|
5262
|
+
const quote = content[cursor];
|
|
5263
|
+
if (quote !== '"' && quote !== "'") return null;
|
|
5264
|
+
const valueStart = ++cursor;
|
|
5265
|
+
while (cursor < content.length && content[cursor] !== quote) cursor++;
|
|
5266
|
+
if (cursor === content.length) return null;
|
|
5267
|
+
attributes.push({ name, value: content.slice(valueStart, cursor) });
|
|
5268
|
+
cursor++;
|
|
5269
|
+
}
|
|
5270
|
+
return null;
|
|
5271
|
+
}
|
|
5272
|
+
function isEscapedBacktick(line, offset) {
|
|
5273
|
+
let slashes = 0;
|
|
5274
|
+
for (let i = offset - 1; line[i] === "\\"; i--) slashes++;
|
|
5275
|
+
return slashes % 2 === 1;
|
|
5276
|
+
}
|
|
5277
|
+
var XmlContainerTracker = class {
|
|
5278
|
+
constructor(rootTag) {
|
|
5279
|
+
this.rootTag = rootTag;
|
|
5280
|
+
}
|
|
5281
|
+
rootTag;
|
|
5282
|
+
depth = 0;
|
|
5283
|
+
inComment = false;
|
|
5284
|
+
inCdata = false;
|
|
5285
|
+
inlineTicks = 0;
|
|
5286
|
+
fenceMarker = null;
|
|
5287
|
+
consumeLine(line, startOffset = 0) {
|
|
5288
|
+
const fence = /^[ \t]*(`{3,}|~{3,})(.*)$/.exec(line);
|
|
5289
|
+
if (!this.inComment && !this.inCdata && this.inlineTicks === 0 && fence) {
|
|
5290
|
+
const marker = fence[1];
|
|
5291
|
+
const char = marker[0];
|
|
5292
|
+
const suffix = fence[2];
|
|
5293
|
+
if (marker.length >= 3) {
|
|
5294
|
+
if (!this.fenceMarker) {
|
|
5295
|
+
this.fenceMarker = { char, ticks: marker.length };
|
|
5296
|
+
return null;
|
|
5297
|
+
}
|
|
5298
|
+
if (char === this.fenceMarker.char && marker.length >= this.fenceMarker.ticks && suffix.trim() === "") {
|
|
5299
|
+
this.fenceMarker = null;
|
|
5300
|
+
}
|
|
5301
|
+
return null;
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
if (this.fenceMarker) return null;
|
|
5305
|
+
for (let i = startOffset; i < line.length; i++) {
|
|
5306
|
+
if (this.inlineTicks > 0) {
|
|
5307
|
+
if (line[i] === "`") {
|
|
5308
|
+
let end = i;
|
|
5309
|
+
while (line[end] === "`") end++;
|
|
5310
|
+
if (end - i === this.inlineTicks) this.inlineTicks = 0;
|
|
5311
|
+
i = end - 1;
|
|
5312
|
+
}
|
|
5313
|
+
continue;
|
|
5314
|
+
}
|
|
5315
|
+
if (this.inComment) {
|
|
5316
|
+
const end = line.indexOf("-->", i);
|
|
5317
|
+
if (end === -1) return null;
|
|
5318
|
+
this.inComment = false;
|
|
5319
|
+
i = end + 2;
|
|
5320
|
+
continue;
|
|
5321
|
+
}
|
|
5322
|
+
if (this.inCdata) {
|
|
5323
|
+
const end = line.indexOf("]]>", i);
|
|
5324
|
+
if (end === -1) return null;
|
|
5325
|
+
this.inCdata = false;
|
|
5326
|
+
i = end + 2;
|
|
5327
|
+
continue;
|
|
5328
|
+
}
|
|
5329
|
+
if (line.startsWith("<!--", i)) {
|
|
5330
|
+
this.inComment = true;
|
|
5331
|
+
i += 3;
|
|
5332
|
+
continue;
|
|
5333
|
+
}
|
|
5334
|
+
if (line.startsWith("<![CDATA[", i)) {
|
|
5335
|
+
this.inCdata = true;
|
|
5336
|
+
i += 8;
|
|
5337
|
+
continue;
|
|
5338
|
+
}
|
|
5339
|
+
if (line[i] === "`" && !isEscapedBacktick(line, i)) {
|
|
5340
|
+
let end = i;
|
|
5341
|
+
while (line[end] === "`") end++;
|
|
5342
|
+
this.inlineTicks = end - i;
|
|
5343
|
+
i = end - 1;
|
|
5344
|
+
continue;
|
|
5345
|
+
}
|
|
5346
|
+
if (line[i] !== "<") continue;
|
|
5347
|
+
const tag = readXmlTag(line, i);
|
|
5348
|
+
if (!tag) continue;
|
|
5349
|
+
const tagEnd = i + tag.raw.length;
|
|
5350
|
+
if (tag.tagName === this.rootTag) {
|
|
5351
|
+
if (tag.isClosing) this.depth--;
|
|
5352
|
+
else if (!tag.isSelfClosing) this.depth++;
|
|
5353
|
+
if (this.depth === 0) return tagEnd;
|
|
5354
|
+
}
|
|
5355
|
+
i = tagEnd - 1;
|
|
5356
|
+
}
|
|
5357
|
+
return null;
|
|
5358
|
+
}
|
|
5359
|
+
};
|
|
5360
|
+
|
|
5361
|
+
// source/tokenize.ts
|
|
5362
|
+
var NOT_WHITESPACE = /\S/;
|
|
5363
|
+
function countStructuralBraces(source) {
|
|
5364
|
+
let opens = 0;
|
|
5365
|
+
let closes = 0;
|
|
5366
|
+
let inString = false;
|
|
5367
|
+
let escaped = false;
|
|
5368
|
+
for (let i = 0; i < source.length; i++) {
|
|
5369
|
+
const char = source[i];
|
|
5370
|
+
if (inString) {
|
|
5371
|
+
if (escaped) escaped = false;
|
|
5372
|
+
else if (char === "\\") escaped = true;
|
|
5373
|
+
else if (char === '"') inString = false;
|
|
5374
|
+
continue;
|
|
5375
|
+
}
|
|
5376
|
+
if (char === '"') inString = true;
|
|
5377
|
+
else if (char === "{") opens++;
|
|
5378
|
+
else if (char === "}") closes++;
|
|
5379
|
+
}
|
|
5380
|
+
return { opens, closes };
|
|
5381
|
+
}
|
|
5382
|
+
function firstJsonKey(content) {
|
|
5383
|
+
const match = /^\{\s*"([^"]+)"/.exec(content.trimStart());
|
|
5384
|
+
return match ? match[1] : null;
|
|
5385
|
+
}
|
|
5386
|
+
function jsonMeta(content) {
|
|
5387
|
+
const meta = {};
|
|
5388
|
+
const key = firstJsonKey(content);
|
|
5389
|
+
if (key) meta.firstKey = key;
|
|
5390
|
+
try {
|
|
5391
|
+
const parsed = JSON.parse(content.trim());
|
|
5392
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
5393
|
+
const kind = parsed.__kind;
|
|
5394
|
+
if (typeof kind === "string" && kind.trim()) meta.kind = kind;
|
|
5395
|
+
}
|
|
5396
|
+
} catch {
|
|
5397
|
+
const head = /^\{\s*"__kind"\s*:\s*"([^"]*)"/.exec(content.trimStart());
|
|
5398
|
+
if (head) meta.kind = head[1];
|
|
5399
|
+
}
|
|
5400
|
+
if (typeof meta.kind === "string" && meta.kind.startsWith("directive_v")) {
|
|
5401
|
+
meta.directive = true;
|
|
5402
|
+
}
|
|
5403
|
+
if (key === "matrx_version") meta.directive = true;
|
|
5404
|
+
if (key && TYPED_JSON_ROOT_KEYS[key]) meta.typed = TYPED_JSON_ROOT_KEYS[key];
|
|
5405
|
+
return meta;
|
|
5406
|
+
}
|
|
5407
|
+
function isRecognizedJsonHead(content) {
|
|
5408
|
+
const key = firstJsonKey(content);
|
|
5409
|
+
if (!key) return false;
|
|
5410
|
+
return key === "__kind" || key === "matrx_version" || key in TYPED_JSON_ROOT_KEYS;
|
|
5411
|
+
}
|
|
5412
|
+
function matchingJsonObjectEnd(source, start, limit) {
|
|
5413
|
+
if (source[start] !== "{") return null;
|
|
5414
|
+
const stack = ["{"];
|
|
5415
|
+
let inString = false;
|
|
5416
|
+
let escaped = false;
|
|
5417
|
+
for (let index = start + 1; index < limit; index++) {
|
|
5418
|
+
const char = source[index];
|
|
5419
|
+
if (inString) {
|
|
5420
|
+
if (escaped) escaped = false;
|
|
5421
|
+
else if (char === "\\") escaped = true;
|
|
5422
|
+
else if (char === '"') inString = false;
|
|
5423
|
+
continue;
|
|
5424
|
+
}
|
|
5425
|
+
if (char === '"') {
|
|
5426
|
+
inString = true;
|
|
5427
|
+
continue;
|
|
5428
|
+
}
|
|
5429
|
+
if (char === "{" || char === "[") {
|
|
5430
|
+
stack.push(char);
|
|
5431
|
+
continue;
|
|
5432
|
+
}
|
|
5433
|
+
if (char !== "}" && char !== "]") continue;
|
|
5434
|
+
const opening = stack.pop();
|
|
5435
|
+
if (char === "}" && opening !== "{" || char === "]" && opening !== "[") return null;
|
|
5436
|
+
if (stack.length === 0) return index + 1;
|
|
5437
|
+
}
|
|
5438
|
+
return null;
|
|
5439
|
+
}
|
|
5440
|
+
function declaredKind(candidate) {
|
|
5441
|
+
try {
|
|
5442
|
+
const parsed = JSON.parse(candidate);
|
|
5443
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
5444
|
+
const kind = parsed.__kind;
|
|
5445
|
+
return typeof kind === "string" && kind.trim() ? kind : null;
|
|
5446
|
+
} catch {
|
|
5447
|
+
return null;
|
|
5448
|
+
}
|
|
5449
|
+
}
|
|
5450
|
+
function isUnclosedGenericXmlOpening(source) {
|
|
5451
|
+
const text = source.trimStart();
|
|
5452
|
+
const prefix = /^<([A-Za-z_][\w.:-]*)(?=\s|\/|$)/.exec(text);
|
|
5453
|
+
if (!prefix || KNOWN_XML_TAG_NAMES.has(prefix[1]) || ALLOWED_RAW_HTML_TAGS.has(prefix[1].toLowerCase()))
|
|
5454
|
+
return false;
|
|
5455
|
+
let quote = null;
|
|
5456
|
+
for (let i = prefix[0].length; i < text.length; i++) {
|
|
5457
|
+
if (quote) {
|
|
5458
|
+
if (text[i] === quote) quote = null;
|
|
5459
|
+
} else if (text[i] === "'" || text[i] === '"') quote = text[i];
|
|
5460
|
+
else if (text[i] === ">") return false;
|
|
5461
|
+
}
|
|
5462
|
+
return true;
|
|
5463
|
+
}
|
|
5464
|
+
function jsonStringState(line, from, upTo, state) {
|
|
5465
|
+
let { inString, escaped } = state;
|
|
5466
|
+
for (let idx = from; idx < upTo; idx++) {
|
|
5467
|
+
const ch = line[idx];
|
|
5468
|
+
if (escaped) {
|
|
5469
|
+
escaped = false;
|
|
5470
|
+
continue;
|
|
5471
|
+
}
|
|
5472
|
+
if (ch === "\\" && inString) {
|
|
5473
|
+
escaped = true;
|
|
5474
|
+
continue;
|
|
5475
|
+
}
|
|
5476
|
+
if (ch === '"') inString = !inString;
|
|
5477
|
+
}
|
|
5478
|
+
return { inString, escaped };
|
|
5479
|
+
}
|
|
5480
|
+
function backtickRunLength(str2, pos) {
|
|
5481
|
+
let n = 0;
|
|
5482
|
+
while (pos + n < str2.length && str2[pos + n] === "`") n++;
|
|
5483
|
+
return n;
|
|
5484
|
+
}
|
|
5485
|
+
var MEDIA_REF_PREFIXES = ["[Image URL:", "[Video URL:", "[Audio URL:"];
|
|
5486
|
+
var Tokenizer = class {
|
|
5487
|
+
constructor(text) {
|
|
5488
|
+
this.text = text;
|
|
5489
|
+
this.n = text.length;
|
|
5490
|
+
let start = 0;
|
|
5491
|
+
for (let i = 0; i < text.length; i++) {
|
|
5492
|
+
if (text.charCodeAt(i) === 10) {
|
|
5493
|
+
this.lineStarts.push(start);
|
|
5494
|
+
this.contentEnds.push(i > start && text.charCodeAt(i - 1) === 13 ? i - 1 : i);
|
|
5495
|
+
this.lineEnds.push(i + 1);
|
|
5496
|
+
start = i + 1;
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
if (start < text.length || text.length === 0) {
|
|
5500
|
+
this.lineStarts.push(start);
|
|
5501
|
+
this.contentEnds.push(text.length);
|
|
5502
|
+
this.lineEnds.push(text.length);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
text;
|
|
5506
|
+
n;
|
|
5507
|
+
lineStarts = [];
|
|
5508
|
+
contentEnds = [];
|
|
5509
|
+
lineEnds = [];
|
|
5510
|
+
blocks = [];
|
|
5511
|
+
blockCache = /* @__PURE__ */ new Map();
|
|
5512
|
+
/** Lazily built: for line i, first line j >= i where brace net from i drops to <= 0. */
|
|
5513
|
+
braceStop = null;
|
|
5514
|
+
braceNet = null;
|
|
5515
|
+
get lineCount() {
|
|
5516
|
+
return this.lineStarts.length;
|
|
5517
|
+
}
|
|
5518
|
+
lineOf(pos) {
|
|
5519
|
+
let lo = 0;
|
|
5520
|
+
let hi = this.lineStarts.length - 1;
|
|
5521
|
+
while (lo < hi) {
|
|
5522
|
+
const mid = lo + hi + 1 >>> 1;
|
|
5523
|
+
if (this.lineStarts[mid] <= pos) lo = mid;
|
|
5524
|
+
else hi = mid - 1;
|
|
5525
|
+
}
|
|
5526
|
+
return lo;
|
|
5527
|
+
}
|
|
5528
|
+
isBlank(from, to) {
|
|
5529
|
+
return !NOT_WHITESPACE.test(this.text.slice(from, to));
|
|
5530
|
+
}
|
|
5531
|
+
lineIsBlank(li) {
|
|
5532
|
+
return this.isBlank(this.lineStarts[li], this.contentEnds[li]);
|
|
5533
|
+
}
|
|
5534
|
+
lineText(li) {
|
|
5535
|
+
return this.text.slice(this.lineStarts[li], this.contentEnds[li]);
|
|
5536
|
+
}
|
|
5537
|
+
run() {
|
|
5538
|
+
let pos = 0;
|
|
5539
|
+
while (pos < this.n) {
|
|
5540
|
+
const li = this.lineOf(pos);
|
|
5541
|
+
const ce = this.contentEnds[li];
|
|
5542
|
+
if (this.isBlank(pos, ce)) {
|
|
5543
|
+
pos = this.gap(pos, li);
|
|
5544
|
+
continue;
|
|
5545
|
+
}
|
|
5546
|
+
const island = this.detectBlock(pos, li, true);
|
|
5547
|
+
if (island) {
|
|
5548
|
+
this.push({ kind: "island", start: island.start, end: island.end, island, inlines: [] });
|
|
5549
|
+
pos = island.end;
|
|
5550
|
+
continue;
|
|
5551
|
+
}
|
|
5552
|
+
pos = this.paragraph(pos, li);
|
|
5553
|
+
}
|
|
5554
|
+
return this.blocks;
|
|
5555
|
+
}
|
|
5556
|
+
push(block) {
|
|
5557
|
+
if (block.end <= block.start) return;
|
|
5558
|
+
if (block.kind === "prose" && this.isBlank(block.start, block.end)) {
|
|
5559
|
+
block = { kind: "gap", start: block.start, end: block.end, island: null, inlines: [] };
|
|
5560
|
+
}
|
|
5561
|
+
const last = this.blocks[this.blocks.length - 1];
|
|
5562
|
+
if (block.kind === "gap" && last && last.kind === "gap" && last.end === block.start) {
|
|
5563
|
+
last.end = block.end;
|
|
5564
|
+
return;
|
|
5565
|
+
}
|
|
5566
|
+
this.blocks.push(block);
|
|
5567
|
+
}
|
|
5568
|
+
gap(pos, li) {
|
|
5569
|
+
let end = this.lineEnds[li];
|
|
5570
|
+
let k = li + 1;
|
|
5571
|
+
while (k < this.lineCount && this.lineIsBlank(k)) {
|
|
5572
|
+
end = this.lineEnds[k];
|
|
5573
|
+
k++;
|
|
5574
|
+
}
|
|
5575
|
+
this.push({ kind: "gap", start: pos, end, island: null, inlines: [] });
|
|
5576
|
+
return end;
|
|
5577
|
+
}
|
|
5578
|
+
// ── block detection ────────────────────────────────────────────────────
|
|
5579
|
+
detectBlock(p, li, atParaStart) {
|
|
5580
|
+
const cacheKey = atParaStart ? -1 - p : p;
|
|
5581
|
+
if (this.blockCache.has(cacheKey)) return this.blockCache.get(cacheKey) ?? null;
|
|
5582
|
+
const found = this.detectBlockUncached(p, li, atParaStart);
|
|
5583
|
+
this.blockCache.set(cacheKey, found);
|
|
5584
|
+
return found;
|
|
5585
|
+
}
|
|
5586
|
+
detectBlockUncached(p, li, atParaStart) {
|
|
5587
|
+
const ce = this.contentEnds[li];
|
|
5588
|
+
const seg = this.text.slice(p, ce);
|
|
5589
|
+
const t = seg.trimStart();
|
|
5590
|
+
const indent = seg.length - t.length;
|
|
5591
|
+
const tp = p + indent;
|
|
5592
|
+
if (p === 0) {
|
|
5593
|
+
const fm = this.frontMatter(li, t);
|
|
5594
|
+
if (fm) return fm;
|
|
5595
|
+
}
|
|
5596
|
+
if (t.startsWith("```")) return this.backtickFence(p, li, t);
|
|
5597
|
+
if (indent <= 3 && t.startsWith("~~~")) {
|
|
5598
|
+
const tilde = this.tildeFence(p, li, t);
|
|
5599
|
+
if (tilde) return tilde;
|
|
5600
|
+
}
|
|
5601
|
+
if (t.startsWith("$$")) {
|
|
5602
|
+
const math = this.mathBlock(p, tp);
|
|
5603
|
+
if (math) return math;
|
|
5604
|
+
}
|
|
5605
|
+
if (t.startsWith("<")) {
|
|
5606
|
+
const attr = this.attributeXml(p, tp, t);
|
|
5607
|
+
if (attr) return attr;
|
|
5608
|
+
const region = this.xmlRegion(p, tp, t);
|
|
5609
|
+
if (region) return region;
|
|
5610
|
+
const container = this.genericXml(p, li, seg, indent);
|
|
5611
|
+
if (container) return container;
|
|
5612
|
+
if (t.startsWith("<!--")) {
|
|
5613
|
+
const comment = this.blockComment(p, tp, li);
|
|
5614
|
+
if (comment) return comment;
|
|
5615
|
+
}
|
|
5616
|
+
const html = this.htmlBlock(p, li, t, tp, atParaStart);
|
|
5617
|
+
if (html) return html;
|
|
5618
|
+
}
|
|
5619
|
+
if (t.startsWith("{")) {
|
|
5620
|
+
const json = this.bareJson(p, li, tp);
|
|
5621
|
+
if (json) return json;
|
|
5622
|
+
}
|
|
5623
|
+
if (TREE_GLYPHS.test(t)) {
|
|
5624
|
+
const tree = this.tree(p, li);
|
|
5625
|
+
if (tree) return tree;
|
|
5626
|
+
}
|
|
5627
|
+
return null;
|
|
5628
|
+
}
|
|
5629
|
+
island(type, start, end, complete, meta = {}) {
|
|
5630
|
+
return { type, start, end, complete, meta };
|
|
5631
|
+
}
|
|
5632
|
+
frontMatter(li, t) {
|
|
5633
|
+
if (this.lineText(li) !== "---" || t !== "---") return null;
|
|
5634
|
+
let sawContent = false;
|
|
5635
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5636
|
+
const line = this.lineText(k);
|
|
5637
|
+
if (line === "---" || line === "...") {
|
|
5638
|
+
return sawContent ? this.island("front_matter", 0, this.contentEnds[k], true) : null;
|
|
5639
|
+
}
|
|
5640
|
+
if (!NOT_WHITESPACE.test(line)) continue;
|
|
5641
|
+
if (!/^(?:[A-Za-z0-9_"'-][^:]*:(?:\s|$)|\s+\S|-\s)/.test(line)) return null;
|
|
5642
|
+
sawContent = true;
|
|
5643
|
+
}
|
|
5644
|
+
return null;
|
|
5645
|
+
}
|
|
5646
|
+
/** Backtick fence — the splitter's close rules, JSON-string aware for ```json. */
|
|
5647
|
+
backtickFence(p, li, t) {
|
|
5648
|
+
const openTicks = backtickRunLength(t, 0);
|
|
5649
|
+
const info = t.slice(openTicks).trim();
|
|
5650
|
+
const lang = info.split(/\s/)[0] ?? "";
|
|
5651
|
+
const isJson = lang === "json";
|
|
5652
|
+
const normalized = normalizeFenceLanguage(lang);
|
|
5653
|
+
const meta = { fence: "`", ticks: openTicks, lang };
|
|
5654
|
+
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
5655
|
+
let state = { inString: false, escaped: false };
|
|
5656
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5657
|
+
const line = this.lineText(k);
|
|
5658
|
+
const trimmedLine = line.trim();
|
|
5659
|
+
if (trimmedLine.startsWith("```")) {
|
|
5660
|
+
const at2 = line.indexOf("```");
|
|
5661
|
+
if (isJson) {
|
|
5662
|
+
const probe = jsonStringState(line, 0, at2, state);
|
|
5663
|
+
if (probe.inString) {
|
|
5664
|
+
state = jsonStringState(line, at2, line.length, probe);
|
|
5665
|
+
continue;
|
|
5666
|
+
}
|
|
5667
|
+
}
|
|
5668
|
+
const closeTicks = backtickRunLength(trimmedLine, 0);
|
|
5669
|
+
const isBare = trimmedLine.slice(closeTicks).trim() === "";
|
|
5670
|
+
if (isBare && closeTicks >= openTicks) {
|
|
5671
|
+
return this.fenceIsland(p, this.contentEnds[k], true, meta, li, k, isJson);
|
|
5672
|
+
}
|
|
5673
|
+
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
5674
|
+
continue;
|
|
5675
|
+
}
|
|
5676
|
+
const at = line.indexOf("```");
|
|
5677
|
+
if (at !== -1) {
|
|
5678
|
+
if (isJson) {
|
|
5679
|
+
const probe = jsonStringState(line, 0, at, state);
|
|
5680
|
+
if (probe.inString) {
|
|
5681
|
+
state = jsonStringState(line, at, line.length, probe);
|
|
5682
|
+
continue;
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
const closeTicks = backtickRunLength(line, at);
|
|
5686
|
+
const after = line.slice(at + closeTicks).trim();
|
|
5687
|
+
if (closeTicks >= openTicks && after === "") {
|
|
5688
|
+
return this.fenceIsland(p, this.contentEnds[k], true, meta, li, k, isJson);
|
|
5689
|
+
}
|
|
5690
|
+
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
5691
|
+
continue;
|
|
5692
|
+
}
|
|
5693
|
+
if (isJson) state = jsonStringState(line, 0, line.length, state);
|
|
5694
|
+
}
|
|
5695
|
+
return this.fenceIsland(p, this.n, false, meta, li, this.lineCount, isJson);
|
|
5696
|
+
}
|
|
5697
|
+
fenceIsland(p, end, complete, meta, openLine, closeLine, isJson) {
|
|
5698
|
+
if (isJson && openLine + 1 < this.lineCount) {
|
|
5699
|
+
const bodyEnd = closeLine < this.lineCount ? this.lineStarts[closeLine] : this.n;
|
|
5700
|
+
const body = this.text.slice(this.lineStarts[openLine + 1], Math.max(bodyEnd, this.lineStarts[openLine + 1]));
|
|
5701
|
+
if (body.trimStart().startsWith("{")) Object.assign(meta, jsonMeta(body));
|
|
5702
|
+
}
|
|
5703
|
+
return this.island("fence", p, end, complete, meta);
|
|
5704
|
+
}
|
|
5705
|
+
tildeFence(p, li, t) {
|
|
5706
|
+
const open = /^(~{3,})(.*)$/.exec(t);
|
|
5707
|
+
if (!open) return null;
|
|
5708
|
+
const ticks = open[1].length;
|
|
5709
|
+
const lang = open[2].trim().split(/\s/)[0] ?? "";
|
|
5710
|
+
const meta = { fence: "~", ticks, lang };
|
|
5711
|
+
const normalized = normalizeFenceLanguage(lang);
|
|
5712
|
+
if (normalized && SPECIAL_CODE_LANGUAGES.has(normalized)) meta.special = normalized;
|
|
5713
|
+
const close = new RegExp(`^ {0,3}~{${ticks},}[ \\t]*$`);
|
|
5714
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5715
|
+
if (close.test(this.lineText(k))) {
|
|
5716
|
+
return this.island("fence", p, this.contentEnds[k], true, meta);
|
|
5717
|
+
}
|
|
5718
|
+
}
|
|
5719
|
+
return this.island("fence", p, this.n, false, meta);
|
|
5720
|
+
}
|
|
5721
|
+
mathBlock(p, tp) {
|
|
5722
|
+
const li = this.lineOf(tp);
|
|
5723
|
+
const ce = this.contentEnds[li];
|
|
5724
|
+
const sameLine = this.text.indexOf("$$", tp + 2);
|
|
5725
|
+
if (sameLine !== -1 && sameLine + 2 <= ce) {
|
|
5726
|
+
if (!this.isBlank(sameLine + 2, ce)) return null;
|
|
5727
|
+
if (sameLine === tp + 2) return null;
|
|
5728
|
+
return this.island("math_block", p, sameLine + 2, true, { delimiter: "$$" });
|
|
5729
|
+
}
|
|
5730
|
+
const close = this.text.indexOf("$$", tp + 2);
|
|
5731
|
+
if (close === -1) return this.island("math_block", p, this.n, false, { delimiter: "$$" });
|
|
5732
|
+
return this.island("math_block", p, close + 2, true, { delimiter: "$$" });
|
|
5733
|
+
}
|
|
5734
|
+
/** `<artifact …>`, `<decision …>`, … at the start of a line. */
|
|
5735
|
+
attributeXml(p, tp, t) {
|
|
5736
|
+
for (const name of ATTRIBUTE_XML_NAMES) {
|
|
5737
|
+
const prefix = `<${name}`;
|
|
5738
|
+
if (!t.startsWith(prefix)) continue;
|
|
5739
|
+
const next = t[prefix.length];
|
|
5740
|
+
if (next !== " " && next !== ">" && next !== "/" && next !== " ") continue;
|
|
5741
|
+
return this.attributeElement(name, p, tp, "xml_attr");
|
|
5742
|
+
}
|
|
5743
|
+
return null;
|
|
5744
|
+
}
|
|
5745
|
+
/** Opening tag at `tp` through its `</name>` (or to EOF when still streaming). */
|
|
5746
|
+
attributeElement(name, p, tp, type) {
|
|
5747
|
+
const li = this.lineOf(tp);
|
|
5748
|
+
const ce = this.contentEnds[li];
|
|
5749
|
+
const tag = readXmlTag(this.text, tp);
|
|
5750
|
+
let openEnd;
|
|
5751
|
+
if (tag && !tag.isClosing) {
|
|
5752
|
+
if (tag.isSelfClosing) {
|
|
5753
|
+
return this.island(type, p, tp + tag.raw.length, true, { tag: name, selfClosing: true });
|
|
5754
|
+
}
|
|
5755
|
+
openEnd = tp + tag.raw.length;
|
|
5756
|
+
} else {
|
|
5757
|
+
const gt = this.text.indexOf(">", tp);
|
|
5758
|
+
if (gt === -1) return this.island(type, p, this.n, false, { tag: name });
|
|
5759
|
+
if (gt >= ce) return null;
|
|
5760
|
+
openEnd = gt + 1;
|
|
5761
|
+
}
|
|
5762
|
+
const closer = `</${name}>`;
|
|
5763
|
+
const close = this.text.indexOf(closer, openEnd);
|
|
5764
|
+
if (close === -1) return this.island(type, p, this.n, false, { tag: name });
|
|
5765
|
+
return this.island(type, p, close + closer.length, true, { tag: name });
|
|
5766
|
+
}
|
|
5767
|
+
/** `<thinking>`, `<flashcards>`, … — exact attribute-less openers. */
|
|
5768
|
+
xmlRegion(p, tp, t) {
|
|
5769
|
+
for (const name of XML_TAG_BLOCK_NAMES) {
|
|
5770
|
+
const opener = `<${name}>`;
|
|
5771
|
+
if (!t.startsWith(opener)) continue;
|
|
5772
|
+
const closer = `</${name}>`;
|
|
5773
|
+
const close = this.text.indexOf(closer, tp + opener.length);
|
|
5774
|
+
if (close === -1) return this.island("xml_region", p, this.n, false, { tag: name });
|
|
5775
|
+
return this.island("xml_region", p, close + closer.length, true, { tag: name });
|
|
5776
|
+
}
|
|
5777
|
+
return null;
|
|
5778
|
+
}
|
|
5779
|
+
/** Line-leading unknown XML element — balanced by the renderer's own tracker. */
|
|
5780
|
+
genericXml(p, li, seg, indent) {
|
|
5781
|
+
const first = seg.slice(indent);
|
|
5782
|
+
const opening = readXmlTag(first, 0);
|
|
5783
|
+
if (opening && !opening.isClosing) {
|
|
5784
|
+
const root = opening.tagName;
|
|
5785
|
+
if (KNOWN_XML_TAG_NAMES.has(root) || ALLOWED_RAW_HTML_TAGS.has(root.toLowerCase())) {
|
|
5786
|
+
return null;
|
|
5787
|
+
}
|
|
5788
|
+
const tracker = new XmlContainerTracker(root);
|
|
5789
|
+
const firstEnd = tracker.consumeLine(seg, indent);
|
|
5790
|
+
if (firstEnd !== null) {
|
|
5791
|
+
return this.island("xml_container", p, p + firstEnd, true, { tag: root });
|
|
5792
|
+
}
|
|
5793
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5794
|
+
const end = tracker.consumeLine(this.lineText(k));
|
|
5795
|
+
if (end !== null) {
|
|
5796
|
+
return this.island("xml_container", p, this.lineStarts[k] + end, true, {
|
|
5797
|
+
tag: root
|
|
5798
|
+
});
|
|
5799
|
+
}
|
|
5800
|
+
}
|
|
5801
|
+
return this.island("xml_container", p, this.n, false, { tag: root });
|
|
5802
|
+
}
|
|
5803
|
+
if (isUnclosedGenericXmlOpening(seg)) {
|
|
5804
|
+
return this.island("xml_container", p, this.n, false, {});
|
|
5805
|
+
}
|
|
5806
|
+
return null;
|
|
5807
|
+
}
|
|
5808
|
+
blockComment(p, tp, li) {
|
|
5809
|
+
const close = this.text.indexOf("-->", tp + 4);
|
|
5810
|
+
if (close === -1) return this.island("html_comment", p, this.n, false);
|
|
5811
|
+
const end = close + 3;
|
|
5812
|
+
const raw = this.text.slice(tp, end);
|
|
5813
|
+
const anchor = PINNED_ANCHOR_RE.exec(raw);
|
|
5814
|
+
const ce = this.contentEnds[li];
|
|
5815
|
+
if (anchor) {
|
|
5816
|
+
if (end <= ce && !this.isBlank(end, ce)) return null;
|
|
5817
|
+
return this.island("anchor", p, end, true, { id: anchor[1] });
|
|
5818
|
+
}
|
|
5819
|
+
return this.island("html_comment", p, end, true);
|
|
5820
|
+
}
|
|
5821
|
+
htmlBlock(p, li, t, tp, atParaStart) {
|
|
5822
|
+
const tag = readXmlTag(t, 0);
|
|
5823
|
+
if (!tag) return null;
|
|
5824
|
+
const name = tag.tagName.toLowerCase();
|
|
5825
|
+
if (!ALLOWED_RAW_HTML_TAGS.has(name)) return null;
|
|
5826
|
+
if (name === "pre" && !tag.isClosing) {
|
|
5827
|
+
const closer = /<\/pre\s*>/gi;
|
|
5828
|
+
closer.lastIndex = tp;
|
|
5829
|
+
const close = closer.exec(this.text);
|
|
5830
|
+
if (!close) return this.island("html_block", p, this.n, false, { tag: name });
|
|
5831
|
+
return this.island("html_block", p, close.index + close[0].length, true, { tag: name });
|
|
5832
|
+
}
|
|
5833
|
+
const typeSix = HTML_BLOCK_LEVEL_TAGS.has(name);
|
|
5834
|
+
const typeSeven = atParaStart && t.trimEnd() === tag.raw;
|
|
5835
|
+
if (!typeSix && !typeSeven) return null;
|
|
5836
|
+
let end = this.contentEnds[li];
|
|
5837
|
+
for (let k = li + 1; k < this.lineCount && !this.lineIsBlank(k); k++) {
|
|
5838
|
+
end = this.contentEnds[k];
|
|
5839
|
+
}
|
|
5840
|
+
return this.island("html_block", p, end, true, { tag: name });
|
|
5841
|
+
}
|
|
5842
|
+
buildBraceTables() {
|
|
5843
|
+
const count = this.lineCount;
|
|
5844
|
+
const prefix = new Int32Array(count + 1);
|
|
5845
|
+
for (let i = 0; i < count; i++) {
|
|
5846
|
+
const { opens, closes } = countStructuralBraces(this.lineText(i));
|
|
5847
|
+
prefix[i + 1] = prefix[i] + opens - closes;
|
|
5848
|
+
}
|
|
5849
|
+
const stop = new Int32Array(count).fill(-1);
|
|
5850
|
+
const stack = [];
|
|
5851
|
+
for (let m = 0; m <= count; m++) {
|
|
5852
|
+
const value = prefix[m];
|
|
5853
|
+
while (stack.length && prefix[stack[stack.length - 1]] >= value) {
|
|
5854
|
+
const i = stack.pop();
|
|
5855
|
+
if (i < count) stop[i] = m - 1;
|
|
5856
|
+
}
|
|
5857
|
+
stack.push(m);
|
|
5858
|
+
}
|
|
5859
|
+
this.braceNet = prefix;
|
|
5860
|
+
this.braceStop = stop;
|
|
5861
|
+
}
|
|
5862
|
+
/** A line-leading bare JSON object — the splitter's structural brace walk. */
|
|
5863
|
+
bareJson(p, li, tp) {
|
|
5864
|
+
let lastLine = null;
|
|
5865
|
+
let net = 0;
|
|
5866
|
+
if (p === this.lineStarts[li]) {
|
|
5867
|
+
if (!this.braceStop || !this.braceNet) this.buildBraceTables();
|
|
5868
|
+
const stop = this.braceStop[li];
|
|
5869
|
+
if (stop !== -1) {
|
|
5870
|
+
lastLine = stop;
|
|
5871
|
+
net = this.braceNet[stop + 1] - this.braceNet[li];
|
|
5872
|
+
}
|
|
5873
|
+
} else {
|
|
5874
|
+
let running = 0;
|
|
5875
|
+
for (let k = li; k < this.lineCount; k++) {
|
|
5876
|
+
const line = k === li ? this.text.slice(tp, this.contentEnds[li]) : this.lineText(k);
|
|
5877
|
+
const { opens, closes } = countStructuralBraces(line);
|
|
5878
|
+
running += opens - closes;
|
|
5879
|
+
if (running <= 0) {
|
|
5880
|
+
lastLine = k;
|
|
5881
|
+
net = running;
|
|
5882
|
+
break;
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
if (lastLine === null) {
|
|
5887
|
+
const partial = this.text.slice(tp);
|
|
5888
|
+
if (isRecognizedJsonHead(partial)) {
|
|
5889
|
+
return this.island("json", p, this.n, false, jsonMeta(partial));
|
|
5890
|
+
}
|
|
5891
|
+
return null;
|
|
5892
|
+
}
|
|
5893
|
+
if (net !== 0) return null;
|
|
5894
|
+
const end = this.contentEnds[lastLine];
|
|
5895
|
+
const content = this.text.slice(tp, end).trim();
|
|
5896
|
+
try {
|
|
5897
|
+
const parsed = JSON.parse(content);
|
|
5898
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
5899
|
+
} catch {
|
|
5900
|
+
return null;
|
|
5901
|
+
}
|
|
5902
|
+
return this.island("json", p, end, true, jsonMeta(content));
|
|
5903
|
+
}
|
|
5904
|
+
tree(p, li) {
|
|
5905
|
+
let treeLines = 0;
|
|
5906
|
+
let lastTree = li;
|
|
5907
|
+
let k = li;
|
|
5908
|
+
while (k < this.lineCount) {
|
|
5909
|
+
if (this.lineIsBlank(k)) {
|
|
5910
|
+
k++;
|
|
5911
|
+
continue;
|
|
5912
|
+
}
|
|
5913
|
+
if (!TREE_GLYPHS.test(this.lineText(k))) break;
|
|
5914
|
+
treeLines++;
|
|
5915
|
+
lastTree = k;
|
|
5916
|
+
k++;
|
|
5917
|
+
}
|
|
5918
|
+
if (treeLines < 3) return null;
|
|
5919
|
+
return this.island("tree", p, this.contentEnds[lastTree], true);
|
|
5920
|
+
}
|
|
5921
|
+
// ── prose ──────────────────────────────────────────────────────────────
|
|
5922
|
+
paragraph(pos, li) {
|
|
5923
|
+
let end = this.contentEnds[li];
|
|
5924
|
+
for (let k = li + 1; k < this.lineCount; k++) {
|
|
5925
|
+
if (this.lineIsBlank(k)) break;
|
|
5926
|
+
if (this.detectBlock(this.lineStarts[k], k, false)) break;
|
|
5927
|
+
end = this.contentEnds[k];
|
|
5928
|
+
}
|
|
5929
|
+
const scan = this.scanInline(pos, end);
|
|
5930
|
+
if (!scan.breakIsland) {
|
|
5931
|
+
this.push({ kind: "prose", start: pos, end, island: null, inlines: scan.inlines });
|
|
5932
|
+
return end;
|
|
5933
|
+
}
|
|
5934
|
+
const br = scan.breakIsland;
|
|
5935
|
+
if (scan.orphan) {
|
|
5936
|
+
let start = pos;
|
|
5937
|
+
while (this.blocks.length) {
|
|
5938
|
+
const last = this.blocks[this.blocks.length - 1];
|
|
5939
|
+
if (last.kind === "island") break;
|
|
5940
|
+
this.blocks.pop();
|
|
5941
|
+
start = last.start;
|
|
5942
|
+
}
|
|
5943
|
+
const island = { ...br, start };
|
|
5944
|
+
this.push({ kind: "island", start, end: island.end, island, inlines: [] });
|
|
5945
|
+
return island.end;
|
|
5946
|
+
}
|
|
5947
|
+
this.push({
|
|
5948
|
+
kind: "prose",
|
|
5949
|
+
start: pos,
|
|
5950
|
+
end: br.start,
|
|
5951
|
+
island: null,
|
|
5952
|
+
inlines: scan.inlines.filter((inline) => inline.end <= br.start)
|
|
5953
|
+
});
|
|
5954
|
+
this.push({ kind: "island", start: br.start, end: br.end, island: br, inlines: [] });
|
|
5955
|
+
return br.end;
|
|
5956
|
+
}
|
|
5957
|
+
scanInline(ps, pe) {
|
|
5958
|
+
const text = this.text;
|
|
5959
|
+
const inlines = [];
|
|
5960
|
+
const hasKind = text.slice(ps, pe).includes("__kind");
|
|
5961
|
+
const stop = (island, orphan = false) => ({ inlines, breakIsland: island, orphan });
|
|
5962
|
+
let i = ps;
|
|
5963
|
+
while (i < pe) {
|
|
5964
|
+
const ch = text[i];
|
|
5965
|
+
if (ch === "\\") {
|
|
5966
|
+
const next = text[i + 1];
|
|
5967
|
+
if (next === "(" || next === "[") {
|
|
5968
|
+
const closer = next === "(" ? "\\)" : "\\]";
|
|
5969
|
+
const close = text.indexOf(closer, i + 2);
|
|
5970
|
+
if (close !== -1 && close + 2 <= pe) {
|
|
5971
|
+
inlines.push(this.island("math_inline", i, close + 2, true, { delimiter: next === "(" ? "\\(" : "\\[" }));
|
|
5972
|
+
i = close + 2;
|
|
5973
|
+
continue;
|
|
5974
|
+
}
|
|
5975
|
+
}
|
|
5976
|
+
i += 2;
|
|
5977
|
+
continue;
|
|
5978
|
+
}
|
|
5979
|
+
if (ch === "`") {
|
|
5980
|
+
const n = backtickRunLength(text, i);
|
|
5981
|
+
const run = "`".repeat(n);
|
|
5982
|
+
let k = i + n;
|
|
5983
|
+
let close = -1;
|
|
5984
|
+
while (k < pe) {
|
|
5985
|
+
const idx = text.indexOf(run, k);
|
|
5986
|
+
if (idx === -1 || idx >= pe) break;
|
|
5987
|
+
if (text[idx + n] === "`") {
|
|
5988
|
+
let m = idx;
|
|
5989
|
+
while (text[m] === "`") m++;
|
|
5990
|
+
k = m;
|
|
5991
|
+
continue;
|
|
5992
|
+
}
|
|
5993
|
+
close = idx;
|
|
5994
|
+
break;
|
|
5995
|
+
}
|
|
5996
|
+
i = close === -1 ? i + n : close + n;
|
|
5997
|
+
continue;
|
|
5998
|
+
}
|
|
5999
|
+
if (ch === "$") {
|
|
6000
|
+
if (text[i + 1] === "$") {
|
|
6001
|
+
const close = text.indexOf("$$", i + 2);
|
|
6002
|
+
if (close !== -1 && close + 2 <= pe && close > i + 2) {
|
|
6003
|
+
inlines.push(this.island("math_inline", i, close + 2, true, { delimiter: "$$" }));
|
|
6004
|
+
i = close + 2;
|
|
6005
|
+
} else {
|
|
6006
|
+
i += 2;
|
|
6007
|
+
}
|
|
6008
|
+
continue;
|
|
6009
|
+
}
|
|
6010
|
+
const end = singleDollarMathEnd(text, i, pe);
|
|
6011
|
+
if (end > 0) {
|
|
6012
|
+
inlines.push(this.island("math_inline", i, end, true, { delimiter: "$" }));
|
|
6013
|
+
i = end;
|
|
6014
|
+
continue;
|
|
6015
|
+
}
|
|
6016
|
+
i++;
|
|
6017
|
+
continue;
|
|
6018
|
+
}
|
|
6019
|
+
if (ch === "{") {
|
|
6020
|
+
if (text[i + 1] === "{") {
|
|
6021
|
+
const close = text.indexOf("}}", i + 2);
|
|
6022
|
+
if (close !== -1 && close + 2 <= pe && close - i <= 200 && !text.slice(i, close).includes("\n")) {
|
|
6023
|
+
const raw = text.slice(i, close + 2);
|
|
6024
|
+
const strict = STRICT_VARIABLE_RE.exec(raw);
|
|
6025
|
+
inlines.push(
|
|
6026
|
+
this.island("variable", i, close + 2, true, strict ? { name: strict[1] } : { template: true })
|
|
6027
|
+
);
|
|
6028
|
+
i = close + 2;
|
|
6029
|
+
continue;
|
|
6030
|
+
}
|
|
6031
|
+
i += 2;
|
|
6032
|
+
continue;
|
|
6033
|
+
}
|
|
6034
|
+
if (hasKind) {
|
|
6035
|
+
const end = matchingJsonObjectEnd(text, i, pe);
|
|
6036
|
+
if (end !== null) {
|
|
6037
|
+
const kind = declaredKind(text.slice(i, end));
|
|
6038
|
+
if (kind) {
|
|
6039
|
+
inlines.push(this.island("kind_json", i, end, true, jsonMeta(text.slice(i, end))));
|
|
6040
|
+
i = end;
|
|
6041
|
+
continue;
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
i++;
|
|
6046
|
+
continue;
|
|
6047
|
+
}
|
|
6048
|
+
if (ch === "[") {
|
|
6049
|
+
const media = MEDIA_REF_PREFIXES.find((prefix) => text.startsWith(prefix, i));
|
|
6050
|
+
if (media) {
|
|
6051
|
+
const close = text.indexOf("]", i + media.length);
|
|
6052
|
+
if (close !== -1 && close < pe && !text.slice(i, close).includes("\n")) {
|
|
6053
|
+
inlines.push(this.island("media_ref", i, close + 1, true, { media: media.slice(1, 6).toLowerCase() }));
|
|
6054
|
+
i = close + 1;
|
|
6055
|
+
continue;
|
|
6056
|
+
}
|
|
6057
|
+
}
|
|
6058
|
+
i++;
|
|
6059
|
+
continue;
|
|
6060
|
+
}
|
|
6061
|
+
if (ch === "<") {
|
|
6062
|
+
if (text.startsWith("<!--", i)) {
|
|
6063
|
+
const close = text.indexOf("-->", i + 4);
|
|
6064
|
+
if (close === -1) return stop(this.island("html_comment", i, this.n, false));
|
|
6065
|
+
const end = close + 3;
|
|
6066
|
+
const anchor = PINNED_ANCHOR_RE.exec(text.slice(i, end));
|
|
6067
|
+
const type = anchor ? "anchor" : "html_comment";
|
|
6068
|
+
const meta = anchor ? { id: anchor[1] } : {};
|
|
6069
|
+
if (end > pe) return stop(this.island(type, i, end, true, meta));
|
|
6070
|
+
inlines.push(this.island(type, i, end, true, meta));
|
|
6071
|
+
i = end;
|
|
6072
|
+
continue;
|
|
6073
|
+
}
|
|
6074
|
+
const cite = CITE_RE.exec(text.slice(i, i + 64));
|
|
6075
|
+
if (cite) {
|
|
6076
|
+
inlines.push(this.island("cite", i, i + cite[0].length, true, { n: Number(cite[1]) }));
|
|
6077
|
+
i += cite[0].length;
|
|
6078
|
+
continue;
|
|
6079
|
+
}
|
|
6080
|
+
const tag = readXmlTag(text, i);
|
|
6081
|
+
if (tag && i + tag.raw.length <= pe) {
|
|
6082
|
+
const name = tag.tagName;
|
|
6083
|
+
const tagEnd = i + tag.raw.length;
|
|
6084
|
+
const isAttr = ATTRIBUTE_XML_NAMES.includes(name);
|
|
6085
|
+
const isThinking = THINKING_FAMILY.includes(name) && tag.raw === `<${name}>`;
|
|
6086
|
+
if (!tag.isClosing && (isAttr || isThinking)) {
|
|
6087
|
+
if (tag.isSelfClosing) {
|
|
6088
|
+
inlines.push(this.island("xml_inline", i, tagEnd, true, { tag: name, selfClosing: true }));
|
|
6089
|
+
i = tagEnd;
|
|
6090
|
+
continue;
|
|
6091
|
+
}
|
|
6092
|
+
const closer = `</${name}>`;
|
|
6093
|
+
const close = text.indexOf(closer, tagEnd);
|
|
6094
|
+
const blockType = isAttr ? "xml_attr" : "xml_region";
|
|
6095
|
+
if (close === -1) return stop(this.island(blockType, i, this.n, false, { tag: name }));
|
|
6096
|
+
const end = close + closer.length;
|
|
6097
|
+
if (end > pe) return stop(this.island(blockType, i, end, true, { tag: name }));
|
|
6098
|
+
inlines.push(this.island("xml_inline", i, end, true, { tag: name }));
|
|
6099
|
+
i = end;
|
|
6100
|
+
continue;
|
|
6101
|
+
}
|
|
6102
|
+
if (tag.isClosing && THINKING_FAMILY.includes(name)) {
|
|
6103
|
+
return stop(this.island("xml_region", i, tagEnd, true, { tag: name, continuation: true }), true);
|
|
6104
|
+
}
|
|
6105
|
+
const lower = name.toLowerCase();
|
|
6106
|
+
inlines.push(
|
|
6107
|
+
this.island(ALLOWED_RAW_HTML_TAGS.has(lower) ? "html_tag" : "xml_tag", i, tagEnd, true, {
|
|
6108
|
+
tag: name
|
|
6109
|
+
})
|
|
6110
|
+
);
|
|
6111
|
+
i = tagEnd;
|
|
6112
|
+
continue;
|
|
6113
|
+
}
|
|
6114
|
+
const pending = ATTRIBUTE_XML_NAMES.find(
|
|
6115
|
+
(name) => text.startsWith(`<${name}`, i) && /\s/.test(text[i + name.length + 1] ?? "")
|
|
6116
|
+
);
|
|
6117
|
+
if (pending && text.indexOf(">", i) === -1) {
|
|
6118
|
+
return stop(this.island("xml_attr", i, this.n, false, { tag: pending }));
|
|
6119
|
+
}
|
|
6120
|
+
i++;
|
|
6121
|
+
continue;
|
|
6122
|
+
}
|
|
6123
|
+
i++;
|
|
6124
|
+
}
|
|
6125
|
+
return { inlines, breakIsland: null, orphan: false };
|
|
6126
|
+
}
|
|
6127
|
+
};
|
|
6128
|
+
function tokenizeSource(text) {
|
|
6129
|
+
const raw = new Tokenizer(text).run();
|
|
6130
|
+
const cp = buildCodePointIndex(text);
|
|
6131
|
+
const inline = (island) => ({
|
|
6132
|
+
islandType: island.type,
|
|
6133
|
+
inline: true,
|
|
6134
|
+
complete: island.complete,
|
|
6135
|
+
meta: island.meta,
|
|
6136
|
+
start: island.start,
|
|
6137
|
+
end: island.end,
|
|
6138
|
+
startCp: cp.toCodePoint(island.start),
|
|
6139
|
+
endCp: cp.toCodePoint(island.end),
|
|
6140
|
+
raw: text.slice(island.start, island.end)
|
|
6141
|
+
});
|
|
6142
|
+
return raw.map(
|
|
6143
|
+
(block) => ({
|
|
6144
|
+
kind: block.kind,
|
|
6145
|
+
islandType: block.island?.type ?? null,
|
|
6146
|
+
complete: block.island ? block.island.complete : true,
|
|
6147
|
+
meta: block.island?.meta ?? {},
|
|
6148
|
+
inlines: block.inlines.map(inline),
|
|
6149
|
+
start: block.start,
|
|
6150
|
+
end: block.end,
|
|
6151
|
+
startCp: cp.toCodePoint(block.start),
|
|
6152
|
+
endCp: cp.toCodePoint(block.end),
|
|
6153
|
+
raw: text.slice(block.start, block.end)
|
|
6154
|
+
})
|
|
6155
|
+
);
|
|
6156
|
+
}
|
|
6157
|
+
function joinSource(blocks) {
|
|
6158
|
+
let out = "";
|
|
6159
|
+
for (const block of blocks) out += block.raw;
|
|
6160
|
+
return out;
|
|
6161
|
+
}
|
|
6162
|
+
function listIslands(blocks) {
|
|
6163
|
+
const out = [];
|
|
6164
|
+
for (const block of blocks) {
|
|
6165
|
+
if (block.kind === "island" && block.islandType) {
|
|
6166
|
+
out.push({
|
|
6167
|
+
islandType: block.islandType,
|
|
6168
|
+
inline: false,
|
|
6169
|
+
complete: block.complete,
|
|
6170
|
+
meta: block.meta,
|
|
6171
|
+
start: block.start,
|
|
6172
|
+
end: block.end,
|
|
6173
|
+
startCp: block.startCp,
|
|
6174
|
+
endCp: block.endCp,
|
|
6175
|
+
raw: block.raw
|
|
6176
|
+
});
|
|
6177
|
+
} else {
|
|
6178
|
+
out.push(...block.inlines);
|
|
6179
|
+
}
|
|
6180
|
+
}
|
|
6181
|
+
return out;
|
|
6182
|
+
}
|
|
6183
|
+
function blockAt(blocks, offset) {
|
|
6184
|
+
let lo = 0;
|
|
6185
|
+
let hi = blocks.length - 1;
|
|
6186
|
+
while (lo <= hi) {
|
|
6187
|
+
const mid = lo + hi >>> 1;
|
|
6188
|
+
const block = blocks[mid];
|
|
6189
|
+
if (offset < block.start) hi = mid - 1;
|
|
6190
|
+
else if (offset >= block.end) lo = mid + 1;
|
|
6191
|
+
else return block;
|
|
6192
|
+
}
|
|
6193
|
+
return null;
|
|
6194
|
+
}
|
|
6195
|
+
|
|
6196
|
+
// source/splice.ts
|
|
6197
|
+
var SourceSpliceError = class extends Error {
|
|
6198
|
+
constructor(code, message) {
|
|
6199
|
+
super(message);
|
|
6200
|
+
this.code = code;
|
|
6201
|
+
this.name = "SourceSpliceError";
|
|
6202
|
+
}
|
|
6203
|
+
code;
|
|
6204
|
+
};
|
|
6205
|
+
function blockEdit(block, text) {
|
|
6206
|
+
return { start: block.start, end: block.end, text };
|
|
6207
|
+
}
|
|
6208
|
+
function blockRangeEdit(first, last, text) {
|
|
6209
|
+
return { start: first.start, end: last.end, text };
|
|
6210
|
+
}
|
|
6211
|
+
function commonPrefix(a, b) {
|
|
6212
|
+
const max = Math.min(a.length, b.length);
|
|
6213
|
+
let i = 0;
|
|
6214
|
+
while (i < max && a.charCodeAt(i) === b.charCodeAt(i)) i++;
|
|
6215
|
+
if (i > 0 && (splitsSurrogatePair(a, i) || splitsSurrogatePair(b, i))) i--;
|
|
6216
|
+
return i;
|
|
6217
|
+
}
|
|
6218
|
+
function commonSuffix(a, b, prefix) {
|
|
6219
|
+
const max = Math.min(a.length, b.length) - prefix;
|
|
6220
|
+
let i = 0;
|
|
6221
|
+
while (i < max && a.charCodeAt(a.length - 1 - i) === b.charCodeAt(b.length - 1 - i)) i++;
|
|
6222
|
+
if (i > 0 && (splitsSurrogatePair(a, a.length - i) || splitsSurrogatePair(b, b.length - i))) i--;
|
|
6223
|
+
return i;
|
|
6224
|
+
}
|
|
6225
|
+
function spliceSave(original, edits, options = {}) {
|
|
6226
|
+
const blocks = options.blocks ?? tokenizeSource(original);
|
|
6227
|
+
if (options.blocks && joinSource(blocks) !== original) {
|
|
6228
|
+
throw new SourceSpliceError("stale_blocks", "the blocks passed in were not cut from this original text");
|
|
6229
|
+
}
|
|
6230
|
+
const boundaries = /* @__PURE__ */ new Set([0, original.length]);
|
|
6231
|
+
for (const block of blocks) {
|
|
6232
|
+
boundaries.add(block.start);
|
|
6233
|
+
boundaries.add(block.end);
|
|
6234
|
+
}
|
|
6235
|
+
const sorted = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
6236
|
+
let previousEnd = -1;
|
|
6237
|
+
for (const edit of sorted) {
|
|
6238
|
+
if (edit.start < 0 || edit.end > original.length || edit.start > edit.end) {
|
|
6239
|
+
throw new SourceSpliceError("out_of_range", `edit [${edit.start}, ${edit.end}) is outside the text`);
|
|
6240
|
+
}
|
|
6241
|
+
if (!boundaries.has(edit.start) || !boundaries.has(edit.end)) {
|
|
6242
|
+
throw new SourceSpliceError(
|
|
6243
|
+
"misaligned",
|
|
6244
|
+
`edit [${edit.start}, ${edit.end}) does not start and end on block boundaries`
|
|
6245
|
+
);
|
|
6246
|
+
}
|
|
6247
|
+
if (edit.start < previousEnd) {
|
|
6248
|
+
throw new SourceSpliceError("overlap", `edit [${edit.start}, ${edit.end}) overlaps the previous edit`);
|
|
6249
|
+
}
|
|
6250
|
+
previousEnd = edit.end;
|
|
6251
|
+
}
|
|
6252
|
+
const effective = [];
|
|
6253
|
+
for (const edit of sorted) {
|
|
6254
|
+
const old = original.slice(edit.start, edit.end);
|
|
6255
|
+
if (old === edit.text) continue;
|
|
6256
|
+
const prefix = commonPrefix(old, edit.text);
|
|
6257
|
+
const suffix = commonSuffix(old, edit.text, prefix);
|
|
6258
|
+
effective.push({
|
|
6259
|
+
blockStart: edit.start,
|
|
6260
|
+
blockEnd: edit.end,
|
|
6261
|
+
oldStart: edit.start + prefix,
|
|
6262
|
+
oldEnd: edit.end - suffix,
|
|
6263
|
+
replacement: edit.text.slice(prefix, edit.text.length - suffix)
|
|
6264
|
+
});
|
|
6265
|
+
}
|
|
6266
|
+
if (effective.length === 0) {
|
|
6267
|
+
return {
|
|
6268
|
+
text: original,
|
|
6269
|
+
changed: false,
|
|
6270
|
+
changes: [],
|
|
6271
|
+
integrity: { ok: true, bytesOutsideEditsIdentical: true, disturbed: [] },
|
|
6272
|
+
blocks
|
|
6273
|
+
};
|
|
6274
|
+
}
|
|
6275
|
+
let text = "";
|
|
6276
|
+
let cursor = 0;
|
|
6277
|
+
let delta = 0;
|
|
6278
|
+
const pending = [];
|
|
6279
|
+
for (const edit of effective) {
|
|
6280
|
+
text += original.slice(cursor, edit.oldStart);
|
|
6281
|
+
const newStart = edit.oldStart + delta;
|
|
6282
|
+
text += edit.replacement;
|
|
6283
|
+
const newEnd = newStart + edit.replacement.length;
|
|
6284
|
+
pending.push({
|
|
6285
|
+
blockStart: edit.blockStart,
|
|
6286
|
+
blockEnd: edit.blockEnd,
|
|
6287
|
+
oldStart: edit.oldStart,
|
|
6288
|
+
oldEnd: edit.oldEnd,
|
|
6289
|
+
newStart,
|
|
6290
|
+
newEnd
|
|
6291
|
+
});
|
|
6292
|
+
delta += edit.replacement.length - (edit.oldEnd - edit.oldStart);
|
|
6293
|
+
cursor = edit.oldEnd;
|
|
6294
|
+
}
|
|
6295
|
+
text += original.slice(cursor);
|
|
6296
|
+
const oldCp = buildCodePointIndex(original);
|
|
6297
|
+
const newCp = buildCodePointIndex(text);
|
|
6298
|
+
const changes = pending.map((change) => ({
|
|
6299
|
+
...change,
|
|
6300
|
+
oldStartCp: oldCp.toCodePoint(change.oldStart),
|
|
6301
|
+
oldEndCp: oldCp.toCodePoint(change.oldEnd),
|
|
6302
|
+
newStartCp: newCp.toCodePoint(change.newStart),
|
|
6303
|
+
newEndCp: newCp.toCodePoint(change.newEnd)
|
|
6304
|
+
}));
|
|
6305
|
+
let bytesOutsideEditsIdentical = true;
|
|
6306
|
+
let oldCursor = 0;
|
|
6307
|
+
let newCursor = 0;
|
|
6308
|
+
for (const change of changes) {
|
|
6309
|
+
if (original.slice(oldCursor, change.oldStart) !== text.slice(newCursor, change.newStart)) {
|
|
6310
|
+
bytesOutsideEditsIdentical = false;
|
|
6311
|
+
}
|
|
6312
|
+
oldCursor = change.oldEnd;
|
|
6313
|
+
newCursor = change.newEnd;
|
|
6314
|
+
}
|
|
6315
|
+
if (original.slice(oldCursor) !== text.slice(newCursor)) bytesOutsideEditsIdentical = false;
|
|
6316
|
+
const newBlocks = tokenizeSource(text);
|
|
6317
|
+
const newIslandsByStart = /* @__PURE__ */ new Map();
|
|
6318
|
+
for (const island of listIslands(newBlocks)) {
|
|
6319
|
+
const list = newIslandsByStart.get(island.start);
|
|
6320
|
+
if (list) list.push(island.raw);
|
|
6321
|
+
else newIslandsByStart.set(island.start, [island.raw]);
|
|
6322
|
+
}
|
|
6323
|
+
const disturbed = [];
|
|
6324
|
+
for (const island of listIslands(blocks)) {
|
|
6325
|
+
const touched = effective.some(
|
|
6326
|
+
(edit) => island.start < edit.blockEnd && island.end > edit.blockStart
|
|
6327
|
+
);
|
|
6328
|
+
if (touched) continue;
|
|
6329
|
+
const mappedStart = mapPosition(changes, island.start, { assoc: 1 }).pos;
|
|
6330
|
+
const candidates = newIslandsByStart.get(mappedStart);
|
|
6331
|
+
if (!candidates || !candidates.includes(island.raw)) {
|
|
6332
|
+
disturbed.push({
|
|
6333
|
+
islandType: island.islandType,
|
|
6334
|
+
inline: island.inline,
|
|
6335
|
+
start: island.start,
|
|
6336
|
+
end: island.end,
|
|
6337
|
+
mappedStart
|
|
6338
|
+
});
|
|
6339
|
+
}
|
|
6340
|
+
}
|
|
6341
|
+
const integrity = {
|
|
6342
|
+
ok: bytesOutsideEditsIdentical && disturbed.length === 0,
|
|
6343
|
+
bytesOutsideEditsIdentical,
|
|
6344
|
+
disturbed
|
|
6345
|
+
};
|
|
6346
|
+
if (options.requireIntegrity && !integrity.ok) {
|
|
6347
|
+
throw new SourceSpliceError(
|
|
6348
|
+
"integrity",
|
|
6349
|
+
`the edit disturbed ${disturbed.length} protected island(s) outside the edited blocks`
|
|
6350
|
+
);
|
|
6351
|
+
}
|
|
6352
|
+
return { text, changed: true, changes, integrity, blocks: newBlocks };
|
|
6353
|
+
}
|
|
6354
|
+
function mapPosition(changes, pos, options = {}) {
|
|
6355
|
+
const assoc = options.assoc ?? 1;
|
|
6356
|
+
const cp = options.unit === "codepoint";
|
|
6357
|
+
let delta = 0;
|
|
6358
|
+
for (const change of changes) {
|
|
6359
|
+
const oldStart = cp ? change.oldStartCp : change.oldStart;
|
|
6360
|
+
const oldEnd = cp ? change.oldEndCp : change.oldEnd;
|
|
6361
|
+
const newStart = cp ? change.newStartCp : change.newStart;
|
|
6362
|
+
const newEnd = cp ? change.newEndCp : change.newEnd;
|
|
6363
|
+
if (pos < oldStart) break;
|
|
6364
|
+
if (pos > oldEnd) {
|
|
6365
|
+
delta = newEnd - oldEnd;
|
|
6366
|
+
continue;
|
|
6367
|
+
}
|
|
6368
|
+
if (oldStart === oldEnd) {
|
|
6369
|
+
return { pos: assoc < 0 ? newStart : newEnd, deleted: false };
|
|
6370
|
+
}
|
|
6371
|
+
if (pos === oldStart) return { pos: newStart, deleted: false };
|
|
6372
|
+
if (pos === oldEnd) return { pos: newEnd, deleted: false };
|
|
6373
|
+
return { pos: assoc < 0 ? newStart : newEnd, deleted: true };
|
|
6374
|
+
}
|
|
6375
|
+
return { pos: pos + delta, deleted: false };
|
|
6376
|
+
}
|
|
6377
|
+
function mapRange(changes, start, end, unit = "utf16") {
|
|
6378
|
+
const cp = unit === "codepoint";
|
|
6379
|
+
const mappedStart = mapPosition(changes, start, { assoc: 1, unit });
|
|
6380
|
+
const mappedEnd = mapPosition(changes, end, { assoc: -1, unit });
|
|
6381
|
+
const touched = changes.some((change) => {
|
|
6382
|
+
const oldStart = cp ? change.oldStartCp : change.oldStart;
|
|
6383
|
+
const oldEnd = cp ? change.oldEndCp : change.oldEnd;
|
|
6384
|
+
return oldStart < end && oldEnd > start;
|
|
6385
|
+
});
|
|
6386
|
+
const newStart = mappedStart.pos;
|
|
6387
|
+
const newEnd = Math.max(newStart, mappedEnd.pos);
|
|
6388
|
+
return { start: newStart, end: newEnd, touched, collapsed: newEnd === newStart && end > start };
|
|
6389
|
+
}
|
|
6390
|
+
|
|
4976
6391
|
exports.ACTION_BY_CLASS = ACTION_BY_CLASS;
|
|
4977
6392
|
exports.CAPABILITY_BY_CLASS = CAPABILITY_BY_CLASS;
|
|
4978
6393
|
exports.CLASSES = CLASSES;
|
|
@@ -4996,9 +6411,15 @@ exports.ROOT_STORAGE_NAME = ROOT_STORAGE_NAME;
|
|
|
4996
6411
|
exports.RUN_RESULT_KIND = RUN_RESULT_KIND;
|
|
4997
6412
|
exports.SIDE_EFFECT_CLASSES = SIDE_EFFECT_CLASSES;
|
|
4998
6413
|
exports.SLUG_PREFIX = SLUG_PREFIX;
|
|
6414
|
+
exports.SourceSpliceError = SourceSpliceError;
|
|
6415
|
+
exports.XmlContainerTracker = XmlContainerTracker;
|
|
4999
6416
|
exports.advancePartialKind = advancePartialKind;
|
|
5000
6417
|
exports.asKindInstance = asKindInstance;
|
|
6418
|
+
exports.blockAt = blockAt;
|
|
6419
|
+
exports.blockEdit = blockEdit;
|
|
6420
|
+
exports.blockRangeEdit = blockRangeEdit;
|
|
5001
6421
|
exports.buildAgentSchemaWithRenderBlockSupport = buildAgentSchemaWithRenderBlockSupport;
|
|
6422
|
+
exports.buildCodePointIndex = buildCodePointIndex;
|
|
5002
6423
|
exports.buildCompliantKindSnapshot = buildCompliantKindSnapshot;
|
|
5003
6424
|
exports.buildDirectiveSlug = buildDirectiveSlug;
|
|
5004
6425
|
exports.buildKindDirective = buildKindDirective;
|
|
@@ -5043,17 +6464,22 @@ exports.isLegacyShell = isLegacyShell;
|
|
|
5043
6464
|
exports.isProvisionalKind = isProvisionalKind;
|
|
5044
6465
|
exports.isReservedDirectiveSlug = isReservedDirectiveSlug;
|
|
5045
6466
|
exports.isScalarArrayType = isScalarArrayType;
|
|
6467
|
+
exports.isSingleDollarMath = isSingleDollarMath;
|
|
5046
6468
|
exports.isTerminalKindEvent = isTerminalKindEvent;
|
|
5047
6469
|
exports.itemFacts = itemFacts;
|
|
5048
6470
|
exports.itemSubtitle = itemSubtitle;
|
|
5049
6471
|
exports.itemTitle = itemTitle;
|
|
6472
|
+
exports.joinSource = joinSource;
|
|
5050
6473
|
exports.kindSchemaFromJsonSchema = kindSchemaFromJsonSchema;
|
|
5051
6474
|
exports.kindSchemaToJsonSchema = kindSchemaToJsonSchema;
|
|
5052
6475
|
exports.kindSchemaToStorage = kindSchemaToStorage;
|
|
5053
6476
|
exports.kindVerdictOf = kindVerdictOf;
|
|
5054
6477
|
exports.legacyShellUses = legacyShellUses;
|
|
6478
|
+
exports.listIslands = listIslands;
|
|
5055
6479
|
exports.looksLikeDirectiveHead = looksLikeDirectiveHead;
|
|
5056
6480
|
exports.makePartialKindStalenessGate = makePartialKindStalenessGate;
|
|
6481
|
+
exports.mapPosition = mapPosition;
|
|
6482
|
+
exports.mapRange = mapRange;
|
|
5057
6483
|
exports.mergeResidueIntoValue = mergeResidueIntoValue;
|
|
5058
6484
|
exports.normalizeAiSchemaInput = normalizeAiSchemaInput;
|
|
5059
6485
|
exports.normalizeJsonRegion = normalizeJsonRegion;
|
|
@@ -5068,6 +6494,7 @@ exports.readObjectKind = readObjectKind;
|
|
|
5068
6494
|
exports.readOutputRef = readOutputRef;
|
|
5069
6495
|
exports.readPartialKindEvent = readPartialKindEvent;
|
|
5070
6496
|
exports.readRunResultValue = readRunResultValue;
|
|
6497
|
+
exports.readXmlTag = readXmlTag;
|
|
5071
6498
|
exports.reconstructRegionValue = reconstructRegionValue;
|
|
5072
6499
|
exports.rehydrateNodeOutcome = rehydrateNodeOutcome;
|
|
5073
6500
|
exports.rehydrateRunResult = rehydrateRunResult;
|
|
@@ -5082,9 +6509,15 @@ exports.scalarArrayItemType = scalarArrayItemType;
|
|
|
5082
6509
|
exports.schemaLayoutMode = schemaLayoutMode;
|
|
5083
6510
|
exports.schemaStructureDepth = schemaStructureDepth;
|
|
5084
6511
|
exports.setJsonRootKeyLookup = setJsonRootKeyLookup;
|
|
6512
|
+
exports.singleDollarMathEnd = singleDollarMathEnd;
|
|
6513
|
+
exports.spliceSave = spliceSave;
|
|
6514
|
+
exports.splitsSurrogatePair = splitsSurrogatePair;
|
|
5085
6515
|
exports.storageToKindSchema = storageToKindSchema;
|
|
5086
6516
|
exports.stripKindDeep = stripKindDeep;
|
|
5087
6517
|
exports.titleCaseToken = titleCaseToken;
|
|
6518
|
+
exports.toCodePointOffset = toCodePointOffset;
|
|
6519
|
+
exports.toUtf16Offset = toUtf16Offset;
|
|
6520
|
+
exports.tokenizeSource = tokenizeSource;
|
|
5088
6521
|
exports.tryDecodeDirective = tryDecodeDirective;
|
|
5089
6522
|
exports.tryDecodeDirectiveContent = tryDecodeDirectiveContent;
|
|
5090
6523
|
exports.validateBlockSchemaSavePlan = validateBlockSchemaSavePlan;
|