@stream-mdx/react 0.4.0 → 0.5.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 +14 -4
- package/README.md +65 -15
- package/dist/components/bottom-stick-scroll-area.cjs +344 -0
- package/dist/components/bottom-stick-scroll-area.d.cts +36 -0
- package/dist/components/bottom-stick-scroll-area.d.ts +36 -0
- package/dist/components/bottom-stick-scroll-area.mjs +320 -0
- package/dist/components/index.cjs +503 -172
- package/dist/components/index.d.cts +3 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.mjs +350 -22
- package/dist/{index-D2MYusAM.d.cts → index-BVhp_pcF.d.ts} +3 -0
- package/dist/{index-D2MYusAM.d.ts → index-EaeouYp2.d.cts} +3 -0
- package/dist/index.cjs +1472 -486
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.mjs +1256 -272
- package/dist/mdx-client.cjs +1 -1
- package/dist/mdx-client.mjs +1 -1
- package/dist/mdx-coordinator.cjs +28 -7
- package/dist/mdx-coordinator.d.cts +2 -2
- package/dist/mdx-coordinator.d.ts +2 -2
- package/dist/mdx-coordinator.mjs +28 -7
- package/dist/renderer/node-views.cjs +271 -26
- package/dist/renderer/node-views.d.cts +3 -1
- package/dist/renderer/node-views.d.ts +3 -1
- package/dist/renderer/node-views.mjs +272 -27
- package/dist/renderer/patch-batching.cjs +2 -0
- package/dist/renderer/patch-batching.d.cts +1 -1
- package/dist/renderer/patch-batching.d.ts +1 -1
- package/dist/renderer/patch-batching.mjs +2 -1
- package/dist/renderer/patch-commit-scheduler.cjs +82 -30
- package/dist/renderer/patch-commit-scheduler.d.cts +15 -3
- package/dist/renderer/patch-commit-scheduler.d.ts +15 -3
- package/dist/renderer/patch-commit-scheduler.mjs +84 -30
- package/dist/renderer/store.cjs +382 -36
- package/dist/renderer/store.d.cts +3 -0
- package/dist/renderer/store.d.ts +3 -0
- package/dist/renderer/store.mjs +382 -36
- package/dist/renderer.cjs +758 -114
- package/dist/renderer.d.cts +3 -1
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.mjs +760 -116
- package/dist/server.cjs +669 -71
- package/dist/server.d.cts +5 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.mjs +670 -72
- package/dist/{streaming-markdown-DOc2uLiP.d.ts → streaming-markdown-CEI9eDZm.d.ts} +3 -1
- package/dist/{streaming-markdown-D-JcByd9.d.cts → streaming-markdown-DRisClXb.d.cts} +3 -1
- package/dist/streaming-markdown.cjs +788 -121
- package/dist/streaming-markdown.d.cts +4 -2
- package/dist/streaming-markdown.d.ts +4 -2
- package/dist/streaming-markdown.mjs +790 -123
- package/dist/utils/inline-html.d.cts +3 -1
- package/dist/utils/inline-html.d.ts +3 -1
- package/package.json +14 -3
- package/shadcn/README.md +28 -0
- package/shadcn/bottom-stick-scroll-area.tsx +358 -0
package/dist/server.cjs
CHANGED
|
@@ -102,7 +102,7 @@ var init_mdx_client = __esm({
|
|
|
102
102
|
if (block.type !== "mdx") {
|
|
103
103
|
throw new Error("Block is not MDX type");
|
|
104
104
|
}
|
|
105
|
-
const cacheKey = block.id
|
|
105
|
+
const cacheKey = `${block.id}\0${block.payload.raw ?? ""}`;
|
|
106
106
|
const cachedPromise = this.cache.get(cacheKey);
|
|
107
107
|
if (cachedPromise) {
|
|
108
108
|
return cachedPromise;
|
|
@@ -972,7 +972,11 @@ var defaultInlineComponents = {
|
|
|
972
972
|
const n = number ?? "?";
|
|
973
973
|
const href = number ? `#fn:${n}` : void 0;
|
|
974
974
|
const id = number ? `fnref:${n}` : void 0;
|
|
975
|
-
return import_react6.default.createElement(
|
|
975
|
+
return import_react6.default.createElement(
|
|
976
|
+
"sup",
|
|
977
|
+
{ className: "footnote-ref" },
|
|
978
|
+
import_react6.default.createElement("a", { href, id, "data-label": label, style: { color: "var(--foreground)", textDecoration: "underline" } }, String(n))
|
|
979
|
+
);
|
|
976
980
|
}
|
|
977
981
|
};
|
|
978
982
|
var defaultBlockComponents = {
|
|
@@ -1115,7 +1119,7 @@ var defaultBlockComponents = {
|
|
|
1115
1119
|
dangerouslySetInnerHTML: { __html: htmlString }
|
|
1116
1120
|
});
|
|
1117
1121
|
},
|
|
1118
|
-
mdx: ({ compiledRef, compiledModule, status, errorMessage
|
|
1122
|
+
mdx: ({ compiledRef, compiledModule, status, errorMessage }) => {
|
|
1119
1123
|
const [Comp, setComp] = import_react6.default.useState(null);
|
|
1120
1124
|
const [internalError, setInternalError] = import_react6.default.useState(null);
|
|
1121
1125
|
const [renderError, setRenderError] = import_react6.default.useState(null);
|
|
@@ -1216,7 +1220,6 @@ var defaultBlockComponents = {
|
|
|
1216
1220
|
const waitingForVisibility = deferHydration && !shouldHydrate && Boolean(resolvedId);
|
|
1217
1221
|
const pendingLabel = waitingForVisibility ? "MDX ready (hydrate on view)\u2026" : compileMode === "worker" ? "Compiling MDX (client)\u2026" : "Compiling MDX (server)\u2026";
|
|
1218
1222
|
const message = failureMessage ?? (compileMode === "worker" ? "MDX compilation failed in client mode. Try switching back to server rendering or restarting the stream." : "MDX compilation failed. Try restarting the stream.");
|
|
1219
|
-
const rawFallback = typeof raw === "string" && raw.trim().length > 0 ? import_react6.default.createElement("pre", { className: "markdown-mdx-fallback" }, raw) : null;
|
|
1220
1223
|
const allowRetry = Boolean(internalError || renderError);
|
|
1221
1224
|
const retryButton = allowRetry ? import_react6.default.createElement(
|
|
1222
1225
|
"button",
|
|
@@ -1228,9 +1231,10 @@ var defaultBlockComponents = {
|
|
|
1228
1231
|
"Retry hydration"
|
|
1229
1232
|
) : null;
|
|
1230
1233
|
const errorPanel = import_react6.default.createElement(
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
import_react6.default.createElement(
|
|
1234
|
+
import_react6.default.Fragment,
|
|
1235
|
+
null,
|
|
1236
|
+
import_react6.default.createElement("strong", null, "MDX failed"),
|
|
1237
|
+
import_react6.default.createElement("div", null, message)
|
|
1234
1238
|
);
|
|
1235
1239
|
if (Comp && !failureMessage) {
|
|
1236
1240
|
return import_react6.default.createElement(
|
|
@@ -1253,14 +1257,13 @@ var defaultBlockComponents = {
|
|
|
1253
1257
|
return import_react6.default.createElement(
|
|
1254
1258
|
"div",
|
|
1255
1259
|
{
|
|
1256
|
-
className: "markdown-mdx",
|
|
1260
|
+
className: "markdown-mdx error",
|
|
1257
1261
|
ref: containerRef,
|
|
1258
1262
|
"data-mdx-ref": pendingId,
|
|
1259
1263
|
"data-mdx-status": "error",
|
|
1260
1264
|
"data-mdx-mode": compileMode
|
|
1261
1265
|
},
|
|
1262
1266
|
errorPanel,
|
|
1263
|
-
rawFallback,
|
|
1264
1267
|
retryButton
|
|
1265
1268
|
);
|
|
1266
1269
|
}
|
|
@@ -1287,7 +1290,16 @@ var defaultBlockComponents = {
|
|
|
1287
1290
|
{ key: item.number, id: `fn:${item.number}` },
|
|
1288
1291
|
renderInlineNodes(item.inlines, defaultInlineComponents),
|
|
1289
1292
|
" ",
|
|
1290
|
-
import_react6.default.createElement(
|
|
1293
|
+
import_react6.default.createElement(
|
|
1294
|
+
"a",
|
|
1295
|
+
{
|
|
1296
|
+
href: `#fnref:${item.number}`,
|
|
1297
|
+
className: "footnote-backref",
|
|
1298
|
+
"aria-label": "Back to content",
|
|
1299
|
+
style: { color: "var(--foreground)", textDecoration: "underline" }
|
|
1300
|
+
},
|
|
1301
|
+
"\u21A9"
|
|
1302
|
+
)
|
|
1291
1303
|
)
|
|
1292
1304
|
);
|
|
1293
1305
|
return import_react6.default.createElement("section", { className: "footnotes" }, import_react6.default.createElement("hr", {}), import_react6.default.createElement("ol", {}, listItems));
|
|
@@ -1835,6 +1847,7 @@ var DeferredRenderContext = import_react8.default.createContext(null);
|
|
|
1835
1847
|
|
|
1836
1848
|
// src/renderer/store.ts
|
|
1837
1849
|
var import_core4 = require("@stream-mdx/core");
|
|
1850
|
+
var import_patch_batching = require("@stream-mdx/core/perf/patch-batching");
|
|
1838
1851
|
|
|
1839
1852
|
// src/renderer/list-utils.ts
|
|
1840
1853
|
function updateNodeDepth(node, depth) {
|
|
@@ -1933,12 +1946,28 @@ function snapshotToBlock(node) {
|
|
|
1933
1946
|
}
|
|
1934
1947
|
return null;
|
|
1935
1948
|
}
|
|
1936
|
-
var
|
|
1937
|
-
var
|
|
1949
|
+
var nodeSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
|
|
1950
|
+
var childrenSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
|
|
1938
1951
|
var NODE_SNAPSHOT_CACHE_LIMIT = 5e4;
|
|
1939
1952
|
var CHILDREN_SNAPSHOT_CACHE_LIMIT = 5e4;
|
|
1940
1953
|
var NODE_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(NODE_SNAPSHOT_CACHE_LIMIT * 0.1));
|
|
1941
1954
|
var CHILDREN_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(CHILDREN_SNAPSHOT_CACHE_LIMIT * 0.1));
|
|
1955
|
+
function getNodeSnapshotCacheForMap(map) {
|
|
1956
|
+
let cache = nodeSnapshotCacheByMap.get(map);
|
|
1957
|
+
if (!cache) {
|
|
1958
|
+
cache = /* @__PURE__ */ new Map();
|
|
1959
|
+
nodeSnapshotCacheByMap.set(map, cache);
|
|
1960
|
+
}
|
|
1961
|
+
return cache;
|
|
1962
|
+
}
|
|
1963
|
+
function getChildrenSnapshotCacheForMap(map) {
|
|
1964
|
+
let cache = childrenSnapshotCacheByMap.get(map);
|
|
1965
|
+
if (!cache) {
|
|
1966
|
+
cache = /* @__PURE__ */ new Map();
|
|
1967
|
+
childrenSnapshotCacheByMap.set(map, cache);
|
|
1968
|
+
}
|
|
1969
|
+
return cache;
|
|
1970
|
+
}
|
|
1942
1971
|
function pruneCache(cache, max, buffer) {
|
|
1943
1972
|
if (cache.size <= max + buffer) return;
|
|
1944
1973
|
let remaining = cache.size - max;
|
|
@@ -1948,6 +1977,14 @@ function pruneCache(cache, max, buffer) {
|
|
|
1948
1977
|
if (remaining <= 0) break;
|
|
1949
1978
|
}
|
|
1950
1979
|
}
|
|
1980
|
+
function deleteSnapshotCacheEntry(map, id) {
|
|
1981
|
+
getNodeSnapshotCacheForMap(map).delete(id);
|
|
1982
|
+
getChildrenSnapshotCacheForMap(map).delete(id);
|
|
1983
|
+
}
|
|
1984
|
+
function clearSnapshotCaches(map) {
|
|
1985
|
+
getNodeSnapshotCacheForMap(map).clear();
|
|
1986
|
+
getChildrenSnapshotCacheForMap(map).clear();
|
|
1987
|
+
}
|
|
1951
1988
|
var EMPTY_CHILDREN = Object.freeze([]);
|
|
1952
1989
|
var EMPTY_NODE_SNAPSHOT = Object.freeze({ version: -1, node: void 0 });
|
|
1953
1990
|
var EMPTY_CHILDREN_SNAPSHOT = Object.freeze({
|
|
@@ -2115,9 +2152,57 @@ function createRootRecord() {
|
|
|
2115
2152
|
parentId: null,
|
|
2116
2153
|
children: [],
|
|
2117
2154
|
props: {},
|
|
2118
|
-
version: 0
|
|
2155
|
+
version: 0,
|
|
2156
|
+
blockEpoch: 0
|
|
2119
2157
|
};
|
|
2120
2158
|
}
|
|
2159
|
+
function readNumericEpoch(value) {
|
|
2160
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
2161
|
+
return void 0;
|
|
2162
|
+
}
|
|
2163
|
+
return value;
|
|
2164
|
+
}
|
|
2165
|
+
function readSnapshotBlockEpoch(snapshot) {
|
|
2166
|
+
return readNumericEpoch(snapshot.meta?.blockEpoch);
|
|
2167
|
+
}
|
|
2168
|
+
function readPatchBlockEpoch(patch) {
|
|
2169
|
+
if (patch.op === "setHTML") {
|
|
2170
|
+
return readNumericEpoch(patch.patchMeta?.blockEpoch);
|
|
2171
|
+
}
|
|
2172
|
+
return readNumericEpoch(patch.meta?.blockEpoch);
|
|
2173
|
+
}
|
|
2174
|
+
function readPatchParseEpoch(patch) {
|
|
2175
|
+
if (patch.op === "setHTML") {
|
|
2176
|
+
return readNumericEpoch(patch.patchMeta?.parseEpoch);
|
|
2177
|
+
}
|
|
2178
|
+
return readNumericEpoch(patch.meta?.parseEpoch);
|
|
2179
|
+
}
|
|
2180
|
+
function readPatchTx(patch) {
|
|
2181
|
+
if (patch.op === "setHTML") {
|
|
2182
|
+
return readNumericEpoch(patch.patchMeta?.tx);
|
|
2183
|
+
}
|
|
2184
|
+
return readNumericEpoch(patch.meta?.tx);
|
|
2185
|
+
}
|
|
2186
|
+
function assignSubtreeBlockEpoch(map, rootId, blockEpoch, touched) {
|
|
2187
|
+
const root = map.get(rootId);
|
|
2188
|
+
if (!root) return;
|
|
2189
|
+
const stack = [root];
|
|
2190
|
+
while (stack.length > 0) {
|
|
2191
|
+
const node = stack.pop();
|
|
2192
|
+
if (!node) continue;
|
|
2193
|
+
if (node.blockEpoch !== blockEpoch) {
|
|
2194
|
+
node.blockEpoch = blockEpoch;
|
|
2195
|
+
node.version += 1;
|
|
2196
|
+
touched?.add(node.id);
|
|
2197
|
+
}
|
|
2198
|
+
for (const childId of node.children) {
|
|
2199
|
+
const child = map.get(childId);
|
|
2200
|
+
if (child) {
|
|
2201
|
+
stack.push(child);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2121
2206
|
function ensureArray(value) {
|
|
2122
2207
|
return Array.isArray(value) ? value : [];
|
|
2123
2208
|
}
|
|
@@ -2177,7 +2262,7 @@ function escapeHtml2(value) {
|
|
|
2177
2262
|
}
|
|
2178
2263
|
function stripOuterLineSpan(html) {
|
|
2179
2264
|
if (!html) return null;
|
|
2180
|
-
const openTagMatch = html.match(/<span[^>]*>/i);
|
|
2265
|
+
const openTagMatch = html.match(/<span[^>]*class="[^"]*\bline\b[^"]*"[^>]*>/i);
|
|
2181
2266
|
if (!openTagMatch) {
|
|
2182
2267
|
return null;
|
|
2183
2268
|
}
|
|
@@ -2226,8 +2311,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
2226
2311
|
if (node.type !== "code-line") {
|
|
2227
2312
|
anomalies.push(`type-mismatch:${node.id}:${node.type}`);
|
|
2228
2313
|
nodes.delete(node.id);
|
|
2229
|
-
|
|
2230
|
-
childrenSnapshotCache.delete(node.id);
|
|
2314
|
+
deleteSnapshotCacheEntry(nodes, node.id);
|
|
2231
2315
|
touched.add(node.id);
|
|
2232
2316
|
continue;
|
|
2233
2317
|
}
|
|
@@ -2271,16 +2355,14 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
2271
2355
|
const existing = nodes.get(desiredId);
|
|
2272
2356
|
if (existing && existing !== node) {
|
|
2273
2357
|
nodes.delete(desiredId);
|
|
2274
|
-
|
|
2275
|
-
childrenSnapshotCache.delete(desiredId);
|
|
2358
|
+
deleteSnapshotCacheEntry(nodes, desiredId);
|
|
2276
2359
|
touched.add(desiredId);
|
|
2277
2360
|
mutated = true;
|
|
2278
2361
|
}
|
|
2279
2362
|
if (node.id !== desiredId) {
|
|
2280
2363
|
const previousId = node.id;
|
|
2281
2364
|
nodes.delete(node.id);
|
|
2282
|
-
|
|
2283
|
-
childrenSnapshotCache.delete(previousId);
|
|
2365
|
+
deleteSnapshotCacheEntry(nodes, previousId);
|
|
2284
2366
|
node.id = desiredId;
|
|
2285
2367
|
nodes.set(desiredId, node);
|
|
2286
2368
|
touched.add(desiredId);
|
|
@@ -2311,8 +2393,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
2311
2393
|
for (const [id, record] of nodes) {
|
|
2312
2394
|
if (record.parentId === parent.id && record.type === "code-line" && !allowed.has(id)) {
|
|
2313
2395
|
nodes.delete(id);
|
|
2314
|
-
|
|
2315
|
-
childrenSnapshotCache.delete(id);
|
|
2396
|
+
deleteSnapshotCacheEntry(nodes, id);
|
|
2316
2397
|
touched.add(id);
|
|
2317
2398
|
mutated = true;
|
|
2318
2399
|
}
|
|
@@ -2396,8 +2477,10 @@ function normalizeCodeLineProps(incoming, previous) {
|
|
|
2396
2477
|
}
|
|
2397
2478
|
function createCodeLineProps(index, text, highlight, tokens) {
|
|
2398
2479
|
const safeText = typeof text === "string" ? text : "";
|
|
2399
|
-
const
|
|
2400
|
-
const
|
|
2480
|
+
const hasIncomingHighlight = typeof highlight === "string" && highlight.trim().length > 0;
|
|
2481
|
+
const sanitized = hasIncomingHighlight ? sanitizeLineInnerHtml(highlight, safeText) : null;
|
|
2482
|
+
const hasRenderableTokens = tokens !== void 0 && tokens !== null;
|
|
2483
|
+
const html = sanitized && sanitized.length >= safeText.length ? sanitized : hasRenderableTokens ? null : escapeHtml2(safeText);
|
|
2401
2484
|
const props = {
|
|
2402
2485
|
index,
|
|
2403
2486
|
text: safeText,
|
|
@@ -2508,6 +2591,18 @@ function validateCodeBlockChildren(nodes, parent) {
|
|
|
2508
2591
|
issues.push({ kind: "dangling-node", id });
|
|
2509
2592
|
}
|
|
2510
2593
|
}
|
|
2594
|
+
if (parent.block?.isFinalized) {
|
|
2595
|
+
const snapshot = (0, import_core4.createBlockSnapshot)((0, import_core4.cloneBlock)(parent.block));
|
|
2596
|
+
const expectedChildren = ensureArray(snapshot.children).filter((child) => child.type === "code-line");
|
|
2597
|
+
if (expectedChildren.length !== parent.children.length) {
|
|
2598
|
+
issues.push({
|
|
2599
|
+
kind: "line-count-mismatch",
|
|
2600
|
+
id: parent.id,
|
|
2601
|
+
expected: expectedChildren.length,
|
|
2602
|
+
actual: parent.children.length
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2511
2606
|
return { ok: issues.length === 0, issues };
|
|
2512
2607
|
}
|
|
2513
2608
|
function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
|
|
@@ -2524,7 +2619,7 @@ function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
|
|
|
2524
2619
|
}
|
|
2525
2620
|
const snapshot = (0, import_core4.createBlockSnapshot)((0, import_core4.cloneBlock)(parent.block));
|
|
2526
2621
|
removeSubtree(nodes, parent.id, touched);
|
|
2527
|
-
const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot);
|
|
2622
|
+
const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot, touched, parent.blockEpoch);
|
|
2528
2623
|
if (!inserted) {
|
|
2529
2624
|
return false;
|
|
2530
2625
|
}
|
|
@@ -2547,7 +2642,55 @@ function arraysEqual(a, b) {
|
|
|
2547
2642
|
}
|
|
2548
2643
|
return true;
|
|
2549
2644
|
}
|
|
2550
|
-
function
|
|
2645
|
+
function expectedCodeLineId(parentId, index) {
|
|
2646
|
+
return `${parentId}::line:${index}`;
|
|
2647
|
+
}
|
|
2648
|
+
function collectStoreInvariantViolations(nodes) {
|
|
2649
|
+
const violations = [];
|
|
2650
|
+
for (const [id, node] of nodes) {
|
|
2651
|
+
if (node.type === "__root__") continue;
|
|
2652
|
+
if (node.children.length > 1) {
|
|
2653
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2654
|
+
for (const childId of node.children) {
|
|
2655
|
+
if (seen.has(childId)) {
|
|
2656
|
+
violations.push(`duplicate child id:${id}:${childId}`);
|
|
2657
|
+
} else {
|
|
2658
|
+
seen.add(childId);
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
if (node.type === "list") {
|
|
2663
|
+
const parent = node.parentId ? nodes.get(node.parentId) : void 0;
|
|
2664
|
+
if (parent?.type === "list-item" && node.children.length === 0) {
|
|
2665
|
+
violations.push(`empty nested list:${id}`);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
if (node.type === "code") {
|
|
2669
|
+
for (let index = 0; index < node.children.length; index += 1) {
|
|
2670
|
+
const childId = node.children[index];
|
|
2671
|
+
const child = nodes.get(childId);
|
|
2672
|
+
if (!child) {
|
|
2673
|
+
violations.push(`missing code-line node:${node.id}:${childId}`);
|
|
2674
|
+
continue;
|
|
2675
|
+
}
|
|
2676
|
+
if (child.type !== "code-line") {
|
|
2677
|
+
violations.push(`non-code child in code block:${node.id}:${child.id}:${child.type}`);
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
const expectedId = expectedCodeLineId(node.id, index);
|
|
2681
|
+
if (child.id !== expectedId) {
|
|
2682
|
+
violations.push(`code-line id mismatch:${node.id}:${child.id}:${expectedId}`);
|
|
2683
|
+
}
|
|
2684
|
+
const indexProp = typeof child.props?.index === "number" ? child.props.index : null;
|
|
2685
|
+
if (indexProp !== index) {
|
|
2686
|
+
violations.push(`code-line index mismatch:${node.id}:${child.id}:${indexProp}:${index}`);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
return violations;
|
|
2692
|
+
}
|
|
2693
|
+
function buildNodeFromSnapshot(map, snapshot, parentId, blockEpoch) {
|
|
2551
2694
|
const record = {
|
|
2552
2695
|
id: snapshot.id,
|
|
2553
2696
|
type: snapshot.type,
|
|
@@ -2557,6 +2700,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
|
|
|
2557
2700
|
meta: snapshot.meta ? { ...snapshot.meta } : void 0,
|
|
2558
2701
|
range: snapshot.range ? { ...snapshot.range } : void 0,
|
|
2559
2702
|
version: 0,
|
|
2703
|
+
blockEpoch,
|
|
2560
2704
|
block: snapshotToBlock(snapshot) ?? void 0
|
|
2561
2705
|
};
|
|
2562
2706
|
if (record.type === "code") {
|
|
@@ -2565,7 +2709,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
|
|
|
2565
2709
|
map.set(record.id, record);
|
|
2566
2710
|
const childSnapshots = ensureArray(snapshot.children);
|
|
2567
2711
|
for (const child of childSnapshots) {
|
|
2568
|
-
const childRecord = buildNodeFromSnapshot(map, child, record.id);
|
|
2712
|
+
const childRecord = buildNodeFromSnapshot(map, child, record.id, blockEpoch);
|
|
2569
2713
|
record.children.push(childRecord.id);
|
|
2570
2714
|
}
|
|
2571
2715
|
return record;
|
|
@@ -2593,8 +2737,7 @@ function removeSubtree(map, id, touched) {
|
|
|
2593
2737
|
}
|
|
2594
2738
|
}
|
|
2595
2739
|
map.delete(node.id);
|
|
2596
|
-
|
|
2597
|
-
childrenSnapshotCache.delete(node.id);
|
|
2740
|
+
deleteSnapshotCacheEntry(map, node.id);
|
|
2598
2741
|
touched.add(node.id);
|
|
2599
2742
|
for (const childId of node.children) {
|
|
2600
2743
|
const child = map.get(childId);
|
|
@@ -2626,13 +2769,13 @@ function resolveParent(map, path) {
|
|
|
2626
2769
|
}
|
|
2627
2770
|
return map.get(path.blockId);
|
|
2628
2771
|
}
|
|
2629
|
-
function insertSnapshotAt(map, parent, index, snapshot, touched) {
|
|
2772
|
+
function insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
|
|
2630
2773
|
if (!parent) return null;
|
|
2631
2774
|
const existingIndex = parent.children.indexOf(snapshot.id);
|
|
2632
2775
|
if (existingIndex !== -1) {
|
|
2633
2776
|
removeSubtree(map, parent.children[existingIndex], touched ?? /* @__PURE__ */ new Set());
|
|
2634
2777
|
}
|
|
2635
|
-
const record = buildNodeFromSnapshot(map, snapshot, parent.id);
|
|
2778
|
+
const record = buildNodeFromSnapshot(map, snapshot, parent.id, blockEpoch ?? readSnapshotBlockEpoch(snapshot) ?? parent.blockEpoch);
|
|
2636
2779
|
const insertedIds = [];
|
|
2637
2780
|
const queue = [record];
|
|
2638
2781
|
while (queue.length > 0) {
|
|
@@ -2650,13 +2793,13 @@ function insertSnapshotAt(map, parent, index, snapshot, touched) {
|
|
|
2650
2793
|
ensureUniqueChildren(parent, touched);
|
|
2651
2794
|
return { record, insertedIds };
|
|
2652
2795
|
}
|
|
2653
|
-
function replaceSnapshotAt(map, parent, index, snapshot, touched) {
|
|
2796
|
+
function replaceSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
|
|
2654
2797
|
if (!parent) return;
|
|
2655
2798
|
if (index < 0 || index >= parent.children.length) return;
|
|
2656
2799
|
const targetId = parent.children[index];
|
|
2657
2800
|
parent.children.splice(index, 1);
|
|
2658
2801
|
removeSubtree(map, targetId, touched);
|
|
2659
|
-
const inserted = insertSnapshotAt(map, parent, index, snapshot, touched);
|
|
2802
|
+
const inserted = insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch);
|
|
2660
2803
|
if (inserted) {
|
|
2661
2804
|
for (const id of inserted.insertedIds) {
|
|
2662
2805
|
touched.add(id);
|
|
@@ -2676,6 +2819,24 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2676
2819
|
const listeners = /* @__PURE__ */ new Set();
|
|
2677
2820
|
let notifyScheduled = false;
|
|
2678
2821
|
let lastCoalescingMetrics = null;
|
|
2822
|
+
let invariantViolations = [];
|
|
2823
|
+
let transientDiagnostics = [];
|
|
2824
|
+
let nextBlockEpoch = 1;
|
|
2825
|
+
const debugCounters = {
|
|
2826
|
+
appendLineGuardRejected: 0,
|
|
2827
|
+
staleEpochRejected: 0,
|
|
2828
|
+
storeInvariantViolationCount: 0
|
|
2829
|
+
};
|
|
2830
|
+
const allocateBlockEpoch = (preferred) => {
|
|
2831
|
+
const resolved = readNumericEpoch(preferred);
|
|
2832
|
+
if (resolved !== void 0) {
|
|
2833
|
+
nextBlockEpoch = Math.max(nextBlockEpoch, resolved + 1);
|
|
2834
|
+
return resolved;
|
|
2835
|
+
}
|
|
2836
|
+
const epoch = nextBlockEpoch;
|
|
2837
|
+
nextBlockEpoch += 1;
|
|
2838
|
+
return epoch;
|
|
2839
|
+
};
|
|
2679
2840
|
const getRaf = () => {
|
|
2680
2841
|
if (typeof globalThis !== "undefined" && typeof globalThis.requestAnimationFrame === "function") {
|
|
2681
2842
|
return globalThis.requestAnimationFrame;
|
|
@@ -2707,9 +2868,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2707
2868
|
}
|
|
2708
2869
|
}
|
|
2709
2870
|
}
|
|
2871
|
+
function appendDiagnostic(message) {
|
|
2872
|
+
transientDiagnostics = [...transientDiagnostics, message];
|
|
2873
|
+
invariantViolations = [...invariantViolations, message];
|
|
2874
|
+
}
|
|
2710
2875
|
function rebuildFromBlocks(blocks) {
|
|
2711
|
-
|
|
2712
|
-
childrenSnapshotCache.clear();
|
|
2876
|
+
clearSnapshotCaches(nodes);
|
|
2713
2877
|
nodes = /* @__PURE__ */ new Map();
|
|
2714
2878
|
const root = createRootRecord();
|
|
2715
2879
|
nodes.set(root.id, root);
|
|
@@ -2718,9 +2882,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2718
2882
|
for (let i = 0; i < blocks.length; i++) {
|
|
2719
2883
|
const block = (0, import_core4.cloneBlock)(blocks[i]);
|
|
2720
2884
|
const snapshot = (0, import_core4.createBlockSnapshot)(block);
|
|
2721
|
-
insertSnapshotAt(nodes, root, root.children.length, snapshot);
|
|
2885
|
+
insertSnapshotAt(nodes, root, root.children.length, snapshot, void 0, allocateBlockEpoch(readSnapshotBlockEpoch(snapshot)));
|
|
2722
2886
|
}
|
|
2723
2887
|
normalizeAllListDepths(nodes, /* @__PURE__ */ new Set(), import_core4.PATCH_ROOT_ID);
|
|
2888
|
+
transientDiagnostics = [];
|
|
2889
|
+
invariantViolations = collectStoreInvariantViolations(nodes);
|
|
2890
|
+
debugCounters.storeInvariantViolationCount += invariantViolations.length;
|
|
2724
2891
|
version++;
|
|
2725
2892
|
blocksDirty = true;
|
|
2726
2893
|
scheduleNotify();
|
|
@@ -2785,11 +2952,57 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2785
2952
|
listDepthDirty = true;
|
|
2786
2953
|
}
|
|
2787
2954
|
};
|
|
2788
|
-
const
|
|
2955
|
+
const semanticTxEpochs = /* @__PURE__ */ new Map();
|
|
2956
|
+
const resolveBlockEpochTarget = (patch, at) => {
|
|
2957
|
+
if ((patch.op === "insertChild" || patch.op === "replaceChild") && at.blockId === import_core4.PATCH_ROOT_ID) {
|
|
2958
|
+
const nodeId = patch.node?.id;
|
|
2959
|
+
return nodeId ? nodes.get(nodeId) : void 0;
|
|
2960
|
+
}
|
|
2961
|
+
return nodes.get(at.blockId) ?? (at.nodeId ? nodes.get(at.nodeId) : void 0);
|
|
2962
|
+
};
|
|
2963
|
+
const rejectStaleEpochPatch = (patch, at) => {
|
|
2964
|
+
const expectedBlockEpoch = readPatchBlockEpoch(patch);
|
|
2965
|
+
const patchTx = readPatchTx(patch);
|
|
2966
|
+
if (expectedBlockEpoch === void 0) {
|
|
2967
|
+
return false;
|
|
2968
|
+
}
|
|
2969
|
+
if ((0, import_patch_batching.getPatchKind)(patch) !== "semantic" || !at) {
|
|
2970
|
+
return false;
|
|
2971
|
+
}
|
|
2972
|
+
const target = resolveBlockEpochTarget(patch, at);
|
|
2973
|
+
if (!target || target.blockEpoch === expectedBlockEpoch) {
|
|
2974
|
+
return false;
|
|
2975
|
+
}
|
|
2976
|
+
const txEpoch = at.blockId && patchTx !== void 0 ? semanticTxEpochs.get(at.blockId) : void 0;
|
|
2977
|
+
if (txEpoch && txEpoch.tx === patchTx && txEpoch.blockEpoch === target.blockEpoch) {
|
|
2978
|
+
return false;
|
|
2979
|
+
}
|
|
2980
|
+
debugCounters.staleEpochRejected += 1;
|
|
2981
|
+
appendDiagnostic(
|
|
2982
|
+
`stale epoch rejected:${patch.op}:${target.id}:expected=${expectedBlockEpoch}:actual=${target.blockEpoch}`
|
|
2983
|
+
);
|
|
2984
|
+
return true;
|
|
2985
|
+
};
|
|
2986
|
+
const advanceBlockEpoch = (blockId, touchedSet, preferredEpoch, tx) => {
|
|
2987
|
+
if (!blockId || blockId === import_core4.PATCH_ROOT_ID) {
|
|
2988
|
+
return null;
|
|
2989
|
+
}
|
|
2990
|
+
const blockRoot = nodes.get(blockId);
|
|
2991
|
+
if (!blockRoot) {
|
|
2992
|
+
return null;
|
|
2993
|
+
}
|
|
2994
|
+
const nextEpoch = allocateBlockEpoch(preferredEpoch);
|
|
2995
|
+
assignSubtreeBlockEpoch(nodes, blockRoot.id, nextEpoch, touchedSet);
|
|
2996
|
+
if (tx !== void 0) {
|
|
2997
|
+
semanticTxEpochs.set(blockRoot.id, { tx, blockEpoch: nextEpoch });
|
|
2998
|
+
}
|
|
2999
|
+
return nextEpoch;
|
|
3000
|
+
};
|
|
3001
|
+
const applyPropsUpdate = (at, props, preferredEpoch, tx, semanticUpdate = false) => {
|
|
2789
3002
|
const perfStartLocal = perfEnabled ? getPerfNow() : 0;
|
|
2790
3003
|
const targetId = at.nodeId ?? at.blockId;
|
|
2791
3004
|
if (!targetId) return;
|
|
2792
|
-
|
|
3005
|
+
let target = nodes.get(targetId);
|
|
2793
3006
|
if (!target) return;
|
|
2794
3007
|
const parentType = target.parentId ? nodes.get(target.parentId)?.type : void 0;
|
|
2795
3008
|
if (debugListPatches && (isListRelated(target.type) || isListRelated(parentType))) {
|
|
@@ -2816,6 +3029,9 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2816
3029
|
propsChanged = true;
|
|
2817
3030
|
}
|
|
2818
3031
|
if (propsChanged) {
|
|
3032
|
+
if (semanticUpdate) {
|
|
3033
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
3034
|
+
}
|
|
2819
3035
|
target.version++;
|
|
2820
3036
|
touched.add(target.id);
|
|
2821
3037
|
const parent = target.parentId ? nodes.get(target.parentId) : void 0;
|
|
@@ -2879,16 +3095,56 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2879
3095
|
}
|
|
2880
3096
|
}
|
|
2881
3097
|
let blockChanged = false;
|
|
3098
|
+
let typeChanged = false;
|
|
3099
|
+
let rebuiltFromFinalizedBlock = false;
|
|
3100
|
+
let blockEpochAdvanced = false;
|
|
2882
3101
|
const incomingBlock = props?.block;
|
|
2883
3102
|
if (incomingBlock && typeof incomingBlock === "object") {
|
|
2884
3103
|
target.block = (0, import_core4.cloneBlock)(incomingBlock);
|
|
3104
|
+
const previousType = target.type;
|
|
3105
|
+
const incomingType = typeof target.block.type === "string" ? target.block.type : target.type;
|
|
3106
|
+
if (incomingType !== target.type) {
|
|
3107
|
+
target.type = incomingType;
|
|
3108
|
+
typeChanged = true;
|
|
3109
|
+
if (isListRelated(previousType) || isListRelated(target.type)) {
|
|
3110
|
+
markListDepthDirty(target.id);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
2885
3113
|
if (target.type === "code") {
|
|
2886
3114
|
applyCodeBlockMetadata(target);
|
|
2887
3115
|
}
|
|
3116
|
+
if (semanticUpdate) {
|
|
3117
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
3118
|
+
blockEpochAdvanced = true;
|
|
3119
|
+
}
|
|
3120
|
+
const shouldRebuildFinalizedTree = !at.nodeId && target.id === at.blockId && Boolean(target.block?.isFinalized) && target.type !== "code" && Boolean(target.parentId);
|
|
3121
|
+
if (shouldRebuildFinalizedTree) {
|
|
3122
|
+
const parent = target.parentId ? nodes.get(target.parentId) : void 0;
|
|
3123
|
+
const index = parent ? parent.children.indexOf(target.id) : -1;
|
|
3124
|
+
if (parent && index !== -1) {
|
|
3125
|
+
const snapshot = (0, import_core4.createBlockSnapshot)((0, import_core4.cloneBlock)(target.block));
|
|
3126
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
|
|
3127
|
+
mutatedBlocks = true;
|
|
3128
|
+
blockChanged = true;
|
|
3129
|
+
rebuiltFromFinalizedBlock = true;
|
|
3130
|
+
const rebuilt = nodes.get(target.id);
|
|
3131
|
+
if (isListRelated(previousType) || isListRelated(rebuilt?.type)) {
|
|
3132
|
+
markListDepthDirty(target.id);
|
|
3133
|
+
}
|
|
3134
|
+
target = rebuilt ?? target;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
2888
3137
|
mutatedBlocks = true;
|
|
2889
3138
|
blockChanged = true;
|
|
2890
3139
|
}
|
|
2891
|
-
if (
|
|
3140
|
+
if (rebuiltFromFinalizedBlock) {
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
if (propsChanged || blockChanged || typeChanged) {
|
|
3144
|
+
if (semanticUpdate && !blockEpochAdvanced) {
|
|
3145
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
3146
|
+
blockEpochAdvanced = true;
|
|
3147
|
+
}
|
|
2892
3148
|
target.version++;
|
|
2893
3149
|
touched.add(target.id);
|
|
2894
3150
|
let parentForNormalization;
|
|
@@ -2931,13 +3187,22 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2931
3187
|
const applyPatch = (patch) => {
|
|
2932
3188
|
switch (patch.op) {
|
|
2933
3189
|
case "insertChild": {
|
|
3190
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
2934
3191
|
const parent = resolveParent(nodes, patch.at);
|
|
2935
|
-
const inserted = insertSnapshotAt(
|
|
3192
|
+
const inserted = insertSnapshotAt(
|
|
3193
|
+
nodes,
|
|
3194
|
+
parent,
|
|
3195
|
+
patch.index,
|
|
3196
|
+
patch.node,
|
|
3197
|
+
touched,
|
|
3198
|
+
allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
|
|
3199
|
+
);
|
|
2936
3200
|
if (inserted) {
|
|
2937
3201
|
for (const id of inserted.insertedIds) {
|
|
2938
3202
|
touched.add(id);
|
|
2939
3203
|
}
|
|
2940
3204
|
mutatedBlocks = true;
|
|
3205
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
2941
3206
|
if (isListRelated(inserted.record.type) || isListRelated(parent?.type)) {
|
|
2942
3207
|
markListDepthDirty(inserted.record.id);
|
|
2943
3208
|
if (parent) {
|
|
@@ -2957,6 +3222,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2957
3222
|
break;
|
|
2958
3223
|
}
|
|
2959
3224
|
case "deleteChild": {
|
|
3225
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
2960
3226
|
const parent = resolveParent(nodes, patch.at);
|
|
2961
3227
|
if (!parent) break;
|
|
2962
3228
|
if (patch.index < 0 || patch.index >= parent.children.length) break;
|
|
@@ -2968,6 +3234,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2968
3234
|
touched.add(parent.id);
|
|
2969
3235
|
ensureUniqueChildren(parent, touched);
|
|
2970
3236
|
mutatedBlocks = true;
|
|
3237
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
2971
3238
|
if (isListRelated(parent.type) || isListRelated(removedNode?.type)) {
|
|
2972
3239
|
markListDepthDirty(parent.id);
|
|
2973
3240
|
}
|
|
@@ -2984,11 +3251,20 @@ function createRendererStore(initialBlocks = []) {
|
|
|
2984
3251
|
break;
|
|
2985
3252
|
}
|
|
2986
3253
|
case "replaceChild": {
|
|
3254
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
2987
3255
|
const parent = resolveParent(nodes, patch.at);
|
|
2988
3256
|
if (!parent) break;
|
|
2989
|
-
replaceSnapshotAt(
|
|
3257
|
+
replaceSnapshotAt(
|
|
3258
|
+
nodes,
|
|
3259
|
+
parent,
|
|
3260
|
+
patch.index,
|
|
3261
|
+
patch.node,
|
|
3262
|
+
touched,
|
|
3263
|
+
allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
|
|
3264
|
+
);
|
|
2990
3265
|
touched.add(parent.id);
|
|
2991
3266
|
mutatedBlocks = true;
|
|
3267
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
2992
3268
|
if (isListRelated(parent.type)) {
|
|
2993
3269
|
markListDepthDirty(parent.id);
|
|
2994
3270
|
}
|
|
@@ -3002,18 +3278,33 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3002
3278
|
break;
|
|
3003
3279
|
}
|
|
3004
3280
|
case "setProps": {
|
|
3005
|
-
|
|
3281
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3282
|
+
applyPropsUpdate(patch.at, patch.props, readPatchParseEpoch(patch), readPatchTx(patch), (0, import_patch_batching.getPatchKind)(patch) === "semantic");
|
|
3006
3283
|
break;
|
|
3007
3284
|
}
|
|
3008
3285
|
case "setPropsBatch": {
|
|
3009
3286
|
const entries2 = Array.isArray(patch.entries) ? patch.entries : [];
|
|
3010
3287
|
for (const entry of entries2) {
|
|
3011
3288
|
if (!entry || !entry.at) continue;
|
|
3012
|
-
|
|
3289
|
+
const entryPatch = {
|
|
3290
|
+
op: "setProps",
|
|
3291
|
+
at: entry.at,
|
|
3292
|
+
props: entry.props,
|
|
3293
|
+
meta: entry.meta ?? patch.meta
|
|
3294
|
+
};
|
|
3295
|
+
if (rejectStaleEpochPatch(entryPatch, entry.at)) continue;
|
|
3296
|
+
applyPropsUpdate(
|
|
3297
|
+
entry.at,
|
|
3298
|
+
entry.props,
|
|
3299
|
+
readPatchParseEpoch(entryPatch),
|
|
3300
|
+
readPatchTx(entryPatch),
|
|
3301
|
+
(0, import_patch_batching.getPatchKind)(entryPatch) === "semantic"
|
|
3302
|
+
);
|
|
3013
3303
|
}
|
|
3014
3304
|
break;
|
|
3015
3305
|
}
|
|
3016
3306
|
case "finalize": {
|
|
3307
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3017
3308
|
const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
|
|
3018
3309
|
if (!target || !target.block) break;
|
|
3019
3310
|
if (!target.block.isFinalized) {
|
|
@@ -3022,6 +3313,17 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3022
3313
|
target.version++;
|
|
3023
3314
|
touched.add(target.id);
|
|
3024
3315
|
mutatedBlocks = true;
|
|
3316
|
+
if (!patch.at.nodeId && target.id === patch.at.blockId && target.type !== "code" && target.parentId) {
|
|
3317
|
+
const parent = nodes.get(target.parentId);
|
|
3318
|
+
const index = parent ? parent.children.indexOf(target.id) : -1;
|
|
3319
|
+
if (parent && index !== -1) {
|
|
3320
|
+
const snapshot = (0, import_core4.createBlockSnapshot)((0, import_core4.cloneBlock)(target.block));
|
|
3321
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
|
|
3322
|
+
if (isListRelated(target.type) || isListRelated(snapshot.type)) {
|
|
3323
|
+
markListDepthDirty(target.id);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3025
3327
|
}
|
|
3026
3328
|
if (isListRelated(target.type)) {
|
|
3027
3329
|
markListDepthDirty(target.id);
|
|
@@ -3035,6 +3337,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3035
3337
|
break;
|
|
3036
3338
|
}
|
|
3037
3339
|
case "reorder": {
|
|
3340
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3038
3341
|
const parent = resolveParent(nodes, patch.at);
|
|
3039
3342
|
if (!parent) break;
|
|
3040
3343
|
const length = parent.children.length;
|
|
@@ -3049,6 +3352,9 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3049
3352
|
parent.children.splice(to, 0, ...moved);
|
|
3050
3353
|
parent.version++;
|
|
3051
3354
|
touched.add(parent.id);
|
|
3355
|
+
if ((0, import_patch_batching.getPatchKind)(patch) === "semantic") {
|
|
3356
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
3357
|
+
}
|
|
3052
3358
|
if (isListRelated(parent.type)) {
|
|
3053
3359
|
markListDepthDirty(parent.id);
|
|
3054
3360
|
}
|
|
@@ -3064,15 +3370,31 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3064
3370
|
break;
|
|
3065
3371
|
}
|
|
3066
3372
|
case "appendLines": {
|
|
3373
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3067
3374
|
const parent = resolveParent(nodes, patch.at);
|
|
3068
3375
|
if (!parent) break;
|
|
3069
|
-
|
|
3376
|
+
if (parent.type !== "code") {
|
|
3377
|
+
debugCounters.appendLineGuardRejected += 1;
|
|
3378
|
+
appendDiagnostic(`appendLines target not code:${parent.id}:${parent.type}`);
|
|
3379
|
+
break;
|
|
3380
|
+
}
|
|
3381
|
+
const requestedStartIndex = Math.max(0, patch.startIndex);
|
|
3382
|
+
const startIndex = Math.max(0, Math.min(requestedStartIndex, parent.children.length));
|
|
3070
3383
|
const lineCount = patch.lines?.length ?? 0;
|
|
3071
3384
|
if (lineCount === 0) break;
|
|
3385
|
+
const prefixMatches = parent.children.every((childId, index) => childId === expectedCodeLineId(parent.id, index));
|
|
3386
|
+
const isStrictTailAppend = requestedStartIndex === parent.children.length;
|
|
3387
|
+
if (!isStrictTailAppend || !prefixMatches) {
|
|
3388
|
+
debugCounters.appendLineGuardRejected += 1;
|
|
3389
|
+
appendDiagnostic(
|
|
3390
|
+
`appendLines guard rejected:${parent.id}:requestedStart=${requestedStartIndex}:childCount=${parent.children.length}:prefixMatches=${prefixMatches}`
|
|
3391
|
+
);
|
|
3392
|
+
break;
|
|
3393
|
+
}
|
|
3072
3394
|
const originalChildren = parent.children.slice();
|
|
3073
3395
|
const insertedIds = [];
|
|
3074
3396
|
const insertedSet = /* @__PURE__ */ new Set();
|
|
3075
|
-
const isTrailingAppend =
|
|
3397
|
+
const isTrailingAppend = true;
|
|
3076
3398
|
let parentChildrenChanged = false;
|
|
3077
3399
|
let codeLinesMutated = false;
|
|
3078
3400
|
for (let offset = 0; offset < lineCount; offset++) {
|
|
@@ -3114,7 +3436,8 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3114
3436
|
parentId: parent.id,
|
|
3115
3437
|
children: [],
|
|
3116
3438
|
props: nextLineProps,
|
|
3117
|
-
version: 0
|
|
3439
|
+
version: 0,
|
|
3440
|
+
blockEpoch: parent.blockEpoch
|
|
3118
3441
|
};
|
|
3119
3442
|
nodes.set(lineId, record);
|
|
3120
3443
|
touched.add(lineId);
|
|
@@ -3164,6 +3487,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3164
3487
|
break;
|
|
3165
3488
|
}
|
|
3166
3489
|
case "setHTML": {
|
|
3490
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3167
3491
|
const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
|
|
3168
3492
|
if (!target) break;
|
|
3169
3493
|
const sanitized = patch.sanitized ? patch.html : (0, import_core4.sanitizeHTML)(patch.html);
|
|
@@ -3206,6 +3530,30 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3206
3530
|
applyPatch(patch);
|
|
3207
3531
|
}
|
|
3208
3532
|
}
|
|
3533
|
+
const finalizedTableRoots = /* @__PURE__ */ new Set();
|
|
3534
|
+
for (const touchedId of touched) {
|
|
3535
|
+
let cursor = nodes.get(touchedId);
|
|
3536
|
+
while (cursor && cursor.id !== import_core4.PATCH_ROOT_ID) {
|
|
3537
|
+
if (cursor.type === "table" && cursor.block?.isFinalized && cursor.parentId) {
|
|
3538
|
+
finalizedTableRoots.add(cursor.id);
|
|
3539
|
+
break;
|
|
3540
|
+
}
|
|
3541
|
+
cursor = cursor.parentId ? nodes.get(cursor.parentId) : void 0;
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
if (finalizedTableRoots.size > 0) {
|
|
3545
|
+
for (const tableId of finalizedTableRoots) {
|
|
3546
|
+
const tableNode = nodes.get(tableId);
|
|
3547
|
+
if (!tableNode || !tableNode.block || !tableNode.parentId) continue;
|
|
3548
|
+
const parent = nodes.get(tableNode.parentId);
|
|
3549
|
+
if (!parent) continue;
|
|
3550
|
+
const index = parent.children.indexOf(tableNode.id);
|
|
3551
|
+
if (index === -1) continue;
|
|
3552
|
+
const snapshot = (0, import_core4.createBlockSnapshot)((0, import_core4.cloneBlock)(tableNode.block));
|
|
3553
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, tableNode.blockEpoch);
|
|
3554
|
+
mutatedBlocks = true;
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3209
3557
|
if (mutatedBlocks) {
|
|
3210
3558
|
blocksDirty = true;
|
|
3211
3559
|
}
|
|
@@ -3226,6 +3574,8 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3226
3574
|
normalizeListDepthsForIds(nodes, touched, listDepthDirtyNodes, import_core4.PATCH_ROOT_ID);
|
|
3227
3575
|
}
|
|
3228
3576
|
}
|
|
3577
|
+
invariantViolations = [...collectStoreInvariantViolations(nodes), ...transientDiagnostics];
|
|
3578
|
+
debugCounters.storeInvariantViolationCount += invariantViolations.length;
|
|
3229
3579
|
if (touched.size > 0 || mutatedBlocks) {
|
|
3230
3580
|
version++;
|
|
3231
3581
|
scheduleNotify();
|
|
@@ -3274,6 +3624,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3274
3624
|
if (!record) {
|
|
3275
3625
|
return EMPTY_NODE_SNAPSHOT;
|
|
3276
3626
|
}
|
|
3627
|
+
const nodeSnapshotCache = getNodeSnapshotCacheForMap(nodes);
|
|
3277
3628
|
const cached = nodeSnapshotCache.get(id);
|
|
3278
3629
|
if (cached && cached.version === record.version && cached.node === record) {
|
|
3279
3630
|
return cached;
|
|
@@ -3293,6 +3644,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3293
3644
|
if (!record) {
|
|
3294
3645
|
return EMPTY_CHILDREN_SNAPSHOT;
|
|
3295
3646
|
}
|
|
3647
|
+
const childrenSnapshotCache = getChildrenSnapshotCacheForMap(nodes);
|
|
3296
3648
|
const cached = childrenSnapshotCache.get(id);
|
|
3297
3649
|
if (cached && cached.version === record.version) {
|
|
3298
3650
|
return cached;
|
|
@@ -3302,6 +3654,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3302
3654
|
pruneCache(childrenSnapshotCache, CHILDREN_SNAPSHOT_CACHE_LIMIT, CHILDREN_SNAPSHOT_CACHE_BUFFER);
|
|
3303
3655
|
return snapshot;
|
|
3304
3656
|
},
|
|
3657
|
+
getInvariantViolations() {
|
|
3658
|
+
return invariantViolations.slice();
|
|
3659
|
+
},
|
|
3660
|
+
getDebugCounters() {
|
|
3661
|
+
return { ...debugCounters };
|
|
3662
|
+
},
|
|
3305
3663
|
getLastCoalescingMetrics() {
|
|
3306
3664
|
return lastCoalescingMetrics;
|
|
3307
3665
|
},
|
|
@@ -3750,8 +4108,10 @@ var ListBlockView = import_react10.default.memo(
|
|
|
3750
4108
|
isFinalized: block?.isFinalized ?? false
|
|
3751
4109
|
});
|
|
3752
4110
|
}, [listDebugEnabled, blockId, depth, ordered, childIds, listItemIds, store, block?.isFinalized]);
|
|
3753
|
-
const listStyle = import_react10.default.useMemo(() => {
|
|
3754
|
-
if (!ordered || listItemIds.length === 0)
|
|
4111
|
+
const { listStyle, markerDigits } = import_react10.default.useMemo(() => {
|
|
4112
|
+
if (!ordered || listItemIds.length === 0) {
|
|
4113
|
+
return { listStyle: void 0, markerDigits: 1 };
|
|
4114
|
+
}
|
|
3755
4115
|
let maxDigits = 1;
|
|
3756
4116
|
listItemIds.forEach((childId, index) => {
|
|
3757
4117
|
const childNode = store.getNode(childId);
|
|
@@ -3763,9 +4123,14 @@ var ListBlockView = import_react10.default.memo(
|
|
|
3763
4123
|
const digits = digitMatch ? digitMatch.length : text.length;
|
|
3764
4124
|
if (digits > maxDigits) maxDigits = digits;
|
|
3765
4125
|
});
|
|
3766
|
-
const baseIndent = depth === 0 ? "
|
|
4126
|
+
const baseIndent = depth === 0 ? "2rem" : depth === 1 ? "1.75rem" : "1.5rem";
|
|
4127
|
+
const extraDigits = Math.max(0, maxDigits - 1);
|
|
3767
4128
|
return {
|
|
3768
|
-
|
|
4129
|
+
markerDigits: maxDigits,
|
|
4130
|
+
listStyle: {
|
|
4131
|
+
["--list-indent"]: extraDigits > 0 ? `calc(${baseIndent} + ${extraDigits}ch)` : baseIndent,
|
|
4132
|
+
["--list-marker-digits"]: String(maxDigits)
|
|
4133
|
+
}
|
|
3769
4134
|
};
|
|
3770
4135
|
}, [ordered, listItemIds, store, depth]);
|
|
3771
4136
|
if (listItemIds.length === 0) return null;
|
|
@@ -3774,6 +4139,7 @@ var ListBlockView = import_react10.default.memo(
|
|
|
3774
4139
|
{
|
|
3775
4140
|
className: `markdown-list ${ordered ? "ordered" : "unordered"}`,
|
|
3776
4141
|
"data-list-depth": depth,
|
|
4142
|
+
"data-marker-digits": ordered ? markerDigits : void 0,
|
|
3777
4143
|
"data-list-id": listDebugEnabled ? blockId : void 0,
|
|
3778
4144
|
style: listStyle,
|
|
3779
4145
|
children: listItemIds.map((childId, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -3955,6 +4321,7 @@ var CodeBlockView = import_react10.default.memo(({ store, blockId, registry }) =
|
|
|
3955
4321
|
deferredConfig ? { ...deferredConfig, enabled: true } : { enabled: false }
|
|
3956
4322
|
);
|
|
3957
4323
|
if (!node || !node.block) return null;
|
|
4324
|
+
const blockIsFinalized = node.block.isFinalized;
|
|
3958
4325
|
const nodeVersion = node.version;
|
|
3959
4326
|
const lines = import_react10.default.useMemo(() => {
|
|
3960
4327
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -3985,13 +4352,25 @@ var CodeBlockView = import_react10.default.memo(({ store, blockId, registry }) =
|
|
|
3985
4352
|
const lang = typeof node.props?.lang === "string" ? node.props?.lang : node.block.payload.meta?.lang;
|
|
3986
4353
|
const preAttrs = node.props?.preAttrs ?? void 0;
|
|
3987
4354
|
const codeAttrs = node.props?.codeAttrs ?? void 0;
|
|
4355
|
+
const blockMeta = node.block.payload.meta ?? void 0;
|
|
4356
|
+
const lazyEnabled = Boolean(blockMeta?.lazyTokenization);
|
|
4357
|
+
const lazyTokenizedUntil = typeof blockMeta?.lazyTokenizedUntil === "number" ? blockMeta.lazyTokenizedUntil : 0;
|
|
4358
|
+
const lazyHighlightedLines = Array.isArray(blockMeta?.highlightedLines) ? blockMeta.highlightedLines.length : 0;
|
|
4359
|
+
const lazyTokenLines = Array.isArray(blockMeta?.tokenLines) ? blockMeta.tokenLines.length : 0;
|
|
4360
|
+
const lazySourceTextLineCount = import_react10.default.useMemo(() => {
|
|
4361
|
+
const raw = typeof node.block?.payload.raw === "string" ? node.block.payload.raw : "";
|
|
4362
|
+
const metaCode = typeof blockMeta?.code === "string" ? blockMeta.code : null;
|
|
4363
|
+
const sourceText = resolveCodeSourceText(raw, metaCode);
|
|
4364
|
+
if (sourceText.length === 0) return 0;
|
|
4365
|
+
return sourceText.split("\n").length;
|
|
4366
|
+
}, [blockMeta, node.block?.payload.raw]);
|
|
4367
|
+
const totalLazyLineCount = Math.max(lines.length, lazyHighlightedLines, lazyTokenLines, lazySourceTextLineCount);
|
|
3988
4368
|
const virtualizationConfig = DEFAULT_VIRTUALIZED_CODE_CONFIG;
|
|
3989
4369
|
const virtualizationDisabled = typeof process !== "undefined" && typeof process.env === "object" && process.env.STREAM_MDX_DISABLE_VIRTUALIZED_CODE === "true";
|
|
3990
|
-
const
|
|
4370
|
+
const canVirtualize = node.block.isFinalized && !lazyEnabled;
|
|
4371
|
+
const shouldVirtualize = canVirtualize && !virtualizationDisabled && virtualizationConfig.enabled && lines.length >= virtualizationConfig.virtualizeThreshold;
|
|
3991
4372
|
const virtualization = useVirtualizedCode(lines, shouldVirtualize ? virtualizationConfig : { ...virtualizationConfig, enabled: false });
|
|
3992
4373
|
const highlightRequester = useCodeHighlightRequester();
|
|
3993
|
-
const lazyEnabled = Boolean(node.block.payload.meta?.lazyTokenization);
|
|
3994
|
-
const lazyTokenizedUntil = typeof node.block.payload.meta?.lazyTokenizedUntil === "number" ? node.block.payload.meta.lazyTokenizedUntil : 0;
|
|
3995
4374
|
const lastRangeRef = import_react10.default.useRef(null);
|
|
3996
4375
|
const rafRef = import_react10.default.useRef(null);
|
|
3997
4376
|
const idleRef = import_react10.default.useRef(null);
|
|
@@ -4072,29 +4451,111 @@ var CodeBlockView = import_react10.default.memo(({ store, blockId, registry }) =
|
|
|
4072
4451
|
virtualization.window.startIndex,
|
|
4073
4452
|
virtualization.window.endIndex
|
|
4074
4453
|
]);
|
|
4075
|
-
|
|
4076
|
-
if (
|
|
4077
|
-
|
|
4454
|
+
import_react10.default.useEffect(() => {
|
|
4455
|
+
if (shouldVirtualize || !blockIsFinalized || !lazyEnabled || !highlightRequester) return;
|
|
4456
|
+
if (totalLazyLineCount <= 0 || lazyTokenizedUntil >= totalLazyLineCount) return;
|
|
4457
|
+
const nextRange = {
|
|
4458
|
+
visibleStart: 0,
|
|
4459
|
+
visibleEnd: totalLazyLineCount,
|
|
4460
|
+
prefetchStart: 0,
|
|
4461
|
+
prefetchEnd: totalLazyLineCount,
|
|
4462
|
+
tokenizedUntil: lazyTokenizedUntil
|
|
4463
|
+
};
|
|
4464
|
+
const last = lastRangeRef.current;
|
|
4465
|
+
if (last && last.visibleStart === nextRange.visibleStart && last.visibleEnd === nextRange.visibleEnd && last.prefetchStart === nextRange.prefetchStart && last.prefetchEnd === nextRange.prefetchEnd && last.tokenizedUntil === nextRange.tokenizedUntil) {
|
|
4466
|
+
return;
|
|
4078
4467
|
}
|
|
4468
|
+
lastRangeRef.current = nextRange;
|
|
4469
|
+
if (rafRef.current !== null && typeof cancelAnimationFrame === "function") {
|
|
4470
|
+
cancelAnimationFrame(rafRef.current);
|
|
4471
|
+
}
|
|
4472
|
+
const requestFullHighlight = () => {
|
|
4473
|
+
highlightRequester({
|
|
4474
|
+
blockId,
|
|
4475
|
+
startLine: 0,
|
|
4476
|
+
endLine: totalLazyLineCount,
|
|
4477
|
+
priority: "visible",
|
|
4478
|
+
reason: "finalize-full"
|
|
4479
|
+
});
|
|
4480
|
+
rafRef.current = null;
|
|
4481
|
+
};
|
|
4482
|
+
if (typeof requestAnimationFrame === "function") {
|
|
4483
|
+
rafRef.current = requestAnimationFrame(requestFullHighlight);
|
|
4484
|
+
} else {
|
|
4485
|
+
requestFullHighlight();
|
|
4486
|
+
}
|
|
4487
|
+
}, [blockId, blockIsFinalized, highlightRequester, lazyEnabled, lazyTokenizedUntil, shouldVirtualize, totalLazyLineCount]);
|
|
4488
|
+
const highlightedHtml = import_react10.default.useMemo(() => {
|
|
4489
|
+
const blockHtml = node.block?.payload.highlightedHtml ?? "";
|
|
4490
|
+
const raw = typeof node.block?.payload.raw === "string" ? node.block.payload.raw : "";
|
|
4491
|
+
const metaCode = typeof node.block?.payload.meta?.code === "string" ? (node.block?.payload.meta).code : null;
|
|
4492
|
+
const sourceText = resolveCodeSourceText(raw, metaCode);
|
|
4493
|
+
const sourceEndsWithNewline = sourceText.endsWith("\n");
|
|
4494
|
+
const sourceLines = sourceText.length > 0 ? sourceText.split("\n") : [];
|
|
4495
|
+
const rawFallbackLines = buildCodeFallbackLines(raw);
|
|
4496
|
+
const deterministicWrapperAttrs = (0, import_core5.getDefaultCodeWrapperAttributes)(lang);
|
|
4497
|
+
const composedPreAttrs = deterministicWrapperAttrs.preAttrs;
|
|
4498
|
+
const composedCodeAttrs = deterministicWrapperAttrs.codeAttrs;
|
|
4499
|
+
const shouldAppendTerminalNewline = (renderedLines) => {
|
|
4500
|
+
if (!sourceEndsWithNewline || renderedLines.length === 0 || lines.length === 0) {
|
|
4501
|
+
return false;
|
|
4502
|
+
}
|
|
4503
|
+
const lastRendered = renderedLines[renderedLines.length - 1];
|
|
4504
|
+
const lastKnown = lines[lines.length - 1];
|
|
4505
|
+
if (!lastRendered || !lastKnown || lastRendered.index !== lastKnown.index || lastRendered.text === "") {
|
|
4506
|
+
return false;
|
|
4507
|
+
}
|
|
4508
|
+
const expectedRenderedLineCount = Math.max(0, sourceLines.length - 1);
|
|
4509
|
+
const expectedLastRenderedLine = sourceLines.length >= 2 ? sourceLines[sourceLines.length - 2] ?? "" : "";
|
|
4510
|
+
return renderedLines.length === expectedRenderedLineCount && lastRendered.text === expectedLastRenderedLine;
|
|
4511
|
+
};
|
|
4079
4512
|
if (lines.length > 0) {
|
|
4080
4513
|
const hasLineHtml = lines.some((line) => typeof line.html === "string" && line.html.length > 0);
|
|
4081
|
-
if (!hasLineHtml &&
|
|
4082
|
-
return
|
|
4514
|
+
if (!hasLineHtml && blockHtml) {
|
|
4515
|
+
return blockHtml;
|
|
4083
4516
|
}
|
|
4084
4517
|
if (shouldVirtualize) {
|
|
4085
|
-
return composeHighlightedHtml(
|
|
4518
|
+
return composeHighlightedHtml(
|
|
4519
|
+
virtualization.window.visibleLines,
|
|
4520
|
+
composedPreAttrs,
|
|
4521
|
+
composedCodeAttrs,
|
|
4522
|
+
shouldAppendTerminalNewline(virtualization.window.visibleLines)
|
|
4523
|
+
);
|
|
4086
4524
|
}
|
|
4087
|
-
return composeHighlightedHtml(lines,
|
|
4525
|
+
return composeHighlightedHtml(lines, composedPreAttrs, composedCodeAttrs, shouldAppendTerminalNewline(lines));
|
|
4526
|
+
}
|
|
4527
|
+
if (!shouldVirtualize && blockHtml) {
|
|
4528
|
+
const hasHighlightedLines = blockHtml.includes('class="line"');
|
|
4529
|
+
if (!hasHighlightedLines && rawFallbackLines.length > 0) {
|
|
4530
|
+
return composeHighlightedHtml(
|
|
4531
|
+
rawFallbackLines,
|
|
4532
|
+
composedPreAttrs,
|
|
4533
|
+
composedCodeAttrs,
|
|
4534
|
+
sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
|
|
4535
|
+
);
|
|
4536
|
+
}
|
|
4537
|
+
return blockHtml;
|
|
4088
4538
|
}
|
|
4089
|
-
const blockHtml = node.block?.payload.highlightedHtml ?? "";
|
|
4090
4539
|
if (blockHtml?.trim().match(/^```[\w-]*\s*$/)) {
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4540
|
+
if (rawFallbackLines.length > 0) {
|
|
4541
|
+
return composeHighlightedHtml(
|
|
4542
|
+
rawFallbackLines,
|
|
4543
|
+
composedPreAttrs,
|
|
4544
|
+
composedCodeAttrs,
|
|
4545
|
+
sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
|
|
4546
|
+
);
|
|
4094
4547
|
}
|
|
4095
4548
|
}
|
|
4549
|
+
if (!blockHtml && rawFallbackLines.length > 0) {
|
|
4550
|
+
return composeHighlightedHtml(
|
|
4551
|
+
rawFallbackLines,
|
|
4552
|
+
composedPreAttrs,
|
|
4553
|
+
composedCodeAttrs,
|
|
4554
|
+
sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
|
|
4555
|
+
);
|
|
4556
|
+
}
|
|
4096
4557
|
return blockHtml;
|
|
4097
|
-
}, [shouldVirtualize, node.block?.payload.highlightedHtml,
|
|
4558
|
+
}, [blockIsFinalized, shouldVirtualize, node.block?.payload.highlightedHtml, node.block?.payload.meta, lines, lang, virtualization.window.visibleLines, node.block?.payload.raw]);
|
|
4098
4559
|
const rendered = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
4099
4560
|
CodeComponent,
|
|
4100
4561
|
{
|
|
@@ -4106,13 +4567,19 @@ var CodeBlockView = import_react10.default.memo(({ store, blockId, registry }) =
|
|
|
4106
4567
|
codeAttrs
|
|
4107
4568
|
}
|
|
4108
4569
|
);
|
|
4570
|
+
const effectiveLineCount = import_react10.default.useMemo(() => {
|
|
4571
|
+
if (lines.length > 0) {
|
|
4572
|
+
return lines.length;
|
|
4573
|
+
}
|
|
4574
|
+
return 1;
|
|
4575
|
+
}, [lines.length]);
|
|
4109
4576
|
const codeFrameClass = "not-prose flex flex-col rounded-lg border border-input pt-1 font-mono text-sm";
|
|
4110
4577
|
const codeMetricsAttrs = {
|
|
4111
4578
|
"data-code-block": "true",
|
|
4112
4579
|
"data-block-id": blockId,
|
|
4113
4580
|
"data-code-virtualized": shouldVirtualize ? "true" : "false",
|
|
4114
|
-
"data-code-total-lines": String(
|
|
4115
|
-
"data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines :
|
|
4581
|
+
"data-code-total-lines": String(effectiveLineCount),
|
|
4582
|
+
"data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines : effectiveLineCount),
|
|
4116
4583
|
"data-code-window-size": String(virtualization.config.windowSize)
|
|
4117
4584
|
};
|
|
4118
4585
|
const codeView = shouldVirtualize ? (() => {
|
|
@@ -4158,21 +4625,152 @@ var CodeBlockView = import_react10.default.memo(({ store, blockId, registry }) =
|
|
|
4158
4625
|
return codeView;
|
|
4159
4626
|
});
|
|
4160
4627
|
CodeBlockView.displayName = "CodeBlockView";
|
|
4161
|
-
function composeHighlightedHtml(lines, preAttrs, codeAttrs) {
|
|
4628
|
+
function composeHighlightedHtml(lines, preAttrs, codeAttrs, appendTerminalNewline = false) {
|
|
4162
4629
|
const lineMarkup = lines.map((line) => {
|
|
4163
|
-
const content = line.html ?? escapeHtml3(line.text);
|
|
4630
|
+
const content = line.html ?? renderTokenLineHtml(line.tokens) ?? escapeHtml3(line.text);
|
|
4164
4631
|
const dataLine = Number.isFinite(line.index) ? ` data-line="${line.index + 1}"` : "";
|
|
4165
4632
|
return `<span class="line"${dataLine}>${content}</span>`;
|
|
4166
4633
|
}).join("\n");
|
|
4167
4634
|
const preAttr = attrsToString(preAttrs);
|
|
4168
4635
|
const codeAttr = attrsToString(codeAttrs);
|
|
4169
|
-
return `<pre${preAttr}><code${codeAttr}>${lineMarkup}
|
|
4170
|
-
|
|
4636
|
+
return `<pre${preAttr}><code${codeAttr}>${lineMarkup}${appendTerminalNewline ? "\n" : ""}</code></pre>`;
|
|
4637
|
+
}
|
|
4638
|
+
var TOKEN_FONT_STYLE_ITALIC = 1;
|
|
4639
|
+
var TOKEN_FONT_STYLE_BOLD = 2;
|
|
4640
|
+
var TOKEN_FONT_STYLE_UNDERLINE = 4;
|
|
4641
|
+
var TOKEN_FONT_STYLE_STRIKETHROUGH = 8;
|
|
4642
|
+
function renderTokenLineHtml(tokens) {
|
|
4643
|
+
if (!tokens || !Array.isArray(tokens.spans)) {
|
|
4644
|
+
return null;
|
|
4645
|
+
}
|
|
4646
|
+
return mergeRenderableTokenSpans(tokens.spans).map((span) => renderTokenSpanHtml(span)).join("");
|
|
4647
|
+
}
|
|
4648
|
+
function renderTokenSpanHtml(span) {
|
|
4649
|
+
const dark = span.v?.dark;
|
|
4650
|
+
const light = span.v?.light;
|
|
4651
|
+
const single = span.s;
|
|
4652
|
+
const styles = [];
|
|
4653
|
+
if (dark?.fg) {
|
|
4654
|
+
styles.push(`--shiki-dark:${dark.fg}`);
|
|
4655
|
+
}
|
|
4656
|
+
if (light?.fg) {
|
|
4657
|
+
styles.push(`--shiki-light:${light.fg}`);
|
|
4658
|
+
}
|
|
4659
|
+
if (!light?.fg && !dark?.fg && single?.fg) {
|
|
4660
|
+
styles.push(`color:${single.fg}`);
|
|
4661
|
+
}
|
|
4662
|
+
const fontStyle = light?.fs ?? dark?.fs ?? single?.fs;
|
|
4663
|
+
if (typeof fontStyle === "number" && fontStyle > 0) {
|
|
4664
|
+
if (fontStyle & TOKEN_FONT_STYLE_ITALIC) {
|
|
4665
|
+
styles.push("font-style: italic");
|
|
4666
|
+
}
|
|
4667
|
+
if (fontStyle & TOKEN_FONT_STYLE_BOLD) {
|
|
4668
|
+
styles.push("font-weight: bold");
|
|
4669
|
+
}
|
|
4670
|
+
const decorations = [];
|
|
4671
|
+
if (fontStyle & TOKEN_FONT_STYLE_UNDERLINE) {
|
|
4672
|
+
decorations.push("underline");
|
|
4673
|
+
}
|
|
4674
|
+
if (fontStyle & TOKEN_FONT_STYLE_STRIKETHROUGH) {
|
|
4675
|
+
decorations.push("line-through");
|
|
4676
|
+
}
|
|
4677
|
+
if (decorations.length > 0) {
|
|
4678
|
+
styles.push(`text-decoration: ${decorations.join(" ")}`);
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
const content = escapeHtml3(span.t ?? "");
|
|
4682
|
+
if (styles.length === 0) {
|
|
4683
|
+
return content;
|
|
4684
|
+
}
|
|
4685
|
+
return `<span style="${styles.join(";")}">${content}</span>`;
|
|
4686
|
+
}
|
|
4687
|
+
function mergeRenderableTokenSpans(spans) {
|
|
4688
|
+
if (spans.length <= 1) {
|
|
4689
|
+
return spans.slice();
|
|
4690
|
+
}
|
|
4691
|
+
const merged = [];
|
|
4692
|
+
let pendingWhitespace = "";
|
|
4693
|
+
for (const span of spans) {
|
|
4694
|
+
const text = typeof span.t === "string" ? span.t : "";
|
|
4695
|
+
if (/^[\t ]+$/.test(text)) {
|
|
4696
|
+
pendingWhitespace += text;
|
|
4697
|
+
continue;
|
|
4698
|
+
}
|
|
4699
|
+
const nextSpan = pendingWhitespace ? { ...span, t: `${pendingWhitespace}${text}` } : span;
|
|
4700
|
+
pendingWhitespace = "";
|
|
4701
|
+
const previous = merged[merged.length - 1];
|
|
4702
|
+
if (previous && sameTokenSpanStyle(previous, nextSpan)) {
|
|
4703
|
+
previous.t += nextSpan.t;
|
|
4704
|
+
} else {
|
|
4705
|
+
merged.push({ ...nextSpan });
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
if (pendingWhitespace) {
|
|
4709
|
+
const previous = merged[merged.length - 1];
|
|
4710
|
+
if (previous) {
|
|
4711
|
+
previous.t += pendingWhitespace;
|
|
4712
|
+
} else {
|
|
4713
|
+
merged.push({ t: pendingWhitespace });
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
return merged;
|
|
4717
|
+
}
|
|
4718
|
+
function sameTokenSpanStyle(a, b) {
|
|
4719
|
+
const variantKeys = /* @__PURE__ */ new Set([
|
|
4720
|
+
...Object.keys(a.v ?? {}),
|
|
4721
|
+
...Object.keys(b.v ?? {})
|
|
4722
|
+
]);
|
|
4723
|
+
for (const key of variantKeys) {
|
|
4724
|
+
const aStyle = a.v?.[key];
|
|
4725
|
+
const bStyle = b.v?.[key];
|
|
4726
|
+
if ((aStyle?.fg ?? null) !== (bStyle?.fg ?? null)) return false;
|
|
4727
|
+
if ((aStyle?.bg ?? null) !== (bStyle?.bg ?? null)) return false;
|
|
4728
|
+
if ((aStyle?.fs ?? null) !== (bStyle?.fs ?? null)) return false;
|
|
4729
|
+
}
|
|
4730
|
+
if ((a.s?.fg ?? null) !== (b.s?.fg ?? null)) return false;
|
|
4731
|
+
if ((a.s?.bg ?? null) !== (b.s?.bg ?? null)) return false;
|
|
4732
|
+
if ((a.s?.fs ?? null) !== (b.s?.fs ?? null)) return false;
|
|
4733
|
+
return true;
|
|
4734
|
+
}
|
|
4735
|
+
function resolveCodeSourceText(raw, metaCode) {
|
|
4736
|
+
if (metaCode !== null) {
|
|
4737
|
+
return metaCode.replace(/\r\n?/g, "\n");
|
|
4738
|
+
}
|
|
4739
|
+
if (!raw) return "";
|
|
4740
|
+
const normalized = raw.replace(/\r\n?/g, "\n");
|
|
4741
|
+
const fenced = (0, import_core5.stripCodeFence)(normalized);
|
|
4742
|
+
return fenced.hadFence ? fenced.code : normalized;
|
|
4171
4743
|
}
|
|
4172
4744
|
function attrsToString(attrs) {
|
|
4173
4745
|
if (!attrs) return "";
|
|
4174
4746
|
return Object.entries(attrs).map(([key, value]) => ` ${key}="${escapeAttribute(value)}"`).join("");
|
|
4175
4747
|
}
|
|
4748
|
+
function buildCodeFallbackLines(raw) {
|
|
4749
|
+
const normalized = normalizeCodeFallbackRaw(raw);
|
|
4750
|
+
if (normalized.length === 0) {
|
|
4751
|
+
if (/^\s*```[^\n]*\n?\s*$/.test(raw)) {
|
|
4752
|
+
return [{ id: "temp-0", index: 0, text: "", html: null }];
|
|
4753
|
+
}
|
|
4754
|
+
return [];
|
|
4755
|
+
}
|
|
4756
|
+
return normalized.split("\n").map((line, index) => ({
|
|
4757
|
+
id: `temp-${index}`,
|
|
4758
|
+
index,
|
|
4759
|
+
text: line,
|
|
4760
|
+
html: null
|
|
4761
|
+
}));
|
|
4762
|
+
}
|
|
4763
|
+
function normalizeCodeFallbackRaw(raw) {
|
|
4764
|
+
if (!raw) return "";
|
|
4765
|
+
const normalized = raw.replace(/\r\n?/g, "\n");
|
|
4766
|
+
const fenced = (0, import_core5.stripCodeFence)(normalized);
|
|
4767
|
+
if (!fenced.hadFence) {
|
|
4768
|
+
return normalized.replace(/\n+$/, "");
|
|
4769
|
+
}
|
|
4770
|
+
let code = fenced.code;
|
|
4771
|
+
code = code.replace(/\n+$/, "");
|
|
4772
|
+
return code;
|
|
4773
|
+
}
|
|
4176
4774
|
function escapeHtml3(value) {
|
|
4177
4775
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4178
4776
|
}
|