@beyondwork/docx-react-component 1.0.131 → 1.0.133

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.
Files changed (100) hide show
  1. package/dist/api/public-types.cjs +837 -224
  2. package/dist/api/public-types.d.cts +2 -2
  3. package/dist/api/public-types.d.ts +2 -2
  4. package/dist/api/public-types.js +5 -3
  5. package/dist/api/v3.cjs +9679 -7492
  6. package/dist/api/v3.d.cts +3 -3
  7. package/dist/api/v3.d.ts +3 -3
  8. package/dist/api/v3.js +10 -10
  9. package/dist/{canonical-document-BMtONpgf.d.cts → canonical-document-CfZIc-fC.d.cts} +1 -1
  10. package/dist/{canonical-document-BMtONpgf.d.ts → canonical-document-CfZIc-fC.d.ts} +1 -1
  11. package/dist/{chunk-7G5GR3VV.js → chunk-224TSMEB.js} +635 -89
  12. package/dist/{chunk-YLL7MF5C.js → chunk-3JEE5RJU.js} +103 -135
  13. package/dist/{chunk-35RHOE6I.js → chunk-57HTKX3P.js} +10 -1162
  14. package/dist/{chunk-A66ZVUAT.js → chunk-5KTJKTNE.js} +236 -10
  15. package/dist/{chunk-THVM6EP5.js → chunk-CVSD3UNK.js} +1322 -102
  16. package/dist/{chunk-VRKK2CSZ.js → chunk-EFEW7BTT.js} +2 -2
  17. package/dist/{chunk-KNHMXKC6.js → chunk-INLRCC4N.js} +2 -2
  18. package/dist/{chunk-HYHCRMR7.js → chunk-KL4TZSZV.js} +1 -1
  19. package/dist/{chunk-T5YYFDZB.js → chunk-MQ5GAJ54.js} +68 -39
  20. package/dist/{chunk-CI2TD3T4.js → chunk-NJFKPDNG.js} +216 -2
  21. package/dist/{chunk-WZDKNF37.js → chunk-OTRVGNZQ.js} +2934 -1815
  22. package/dist/{chunk-4YCWECLZ.js → chunk-PZIEOEJZ.js} +1 -1
  23. package/dist/{chunk-PHMWH23E.js → chunk-QTRJLKR2.js} +1 -1
  24. package/dist/{chunk-QXKQPUOM.js → chunk-REFHJ2FN.js} +3 -3
  25. package/dist/{chunk-ZVC23LKV.js → chunk-RP76USJE.js} +1 -1
  26. package/dist/{chunk-3YCQM2RV.js → chunk-S3PEKX6H.js} +249 -45
  27. package/dist/{chunk-M7YRJX6V.js → chunk-T66OS7MN.js} +8 -3
  28. package/dist/{chunk-DGA7M77X.js → chunk-V2JF42SI.js} +2 -2
  29. package/dist/{chunk-Q7Y57KOK.js → chunk-WDDFU2N2.js} +2 -2
  30. package/dist/{chunk-A3GSNB4G.js → chunk-XBQFDBXE.js} +147 -13
  31. package/dist/{chunk-6TBLDBCL.js → chunk-ZFCZ7XXH.js} +1 -1
  32. package/dist/compare.d.cts +1 -1
  33. package/dist/compare.d.ts +1 -1
  34. package/dist/core/commands/formatting-commands.d.cts +2 -2
  35. package/dist/core/commands/formatting-commands.d.ts +2 -2
  36. package/dist/core/commands/image-commands.cjs +344 -20
  37. package/dist/core/commands/image-commands.d.cts +2 -2
  38. package/dist/core/commands/image-commands.d.ts +2 -2
  39. package/dist/core/commands/image-commands.js +5 -5
  40. package/dist/core/commands/section-layout-commands.d.cts +2 -2
  41. package/dist/core/commands/section-layout-commands.d.ts +2 -2
  42. package/dist/core/commands/style-commands.d.cts +2 -2
  43. package/dist/core/commands/style-commands.d.ts +2 -2
  44. package/dist/core/commands/table-structure-commands.cjs +344 -20
  45. package/dist/core/commands/table-structure-commands.d.cts +2 -2
  46. package/dist/core/commands/table-structure-commands.d.ts +2 -2
  47. package/dist/core/commands/table-structure-commands.js +4 -4
  48. package/dist/core/commands/text-commands.cjs +411 -58
  49. package/dist/core/commands/text-commands.d.cts +13 -2
  50. package/dist/core/commands/text-commands.d.ts +13 -2
  51. package/dist/core/commands/text-commands.js +5 -5
  52. package/dist/core/selection/mapping.d.cts +2 -2
  53. package/dist/core/selection/mapping.d.ts +2 -2
  54. package/dist/core/state/editor-state.d.cts +2 -2
  55. package/dist/core/state/editor-state.d.ts +2 -2
  56. package/dist/index.cjs +7199 -4004
  57. package/dist/index.d.cts +5 -5
  58. package/dist/index.d.ts +5 -5
  59. package/dist/index.js +814 -120
  60. package/dist/io/docx-session.cjs +7 -2
  61. package/dist/io/docx-session.d.cts +4 -4
  62. package/dist/io/docx-session.d.ts +4 -4
  63. package/dist/io/docx-session.js +4 -4
  64. package/dist/legal.d.cts +1 -1
  65. package/dist/legal.d.ts +1 -1
  66. package/dist/legal.js +3 -3
  67. package/dist/{loader-DiY_ZgKl.d.cts → loader-B2H99237.d.cts} +3 -3
  68. package/dist/{loader-B-aL5HGD.d.ts → loader-DfTjqVwn.d.ts} +3 -3
  69. package/dist/{public-types-gvubspUI.d.cts → public-types-B5lOUIrP.d.ts} +930 -338
  70. package/dist/{public-types-DyqnxxO9.d.ts → public-types-S8gTYwKo.d.cts} +930 -338
  71. package/dist/public-types.cjs +837 -224
  72. package/dist/public-types.d.cts +2 -2
  73. package/dist/public-types.d.ts +2 -2
  74. package/dist/public-types.js +5 -3
  75. package/dist/runtime/collab.d.cts +3 -3
  76. package/dist/runtime/collab.d.ts +3 -3
  77. package/dist/runtime/document-runtime.cjs +1403 -438
  78. package/dist/runtime/document-runtime.d.cts +2 -2
  79. package/dist/runtime/document-runtime.d.ts +2 -2
  80. package/dist/runtime/document-runtime.js +14 -14
  81. package/dist/{session-CDB0hohT.d.ts → session-CBDIOYXA.d.ts} +3 -3
  82. package/dist/{session-BUN6B-Vj.d.cts → session-CR2A1hGZ.d.cts} +3 -3
  83. package/dist/session.cjs +7 -2
  84. package/dist/session.d.cts +5 -5
  85. package/dist/session.d.ts +5 -5
  86. package/dist/session.js +5 -5
  87. package/dist/tailwind.cjs +905 -322
  88. package/dist/tailwind.d.cts +2 -2
  89. package/dist/tailwind.d.ts +2 -2
  90. package/dist/tailwind.js +7 -7
  91. package/dist/{types-VWH6CRvG.d.ts → types-B-90ywjU.d.ts} +2 -2
  92. package/dist/{types-C4bz3kDU.d.cts → types-yty2K-hk.d.cts} +2 -2
  93. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +3 -3
  94. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +3 -3
  95. package/dist/ui-tailwind/editor-surface/search-plugin.js +4 -4
  96. package/dist/ui-tailwind.cjs +905 -322
  97. package/dist/ui-tailwind.d.cts +4 -3
  98. package/dist/ui-tailwind.d.ts +4 -3
  99. package/dist/ui-tailwind.js +7 -7
  100. package/package.json +1 -1
@@ -19,13 +19,13 @@ import {
19
19
  resolveSectionVariants,
20
20
  sectionSupportsStoryTarget,
21
21
  storyTargetKey
22
- } from "./chunk-A3GSNB4G.js";
22
+ } from "./chunk-XBQFDBXE.js";
23
23
  import {
24
24
  createSelectionSnapshot
25
25
  } from "./chunk-OYGMRRR7.js";
26
26
  import {
27
27
  collectCanonicalLayoutInputs
28
- } from "./chunk-A66ZVUAT.js";
28
+ } from "./chunk-5KTJKTNE.js";
29
29
  import {
30
30
  MAIN_STORY_TARGET,
31
31
  createDetachedAnchor,
@@ -110,6 +110,1175 @@ function createScopeTagRegistry() {
110
110
  };
111
111
  }
112
112
 
