@incremark/core 0.2.3 → 0.2.5
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/dist/detector/index.d.ts +1 -1
- package/dist/detector/index.js +59 -2
- package/dist/detector/index.js.map +1 -1
- package/dist/{index-3rgnFbip.d.ts → index-BfVDhalw.d.ts} +14 -0
- package/dist/index.d.ts +10 -4
- package/dist/index.js +152 -53
- package/dist/index.js.map +1 -1
- package/package.json +9 -5
- package/src/transformer/styles.css +0 -33
package/dist/detector/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, T as isFootnoteContinuation, S as isFootnoteDefinitionStart, h as isHeading, m as isHtmlBlock, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, u as updateContext } from '../index-
|
|
1
|
+
export { r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, T as isFootnoteContinuation, S as isFootnoteDefinitionStart, h as isHeading, m as isHtmlBlock, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, u as updateContext } from '../index-BfVDhalw.js';
|
|
2
2
|
import 'mdast';
|
|
3
3
|
import 'micromark-util-types';
|
|
4
4
|
import 'mdast-util-from-markdown';
|
package/dist/detector/index.js
CHANGED
|
@@ -78,7 +78,7 @@ function detectContainer(line, config) {
|
|
|
78
78
|
if (!pattern) {
|
|
79
79
|
const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, "\\$&");
|
|
80
80
|
pattern = new RegExp(
|
|
81
|
-
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s
|
|
81
|
+
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s*(\\w[\\w-]*))?(?:\\{[^}]*\\})?(?:\\s+(.*))?\\s*$`
|
|
82
82
|
);
|
|
83
83
|
containerPatternCache.set(cacheKey, pattern);
|
|
84
84
|
}
|
|
@@ -130,9 +130,17 @@ function createInitialContext() {
|
|
|
130
130
|
listDepth: 0,
|
|
131
131
|
blockquoteDepth: 0,
|
|
132
132
|
inContainer: false,
|
|
133
|
-
containerDepth: 0
|
|
133
|
+
containerDepth: 0,
|
|
134
|
+
inList: false
|
|
134
135
|
};
|
|
135
136
|
}
|
|
137
|
+
function isListContinuation(line, listIndent) {
|
|
138
|
+
if (isEmptyLine(line)) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
142
|
+
return contentIndent > listIndent;
|
|
143
|
+
}
|
|
136
144
|
function updateContext(line, context, containerConfig) {
|
|
137
145
|
const newContext = { ...context };
|
|
138
146
|
const containerCfg = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
@@ -167,6 +175,7 @@ function updateContext(line, context, containerConfig) {
|
|
|
167
175
|
newContext.containerDepth = context.containerDepth + 1;
|
|
168
176
|
return newContext;
|
|
169
177
|
}
|
|
178
|
+
return newContext;
|
|
170
179
|
} else {
|
|
171
180
|
const container = detectContainer(line, containerCfg);
|
|
172
181
|
if (container && !container.isEnd) {
|
|
@@ -178,6 +187,54 @@ function updateContext(line, context, containerConfig) {
|
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
189
|
}
|
|
190
|
+
const listItem = isListItemStart(line);
|
|
191
|
+
if (context.inList) {
|
|
192
|
+
if (context.listMayEnd) {
|
|
193
|
+
if (listItem) {
|
|
194
|
+
if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {
|
|
195
|
+
newContext.listMayEnd = false;
|
|
196
|
+
return newContext;
|
|
197
|
+
}
|
|
198
|
+
newContext.inList = true;
|
|
199
|
+
newContext.listOrdered = listItem.ordered;
|
|
200
|
+
newContext.listIndent = listItem.indent;
|
|
201
|
+
newContext.listMayEnd = false;
|
|
202
|
+
return newContext;
|
|
203
|
+
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
204
|
+
newContext.listMayEnd = isEmptyLine(line);
|
|
205
|
+
return newContext;
|
|
206
|
+
} else {
|
|
207
|
+
newContext.inList = false;
|
|
208
|
+
newContext.listOrdered = void 0;
|
|
209
|
+
newContext.listIndent = void 0;
|
|
210
|
+
newContext.listMayEnd = false;
|
|
211
|
+
return newContext;
|
|
212
|
+
}
|
|
213
|
+
} else {
|
|
214
|
+
if (listItem) {
|
|
215
|
+
return newContext;
|
|
216
|
+
} else if (isEmptyLine(line)) {
|
|
217
|
+
newContext.listMayEnd = true;
|
|
218
|
+
return newContext;
|
|
219
|
+
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
220
|
+
return newContext;
|
|
221
|
+
} else {
|
|
222
|
+
newContext.inList = false;
|
|
223
|
+
newContext.listOrdered = void 0;
|
|
224
|
+
newContext.listIndent = void 0;
|
|
225
|
+
newContext.listMayEnd = false;
|
|
226
|
+
return newContext;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
if (listItem) {
|
|
231
|
+
newContext.inList = true;
|
|
232
|
+
newContext.listOrdered = listItem.ordered;
|
|
233
|
+
newContext.listIndent = listItem.indent;
|
|
234
|
+
newContext.listMayEnd = false;
|
|
235
|
+
return newContext;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
181
238
|
return newContext;
|
|
182
239
|
}
|
|
183
240
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/detector/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * 块类型检测与边界判断\n *\n * Markdown 块级元素的识别规则\n */\n\nimport type { BlockContext, ContainerConfig, ContainerMatch } from '../types'\n\n// ============ 预编译正则表达式(性能优化) ============\n\nconst RE_FENCE_START = /^(\\s*)((`{3,})|(~{3,}))/\nconst RE_EMPTY_LINE = /^\\s*$/\nconst RE_HEADING = /^#{1,6}\\s/\nconst RE_THEMATIC_BREAK = /^(\\*{3,}|-{3,}|_{3,})\\s*$/\nconst RE_UNORDERED_LIST = /^(\\s*)([-*+])\\s/\nconst RE_ORDERED_LIST = /^(\\s*)(\\d{1,9})[.)]\\s/\nconst RE_BLOCKQUOTE = /^\\s{0,3}>/\nconst RE_HTML_BLOCK_1 = /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i\nconst RE_HTML_BLOCK_2 = /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/\nconst RE_TABLE_DELIMITER = /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/\nconst RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g\nconst RE_FOOTNOTE_DEFINITION = /^\\[\\^[^\\]]+\\]:\\s/\nconst RE_FOOTNOTE_CONTINUATION = /^(?: |\\t)/\n\n/** fence 结束模式缓存 */\nconst fenceEndPatternCache = new Map<string, RegExp>()\n\n/** 容器模式缓存 */\nconst containerPatternCache = new Map<string, RegExp>()\n\n// ============ 代码块检测 ============\n\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(RE_FENCE_START)\n if (match) {\n const fence = match[2]\n const char = fence[0]\n return { char, length: fence.length }\n }\n return null\n}\n\n/**\n * 检测行是否是代码块 fence 结束\n */\nexport function detectFenceEnd(line: string, context: BlockContext): boolean {\n if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {\n return false\n }\n\n // 使用缓存的正则表达式\n const cacheKey = `${context.fenceChar}-${context.fenceLength}`\n let pattern = fenceEndPatternCache.get(cacheKey)\n if (!pattern) {\n pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n fenceEndPatternCache.set(cacheKey, pattern)\n }\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return RE_EMPTY_LINE.test(line)\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return RE_HEADING.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return RE_THEMATIC_BREAK.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 无序列表: - * +\n const unordered = line.match(RE_UNORDERED_LIST)\n if (unordered) {\n return { ordered: false, indent: unordered[1].length }\n }\n\n // 有序列表: 1. 2) 等\n const ordered = line.match(RE_ORDERED_LIST)\n if (ordered) {\n return { ordered: true, indent: ordered[1].length }\n }\n\n return null\n}\n\n/**\n * 检测是否是引用块开始\n */\nexport function isBlockquoteStart(line: string): boolean {\n return RE_BLOCKQUOTE.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line)\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return RE_TABLE_DELIMITER.test(line.trim())\n}\n\n// ============ 脚注检测 ============\n\n/**\n * 检测是否是脚注定义的起始行\n * 格式: [^id]: content\n * \n * @example\n * isFootnoteDefinitionStart('[^1]: 脚注内容') // true\n * isFootnoteDefinitionStart('[^note]: 内容') // true\n * isFootnoteDefinitionStart(' 缩进内容') // false\n */\nexport function isFootnoteDefinitionStart(line: string): boolean {\n return RE_FOOTNOTE_DEFINITION.test(line)\n}\n\n/**\n * 检测是否是脚注定义的延续行(缩进行)\n * 至少4个空格或1个tab\n * \n * @example\n * isFootnoteContinuation(' 第二行') // true\n * isFootnoteContinuation('\\t第二行') // true\n * isFootnoteContinuation(' 两个空格') // false\n */\nexport function isFootnoteContinuation(line: string): boolean {\n return RE_FOOTNOTE_CONTINUATION.test(line)\n}\n\n// ============ 容器检测 ============\n\n/**\n * 检测容器开始或结束\n *\n * 支持格式:\n * - ::: name 开始\n * - ::: name attr 开始(带属性)\n * - ::: 结束\n * - :::::: name 开始(更长的标记,用于嵌套)\n */\nexport function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null {\n const marker = config?.marker || ':'\n const minLength = config?.minMarkerLength || 3\n\n // 使用缓存的正则表达式\n const cacheKey = `${marker}-${minLength}`\n let pattern = containerPatternCache.get(cacheKey)\n if (!pattern) {\n const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, '\\\\$&')\n pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s+(\\\\w[\\\\w-]*))?(?:\\\\s+(.*))?\\\\s*$`\n )\n containerPatternCache.set(cacheKey, pattern)\n }\n\n const match = line.match(pattern)\n if (!match) {\n return null\n }\n\n const markerLength = match[2].length\n const name = match[3] || ''\n const isEnd = !name && !match[4]\n\n if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {\n if (!config.allowedNames.includes(name)) {\n return null\n }\n }\n\n return { name, markerLength, isEnd }\n}\n\n/**\n * 检测容器结束\n */\nexport function detectContainerEnd(\n line: string,\n context: BlockContext,\n config?: ContainerConfig\n): boolean {\n if (!context.inContainer || !context.containerMarkerLength) {\n return false\n }\n\n const result = detectContainer(line, config)\n if (!result) {\n return false\n }\n\n return result.isEnd && result.markerLength >= context.containerMarkerLength\n}\n\n// ============ 边界检测 ============\n\n/**\n * 判断两行之间是否构成块边界\n */\nexport function isBlockBoundary(\n prevLine: string,\n currentLine: string,\n context: BlockContext\n): boolean {\n if (context.inFencedCode) {\n return detectFenceEnd(currentLine, context)\n }\n\n if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {\n return true\n }\n\n if (isHeading(currentLine) && !isEmptyLine(prevLine)) {\n return true\n }\n\n if (isThematicBreak(currentLine)) {\n return true\n }\n\n if (detectFenceStart(currentLine)) {\n return true\n }\n\n return false\n}\n\n// ============ 上下文管理 ============\n\n/**\n * 创建初始上下文\n */\nexport function createInitialContext(): BlockContext {\n return {\n inFencedCode: false,\n listDepth: 0,\n blockquoteDepth: 0,\n inContainer: false,\n containerDepth: 0\n }\n}\n\n/**\n * 更新上下文(处理一行后)\n */\nexport function updateContext(\n line: string,\n context: BlockContext,\n containerConfig?: ContainerConfig | boolean\n): BlockContext {\n const newContext = { ...context }\n\n const containerCfg =\n containerConfig === true ? {} : containerConfig === false ? undefined : containerConfig\n\n // 代码块优先级最高\n if (context.inFencedCode) {\n if (detectFenceEnd(line, context)) {\n newContext.inFencedCode = false\n newContext.fenceChar = undefined\n newContext.fenceLength = undefined\n }\n return newContext\n }\n\n const fence = detectFenceStart(line)\n if (fence) {\n newContext.inFencedCode = true\n newContext.fenceChar = fence.char\n newContext.fenceLength = fence.length\n return newContext\n }\n\n // 容器处理\n if (containerCfg !== undefined) {\n if (context.inContainer) {\n if (detectContainerEnd(line, context, containerCfg)) {\n newContext.containerDepth = context.containerDepth - 1\n if (newContext.containerDepth === 0) {\n newContext.inContainer = false\n newContext.containerMarkerLength = undefined\n newContext.containerName = undefined\n }\n return newContext\n }\n\n const nested = detectContainer(line, containerCfg)\n if (nested && !nested.isEnd) {\n newContext.containerDepth = context.containerDepth + 1\n return newContext\n }\n } else {\n const container = detectContainer(line, containerCfg)\n if (container && !container.isEnd) {\n newContext.inContainer = true\n newContext.containerMarkerLength = container.markerLength\n newContext.containerName = container.name\n newContext.containerDepth = 1\n return newContext\n }\n }\n }\n\n return newContext\n}\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/detector/index.ts"],"names":[],"mappings":";AAUA,IAAM,cAAA,GAAiB,yBAAA;AACvB,IAAM,aAAA,GAAgB,OAAA;AACtB,IAAM,UAAA,GAAa,WAAA;AACnB,IAAM,iBAAA,GAAoB,2BAAA;AAC1B,IAAM,iBAAA,GAAoB,iBAAA;AAC1B,IAAM,eAAA,GAAkB,uBAAA;AACxB,IAAM,aAAA,GAAgB,WAAA;AACtB,IAAM,eAAA,GAAkB,kEAAA;AACxB,IAAM,eAAA,GAAkB,2CAAA;AACxB,IAAM,kBAAA,GAAqB,6CAAA;AAC3B,IAAM,iBAAA,GAAoB,qBAAA;AAC1B,IAAM,sBAAA,GAAyB,kBAAA;AAC/B,IAAM,wBAAA,GAA2B,cAAA;AAGjC,IAAM,oBAAA,uBAA2B,GAAA,EAAoB;AAGrD,IAAM,qBAAA,uBAA4B,GAAA,EAAoB;AAO/C,SAAS,iBAAiB,IAAA,EAAuD;AACtF,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,cAAc,CAAA;AACvC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAM,KAAA,GAAQ,MAAM,CAAC,CAAA;AACrB,IAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AACpB,IAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,CAAM,MAAA,EAAO;AAAA,EACtC;AACA,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,cAAA,CAAe,MAAc,OAAA,EAAgC;AAC3E,EAAA,IAAI,CAAC,QAAQ,YAAA,IAAgB,CAAC,QAAQ,SAAA,IAAa,CAAC,QAAQ,WAAA,EAAa;AACvE,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,MAAM,WAAW,CAAA,EAAG,OAAA,CAAQ,SAAS,CAAA,CAAA,EAAI,QAAQ,WAAW,CAAA,CAAA;AAC5D,EAAA,IAAI,OAAA,GAAU,oBAAA,CAAqB,GAAA,CAAI,QAAQ,CAAA;AAC/C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAA,GAAU,IAAI,OAAO,CAAA,SAAA,EAAY,OAAA,CAAQ,SAAS,CAAA,CAAA,EAAI,OAAA,CAAQ,WAAW,CAAA,OAAA,CAAS,CAAA;AAClF,IAAA,oBAAA,CAAqB,GAAA,CAAI,UAAU,OAAO,CAAA;AAAA,EAC5C;AACA,EAAA,OAAO,OAAA,CAAQ,KAAK,IAAI,CAAA;AAC1B;AAOO,SAAS,YAAY,IAAA,EAAuB;AACjD,EAAA,OAAO,aAAA,CAAc,KAAK,IAAI,CAAA;AAChC;AAKO,SAAS,UAAU,IAAA,EAAuB;AAC/C,EAAA,OAAO,UAAA,CAAW,KAAK,IAAI,CAAA;AAC7B;AAKO,SAAS,gBAAgB,IAAA,EAAuB;AACrD,EAAA,OAAO,iBAAA,CAAkB,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,CAAA;AAC3C;AAKO,SAAS,gBAAgB,IAAA,EAA2D;AAEzF,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,KAAA,CAAM,iBAAiB,CAAA;AAC9C,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,QAAQ,SAAA,CAAU,CAAC,EAAE,MAAA,EAAO;AAAA,EACvD;AAGA,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,eAAe,CAAA;AAC1C,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,EAAM,QAAQ,OAAA,CAAQ,CAAC,EAAE,MAAA,EAAO;AAAA,EACpD;AAEA,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,kBAAkB,IAAA,EAAuB;AACvD,EAAA,OAAO,aAAA,CAAc,KAAK,IAAI,CAAA;AAChC;AAKO,SAAS,YAAY,IAAA,EAAuB;AACjD,EAAA,OAAO,gBAAgB,IAAA,CAAK,IAAI,CAAA,IAAK,eAAA,CAAgB,KAAK,IAAI,CAAA;AAChE;AAKO,SAAS,iBAAiB,IAAA,EAAuB;AACtD,EAAA,OAAO,kBAAA,CAAmB,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,CAAA;AAC5C;AAaO,SAAS,0BAA0B,IAAA,EAAuB;AAC/D,EAAA,OAAO,sBAAA,CAAuB,KAAK,IAAI,CAAA;AACzC;AAWO,SAAS,uBAAuB,IAAA,EAAuB;AAC5D,EAAA,OAAO,wBAAA,CAAyB,KAAK,IAAI,CAAA;AAC3C;AAaO,SAAS,eAAA,CAAgB,MAAc,MAAA,EAAiD;AAC7F,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,GAAA;AACjC,EAAA,MAAM,SAAA,GAAY,QAAQ,eAAA,IAAmB,CAAA;AAG7C,EAAA,MAAM,QAAA,GAAW,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AACvC,EAAA,IAAI,OAAA,GAAU,qBAAA,CAAsB,GAAA,CAAI,QAAQ,CAAA;AAChD,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,OAAA,CAAQ,iBAAA,EAAmB,MAAM,CAAA;AAI9D,IAAA,OAAA,GAAU,IAAI,MAAA;AAAA,MACZ,CAAA,QAAA,EAAW,aAAa,CAAA,CAAA,EAAI,SAAS,CAAA,0DAAA;AAAA,KACvC;AACA,IAAA,qBAAA,CAAsB,GAAA,CAAI,UAAU,OAAO,CAAA;AAAA,EAC7C;AAEA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAChC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,MAAM,YAAA,GAAe,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,CAAC,CAAA,IAAK,EAAA;AACzB,EAAA,MAAM,KAAA,GAAQ,CAAC,IAAA,IAAQ,CAAC,MAAM,CAAC,CAAA;AAE/B,EAAA,IAAI,CAAC,KAAA,IAAS,MAAA,EAAQ,gBAAgB,MAAA,CAAO,YAAA,CAAa,SAAS,CAAA,EAAG;AACpE,IAAA,IAAI,CAAC,MAAA,CAAO,YAAA,CAAa,QAAA,CAAS,IAAI,CAAA,EAAG;AACvC,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,IAAA,EAAM,YAAA,EAAc,KAAA,EAAM;AACrC;AAKO,SAAS,kBAAA,CACd,IAAA,EACA,OAAA,EACA,MAAA,EACS;AACT,EAAA,IAAI,CAAC,OAAA,CAAQ,WAAA,IAAe,CAAC,QAAQ,qBAAA,EAAuB;AAC1D,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,IAAA,EAAM,MAAM,CAAA;AAC3C,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO,MAAA,CAAO,KAAA,IAAS,MAAA,CAAO,YAAA,IAAgB,OAAA,CAAQ,qBAAA;AACxD;AAOO,SAAS,eAAA,CACd,QAAA,EACA,WAAA,EACA,OAAA,EACS;AACT,EAAA,IAAI,QAAQ,YAAA,EAAc;AACxB,IAAA,OAAO,cAAA,CAAe,aAAa,OAAO,CAAA;AAAA,EAC5C;AAEA,EAAA,IAAI,YAAY,QAAQ,CAAA,IAAK,CAAC,WAAA,CAAY,WAAW,CAAA,EAAG;AACtD,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,UAAU,WAAW,CAAA,IAAK,CAAC,WAAA,CAAY,QAAQ,CAAA,EAAG;AACpD,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,eAAA,CAAgB,WAAW,CAAA,EAAG;AAChC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,IAAI,gBAAA,CAAiB,WAAW,CAAA,EAAG;AACjC,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,KAAA;AACT;AAOO,SAAS,oBAAA,GAAqC;AACnD,EAAA,OAAO;AAAA,IACL,YAAA,EAAc,KAAA;AAAA,IACd,SAAA,EAAW,CAAA;AAAA,IACX,eAAA,EAAiB,CAAA;AAAA,IACjB,WAAA,EAAa,KAAA;AAAA,IACb,cAAA,EAAgB,CAAA;AAAA,IAChB,MAAA,EAAQ;AAAA,GACV;AACF;AAOA,SAAS,kBAAA,CAAmB,MAAc,UAAA,EAA6B;AAErE,EAAA,IAAI,WAAA,CAAY,IAAI,CAAA,EAAG;AACrB,IAAA,OAAO,IAAA;AAAA,EACT;AAIA,EAAA,MAAM,gBAAgB,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,GAAI,CAAC,EAAE,MAAA,IAAU,CAAA;AAC1D,EAAA,OAAO,aAAA,GAAgB,UAAA;AACzB;AAKO,SAAS,aAAA,CACd,IAAA,EACA,OAAA,EACA,eAAA,EACc;AACd,EAAA,MAAM,UAAA,GAAa,EAAE,GAAG,OAAA,EAAQ;AAEhC,EAAA,MAAM,eACJ,eAAA,KAAoB,IAAA,GAAO,EAAC,GAAI,eAAA,KAAoB,QAAQ,MAAA,GAAY,eAAA;AAG1E,EAAA,IAAI,QAAQ,YAAA,EAAc;AACxB,IAAA,IAAI,cAAA,CAAe,IAAA,EAAM,OAAO,CAAA,EAAG;AACjC,MAAA,UAAA,CAAW,YAAA,GAAe,KAAA;AAC1B,MAAA,UAAA,CAAW,SAAA,GAAY,MAAA;AACvB,MAAA,UAAA,CAAW,WAAA,GAAc,MAAA;AAAA,IAC3B;AACA,IAAA,OAAO,UAAA;AAAA,EACT;AAEA,EAAA,MAAM,KAAA,GAAQ,iBAAiB,IAAI,CAAA;AACnC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,UAAA,CAAW,YAAA,GAAe,IAAA;AAC1B,IAAA,UAAA,CAAW,YAAY,KAAA,CAAM,IAAA;AAC7B,IAAA,UAAA,CAAW,cAAc,KAAA,CAAM,MAAA;AAC/B,IAAA,OAAO,UAAA;AAAA,EACT;AAGA,EAAA,IAAI,iBAAiB,MAAA,EAAW;AAC9B,IAAA,IAAI,QAAQ,WAAA,EAAa;AAEvB,MAAA,IAAI,kBAAA,CAAmB,IAAA,EAAM,OAAA,EAAS,YAAY,CAAA,EAAG;AACnD,QAAA,UAAA,CAAW,cAAA,GAAiB,QAAQ,cAAA,GAAiB,CAAA;AACrD,QAAA,IAAI,UAAA,CAAW,mBAAmB,CAAA,EAAG;AACnC,UAAA,UAAA,CAAW,WAAA,GAAc,KAAA;AACzB,UAAA,UAAA,CAAW,qBAAA,GAAwB,MAAA;AACnC,UAAA,UAAA,CAAW,aAAA,GAAgB,MAAA;AAAA,QAC7B;AACA,QAAA,OAAO,UAAA;AAAA,MACT;AAGA,MAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,IAAA,EAAM,YAAY,CAAA;AACjD,MAAA,IAAI,MAAA,IAAU,CAAC,MAAA,CAAO,KAAA,EAAO;AAC3B,QAAA,UAAA,CAAW,cAAA,GAAiB,QAAQ,cAAA,GAAiB,CAAA;AACrD,QAAA,OAAO,UAAA;AAAA,MACT;AAKA,MAAA,OAAO,UAAA;AAAA,IACT,CAAA,MAAO;AAEL,MAAA,MAAM,SAAA,GAAY,eAAA,CAAgB,IAAA,EAAM,YAAY,CAAA;AACpD,MAAA,IAAI,SAAA,IAAa,CAAC,SAAA,CAAU,KAAA,EAAO;AACjC,QAAA,UAAA,CAAW,WAAA,GAAc,IAAA;AACzB,QAAA,UAAA,CAAW,wBAAwB,SAAA,CAAU,YAAA;AAC7C,QAAA,UAAA,CAAW,gBAAgB,SAAA,CAAU,IAAA;AACrC,QAAA,UAAA,CAAW,cAAA,GAAiB,CAAA;AAC5B,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,QAAA,GAAW,gBAAgB,IAAI,CAAA;AAErC,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAElB,IAAA,IAAI,QAAQ,UAAA,EAAY;AAEtB,MAAA,IAAI,QAAA,EAAU;AAGZ,QAAA,IAAI,SAAS,OAAA,KAAY,OAAA,CAAQ,eAAe,QAAA,CAAS,MAAA,KAAW,QAAQ,UAAA,EAAY;AAEtF,UAAA,UAAA,CAAW,UAAA,GAAa,KAAA;AACxB,UAAA,OAAO,UAAA;AAAA,QACT;AAEA,QAAA,UAAA,CAAW,MAAA,GAAS,IAAA;AACpB,QAAA,UAAA,CAAW,cAAc,QAAA,CAAS,OAAA;AAClC,QAAA,UAAA,CAAW,aAAa,QAAA,CAAS,MAAA;AACjC,QAAA,UAAA,CAAW,UAAA,GAAa,KAAA;AACxB,QAAA,OAAO,UAAA;AAAA,MACT,WAAW,kBAAA,CAAmB,IAAA,EAAM,OAAA,CAAQ,UAAA,IAAc,CAAC,CAAA,EAAG;AAE5D,QAAA,UAAA,CAAW,UAAA,GAAa,YAAY,IAAI,CAAA;AACxC,QAAA,OAAO,UAAA;AAAA,MACT,CAAA,MAAO;AAEL,QAAA,UAAA,CAAW,MAAA,GAAS,KAAA;AACpB,QAAA,UAAA,CAAW,WAAA,GAAc,MAAA;AACzB,QAAA,UAAA,CAAW,UAAA,GAAa,MAAA;AACxB,QAAA,UAAA,CAAW,UAAA,GAAa,KAAA;AACxB,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,IAAI,QAAA,EAAU;AAEZ,QAAA,OAAO,UAAA;AAAA,MACT,CAAA,MAAA,IAAW,WAAA,CAAY,IAAI,CAAA,EAAG;AAE5B,QAAA,UAAA,CAAW,UAAA,GAAa,IAAA;AACxB,QAAA,OAAO,UAAA;AAAA,MACT,WAAW,kBAAA,CAAmB,IAAA,EAAM,OAAA,CAAQ,UAAA,IAAc,CAAC,CAAA,EAAG;AAE5D,QAAA,OAAO,UAAA;AAAA,MACT,CAAA,MAAO;AAEL,QAAA,UAAA,CAAW,MAAA,GAAS,KAAA;AACpB,QAAA,UAAA,CAAW,WAAA,GAAc,MAAA;AACzB,QAAA,UAAA,CAAW,UAAA,GAAa,MAAA;AACxB,QAAA,UAAA,CAAW,UAAA,GAAa,KAAA;AACxB,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAA,MAAO;AAEL,IAAA,IAAI,QAAA,EAAU;AAEZ,MAAA,UAAA,CAAW,MAAA,GAAS,IAAA;AACpB,MAAA,UAAA,CAAW,cAAc,QAAA,CAAS,OAAA;AAClC,MAAA,UAAA,CAAW,aAAa,QAAA,CAAS,MAAA;AACjC,MAAA,UAAA,CAAW,UAAA,GAAa,KAAA;AACxB,MAAA,OAAO,UAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,OAAO,UAAA;AACT","file":"index.js","sourcesContent":["/**\n * 块类型检测与边界判断\n *\n * Markdown 块级元素的识别规则\n */\n\nimport type { BlockContext, ContainerConfig, ContainerMatch } from '../types'\n\n// ============ 预编译正则表达式(性能优化) ============\n\nconst RE_FENCE_START = /^(\\s*)((`{3,})|(~{3,}))/\nconst RE_EMPTY_LINE = /^\\s*$/\nconst RE_HEADING = /^#{1,6}\\s/\nconst RE_THEMATIC_BREAK = /^(\\*{3,}|-{3,}|_{3,})\\s*$/\nconst RE_UNORDERED_LIST = /^(\\s*)([-*+])\\s/\nconst RE_ORDERED_LIST = /^(\\s*)(\\d{1,9})[.)]\\s/\nconst RE_BLOCKQUOTE = /^\\s{0,3}>/\nconst RE_HTML_BLOCK_1 = /^\\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\\?|!\\[CDATA\\[)/i\nconst RE_HTML_BLOCK_2 = /^\\s{0,3}<\\/?[a-zA-Z][a-zA-Z0-9-]*(\\s|>|$)/\nconst RE_TABLE_DELIMITER = /^\\|?\\s*:?-{3,}:?\\s*(\\|\\s*:?-{3,}:?\\s*)*\\|?$/\nconst RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\\]\\\\]/g\nconst RE_FOOTNOTE_DEFINITION = /^\\[\\^[^\\]]+\\]:\\s/\nconst RE_FOOTNOTE_CONTINUATION = /^(?: |\\t)/\n\n/** fence 结束模式缓存 */\nconst fenceEndPatternCache = new Map<string, RegExp>()\n\n/** 容器模式缓存 */\nconst containerPatternCache = new Map<string, RegExp>()\n\n// ============ 代码块检测 ============\n\n/**\n * 检测行是否是代码块 fence 开始\n */\nexport function detectFenceStart(line: string): { char: string; length: number } | null {\n const match = line.match(RE_FENCE_START)\n if (match) {\n const fence = match[2]\n const char = fence[0]\n return { char, length: fence.length }\n }\n return null\n}\n\n/**\n * 检测行是否是代码块 fence 结束\n */\nexport function detectFenceEnd(line: string, context: BlockContext): boolean {\n if (!context.inFencedCode || !context.fenceChar || !context.fenceLength) {\n return false\n }\n\n // 使用缓存的正则表达式\n const cacheKey = `${context.fenceChar}-${context.fenceLength}`\n let pattern = fenceEndPatternCache.get(cacheKey)\n if (!pattern) {\n pattern = new RegExp(`^\\\\s{0,3}${context.fenceChar}{${context.fenceLength},}\\\\s*$`)\n fenceEndPatternCache.set(cacheKey, pattern)\n }\n return pattern.test(line)\n}\n\n// ============ 行类型检测 ============\n\n/**\n * 检测是否是空行或仅包含空白字符\n */\nexport function isEmptyLine(line: string): boolean {\n return RE_EMPTY_LINE.test(line)\n}\n\n/**\n * 检测是否是标题行\n */\nexport function isHeading(line: string): boolean {\n return RE_HEADING.test(line)\n}\n\n/**\n * 检测是否是 thematic break(水平线)\n */\nexport function isThematicBreak(line: string): boolean {\n return RE_THEMATIC_BREAK.test(line.trim())\n}\n\n/**\n * 检测是否是列表项开始\n */\nexport function isListItemStart(line: string): { ordered: boolean; indent: number } | null {\n // 无序列表: - * +\n const unordered = line.match(RE_UNORDERED_LIST)\n if (unordered) {\n return { ordered: false, indent: unordered[1].length }\n }\n\n // 有序列表: 1. 2) 等\n const ordered = line.match(RE_ORDERED_LIST)\n if (ordered) {\n return { ordered: true, indent: ordered[1].length }\n }\n\n return null\n}\n\n/**\n * 检测是否是引用块开始\n */\nexport function isBlockquoteStart(line: string): boolean {\n return RE_BLOCKQUOTE.test(line)\n}\n\n/**\n * 检测是否是 HTML 块\n */\nexport function isHtmlBlock(line: string): boolean {\n return RE_HTML_BLOCK_1.test(line) || RE_HTML_BLOCK_2.test(line)\n}\n\n/**\n * 检测表格分隔行\n */\nexport function isTableDelimiter(line: string): boolean {\n return RE_TABLE_DELIMITER.test(line.trim())\n}\n\n// ============ 脚注检测 ============\n\n/**\n * 检测是否是脚注定义的起始行\n * 格式: [^id]: content\n * \n * @example\n * isFootnoteDefinitionStart('[^1]: 脚注内容') // true\n * isFootnoteDefinitionStart('[^note]: 内容') // true\n * isFootnoteDefinitionStart(' 缩进内容') // false\n */\nexport function isFootnoteDefinitionStart(line: string): boolean {\n return RE_FOOTNOTE_DEFINITION.test(line)\n}\n\n/**\n * 检测是否是脚注定义的延续行(缩进行)\n * 至少4个空格或1个tab\n * \n * @example\n * isFootnoteContinuation(' 第二行') // true\n * isFootnoteContinuation('\\t第二行') // true\n * isFootnoteContinuation(' 两个空格') // false\n */\nexport function isFootnoteContinuation(line: string): boolean {\n return RE_FOOTNOTE_CONTINUATION.test(line)\n}\n\n// ============ 容器检测 ============\n\n/**\n * 检测容器开始或结束\n *\n * 支持格式:\n * - ::: name 开始\n * - ::: name attr 开始(带属性)\n * - ::: 结束\n * - :::::: name 开始(更长的标记,用于嵌套)\n */\nexport function detectContainer(line: string, config?: ContainerConfig): ContainerMatch | null {\n const marker = config?.marker || ':'\n const minLength = config?.minMarkerLength || 3\n\n // 使用缓存的正则表达式\n const cacheKey = `${marker}-${minLength}`\n let pattern = containerPatternCache.get(cacheKey)\n if (!pattern) {\n const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, '\\\\$&')\n // 支持两种格式:\n // 1. ::: name attr (有空格分隔)\n // 2. :::name{...} (directive 语法,无空格)\n pattern = new RegExp(\n `^(\\\\s*)(${escapedMarker}{${minLength},})(?:\\\\s*(\\\\w[\\\\w-]*))?(?:\\\\{[^}]*\\\\})?(?:\\\\s+(.*))?\\\\s*$`\n )\n containerPatternCache.set(cacheKey, pattern)\n }\n\n const match = line.match(pattern)\n if (!match) {\n return null\n }\n\n const markerLength = match[2].length\n const name = match[3] || ''\n const isEnd = !name && !match[4]\n\n if (!isEnd && config?.allowedNames && config.allowedNames.length > 0) {\n if (!config.allowedNames.includes(name)) {\n return null\n }\n }\n\n return { name, markerLength, isEnd }\n}\n\n/**\n * 检测容器结束\n */\nexport function detectContainerEnd(\n line: string,\n context: BlockContext,\n config?: ContainerConfig\n): boolean {\n if (!context.inContainer || !context.containerMarkerLength) {\n return false\n }\n\n const result = detectContainer(line, config)\n if (!result) {\n return false\n }\n\n return result.isEnd && result.markerLength >= context.containerMarkerLength\n}\n\n// ============ 边界检测 ============\n\n/**\n * 判断两行之间是否构成块边界\n */\nexport function isBlockBoundary(\n prevLine: string,\n currentLine: string,\n context: BlockContext\n): boolean {\n if (context.inFencedCode) {\n return detectFenceEnd(currentLine, context)\n }\n\n if (isEmptyLine(prevLine) && !isEmptyLine(currentLine)) {\n return true\n }\n\n if (isHeading(currentLine) && !isEmptyLine(prevLine)) {\n return true\n }\n\n if (isThematicBreak(currentLine)) {\n return true\n }\n\n if (detectFenceStart(currentLine)) {\n return true\n }\n\n return false\n}\n\n// ============ 上下文管理 ============\n\n/**\n * 创建初始上下文\n */\nexport function createInitialContext(): BlockContext {\n return {\n inFencedCode: false,\n listDepth: 0,\n blockquoteDepth: 0,\n inContainer: false,\n containerDepth: 0,\n inList: false\n }\n}\n\n/**\n * 检测是否是列表项的延续内容(缩进内容或空行)\n * @param line 当前行\n * @param listIndent 列表的基础缩进\n */\nfunction isListContinuation(line: string, listIndent: number): boolean {\n // 空行可能是列表内部的段落分隔\n if (isEmptyLine(line)) {\n return true\n }\n\n // 检查是否有足够的缩进(列表内容至少需要缩进到列表标记之后)\n // 通常列表标记后至少需要 2 个字符的缩进(如 \"1. \" 或 \"- \")\n const contentIndent = line.match(/^(\\s*)/)?.[1].length ?? 0\n return contentIndent > listIndent\n}\n\n/**\n * 更新上下文(处理一行后)\n */\nexport function updateContext(\n line: string,\n context: BlockContext,\n containerConfig?: ContainerConfig | boolean\n): BlockContext {\n const newContext = { ...context }\n\n const containerCfg =\n containerConfig === true ? {} : containerConfig === false ? undefined : containerConfig\n\n // 代码块优先级最高\n if (context.inFencedCode) {\n if (detectFenceEnd(line, context)) {\n newContext.inFencedCode = false\n newContext.fenceChar = undefined\n newContext.fenceLength = undefined\n }\n return newContext\n }\n\n const fence = detectFenceStart(line)\n if (fence) {\n newContext.inFencedCode = true\n newContext.fenceChar = fence.char\n newContext.fenceLength = fence.length\n return newContext\n }\n\n // 容器处理\n if (containerCfg !== undefined) {\n if (context.inContainer) {\n // 检查是否是容器结束\n if (detectContainerEnd(line, context, containerCfg)) {\n newContext.containerDepth = context.containerDepth - 1\n if (newContext.containerDepth === 0) {\n newContext.inContainer = false\n newContext.containerMarkerLength = undefined\n newContext.containerName = undefined\n }\n return newContext\n }\n\n // 检查是否是嵌套容器开始\n const nested = detectContainer(line, containerCfg)\n if (nested && !nested.isEnd) {\n newContext.containerDepth = context.containerDepth + 1\n return newContext\n }\n\n // ⚠️ 关键:在容器内,无论是什么内容(空行、列表、段落等),都保持 inContainer = true\n // 只有容器结束标记才能改变容器状态\n // 这里不需要做任何操作,因为 newContext 已经复制了 context,inContainer 已经是 true\n return newContext\n } else {\n // 不在容器内,检查是否是容器开始\n const container = detectContainer(line, containerCfg)\n if (container && !container.isEnd) {\n newContext.inContainer = true\n newContext.containerMarkerLength = container.markerLength\n newContext.containerName = container.name\n newContext.containerDepth = 1\n return newContext\n }\n }\n }\n\n // 列表处理\n const listItem = isListItemStart(line)\n\n if (context.inList) {\n // 已经在列表中\n if (context.listMayEnd) {\n // 上一行是空行,需要确认列表是否结束\n if (listItem) {\n // 遇到新的列表项\n // 检查是否是同类型列表的延续\n if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {\n // 同类型同级别列表项,列表继续\n newContext.listMayEnd = false\n return newContext\n }\n // 不同类型或不同级别,列表结束,新列表开始\n newContext.inList = true\n newContext.listOrdered = listItem.ordered\n newContext.listIndent = listItem.indent\n newContext.listMayEnd = false\n return newContext\n } else if (isListContinuation(line, context.listIndent ?? 0)) {\n // 缩进内容或空行,列表继续\n newContext.listMayEnd = isEmptyLine(line)\n return newContext\n } else {\n // 非列表内容,列表结束\n newContext.inList = false\n newContext.listOrdered = undefined\n newContext.listIndent = undefined\n newContext.listMayEnd = false\n return newContext\n }\n } else {\n // 上一行不是空行\n if (listItem) {\n // 新列表项(可能是同级或嵌套)\n return newContext\n } else if (isEmptyLine(line)) {\n // 遇到空行,列表可能结束\n newContext.listMayEnd = true\n return newContext\n } else if (isListContinuation(line, context.listIndent ?? 0)) {\n // 缩进内容,列表继续\n return newContext\n } else {\n // 非缩进非列表内容,列表结束\n newContext.inList = false\n newContext.listOrdered = undefined\n newContext.listIndent = undefined\n newContext.listMayEnd = false\n return newContext\n }\n }\n } else {\n // 不在列表中\n if (listItem) {\n // 列表开始\n newContext.inList = true\n newContext.listOrdered = listItem.ordered\n newContext.listIndent = listItem.indent\n newContext.listMayEnd = false\n return newContext\n }\n }\n\n return newContext\n}\n\n"]}
|
|
@@ -228,6 +228,12 @@ interface ParserState {
|
|
|
228
228
|
interface ParserOptions {
|
|
229
229
|
/** 启用 GFM 扩展(表格、任务列表等) */
|
|
230
230
|
gfm?: boolean;
|
|
231
|
+
/**
|
|
232
|
+
* 启用数学公式支持($..$ 行内公式和 $$...$$ 块级公式)
|
|
233
|
+
* - false/undefined: 禁用(默认)
|
|
234
|
+
* - true: 启用数学公式解析
|
|
235
|
+
*/
|
|
236
|
+
math?: boolean;
|
|
231
237
|
/**
|
|
232
238
|
* 启用 ::: 容器语法支持(用于边界检测)
|
|
233
239
|
* - false: 禁用(默认)
|
|
@@ -273,6 +279,14 @@ interface BlockContext {
|
|
|
273
279
|
containerName?: string;
|
|
274
280
|
/** 容器嵌套深度(支持嵌套容器) */
|
|
275
281
|
containerDepth: number;
|
|
282
|
+
/** 当前是否在列表中 */
|
|
283
|
+
inList: boolean;
|
|
284
|
+
/** 当前列表是否是有序列表 */
|
|
285
|
+
listOrdered?: boolean;
|
|
286
|
+
/** 当前列表的基础缩进 */
|
|
287
|
+
listIndent?: number;
|
|
288
|
+
/** 遇到空行后,列表可能结束(等待下一行确认) */
|
|
289
|
+
listMayEnd?: boolean;
|
|
276
290
|
}
|
|
277
291
|
/**
|
|
278
292
|
* 容器检测结果
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as ParserOptions, I as IncrementalUpdate, a as ParsedBlock, D as DefinitionMap, F as FootnoteDefinitionMap, b as ParserState, B as BlockStatus } from './index-
|
|
2
|
-
export { A as AstNode, c as BlockContext, e as BlockTypeInfo, C as ContainerConfig, d as ContainerMatch, J as HTML_ATTR_BLACKLIST, K as HTML_PROTOCOL_BLACKLIST, H as HTML_TAG_BLACKLIST, N as HtmlAttrInfo, R as HtmlContentType, M as HtmlElementNode, Q as HtmlTreeExtensionOptions, O as ParsedHtmlTag, s as createHtmlTreeTransformer, r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, x as detectHtmlContentType, E as findHtmlElementsByTag, G as htmlElementToString, L as htmlTreeExtension, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, h as isHeading, m as isHtmlBlock, y as isHtmlElementNode, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, w as parseHtmlFragment, v as parseHtmlTag, t as transformHtmlNodes, u as updateContext, z as walkHtmlElements } from './index-
|
|
1
|
+
import { P as ParserOptions, I as IncrementalUpdate, a as ParsedBlock, D as DefinitionMap, F as FootnoteDefinitionMap, b as ParserState, B as BlockStatus } from './index-BfVDhalw.js';
|
|
2
|
+
export { A as AstNode, c as BlockContext, e as BlockTypeInfo, C as ContainerConfig, d as ContainerMatch, J as HTML_ATTR_BLACKLIST, K as HTML_PROTOCOL_BLACKLIST, H as HTML_TAG_BLACKLIST, N as HtmlAttrInfo, R as HtmlContentType, M as HtmlElementNode, Q as HtmlTreeExtensionOptions, O as ParsedHtmlTag, s as createHtmlTreeTransformer, r as createInitialContext, o as detectContainer, p as detectContainerEnd, g as detectFenceEnd, f as detectFenceStart, x as detectHtmlContentType, E as findHtmlElementsByTag, G as htmlElementToString, L as htmlTreeExtension, q as isBlockBoundary, l as isBlockquoteStart, i as isEmptyLine, h as isHeading, m as isHtmlBlock, y as isHtmlElementNode, k as isListItemStart, n as isTableDelimiter, j as isThematicBreak, w as parseHtmlFragment, v as parseHtmlTag, t as transformHtmlNodes, u as updateContext, z as walkHtmlElements } from './index-BfVDhalw.js';
|
|
3
3
|
import { Root, RootContent, Text } from 'mdast';
|
|
4
4
|
export { Root, RootContent } from 'mdast';
|
|
5
5
|
export { calculateLineOffset, generateId, joinLines, resetIdCounter, splitLines } from './utils/index.js';
|
|
@@ -40,7 +40,7 @@ declare class IncremarkParser {
|
|
|
40
40
|
*/
|
|
41
41
|
private convertHtmlToText;
|
|
42
42
|
private parse;
|
|
43
|
-
private
|
|
43
|
+
private updateDefinitionsFromCompletedBlocks;
|
|
44
44
|
private findDefinition;
|
|
45
45
|
private findFootnoteDefinition;
|
|
46
46
|
/**
|
|
@@ -94,7 +94,7 @@ declare class IncremarkParser {
|
|
|
94
94
|
finalize(): IncrementalUpdate;
|
|
95
95
|
/**
|
|
96
96
|
* 强制中断解析,将所有待处理内容标记为完成
|
|
97
|
-
*
|
|
97
|
+
* @deprecated 请使用 finalize() 代替,功能完全相同
|
|
98
98
|
*/
|
|
99
99
|
abort(): IncrementalUpdate;
|
|
100
100
|
/**
|
|
@@ -221,6 +221,11 @@ interface TransformerOptions {
|
|
|
221
221
|
plugins?: TransformerPlugin[];
|
|
222
222
|
/** 状态变化回调 */
|
|
223
223
|
onChange?: (displayBlocks: DisplayBlock[]) => void;
|
|
224
|
+
/**
|
|
225
|
+
* 所有动画完成时的回调
|
|
226
|
+
* 当队列中没有更多 block 需要处理时触发
|
|
227
|
+
*/
|
|
228
|
+
onAllComplete?: () => void;
|
|
224
229
|
/**
|
|
225
230
|
* 是否在页面不可见时自动暂停
|
|
226
231
|
* 默认 true,节省资源
|
|
@@ -384,6 +389,7 @@ declare class BlockTransformer<T = unknown> {
|
|
|
384
389
|
private clearCache;
|
|
385
390
|
/**
|
|
386
391
|
* 获取累积的 chunks(用于 fade-in 效果)
|
|
392
|
+
* stableChars 表示在 chunks 之前的稳定字符数
|
|
387
393
|
*/
|
|
388
394
|
private getAccumulatedChunks;
|
|
389
395
|
}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import { fromMarkdown } from 'mdast-util-from-markdown';
|
|
|
2
2
|
import { gfmFromMarkdown } from 'mdast-util-gfm';
|
|
3
3
|
import { gfm } from 'micromark-extension-gfm';
|
|
4
4
|
import { gfmFootnoteFromMarkdown } from 'mdast-util-gfm-footnote';
|
|
5
|
+
import { math } from 'micromark-extension-math';
|
|
6
|
+
import { mathFromMarkdown } from 'mdast-util-math';
|
|
5
7
|
import { codes, constants, types } from 'micromark-util-symbol';
|
|
6
8
|
import { markdownLineEndingOrSpace } from 'micromark-util-character';
|
|
7
9
|
import { factoryDestination } from 'micromark-factory-destination';
|
|
@@ -10,6 +12,8 @@ import { factoryLabel } from 'micromark-factory-label';
|
|
|
10
12
|
import { factoryWhitespace } from 'micromark-factory-whitespace';
|
|
11
13
|
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
|
|
12
14
|
import { normalizeIdentifier } from 'micromark-util-normalize-identifier';
|
|
15
|
+
import { directive } from 'micromark-extension-directive';
|
|
16
|
+
import { directiveFromMarkdown } from 'mdast-util-directive';
|
|
13
17
|
|
|
14
18
|
// src/parser/IncremarkParser.ts
|
|
15
19
|
|
|
@@ -938,7 +942,7 @@ function detectContainer(line, config) {
|
|
|
938
942
|
if (!pattern) {
|
|
939
943
|
const escapedMarker = marker.replace(RE_ESCAPE_SPECIAL, "\\$&");
|
|
940
944
|
pattern = new RegExp(
|
|
941
|
-
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s
|
|
945
|
+
`^(\\s*)(${escapedMarker}{${minLength},})(?:\\s*(\\w[\\w-]*))?(?:\\{[^}]*\\})?(?:\\s+(.*))?\\s*$`
|
|
942
946
|
);
|
|
943
947
|
containerPatternCache.set(cacheKey, pattern);
|
|
944
948
|
}
|
|
@@ -990,9 +994,17 @@ function createInitialContext() {
|
|
|
990
994
|
listDepth: 0,
|
|
991
995
|
blockquoteDepth: 0,
|
|
992
996
|
inContainer: false,
|
|
993
|
-
containerDepth: 0
|
|
997
|
+
containerDepth: 0,
|
|
998
|
+
inList: false
|
|
994
999
|
};
|
|
995
1000
|
}
|
|
1001
|
+
function isListContinuation(line, listIndent) {
|
|
1002
|
+
if (isEmptyLine(line)) {
|
|
1003
|
+
return true;
|
|
1004
|
+
}
|
|
1005
|
+
const contentIndent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
1006
|
+
return contentIndent > listIndent;
|
|
1007
|
+
}
|
|
996
1008
|
function updateContext(line, context, containerConfig) {
|
|
997
1009
|
const newContext = { ...context };
|
|
998
1010
|
const containerCfg = containerConfig === true ? {} : containerConfig === false ? void 0 : containerConfig;
|
|
@@ -1027,6 +1039,7 @@ function updateContext(line, context, containerConfig) {
|
|
|
1027
1039
|
newContext.containerDepth = context.containerDepth + 1;
|
|
1028
1040
|
return newContext;
|
|
1029
1041
|
}
|
|
1042
|
+
return newContext;
|
|
1030
1043
|
} else {
|
|
1031
1044
|
const container = detectContainer(line, containerCfg);
|
|
1032
1045
|
if (container && !container.isEnd) {
|
|
@@ -1038,6 +1051,54 @@ function updateContext(line, context, containerConfig) {
|
|
|
1038
1051
|
}
|
|
1039
1052
|
}
|
|
1040
1053
|
}
|
|
1054
|
+
const listItem = isListItemStart(line);
|
|
1055
|
+
if (context.inList) {
|
|
1056
|
+
if (context.listMayEnd) {
|
|
1057
|
+
if (listItem) {
|
|
1058
|
+
if (listItem.ordered === context.listOrdered && listItem.indent === context.listIndent) {
|
|
1059
|
+
newContext.listMayEnd = false;
|
|
1060
|
+
return newContext;
|
|
1061
|
+
}
|
|
1062
|
+
newContext.inList = true;
|
|
1063
|
+
newContext.listOrdered = listItem.ordered;
|
|
1064
|
+
newContext.listIndent = listItem.indent;
|
|
1065
|
+
newContext.listMayEnd = false;
|
|
1066
|
+
return newContext;
|
|
1067
|
+
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
1068
|
+
newContext.listMayEnd = isEmptyLine(line);
|
|
1069
|
+
return newContext;
|
|
1070
|
+
} else {
|
|
1071
|
+
newContext.inList = false;
|
|
1072
|
+
newContext.listOrdered = void 0;
|
|
1073
|
+
newContext.listIndent = void 0;
|
|
1074
|
+
newContext.listMayEnd = false;
|
|
1075
|
+
return newContext;
|
|
1076
|
+
}
|
|
1077
|
+
} else {
|
|
1078
|
+
if (listItem) {
|
|
1079
|
+
return newContext;
|
|
1080
|
+
} else if (isEmptyLine(line)) {
|
|
1081
|
+
newContext.listMayEnd = true;
|
|
1082
|
+
return newContext;
|
|
1083
|
+
} else if (isListContinuation(line, context.listIndent ?? 0)) {
|
|
1084
|
+
return newContext;
|
|
1085
|
+
} else {
|
|
1086
|
+
newContext.inList = false;
|
|
1087
|
+
newContext.listOrdered = void 0;
|
|
1088
|
+
newContext.listIndent = void 0;
|
|
1089
|
+
newContext.listMayEnd = false;
|
|
1090
|
+
return newContext;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
} else {
|
|
1094
|
+
if (listItem) {
|
|
1095
|
+
newContext.inList = true;
|
|
1096
|
+
newContext.listOrdered = listItem.ordered;
|
|
1097
|
+
newContext.listIndent = listItem.indent;
|
|
1098
|
+
newContext.listMayEnd = false;
|
|
1099
|
+
return newContext;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1041
1102
|
return newContext;
|
|
1042
1103
|
}
|
|
1043
1104
|
|
|
@@ -1186,6 +1247,14 @@ var IncremarkParser = class {
|
|
|
1186
1247
|
extensions.push(gfm());
|
|
1187
1248
|
mdastExtensions.push(...gfmFromMarkdown(), gfmFootnoteFromMarkdown());
|
|
1188
1249
|
}
|
|
1250
|
+
if (this.options.math) {
|
|
1251
|
+
extensions.push(math());
|
|
1252
|
+
mdastExtensions.push(mathFromMarkdown());
|
|
1253
|
+
}
|
|
1254
|
+
if (this.containerConfig !== void 0) {
|
|
1255
|
+
extensions.push(directive());
|
|
1256
|
+
mdastExtensions.push(directiveFromMarkdown());
|
|
1257
|
+
}
|
|
1189
1258
|
if (this.options.extensions) {
|
|
1190
1259
|
extensions.push(...this.options.extensions);
|
|
1191
1260
|
}
|
|
@@ -1204,7 +1273,7 @@ var IncremarkParser = class {
|
|
|
1204
1273
|
}
|
|
1205
1274
|
return ast;
|
|
1206
1275
|
}
|
|
1207
|
-
|
|
1276
|
+
updateDefinitionsFromCompletedBlocks(blocks) {
|
|
1208
1277
|
for (const block of blocks) {
|
|
1209
1278
|
this.definitionMap = {
|
|
1210
1279
|
...this.definitionMap,
|
|
@@ -1218,17 +1287,17 @@ var IncremarkParser = class {
|
|
|
1218
1287
|
}
|
|
1219
1288
|
findDefinition(block) {
|
|
1220
1289
|
const definitions = [];
|
|
1221
|
-
function
|
|
1290
|
+
function findDefinitionRecursive(node) {
|
|
1222
1291
|
if (isDefinitionNode(node)) {
|
|
1223
1292
|
definitions.push(node);
|
|
1224
1293
|
}
|
|
1225
1294
|
if ("children" in node && Array.isArray(node.children)) {
|
|
1226
1295
|
for (const child of node.children) {
|
|
1227
|
-
|
|
1296
|
+
findDefinitionRecursive(child);
|
|
1228
1297
|
}
|
|
1229
1298
|
}
|
|
1230
1299
|
}
|
|
1231
|
-
|
|
1300
|
+
findDefinitionRecursive(block.node);
|
|
1232
1301
|
return definitions.reduce((acc, node) => {
|
|
1233
1302
|
acc[node.identifier] = node;
|
|
1234
1303
|
return acc;
|
|
@@ -1331,7 +1400,7 @@ var IncremarkParser = class {
|
|
|
1331
1400
|
if (tempContext.inContainer) {
|
|
1332
1401
|
continue;
|
|
1333
1402
|
}
|
|
1334
|
-
const stablePoint = this.checkStability(i);
|
|
1403
|
+
const stablePoint = this.checkStability(i, tempContext);
|
|
1335
1404
|
if (stablePoint >= 0) {
|
|
1336
1405
|
stableLine = stablePoint;
|
|
1337
1406
|
stableContext = { ...tempContext };
|
|
@@ -1339,12 +1408,26 @@ var IncremarkParser = class {
|
|
|
1339
1408
|
}
|
|
1340
1409
|
return { line: stableLine, contextAtLine: stableContext };
|
|
1341
1410
|
}
|
|
1342
|
-
checkStability(lineIndex) {
|
|
1411
|
+
checkStability(lineIndex, context) {
|
|
1343
1412
|
if (lineIndex === 0) {
|
|
1344
1413
|
return -1;
|
|
1345
1414
|
}
|
|
1346
1415
|
const line = this.lines[lineIndex];
|
|
1347
1416
|
const prevLine = this.lines[lineIndex - 1];
|
|
1417
|
+
if (context.inContainer) {
|
|
1418
|
+
if (this.containerConfig !== void 0) {
|
|
1419
|
+
const containerEnd = detectContainerEnd(line, context, this.containerConfig);
|
|
1420
|
+
if (containerEnd) {
|
|
1421
|
+
return lineIndex - 1;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return -1;
|
|
1425
|
+
}
|
|
1426
|
+
if (context.inList) {
|
|
1427
|
+
if (!context.listMayEnd) {
|
|
1428
|
+
return -1;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1348
1431
|
if (isHeading(prevLine) || isThematicBreak(prevLine)) {
|
|
1349
1432
|
return lineIndex - 1;
|
|
1350
1433
|
}
|
|
@@ -1384,7 +1467,7 @@ var IncremarkParser = class {
|
|
|
1384
1467
|
if (isBlockquoteStart(line) && !isBlockquoteStart(prevLine)) {
|
|
1385
1468
|
return lineIndex - 1;
|
|
1386
1469
|
}
|
|
1387
|
-
if (isListItemStart(line) && !isListItemStart(prevLine)) {
|
|
1470
|
+
if (!context.inList && isListItemStart(line) && !isListItemStart(prevLine)) {
|
|
1388
1471
|
return lineIndex - 1;
|
|
1389
1472
|
}
|
|
1390
1473
|
if (this.containerConfig !== void 0) {
|
|
@@ -1397,7 +1480,7 @@ var IncremarkParser = class {
|
|
|
1397
1480
|
}
|
|
1398
1481
|
}
|
|
1399
1482
|
}
|
|
1400
|
-
if (isEmptyLine(line) && !isEmptyLine(prevLine)) {
|
|
1483
|
+
if (isEmptyLine(line) && !isEmptyLine(prevLine) && !context.inList) {
|
|
1401
1484
|
return lineIndex;
|
|
1402
1485
|
}
|
|
1403
1486
|
return -1;
|
|
@@ -1475,7 +1558,7 @@ var IncremarkParser = class {
|
|
|
1475
1558
|
const newBlocks = this.nodesToBlocks(ast.children, stableOffset, stableText, "completed");
|
|
1476
1559
|
this.completedBlocks.push(...newBlocks);
|
|
1477
1560
|
update.completed = newBlocks;
|
|
1478
|
-
this.
|
|
1561
|
+
this.updateDefinitionsFromCompletedBlocks(newBlocks);
|
|
1479
1562
|
this.context = contextAtLine;
|
|
1480
1563
|
this.pendingStartLine = stableBoundary + 1;
|
|
1481
1564
|
}
|
|
@@ -1548,7 +1631,7 @@ var IncremarkParser = class {
|
|
|
1548
1631
|
);
|
|
1549
1632
|
this.completedBlocks.push(...finalBlocks);
|
|
1550
1633
|
update.completed = finalBlocks;
|
|
1551
|
-
this.
|
|
1634
|
+
this.updateDefinitionsFromCompletedBlocks(finalBlocks);
|
|
1552
1635
|
}
|
|
1553
1636
|
}
|
|
1554
1637
|
this.lastPendingBlocks = [];
|
|
@@ -1566,7 +1649,7 @@ var IncremarkParser = class {
|
|
|
1566
1649
|
}
|
|
1567
1650
|
/**
|
|
1568
1651
|
* 强制中断解析,将所有待处理内容标记为完成
|
|
1569
|
-
*
|
|
1652
|
+
* @deprecated 请使用 finalize() 代替,功能完全相同
|
|
1570
1653
|
*/
|
|
1571
1654
|
abort() {
|
|
1572
1655
|
return this.finalize();
|
|
@@ -1775,54 +1858,57 @@ function appendToAst(baseNode, sourceNode, startChars, endChars, accumulatedChun
|
|
|
1775
1858
|
if (endChars <= startChars) {
|
|
1776
1859
|
return baseNode;
|
|
1777
1860
|
}
|
|
1778
|
-
const
|
|
1779
|
-
if (!
|
|
1861
|
+
const fullSlice = sliceAst(sourceNode, endChars, accumulatedChunks);
|
|
1862
|
+
if (!fullSlice) {
|
|
1780
1863
|
return baseNode;
|
|
1781
1864
|
}
|
|
1782
|
-
return
|
|
1865
|
+
return smartMergeAst(baseNode, fullSlice);
|
|
1783
1866
|
}
|
|
1784
|
-
function
|
|
1785
|
-
if (baseNode.type !==
|
|
1786
|
-
return
|
|
1867
|
+
function smartMergeAst(baseNode, fullSlice) {
|
|
1868
|
+
if (baseNode.type !== fullSlice.type) {
|
|
1869
|
+
return fullSlice;
|
|
1787
1870
|
}
|
|
1788
1871
|
const base = baseNode;
|
|
1789
|
-
const
|
|
1790
|
-
if (
|
|
1791
|
-
|
|
1792
|
-
const partChunks = part.chunks || [];
|
|
1793
|
-
const mergedChunks = [...baseChunks, ...partChunks];
|
|
1794
|
-
const mergedValue = base.value + part.value;
|
|
1795
|
-
const baseStableLength = base.stableLength ?? 0;
|
|
1796
|
-
const result = {
|
|
1797
|
-
...base,
|
|
1798
|
-
value: mergedValue,
|
|
1799
|
-
stableLength: mergedChunks.length > 0 ? baseStableLength : void 0,
|
|
1800
|
-
chunks: mergedChunks.length > 0 ? mergedChunks : void 0
|
|
1801
|
-
};
|
|
1802
|
-
return result;
|
|
1872
|
+
const full = fullSlice;
|
|
1873
|
+
if (full.value !== void 0) {
|
|
1874
|
+
return fullSlice;
|
|
1803
1875
|
}
|
|
1804
|
-
if (base.children && Array.isArray(base.children) &&
|
|
1805
|
-
if (
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
return
|
|
1811
|
-
...base,
|
|
1812
|
-
children: [
|
|
1813
|
-
...base.children.slice(0, -1),
|
|
1814
|
-
merged,
|
|
1815
|
-
...part.children.slice(1)
|
|
1816
|
-
]
|
|
1817
|
-
};
|
|
1876
|
+
if (base.children && Array.isArray(base.children) && full.children && Array.isArray(full.children)) {
|
|
1877
|
+
if (full.children.length < base.children.length) {
|
|
1878
|
+
return fullSlice;
|
|
1879
|
+
}
|
|
1880
|
+
if (full.children.length === base.children.length) {
|
|
1881
|
+
if (base.children.length === 0) {
|
|
1882
|
+
return fullSlice;
|
|
1818
1883
|
}
|
|
1884
|
+
const lastIndex = base.children.length - 1;
|
|
1885
|
+
const mergedLast2 = smartMergeAst(
|
|
1886
|
+
base.children[lastIndex],
|
|
1887
|
+
full.children[lastIndex]
|
|
1888
|
+
);
|
|
1889
|
+
return {
|
|
1890
|
+
...full,
|
|
1891
|
+
children: [
|
|
1892
|
+
...base.children.slice(0, lastIndex),
|
|
1893
|
+
mergedLast2
|
|
1894
|
+
]
|
|
1895
|
+
};
|
|
1819
1896
|
}
|
|
1897
|
+
const baseLastIndex = base.children.length - 1;
|
|
1898
|
+
const mergedLast = smartMergeAst(
|
|
1899
|
+
base.children[baseLastIndex],
|
|
1900
|
+
full.children[baseLastIndex]
|
|
1901
|
+
);
|
|
1820
1902
|
return {
|
|
1821
|
-
...
|
|
1822
|
-
children: [
|
|
1903
|
+
...full,
|
|
1904
|
+
children: [
|
|
1905
|
+
...base.children.slice(0, baseLastIndex),
|
|
1906
|
+
mergedLast,
|
|
1907
|
+
...full.children.slice(base.children.length)
|
|
1908
|
+
]
|
|
1823
1909
|
};
|
|
1824
1910
|
}
|
|
1825
|
-
return
|
|
1911
|
+
return fullSlice;
|
|
1826
1912
|
}
|
|
1827
1913
|
function cloneNode(node) {
|
|
1828
1914
|
if (typeof structuredClone === "function") {
|
|
@@ -1872,6 +1958,7 @@ var BlockTransformer = class {
|
|
|
1872
1958
|
plugins: options.plugins ?? [],
|
|
1873
1959
|
onChange: options.onChange ?? (() => {
|
|
1874
1960
|
}),
|
|
1961
|
+
onAllComplete: options.onAllComplete ?? null,
|
|
1875
1962
|
pauseOnHidden: options.pauseOnHidden ?? true
|
|
1876
1963
|
};
|
|
1877
1964
|
this.state = {
|
|
@@ -1898,12 +1985,13 @@ var BlockTransformer = class {
|
|
|
1898
1985
|
if (this.state.currentBlock) {
|
|
1899
1986
|
const updated = blocks.find((b) => b.id === this.state.currentBlock.id);
|
|
1900
1987
|
if (updated && updated.node !== this.state.currentBlock.node) {
|
|
1901
|
-
this.clearCache();
|
|
1902
1988
|
const oldTotal = this.cachedTotalChars ?? this.countChars(this.state.currentBlock.node);
|
|
1903
1989
|
const newTotal = this.countChars(updated.node);
|
|
1904
1990
|
if (newTotal < oldTotal || newTotal < this.state.currentProgress) {
|
|
1905
1991
|
this.state.currentProgress = Math.min(this.state.currentProgress, newTotal);
|
|
1992
|
+
this.chunks = [];
|
|
1906
1993
|
}
|
|
1994
|
+
this.clearCache();
|
|
1907
1995
|
this.state.currentBlock = updated;
|
|
1908
1996
|
if (!this.rafId && !this.isPaused) {
|
|
1909
1997
|
if (this.state.currentProgress < newTotal) {
|
|
@@ -1920,9 +2008,15 @@ var BlockTransformer = class {
|
|
|
1920
2008
|
if (this.state.currentBlock?.id === block.id) {
|
|
1921
2009
|
const oldTotal = this.cachedTotalChars ?? this.countChars(this.state.currentBlock.node);
|
|
1922
2010
|
const newTotal = this.countChars(block.node);
|
|
2011
|
+
if (newTotal !== oldTotal) {
|
|
2012
|
+
this.clearCache();
|
|
2013
|
+
}
|
|
2014
|
+
if (newTotal < oldTotal || newTotal < this.state.currentProgress) {
|
|
2015
|
+
this.state.currentProgress = Math.min(this.state.currentProgress, newTotal);
|
|
2016
|
+
this.chunks = [];
|
|
2017
|
+
}
|
|
1923
2018
|
this.state.currentBlock = block;
|
|
1924
2019
|
if (newTotal > oldTotal && !this.rafId && !this.isPaused && this.state.currentProgress >= oldTotal) {
|
|
1925
|
-
this.clearCache();
|
|
1926
2020
|
this.startIfNeeded();
|
|
1927
2021
|
}
|
|
1928
2022
|
}
|
|
@@ -1946,6 +2040,7 @@ var BlockTransformer = class {
|
|
|
1946
2040
|
this.chunks = [];
|
|
1947
2041
|
this.clearCache();
|
|
1948
2042
|
this.emit();
|
|
2043
|
+
this.options.onAllComplete?.();
|
|
1949
2044
|
}
|
|
1950
2045
|
/**
|
|
1951
2046
|
* 重置状态
|
|
@@ -2202,6 +2297,7 @@ var BlockTransformer = class {
|
|
|
2202
2297
|
this.isRunning = false;
|
|
2203
2298
|
this.cancelRaf();
|
|
2204
2299
|
this.emit();
|
|
2300
|
+
this.options.onAllComplete?.();
|
|
2205
2301
|
}
|
|
2206
2302
|
}
|
|
2207
2303
|
cancelRaf() {
|
|
@@ -2304,10 +2400,13 @@ var BlockTransformer = class {
|
|
|
2304
2400
|
}
|
|
2305
2401
|
/**
|
|
2306
2402
|
* 获取累积的 chunks(用于 fade-in 效果)
|
|
2403
|
+
* stableChars 表示在 chunks 之前的稳定字符数
|
|
2307
2404
|
*/
|
|
2308
2405
|
getAccumulatedChunks() {
|
|
2309
2406
|
if (this.options.effect === "fade-in" && this.chunks.length > 0) {
|
|
2310
|
-
|
|
2407
|
+
const chunksLength = this.chunks.reduce((sum, c) => sum + c.text.length, 0);
|
|
2408
|
+
const stableChars = this.state.currentProgress - chunksLength;
|
|
2409
|
+
return { stableChars: Math.max(0, stableChars), chunks: this.chunks };
|
|
2311
2410
|
}
|
|
2312
2411
|
return void 0;
|
|
2313
2412
|
}
|