@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
|
@@ -81,7 +81,7 @@ var init_mdx_client = __esm({
|
|
|
81
81
|
if (block.type !== "mdx") {
|
|
82
82
|
throw new Error("Block is not MDX type");
|
|
83
83
|
}
|
|
84
|
-
const cacheKey = block.id
|
|
84
|
+
const cacheKey = `${block.id}\0${block.payload.raw ?? ""}`;
|
|
85
85
|
const cachedPromise = this.cache.get(cacheKey);
|
|
86
86
|
if (cachedPromise) {
|
|
87
87
|
return cachedPromise;
|
|
@@ -1173,7 +1173,11 @@ var defaultInlineComponents = {
|
|
|
1173
1173
|
const n = number ?? "?";
|
|
1174
1174
|
const href = number ? `#fn:${n}` : void 0;
|
|
1175
1175
|
const id = number ? `fnref:${n}` : void 0;
|
|
1176
|
-
return React4.createElement(
|
|
1176
|
+
return React4.createElement(
|
|
1177
|
+
"sup",
|
|
1178
|
+
{ className: "footnote-ref" },
|
|
1179
|
+
React4.createElement("a", { href, id, "data-label": label, style: { color: "var(--foreground)", textDecoration: "underline" } }, String(n))
|
|
1180
|
+
);
|
|
1177
1181
|
}
|
|
1178
1182
|
};
|
|
1179
1183
|
var defaultBlockComponents = {
|
|
@@ -1316,7 +1320,7 @@ var defaultBlockComponents = {
|
|
|
1316
1320
|
dangerouslySetInnerHTML: { __html: htmlString }
|
|
1317
1321
|
});
|
|
1318
1322
|
},
|
|
1319
|
-
mdx: ({ compiledRef, compiledModule, status, errorMessage
|
|
1323
|
+
mdx: ({ compiledRef, compiledModule, status, errorMessage }) => {
|
|
1320
1324
|
const [Comp, setComp] = React4.useState(null);
|
|
1321
1325
|
const [internalError, setInternalError] = React4.useState(null);
|
|
1322
1326
|
const [renderError, setRenderError] = React4.useState(null);
|
|
@@ -1417,7 +1421,6 @@ var defaultBlockComponents = {
|
|
|
1417
1421
|
const waitingForVisibility = deferHydration && !shouldHydrate && Boolean(resolvedId);
|
|
1418
1422
|
const pendingLabel = waitingForVisibility ? "MDX ready (hydrate on view)\u2026" : compileMode === "worker" ? "Compiling MDX (client)\u2026" : "Compiling MDX (server)\u2026";
|
|
1419
1423
|
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.");
|
|
1420
|
-
const rawFallback = typeof raw === "string" && raw.trim().length > 0 ? React4.createElement("pre", { className: "markdown-mdx-fallback" }, raw) : null;
|
|
1421
1424
|
const allowRetry = Boolean(internalError || renderError);
|
|
1422
1425
|
const retryButton = allowRetry ? React4.createElement(
|
|
1423
1426
|
"button",
|
|
@@ -1429,9 +1432,10 @@ var defaultBlockComponents = {
|
|
|
1429
1432
|
"Retry hydration"
|
|
1430
1433
|
) : null;
|
|
1431
1434
|
const errorPanel = React4.createElement(
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
React4.createElement(
|
|
1435
|
+
React4.Fragment,
|
|
1436
|
+
null,
|
|
1437
|
+
React4.createElement("strong", null, "MDX failed"),
|
|
1438
|
+
React4.createElement("div", null, message)
|
|
1435
1439
|
);
|
|
1436
1440
|
if (Comp && !failureMessage) {
|
|
1437
1441
|
return React4.createElement(
|
|
@@ -1454,14 +1458,13 @@ var defaultBlockComponents = {
|
|
|
1454
1458
|
return React4.createElement(
|
|
1455
1459
|
"div",
|
|
1456
1460
|
{
|
|
1457
|
-
className: "markdown-mdx",
|
|
1461
|
+
className: "markdown-mdx error",
|
|
1458
1462
|
ref: containerRef,
|
|
1459
1463
|
"data-mdx-ref": pendingId,
|
|
1460
1464
|
"data-mdx-status": "error",
|
|
1461
1465
|
"data-mdx-mode": compileMode
|
|
1462
1466
|
},
|
|
1463
1467
|
errorPanel,
|
|
1464
|
-
rawFallback,
|
|
1465
1468
|
retryButton
|
|
1466
1469
|
);
|
|
1467
1470
|
}
|
|
@@ -1488,7 +1491,16 @@ var defaultBlockComponents = {
|
|
|
1488
1491
|
{ key: item.number, id: `fn:${item.number}` },
|
|
1489
1492
|
renderInlineNodes(item.inlines, defaultInlineComponents),
|
|
1490
1493
|
" ",
|
|
1491
|
-
React4.createElement(
|
|
1494
|
+
React4.createElement(
|
|
1495
|
+
"a",
|
|
1496
|
+
{
|
|
1497
|
+
href: `#fnref:${item.number}`,
|
|
1498
|
+
className: "footnote-backref",
|
|
1499
|
+
"aria-label": "Back to content",
|
|
1500
|
+
style: { color: "var(--foreground)", textDecoration: "underline" }
|
|
1501
|
+
},
|
|
1502
|
+
"\u21A9"
|
|
1503
|
+
)
|
|
1492
1504
|
)
|
|
1493
1505
|
);
|
|
1494
1506
|
return React4.createElement("section", { className: "footnotes" }, React4.createElement("hr", {}), React4.createElement("ol", {}, listItems));
|
|
@@ -2034,7 +2046,10 @@ function useCodeHighlightRequester() {
|
|
|
2034
2046
|
}
|
|
2035
2047
|
|
|
2036
2048
|
// src/renderer/patch-batching.ts
|
|
2037
|
-
import { isHeavyPatch, splitPatchBatch } from "@stream-mdx/core/perf/patch-batching";
|
|
2049
|
+
import { getPatchKind, isHeavyPatch, splitPatchBatch } from "@stream-mdx/core/perf/patch-batching";
|
|
2050
|
+
|
|
2051
|
+
// src/renderer/patch-commit-scheduler.ts
|
|
2052
|
+
import { getPatchKind as getPatchKind2 } from "@stream-mdx/core/perf/patch-batching";
|
|
2038
2053
|
|
|
2039
2054
|
// src/renderer/patch-coalescing.ts
|
|
2040
2055
|
import {
|
|
@@ -2052,6 +2067,9 @@ var COALESCING_DURATION_ACTIVATE_MS = 6;
|
|
|
2052
2067
|
var COALESCING_DURATION_DEACTIVATE_MS = 4;
|
|
2053
2068
|
var RETAIN_TOUCHED_IN_HISTORY = typeof process !== "undefined" && process.env ? process.env.NODE_ENV !== "production" : true;
|
|
2054
2069
|
var EMPTY_TOUCHED_SET = /* @__PURE__ */ new Set();
|
|
2070
|
+
function shouldSkipCoalescing(patches) {
|
|
2071
|
+
return patches.length <= 1;
|
|
2072
|
+
}
|
|
2055
2073
|
function getDefaultNow() {
|
|
2056
2074
|
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
2057
2075
|
return () => performance.now();
|
|
@@ -2078,8 +2096,9 @@ function getDefaultCancelRaf() {
|
|
|
2078
2096
|
}
|
|
2079
2097
|
var PatchCommitScheduler = class {
|
|
2080
2098
|
constructor(params) {
|
|
2081
|
-
this.
|
|
2082
|
-
this.
|
|
2099
|
+
this.semanticQueue = [];
|
|
2100
|
+
this.enrichmentHighQueue = [];
|
|
2101
|
+
this.enrichmentLowQueue = [];
|
|
2083
2102
|
this.scheduled = false;
|
|
2084
2103
|
this.flushing = false;
|
|
2085
2104
|
this.scheduledHandle = null;
|
|
@@ -2091,6 +2110,7 @@ var PatchCommitScheduler = class {
|
|
|
2091
2110
|
this.scheduleToken = 0;
|
|
2092
2111
|
this.coalescingDurationSamples = [];
|
|
2093
2112
|
this.adaptiveBudgetActive = false;
|
|
2113
|
+
this.flushCount = 0;
|
|
2094
2114
|
this.store = params.store;
|
|
2095
2115
|
this.onFlush = params.onFlush;
|
|
2096
2116
|
this.frameBudgetMs = Math.max(1, params.options?.frameBudgetMs ?? 10);
|
|
@@ -2106,6 +2126,8 @@ var PatchCommitScheduler = class {
|
|
|
2106
2126
|
this.maxLowPriorityBatchesPerFlush = params.options?.maxLowPriorityBatchesPerFlush ?? 2;
|
|
2107
2127
|
this.urgentQueueThreshold = Math.max(1, params.options?.urgentQueueThreshold ?? 4);
|
|
2108
2128
|
this.historyLimit = Math.max(1, params.options?.historyLimit ?? 200);
|
|
2129
|
+
this.startupMicrotaskFlushes = Math.max(0, Math.floor(params.options?.startupMicrotaskFlushes ?? 0));
|
|
2130
|
+
this.adaptiveBudgeting = params.options?.adaptiveBudgeting ?? true;
|
|
2109
2131
|
const requestedBatch = params.options?.batch;
|
|
2110
2132
|
if (requestedBatch === "microtask") {
|
|
2111
2133
|
this.batchStrategy = "microtask";
|
|
@@ -2132,19 +2154,23 @@ var PatchCommitScheduler = class {
|
|
|
2132
2154
|
patches: input.patches,
|
|
2133
2155
|
meta: input.meta ?? {},
|
|
2134
2156
|
receivedAt: input.meta?.receivedAt ?? this.now(),
|
|
2157
|
+
kind: input.meta?.kind === "semantic" || input.meta?.kind === "enrichment" ? input.meta.kind : input.patches.some((patch) => getPatchKind2(patch) === "semantic") ? "semantic" : "enrichment",
|
|
2135
2158
|
priority: input.meta?.priority === "low" ? "low" : "high"
|
|
2136
2159
|
};
|
|
2137
|
-
if (batch.
|
|
2138
|
-
|
|
2160
|
+
if (batch.kind === "semantic") {
|
|
2161
|
+
batch.priority = "high";
|
|
2162
|
+
this.semanticQueue.push(batch);
|
|
2163
|
+
} else if (batch.priority === "low") {
|
|
2164
|
+
this.enrichmentLowQueue.push(batch);
|
|
2139
2165
|
} else {
|
|
2140
|
-
this.
|
|
2166
|
+
this.enrichmentHighQueue.push(batch);
|
|
2141
2167
|
}
|
|
2142
2168
|
if (!this.paused) {
|
|
2143
2169
|
this.schedule();
|
|
2144
2170
|
}
|
|
2145
2171
|
}
|
|
2146
2172
|
flushAll() {
|
|
2147
|
-
if (this.
|
|
2173
|
+
if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0 && !this.flushing) {
|
|
2148
2174
|
return null;
|
|
2149
2175
|
}
|
|
2150
2176
|
this.cancelScheduled();
|
|
@@ -2154,9 +2180,11 @@ var PatchCommitScheduler = class {
|
|
|
2154
2180
|
}
|
|
2155
2181
|
clear() {
|
|
2156
2182
|
this.cancelScheduled();
|
|
2157
|
-
this.
|
|
2158
|
-
this.
|
|
2183
|
+
this.semanticQueue = [];
|
|
2184
|
+
this.enrichmentHighQueue = [];
|
|
2185
|
+
this.enrichmentLowQueue = [];
|
|
2159
2186
|
this.flushing = false;
|
|
2187
|
+
this.flushCount = 0;
|
|
2160
2188
|
this.coalescingDurationSamples = [];
|
|
2161
2189
|
this.adaptiveBudgetActive = false;
|
|
2162
2190
|
this.adaptiveHighBatchCap = void 0;
|
|
@@ -2164,10 +2192,10 @@ var PatchCommitScheduler = class {
|
|
|
2164
2192
|
this.maybeResolveIdle();
|
|
2165
2193
|
}
|
|
2166
2194
|
getPendingCount() {
|
|
2167
|
-
return this.
|
|
2195
|
+
return this.semanticQueue.length + this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
2168
2196
|
}
|
|
2169
2197
|
isIdle() {
|
|
2170
|
-
return !this.flushing && !this.scheduled && this.
|
|
2198
|
+
return !this.flushing && !this.scheduled && this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0;
|
|
2171
2199
|
}
|
|
2172
2200
|
awaitIdle() {
|
|
2173
2201
|
if (this.isIdle()) {
|
|
@@ -2207,7 +2235,8 @@ var PatchCommitScheduler = class {
|
|
|
2207
2235
|
this.runFlush(this.frameBudgetMs, false);
|
|
2208
2236
|
this.maybeResolveIdle();
|
|
2209
2237
|
};
|
|
2210
|
-
|
|
2238
|
+
const useStartupMicrotask = this.batchStrategy !== "microtask" && this.startupMicrotaskFlushes > 0 && this.flushCount < this.startupMicrotaskFlushes && typeof queueMicrotask === "function";
|
|
2239
|
+
if ((this.batchStrategy === "microtask" || useStartupMicrotask) && typeof queueMicrotask === "function") {
|
|
2211
2240
|
this.scheduledViaRaf = false;
|
|
2212
2241
|
this.scheduledHandle = token;
|
|
2213
2242
|
queueMicrotask(execute);
|
|
@@ -2237,12 +2266,14 @@ var PatchCommitScheduler = class {
|
|
|
2237
2266
|
if (this.paused && !manual) {
|
|
2238
2267
|
return null;
|
|
2239
2268
|
}
|
|
2240
|
-
if (this.
|
|
2269
|
+
if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0) {
|
|
2241
2270
|
return null;
|
|
2242
2271
|
}
|
|
2243
2272
|
this.flushing = true;
|
|
2244
2273
|
const startedAt = this.now();
|
|
2245
|
-
const
|
|
2274
|
+
const initialSemanticQueueSize = this.semanticQueue.length;
|
|
2275
|
+
const initialEnrichmentQueueSize = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
2276
|
+
const initialQueueSize = initialSemanticQueueSize + initialEnrichmentQueueSize;
|
|
2246
2277
|
const batches = [];
|
|
2247
2278
|
let totalInputPatches = 0;
|
|
2248
2279
|
let totalAppliedPatches = 0;
|
|
@@ -2257,7 +2288,15 @@ var PatchCommitScheduler = class {
|
|
|
2257
2288
|
consumed += 1;
|
|
2258
2289
|
const applyStart = this.now();
|
|
2259
2290
|
const queueDelayMs = Math.max(0, applyStart - next.receivedAt);
|
|
2260
|
-
|
|
2291
|
+
let coalesced;
|
|
2292
|
+
let coalescingMetrics;
|
|
2293
|
+
if (shouldSkipCoalescing(next.patches)) {
|
|
2294
|
+
coalesced = next.patches;
|
|
2295
|
+
} else {
|
|
2296
|
+
const result = coalescePatchesWithMetrics(next.patches, DEFAULT_COALESCE_CONFIG);
|
|
2297
|
+
coalesced = result.patches;
|
|
2298
|
+
coalescingMetrics = result.metrics;
|
|
2299
|
+
}
|
|
2261
2300
|
const touched = this.store.applyPatches(coalesced, {
|
|
2262
2301
|
coalesced: true,
|
|
2263
2302
|
metrics: coalescingMetrics
|
|
@@ -2277,6 +2316,7 @@ var PatchCommitScheduler = class {
|
|
|
2277
2316
|
receivedAt: next.receivedAt,
|
|
2278
2317
|
appliedAt: applyStart,
|
|
2279
2318
|
priority: next.priority,
|
|
2319
|
+
kind: next.kind,
|
|
2280
2320
|
coalescing: coalescingMetrics
|
|
2281
2321
|
});
|
|
2282
2322
|
if (maxBatches && batches.length - startingBatchCount >= maxBatches) {
|
|
@@ -2293,27 +2333,32 @@ var PatchCommitScheduler = class {
|
|
|
2293
2333
|
queue.splice(0, consumed);
|
|
2294
2334
|
}
|
|
2295
2335
|
};
|
|
2296
|
-
|
|
2336
|
+
processQueue(this.semanticQueue, void 0, Number.POSITIVE_INFINITY);
|
|
2337
|
+
const initialHighQueueSize = this.enrichmentHighQueue.length;
|
|
2297
2338
|
const configuredHighLimit = manual || !this.maxBatchesPerFlush ? void 0 : initialHighQueueSize > this.maxBatchesPerFlush ? void 0 : this.maxBatchesPerFlush;
|
|
2298
|
-
const highBatchLimit = this.adaptiveHighBatchCap ?? configuredHighLimit;
|
|
2299
|
-
processQueue(this.
|
|
2339
|
+
const highBatchLimit = this.adaptiveBudgeting ? this.adaptiveHighBatchCap ?? configuredHighLimit : configuredHighLimit;
|
|
2340
|
+
processQueue(this.enrichmentHighQueue, highBatchLimit, budgetMs);
|
|
2300
2341
|
if (manual) {
|
|
2301
|
-
processQueue(this.
|
|
2302
|
-
} else if (this.
|
|
2342
|
+
processQueue(this.enrichmentLowQueue, void 0, Number.POSITIVE_INFINITY);
|
|
2343
|
+
} else if (this.enrichmentLowQueue.length > 0) {
|
|
2303
2344
|
elapsed = this.now() - startedAt;
|
|
2304
2345
|
const budgetForLow = Math.max(0, this.lowPriorityFrameBudgetMs - elapsed);
|
|
2305
|
-
|
|
2306
|
-
|
|
2346
|
+
const processedHigherPriorityWork = batches.length > 0;
|
|
2347
|
+
if (budgetForLow > 0 || !processedHigherPriorityWork) {
|
|
2348
|
+
const initialLowQueueSize = this.enrichmentLowQueue.length;
|
|
2307
2349
|
const configuredLowLimit = manual || !this.maxLowPriorityBatchesPerFlush ? void 0 : initialLowQueueSize > this.maxLowPriorityBatchesPerFlush ? void 0 : this.maxLowPriorityBatchesPerFlush;
|
|
2308
|
-
const lowBatchLimit = this.adaptiveLowBatchCap ?? configuredLowLimit;
|
|
2309
|
-
processQueue(this.
|
|
2350
|
+
const lowBatchLimit = this.adaptiveBudgeting ? this.adaptiveLowBatchCap ?? configuredLowLimit : configuredLowLimit;
|
|
2351
|
+
processQueue(this.enrichmentLowQueue, lowBatchLimit, budgetForLow);
|
|
2310
2352
|
}
|
|
2311
2353
|
}
|
|
2312
2354
|
this.flushing = false;
|
|
2313
2355
|
const completedAt = this.now();
|
|
2314
2356
|
const totalDurationMs = completedAt - startedAt;
|
|
2315
|
-
const
|
|
2357
|
+
const remainingSemantic = this.semanticQueue.length;
|
|
2358
|
+
const remainingEnrichment = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
2359
|
+
const remaining = remainingSemantic + remainingEnrichment;
|
|
2316
2360
|
if (batches.length > 0) {
|
|
2361
|
+
this.flushCount += 1;
|
|
2317
2362
|
try {
|
|
2318
2363
|
this.onFlush({
|
|
2319
2364
|
batches,
|
|
@@ -2321,7 +2366,11 @@ var PatchCommitScheduler = class {
|
|
|
2321
2366
|
totalAppliedPatches,
|
|
2322
2367
|
totalDurationMs,
|
|
2323
2368
|
remainingQueueSize: remaining,
|
|
2369
|
+
remainingSemanticQueueSize: remainingSemantic,
|
|
2370
|
+
remainingEnrichmentQueueSize: remainingEnrichment,
|
|
2324
2371
|
queueDepthBefore: initialQueueSize,
|
|
2372
|
+
semanticQueueDepthBefore: initialSemanticQueueSize,
|
|
2373
|
+
enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
|
|
2325
2374
|
flushStartedAt: startedAt,
|
|
2326
2375
|
flushCompletedAt: completedAt
|
|
2327
2376
|
});
|
|
@@ -2332,21 +2381,27 @@ var PatchCommitScheduler = class {
|
|
|
2332
2381
|
if (remaining > 0 && !this.paused) {
|
|
2333
2382
|
this.schedule();
|
|
2334
2383
|
}
|
|
2335
|
-
const coalescingStats = this.computeCoalescingDurationStats();
|
|
2336
|
-
this.
|
|
2337
|
-
|
|
2384
|
+
const coalescingStats = this.adaptiveBudgeting ? this.computeCoalescingDurationStats() : { sampleCount: this.coalescingDurationSamples.length, p95: null };
|
|
2385
|
+
if (this.adaptiveBudgeting) {
|
|
2386
|
+
this.updateAdaptiveBudget(coalescingStats.p95);
|
|
2387
|
+
}
|
|
2388
|
+
const adaptiveState = this.adaptiveBudgeting ? this.snapshotAdaptiveBudget(coalescingStats.p95 ?? null) : void 0;
|
|
2338
2389
|
const flushResult = batches.length > 0 ? {
|
|
2339
2390
|
batches,
|
|
2340
2391
|
totalPatches: totalInputPatches,
|
|
2341
2392
|
totalAppliedPatches,
|
|
2342
2393
|
totalDurationMs,
|
|
2343
2394
|
remainingQueueSize: remaining,
|
|
2395
|
+
remainingSemanticQueueSize: remainingSemantic,
|
|
2396
|
+
remainingEnrichmentQueueSize: remainingEnrichment,
|
|
2344
2397
|
queueDepthBefore: initialQueueSize,
|
|
2398
|
+
semanticQueueDepthBefore: initialSemanticQueueSize,
|
|
2399
|
+
enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
|
|
2345
2400
|
flushStartedAt: startedAt,
|
|
2346
2401
|
flushCompletedAt: completedAt,
|
|
2347
2402
|
coalescingDurationP95: coalescingStats.p95 ?? void 0,
|
|
2348
2403
|
coalescingDurationSampleCount: coalescingStats.sampleCount,
|
|
2349
|
-
adaptiveBudgetActive: adaptiveState
|
|
2404
|
+
adaptiveBudgetActive: adaptiveState?.active,
|
|
2350
2405
|
adaptiveBudgetState: adaptiveState
|
|
2351
2406
|
} : null;
|
|
2352
2407
|
if (flushResult) {
|
|
@@ -2382,6 +2437,9 @@ var PatchCommitScheduler = class {
|
|
|
2382
2437
|
};
|
|
2383
2438
|
}
|
|
2384
2439
|
updateAdaptiveBudget(p95) {
|
|
2440
|
+
if (!this.adaptiveBudgeting) {
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2385
2443
|
if (p95 !== null && p95 > COALESCING_DURATION_ACTIVATE_MS) {
|
|
2386
2444
|
if (!this.adaptiveBudgetActive) {
|
|
2387
2445
|
this.adaptiveBudgetActive = true;
|
|
@@ -2455,11 +2513,13 @@ var PatchCommitScheduler = class {
|
|
|
2455
2513
|
}
|
|
2456
2514
|
restart() {
|
|
2457
2515
|
this.cancelScheduled();
|
|
2458
|
-
this.
|
|
2459
|
-
this.
|
|
2516
|
+
this.semanticQueue = [];
|
|
2517
|
+
this.enrichmentHighQueue = [];
|
|
2518
|
+
this.enrichmentLowQueue = [];
|
|
2460
2519
|
this.flushing = false;
|
|
2461
2520
|
this.sequence = 0;
|
|
2462
2521
|
this.history = [];
|
|
2522
|
+
this.flushCount = 0;
|
|
2463
2523
|
this.scheduleToken++;
|
|
2464
2524
|
this.paused = false;
|
|
2465
2525
|
this.coalescingDurationSamples = [];
|
|
@@ -2506,6 +2566,12 @@ var PatchCommitScheduler = class {
|
|
|
2506
2566
|
if (typeof options.historyLimit !== "undefined") {
|
|
2507
2567
|
this.setHistoryLimit(options.historyLimit);
|
|
2508
2568
|
}
|
|
2569
|
+
if (typeof options.startupMicrotaskFlushes !== "undefined") {
|
|
2570
|
+
this.startupMicrotaskFlushes = Math.max(0, Math.floor(options.startupMicrotaskFlushes));
|
|
2571
|
+
}
|
|
2572
|
+
if (typeof options.adaptiveBudgeting !== "undefined") {
|
|
2573
|
+
this.adaptiveBudgeting = options.adaptiveBudgeting;
|
|
2574
|
+
}
|
|
2509
2575
|
if (typeof options.batch !== "undefined") {
|
|
2510
2576
|
const requestedBatch = options.batch;
|
|
2511
2577
|
if (requestedBatch === "microtask") {
|
|
@@ -2540,6 +2606,7 @@ import {
|
|
|
2540
2606
|
sanitizeHTML as sanitizeHTML2,
|
|
2541
2607
|
PATCH_ROOT_ID
|
|
2542
2608
|
} from "@stream-mdx/core";
|
|
2609
|
+
import { getPatchKind as getPatchKind3 } from "@stream-mdx/core/perf/patch-batching";
|
|
2543
2610
|
|
|
2544
2611
|
// src/renderer/list-utils.ts
|
|
2545
2612
|
function updateNodeDepth(node, depth) {
|
|
@@ -2635,12 +2702,28 @@ function snapshotToBlock(node) {
|
|
|
2635
2702
|
}
|
|
2636
2703
|
return null;
|
|
2637
2704
|
}
|
|
2638
|
-
var
|
|
2639
|
-
var
|
|
2705
|
+
var nodeSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
|
|
2706
|
+
var childrenSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
|
|
2640
2707
|
var NODE_SNAPSHOT_CACHE_LIMIT = 5e4;
|
|
2641
2708
|
var CHILDREN_SNAPSHOT_CACHE_LIMIT = 5e4;
|
|
2642
2709
|
var NODE_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(NODE_SNAPSHOT_CACHE_LIMIT * 0.1));
|
|
2643
2710
|
var CHILDREN_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(CHILDREN_SNAPSHOT_CACHE_LIMIT * 0.1));
|
|
2711
|
+
function getNodeSnapshotCacheForMap(map) {
|
|
2712
|
+
let cache = nodeSnapshotCacheByMap.get(map);
|
|
2713
|
+
if (!cache) {
|
|
2714
|
+
cache = /* @__PURE__ */ new Map();
|
|
2715
|
+
nodeSnapshotCacheByMap.set(map, cache);
|
|
2716
|
+
}
|
|
2717
|
+
return cache;
|
|
2718
|
+
}
|
|
2719
|
+
function getChildrenSnapshotCacheForMap(map) {
|
|
2720
|
+
let cache = childrenSnapshotCacheByMap.get(map);
|
|
2721
|
+
if (!cache) {
|
|
2722
|
+
cache = /* @__PURE__ */ new Map();
|
|
2723
|
+
childrenSnapshotCacheByMap.set(map, cache);
|
|
2724
|
+
}
|
|
2725
|
+
return cache;
|
|
2726
|
+
}
|
|
2644
2727
|
function pruneCache(cache, max, buffer) {
|
|
2645
2728
|
if (cache.size <= max + buffer) return;
|
|
2646
2729
|
let remaining = cache.size - max;
|
|
@@ -2650,6 +2733,14 @@ function pruneCache(cache, max, buffer) {
|
|
|
2650
2733
|
if (remaining <= 0) break;
|
|
2651
2734
|
}
|
|
2652
2735
|
}
|
|
2736
|
+
function deleteSnapshotCacheEntry(map, id) {
|
|
2737
|
+
getNodeSnapshotCacheForMap(map).delete(id);
|
|
2738
|
+
getChildrenSnapshotCacheForMap(map).delete(id);
|
|
2739
|
+
}
|
|
2740
|
+
function clearSnapshotCaches(map) {
|
|
2741
|
+
getNodeSnapshotCacheForMap(map).clear();
|
|
2742
|
+
getChildrenSnapshotCacheForMap(map).clear();
|
|
2743
|
+
}
|
|
2653
2744
|
var EMPTY_CHILDREN = Object.freeze([]);
|
|
2654
2745
|
var EMPTY_NODE_SNAPSHOT = Object.freeze({ version: -1, node: void 0 });
|
|
2655
2746
|
var EMPTY_CHILDREN_SNAPSHOT = Object.freeze({
|
|
@@ -2817,9 +2908,57 @@ function createRootRecord() {
|
|
|
2817
2908
|
parentId: null,
|
|
2818
2909
|
children: [],
|
|
2819
2910
|
props: {},
|
|
2820
|
-
version: 0
|
|
2911
|
+
version: 0,
|
|
2912
|
+
blockEpoch: 0
|
|
2821
2913
|
};
|
|
2822
2914
|
}
|
|
2915
|
+
function readNumericEpoch(value) {
|
|
2916
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
2917
|
+
return void 0;
|
|
2918
|
+
}
|
|
2919
|
+
return value;
|
|
2920
|
+
}
|
|
2921
|
+
function readSnapshotBlockEpoch(snapshot) {
|
|
2922
|
+
return readNumericEpoch(snapshot.meta?.blockEpoch);
|
|
2923
|
+
}
|
|
2924
|
+
function readPatchBlockEpoch(patch) {
|
|
2925
|
+
if (patch.op === "setHTML") {
|
|
2926
|
+
return readNumericEpoch(patch.patchMeta?.blockEpoch);
|
|
2927
|
+
}
|
|
2928
|
+
return readNumericEpoch(patch.meta?.blockEpoch);
|
|
2929
|
+
}
|
|
2930
|
+
function readPatchParseEpoch(patch) {
|
|
2931
|
+
if (patch.op === "setHTML") {
|
|
2932
|
+
return readNumericEpoch(patch.patchMeta?.parseEpoch);
|
|
2933
|
+
}
|
|
2934
|
+
return readNumericEpoch(patch.meta?.parseEpoch);
|
|
2935
|
+
}
|
|
2936
|
+
function readPatchTx(patch) {
|
|
2937
|
+
if (patch.op === "setHTML") {
|
|
2938
|
+
return readNumericEpoch(patch.patchMeta?.tx);
|
|
2939
|
+
}
|
|
2940
|
+
return readNumericEpoch(patch.meta?.tx);
|
|
2941
|
+
}
|
|
2942
|
+
function assignSubtreeBlockEpoch(map, rootId, blockEpoch, touched) {
|
|
2943
|
+
const root = map.get(rootId);
|
|
2944
|
+
if (!root) return;
|
|
2945
|
+
const stack = [root];
|
|
2946
|
+
while (stack.length > 0) {
|
|
2947
|
+
const node = stack.pop();
|
|
2948
|
+
if (!node) continue;
|
|
2949
|
+
if (node.blockEpoch !== blockEpoch) {
|
|
2950
|
+
node.blockEpoch = blockEpoch;
|
|
2951
|
+
node.version += 1;
|
|
2952
|
+
touched?.add(node.id);
|
|
2953
|
+
}
|
|
2954
|
+
for (const childId of node.children) {
|
|
2955
|
+
const child = map.get(childId);
|
|
2956
|
+
if (child) {
|
|
2957
|
+
stack.push(child);
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2823
2962
|
function ensureArray(value) {
|
|
2824
2963
|
return Array.isArray(value) ? value : [];
|
|
2825
2964
|
}
|
|
@@ -2879,7 +3018,7 @@ function escapeHtml2(value) {
|
|
|
2879
3018
|
}
|
|
2880
3019
|
function stripOuterLineSpan(html) {
|
|
2881
3020
|
if (!html) return null;
|
|
2882
|
-
const openTagMatch = html.match(/<span[^>]*>/i);
|
|
3021
|
+
const openTagMatch = html.match(/<span[^>]*class="[^"]*\bline\b[^"]*"[^>]*>/i);
|
|
2883
3022
|
if (!openTagMatch) {
|
|
2884
3023
|
return null;
|
|
2885
3024
|
}
|
|
@@ -2928,8 +3067,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
2928
3067
|
if (node.type !== "code-line") {
|
|
2929
3068
|
anomalies.push(`type-mismatch:${node.id}:${node.type}`);
|
|
2930
3069
|
nodes.delete(node.id);
|
|
2931
|
-
|
|
2932
|
-
childrenSnapshotCache.delete(node.id);
|
|
3070
|
+
deleteSnapshotCacheEntry(nodes, node.id);
|
|
2933
3071
|
touched.add(node.id);
|
|
2934
3072
|
continue;
|
|
2935
3073
|
}
|
|
@@ -2973,16 +3111,14 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
2973
3111
|
const existing = nodes.get(desiredId);
|
|
2974
3112
|
if (existing && existing !== node) {
|
|
2975
3113
|
nodes.delete(desiredId);
|
|
2976
|
-
|
|
2977
|
-
childrenSnapshotCache.delete(desiredId);
|
|
3114
|
+
deleteSnapshotCacheEntry(nodes, desiredId);
|
|
2978
3115
|
touched.add(desiredId);
|
|
2979
3116
|
mutated = true;
|
|
2980
3117
|
}
|
|
2981
3118
|
if (node.id !== desiredId) {
|
|
2982
3119
|
const previousId = node.id;
|
|
2983
3120
|
nodes.delete(node.id);
|
|
2984
|
-
|
|
2985
|
-
childrenSnapshotCache.delete(previousId);
|
|
3121
|
+
deleteSnapshotCacheEntry(nodes, previousId);
|
|
2986
3122
|
node.id = desiredId;
|
|
2987
3123
|
nodes.set(desiredId, node);
|
|
2988
3124
|
touched.add(desiredId);
|
|
@@ -3013,8 +3149,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
3013
3149
|
for (const [id, record] of nodes) {
|
|
3014
3150
|
if (record.parentId === parent.id && record.type === "code-line" && !allowed.has(id)) {
|
|
3015
3151
|
nodes.delete(id);
|
|
3016
|
-
|
|
3017
|
-
childrenSnapshotCache.delete(id);
|
|
3152
|
+
deleteSnapshotCacheEntry(nodes, id);
|
|
3018
3153
|
touched.add(id);
|
|
3019
3154
|
mutated = true;
|
|
3020
3155
|
}
|
|
@@ -3098,8 +3233,10 @@ function normalizeCodeLineProps(incoming, previous) {
|
|
|
3098
3233
|
}
|
|
3099
3234
|
function createCodeLineProps(index, text, highlight, tokens) {
|
|
3100
3235
|
const safeText = typeof text === "string" ? text : "";
|
|
3101
|
-
const
|
|
3102
|
-
const
|
|
3236
|
+
const hasIncomingHighlight = typeof highlight === "string" && highlight.trim().length > 0;
|
|
3237
|
+
const sanitized = hasIncomingHighlight ? sanitizeLineInnerHtml(highlight, safeText) : null;
|
|
3238
|
+
const hasRenderableTokens = tokens !== void 0 && tokens !== null;
|
|
3239
|
+
const html = sanitized && sanitized.length >= safeText.length ? sanitized : hasRenderableTokens ? null : escapeHtml2(safeText);
|
|
3103
3240
|
const props = {
|
|
3104
3241
|
index,
|
|
3105
3242
|
text: safeText,
|
|
@@ -3210,6 +3347,18 @@ function validateCodeBlockChildren(nodes, parent) {
|
|
|
3210
3347
|
issues.push({ kind: "dangling-node", id });
|
|
3211
3348
|
}
|
|
3212
3349
|
}
|
|
3350
|
+
if (parent.block?.isFinalized) {
|
|
3351
|
+
const snapshot = createBlockSnapshot(cloneBlock(parent.block));
|
|
3352
|
+
const expectedChildren = ensureArray(snapshot.children).filter((child) => child.type === "code-line");
|
|
3353
|
+
if (expectedChildren.length !== parent.children.length) {
|
|
3354
|
+
issues.push({
|
|
3355
|
+
kind: "line-count-mismatch",
|
|
3356
|
+
id: parent.id,
|
|
3357
|
+
expected: expectedChildren.length,
|
|
3358
|
+
actual: parent.children.length
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3213
3362
|
return { ok: issues.length === 0, issues };
|
|
3214
3363
|
}
|
|
3215
3364
|
function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
|
|
@@ -3226,7 +3375,7 @@ function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
|
|
|
3226
3375
|
}
|
|
3227
3376
|
const snapshot = createBlockSnapshot(cloneBlock(parent.block));
|
|
3228
3377
|
removeSubtree(nodes, parent.id, touched);
|
|
3229
|
-
const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot);
|
|
3378
|
+
const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot, touched, parent.blockEpoch);
|
|
3230
3379
|
if (!inserted) {
|
|
3231
3380
|
return false;
|
|
3232
3381
|
}
|
|
@@ -3249,7 +3398,55 @@ function arraysEqual(a, b) {
|
|
|
3249
3398
|
}
|
|
3250
3399
|
return true;
|
|
3251
3400
|
}
|
|
3252
|
-
function
|
|
3401
|
+
function expectedCodeLineId(parentId, index) {
|
|
3402
|
+
return `${parentId}::line:${index}`;
|
|
3403
|
+
}
|
|
3404
|
+
function collectStoreInvariantViolations(nodes) {
|
|
3405
|
+
const violations = [];
|
|
3406
|
+
for (const [id, node] of nodes) {
|
|
3407
|
+
if (node.type === "__root__") continue;
|
|
3408
|
+
if (node.children.length > 1) {
|
|
3409
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3410
|
+
for (const childId of node.children) {
|
|
3411
|
+
if (seen.has(childId)) {
|
|
3412
|
+
violations.push(`duplicate child id:${id}:${childId}`);
|
|
3413
|
+
} else {
|
|
3414
|
+
seen.add(childId);
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
if (node.type === "list") {
|
|
3419
|
+
const parent = node.parentId ? nodes.get(node.parentId) : void 0;
|
|
3420
|
+
if (parent?.type === "list-item" && node.children.length === 0) {
|
|
3421
|
+
violations.push(`empty nested list:${id}`);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
if (node.type === "code") {
|
|
3425
|
+
for (let index = 0; index < node.children.length; index += 1) {
|
|
3426
|
+
const childId = node.children[index];
|
|
3427
|
+
const child = nodes.get(childId);
|
|
3428
|
+
if (!child) {
|
|
3429
|
+
violations.push(`missing code-line node:${node.id}:${childId}`);
|
|
3430
|
+
continue;
|
|
3431
|
+
}
|
|
3432
|
+
if (child.type !== "code-line") {
|
|
3433
|
+
violations.push(`non-code child in code block:${node.id}:${child.id}:${child.type}`);
|
|
3434
|
+
continue;
|
|
3435
|
+
}
|
|
3436
|
+
const expectedId = expectedCodeLineId(node.id, index);
|
|
3437
|
+
if (child.id !== expectedId) {
|
|
3438
|
+
violations.push(`code-line id mismatch:${node.id}:${child.id}:${expectedId}`);
|
|
3439
|
+
}
|
|
3440
|
+
const indexProp = typeof child.props?.index === "number" ? child.props.index : null;
|
|
3441
|
+
if (indexProp !== index) {
|
|
3442
|
+
violations.push(`code-line index mismatch:${node.id}:${child.id}:${indexProp}:${index}`);
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
return violations;
|
|
3448
|
+
}
|
|
3449
|
+
function buildNodeFromSnapshot(map, snapshot, parentId, blockEpoch) {
|
|
3253
3450
|
const record = {
|
|
3254
3451
|
id: snapshot.id,
|
|
3255
3452
|
type: snapshot.type,
|
|
@@ -3259,6 +3456,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
|
|
|
3259
3456
|
meta: snapshot.meta ? { ...snapshot.meta } : void 0,
|
|
3260
3457
|
range: snapshot.range ? { ...snapshot.range } : void 0,
|
|
3261
3458
|
version: 0,
|
|
3459
|
+
blockEpoch,
|
|
3262
3460
|
block: snapshotToBlock(snapshot) ?? void 0
|
|
3263
3461
|
};
|
|
3264
3462
|
if (record.type === "code") {
|
|
@@ -3267,7 +3465,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
|
|
|
3267
3465
|
map.set(record.id, record);
|
|
3268
3466
|
const childSnapshots = ensureArray(snapshot.children);
|
|
3269
3467
|
for (const child of childSnapshots) {
|
|
3270
|
-
const childRecord = buildNodeFromSnapshot(map, child, record.id);
|
|
3468
|
+
const childRecord = buildNodeFromSnapshot(map, child, record.id, blockEpoch);
|
|
3271
3469
|
record.children.push(childRecord.id);
|
|
3272
3470
|
}
|
|
3273
3471
|
return record;
|
|
@@ -3295,8 +3493,7 @@ function removeSubtree(map, id, touched) {
|
|
|
3295
3493
|
}
|
|
3296
3494
|
}
|
|
3297
3495
|
map.delete(node.id);
|
|
3298
|
-
|
|
3299
|
-
childrenSnapshotCache.delete(node.id);
|
|
3496
|
+
deleteSnapshotCacheEntry(map, node.id);
|
|
3300
3497
|
touched.add(node.id);
|
|
3301
3498
|
for (const childId of node.children) {
|
|
3302
3499
|
const child = map.get(childId);
|
|
@@ -3328,13 +3525,13 @@ function resolveParent(map, path) {
|
|
|
3328
3525
|
}
|
|
3329
3526
|
return map.get(path.blockId);
|
|
3330
3527
|
}
|
|
3331
|
-
function insertSnapshotAt(map, parent, index, snapshot, touched) {
|
|
3528
|
+
function insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
|
|
3332
3529
|
if (!parent) return null;
|
|
3333
3530
|
const existingIndex = parent.children.indexOf(snapshot.id);
|
|
3334
3531
|
if (existingIndex !== -1) {
|
|
3335
3532
|
removeSubtree(map, parent.children[existingIndex], touched ?? /* @__PURE__ */ new Set());
|
|
3336
3533
|
}
|
|
3337
|
-
const record = buildNodeFromSnapshot(map, snapshot, parent.id);
|
|
3534
|
+
const record = buildNodeFromSnapshot(map, snapshot, parent.id, blockEpoch ?? readSnapshotBlockEpoch(snapshot) ?? parent.blockEpoch);
|
|
3338
3535
|
const insertedIds = [];
|
|
3339
3536
|
const queue = [record];
|
|
3340
3537
|
while (queue.length > 0) {
|
|
@@ -3352,13 +3549,13 @@ function insertSnapshotAt(map, parent, index, snapshot, touched) {
|
|
|
3352
3549
|
ensureUniqueChildren(parent, touched);
|
|
3353
3550
|
return { record, insertedIds };
|
|
3354
3551
|
}
|
|
3355
|
-
function replaceSnapshotAt(map, parent, index, snapshot, touched) {
|
|
3552
|
+
function replaceSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
|
|
3356
3553
|
if (!parent) return;
|
|
3357
3554
|
if (index < 0 || index >= parent.children.length) return;
|
|
3358
3555
|
const targetId = parent.children[index];
|
|
3359
3556
|
parent.children.splice(index, 1);
|
|
3360
3557
|
removeSubtree(map, targetId, touched);
|
|
3361
|
-
const inserted = insertSnapshotAt(map, parent, index, snapshot, touched);
|
|
3558
|
+
const inserted = insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch);
|
|
3362
3559
|
if (inserted) {
|
|
3363
3560
|
for (const id of inserted.insertedIds) {
|
|
3364
3561
|
touched.add(id);
|
|
@@ -3378,6 +3575,24 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3378
3575
|
const listeners = /* @__PURE__ */ new Set();
|
|
3379
3576
|
let notifyScheduled = false;
|
|
3380
3577
|
let lastCoalescingMetrics = null;
|
|
3578
|
+
let invariantViolations = [];
|
|
3579
|
+
let transientDiagnostics = [];
|
|
3580
|
+
let nextBlockEpoch = 1;
|
|
3581
|
+
const debugCounters = {
|
|
3582
|
+
appendLineGuardRejected: 0,
|
|
3583
|
+
staleEpochRejected: 0,
|
|
3584
|
+
storeInvariantViolationCount: 0
|
|
3585
|
+
};
|
|
3586
|
+
const allocateBlockEpoch = (preferred) => {
|
|
3587
|
+
const resolved = readNumericEpoch(preferred);
|
|
3588
|
+
if (resolved !== void 0) {
|
|
3589
|
+
nextBlockEpoch = Math.max(nextBlockEpoch, resolved + 1);
|
|
3590
|
+
return resolved;
|
|
3591
|
+
}
|
|
3592
|
+
const epoch = nextBlockEpoch;
|
|
3593
|
+
nextBlockEpoch += 1;
|
|
3594
|
+
return epoch;
|
|
3595
|
+
};
|
|
3381
3596
|
const getRaf = () => {
|
|
3382
3597
|
if (typeof globalThis !== "undefined" && typeof globalThis.requestAnimationFrame === "function") {
|
|
3383
3598
|
return globalThis.requestAnimationFrame;
|
|
@@ -3409,9 +3624,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3409
3624
|
}
|
|
3410
3625
|
}
|
|
3411
3626
|
}
|
|
3627
|
+
function appendDiagnostic(message) {
|
|
3628
|
+
transientDiagnostics = [...transientDiagnostics, message];
|
|
3629
|
+
invariantViolations = [...invariantViolations, message];
|
|
3630
|
+
}
|
|
3412
3631
|
function rebuildFromBlocks(blocks) {
|
|
3413
|
-
|
|
3414
|
-
childrenSnapshotCache.clear();
|
|
3632
|
+
clearSnapshotCaches(nodes);
|
|
3415
3633
|
nodes = /* @__PURE__ */ new Map();
|
|
3416
3634
|
const root = createRootRecord();
|
|
3417
3635
|
nodes.set(root.id, root);
|
|
@@ -3420,9 +3638,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3420
3638
|
for (let i = 0; i < blocks.length; i++) {
|
|
3421
3639
|
const block = cloneBlock(blocks[i]);
|
|
3422
3640
|
const snapshot = createBlockSnapshot(block);
|
|
3423
|
-
insertSnapshotAt(nodes, root, root.children.length, snapshot);
|
|
3641
|
+
insertSnapshotAt(nodes, root, root.children.length, snapshot, void 0, allocateBlockEpoch(readSnapshotBlockEpoch(snapshot)));
|
|
3424
3642
|
}
|
|
3425
3643
|
normalizeAllListDepths(nodes, /* @__PURE__ */ new Set(), PATCH_ROOT_ID);
|
|
3644
|
+
transientDiagnostics = [];
|
|
3645
|
+
invariantViolations = collectStoreInvariantViolations(nodes);
|
|
3646
|
+
debugCounters.storeInvariantViolationCount += invariantViolations.length;
|
|
3426
3647
|
version++;
|
|
3427
3648
|
blocksDirty = true;
|
|
3428
3649
|
scheduleNotify();
|
|
@@ -3487,11 +3708,57 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3487
3708
|
listDepthDirty = true;
|
|
3488
3709
|
}
|
|
3489
3710
|
};
|
|
3490
|
-
const
|
|
3711
|
+
const semanticTxEpochs = /* @__PURE__ */ new Map();
|
|
3712
|
+
const resolveBlockEpochTarget = (patch, at) => {
|
|
3713
|
+
if ((patch.op === "insertChild" || patch.op === "replaceChild") && at.blockId === PATCH_ROOT_ID) {
|
|
3714
|
+
const nodeId = patch.node?.id;
|
|
3715
|
+
return nodeId ? nodes.get(nodeId) : void 0;
|
|
3716
|
+
}
|
|
3717
|
+
return nodes.get(at.blockId) ?? (at.nodeId ? nodes.get(at.nodeId) : void 0);
|
|
3718
|
+
};
|
|
3719
|
+
const rejectStaleEpochPatch = (patch, at) => {
|
|
3720
|
+
const expectedBlockEpoch = readPatchBlockEpoch(patch);
|
|
3721
|
+
const patchTx = readPatchTx(patch);
|
|
3722
|
+
if (expectedBlockEpoch === void 0) {
|
|
3723
|
+
return false;
|
|
3724
|
+
}
|
|
3725
|
+
if (getPatchKind3(patch) !== "semantic" || !at) {
|
|
3726
|
+
return false;
|
|
3727
|
+
}
|
|
3728
|
+
const target = resolveBlockEpochTarget(patch, at);
|
|
3729
|
+
if (!target || target.blockEpoch === expectedBlockEpoch) {
|
|
3730
|
+
return false;
|
|
3731
|
+
}
|
|
3732
|
+
const txEpoch = at.blockId && patchTx !== void 0 ? semanticTxEpochs.get(at.blockId) : void 0;
|
|
3733
|
+
if (txEpoch && txEpoch.tx === patchTx && txEpoch.blockEpoch === target.blockEpoch) {
|
|
3734
|
+
return false;
|
|
3735
|
+
}
|
|
3736
|
+
debugCounters.staleEpochRejected += 1;
|
|
3737
|
+
appendDiagnostic(
|
|
3738
|
+
`stale epoch rejected:${patch.op}:${target.id}:expected=${expectedBlockEpoch}:actual=${target.blockEpoch}`
|
|
3739
|
+
);
|
|
3740
|
+
return true;
|
|
3741
|
+
};
|
|
3742
|
+
const advanceBlockEpoch = (blockId, touchedSet, preferredEpoch, tx) => {
|
|
3743
|
+
if (!blockId || blockId === PATCH_ROOT_ID) {
|
|
3744
|
+
return null;
|
|
3745
|
+
}
|
|
3746
|
+
const blockRoot = nodes.get(blockId);
|
|
3747
|
+
if (!blockRoot) {
|
|
3748
|
+
return null;
|
|
3749
|
+
}
|
|
3750
|
+
const nextEpoch = allocateBlockEpoch(preferredEpoch);
|
|
3751
|
+
assignSubtreeBlockEpoch(nodes, blockRoot.id, nextEpoch, touchedSet);
|
|
3752
|
+
if (tx !== void 0) {
|
|
3753
|
+
semanticTxEpochs.set(blockRoot.id, { tx, blockEpoch: nextEpoch });
|
|
3754
|
+
}
|
|
3755
|
+
return nextEpoch;
|
|
3756
|
+
};
|
|
3757
|
+
const applyPropsUpdate = (at, props, preferredEpoch, tx, semanticUpdate = false) => {
|
|
3491
3758
|
const perfStartLocal = perfEnabled ? getPerfNow() : 0;
|
|
3492
3759
|
const targetId = at.nodeId ?? at.blockId;
|
|
3493
3760
|
if (!targetId) return;
|
|
3494
|
-
|
|
3761
|
+
let target = nodes.get(targetId);
|
|
3495
3762
|
if (!target) return;
|
|
3496
3763
|
const parentType = target.parentId ? nodes.get(target.parentId)?.type : void 0;
|
|
3497
3764
|
if (debugListPatches && (isListRelated(target.type) || isListRelated(parentType))) {
|
|
@@ -3518,6 +3785,9 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3518
3785
|
propsChanged = true;
|
|
3519
3786
|
}
|
|
3520
3787
|
if (propsChanged) {
|
|
3788
|
+
if (semanticUpdate) {
|
|
3789
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
3790
|
+
}
|
|
3521
3791
|
target.version++;
|
|
3522
3792
|
touched.add(target.id);
|
|
3523
3793
|
const parent = target.parentId ? nodes.get(target.parentId) : void 0;
|
|
@@ -3581,16 +3851,56 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3581
3851
|
}
|
|
3582
3852
|
}
|
|
3583
3853
|
let blockChanged = false;
|
|
3854
|
+
let typeChanged = false;
|
|
3855
|
+
let rebuiltFromFinalizedBlock = false;
|
|
3856
|
+
let blockEpochAdvanced = false;
|
|
3584
3857
|
const incomingBlock = props?.block;
|
|
3585
3858
|
if (incomingBlock && typeof incomingBlock === "object") {
|
|
3586
3859
|
target.block = cloneBlock(incomingBlock);
|
|
3860
|
+
const previousType = target.type;
|
|
3861
|
+
const incomingType = typeof target.block.type === "string" ? target.block.type : target.type;
|
|
3862
|
+
if (incomingType !== target.type) {
|
|
3863
|
+
target.type = incomingType;
|
|
3864
|
+
typeChanged = true;
|
|
3865
|
+
if (isListRelated(previousType) || isListRelated(target.type)) {
|
|
3866
|
+
markListDepthDirty(target.id);
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3587
3869
|
if (target.type === "code") {
|
|
3588
3870
|
applyCodeBlockMetadata(target);
|
|
3589
3871
|
}
|
|
3872
|
+
if (semanticUpdate) {
|
|
3873
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
3874
|
+
blockEpochAdvanced = true;
|
|
3875
|
+
}
|
|
3876
|
+
const shouldRebuildFinalizedTree = !at.nodeId && target.id === at.blockId && Boolean(target.block?.isFinalized) && target.type !== "code" && Boolean(target.parentId);
|
|
3877
|
+
if (shouldRebuildFinalizedTree) {
|
|
3878
|
+
const parent = target.parentId ? nodes.get(target.parentId) : void 0;
|
|
3879
|
+
const index = parent ? parent.children.indexOf(target.id) : -1;
|
|
3880
|
+
if (parent && index !== -1) {
|
|
3881
|
+
const snapshot = createBlockSnapshot(cloneBlock(target.block));
|
|
3882
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
|
|
3883
|
+
mutatedBlocks = true;
|
|
3884
|
+
blockChanged = true;
|
|
3885
|
+
rebuiltFromFinalizedBlock = true;
|
|
3886
|
+
const rebuilt = nodes.get(target.id);
|
|
3887
|
+
if (isListRelated(previousType) || isListRelated(rebuilt?.type)) {
|
|
3888
|
+
markListDepthDirty(target.id);
|
|
3889
|
+
}
|
|
3890
|
+
target = rebuilt ?? target;
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3590
3893
|
mutatedBlocks = true;
|
|
3591
3894
|
blockChanged = true;
|
|
3592
3895
|
}
|
|
3593
|
-
if (
|
|
3896
|
+
if (rebuiltFromFinalizedBlock) {
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
if (propsChanged || blockChanged || typeChanged) {
|
|
3900
|
+
if (semanticUpdate && !blockEpochAdvanced) {
|
|
3901
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
3902
|
+
blockEpochAdvanced = true;
|
|
3903
|
+
}
|
|
3594
3904
|
target.version++;
|
|
3595
3905
|
touched.add(target.id);
|
|
3596
3906
|
let parentForNormalization;
|
|
@@ -3633,13 +3943,22 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3633
3943
|
const applyPatch = (patch) => {
|
|
3634
3944
|
switch (patch.op) {
|
|
3635
3945
|
case "insertChild": {
|
|
3946
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3636
3947
|
const parent = resolveParent(nodes, patch.at);
|
|
3637
|
-
const inserted = insertSnapshotAt(
|
|
3948
|
+
const inserted = insertSnapshotAt(
|
|
3949
|
+
nodes,
|
|
3950
|
+
parent,
|
|
3951
|
+
patch.index,
|
|
3952
|
+
patch.node,
|
|
3953
|
+
touched,
|
|
3954
|
+
allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
|
|
3955
|
+
);
|
|
3638
3956
|
if (inserted) {
|
|
3639
3957
|
for (const id of inserted.insertedIds) {
|
|
3640
3958
|
touched.add(id);
|
|
3641
3959
|
}
|
|
3642
3960
|
mutatedBlocks = true;
|
|
3961
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
3643
3962
|
if (isListRelated(inserted.record.type) || isListRelated(parent?.type)) {
|
|
3644
3963
|
markListDepthDirty(inserted.record.id);
|
|
3645
3964
|
if (parent) {
|
|
@@ -3659,6 +3978,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3659
3978
|
break;
|
|
3660
3979
|
}
|
|
3661
3980
|
case "deleteChild": {
|
|
3981
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3662
3982
|
const parent = resolveParent(nodes, patch.at);
|
|
3663
3983
|
if (!parent) break;
|
|
3664
3984
|
if (patch.index < 0 || patch.index >= parent.children.length) break;
|
|
@@ -3670,6 +3990,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3670
3990
|
touched.add(parent.id);
|
|
3671
3991
|
ensureUniqueChildren(parent, touched);
|
|
3672
3992
|
mutatedBlocks = true;
|
|
3993
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
3673
3994
|
if (isListRelated(parent.type) || isListRelated(removedNode?.type)) {
|
|
3674
3995
|
markListDepthDirty(parent.id);
|
|
3675
3996
|
}
|
|
@@ -3686,11 +4007,20 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3686
4007
|
break;
|
|
3687
4008
|
}
|
|
3688
4009
|
case "replaceChild": {
|
|
4010
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3689
4011
|
const parent = resolveParent(nodes, patch.at);
|
|
3690
4012
|
if (!parent) break;
|
|
3691
|
-
replaceSnapshotAt(
|
|
4013
|
+
replaceSnapshotAt(
|
|
4014
|
+
nodes,
|
|
4015
|
+
parent,
|
|
4016
|
+
patch.index,
|
|
4017
|
+
patch.node,
|
|
4018
|
+
touched,
|
|
4019
|
+
allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
|
|
4020
|
+
);
|
|
3692
4021
|
touched.add(parent.id);
|
|
3693
4022
|
mutatedBlocks = true;
|
|
4023
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
3694
4024
|
if (isListRelated(parent.type)) {
|
|
3695
4025
|
markListDepthDirty(parent.id);
|
|
3696
4026
|
}
|
|
@@ -3704,18 +4034,33 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3704
4034
|
break;
|
|
3705
4035
|
}
|
|
3706
4036
|
case "setProps": {
|
|
3707
|
-
|
|
4037
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
4038
|
+
applyPropsUpdate(patch.at, patch.props, readPatchParseEpoch(patch), readPatchTx(patch), getPatchKind3(patch) === "semantic");
|
|
3708
4039
|
break;
|
|
3709
4040
|
}
|
|
3710
4041
|
case "setPropsBatch": {
|
|
3711
4042
|
const entries2 = Array.isArray(patch.entries) ? patch.entries : [];
|
|
3712
4043
|
for (const entry of entries2) {
|
|
3713
4044
|
if (!entry || !entry.at) continue;
|
|
3714
|
-
|
|
4045
|
+
const entryPatch = {
|
|
4046
|
+
op: "setProps",
|
|
4047
|
+
at: entry.at,
|
|
4048
|
+
props: entry.props,
|
|
4049
|
+
meta: entry.meta ?? patch.meta
|
|
4050
|
+
};
|
|
4051
|
+
if (rejectStaleEpochPatch(entryPatch, entry.at)) continue;
|
|
4052
|
+
applyPropsUpdate(
|
|
4053
|
+
entry.at,
|
|
4054
|
+
entry.props,
|
|
4055
|
+
readPatchParseEpoch(entryPatch),
|
|
4056
|
+
readPatchTx(entryPatch),
|
|
4057
|
+
getPatchKind3(entryPatch) === "semantic"
|
|
4058
|
+
);
|
|
3715
4059
|
}
|
|
3716
4060
|
break;
|
|
3717
4061
|
}
|
|
3718
4062
|
case "finalize": {
|
|
4063
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3719
4064
|
const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
|
|
3720
4065
|
if (!target || !target.block) break;
|
|
3721
4066
|
if (!target.block.isFinalized) {
|
|
@@ -3724,6 +4069,17 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3724
4069
|
target.version++;
|
|
3725
4070
|
touched.add(target.id);
|
|
3726
4071
|
mutatedBlocks = true;
|
|
4072
|
+
if (!patch.at.nodeId && target.id === patch.at.blockId && target.type !== "code" && target.parentId) {
|
|
4073
|
+
const parent = nodes.get(target.parentId);
|
|
4074
|
+
const index = parent ? parent.children.indexOf(target.id) : -1;
|
|
4075
|
+
if (parent && index !== -1) {
|
|
4076
|
+
const snapshot = createBlockSnapshot(cloneBlock(target.block));
|
|
4077
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
|
|
4078
|
+
if (isListRelated(target.type) || isListRelated(snapshot.type)) {
|
|
4079
|
+
markListDepthDirty(target.id);
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
3727
4083
|
}
|
|
3728
4084
|
if (isListRelated(target.type)) {
|
|
3729
4085
|
markListDepthDirty(target.id);
|
|
@@ -3737,6 +4093,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3737
4093
|
break;
|
|
3738
4094
|
}
|
|
3739
4095
|
case "reorder": {
|
|
4096
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3740
4097
|
const parent = resolveParent(nodes, patch.at);
|
|
3741
4098
|
if (!parent) break;
|
|
3742
4099
|
const length = parent.children.length;
|
|
@@ -3751,6 +4108,9 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3751
4108
|
parent.children.splice(to, 0, ...moved);
|
|
3752
4109
|
parent.version++;
|
|
3753
4110
|
touched.add(parent.id);
|
|
4111
|
+
if (getPatchKind3(patch) === "semantic") {
|
|
4112
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
4113
|
+
}
|
|
3754
4114
|
if (isListRelated(parent.type)) {
|
|
3755
4115
|
markListDepthDirty(parent.id);
|
|
3756
4116
|
}
|
|
@@ -3766,15 +4126,31 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3766
4126
|
break;
|
|
3767
4127
|
}
|
|
3768
4128
|
case "appendLines": {
|
|
4129
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3769
4130
|
const parent = resolveParent(nodes, patch.at);
|
|
3770
4131
|
if (!parent) break;
|
|
3771
|
-
|
|
4132
|
+
if (parent.type !== "code") {
|
|
4133
|
+
debugCounters.appendLineGuardRejected += 1;
|
|
4134
|
+
appendDiagnostic(`appendLines target not code:${parent.id}:${parent.type}`);
|
|
4135
|
+
break;
|
|
4136
|
+
}
|
|
4137
|
+
const requestedStartIndex = Math.max(0, patch.startIndex);
|
|
4138
|
+
const startIndex = Math.max(0, Math.min(requestedStartIndex, parent.children.length));
|
|
3772
4139
|
const lineCount = patch.lines?.length ?? 0;
|
|
3773
4140
|
if (lineCount === 0) break;
|
|
4141
|
+
const prefixMatches = parent.children.every((childId, index) => childId === expectedCodeLineId(parent.id, index));
|
|
4142
|
+
const isStrictTailAppend = requestedStartIndex === parent.children.length;
|
|
4143
|
+
if (!isStrictTailAppend || !prefixMatches) {
|
|
4144
|
+
debugCounters.appendLineGuardRejected += 1;
|
|
4145
|
+
appendDiagnostic(
|
|
4146
|
+
`appendLines guard rejected:${parent.id}:requestedStart=${requestedStartIndex}:childCount=${parent.children.length}:prefixMatches=${prefixMatches}`
|
|
4147
|
+
);
|
|
4148
|
+
break;
|
|
4149
|
+
}
|
|
3774
4150
|
const originalChildren = parent.children.slice();
|
|
3775
4151
|
const insertedIds = [];
|
|
3776
4152
|
const insertedSet = /* @__PURE__ */ new Set();
|
|
3777
|
-
const isTrailingAppend =
|
|
4153
|
+
const isTrailingAppend = true;
|
|
3778
4154
|
let parentChildrenChanged = false;
|
|
3779
4155
|
let codeLinesMutated = false;
|
|
3780
4156
|
for (let offset = 0; offset < lineCount; offset++) {
|
|
@@ -3816,7 +4192,8 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3816
4192
|
parentId: parent.id,
|
|
3817
4193
|
children: [],
|
|
3818
4194
|
props: nextLineProps,
|
|
3819
|
-
version: 0
|
|
4195
|
+
version: 0,
|
|
4196
|
+
blockEpoch: parent.blockEpoch
|
|
3820
4197
|
};
|
|
3821
4198
|
nodes.set(lineId, record);
|
|
3822
4199
|
touched.add(lineId);
|
|
@@ -3866,6 +4243,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3866
4243
|
break;
|
|
3867
4244
|
}
|
|
3868
4245
|
case "setHTML": {
|
|
4246
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
3869
4247
|
const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
|
|
3870
4248
|
if (!target) break;
|
|
3871
4249
|
const sanitized = patch.sanitized ? patch.html : sanitizeHTML2(patch.html);
|
|
@@ -3908,6 +4286,30 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3908
4286
|
applyPatch(patch);
|
|
3909
4287
|
}
|
|
3910
4288
|
}
|
|
4289
|
+
const finalizedTableRoots = /* @__PURE__ */ new Set();
|
|
4290
|
+
for (const touchedId of touched) {
|
|
4291
|
+
let cursor = nodes.get(touchedId);
|
|
4292
|
+
while (cursor && cursor.id !== PATCH_ROOT_ID) {
|
|
4293
|
+
if (cursor.type === "table" && cursor.block?.isFinalized && cursor.parentId) {
|
|
4294
|
+
finalizedTableRoots.add(cursor.id);
|
|
4295
|
+
break;
|
|
4296
|
+
}
|
|
4297
|
+
cursor = cursor.parentId ? nodes.get(cursor.parentId) : void 0;
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
if (finalizedTableRoots.size > 0) {
|
|
4301
|
+
for (const tableId of finalizedTableRoots) {
|
|
4302
|
+
const tableNode = nodes.get(tableId);
|
|
4303
|
+
if (!tableNode || !tableNode.block || !tableNode.parentId) continue;
|
|
4304
|
+
const parent = nodes.get(tableNode.parentId);
|
|
4305
|
+
if (!parent) continue;
|
|
4306
|
+
const index = parent.children.indexOf(tableNode.id);
|
|
4307
|
+
if (index === -1) continue;
|
|
4308
|
+
const snapshot = createBlockSnapshot(cloneBlock(tableNode.block));
|
|
4309
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, tableNode.blockEpoch);
|
|
4310
|
+
mutatedBlocks = true;
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
3911
4313
|
if (mutatedBlocks) {
|
|
3912
4314
|
blocksDirty = true;
|
|
3913
4315
|
}
|
|
@@ -3928,6 +4330,8 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3928
4330
|
normalizeListDepthsForIds(nodes, touched, listDepthDirtyNodes, PATCH_ROOT_ID);
|
|
3929
4331
|
}
|
|
3930
4332
|
}
|
|
4333
|
+
invariantViolations = [...collectStoreInvariantViolations(nodes), ...transientDiagnostics];
|
|
4334
|
+
debugCounters.storeInvariantViolationCount += invariantViolations.length;
|
|
3931
4335
|
if (touched.size > 0 || mutatedBlocks) {
|
|
3932
4336
|
version++;
|
|
3933
4337
|
scheduleNotify();
|
|
@@ -3976,6 +4380,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3976
4380
|
if (!record) {
|
|
3977
4381
|
return EMPTY_NODE_SNAPSHOT;
|
|
3978
4382
|
}
|
|
4383
|
+
const nodeSnapshotCache = getNodeSnapshotCacheForMap(nodes);
|
|
3979
4384
|
const cached = nodeSnapshotCache.get(id);
|
|
3980
4385
|
if (cached && cached.version === record.version && cached.node === record) {
|
|
3981
4386
|
return cached;
|
|
@@ -3995,6 +4400,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
3995
4400
|
if (!record) {
|
|
3996
4401
|
return EMPTY_CHILDREN_SNAPSHOT;
|
|
3997
4402
|
}
|
|
4403
|
+
const childrenSnapshotCache = getChildrenSnapshotCacheForMap(nodes);
|
|
3998
4404
|
const cached = childrenSnapshotCache.get(id);
|
|
3999
4405
|
if (cached && cached.version === record.version) {
|
|
4000
4406
|
return cached;
|
|
@@ -4004,6 +4410,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
4004
4410
|
pruneCache(childrenSnapshotCache, CHILDREN_SNAPSHOT_CACHE_LIMIT, CHILDREN_SNAPSHOT_CACHE_BUFFER);
|
|
4005
4411
|
return snapshot;
|
|
4006
4412
|
},
|
|
4413
|
+
getInvariantViolations() {
|
|
4414
|
+
return invariantViolations.slice();
|
|
4415
|
+
},
|
|
4416
|
+
getDebugCounters() {
|
|
4417
|
+
return { ...debugCounters };
|
|
4418
|
+
},
|
|
4007
4419
|
getLastCoalescingMetrics() {
|
|
4008
4420
|
return lastCoalescingMetrics;
|
|
4009
4421
|
},
|
|
@@ -4046,7 +4458,7 @@ function useRendererChildren(store, id) {
|
|
|
4046
4458
|
}
|
|
4047
4459
|
|
|
4048
4460
|
// src/renderer/node-views.tsx
|
|
4049
|
-
import { stripCodeFence } from "@stream-mdx/core";
|
|
4461
|
+
import { getDefaultCodeWrapperAttributes as getDefaultCodeWrapperAttributes2, stripCodeFence } from "@stream-mdx/core";
|
|
4050
4462
|
import React7 from "react";
|
|
4051
4463
|
|
|
4052
4464
|
// src/renderer/deferred-render-context.tsx
|
|
@@ -4488,8 +4900,10 @@ var ListBlockView = React7.memo(
|
|
|
4488
4900
|
isFinalized: block?.isFinalized ?? false
|
|
4489
4901
|
});
|
|
4490
4902
|
}, [listDebugEnabled, blockId, depth, ordered, childIds, listItemIds, store, block?.isFinalized]);
|
|
4491
|
-
const listStyle = React7.useMemo(() => {
|
|
4492
|
-
if (!ordered || listItemIds.length === 0)
|
|
4903
|
+
const { listStyle, markerDigits } = React7.useMemo(() => {
|
|
4904
|
+
if (!ordered || listItemIds.length === 0) {
|
|
4905
|
+
return { listStyle: void 0, markerDigits: 1 };
|
|
4906
|
+
}
|
|
4493
4907
|
let maxDigits = 1;
|
|
4494
4908
|
listItemIds.forEach((childId, index) => {
|
|
4495
4909
|
const childNode = store.getNode(childId);
|
|
@@ -4501,9 +4915,14 @@ var ListBlockView = React7.memo(
|
|
|
4501
4915
|
const digits = digitMatch ? digitMatch.length : text.length;
|
|
4502
4916
|
if (digits > maxDigits) maxDigits = digits;
|
|
4503
4917
|
});
|
|
4504
|
-
const baseIndent = depth === 0 ? "
|
|
4918
|
+
const baseIndent = depth === 0 ? "2rem" : depth === 1 ? "1.75rem" : "1.5rem";
|
|
4919
|
+
const extraDigits = Math.max(0, maxDigits - 1);
|
|
4505
4920
|
return {
|
|
4506
|
-
|
|
4921
|
+
markerDigits: maxDigits,
|
|
4922
|
+
listStyle: {
|
|
4923
|
+
["--list-indent"]: extraDigits > 0 ? `calc(${baseIndent} + ${extraDigits}ch)` : baseIndent,
|
|
4924
|
+
["--list-marker-digits"]: String(maxDigits)
|
|
4925
|
+
}
|
|
4507
4926
|
};
|
|
4508
4927
|
}, [ordered, listItemIds, store, depth]);
|
|
4509
4928
|
if (listItemIds.length === 0) return null;
|
|
@@ -4512,6 +4931,7 @@ var ListBlockView = React7.memo(
|
|
|
4512
4931
|
{
|
|
4513
4932
|
className: `markdown-list ${ordered ? "ordered" : "unordered"}`,
|
|
4514
4933
|
"data-list-depth": depth,
|
|
4934
|
+
"data-marker-digits": ordered ? markerDigits : void 0,
|
|
4515
4935
|
"data-list-id": listDebugEnabled ? blockId : void 0,
|
|
4516
4936
|
style: listStyle,
|
|
4517
4937
|
children: listItemIds.map((childId, index) => /* @__PURE__ */ jsx2(
|
|
@@ -4693,6 +5113,7 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
|
|
|
4693
5113
|
deferredConfig ? { ...deferredConfig, enabled: true } : { enabled: false }
|
|
4694
5114
|
);
|
|
4695
5115
|
if (!node || !node.block) return null;
|
|
5116
|
+
const blockIsFinalized = node.block.isFinalized;
|
|
4696
5117
|
const nodeVersion = node.version;
|
|
4697
5118
|
const lines = React7.useMemo(() => {
|
|
4698
5119
|
const seenIds = /* @__PURE__ */ new Set();
|
|
@@ -4723,13 +5144,25 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
|
|
|
4723
5144
|
const lang = typeof node.props?.lang === "string" ? node.props?.lang : node.block.payload.meta?.lang;
|
|
4724
5145
|
const preAttrs = node.props?.preAttrs ?? void 0;
|
|
4725
5146
|
const codeAttrs = node.props?.codeAttrs ?? void 0;
|
|
5147
|
+
const blockMeta = node.block.payload.meta ?? void 0;
|
|
5148
|
+
const lazyEnabled = Boolean(blockMeta?.lazyTokenization);
|
|
5149
|
+
const lazyTokenizedUntil = typeof blockMeta?.lazyTokenizedUntil === "number" ? blockMeta.lazyTokenizedUntil : 0;
|
|
5150
|
+
const lazyHighlightedLines = Array.isArray(blockMeta?.highlightedLines) ? blockMeta.highlightedLines.length : 0;
|
|
5151
|
+
const lazyTokenLines = Array.isArray(blockMeta?.tokenLines) ? blockMeta.tokenLines.length : 0;
|
|
5152
|
+
const lazySourceTextLineCount = React7.useMemo(() => {
|
|
5153
|
+
const raw = typeof node.block?.payload.raw === "string" ? node.block.payload.raw : "";
|
|
5154
|
+
const metaCode = typeof blockMeta?.code === "string" ? blockMeta.code : null;
|
|
5155
|
+
const sourceText = resolveCodeSourceText(raw, metaCode);
|
|
5156
|
+
if (sourceText.length === 0) return 0;
|
|
5157
|
+
return sourceText.split("\n").length;
|
|
5158
|
+
}, [blockMeta, node.block?.payload.raw]);
|
|
5159
|
+
const totalLazyLineCount = Math.max(lines.length, lazyHighlightedLines, lazyTokenLines, lazySourceTextLineCount);
|
|
4726
5160
|
const virtualizationConfig = DEFAULT_VIRTUALIZED_CODE_CONFIG;
|
|
4727
5161
|
const virtualizationDisabled = typeof process !== "undefined" && typeof process.env === "object" && process.env.STREAM_MDX_DISABLE_VIRTUALIZED_CODE === "true";
|
|
4728
|
-
const
|
|
5162
|
+
const canVirtualize = node.block.isFinalized && !lazyEnabled;
|
|
5163
|
+
const shouldVirtualize = canVirtualize && !virtualizationDisabled && virtualizationConfig.enabled && lines.length >= virtualizationConfig.virtualizeThreshold;
|
|
4729
5164
|
const virtualization = useVirtualizedCode(lines, shouldVirtualize ? virtualizationConfig : { ...virtualizationConfig, enabled: false });
|
|
4730
5165
|
const highlightRequester = useCodeHighlightRequester();
|
|
4731
|
-
const lazyEnabled = Boolean(node.block.payload.meta?.lazyTokenization);
|
|
4732
|
-
const lazyTokenizedUntil = typeof node.block.payload.meta?.lazyTokenizedUntil === "number" ? node.block.payload.meta.lazyTokenizedUntil : 0;
|
|
4733
5166
|
const lastRangeRef = React7.useRef(null);
|
|
4734
5167
|
const rafRef = React7.useRef(null);
|
|
4735
5168
|
const idleRef = React7.useRef(null);
|
|
@@ -4810,29 +5243,111 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
|
|
|
4810
5243
|
virtualization.window.startIndex,
|
|
4811
5244
|
virtualization.window.endIndex
|
|
4812
5245
|
]);
|
|
4813
|
-
|
|
4814
|
-
if (
|
|
4815
|
-
|
|
5246
|
+
React7.useEffect(() => {
|
|
5247
|
+
if (shouldVirtualize || !blockIsFinalized || !lazyEnabled || !highlightRequester) return;
|
|
5248
|
+
if (totalLazyLineCount <= 0 || lazyTokenizedUntil >= totalLazyLineCount) return;
|
|
5249
|
+
const nextRange = {
|
|
5250
|
+
visibleStart: 0,
|
|
5251
|
+
visibleEnd: totalLazyLineCount,
|
|
5252
|
+
prefetchStart: 0,
|
|
5253
|
+
prefetchEnd: totalLazyLineCount,
|
|
5254
|
+
tokenizedUntil: lazyTokenizedUntil
|
|
5255
|
+
};
|
|
5256
|
+
const last = lastRangeRef.current;
|
|
5257
|
+
if (last && last.visibleStart === nextRange.visibleStart && last.visibleEnd === nextRange.visibleEnd && last.prefetchStart === nextRange.prefetchStart && last.prefetchEnd === nextRange.prefetchEnd && last.tokenizedUntil === nextRange.tokenizedUntil) {
|
|
5258
|
+
return;
|
|
5259
|
+
}
|
|
5260
|
+
lastRangeRef.current = nextRange;
|
|
5261
|
+
if (rafRef.current !== null && typeof cancelAnimationFrame === "function") {
|
|
5262
|
+
cancelAnimationFrame(rafRef.current);
|
|
5263
|
+
}
|
|
5264
|
+
const requestFullHighlight = () => {
|
|
5265
|
+
highlightRequester({
|
|
5266
|
+
blockId,
|
|
5267
|
+
startLine: 0,
|
|
5268
|
+
endLine: totalLazyLineCount,
|
|
5269
|
+
priority: "visible",
|
|
5270
|
+
reason: "finalize-full"
|
|
5271
|
+
});
|
|
5272
|
+
rafRef.current = null;
|
|
5273
|
+
};
|
|
5274
|
+
if (typeof requestAnimationFrame === "function") {
|
|
5275
|
+
rafRef.current = requestAnimationFrame(requestFullHighlight);
|
|
5276
|
+
} else {
|
|
5277
|
+
requestFullHighlight();
|
|
4816
5278
|
}
|
|
5279
|
+
}, [blockId, blockIsFinalized, highlightRequester, lazyEnabled, lazyTokenizedUntil, shouldVirtualize, totalLazyLineCount]);
|
|
5280
|
+
const highlightedHtml = React7.useMemo(() => {
|
|
5281
|
+
const blockHtml = node.block?.payload.highlightedHtml ?? "";
|
|
5282
|
+
const raw = typeof node.block?.payload.raw === "string" ? node.block.payload.raw : "";
|
|
5283
|
+
const metaCode = typeof node.block?.payload.meta?.code === "string" ? (node.block?.payload.meta).code : null;
|
|
5284
|
+
const sourceText = resolveCodeSourceText(raw, metaCode);
|
|
5285
|
+
const sourceEndsWithNewline = sourceText.endsWith("\n");
|
|
5286
|
+
const sourceLines = sourceText.length > 0 ? sourceText.split("\n") : [];
|
|
5287
|
+
const rawFallbackLines = buildCodeFallbackLines(raw);
|
|
5288
|
+
const deterministicWrapperAttrs = getDefaultCodeWrapperAttributes2(lang);
|
|
5289
|
+
const composedPreAttrs = deterministicWrapperAttrs.preAttrs;
|
|
5290
|
+
const composedCodeAttrs = deterministicWrapperAttrs.codeAttrs;
|
|
5291
|
+
const shouldAppendTerminalNewline = (renderedLines) => {
|
|
5292
|
+
if (!sourceEndsWithNewline || renderedLines.length === 0 || lines.length === 0) {
|
|
5293
|
+
return false;
|
|
5294
|
+
}
|
|
5295
|
+
const lastRendered = renderedLines[renderedLines.length - 1];
|
|
5296
|
+
const lastKnown = lines[lines.length - 1];
|
|
5297
|
+
if (!lastRendered || !lastKnown || lastRendered.index !== lastKnown.index || lastRendered.text === "") {
|
|
5298
|
+
return false;
|
|
5299
|
+
}
|
|
5300
|
+
const expectedRenderedLineCount = Math.max(0, sourceLines.length - 1);
|
|
5301
|
+
const expectedLastRenderedLine = sourceLines.length >= 2 ? sourceLines[sourceLines.length - 2] ?? "" : "";
|
|
5302
|
+
return renderedLines.length === expectedRenderedLineCount && lastRendered.text === expectedLastRenderedLine;
|
|
5303
|
+
};
|
|
4817
5304
|
if (lines.length > 0) {
|
|
4818
5305
|
const hasLineHtml = lines.some((line) => typeof line.html === "string" && line.html.length > 0);
|
|
4819
|
-
if (!hasLineHtml &&
|
|
4820
|
-
return
|
|
5306
|
+
if (!hasLineHtml && blockHtml) {
|
|
5307
|
+
return blockHtml;
|
|
4821
5308
|
}
|
|
4822
5309
|
if (shouldVirtualize) {
|
|
4823
|
-
return composeHighlightedHtml(
|
|
5310
|
+
return composeHighlightedHtml(
|
|
5311
|
+
virtualization.window.visibleLines,
|
|
5312
|
+
composedPreAttrs,
|
|
5313
|
+
composedCodeAttrs,
|
|
5314
|
+
shouldAppendTerminalNewline(virtualization.window.visibleLines)
|
|
5315
|
+
);
|
|
4824
5316
|
}
|
|
4825
|
-
return composeHighlightedHtml(lines,
|
|
5317
|
+
return composeHighlightedHtml(lines, composedPreAttrs, composedCodeAttrs, shouldAppendTerminalNewline(lines));
|
|
5318
|
+
}
|
|
5319
|
+
if (!shouldVirtualize && blockHtml) {
|
|
5320
|
+
const hasHighlightedLines = blockHtml.includes('class="line"');
|
|
5321
|
+
if (!hasHighlightedLines && rawFallbackLines.length > 0) {
|
|
5322
|
+
return composeHighlightedHtml(
|
|
5323
|
+
rawFallbackLines,
|
|
5324
|
+
composedPreAttrs,
|
|
5325
|
+
composedCodeAttrs,
|
|
5326
|
+
sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
|
|
5327
|
+
);
|
|
5328
|
+
}
|
|
5329
|
+
return blockHtml;
|
|
4826
5330
|
}
|
|
4827
|
-
const blockHtml = node.block?.payload.highlightedHtml ?? "";
|
|
4828
5331
|
if (blockHtml?.trim().match(/^```[\w-]*\s*$/)) {
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
5332
|
+
if (rawFallbackLines.length > 0) {
|
|
5333
|
+
return composeHighlightedHtml(
|
|
5334
|
+
rawFallbackLines,
|
|
5335
|
+
composedPreAttrs,
|
|
5336
|
+
composedCodeAttrs,
|
|
5337
|
+
sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
|
|
5338
|
+
);
|
|
4832
5339
|
}
|
|
4833
5340
|
}
|
|
5341
|
+
if (!blockHtml && rawFallbackLines.length > 0) {
|
|
5342
|
+
return composeHighlightedHtml(
|
|
5343
|
+
rawFallbackLines,
|
|
5344
|
+
composedPreAttrs,
|
|
5345
|
+
composedCodeAttrs,
|
|
5346
|
+
sourceEndsWithNewline && rawFallbackLines[rawFallbackLines.length - 1]?.text !== ""
|
|
5347
|
+
);
|
|
5348
|
+
}
|
|
4834
5349
|
return blockHtml;
|
|
4835
|
-
}, [shouldVirtualize, node.block?.payload.highlightedHtml,
|
|
5350
|
+
}, [blockIsFinalized, shouldVirtualize, node.block?.payload.highlightedHtml, node.block?.payload.meta, lines, lang, virtualization.window.visibleLines, node.block?.payload.raw]);
|
|
4836
5351
|
const rendered = /* @__PURE__ */ jsx2(
|
|
4837
5352
|
CodeComponent,
|
|
4838
5353
|
{
|
|
@@ -4844,13 +5359,19 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
|
|
|
4844
5359
|
codeAttrs
|
|
4845
5360
|
}
|
|
4846
5361
|
);
|
|
5362
|
+
const effectiveLineCount = React7.useMemo(() => {
|
|
5363
|
+
if (lines.length > 0) {
|
|
5364
|
+
return lines.length;
|
|
5365
|
+
}
|
|
5366
|
+
return 1;
|
|
5367
|
+
}, [lines.length]);
|
|
4847
5368
|
const codeFrameClass = "not-prose flex flex-col rounded-lg border border-input pt-1 font-mono text-sm";
|
|
4848
5369
|
const codeMetricsAttrs = {
|
|
4849
5370
|
"data-code-block": "true",
|
|
4850
5371
|
"data-block-id": blockId,
|
|
4851
5372
|
"data-code-virtualized": shouldVirtualize ? "true" : "false",
|
|
4852
|
-
"data-code-total-lines": String(
|
|
4853
|
-
"data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines :
|
|
5373
|
+
"data-code-total-lines": String(effectiveLineCount),
|
|
5374
|
+
"data-code-mounted-lines": String(shouldVirtualize ? virtualization.window.mountedLines : effectiveLineCount),
|
|
4854
5375
|
"data-code-window-size": String(virtualization.config.windowSize)
|
|
4855
5376
|
};
|
|
4856
5377
|
const codeView = shouldVirtualize ? (() => {
|
|
@@ -4896,21 +5417,152 @@ var CodeBlockView = React7.memo(({ store, blockId, registry }) => {
|
|
|
4896
5417
|
return codeView;
|
|
4897
5418
|
});
|
|
4898
5419
|
CodeBlockView.displayName = "CodeBlockView";
|
|
4899
|
-
function composeHighlightedHtml(lines, preAttrs, codeAttrs) {
|
|
5420
|
+
function composeHighlightedHtml(lines, preAttrs, codeAttrs, appendTerminalNewline = false) {
|
|
4900
5421
|
const lineMarkup = lines.map((line) => {
|
|
4901
|
-
const content = line.html ?? escapeHtml3(line.text);
|
|
5422
|
+
const content = line.html ?? renderTokenLineHtml(line.tokens) ?? escapeHtml3(line.text);
|
|
4902
5423
|
const dataLine = Number.isFinite(line.index) ? ` data-line="${line.index + 1}"` : "";
|
|
4903
5424
|
return `<span class="line"${dataLine}>${content}</span>`;
|
|
4904
5425
|
}).join("\n");
|
|
4905
5426
|
const preAttr = attrsToString(preAttrs);
|
|
4906
5427
|
const codeAttr = attrsToString(codeAttrs);
|
|
4907
|
-
return `<pre${preAttr}><code${codeAttr}>${lineMarkup}
|
|
4908
|
-
|
|
5428
|
+
return `<pre${preAttr}><code${codeAttr}>${lineMarkup}${appendTerminalNewline ? "\n" : ""}</code></pre>`;
|
|
5429
|
+
}
|
|
5430
|
+
var TOKEN_FONT_STYLE_ITALIC = 1;
|
|
5431
|
+
var TOKEN_FONT_STYLE_BOLD = 2;
|
|
5432
|
+
var TOKEN_FONT_STYLE_UNDERLINE = 4;
|
|
5433
|
+
var TOKEN_FONT_STYLE_STRIKETHROUGH = 8;
|
|
5434
|
+
function renderTokenLineHtml(tokens) {
|
|
5435
|
+
if (!tokens || !Array.isArray(tokens.spans)) {
|
|
5436
|
+
return null;
|
|
5437
|
+
}
|
|
5438
|
+
return mergeRenderableTokenSpans(tokens.spans).map((span) => renderTokenSpanHtml(span)).join("");
|
|
5439
|
+
}
|
|
5440
|
+
function renderTokenSpanHtml(span) {
|
|
5441
|
+
const dark = span.v?.dark;
|
|
5442
|
+
const light = span.v?.light;
|
|
5443
|
+
const single = span.s;
|
|
5444
|
+
const styles = [];
|
|
5445
|
+
if (dark?.fg) {
|
|
5446
|
+
styles.push(`--shiki-dark:${dark.fg}`);
|
|
5447
|
+
}
|
|
5448
|
+
if (light?.fg) {
|
|
5449
|
+
styles.push(`--shiki-light:${light.fg}`);
|
|
5450
|
+
}
|
|
5451
|
+
if (!light?.fg && !dark?.fg && single?.fg) {
|
|
5452
|
+
styles.push(`color:${single.fg}`);
|
|
5453
|
+
}
|
|
5454
|
+
const fontStyle = light?.fs ?? dark?.fs ?? single?.fs;
|
|
5455
|
+
if (typeof fontStyle === "number" && fontStyle > 0) {
|
|
5456
|
+
if (fontStyle & TOKEN_FONT_STYLE_ITALIC) {
|
|
5457
|
+
styles.push("font-style: italic");
|
|
5458
|
+
}
|
|
5459
|
+
if (fontStyle & TOKEN_FONT_STYLE_BOLD) {
|
|
5460
|
+
styles.push("font-weight: bold");
|
|
5461
|
+
}
|
|
5462
|
+
const decorations = [];
|
|
5463
|
+
if (fontStyle & TOKEN_FONT_STYLE_UNDERLINE) {
|
|
5464
|
+
decorations.push("underline");
|
|
5465
|
+
}
|
|
5466
|
+
if (fontStyle & TOKEN_FONT_STYLE_STRIKETHROUGH) {
|
|
5467
|
+
decorations.push("line-through");
|
|
5468
|
+
}
|
|
5469
|
+
if (decorations.length > 0) {
|
|
5470
|
+
styles.push(`text-decoration: ${decorations.join(" ")}`);
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
const content = escapeHtml3(span.t ?? "");
|
|
5474
|
+
if (styles.length === 0) {
|
|
5475
|
+
return content;
|
|
5476
|
+
}
|
|
5477
|
+
return `<span style="${styles.join(";")}">${content}</span>`;
|
|
5478
|
+
}
|
|
5479
|
+
function mergeRenderableTokenSpans(spans) {
|
|
5480
|
+
if (spans.length <= 1) {
|
|
5481
|
+
return spans.slice();
|
|
5482
|
+
}
|
|
5483
|
+
const merged = [];
|
|
5484
|
+
let pendingWhitespace = "";
|
|
5485
|
+
for (const span of spans) {
|
|
5486
|
+
const text = typeof span.t === "string" ? span.t : "";
|
|
5487
|
+
if (/^[\t ]+$/.test(text)) {
|
|
5488
|
+
pendingWhitespace += text;
|
|
5489
|
+
continue;
|
|
5490
|
+
}
|
|
5491
|
+
const nextSpan = pendingWhitespace ? { ...span, t: `${pendingWhitespace}${text}` } : span;
|
|
5492
|
+
pendingWhitespace = "";
|
|
5493
|
+
const previous = merged[merged.length - 1];
|
|
5494
|
+
if (previous && sameTokenSpanStyle(previous, nextSpan)) {
|
|
5495
|
+
previous.t += nextSpan.t;
|
|
5496
|
+
} else {
|
|
5497
|
+
merged.push({ ...nextSpan });
|
|
5498
|
+
}
|
|
5499
|
+
}
|
|
5500
|
+
if (pendingWhitespace) {
|
|
5501
|
+
const previous = merged[merged.length - 1];
|
|
5502
|
+
if (previous) {
|
|
5503
|
+
previous.t += pendingWhitespace;
|
|
5504
|
+
} else {
|
|
5505
|
+
merged.push({ t: pendingWhitespace });
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
return merged;
|
|
5509
|
+
}
|
|
5510
|
+
function sameTokenSpanStyle(a, b) {
|
|
5511
|
+
const variantKeys = /* @__PURE__ */ new Set([
|
|
5512
|
+
...Object.keys(a.v ?? {}),
|
|
5513
|
+
...Object.keys(b.v ?? {})
|
|
5514
|
+
]);
|
|
5515
|
+
for (const key of variantKeys) {
|
|
5516
|
+
const aStyle = a.v?.[key];
|
|
5517
|
+
const bStyle = b.v?.[key];
|
|
5518
|
+
if ((aStyle?.fg ?? null) !== (bStyle?.fg ?? null)) return false;
|
|
5519
|
+
if ((aStyle?.bg ?? null) !== (bStyle?.bg ?? null)) return false;
|
|
5520
|
+
if ((aStyle?.fs ?? null) !== (bStyle?.fs ?? null)) return false;
|
|
5521
|
+
}
|
|
5522
|
+
if ((a.s?.fg ?? null) !== (b.s?.fg ?? null)) return false;
|
|
5523
|
+
if ((a.s?.bg ?? null) !== (b.s?.bg ?? null)) return false;
|
|
5524
|
+
if ((a.s?.fs ?? null) !== (b.s?.fs ?? null)) return false;
|
|
5525
|
+
return true;
|
|
5526
|
+
}
|
|
5527
|
+
function resolveCodeSourceText(raw, metaCode) {
|
|
5528
|
+
if (metaCode !== null) {
|
|
5529
|
+
return metaCode.replace(/\r\n?/g, "\n");
|
|
5530
|
+
}
|
|
5531
|
+
if (!raw) return "";
|
|
5532
|
+
const normalized = raw.replace(/\r\n?/g, "\n");
|
|
5533
|
+
const fenced = stripCodeFence(normalized);
|
|
5534
|
+
return fenced.hadFence ? fenced.code : normalized;
|
|
4909
5535
|
}
|
|
4910
5536
|
function attrsToString(attrs) {
|
|
4911
5537
|
if (!attrs) return "";
|
|
4912
5538
|
return Object.entries(attrs).map(([key, value]) => ` ${key}="${escapeAttribute(value)}"`).join("");
|
|
4913
5539
|
}
|
|
5540
|
+
function buildCodeFallbackLines(raw) {
|
|
5541
|
+
const normalized = normalizeCodeFallbackRaw(raw);
|
|
5542
|
+
if (normalized.length === 0) {
|
|
5543
|
+
if (/^\s*```[^\n]*\n?\s*$/.test(raw)) {
|
|
5544
|
+
return [{ id: "temp-0", index: 0, text: "", html: null }];
|
|
5545
|
+
}
|
|
5546
|
+
return [];
|
|
5547
|
+
}
|
|
5548
|
+
return normalized.split("\n").map((line, index) => ({
|
|
5549
|
+
id: `temp-${index}`,
|
|
5550
|
+
index,
|
|
5551
|
+
text: line,
|
|
5552
|
+
html: null
|
|
5553
|
+
}));
|
|
5554
|
+
}
|
|
5555
|
+
function normalizeCodeFallbackRaw(raw) {
|
|
5556
|
+
if (!raw) return "";
|
|
5557
|
+
const normalized = raw.replace(/\r\n?/g, "\n");
|
|
5558
|
+
const fenced = stripCodeFence(normalized);
|
|
5559
|
+
if (!fenced.hadFence) {
|
|
5560
|
+
return normalized.replace(/\n+$/, "");
|
|
5561
|
+
}
|
|
5562
|
+
let code = fenced.code;
|
|
5563
|
+
code = code.replace(/\n+$/, "");
|
|
5564
|
+
return code;
|
|
5565
|
+
}
|
|
4914
5566
|
function escapeHtml3(value) {
|
|
4915
5567
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4916
5568
|
}
|
|
@@ -5034,7 +5686,9 @@ var MarkdownRenderer = class {
|
|
|
5034
5686
|
lowPriorityFrameBudgetMs: this.config.performance?.lowPriorityFrameBudgetMs,
|
|
5035
5687
|
urgentQueueThreshold: this.config.performance?.urgentQueueThreshold,
|
|
5036
5688
|
batch: this.config.performance?.batch,
|
|
5037
|
-
historyLimit: this.config.performance?.historyLimit
|
|
5689
|
+
historyLimit: this.config.performance?.historyLimit,
|
|
5690
|
+
startupMicrotaskFlushes: this.config.performance?.startupMicrotaskFlushes,
|
|
5691
|
+
adaptiveBudgeting: this.config.performance?.adaptiveBudgeting
|
|
5038
5692
|
}
|
|
5039
5693
|
});
|
|
5040
5694
|
}
|
|
@@ -5228,18 +5882,8 @@ var MarkdownRenderer = class {
|
|
|
5228
5882
|
const receivedAt = typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
5229
5883
|
const batches = splitPatchBatch(message.patches);
|
|
5230
5884
|
batches.forEach((batch, index) => {
|
|
5231
|
-
const
|
|
5232
|
-
|
|
5233
|
-
case "insertChild":
|
|
5234
|
-
case "replaceChild":
|
|
5235
|
-
case "deleteChild":
|
|
5236
|
-
case "reorder":
|
|
5237
|
-
return true;
|
|
5238
|
-
default:
|
|
5239
|
-
return false;
|
|
5240
|
-
}
|
|
5241
|
-
});
|
|
5242
|
-
const priority = hasStructural ? "high" : batch.some(isHeavyPatch) ? "low" : "high";
|
|
5885
|
+
const hasSemantic = batch.some((patch) => getPatchKind(patch) === "semantic");
|
|
5886
|
+
const priority = hasSemantic ? "high" : batch.some(isHeavyPatch) ? "low" : "high";
|
|
5243
5887
|
this.patchScheduler.enqueue({
|
|
5244
5888
|
patches: batch,
|
|
5245
5889
|
meta: {
|
|
@@ -5384,13 +6028,14 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
|
|
|
5384
6028
|
const mode = options?.mode ?? "server";
|
|
5385
6029
|
const [compiledMap, setCompiledMap] = useState4(/* @__PURE__ */ new Map());
|
|
5386
6030
|
const blockSignatureRef = useRef3(/* @__PURE__ */ new Map());
|
|
6031
|
+
const failedSignatureRef = useRef3(/* @__PURE__ */ new Map());
|
|
5387
6032
|
const applyCompiledRef = useCallback3(
|
|
5388
6033
|
(block, compiledId) => {
|
|
5389
6034
|
if (mode === "worker") {
|
|
5390
6035
|
return;
|
|
5391
6036
|
}
|
|
5392
6037
|
if (options?.workerClient) {
|
|
5393
|
-
options.workerClient.setMdxCompiled(block.id, compiledId);
|
|
6038
|
+
options.workerClient.setMdxCompiled(block.id, compiledId, block.payload.raw ?? "");
|
|
5394
6039
|
return;
|
|
5395
6040
|
}
|
|
5396
6041
|
const store = options?.store;
|
|
@@ -5401,6 +6046,9 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
|
|
|
5401
6046
|
const index = currentBlocks.findIndex((candidate) => candidate.id === block.id);
|
|
5402
6047
|
if (index === -1) return;
|
|
5403
6048
|
const current = currentBlocks[index];
|
|
6049
|
+
if ((current.payload.raw ?? "") !== (block.payload.raw ?? "")) {
|
|
6050
|
+
return;
|
|
6051
|
+
}
|
|
5404
6052
|
if (current.payload.compiledMdxRef?.id === compiledId) {
|
|
5405
6053
|
return;
|
|
5406
6054
|
}
|
|
@@ -5430,7 +6078,13 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
|
|
|
5430
6078
|
if (mode === "worker") {
|
|
5431
6079
|
return;
|
|
5432
6080
|
}
|
|
5433
|
-
const targets = blocks.filter((b) =>
|
|
6081
|
+
const targets = blocks.filter((b) => {
|
|
6082
|
+
if (b.type !== "mdx" || !b.isFinalized || b.payload.compiledMdxRef || compiledMap.has(b.id)) {
|
|
6083
|
+
return false;
|
|
6084
|
+
}
|
|
6085
|
+
const raw = b.payload.raw ?? "";
|
|
6086
|
+
return failedSignatureRef.current.get(b.id) !== raw;
|
|
6087
|
+
});
|
|
5434
6088
|
if (targets.length === 0) return;
|
|
5435
6089
|
let cancelled = false;
|
|
5436
6090
|
(async () => {
|
|
@@ -5449,22 +6103,29 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
|
|
|
5449
6103
|
next.set(block.id, compiled.id);
|
|
5450
6104
|
return next;
|
|
5451
6105
|
});
|
|
6106
|
+
failedSignatureRef.current.delete(block.id);
|
|
5452
6107
|
applyCompiledRef(block, compiled.id);
|
|
5453
6108
|
} catch (e) {
|
|
5454
6109
|
console.warn("MDX compile failed for block", block.id, e);
|
|
6110
|
+
failedSignatureRef.current.set(block.id, block.payload.raw ?? "");
|
|
5455
6111
|
if (options?.workerClient) {
|
|
5456
|
-
options.workerClient.setMdxError(block.id, e instanceof Error ? e.message : String(e));
|
|
6112
|
+
options.workerClient.setMdxError(block.id, e instanceof Error ? e.message : String(e), block.payload.raw ?? "");
|
|
5457
6113
|
} else if (options?.store) {
|
|
6114
|
+
const currentBlocks = options.store.getBlocks();
|
|
6115
|
+
const current = currentBlocks.find((candidate) => candidate.id === block.id);
|
|
6116
|
+
if (!current || (current.payload.raw ?? "") !== (block.payload.raw ?? "")) {
|
|
6117
|
+
continue;
|
|
6118
|
+
}
|
|
5458
6119
|
options.store.applyPatches([
|
|
5459
6120
|
{
|
|
5460
6121
|
op: "setProps",
|
|
5461
6122
|
at: { blockId: block.id, nodeId: block.id },
|
|
5462
6123
|
props: {
|
|
5463
6124
|
block: {
|
|
5464
|
-
...
|
|
6125
|
+
...current,
|
|
5465
6126
|
payload: {
|
|
5466
|
-
...
|
|
5467
|
-
meta: { ...
|
|
6127
|
+
...current.payload,
|
|
6128
|
+
meta: { ...current.payload.meta ?? {}, mdxStatus: "error", mdxError: e instanceof Error ? e.message : String(e) }
|
|
5468
6129
|
}
|
|
5469
6130
|
}
|
|
5470
6131
|
}
|
|
@@ -5497,6 +6158,7 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
|
|
|
5497
6158
|
const currentSignature = candidate.payload.raw ?? "";
|
|
5498
6159
|
if (previousSignature !== void 0 && previousSignature !== currentSignature) {
|
|
5499
6160
|
next.delete(blockId);
|
|
6161
|
+
failedSignatureRef.current.delete(blockId);
|
|
5500
6162
|
mutated = true;
|
|
5501
6163
|
}
|
|
5502
6164
|
}
|
|
@@ -5506,6 +6168,9 @@ function useMdxCoordinator(blocks, compileEndpoint, options) {
|
|
|
5506
6168
|
for (const block of blocks) {
|
|
5507
6169
|
if (block.type === "mdx") {
|
|
5508
6170
|
signatureMap.set(block.id, block.payload.raw ?? "");
|
|
6171
|
+
if (!block.isFinalized || block.payload.compiledMdxRef) {
|
|
6172
|
+
failedSignatureRef.current.delete(block.id);
|
|
6173
|
+
}
|
|
5509
6174
|
}
|
|
5510
6175
|
}
|
|
5511
6176
|
blockSignatureRef.current = signatureMap;
|
|
@@ -5863,7 +6528,9 @@ function StreamingMarkdownComponent({
|
|
|
5863
6528
|
lowPriorityFrameBudgetMs: effectiveScheduling.lowPriorityFrameBudgetMs,
|
|
5864
6529
|
urgentQueueThreshold: effectiveScheduling.urgentQueueThreshold,
|
|
5865
6530
|
batch: effectiveScheduling.batch,
|
|
5866
|
-
historyLimit
|
|
6531
|
+
historyLimit,
|
|
6532
|
+
startupMicrotaskFlushes: effectiveScheduling.startupMicrotaskFlushes,
|
|
6533
|
+
adaptiveBudgeting: effectiveScheduling.adaptiveBudgeting
|
|
5867
6534
|
},
|
|
5868
6535
|
mdx: mdxConfig
|
|
5869
6536
|
};
|