113
+ // src/runtime/telemetry/perf-probe.ts
114
+ var PREDICTED_LANE_COUNTERS = {
115
+ applied: "predictions.applied",
116
+ equivalent: "predictions.equivalent",
117
+ adjusted: "predictions.adjusted",
118
+ rejected: "predictions.rejected",
119
+ rollback: "predictions.rollback",
120
+ structuralDivergence: "predictions.structuralDivergence",
121
+ bailBeforePredict: "predictions.bailBeforePredict",
122
+ refreshSelectionOnly: "predictions.refresh.selectionOnly",
123
+ refreshLocalTextEquivalent: "predictions.refresh.localTextEquivalent",
124
+ refreshSurfaceOnly: "predictions.refresh.surfaceOnly",
125
+ refreshFullProjection: "predictions.refresh.fullProjection",
126
+ refreshBlocked: "predictions.refresh.blocked"
127
+ };
128
+ function startPerfProbe(kind) {
129
+ const state = getEnabledState();
130
+ if (!state) {
131
+ return null;
132
+ }
133
+ const token = `${kind}-${state.nextToken ?? 0}`;
134
+ state.nextToken = (state.nextToken ?? 0) + 1;
135
+ state.pending ??= {};
136
+ state.pending[token] = {
137
+ kind,
138
+ startedAt: performance.now()
139
+ };
140
+ return token;
141
+ }
142
+ function finishPerfProbe(token) {
143
+ if (!token) {
144
+ return null;
145
+ }
146
+ const state = getEnabledState();
147
+ if (!state?.pending?.[token]) {
148
+ return null;
149
+ }
150
+ const pending = state.pending[token];
151
+ delete state.pending[token];
152
+ const sample = {
153
+ token,
154
+ kind: pending.kind,
155
+ durationMs: performance.now() - pending.startedAt,
156
+ recordedAt: Date.now()
157
+ };
158
+ pushSample(state, sample);
159
+ return sample;
160
+ }
161
+ function recordPerfSample(kind, durationMs = 0) {
162
+ const state = getEnabledState();
163
+ if (!state) {
164
+ return null;
165
+ }
166
+ const token = `${kind}-${state.nextToken ?? 0}`;
167
+ state.nextToken = (state.nextToken ?? 0) + 1;
168
+ const sample = {
169
+ token,
170
+ kind,
171
+ durationMs,
172
+ recordedAt: Date.now()
173
+ };
174
+ pushSample(state, sample);
175
+ return sample;
176
+ }
177
+ function incrementInvalidationCounter(counter, amount = 1) {
178
+ const state = getEnabledState();
179
+ if (!state) {
180
+ return 0;
181
+ }
182
+ state.invalidationCounts ??= {};
183
+ state.invalidationCounts[counter] = (state.invalidationCounts[counter] ?? 0) + amount;
184
+ return state.invalidationCounts[counter];
185
+ }
186
+ function getEnabledState() {
187
+ if (typeof window === "undefined") {
188
+ return null;
189
+ }
190
+ const state = window.__DOCX_REACT_PERF_PROBE__;
191
+ if (!state?.enabled) {
192
+ return null;
193
+ }
194
+ return state;
195
+ }
196
+ function pushSample(state, sample) {
197
+ state.samples ??= [];
198
+ state.samples.push(sample);
199
+ const maxSamples = state.maxSamples ?? 20;
200
+ if (state.samples.length > maxSamples) {
201
+ state.samples.splice(0, state.samples.length - maxSamples);
202
+ }
203
+ }
204
+
205
+ // src/runtime/render/decoration-resolver.ts
206
+ function resolveDecorationIndex(input) {
207
+ const workflow = [];
208
+ const comments = [];
209
+ const revisions = [];
210
+ const search = [];
211
+ const locked = [];
212
+ if (input.workflowSegments) {
213
+ for (const segment of input.workflowSegments) {
214
+ const frame = input.anchorIndex.bySelection(
215
+ segment.fromOffset,
216
+ segment.toOffset
217
+ );
218
+ if (!frame) continue;
219
+ workflow.push({
220
+ kind: "workflow",
221
+ refId: segment.scopeId,
222
+ frame
223
+ });
224
+ }
225
+ }
226
+ if (input.comments) {
227
+ for (const thread of input.comments.threads) {
228
+ const frame = resolveRange(
229
+ input.anchorIndex,
230
+ thread.from,
231
+ thread.to
232
+ );
233
+ if (!frame) continue;
234
+ comments.push({
235
+ kind: "comment",
236
+ refId: thread.commentId,
237
+ frame
238
+ });
239
+ void asCommentThread(thread);
240
+ }
241
+ }
242
+ if (input.revisions) {
243
+ for (const revision of input.revisions.revisions) {
244
+ const frame = resolveRange(
245
+ input.anchorIndex,
246
+ revision.from,
247
+ revision.to
248
+ );
249
+ if (!frame) continue;
250
+ revisions.push({
251
+ kind: "revision",
252
+ refId: revision.revisionId,
253
+ frame
254
+ });
255
+ void asRevisionEntry(revision);
256
+ }
257
+ }
258
+ if (input.searchMatches) {
259
+ for (const match of input.searchMatches) {
260
+ const frame = resolveRange(input.anchorIndex, match.from, match.to);
261
+ if (!frame) continue;
262
+ search.push({
263
+ kind: "search",
264
+ refId: match.matchId,
265
+ frame
266
+ });
267
+ }
268
+ }
269
+ if (input.lockedRanges) {
270
+ for (const lock of input.lockedRanges) {
271
+ const frame = resolveRange(input.anchorIndex, lock.from, lock.to);
272
+ if (!frame) continue;
273
+ locked.push({
274
+ kind: "locked",
275
+ refId: lock.lockId,
276
+ frame
277
+ });
278
+ }
279
+ }
280
+ return { workflow, comments, revisions, search, locked };
281
+ }
282
+ function resolveRange(anchorIndex, from, to) {
283
+ if (from >= to) {
284
+ return anchorIndex.byRuntimeOffset(from);
285
+ }
286
+ return anchorIndex.bySelection(from, to);
287
+ }
288
+ function asCommentThread(thread) {
289
+ return thread;
290
+ }
291
+ function asRevisionEntry(entry) {
292
+ return entry;
293
+ }
294
+
295
+ // src/runtime/render/block-fragment-projection.ts
296
+ function classifyBlockKind(blockId) {
297
+ if (blockId.startsWith("paragraph")) return "paragraph";
298
+ if (blockId.startsWith("table")) return "table";
299
+ if (blockId.startsWith("placeholder-culled-")) return "opaque";
300
+ if (blockId.startsWith("opaque")) return "opaque";
301
+ if (blockId.startsWith("section-break")) return "opaque";
302
+ if (blockId.startsWith("sdt")) return "opaque";
303
+ if (blockId.startsWith("custom-xml")) return "opaque";
304
+ if (blockId.startsWith("alt-chunk")) return "opaque";
305
+ if (blockId.startsWith("synthetic")) return "synthetic";
306
+ return "paragraph";
307
+ }
308
+
309
+ // src/runtime/render/render-frame-diff.ts
310
+ function diffRenderFrames(prev, next) {
311
+ if (!prev) {
312
+ return {
313
+ addedPages: next.pages.map((p) => p.page.pageIndex),
314
+ removedPages: [],
315
+ unchangedPages: [],
316
+ changedPages: []
317
+ };
318
+ }
319
+ const prevByIndex = /* @__PURE__ */ new Map();
320
+ for (const page of prev.pages) {
321
+ prevByIndex.set(page.page.pageIndex, page);
322
+ }
323
+ const nextIndices = /* @__PURE__ */ new Set();
324
+ for (const page of next.pages) {
325
+ nextIndices.add(page.page.pageIndex);
326
+ }
327
+ const addedPages = [];
328
+ const removedPages = [];
329
+ const unchangedPages = [];
330
+ const changedPages = [];
331
+ for (const nextPage of next.pages) {
332
+ const pageIndex = nextPage.page.pageIndex;
333
+ const prevPage = prevByIndex.get(pageIndex);
334
+ if (!prevPage) {
335
+ addedPages.push(pageIndex);
336
+ continue;
337
+ }
338
+ const regions = diffPage(prevPage, nextPage, prev.decorationIndex, next.decorationIndex);
339
+ const frameChanged = !rectEquals(prevPage.frame, nextPage.frame);
340
+ const reservationsChanged = !reservationsEqual(
341
+ prevPage.chromeReservations,
342
+ nextPage.chromeReservations
343
+ );
344
+ if (regions.length === 0 && !frameChanged && !reservationsChanged) {
345
+ unchangedPages.push(pageIndex);
346
+ } else {
347
+ changedPages.push({
348
+ pageIndex,
349
+ regions,
350
+ ...frameChanged ? { pageFrameChanged: true } : {}
351
+ });
352
+ }
353
+ }
354
+ for (const prevPage of prev.pages) {
355
+ if (!nextIndices.has(prevPage.page.pageIndex)) {
356
+ removedPages.push(prevPage.page.pageIndex);
357
+ }
358
+ }
359
+ return { addedPages, removedPages, unchangedPages, changedPages };
360
+ }
361
+ function createPagePatchPlan(prev, next, diff = diffRenderFrames(prev, next), options = {}) {
362
+ const prevByIndex = indexPagesByIndex(prev?.pages ?? []);
363
+ const nextByIndex = indexPagesByIndex(next.pages);
364
+ const addedPages = diff.addedPages.map(
365
+ (pageIndex) => pageIdForIndex(nextByIndex, pageIndex)
366
+ );
367
+ const removedPages = diff.removedPages.map(
368
+ (pageIndex) => pageIdForIndex(prevByIndex, pageIndex)
369
+ );
370
+ const unchangedPages = diff.unchangedPages.map(
371
+ (pageIndex) => pageIdForIndex(nextByIndex, pageIndex)
372
+ );
373
+ const changedPages = diff.changedPages.map(
374
+ (entry) => createPagePatchEntry(entry, pageIdForIndex(nextByIndex, entry.pageIndex), options)
375
+ );
376
+ return {
377
+ layoutRevision: options.layoutRevision ?? next.revision,
378
+ geometryRevision: options.geometryRevision ?? next.revision,
379
+ addedPages,
380
+ removedPages,
381
+ unchangedPages,
382
+ changedPages,
383
+ mountChanges: resolveMountChanges({
384
+ addedPages,
385
+ removedPages,
386
+ mountedPageIds: options.mountedPageIds,
387
+ requestedMountPageIds: options.requestedMountPageIds
388
+ })
389
+ };
390
+ }
391
+ function createPagePatchEntry(entry, pageId, options) {
392
+ const regionChanges = entry.regions.map((region) => region.kind);
393
+ const fragmentChanges = uniqueStrings(
394
+ entry.regions.flatMap((region) => region.changedBlockIds)
395
+ );
396
+ const overlayLaneChanges = !entry.pageFrameChanged && entry.regions.length === 0 ? ["chrome-reservations"] : [];
397
+ return {
398
+ pageId,
399
+ pageIndex: entry.pageIndex,
400
+ frameChanged: entry.pageFrameChanged === true,
401
+ regionChanges,
402
+ fragmentChanges,
403
+ lineBoxChanges: fragmentChanges,
404
+ overlayLaneChanges,
405
+ reason: options.reason ?? (overlayLaneChanges.length > 0 ? "overlay" : "layout")
406
+ };
407
+ }
408
+ function resolveMountChanges(input) {
409
+ if (!input.mountedPageIds || !input.requestedMountPageIds) {
410
+ return {
411
+ mountPageIds: input.addedPages,
412
+ unmountPageIds: input.removedPages
413
+ };
414
+ }
415
+ const mounted = new Set(input.mountedPageIds);
416
+ const requested = new Set(input.requestedMountPageIds);
417
+ return {
418
+ mountPageIds: input.requestedMountPageIds.filter((pageId) => !mounted.has(pageId)),
419
+ unmountPageIds: input.mountedPageIds.filter((pageId) => !requested.has(pageId))
420
+ };
421
+ }
422
+ function indexPagesByIndex(pages) {
423
+ return new Map(pages.map((page) => [page.page.pageIndex, page]));
424
+ }
425
+ function pageIdForIndex(pages, pageIndex) {
426
+ return pages.get(pageIndex)?.page.pageId ?? `page:${pageIndex}`;
427
+ }
428
+ function uniqueStrings(values) {
429
+ return [...new Set(values)];
430
+ }
431
+ function diffPage(prev, next, prevIndex, nextIndex) {
432
+ const changed = [];
433
+ const bodyChanges = diffRegion(prev.regions.body, next.regions.body, prevIndex, nextIndex);
434
+ if (bodyChanges.length > 0) {
435
+ changed.push({ kind: "body", changedBlockIds: bodyChanges });
436
+ }
437
+ const headerChanges = diffOptionalRegion(
438
+ prev.regions.header,
439
+ next.regions.header,
440
+ prevIndex,
441
+ nextIndex
442
+ );
443
+ if (headerChanges.length > 0) {
444
+ changed.push({ kind: "header", changedBlockIds: headerChanges });
445
+ }
446
+ const footerChanges = diffOptionalRegion(
447
+ prev.regions.footer,
448
+ next.regions.footer,
449
+ prevIndex,
450
+ nextIndex
451
+ );
452
+ if (footerChanges.length > 0) {
453
+ changed.push({ kind: "footer", changedBlockIds: footerChanges });
454
+ }
455
+ const prevFoot = prev.regions.footnotes ?? [];
456
+ const nextFoot = next.regions.footnotes ?? [];
457
+ if (prevFoot.length !== nextFoot.length) {
458
+ changed.push({ kind: "footnote-area", changedBlockIds: ["<count-changed>"] });
459
+ } else {
460
+ for (let i = 0; i < prevFoot.length; i += 1) {
461
+ const fChanges = diffRegion(prevFoot[i], nextFoot[i], prevIndex, nextIndex);
462
+ if (fChanges.length > 0) {
463
+ changed.push({ kind: "footnote-area", changedBlockIds: fChanges });
464
+ }
465
+ }
466
+ }
467
+ return changed;
468
+ }
469
+ function diffOptionalRegion(prev, next, prevIndex, nextIndex) {
470
+ if (!prev && !next) return [];
471
+ if (!prev && next) return ["<added>"];
472
+ if (prev && !next) return ["<removed>"];
473
+ return diffRegion(prev, next, prevIndex, nextIndex);
474
+ }
475
+ function diffRegion(prev, next, prevIndex, nextIndex) {
476
+ const changed = [];
477
+ if (!rectEquals(prev.frame, next.frame)) {
478
+ changed.push("<region-frame>");
479
+ }
480
+ const prevBlocks = /* @__PURE__ */ new Map();
481
+ for (const block of prev.blocks) {
482
+ prevBlocks.set(block.fragment.blockId, block);
483
+ }
484
+ const nextIds = /* @__PURE__ */ new Set();
485
+ for (const block of next.blocks) {
486
+ nextIds.add(block.fragment.blockId);
487
+ const prevBlock = prevBlocks.get(block.fragment.blockId);
488
+ if (!prevBlock) {
489
+ changed.push(block.fragment.blockId);
490
+ continue;
491
+ }
492
+ if (!blocksStructurallyEqual(prevBlock, block, prevIndex, nextIndex)) {
493
+ changed.push(block.fragment.blockId);
494
+ }
495
+ }
496
+ for (const blockId of prevBlocks.keys()) {
497
+ if (!nextIds.has(blockId)) changed.push(blockId);
498
+ }
499
+ return changed;
500
+ }
501
+ function blocksStructurallyEqual(a, b, aIndex, bIndex) {
502
+ if (a.kind !== b.kind) return false;
503
+ if (a.fragment.regionKind !== b.fragment.regionKind) return false;
504
+ if (a.fragment.from !== b.fragment.from) return false;
505
+ if (a.fragment.to !== b.fragment.to) return false;
506
+ if (!rectEquals(a.frame, b.frame)) return false;
507
+ if (a.lines.length !== b.lines.length) return false;
508
+ for (let i = 0; i < a.lines.length; i += 1) {
509
+ if (!rectEquals(a.lines[i].frame, b.lines[i].frame)) return false;
510
+ }
511
+ const aHash = decorationHashForBlock(a.frame, aIndex);
512
+ const bHash = decorationHashForBlock(b.frame, bIndex);
513
+ if (aHash !== bHash) return false;
514
+ return true;
515
+ }
516
+ function decorationHashForBlock(blockFrame, index) {
517
+ const tokens = [];
518
+ for (const lane of [index.workflow, index.comments, index.revisions, index.search, index.locked]) {
519
+ for (const entry of lane) {
520
+ if (rectIntersects(entry.frame, blockFrame)) {
521
+ tokens.push(`${entry.kind}:${entry.refId}`);
522
+ }
523
+ }
524
+ }
525
+ tokens.sort();
526
+ return tokens.join("|");
527
+ }
528
+ function reservationsEqual(a, b) {
529
+ return a.railLaneTwips === b.railLaneTwips && a.balloonLaneTwips === b.balloonLaneTwips && a.footnoteAreaTwips === b.footnoteAreaTwips && a.pageFrameWidthPx === b.pageFrameWidthPx && a.pageFrameHeightPx === b.pageFrameHeightPx;
530
+ }
531
+ var RECT_EPS = 0.1;
532
+ function rectEquals(a, b) {
533
+ return Math.abs(a.leftPx - b.leftPx) < RECT_EPS && Math.abs(a.topPx - b.topPx) < RECT_EPS && Math.abs(a.widthPx - b.widthPx) < RECT_EPS && Math.abs(a.heightPx - b.heightPx) < RECT_EPS;
534
+ }
535
+ function rectIntersects(a, b) {
536
+ return !(a.leftPx + a.widthPx <= b.leftPx || b.leftPx + b.widthPx <= a.leftPx || a.topPx + a.heightPx <= b.topPx || b.topPx + b.heightPx <= a.topPx);
537
+ }
538
+
539
+ // src/runtime/render/render-frame-types.ts
540
+ var DEFAULT_PX_PER_TWIP = 96 / 1440;
541
+ var PAGE_STACK_GAP_PX = 48;
542
+ function resolveDefaultZoom(input = {}) {
543
+ return {
544
+ pxPerTwip: input.pxPerTwip ?? DEFAULT_PX_PER_TWIP,
545
+ viewportWidthPx: input.viewportWidthPx ?? 900,
546
+ fitMode: input.fitMode ?? "fixed"
547
+ };
548
+ }
549
+ var EMPTY_DECORATION_INDEX = Object.freeze({
550
+ workflow: Object.freeze([]),
551
+ comments: Object.freeze([]),
552
+ revisions: Object.freeze([]),
553
+ search: Object.freeze([]),
554
+ locked: Object.freeze([])
555
+ });
556
+ function defaultChromeReservations(layout, zoom) {
557
+ return {
558
+ railLaneTwips: 360,
559
+ balloonLaneTwips: 2160,
560
+ footnoteAreaTwips: 0,
561
+ pageFrameWidthPx: layout.pageWidth * zoom.pxPerTwip,
562
+ pageFrameHeightPx: layout.pageHeight * zoom.pxPerTwip
563
+ };
564
+ }
565
+
566
+ // src/runtime/render/render-kernel.ts
567
+ function sumDeltasBefore(deltas, runtimeOffset) {
568
+ let total = 0;
569
+ for (const d of deltas) {
570
+ if (d.fromRuntime <= runtimeOffset) total += d.delta;
571
+ }
572
+ return total;
573
+ }
574
+ function createRenderKernel(input) {
575
+ const { facet } = input;
576
+ const getActiveStory = input.getActiveStory ?? (() => MAIN_STORY_TARGET);
577
+ let zoom = input.initialZoom ?? resolveDefaultZoom();
578
+ let cache = null;
579
+ let lastEmittedFrame = null;
580
+ let diCacheKey = null;
581
+ let diCacheValue = null;
582
+ const listeners = /* @__PURE__ */ new Set();
583
+ const unsubscribeFacet = facet.subscribe((event) => {
584
+ if (event.kind === "layout_recomputed" || event.kind === "incremental_relayout" || event.kind === "measurement_backend_ready" || event.kind === "zoom_changed") {
585
+ cache = null;
586
+ }
587
+ });
588
+ void unsubscribeFacet;
589
+ function emit(event) {
590
+ for (const listener of listeners) {
591
+ try {
592
+ listener(event);
593
+ } catch {
594
+ }
595
+ }
596
+ }
597
+ function buildFrame(options) {
598
+ const t0 = typeof performance !== "undefined" ? performance.now() : 0;
599
+ const activeStory = options?.story ?? getActiveStory();
600
+ const measurementFidelity = facet.getMeasurementFidelity();
601
+ const rawPages = facet.getPages();
602
+ const pageRange = options?.pageRange;
603
+ const filteredPages = pageRange ? rawPages.filter(
604
+ (p) => p.pageIndex >= pageRange.fromPageIndex && p.pageIndex <= pageRange.toPageIndex
605
+ ) : rawPages;
606
+ let y = 0;
607
+ const renderPages = [];
608
+ for (const page of filteredPages) {
609
+ const renderPage = buildPage(page, y, zoom, activeStory, facet);
610
+ renderPages.push(renderPage);
611
+ y += renderPage.frame.heightPx + PAGE_STACK_GAP_PX;
612
+ }
613
+ const pendingDeltas = input.getPendingOpDeltas?.() ?? [];
614
+ const baseAnchorIndex = buildAnchorIndex(
615
+ renderPages,
616
+ pendingDeltas,
617
+ zoom.pxPerTwip
618
+ );
619
+ const revision = filteredPages[0] ? Number(extractRevisionFromPageId(filteredPages[0].pageId)) : 0;
620
+ const includeDecorations = options?.includeDecorations ?? true;
621
+ const sources = input.getDecorationSources?.();
622
+ const hasSources = sources !== void 0 && (sources.workflowSegments && sources.workflowSegments.length > 0 || (sources.comments?.threads?.length ?? 0) > 0 || (sources.revisions?.revisions?.length ?? 0) > 0 || sources.searchMatches && sources.searchMatches.length > 0 || sources.lockedRanges && sources.lockedRanges.length > 0);
623
+ const newDIKey = {
624
+ revision: Number.isFinite(revision) ? revision : 0,
625
+ activeStoryKind: activeStory.kind,
626
+ zoomPxPerTwip: zoom.pxPerTwip,
627
+ workflowSegments: sources?.workflowSegments,
628
+ comments: sources?.comments,
629
+ revisions: sources?.revisions,
630
+ searchMatches: sources?.searchMatches,
631
+ lockedRanges: sources?.lockedRanges
632
+ };
633
+ const diCacheHit = hasSources && diCacheKey !== null && diCacheValue !== null && diCacheKey.revision === newDIKey.revision && diCacheKey.activeStoryKind === newDIKey.activeStoryKind && diCacheKey.zoomPxPerTwip === newDIKey.zoomPxPerTwip && diCacheKey.workflowSegments === newDIKey.workflowSegments && diCacheKey.comments === newDIKey.comments && diCacheKey.revisions === newDIKey.revisions && diCacheKey.searchMatches === newDIKey.searchMatches && diCacheKey.lockedRanges === newDIKey.lockedRanges;
634
+ let decorationIndex;
635
+ if (!includeDecorations) {
636
+ decorationIndex = EMPTY_DECORATION_INDEX;
637
+ } else if (hasSources) {
638
+ if (diCacheHit) {
639
+ decorationIndex = diCacheValue;
640
+ } else {
641
+ decorationIndex = resolveDecorationIndex({ anchorIndex: baseAnchorIndex, ...sources });
642
+ diCacheKey = newDIKey;
643
+ diCacheValue = decorationIndex;
644
+ }
645
+ } else {
646
+ decorationIndex = buildDecorationIndex(renderPages);
647
+ }
648
+ const anchorIndex = buildAnchorIndex(
649
+ renderPages,
650
+ pendingDeltas,
651
+ zoom.pxPerTwip,
652
+ decorationIndex
653
+ );
654
+ const frame = {
655
+ revision: Number.isFinite(revision) ? revision : 0,
656
+ measurementFidelity,
657
+ activeStory,
658
+ zoom: { ...zoom },
659
+ pages: renderPages,
660
+ decorationIndex,
661
+ anchorIndex
662
+ };
663
+ if (t0 > 0) recordPerfSample("render.frame_build", performance.now() - t0);
664
+ return frame;
665
+ }
666
+ return {
667
+ getRenderFrame(options) {
668
+ const rebuild = options !== void 0 || cache === null;
669
+ if (!rebuild && cache) {
670
+ return cache.frame;
671
+ }
672
+ const frame = buildFrame(options);
673
+ if (options === void 0) {
674
+ cache = { revision: frame.revision, frame };
675
+ emit({ kind: "frame_built", revision: frame.revision, reason: "full" });
676
+ const diffT0 = typeof performance !== "undefined" ? performance.now() : 0;
677
+ const diff = diffRenderFrames(lastEmittedFrame, frame);
678
+ const patchPlan = createPagePatchPlan(lastEmittedFrame, frame, diff);
679
+ recordPagePatchPlanTelemetry(patchPlan);
680
+ if (diffT0 > 0) {
681
+ recordPerfSample("render.frame_diff", performance.now() - diffT0);
682
+ }
683
+ emit({ kind: "frame_diff", revision: frame.revision, diff, patchPlan });
684
+ lastEmittedFrame = frame;
685
+ }
686
+ return frame;
687
+ },
688
+ getZoom() {
689
+ return { ...zoom };
690
+ },
691
+ setZoom(next) {
692
+ zoom = { ...next };
693
+ cache = null;
694
+ },
695
+ subscribe(listener) {
696
+ listeners.add(listener);
697
+ return () => {
698
+ listeners.delete(listener);
699
+ };
700
+ },
701
+ invalidate() {
702
+ cache = null;
703
+ }
704
+ };
705
+ }
706
+ function recordPagePatchPlanTelemetry(plan) {
707
+ incrementInvalidationCounter(
708
+ "pageRender.patch.addedPages",
709
+ plan.addedPages.length
710
+ );
711
+ incrementInvalidationCounter(
712
+ "pageRender.patch.removedPages",
713
+ plan.removedPages.length
714
+ );
715
+ incrementInvalidationCounter(
716
+ "pageRender.patch.unchangedPages",
717
+ plan.unchangedPages.length
718
+ );
719
+ incrementInvalidationCounter(
720
+ "pageRender.patch.changedFragments",
721
+ plan.changedPages.reduce(
722
+ (total, page) => total + page.fragmentChanges.length,
723
+ 0
724
+ )
725
+ );
726
+ incrementInvalidationCounter(
727
+ "pageRender.patch.mountedPages",
728
+ plan.mountChanges.mountPageIds.length
729
+ );
730
+ incrementInvalidationCounter(
731
+ "pageRender.patch.unmountedPages",
732
+ plan.mountChanges.unmountPageIds.length
733
+ );
734
+ }
735
+ function buildPage(page, topPx, zoom, activeStory, facet) {
736
+ const layout = page.layout;
737
+ const widthPx = layout.pageWidth * zoom.pxPerTwip;
738
+ const heightPx = layout.pageHeight * zoom.pxPerTwip;
739
+ const frame = {
740
+ leftPx: 0,
741
+ topPx,
742
+ widthPx,
743
+ heightPx
744
+ };
745
+ const bodyRegion = buildBodyRegion(
746
+ page,
747
+ topPx,
748
+ zoom,
749
+ activeStory,
750
+ facet
751
+ );
752
+ const regions = {
753
+ body: bodyRegion
754
+ };
755
+ if (page.stories.header) {
756
+ regions.header = buildHeaderFooterRegion(
757
+ page,
758
+ topPx,
759
+ zoom,
760
+ "header",
761
+ page.stories.header,
762
+ facet
763
+ );
764
+ }
765
+ if (page.stories.footer) {
766
+ regions.footer = buildHeaderFooterRegion(
767
+ page,
768
+ topPx,
769
+ zoom,
770
+ "footer",
771
+ page.stories.footer,
772
+ facet
773
+ );
774
+ }
775
+ const footnoteRegions = buildFootnoteRegions(page, topPx, zoom, facet);
776
+ if (footnoteRegions.length > 0) {
777
+ regions.footnotes = footnoteRegions;
778
+ }
779
+ const chromeReservations = {
780
+ ...defaultChromeReservations(layout, zoom)
781
+ };
782
+ return {
783
+ page,
784
+ frame,
785
+ regions,
786
+ chromeReservations
787
+ };
788
+ }
789
+ function buildBodyRegion(page, pageTopPx, zoom, activeStory, facet) {
790
+ const layout = page.layout;
791
+ const bodyLeftTwips = layout.marginLeft;
792
+ const bodyTopTwips = layout.marginTop;
793
+ const bodyWidthTwips = page.regions.body.widthTwips;
794
+ const bodyHeightTwips = page.regions.body.heightTwips;
795
+ const regionFrame = {
796
+ leftPx: bodyLeftTwips * zoom.pxPerTwip,
797
+ topPx: pageTopPx + bodyTopTwips * zoom.pxPerTwip,
798
+ widthPx: bodyWidthTwips * zoom.pxPerTwip,
799
+ heightPx: bodyHeightTwips * zoom.pxPerTwip
800
+ };
801
+ const fragments = facet.getFragmentsForPage(page.pageIndex);
802
+ const bodyLineBoxes = facet.getLineBoxes(page.pageIndex, { region: "body" });
803
+ const blocks = [];
804
+ let blockY = regionFrame.topPx;
805
+ const totalFragmentHeight = fragments.reduce(
806
+ (acc, fragment) => acc + Math.max(0, fragment.heightTwips),
807
+ 0
808
+ );
809
+ for (const fragment of fragments) {
810
+ if (fragment.regionKind !== "body") continue;
811
+ const fragmentHeightTwips = fragment.heightTwips > 0 ? fragment.heightTwips : totalFragmentHeight === 0 ? bodyHeightTwips / Math.max(1, fragments.length) : 0;
812
+ const blockHeightPx = fragmentHeightTwips * zoom.pxPerTwip;
813
+ const fallbackBlockFrame = {
814
+ leftPx: regionFrame.leftPx,
815
+ topPx: blockY,
816
+ widthPx: regionFrame.widthPx,
817
+ heightPx: blockHeightPx
818
+ };
819
+ const blockLines = bodyLineBoxes.filter((box) => box.fragmentId === fragment.fragmentId).map((box) => {
820
+ const lineFrame = lineFrameFromPublicBox(
821
+ box,
822
+ regionFrame,
823
+ pageTopPx,
824
+ bodyTopTwips,
825
+ zoom
826
+ );
827
+ const anchors = [
828
+ {
829
+ runtimeOffset: fragment.from,
830
+ frame: lineFrame,
831
+ fragmentId: fragment.fragmentId,
832
+ blockId: fragment.blockId
833
+ }
834
+ ];
835
+ return {
836
+ line: box,
837
+ frame: lineFrame,
838
+ anchors
839
+ };
840
+ });
841
+ const kind = classifyBlockKind(fragment.blockId);
842
+ const blockFrame = blockFrameFromLineFacts(
843
+ fallbackBlockFrame,
844
+ kind,
845
+ blockLines
846
+ );
847
+ const tablePlan = kind === "table" ? facet.getTableRenderPlan(fragment.blockId, page.pageIndex) : void 0;
848
+ blocks.push({
849
+ fragment,
850
+ frame: blockFrame,
851
+ kind,
852
+ lines: blockLines,
853
+ blockDecorations: [],
854
+ ...tablePlan !== void 0 ? { tablePlan } : {}
855
+ });
856
+ blockY = Math.max(blockY + blockHeightPx, blockFrame.topPx + blockFrame.heightPx);
857
+ }
858
+ return {
859
+ storyTarget: activeStory,
860
+ region: page.regions.body,
861
+ frame: regionFrame,
862
+ blocks
863
+ };
864
+ }
865
+ function lineFrameFromPublicBox(box, regionFrame, pageTopPx, bodyTopTwips, zoom) {
866
+ if (box.rectTwips) {
867
+ return projectPageTwipsRectToFrame(box.rectTwips, pageTopPx, zoom);
868
+ }
869
+ const lineTopPx = pageTopPx + (bodyTopTwips + box.baselineTwips) * zoom.pxPerTwip;
870
+ return {
871
+ leftPx: regionFrame.leftPx,
872
+ topPx: lineTopPx,
873
+ widthPx: Math.min(
874
+ regionFrame.widthPx,
875
+ box.widthTwips * zoom.pxPerTwip
876
+ ),
877
+ heightPx: box.heightTwips * zoom.pxPerTwip
878
+ };
879
+ }
880
+ function projectPageTwipsRectToFrame(rect2, pageTopPx, zoom) {
881
+ return {
882
+ leftPx: rect2.xTwips * zoom.pxPerTwip,
883
+ topPx: pageTopPx + rect2.yTwips * zoom.pxPerTwip,
884
+ widthPx: rect2.widthTwips * zoom.pxPerTwip,
885
+ heightPx: rect2.heightTwips * zoom.pxPerTwip
886
+ };
887
+ }
888
+ function blockFrameFromLineFacts(fallback, kind, lines) {
889
+ const union = unionRenderLineFrames(lines);
890
+ if (!union) return fallback;
891
+ if (kind === "paragraph") return union;
892
+ if (fallback.widthPx <= 0 || fallback.heightPx <= 0) return union;
893
+ return fallback;
894
+ }
895
+ function unionRenderLineFrames(lines) {
896
+ let left = Number.POSITIVE_INFINITY;
897
+ let top = Number.POSITIVE_INFINITY;
898
+ let right = Number.NEGATIVE_INFINITY;
899
+ let bottom = Number.NEGATIVE_INFINITY;
900
+ for (const line of lines) {
901
+ if (line.frame.widthPx <= 0 || line.frame.heightPx <= 0) continue;
902
+ left = Math.min(left, line.frame.leftPx);
903
+ top = Math.min(top, line.frame.topPx);
904
+ right = Math.max(right, line.frame.leftPx + line.frame.widthPx);
905
+ bottom = Math.max(bottom, line.frame.topPx + line.frame.heightPx);
906
+ }
907
+ if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
908
+ return {
909
+ leftPx: left,
910
+ topPx: top,
911
+ widthPx: Math.max(0, right - left),
912
+ heightPx: Math.max(0, bottom - top)
913
+ };
914
+ }
915
+ function buildHeaderFooterRegion(page, pageTopPx, zoom, kind, storyTarget, facet) {
916
+ const layout = page.layout;
917
+ const fallbackWidthTwips = layout.pageWidth - layout.marginLeft - layout.marginRight;
918
+ const fallbackTopTwips = kind === "header" ? layout.headerMargin ?? 720 : layout.pageHeight - layout.marginBottom;
919
+ const fallbackHeightTwips = kind === "header" ? Math.max(0, layout.marginTop - (layout.headerMargin ?? 720)) : Math.max(0, layout.marginBottom - (layout.footerMargin ?? 720));
920
+ const region = (kind === "header" ? page.regions.header : page.regions.footer) ?? {
921
+ kind,
922
+ originTwips: fallbackTopTwips,
923
+ widthTwips: fallbackWidthTwips,
924
+ heightTwips: fallbackHeightTwips,
925
+ fragmentCount: 0
926
+ };
927
+ const frame = {
928
+ leftPx: layout.marginLeft * zoom.pxPerTwip,
929
+ topPx: pageTopPx + region.originTwips * zoom.pxPerTwip,
930
+ widthPx: region.widthTwips * zoom.pxPerTwip,
931
+ heightPx: region.heightTwips * zoom.pxPerTwip
932
+ };
933
+ const regionBlocks = facet.getStoryBlocksForRegion(page.pageIndex, kind);
934
+ const blocks = projectRegionBlocks(
935
+ regionBlocks,
936
+ frame,
937
+ zoom.pxPerTwip,
938
+ page.pageId,
939
+ page.pageIndex,
940
+ kind
941
+ );
942
+ return {
943
+ storyTarget,
944
+ region,
945
+ frame,
946
+ blocks
947
+ };
948
+ }
949
+ function buildFootnoteRegions(page, pageTopPx, zoom, facet) {
950
+ const footnoteRegions = page.regions.footnotes;
951
+ if (!footnoteRegions || footnoteRegions.length === 0) return [];
952
+ const regionBlocks = facet.getStoryBlocksForRegion(
953
+ page.pageIndex,
954
+ "footnote-area"
955
+ );
956
+ if (regionBlocks.length === 0) return [];
957
+ const results = [];
958
+ let cursor = 0;
959
+ for (const region of footnoteRegions) {
960
+ const frame = {
961
+ leftPx: page.layout.marginLeft * zoom.pxPerTwip,
962
+ topPx: pageTopPx + region.originTwips * zoom.pxPerTwip,
963
+ widthPx: region.widthTwips * zoom.pxPerTwip,
964
+ heightPx: region.heightTwips * zoom.pxPerTwip
965
+ };
966
+ const blocksForThisRegion = footnoteRegions.length === 1 ? regionBlocks : regionBlocks.slice(cursor, cursor + region.fragmentCount);
967
+ cursor += region.fragmentCount;
968
+ const blocks = projectRegionBlocks(
969
+ blocksForThisRegion,
970
+ frame,
971
+ zoom.pxPerTwip,
972
+ page.pageId,
973
+ page.pageIndex,
974
+ "footnote-area"
975
+ );
976
+ const firstNote = page.noteAllocations.find(
977
+ (alloc) => alloc.noteKind === "footnote"
978
+ );
979
+ const storyTarget = firstNote ? { kind: "footnote", noteId: firstNote.noteId } : MAIN_STORY_TARGET;
980
+ results.push({
981
+ storyTarget,
982
+ region,
983
+ frame,
984
+ blocks
985
+ });
986
+ }
987
+ return results;
988
+ }
989
+ function projectRegionBlocks(regionBlocks, regionFrame, pxPerTwip, pageId, pageIndex, regionKind) {
990
+ const blocks = [];
991
+ let y = regionFrame.topPx;
992
+ for (let i = 0; i < regionBlocks.length; i += 1) {
993
+ const regionBlock = regionBlocks[i];
994
+ const blockHeightPx = Math.max(0, regionBlock.heightTwips) * pxPerTwip;
995
+ const blockFrame = {
996
+ leftPx: regionFrame.leftPx,
997
+ topPx: y,
998
+ widthPx: regionFrame.widthPx,
999
+ heightPx: blockHeightPx
1000
+ };
1001
+ const fragment = {
1002
+ fragmentId: regionBlock.fragmentId,
1003
+ blockId: regionBlock.blockId,
1004
+ pageId,
1005
+ pageIndex,
1006
+ regionKind,
1007
+ from: regionBlock.runtimeFromOffset,
1008
+ to: regionBlock.runtimeToOffset,
1009
+ heightTwips: regionBlock.heightTwips,
1010
+ orderInRegion: i
1011
+ };
1012
+ blocks.push({
1013
+ fragment,
1014
+ frame: blockFrame,
1015
+ kind: classifyBlockKind(regionBlock.blockId),
1016
+ lines: [],
1017
+ blockDecorations: []
1018
+ });
1019
+ y += blockHeightPx;
1020
+ }
1021
+ return blocks;
1022
+ }
1023
+ function buildAnchorIndex(pages, pendingDeltas = [], pxPerTwip = 1, decorationIndex = EMPTY_DECORATION_INDEX) {
1024
+ const byRuntimeOffset = /* @__PURE__ */ new Map();
1025
+ const byRuntimeOffsetByStory = /* @__PURE__ */ new Map();
1026
+ const byFragmentId = /* @__PURE__ */ new Map();
1027
+ const byBlockId = /* @__PURE__ */ new Map();
1028
+ const byPageIndex = /* @__PURE__ */ new Map();
1029
+ const tableCellRects = /* @__PURE__ */ new Map();
1030
+ const tableColumnEdges = /* @__PURE__ */ new Map();
1031
+ const tableRowEdges = /* @__PURE__ */ new Map();
1032
+ const recordBlock = (block, offsetMap) => {
1033
+ byFragmentId.set(block.fragment.fragmentId, block.frame);
1034
+ byBlockId.set(block.fragment.blockId, block.frame);
1035
+ offsetMap.set(block.fragment.from, block.frame);
1036
+ for (const line of block.lines) {
1037
+ for (const anchor of line.anchors) {
1038
+ offsetMap.set(anchor.runtimeOffset, anchor.frame);
1039
+ }
1040
+ }
1041
+ if (block.kind === "table" && block.tablePlan) {
1042
+ recordTableAnchors(
1043
+ block.fragment.blockId,
1044
+ block.frame,
1045
+ block.tablePlan,
1046
+ pxPerTwip,
1047
+ tableCellRects,
1048
+ tableColumnEdges,
1049
+ tableRowEdges
1050
+ );
1051
+ }
1052
+ };
1053
+ const recordStoryRegion = (region) => {
1054
+ const storyKey = storyTargetKey(region.storyTarget);
1055
+ let storyMap = byRuntimeOffsetByStory.get(storyKey);
1056
+ if (!storyMap) {
1057
+ storyMap = /* @__PURE__ */ new Map();
1058
+ byRuntimeOffsetByStory.set(storyKey, storyMap);
1059
+ }
1060
+ for (const block of region.blocks) {
1061
+ recordBlock(block, storyMap);
1062
+ }
1063
+ };
1064
+ for (const page of pages) {
1065
+ byPageIndex.set(page.page.pageIndex, page.frame);
1066
+ for (const block of page.regions.body.blocks) {
1067
+ recordBlock(block, byRuntimeOffset);
1068
+ }
1069
+ if (page.regions.header) recordStoryRegion(page.regions.header);
1070
+ if (page.regions.footer) recordStoryRegion(page.regions.footer);
1071
+ if (page.regions.footnotes) {
1072
+ for (const footnote of page.regions.footnotes) {
1073
+ recordStoryRegion(footnote);
1074
+ }
1075
+ }
1076
+ }
1077
+ const shiftForDeltas = (offset) => {
1078
+ if (pendingDeltas.length === 0) return offset;
1079
+ return offset - sumDeltasBefore(pendingDeltas, offset);
1080
+ };
1081
+ const resolveByRuntimeOffset = (offset, story) => {
1082
+ const lookup = shiftForDeltas(offset);
1083
+ const targetMap = story && story.kind !== "main" ? byRuntimeOffsetByStory.get(storyTargetKey(story)) ?? byRuntimeOffset : byRuntimeOffset;
1084
+ const exact = targetMap.get(lookup);
1085
+ if (exact) return exact;
1086
+ let best = null;
1087
+ let bestDistance = Number.POSITIVE_INFINITY;
1088
+ for (const [key, rect2] of targetMap) {
1089
+ const distance = Math.abs(key - lookup);
1090
+ if (distance < bestDistance) {
1091
+ best = rect2;
1092
+ bestDistance = distance;
1093
+ }
1094
+ }
1095
+ return best;
1096
+ };
1097
+ return {
1098
+ byRuntimeOffset(offset, story) {
1099
+ return resolveByRuntimeOffset(offset, story);
1100
+ },
1101
+ byBlockId(blockId) {
1102
+ return byBlockId.get(blockId) ?? null;
1103
+ },
1104
+ byFragmentId(fragmentId) {
1105
+ return byFragmentId.get(fragmentId) ?? null;
1106
+ },
1107
+ byPageIndex(pageIndex) {
1108
+ return byPageIndex.get(pageIndex) ?? null;
1109
+ },
1110
+ bySelection(fromOffset, toOffset, story) {
1111
+ const lo = Math.min(fromOffset, toOffset);
1112
+ const hi = Math.max(fromOffset, toOffset);
1113
+ if (lo === hi) {
1114
+ return resolveByRuntimeOffset(lo, story);
1115
+ }
1116
+ const loShifted = shiftForDeltas(lo);
1117
+ const hiShifted = shiftForDeltas(hi);
1118
+ const targetMap = story && story.kind !== "main" ? byRuntimeOffsetByStory.get(storyTargetKey(story)) ?? byRuntimeOffset : byRuntimeOffset;
1119
+ let union = null;
1120
+ for (const [key, rect2] of targetMap) {
1121
+ if (key < loShifted || key >= hiShifted) continue;
1122
+ union = unionRects(union, rect2);
1123
+ }
1124
+ if (union) return union;
1125
+ const fromRect = resolveByRuntimeOffset(lo, story);
1126
+ const toRect = resolveByRuntimeOffset(hi - 1, story);
1127
+ return unionRects(fromRect, toRect);
1128
+ },
1129
+ byTableCell(tableBlockId, rowIndex, columnIndex) {
1130
+ return tableCellRects.get(`${tableBlockId}:${rowIndex}:${columnIndex}`) ?? null;
1131
+ },
1132
+ byTableColumnEdge(tableBlockId, columnIndex) {
1133
+ return tableColumnEdges.get(`${tableBlockId}:${columnIndex}`) ?? null;
1134
+ },
1135
+ byTableRowEdge(tableBlockId, rowIndex) {
1136
+ return tableRowEdges.get(`${tableBlockId}:${rowIndex}`) ?? null;
1137
+ },
1138
+ // P9 Phase A — chrome-kind resolvers sourced from the resolved
1139
+ // decoration index. Empty by default (the initial frame build passes
1140
+ // `EMPTY_DECORATION_INDEX` until decoration resolution runs); the
1141
+ // kernel re-invokes `buildAnchorIndex` with the resolved index so the
1142
+ // final anchor index carries chrome-aware lookups.
1143
+ byScopeId(scopeId) {
1144
+ return decorationIndex.workflow.filter((decoration) => decoration.refId === scopeId).map((decoration) => decoration.frame);
1145
+ },
1146
+ byCommentId(commentId) {
1147
+ const match = decorationIndex.comments.find(
1148
+ (decoration) => decoration.refId === commentId
1149
+ );
1150
+ return match?.frame ?? null;
1151
+ },
1152
+ byRevisionId(revisionId) {
1153
+ const match = decorationIndex.revisions.find(
1154
+ (decoration) => decoration.refId === revisionId
1155
+ );
1156
+ return match?.frame ?? null;
1157
+ },
1158
+ // Refactor/05 Slice 7c substrate: object-specific index. Falls back
1159
+ // to `byBlockId` because the render kernel does not yet carry
1160
+ // per-object metadata on anchor frames — the layout facet would
1161
+ // need to emit `byObjectId` entries on the page graph. When that
1162
+ // follow-up ships, populate a dedicated `byObjectIdMap` here and
1163
+ // consult it before the `byBlockId` fallback. The fallback is
1164
+ // correct for block-level drawings (whose object id equals their
1165
+ // block id); it is `heuristic` for inline images (which share
1166
+ // their block id with the enclosing paragraph). `object-handles.ts`
1167
+ // tags those handles `precision: "heuristic"` accordingly.
1168
+ byObjectId(objectId) {
1169
+ return byBlockId.get(objectId) ?? null;
1170
+ }
1171
+ };
1172
+ }
1173
+ function recordTableAnchors(tableBlockId, blockFrame, plan, pxPerTwip, cellRects, columnEdges, rowEdges) {
1174
+ if (!plan) return;
1175
+ const columnCount = plan.columnsTwips.length;
1176
+ if (columnCount === 0) return;
1177
+ const columnLeftsPx = [blockFrame.leftPx];
1178
+ for (let i = 0; i < columnCount; i += 1) {
1179
+ columnLeftsPx.push(
1180
+ columnLeftsPx[i] + (plan.columnsTwips[i] ?? 0) * pxPerTwip
1181
+ );
1182
+ }
1183
+ const rowCount = Math.max(
1184
+ 1,
1185
+ plan.bandClasses.rows.length || plan.bandClasses.cells.reduce(
1186
+ (max, c) => Math.max(max, c.rowIndex + 1),
1187
+ 0
1188
+ )
1189
+ );
1190
+ const rowHeightPx = rowCount > 0 ? blockFrame.heightPx / rowCount : blockFrame.heightPx;
1191
+ const rowTopsPx = [];
1192
+ for (let r = 0; r <= rowCount; r += 1) {
1193
+ rowTopsPx.push(blockFrame.topPx + r * rowHeightPx);
1194
+ }
1195
+ for (const cell of plan.bandClasses.cells) {
1196
+ const sameRow = plan.bandClasses.cells.filter((c) => c.rowIndex === cell.rowIndex);
1197
+ const sorted = [...sameRow].sort((a, b) => a.columnIndex - b.columnIndex);
1198
+ const idx = sorted.findIndex((c) => c === cell);
1199
+ const next = sorted[idx + 1];
1200
+ const columnSpan = (next?.columnIndex ?? columnCount) - cell.columnIndex;
1201
+ const left = columnLeftsPx[cell.columnIndex] ?? blockFrame.leftPx;
1202
+ const right = columnLeftsPx[cell.columnIndex + columnSpan] ?? blockFrame.leftPx + blockFrame.widthPx;
1203
+ const top = rowTopsPx[cell.rowIndex] ?? blockFrame.topPx;
1204
+ const bottom = rowTopsPx[cell.rowIndex + 1] ?? blockFrame.topPx + blockFrame.heightPx;
1205
+ cellRects.set(`${tableBlockId}:${cell.rowIndex}:${cell.columnIndex}`, {
1206
+ leftPx: left,
1207
+ topPx: top,
1208
+ widthPx: Math.max(0, right - left),
1209
+ heightPx: Math.max(0, bottom - top)
1210
+ });
1211
+ }
1212
+ for (const handle of plan.columnResizeHandles) {
1213
+ const x = blockFrame.leftPx + handle.originTwips * pxPerTwip;
1214
+ columnEdges.set(`${tableBlockId}:${handle.columnIndex}`, {
1215
+ leftPx: x,
1216
+ topPx: blockFrame.topPx,
1217
+ widthPx: 0,
1218
+ heightPx: handle.heightTwips * pxPerTwip
1219
+ });
1220
+ }
1221
+ for (let r = 0; r < rowCount - 1; r += 1) {
1222
+ const y = rowTopsPx[r + 1] ?? blockFrame.topPx + blockFrame.heightPx;
1223
+ rowEdges.set(`${tableBlockId}:${r}`, {
1224
+ leftPx: blockFrame.leftPx,
1225
+ topPx: y,
1226
+ widthPx: blockFrame.widthPx,
1227
+ heightPx: 0
1228
+ });
1229
+ }
1230
+ }
1231
+ function unionRects(a, b) {
1232
+ if (!a) return b ?? null;
1233
+ if (!b) return a;
1234
+ const left = Math.min(a.leftPx, b.leftPx);
1235
+ const top = Math.min(a.topPx, b.topPx);
1236
+ const right = Math.max(a.leftPx + a.widthPx, b.leftPx + b.widthPx);
1237
+ const bottom = Math.max(a.topPx + a.heightPx, b.topPx + b.heightPx);
1238
+ return {
1239
+ leftPx: left,
1240
+ topPx: top,
1241
+ widthPx: right - left,
1242
+ heightPx: bottom - top
1243
+ };
1244
+ }
1245
+ function buildDecorationIndex(pages) {
1246
+ const workflow = [];
1247
+ const comments = [];
1248
+ const revisions = [];
1249
+ const search = [];
1250
+ const locked = [];
1251
+ for (const page of pages) {
1252
+ for (const block of page.regions.body.blocks) {
1253
+ for (const decoration of block.blockDecorations) {
1254
+ switch (decoration.kind) {
1255
+ case "workflow":
1256
+ workflow.push(decoration);
1257
+ break;
1258
+ case "comment":
1259
+ comments.push(decoration);
1260
+ break;
1261
+ case "revision":
1262
+ revisions.push(decoration);
1263
+ break;
1264
+ case "search":
1265
+ search.push(decoration);
1266
+ break;
1267
+ case "locked":
1268
+ locked.push(decoration);
1269
+ break;
1270
+ }
1271
+ }
1272
+ }
1273
+ }
1274
+ return { workflow, comments, revisions, search, locked };
1275
+ }
1276
+ function extractRevisionFromPageId(pageId) {
1277
+ const match = /^page-(\d+)-/.exec(pageId);
1278
+ if (!match) return pageId;
1279
+ return Number(match[1]);
1280
+ }
1281
+
113
1282
  // src/core/selection/review-anchors.ts
