@incremark/core 0.2.4 → 0.2.6
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 +66 -10
- package/dist/detector/index.js.map +1 -1
- package/dist/{index-zDgyJFpf.d.ts → index-BMUkM7mT.d.ts} +18 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +177 -75
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/src/transformer/styles.css +0 -33
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';
|
|
@@ -865,14 +867,12 @@ var RE_FENCE_START = /^(\s*)((`{3,})|(~{3,}))/;
|
|
|
865
867
|
var RE_EMPTY_LINE = /^\s*$/;
|
|
866
868
|
var RE_HEADING = /^#{1,6}\s/;
|
|
867
869
|
var RE_THEMATIC_BREAK = /^(\*{3,}|-{3,}|_{3,})\s*$/;
|
|
868
|
-
var RE_UNORDERED_LIST = /^(\s*)([-*+])\s/;
|
|
869
|
-
var RE_ORDERED_LIST = /^(\s*)(\d{1,9})[.)]\s/;
|
|
870
870
|
var RE_BLOCKQUOTE = /^\s{0,3}>/;
|
|
871
871
|
var RE_HTML_BLOCK_1 = /^\s{0,3}<(script|pre|style|textarea|!--|!DOCTYPE|\?|!\[CDATA\[)/i;
|
|
872
872
|
var RE_HTML_BLOCK_2 = /^\s{0,3}<\/?[a-zA-Z][a-zA-Z0-9-]*(\s|>|$)/;
|
|
873
873
|
var RE_TABLE_DELIMITER = /^\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?$/;
|
|
874
874
|
var RE_ESCAPE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
875
|
-
var RE_FOOTNOTE_DEFINITION = /^\[\^[^\]]
|
|
875
|
+
var RE_FOOTNOTE_DEFINITION = /^\[\^([^\]]+)\]:\s/;
|
|
876
876
|
var RE_FOOTNOTE_CONTINUATION = /^(?: |\t)/;
|
|
877
877
|
var fenceEndPatternCache = /* @__PURE__ */ new Map();
|
|
878
878
|
var containerPatternCache = /* @__PURE__ */ new Map();
|
|
@@ -907,13 +907,23 @@ function isThematicBreak(line) {
|
|
|
907
907
|
return RE_THEMATIC_BREAK.test(line.trim());
|
|
908
908
|
}
|
|
909
909
|
function isListItemStart(line) {
|
|
910
|
-
const
|
|
911
|
-
if (
|
|
912
|
-
return
|
|
910
|
+
const hasListMarker = /^(\s*)([-*+]|\d{1,9}[.)])/.test(line);
|
|
911
|
+
if (!hasListMarker) {
|
|
912
|
+
return null;
|
|
913
913
|
}
|
|
914
|
-
const
|
|
915
|
-
if (
|
|
916
|
-
|
|
914
|
+
const match = line.match(/^(\s*)([-*+]|\d{1,9}[.)])(.*)/);
|
|
915
|
+
if (match) {
|
|
916
|
+
const indent = match[1].length;
|
|
917
|
+
const marker = match[2];
|
|
918
|
+
const rest = match[3];
|
|
919
|
+
if (rest.trim()) {
|
|
920
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
921
|
+
return { ordered: isOrdered, indent };
|
|
922
|
+
}
|
|
923
|
+
if (/^\s+$/.test(rest)) {
|
|
924
|
+
const isOrdered = /^\d{1,9}[.)]/.test(marker);
|
|
925
|
+
return { ordered: isOrdered, indent };
|
|
926
|
+
}
|
|
917
927
|
}
|
|
918
928
|
return null;
|
|
919
929
|
}
|
|
@@ -993,7 +1003,9 @@ function createInitialContext() {
|
|
|
993
1003
|
blockquoteDepth: 0,
|
|
994
1004
|
inContainer: false,
|
|
995
1005
|
containerDepth: 0,
|
|
996
|
-
inList: false
|
|
1006
|
+
inList: false,
|
|
1007
|
+
inFootnote: false,
|
|
1008
|
+
footnoteIdentifier: void 0
|
|
997
1009
|
};
|
|
998
1010
|
}
|
|
999
1011
|
function isListContinuation(line, listIndent) {
|
|
@@ -1049,6 +1061,17 @@ function updateContext(line, context, containerConfig) {
|
|
|
1049
1061
|
}
|
|
1050
1062
|
}
|
|
1051
1063
|
}
|
|
1064
|
+
if (context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
1065
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
1066
|
+
newContext.footnoteIdentifier = identifier;
|
|
1067
|
+
return newContext;
|
|
1068
|
+
}
|
|
1069
|
+
if (!context.inFootnote && isFootnoteDefinitionStart(line)) {
|
|
1070
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
1071
|
+
newContext.inFootnote = true;
|
|
1072
|
+
newContext.footnoteIdentifier = identifier;
|
|
1073
|
+
return newContext;
|
|
1074
|
+
}
|
|
1052
1075
|
const listItem = isListItemStart(line);
|
|
1053
1076
|
if (context.inList) {
|
|
1054
1077
|
if (context.listMayEnd) {
|
|
@@ -1097,6 +1120,41 @@ function updateContext(line, context, containerConfig) {
|
|
|
1097
1120
|
return newContext;
|
|
1098
1121
|
}
|
|
1099
1122
|
}
|
|
1123
|
+
if (context.inFootnote) {
|
|
1124
|
+
if (isEmptyLine(line)) {
|
|
1125
|
+
return newContext;
|
|
1126
|
+
} else if (isListItemStart(line)) {
|
|
1127
|
+
const listItemInfo = isListItemStart(line);
|
|
1128
|
+
if (listItemInfo.indent === 0) {
|
|
1129
|
+
newContext.inFootnote = false;
|
|
1130
|
+
newContext.footnoteIdentifier = void 0;
|
|
1131
|
+
} else {
|
|
1132
|
+
return newContext;
|
|
1133
|
+
}
|
|
1134
|
+
} else if (isHeading(line)) {
|
|
1135
|
+
newContext.inFootnote = false;
|
|
1136
|
+
newContext.footnoteIdentifier = void 0;
|
|
1137
|
+
return newContext;
|
|
1138
|
+
} else if (detectFenceStart(line)) {
|
|
1139
|
+
newContext.inFootnote = false;
|
|
1140
|
+
newContext.footnoteIdentifier = void 0;
|
|
1141
|
+
return newContext;
|
|
1142
|
+
} else if (isBlockquoteStart(line)) {
|
|
1143
|
+
newContext.inFootnote = false;
|
|
1144
|
+
newContext.footnoteIdentifier = void 0;
|
|
1145
|
+
return newContext;
|
|
1146
|
+
} else if (isFootnoteContinuation(line)) {
|
|
1147
|
+
return newContext;
|
|
1148
|
+
} else if (isFootnoteDefinitionStart(line)) {
|
|
1149
|
+
const identifier = line.match(RE_FOOTNOTE_DEFINITION)?.[1];
|
|
1150
|
+
newContext.footnoteIdentifier = identifier;
|
|
1151
|
+
return newContext;
|
|
1152
|
+
} else {
|
|
1153
|
+
newContext.inFootnote = false;
|
|
1154
|
+
newContext.footnoteIdentifier = void 0;
|
|
1155
|
+
return newContext;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1100
1158
|
return newContext;
|
|
1101
1159
|
}
|
|
1102
1160
|
|
|
@@ -1245,6 +1303,10 @@ var IncremarkParser = class {
|
|
|
1245
1303
|
extensions.push(gfm());
|
|
1246
1304
|
mdastExtensions.push(...gfmFromMarkdown(), gfmFootnoteFromMarkdown());
|
|
1247
1305
|
}
|
|
1306
|
+
if (this.options.math) {
|
|
1307
|
+
extensions.push(math());
|
|
1308
|
+
mdastExtensions.push(mathFromMarkdown());
|
|
1309
|
+
}
|
|
1248
1310
|
if (this.containerConfig !== void 0) {
|
|
1249
1311
|
extensions.push(directive());
|
|
1250
1312
|
mdastExtensions.push(directiveFromMarkdown());
|
|
@@ -1267,7 +1329,7 @@ var IncremarkParser = class {
|
|
|
1267
1329
|
}
|
|
1268
1330
|
return ast;
|
|
1269
1331
|
}
|
|
1270
|
-
|
|
1332
|
+
updateDefinitionsFromCompletedBlocks(blocks) {
|
|
1271
1333
|
for (const block of blocks) {
|
|
1272
1334
|
this.definitionMap = {
|
|
1273
1335
|
...this.definitionMap,
|
|
@@ -1281,17 +1343,17 @@ var IncremarkParser = class {
|
|
|
1281
1343
|
}
|
|
1282
1344
|
findDefinition(block) {
|
|
1283
1345
|
const definitions = [];
|
|
1284
|
-
function
|
|
1346
|
+
function findDefinitionRecursive(node) {
|
|
1285
1347
|
if (isDefinitionNode(node)) {
|
|
1286
1348
|
definitions.push(node);
|
|
1287
1349
|
}
|
|
1288
1350
|
if ("children" in node && Array.isArray(node.children)) {
|
|
1289
1351
|
for (const child of node.children) {
|
|
1290
|
-
|
|
1352
|
+
findDefinitionRecursive(child);
|
|
1291
1353
|
}
|
|
1292
1354
|
}
|
|
1293
1355
|
}
|
|
1294
|
-
|
|
1356
|
+
findDefinitionRecursive(block.node);
|
|
1295
1357
|
return definitions.reduce((acc, node) => {
|
|
1296
1358
|
acc[node.identifier] = node;
|
|
1297
1359
|
return acc;
|
|
@@ -1552,7 +1614,7 @@ var IncremarkParser = class {
|
|
|
1552
1614
|
const newBlocks = this.nodesToBlocks(ast.children, stableOffset, stableText, "completed");
|
|
1553
1615
|
this.completedBlocks.push(...newBlocks);
|
|
1554
1616
|
update.completed = newBlocks;
|
|
1555
|
-
this.
|
|
1617
|
+
this.updateDefinitionsFromCompletedBlocks(newBlocks);
|
|
1556
1618
|
this.context = contextAtLine;
|
|
1557
1619
|
this.pendingStartLine = stableBoundary + 1;
|
|
1558
1620
|
}
|
|
@@ -1625,7 +1687,7 @@ var IncremarkParser = class {
|
|
|
1625
1687
|
);
|
|
1626
1688
|
this.completedBlocks.push(...finalBlocks);
|
|
1627
1689
|
update.completed = finalBlocks;
|
|
1628
|
-
this.
|
|
1690
|
+
this.updateDefinitionsFromCompletedBlocks(finalBlocks);
|
|
1629
1691
|
}
|
|
1630
1692
|
}
|
|
1631
1693
|
this.lastPendingBlocks = [];
|
|
@@ -1643,7 +1705,7 @@ var IncremarkParser = class {
|
|
|
1643
1705
|
}
|
|
1644
1706
|
/**
|
|
1645
1707
|
* 强制中断解析,将所有待处理内容标记为完成
|
|
1646
|
-
*
|
|
1708
|
+
* @deprecated 请使用 finalize() 代替,功能完全相同
|
|
1647
1709
|
*/
|
|
1648
1710
|
abort() {
|
|
1649
1711
|
return this.finalize();
|
|
@@ -1852,54 +1914,57 @@ function appendToAst(baseNode, sourceNode, startChars, endChars, accumulatedChun
|
|
|
1852
1914
|
if (endChars <= startChars) {
|
|
1853
1915
|
return baseNode;
|
|
1854
1916
|
}
|
|
1855
|
-
const
|
|
1856
|
-
if (!
|
|
1917
|
+
const fullSlice = sliceAst(sourceNode, endChars, accumulatedChunks);
|
|
1918
|
+
if (!fullSlice) {
|
|
1857
1919
|
return baseNode;
|
|
1858
1920
|
}
|
|
1859
|
-
return
|
|
1921
|
+
return smartMergeAst(baseNode, fullSlice);
|
|
1860
1922
|
}
|
|
1861
|
-
function
|
|
1862
|
-
if (baseNode.type !==
|
|
1863
|
-
return
|
|
1923
|
+
function smartMergeAst(baseNode, fullSlice) {
|
|
1924
|
+
if (baseNode.type !== fullSlice.type) {
|
|
1925
|
+
return fullSlice;
|
|
1864
1926
|
}
|
|
1865
1927
|
const base = baseNode;
|
|
1866
|
-
const
|
|
1867
|
-
if (
|
|
1868
|
-
|
|
1869
|
-
const partChunks = part.chunks || [];
|
|
1870
|
-
const mergedChunks = [...baseChunks, ...partChunks];
|
|
1871
|
-
const mergedValue = base.value + part.value;
|
|
1872
|
-
const baseStableLength = base.stableLength ?? 0;
|
|
1873
|
-
const result = {
|
|
1874
|
-
...base,
|
|
1875
|
-
value: mergedValue,
|
|
1876
|
-
stableLength: mergedChunks.length > 0 ? baseStableLength : void 0,
|
|
1877
|
-
chunks: mergedChunks.length > 0 ? mergedChunks : void 0
|
|
1878
|
-
};
|
|
1879
|
-
return result;
|
|
1928
|
+
const full = fullSlice;
|
|
1929
|
+
if (full.value !== void 0) {
|
|
1930
|
+
return fullSlice;
|
|
1880
1931
|
}
|
|
1881
|
-
if (base.children && Array.isArray(base.children) &&
|
|
1882
|
-
if (
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
return
|
|
1888
|
-
...base,
|
|
1889
|
-
children: [
|
|
1890
|
-
...base.children.slice(0, -1),
|
|
1891
|
-
merged,
|
|
1892
|
-
...part.children.slice(1)
|
|
1893
|
-
]
|
|
1894
|
-
};
|
|
1932
|
+
if (base.children && Array.isArray(base.children) && full.children && Array.isArray(full.children)) {
|
|
1933
|
+
if (full.children.length < base.children.length) {
|
|
1934
|
+
return fullSlice;
|
|
1935
|
+
}
|
|
1936
|
+
if (full.children.length === base.children.length) {
|
|
1937
|
+
if (base.children.length === 0) {
|
|
1938
|
+
return fullSlice;
|
|
1895
1939
|
}
|
|
1940
|
+
const lastIndex = base.children.length - 1;
|
|
1941
|
+
const mergedLast2 = smartMergeAst(
|
|
1942
|
+
base.children[lastIndex],
|
|
1943
|
+
full.children[lastIndex]
|
|
1944
|
+
);
|
|
1945
|
+
return {
|
|
1946
|
+
...full,
|
|
1947
|
+
children: [
|
|
1948
|
+
...base.children.slice(0, lastIndex),
|
|
1949
|
+
mergedLast2
|
|
1950
|
+
]
|
|
1951
|
+
};
|
|
1896
1952
|
}
|
|
1953
|
+
const baseLastIndex = base.children.length - 1;
|
|
1954
|
+
const mergedLast = smartMergeAst(
|
|
1955
|
+
base.children[baseLastIndex],
|
|
1956
|
+
full.children[baseLastIndex]
|
|
1957
|
+
);
|
|
1897
1958
|
return {
|
|
1898
|
-
...
|
|
1899
|
-
children: [
|
|
1959
|
+
...full,
|
|
1960
|
+
children: [
|
|
1961
|
+
...base.children.slice(0, baseLastIndex),
|
|
1962
|
+
mergedLast,
|
|
1963
|
+
...full.children.slice(base.children.length)
|
|
1964
|
+
]
|
|
1900
1965
|
};
|
|
1901
1966
|
}
|
|
1902
|
-
return
|
|
1967
|
+
return fullSlice;
|
|
1903
1968
|
}
|
|
1904
1969
|
function cloneNode(node) {
|
|
1905
1970
|
if (typeof structuredClone === "function") {
|
|
@@ -1949,6 +2014,7 @@ var BlockTransformer = class {
|
|
|
1949
2014
|
plugins: options.plugins ?? [],
|
|
1950
2015
|
onChange: options.onChange ?? (() => {
|
|
1951
2016
|
}),
|
|
2017
|
+
onAllComplete: options.onAllComplete ?? null,
|
|
1952
2018
|
pauseOnHidden: options.pauseOnHidden ?? true
|
|
1953
2019
|
};
|
|
1954
2020
|
this.state = {
|
|
@@ -1975,18 +2041,8 @@ var BlockTransformer = class {
|
|
|
1975
2041
|
if (this.state.currentBlock) {
|
|
1976
2042
|
const updated = blocks.find((b) => b.id === this.state.currentBlock.id);
|
|
1977
2043
|
if (updated && updated.node !== this.state.currentBlock.node) {
|
|
1978
|
-
this.
|
|
1979
|
-
const oldTotal = this.cachedTotalChars ?? this.countChars(this.state.currentBlock.node);
|
|
1980
|
-
const newTotal = this.countChars(updated.node);
|
|
1981
|
-
if (newTotal < oldTotal || newTotal < this.state.currentProgress) {
|
|
1982
|
-
this.state.currentProgress = Math.min(this.state.currentProgress, newTotal);
|
|
1983
|
-
}
|
|
2044
|
+
this.handleContentChange(this.state.currentBlock.node, updated.node, true);
|
|
1984
2045
|
this.state.currentBlock = updated;
|
|
1985
|
-
if (!this.rafId && !this.isPaused) {
|
|
1986
|
-
if (this.state.currentProgress < newTotal) {
|
|
1987
|
-
this.startIfNeeded();
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
2046
|
}
|
|
1991
2047
|
}
|
|
1992
2048
|
}
|
|
@@ -1995,13 +2051,8 @@ var BlockTransformer = class {
|
|
|
1995
2051
|
*/
|
|
1996
2052
|
update(block) {
|
|
1997
2053
|
if (this.state.currentBlock?.id === block.id) {
|
|
1998
|
-
|
|
1999
|
-
const newTotal = this.countChars(block.node);
|
|
2054
|
+
this.handleContentChange(this.state.currentBlock.node, block.node, false);
|
|
2000
2055
|
this.state.currentBlock = block;
|
|
2001
|
-
if (newTotal > oldTotal && !this.rafId && !this.isPaused && this.state.currentProgress >= oldTotal) {
|
|
2002
|
-
this.clearCache();
|
|
2003
|
-
this.startIfNeeded();
|
|
2004
|
-
}
|
|
2005
2056
|
}
|
|
2006
2057
|
}
|
|
2007
2058
|
/**
|
|
@@ -2023,6 +2074,7 @@ var BlockTransformer = class {
|
|
|
2023
2074
|
this.chunks = [];
|
|
2024
2075
|
this.clearCache();
|
|
2025
2076
|
this.emit();
|
|
2077
|
+
this.options.onAllComplete?.();
|
|
2026
2078
|
}
|
|
2027
2079
|
/**
|
|
2028
2080
|
* 重置状态
|
|
@@ -2147,6 +2199,30 @@ var BlockTransformer = class {
|
|
|
2147
2199
|
this.removeVisibilityHandler();
|
|
2148
2200
|
}
|
|
2149
2201
|
// ============ 私有方法 ============
|
|
2202
|
+
/**
|
|
2203
|
+
* 处理 block 内容更新时的字符数变化和进度调整
|
|
2204
|
+
* 统一 push 和 update 方法中的重复逻辑
|
|
2205
|
+
*/
|
|
2206
|
+
handleContentChange(oldNode, newNode, isUpdateFromPush) {
|
|
2207
|
+
const oldTotal = this.cachedTotalChars ?? this.countChars(oldNode);
|
|
2208
|
+
const newTotal = this.countChars(newNode);
|
|
2209
|
+
if (newTotal < oldTotal || newTotal < this.state.currentProgress) {
|
|
2210
|
+
this.state.currentProgress = Math.min(this.state.currentProgress, newTotal);
|
|
2211
|
+
this.chunks = [];
|
|
2212
|
+
}
|
|
2213
|
+
this.clearCache();
|
|
2214
|
+
if (isUpdateFromPush) {
|
|
2215
|
+
if (!this.rafId && !this.isPaused) {
|
|
2216
|
+
if (this.state.currentProgress < newTotal) {
|
|
2217
|
+
this.startIfNeeded();
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
} else {
|
|
2221
|
+
if (newTotal > oldTotal && !this.rafId && !this.isPaused && this.state.currentProgress >= oldTotal) {
|
|
2222
|
+
this.startIfNeeded();
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2150
2226
|
getAllBlockIds() {
|
|
2151
2227
|
return new Set([
|
|
2152
2228
|
...this.state.completedBlocks.map((b) => b.id),
|
|
@@ -2233,8 +2309,21 @@ var BlockTransformer = class {
|
|
|
2233
2309
|
}
|
|
2234
2310
|
/**
|
|
2235
2311
|
* 从 AST 节点中提取指定范围的文本
|
|
2312
|
+
*
|
|
2313
|
+
* 优化说明:
|
|
2314
|
+
* - 提前终止:当 charIndex >= end 时立即返回,避免不必要的遍历
|
|
2315
|
+
* - 局部更新:charIndex 只在需要时更新,减少计算
|
|
2316
|
+
* - 早期返回:发现足够的文本后可以提前退出(当前未实现,可作为未来优化)
|
|
2317
|
+
*
|
|
2318
|
+
* @param node 要提取文本的 AST 节点
|
|
2319
|
+
* @param start 起始字符索引(包含)
|
|
2320
|
+
* @param end 结束字符索引(不包含)
|
|
2321
|
+
* @returns 提取的文本
|
|
2236
2322
|
*/
|
|
2237
2323
|
extractText(node, start, end) {
|
|
2324
|
+
if (start >= end) {
|
|
2325
|
+
return "";
|
|
2326
|
+
}
|
|
2238
2327
|
let result = "";
|
|
2239
2328
|
let charIndex = 0;
|
|
2240
2329
|
function traverse(n) {
|
|
@@ -2242,12 +2331,12 @@ var BlockTransformer = class {
|
|
|
2242
2331
|
if (n.value && typeof n.value === "string") {
|
|
2243
2332
|
const nodeStart = charIndex;
|
|
2244
2333
|
const nodeEnd = charIndex + n.value.length;
|
|
2245
|
-
charIndex = nodeEnd;
|
|
2246
2334
|
const overlapStart = Math.max(start, nodeStart);
|
|
2247
2335
|
const overlapEnd = Math.min(end, nodeEnd);
|
|
2248
2336
|
if (overlapStart < overlapEnd) {
|
|
2249
2337
|
result += n.value.slice(overlapStart - nodeStart, overlapEnd - nodeStart);
|
|
2250
2338
|
}
|
|
2339
|
+
charIndex = nodeEnd;
|
|
2251
2340
|
return charIndex < end;
|
|
2252
2341
|
}
|
|
2253
2342
|
if (n.children && Array.isArray(n.children)) {
|
|
@@ -2279,6 +2368,7 @@ var BlockTransformer = class {
|
|
|
2279
2368
|
this.isRunning = false;
|
|
2280
2369
|
this.cancelRaf();
|
|
2281
2370
|
this.emit();
|
|
2371
|
+
this.options.onAllComplete?.();
|
|
2282
2372
|
}
|
|
2283
2373
|
}
|
|
2284
2374
|
cancelRaf() {
|
|
@@ -2364,12 +2454,21 @@ var BlockTransformer = class {
|
|
|
2364
2454
|
}
|
|
2365
2455
|
/**
|
|
2366
2456
|
* 获取总字符数(带缓存)
|
|
2457
|
+
*
|
|
2458
|
+
* 缓存策略:
|
|
2459
|
+
* - 首次调用时计算并缓存
|
|
2460
|
+
* - 内容更新时通过 clearCache() 清除缓存,下次重新计算
|
|
2461
|
+
* - 切换到新 block 时也会清除缓存
|
|
2367
2462
|
*/
|
|
2368
2463
|
getTotalChars() {
|
|
2369
|
-
if (this.
|
|
2464
|
+
if (!this.state.currentBlock) {
|
|
2465
|
+
this.cachedTotalChars = null;
|
|
2466
|
+
return 0;
|
|
2467
|
+
}
|
|
2468
|
+
if (this.cachedTotalChars === null) {
|
|
2370
2469
|
this.cachedTotalChars = this.countChars(this.state.currentBlock.node);
|
|
2371
2470
|
}
|
|
2372
|
-
return this.cachedTotalChars
|
|
2471
|
+
return this.cachedTotalChars;
|
|
2373
2472
|
}
|
|
2374
2473
|
/**
|
|
2375
2474
|
* 清除缓存(当 block 切换或内容更新时)
|
|
@@ -2381,10 +2480,13 @@ var BlockTransformer = class {
|
|
|
2381
2480
|
}
|
|
2382
2481
|
/**
|
|
2383
2482
|
* 获取累积的 chunks(用于 fade-in 效果)
|
|
2483
|
+
* stableChars 表示在 chunks 之前的稳定字符数
|
|
2384
2484
|
*/
|
|
2385
2485
|
getAccumulatedChunks() {
|
|
2386
2486
|
if (this.options.effect === "fade-in" && this.chunks.length > 0) {
|
|
2387
|
-
|
|
2487
|
+
const chunksLength = this.chunks.reduce((sum, c) => sum + c.text.length, 0);
|
|
2488
|
+
const stableChars = this.state.currentProgress - chunksLength;
|
|
2489
|
+
return { stableChars: Math.max(0, stableChars), chunks: this.chunks };
|
|
2388
2490
|
}
|
|
2389
2491
|
return void 0;
|
|
2390
2492
|
}
|