114
1283
  function detachReviewAnchor(lastKnownRange, reason) {
115
1284
  return createDetachedAnchor(lastKnownRange, reason);
@@ -5112,6 +6281,11 @@ function freezeNumberingLayoutFacts(numbering) {
5112
6281
  if (numbering.numberingSourceRef) Object.freeze(numbering.numberingSourceRef);
5113
6282
  if (numbering.numberingInstanceSourceRef) Object.freeze(numbering.numberingInstanceSourceRef);
5114
6283
  if (numbering.abstractNumberingSourceRef) Object.freeze(numbering.abstractNumberingSourceRef);
6284
+ if (numbering.markerTextPosture) Object.freeze(numbering.markerTextPosture);
6285
+ if (numbering.pictureBulletPosture) {
6286
+ if (numbering.pictureBulletPosture.sourceRef) Object.freeze(numbering.pictureBulletPosture.sourceRef);
6287
+ Object.freeze(numbering.pictureBulletPosture);
6288
+ }
5115
6289
  if (numbering.markerLane) Object.freeze(numbering.markerLane);
5116
6290
  if (numbering.textColumn) Object.freeze(numbering.textColumn);
5117
6291
  if (numbering.tabStops) {
@@ -6087,8 +7261,17 @@ function collectNumberingLayoutFacts(block) {
6087
7261
  ...block.resolvedNumbering?.format !== void 0 ? { format: block.resolvedNumbering.format } : {},
6088
7262
  ...block.resolvedNumbering?.formatPosture !== void 0 ? { formatPosture: { ...block.resolvedNumbering.formatPosture } } : {},
6089
7263
  ...block.numberingPrefix !== void 0 ? { markerText: block.numberingPrefix } : {},
7264
+ ...block.resolvedNumbering?.markerTextPosture !== void 0 ? { markerTextPosture: { ...block.resolvedNumbering.markerTextPosture } } : {},
6090
7265
  ...block.numberingSuffix !== void 0 ? { markerSuffix: block.numberingSuffix } : {},
6091
7266
  ...block.resolvedNumbering?.geometry.markerJustification !== void 0 ? { markerJustification: block.resolvedNumbering.geometry.markerJustification } : {},
7267
+ ...block.resolvedNumbering?.picBulletId !== void 0 ? { picBulletId: block.resolvedNumbering.picBulletId } : {},
7268
+ ...block.resolvedNumbering?.picBulletMediaId !== void 0 ? { picBulletMediaId: block.resolvedNumbering.picBulletMediaId } : {},
7269
+ ...block.resolvedNumbering?.pictureBulletPosture !== void 0 ? {
7270
+ pictureBulletPosture: {
7271
+ ...block.resolvedNumbering.pictureBulletPosture,
7272
+ ...block.resolvedNumbering.pictureBulletPosture.sourceRef !== void 0 ? { sourceRef: { ...block.resolvedNumbering.pictureBulletPosture.sourceRef } } : {}
7273
+ }
7274
+ } : {},
6092
7275
  ...markerLane ? {
6093
7276
  markerLane: {
6094
7277
  startTwips: markerLane.start,
@@ -6902,8 +8085,8 @@ function fnv1a(input) {
6902
8085
  }
6903
8086
 
6904
8087
  // src/runtime/layout/layout-engine-version.ts
6905
- var LAYOUT_ENGINE_VERSION = 93;
6906
- var LAYCACHE_SCHEMA_VERSION = 12;
8088
+ var LAYOUT_ENGINE_VERSION = 94;
8089
+ var LAYCACHE_SCHEMA_VERSION = 13;
6907
8090
 
6908
8091
  // src/runtime/layout/layout-engine-instance.ts
6909
8092
  var FULL_VIEWPORT_WINDOW_KEY = "full";
@@ -7129,11 +8312,45 @@ function createLayoutEngine(options = {}) {
7129
8312
  const telemetryBus = options.telemetryBus;
7130
8313
  const dirtyFieldFamilies = /* @__PURE__ */ new Set();
7131
8314
  const listeners = /* @__PURE__ */ new Set();
7132
- let cachedKey = null;
7133
- let cachedGraph = null;
7134
- let cachedFormatting = null;
7135
- let cachedMapper = null;
8315
+ let cachedFull = null;
8316
+ let cachedWindowed = null;
7136
8317
  let previousPageCount = 0;
8318
+ function isFullViewportKey(key) {
8319
+ return key === FULL_VIEWPORT_WINDOW_KEY;
8320
+ }
8321
+ function getCachedSlot(viewportWindowKeyValue) {
8322
+ return isFullViewportKey(viewportWindowKeyValue) ? cachedFull : cachedWindowed;
8323
+ }
8324
+ function preferredCachedGraph() {
8325
+ return cachedFull?.graph ?? cachedWindowed?.graph ?? null;
8326
+ }
8327
+ function clearAllSlots() {
8328
+ cachedFull = null;
8329
+ cachedWindowed = null;
8330
+ }
8331
+ function clearWindowedSlot() {
8332
+ cachedWindowed = null;
8333
+ }
8334
+ function evictStaleSiblingSlot(freshKey) {
8335
+ const sibling = isFullViewportKey(freshKey.viewportWindowKey) ? cachedWindowed : cachedFull;
8336
+ if (sibling === null) return;
8337
+ if (sibling.key.content === freshKey.content && sibling.key.styles === freshKey.styles && sibling.key.subParts === freshKey.subParts) {
8338
+ return;
8339
+ }
8340
+ if (isFullViewportKey(freshKey.viewportWindowKey)) {
8341
+ cachedWindowed = null;
8342
+ } else {
8343
+ cachedFull = null;
8344
+ }
8345
+ }
8346
+ function commitSlot(slot) {
8347
+ if (isFullViewportKey(slot.key.viewportWindowKey)) {
8348
+ cachedFull = slot;
8349
+ } else {
8350
+ cachedWindowed = slot;
8351
+ }
8352
+ evictStaleSiblingSlot(slot.key);
8353
+ }
7137
8354
  let pendingInvalidation = null;
7138
8355
  function emit(event) {
7139
8356
  for (const listener of listeners) {
@@ -7279,27 +8496,31 @@ function createLayoutEngine(options = {}) {
7279
8496
  subParts: document2.subParts,
7280
8497
  anchors: layoutInputs.anchors
7281
8498
  });
8499
+ const priorGraphForMaterialization = preferredCachedGraph();
7282
8500
  const graph = applyViewportWindowMaterialization(
7283
8501
  measuredGraph,
7284
8502
  viewportWindow,
7285
- cachedGraph
8503
+ priorGraphForMaterialization
7286
8504
  );
7287
- const dirtyFamilies = computeFieldDirtiness(cachedGraph, graph);
8505
+ const priorGraphForFieldDirtiness = preferredCachedGraph();
8506
+ const dirtyFamilies = computeFieldDirtiness(priorGraphForFieldDirtiness, graph);
7288
8507
  for (const family of dirtyFamilies) {
7289
8508
  dirtyFieldFamilies.add(family);
7290
8509
  }
7291
8510
  const formatting = buildResolvedFormattingState(document2, mainSurface);
7292
8511
  const currentPageCount = graph.contentPageCount;
7293
8512
  const pageCountDelta = currentPageCount !== previousPageCount ? { previous: previousPageCount, current: currentPageCount } : void 0;
7294
- cachedKey = {
7295
- content: document2.content,
7296
- styles: document2.styles,
7297
- subParts: document2.subParts,
7298
- viewportWindowKey: viewportWindowKey(viewportWindow)
7299
- };
7300
- cachedGraph = graph;
7301
- cachedFormatting = formatting;
7302
- cachedMapper = createPageFragmentMapper(graph);
8513
+ commitSlot({
8514
+ key: {
8515
+ content: document2.content,
8516
+ styles: document2.styles,
8517
+ subParts: document2.subParts,
8518
+ viewportWindowKey: viewportWindowKey(viewportWindow)
8519
+ },
8520
+ graph,
8521
+ formatting,
8522
+ mapper: createPageFragmentMapper(graph)
8523
+ });
7303
8524
  if (pageCountDelta) {
7304
8525
  emit({
7305
8526
  kind: "page_count_changed",
@@ -7340,7 +8561,7 @@ function createLayoutEngine(options = {}) {
7340
8561
  return graph;
7341
8562
  }
7342
8563
  function incrementalRelayout(input, pending) {
7343
- const priorGraph = cachedGraph;
8564
+ const priorGraph = cachedFull?.graph ?? null;
7344
8565
  const range = pending.result.dirtyPageRange;
7345
8566
  if (!priorGraph || !range) return null;
7346
8567
  const telemetryOn = telemetryBus?.isEnabled("layout") ?? false;
@@ -7433,6 +8654,22 @@ function createLayoutEngine(options = {}) {
7433
8654
  deriveDocumentPageSnapshots(splicedGraph)
7434
8655
  );
7435
8656
  const pageCountDelta = currentPageCount !== previousPageCount ? { previous: previousPageCount, current: currentPageCount } : void 0;
8657
+ const priorMapper = cachedFull?.mapper ?? null;
8658
+ commitSlot({
8659
+ key: {
8660
+ content: document2.content,
8661
+ styles: document2.styles,
8662
+ subParts: document2.subParts,
8663
+ viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
8664
+ },
8665
+ graph: splicedGraph,
8666
+ formatting,
8667
+ mapper: rebuildMapper(
8668
+ priorMapper ?? createPageFragmentMapper(splicedGraph),
8669
+ splicedGraph,
8670
+ firstDirty
8671
+ )
8672
+ });
7436
8673
  if (pageCountDelta) {
7437
8674
  emit({
7438
8675
  kind: "page_count_changed",
@@ -7462,19 +8699,6 @@ function createLayoutEngine(options = {}) {
7462
8699
  ...dirtyFamilies.length > 0 ? { dirtyFieldFamilies: dirtyFamilies } : {},
7463
8700
  ...pageCountDelta ? { pageCountDelta } : {}
7464
8701
  });
7465
- cachedKey = {
7466
- content: document2.content,
7467
- styles: document2.styles,
7468
- subParts: document2.subParts,
7469
- viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
7470
- };
7471
- cachedGraph = splicedGraph;
7472
- cachedFormatting = formatting;
7473
- cachedMapper = rebuildMapper(
7474
- cachedMapper ?? createPageFragmentMapper(splicedGraph),
7475
- splicedGraph,
7476
- firstDirty
7477
- );
7478
8702
  if (telemetryOn) {
7479
8703
  emitRecomputeCompleted(
7480
8704
  "bounded",
@@ -7491,13 +8715,14 @@ function createLayoutEngine(options = {}) {
7491
8715
  const document2 = input.document;
7492
8716
  const normalizedWindow = normalizeViewportPageWindow(input.viewportPageWindow);
7493
8717
  const currentViewportWindowKey = viewportWindowKey(normalizedWindow);
7494
- const keyEqual = cachedGraph !== null && cachedKey !== null && cachedKey.content === document2.content && cachedKey.styles === document2.styles && cachedKey.subParts === document2.subParts && cachedKey.viewportWindowKey === currentViewportWindowKey;
8718
+ const slot = getCachedSlot(currentViewportWindowKey);
8719
+ const keyEqual = slot !== null && slot.key.content === document2.content && slot.key.styles === document2.styles && slot.key.subParts === document2.subParts && slot.key.viewportWindowKey === currentViewportWindowKey;
7495
8720
  if (keyEqual && pendingInvalidation === null) {
7496
- return cachedGraph;
8721
+ return slot.graph;
7497
8722
  }
7498
8723
  const pending = pendingInvalidation;
7499
8724
  pendingInvalidation = null;
7500
- if (pending !== null && pending.result.scope === "bounded" && cachedGraph !== null && normalizedWindow === void 0) {
8725
+ if (pending !== null && pending.result.scope === "bounded" && cachedFull !== null && normalizedWindow === void 0) {
7501
8726
  const spliced = incrementalRelayout(input, pending);
7502
8727
  if (spliced !== null) {
7503
8728
  return spliced;
@@ -7506,16 +8731,39 @@ function createLayoutEngine(options = {}) {
7506
8731
  }
7507
8732
  return fullRebuild(input, pending?.reason);
7508
8733
  }
8734
+ function ensureSlotMapperAndFormatting(slot, document2) {
8735
+ if (slot.mapper === null) {
8736
+ slot.mapper = createPageFragmentMapper(slot.graph);
8737
+ }
8738
+ if (slot.formatting === null) {
8739
+ const mainSurface = createEditorSurfaceSnapshot(
8740
+ document2,
8741
+ createSelectionSnapshot(0, 0),
8742
+ MAIN_STORY_TARGET
8743
+ );
8744
+ slot.formatting = buildResolvedFormattingState(document2, mainSurface);
8745
+ }
8746
+ }
7509
8747
  function getMapper(input) {
7510
8748
  getGraphInternal(input);
7511
- return cachedMapper;
8749
+ const currentViewportWindowKey = viewportWindowKey(
8750
+ normalizeViewportPageWindow(input.viewportPageWindow)
8751
+ );
8752
+ const slot = getCachedSlot(currentViewportWindowKey);
8753
+ ensureSlotMapperAndFormatting(slot, input.document);
8754
+ return slot.mapper;
7512
8755
  }
7513
8756
  function getFormatting(input) {
7514
8757
  getGraphInternal(input);
7515
- return cachedFormatting;
8758
+ const currentViewportWindowKey = viewportWindowKey(
8759
+ normalizeViewportPageWindow(input.viewportPageWindow)
8760
+ );
8761
+ const slot = getCachedSlot(currentViewportWindowKey);
8762
+ ensureSlotMapperAndFormatting(slot, input.document);
8763
+ return slot.formatting;
7516
8764
  }
7517
8765
  if (autoUpgradeToCanvas && options.measurementProvider === void 0 && typeof document !== "undefined" && typeof HTMLCanvasElement !== "undefined") {
7518
- const readCachedRevision = () => cachedGraph?.revision ?? 0;
8766
+ const readCachedRevision = () => preferredCachedGraph()?.revision ?? 0;
7519
8767
  void (async () => {
7520
8768
  try {
7521
8769
  const mod = await import("./measurement-backend-canvas-F7ZYDACK.js");
@@ -7525,10 +8773,7 @@ function createLayoutEngine(options = {}) {
7525
8773
  options.measurementCache,
7526
8774
  telemetryBus
7527
8775
  );
7528
- cachedKey = null;
7529
- cachedGraph = null;
7530
- cachedFormatting = null;
7531
- cachedMapper = null;
8776
+ clearAllSlots();
7532
8777
  emit({
7533
8778
  kind: "measurement_backend_ready",
7534
8779
  revision: readCachedRevision(),
@@ -7572,22 +8817,19 @@ function createLayoutEngine(options = {}) {
7572
8817
  return getMapper(input);
7573
8818
  },
7574
8819
  invalidate(reason) {
7575
- const result = analyzeInvalidation(reason, cachedGraph);
8820
+ const result = analyzeInvalidation(reason, preferredCachedGraph());
7576
8821
  for (const family of result.dirtyFieldFamilies) {
7577
8822
  dirtyFieldFamilies.add(family);
7578
8823
  }
7579
8824
  if (result.scope === "bounded") {
7580
8825
  pendingInvalidation = { reason, result };
7581
8826
  } else {
7582
- cachedKey = null;
7583
- cachedGraph = null;
7584
- cachedFormatting = null;
7585
- cachedMapper = null;
8827
+ clearAllSlots();
7586
8828
  pendingInvalidation = { reason, result };
7587
8829
  }
7588
8830
  },
7589
8831
  analyzeInvalidation(reason) {
7590
- return analyzeInvalidation(reason, cachedGraph);
8832
+ return analyzeInvalidation(reason, preferredCachedGraph());
7591
8833
  },
7592
8834
  getDirtyFieldFamilies() {
7593
8835
  return Array.from(dirtyFieldFamilies);
@@ -7615,14 +8857,11 @@ function createLayoutEngine(options = {}) {
7615
8857
  telemetryBus
7616
8858
  );
7617
8859
  if (previousFidelity !== provider.fidelity) {
7618
- cachedKey = null;
7619
- cachedGraph = null;
7620
- cachedFormatting = null;
7621
- cachedMapper = null;
8860
+ clearAllSlots();
7622
8861
  }
7623
8862
  emit({
7624
8863
  kind: "measurement_backend_ready",
7625
- revision: cachedGraph?.revision ?? 0,
8864
+ revision: preferredCachedGraph()?.revision ?? 0,
7626
8865
  fidelity: provider.fidelity
7627
8866
  });
7628
8867
  },
@@ -7637,30 +8876,33 @@ function createLayoutEngine(options = {}) {
7637
8876
  */
7638
8877
  invalidateMeasurementCache() {
7639
8878
  measurementProvider.invalidateCache();
7640
- cachedKey = null;
7641
- cachedGraph = null;
7642
- cachedFormatting = null;
7643
- cachedMapper = null;
8879
+ clearAllSlots();
7644
8880
  },
7645
8881
  getMeasurementCacheStats() {
7646
8882
  if (!isCachedLayoutMeasurementProvider(measurementProvider)) return null;
7647
8883
  return measurementProvider.measurementCacheStats();
7648
8884
  },
7649
8885
  /**
7650
- * L7 Phase 2.5 — seed the cached graph from a prerender envelope.
7651
- * Populates both `cachedGraph` and `cachedKey` (keyed on the provided
7652
- * document's identity-equal slots) so the next getPageGraph query
7653
- * returns the seeded graph directly. Any subsequent mutation
7654
- * invalidates normally through the existing path.
8886
+ * L7 Phase 2.5 — seed the full-slot cached graph from a prerender
8887
+ * envelope. Populates the full slot (graph + key, with formatting and
8888
+ * mapper computed lazily on first read) so the next viewport-
8889
+ * independent `getPageGraph` query returns the seeded graph directly.
8890
+ * Any subsequent mutation invalidates normally through the existing
8891
+ * path; sibling eviction in `commitSlot` clears `cachedWindowed` if
8892
+ * it carried a different document tuple.
7655
8893
  */
7656
8894
  seedCachedGraph(graph, document2) {
7657
- cachedGraph = graph;
7658
- cachedKey = {
7659
- content: document2.content,
7660
- styles: document2.styles,
7661
- subParts: document2.subParts,
7662
- viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
7663
- };
8895
+ commitSlot({
8896
+ key: {
8897
+ content: document2.content,
8898
+ styles: document2.styles,
8899
+ subParts: document2.subParts,
8900
+ viewportWindowKey: FULL_VIEWPORT_WINDOW_KEY
8901
+ },
8902
+ graph,
8903
+ formatting: null,
8904
+ mapper: null
8905
+ });
7664
8906
  previousPageCount = graph.contentPageCount;
7665
8907
  }
7666
8908
  };
@@ -7941,33 +9183,6 @@ function resolveProjectedRuntimeRange(offsetMap, from, to) {
7941
9183
  return { from: runtimeFrom, to: runtimeTo };
7942
9184
  }
7943
9185
 
7944
- // src/runtime/render/render-frame-types.ts
7945
- var DEFAULT_PX_PER_TWIP = 96 / 1440;
7946
- var PAGE_STACK_GAP_PX = 48;
7947
- function resolveDefaultZoom(input = {}) {
7948
- return {
7949
- pxPerTwip: input.pxPerTwip ?? DEFAULT_PX_PER_TWIP,
7950
- viewportWidthPx: input.viewportWidthPx ?? 900,
7951
- fitMode: input.fitMode ?? "fixed"
7952
- };
7953
- }
7954
- var EMPTY_DECORATION_INDEX = Object.freeze({
7955
- workflow: Object.freeze([]),
7956
- comments: Object.freeze([]),
7957
- revisions: Object.freeze([]),
7958
- search: Object.freeze([]),
7959
- locked: Object.freeze([])
7960
- });
7961
- function defaultChromeReservations(layout, zoom) {
7962
- return {
7963
- railLaneTwips: 360,
7964
- balloonLaneTwips: 2160,
7965
- footnoteAreaTwips: 0,
7966
- pageFrameWidthPx: layout.pageWidth * zoom.pxPerTwip,
7967
- pageFrameHeightPx: layout.pageHeight * zoom.pxPerTwip
7968
- };
7969
- }
7970
-
7971
9186
  // src/runtime/markdown-sanitizer.ts
7972
9187
  function sanitizeMarkdown(raw) {
7973
9188
  let sanitized = false;
@@ -8080,6 +9295,16 @@ export {
8080
9295
  DEFAULT_UNKNOWN_BEHAVIOR,
8081
9296
  DEFAULT_REGISTRY_ENTRIES,
8082
9297
  createScopeTagRegistry,
9298
+ PREDICTED_LANE_COUNTERS,
9299
+ startPerfProbe,
9300
+ finishPerfProbe,
9301
+ recordPerfSample,
9302
+ incrementInvalidationCounter,
9303
+ createPagePatchPlan,
9304
+ DEFAULT_PX_PER_TWIP,
9305
+ PAGE_STACK_GAP_PX,
9306
+ resolveDefaultZoom,
9307
+ createRenderKernel,
8083
9308
  detachReviewAnchor,
8084
9309
  mapReviewAnchor,
8085
9310
  getAnchorRange,
@@ -8127,11 +9352,6 @@ export {
8127
9352
  searchProjectedSurfaceText,
8128
9353
  searchSecondaryStories,
8129
9354
  projectSurfaceText,
8130
- DEFAULT_PX_PER_TWIP,
8131
- PAGE_STACK_GAP_PX,
8132
- resolveDefaultZoom,
8133
- EMPTY_DECORATION_INDEX,
8134
- defaultChromeReservations,
8135
9355
  sanitizeMarkdown,
8136
9356
  sha256Hex,
8137
9357
  ISSUE_METADATA_ID,