@beyondwork/docx-react-component 1.0.105 → 1.0.108
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/package.json +19 -5
- package/src/api/geometry-overlay-rects.ts +5 -0
- package/src/api/package-version.ts +1 -1
- package/src/api/page-anchor-id.ts +5 -0
- package/src/api/public-types.ts +16 -9
- package/src/api/table-node-specs.ts +6 -0
- package/src/api/v3/_create.ts +10 -2
- package/src/api/v3/_page-anchor-id.ts +52 -0
- package/src/api/v3/_runtime-handle.ts +92 -1
- package/src/api/v3/ai/_audit-reference.ts +28 -0
- package/src/api/v3/ai/_audit-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +310 -6
- package/src/api/v3/ai/attach.ts +29 -4
- package/src/api/v3/ai/bundle.ts +6 -2
- package/src/api/v3/ai/inspect.ts +6 -2
- package/src/api/v3/ai/replacement.ts +112 -18
- package/src/api/v3/ai/resolve.ts +2 -2
- package/src/api/v3/ai/review.ts +177 -3
- package/src/api/v3/index.ts +8 -0
- package/src/api/v3/runtime/collab.ts +462 -0
- package/src/api/v3/runtime/document.ts +503 -20
- package/src/api/v3/runtime/geometry.ts +97 -0
- package/src/api/v3/runtime/layout.ts +744 -0
- package/src/api/v3/runtime/perf-probe.ts +14 -0
- package/src/api/v3/runtime/viewport.ts +9 -8
- package/src/api/v3/ui/_types.ts +202 -55
- package/src/api/v3/ui/chrome-preset-model.ts +5 -5
- package/src/api/v3/ui/debug.ts +115 -2
- package/src/api/v3/ui/index.ts +17 -0
- package/src/api/v3/ui/overlays.ts +0 -8
- package/src/api/v3/ui/surface.ts +56 -0
- package/src/api/v3/ui/viewport.ts +119 -9
- package/src/core/commands/image-commands.ts +1 -0
- package/src/core/commands/index.ts +6 -0
- package/src/core/schema/text-schema.ts +43 -5
- package/src/core/selection/mapping.ts +8 -1
- package/src/core/selection/review-anchors.ts +5 -1
- package/src/core/state/text-transaction.ts +8 -2
- package/src/io/export/serialize-revisions.ts +149 -1
- package/src/io/normalize/normalize-text.ts +6 -0
- package/src/io/ooxml/parse-bookmark-references.ts +55 -0
- package/src/io/ooxml/parse-fields.ts +24 -2
- package/src/io/ooxml/parse-headers-footers.ts +38 -5
- package/src/io/ooxml/parse-main-document.ts +153 -9
- package/src/io/ooxml/parse-numbering.ts +20 -0
- package/src/io/ooxml/parse-revisions.ts +19 -8
- package/src/io/opc/package-reader.ts +98 -8
- package/src/model/anchor.ts +4 -3
- package/src/model/canonical-document.ts +220 -2
- package/src/model/canonical-hash.ts +221 -0
- package/src/model/canonical-layout-inputs.ts +245 -6
- package/src/model/layout/index.ts +1 -0
- package/src/model/layout/page-graph-types.ts +147 -1
- package/src/model/review/revision-types.ts +14 -3
- package/src/preservation/store.ts +20 -4
- package/src/review/README.md +1 -1
- package/src/review/store/revision-actions.ts +14 -2
- package/src/runtime/collab/event-types.ts +67 -1
- package/src/runtime/collab/runtime-collab-sync.ts +177 -5
- package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
- package/src/runtime/document-heading-outline.ts +147 -0
- package/src/runtime/document-navigation.ts +8 -243
- package/src/runtime/document-runtime.ts +279 -115
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
- package/src/runtime/formatting/layout-inputs.ts +38 -5
- package/src/runtime/formatting/numbering/geometry.ts +28 -2
- package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
- package/src/runtime/geometry/caret-geometry.ts +5 -6
- package/src/runtime/geometry/geometry-facet.ts +60 -10
- package/src/runtime/geometry/geometry-index.ts +661 -16
- package/src/runtime/geometry/geometry-types.ts +59 -0
- package/src/runtime/geometry/hit-test.ts +11 -1
- package/src/runtime/geometry/overlay-rects.ts +5 -3
- package/src/runtime/geometry/project-anchors.ts +1 -1
- package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
- package/src/runtime/layout/index.ts +6 -0
- package/src/runtime/layout/layout-engine-instance.ts +6 -1
- package/src/runtime/layout/layout-engine-version.ts +188 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +23 -4
- package/src/runtime/layout/paginated-layout-engine.ts +149 -15
- package/src/runtime/layout/project-block-fragments.ts +351 -14
- package/src/runtime/layout/public-facet.ts +162 -24
- package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
- package/src/runtime/layout/table-row-split.ts +92 -35
- package/src/runtime/prerender/cache-envelope.ts +2 -2
- package/src/runtime/prerender/cache-key.ts +5 -4
- package/src/runtime/prerender/customxml-cache.ts +0 -1
- package/src/runtime/render/render-kernel.ts +1 -1
- package/src/runtime/revision-runtime.ts +112 -10
- package/src/runtime/scopes/_scope-dependencies.ts +1 -0
- package/src/runtime/scopes/action-validation.ts +22 -2
- package/src/runtime/scopes/capabilities.ts +316 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
- package/src/runtime/scopes/compiler-service.ts +108 -4
- package/src/runtime/scopes/content-control-evidence.ts +79 -0
- package/src/runtime/scopes/create-issue.ts +5 -5
- package/src/runtime/scopes/evidence.ts +91 -0
- package/src/runtime/scopes/formatting/apply.ts +2 -0
- package/src/runtime/scopes/geometry-evidence.ts +130 -0
- package/src/runtime/scopes/index.ts +54 -0
- package/src/runtime/scopes/issue-lifecycle.ts +224 -0
- package/src/runtime/scopes/layout-evidence.ts +374 -0
- package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
- package/src/runtime/scopes/preservation-boundary.ts +7 -1
- package/src/runtime/scopes/replacement/apply.ts +97 -34
- package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
- package/src/runtime/scopes/semantic-scope-types.ts +242 -3
- package/src/runtime/scopes/visualization.ts +28 -0
- package/src/runtime/surface-projection.ts +44 -5
- package/src/runtime/telemetry/perf-probe.ts +216 -0
- package/src/runtime/virtualized-rendering.ts +36 -1
- package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
- package/src/runtime/workflow/coordinator.ts +39 -11
- package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
- package/src/runtime/workflow/index.ts +4 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +386 -0
- package/src/runtime/workflow/overlay-store.ts +2 -2
- package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
- package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
- package/src/session/_sync-legacy.ts +17 -27
- package/src/session/import/loader.ts +6 -4
- package/src/session/import/source-package-evidence.ts +186 -2
- package/src/session/index.ts +5 -6
- package/src/session/session.ts +30 -56
- package/src/session/types.ts +8 -13
- package/src/shell/session-bootstrap.ts +155 -81
- package/src/ui/WordReviewEditor.tsx +520 -12
- package/src/ui/editor-shell-view.tsx +14 -4
- package/src/ui/editor-surface-controller.tsx +5 -3
- package/src/ui/headless/selection-tool-resolver.ts +1 -2
- package/src/ui/presence-overlay-lane.ts +130 -0
- package/src/ui/ui-controller-factory.ts +17 -0
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
- package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
- package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
- package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
- package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
- package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
- package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
- package/src/ui-tailwind/debug/README.md +4 -1
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
- package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
- package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
- package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
- package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
- package/src/ui-tailwind/review-workspace/types.ts +26 -12
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
- package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
|
@@ -257,6 +257,19 @@ export interface AIIssueSummary {
|
|
|
257
257
|
readonly severity: "info" | "warning" | "error";
|
|
258
258
|
readonly status: "open" | "resolved";
|
|
259
259
|
readonly createdAtUtc?: string;
|
|
260
|
+
readonly statusUpdatedAtUtc?: string;
|
|
261
|
+
readonly resolvedAtUtc?: string;
|
|
262
|
+
readonly resolvedBy?: string;
|
|
263
|
+
readonly reopenedAtUtc?: string;
|
|
264
|
+
readonly reopenedBy?: string;
|
|
265
|
+
readonly lastTransition?: {
|
|
266
|
+
readonly action: "resolve" | "reopen";
|
|
267
|
+
readonly actorId: string;
|
|
268
|
+
readonly at: string;
|
|
269
|
+
readonly origin: string;
|
|
270
|
+
readonly fromStatus: "open" | "resolved";
|
|
271
|
+
readonly toStatus: "open" | "resolved";
|
|
272
|
+
};
|
|
260
273
|
}
|
|
261
274
|
|
|
262
275
|
export interface ScopeBundleEvidence {
|
|
@@ -264,6 +277,37 @@ export interface ScopeBundleEvidence {
|
|
|
264
277
|
readonly reviewItemIds?: readonly string[];
|
|
265
278
|
readonly overlappingWorkflowScopeIds?: readonly string[];
|
|
266
279
|
readonly compatibilityFlags?: readonly string[];
|
|
280
|
+
/**
|
|
281
|
+
* Layout evidence projected from L04/L05 join surfaces when present.
|
|
282
|
+
* Missing layout truth is represented explicitly; Layer 08 does not infer
|
|
283
|
+
* page slices, continuation, or divergence rows from content or DOM state.
|
|
284
|
+
*/
|
|
285
|
+
readonly layout?: ScopeLayoutEvidence;
|
|
286
|
+
/**
|
|
287
|
+
* Geometry envelope evidence projected from Layer 05 when a geometry facet
|
|
288
|
+
* is wired. Missing/cold geometry is represented explicitly as
|
|
289
|
+
* `requires-rehydration` or `unavailable`; Layer 08 never fabricates rects.
|
|
290
|
+
*/
|
|
291
|
+
readonly geometry?: ScopeGeometryEvidence;
|
|
292
|
+
/**
|
|
293
|
+
* Presentation hint only. Consumers may use this to choose a cheap inline
|
|
294
|
+
* treatment for field-like scopes versus a broader overlay treatment for
|
|
295
|
+
* everything else, but it is not layout or geometry authority.
|
|
296
|
+
*/
|
|
297
|
+
readonly visualization?: ScopeVisualizationHint;
|
|
298
|
+
/**
|
|
299
|
+
* Current action capability verdicts for this scope. These are evidence
|
|
300
|
+
* only: they explain what the shipped compiler/runtime can lower today
|
|
301
|
+
* without broadening the mutation surface.
|
|
302
|
+
*/
|
|
303
|
+
readonly capabilities?: ScopeCapabilities;
|
|
304
|
+
/**
|
|
305
|
+
* Content-control / SDT overlap evidence. These are preserve-first wrapper
|
|
306
|
+
* facts: when present on a target scope, replacement capability verdicts and
|
|
307
|
+
* validation must surface blockers instead of treating the wrapper like
|
|
308
|
+
* ordinary editable text.
|
|
309
|
+
*/
|
|
310
|
+
readonly contentControls?: ScopeContentControlEvidence;
|
|
267
311
|
/**
|
|
268
312
|
* Agent-authored explanations on this scope, read back from Layer-06
|
|
269
313
|
* metadata entries keyed by `metadataId: "ai.explanation"`. Adversarial-
|
|
@@ -279,6 +323,192 @@ export interface ScopeBundleEvidence {
|
|
|
279
323
|
readonly aiIssues?: readonly AIIssueSummary[];
|
|
280
324
|
}
|
|
281
325
|
|
|
326
|
+
export type ScopeEvidenceAvailability =
|
|
327
|
+
| "available"
|
|
328
|
+
| "partial"
|
|
329
|
+
| "degraded"
|
|
330
|
+
| "requires-rehydration"
|
|
331
|
+
| "unavailable";
|
|
332
|
+
|
|
333
|
+
export interface ScopeLayoutContinuationEvidence {
|
|
334
|
+
readonly pageIds?: readonly string[];
|
|
335
|
+
readonly pageCount?: number;
|
|
336
|
+
readonly crossesPageBoundary?: boolean;
|
|
337
|
+
readonly continuedFromPreviousPage?: boolean;
|
|
338
|
+
readonly continuesToNextPage?: boolean;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface ScopeTableFramePageEvidence {
|
|
342
|
+
readonly pageId: string;
|
|
343
|
+
readonly pageIndex: number;
|
|
344
|
+
readonly fragmentId: string;
|
|
345
|
+
readonly rowRange?: {
|
|
346
|
+
readonly from: number;
|
|
347
|
+
readonly to: number;
|
|
348
|
+
readonly totalRows: number;
|
|
349
|
+
};
|
|
350
|
+
readonly continuesFromPreviousPage?: boolean;
|
|
351
|
+
readonly continuesToNextPage?: boolean;
|
|
352
|
+
readonly repeatedHeaderRowIndexes?: readonly number[];
|
|
353
|
+
readonly splitRowCarry?: readonly {
|
|
354
|
+
readonly rowIndex: number;
|
|
355
|
+
readonly continuesFromPreviousPage: boolean;
|
|
356
|
+
readonly continuesToNextPage: boolean;
|
|
357
|
+
}[];
|
|
358
|
+
readonly verticalMergeCarry?: readonly {
|
|
359
|
+
readonly columnIndex: number;
|
|
360
|
+
readonly restartRowIndex: number;
|
|
361
|
+
}[];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export interface ScopeTableFrameEvidence {
|
|
365
|
+
readonly source: "runtime.layout.table-frame-continuation";
|
|
366
|
+
readonly blockId: string;
|
|
367
|
+
readonly scopeKind: "table" | "table-row" | "table-cell";
|
|
368
|
+
readonly rowIndex?: number;
|
|
369
|
+
readonly cellIndex?: number;
|
|
370
|
+
readonly pageIds?: readonly string[];
|
|
371
|
+
readonly pageSliceIds?: readonly string[];
|
|
372
|
+
readonly layoutObjectIds?: readonly string[];
|
|
373
|
+
readonly rowRangesByPage?: readonly ScopeTableFramePageEvidence[];
|
|
374
|
+
readonly repeatedHeaderRowIndexes?: readonly number[];
|
|
375
|
+
readonly splitRowCarry?: readonly {
|
|
376
|
+
readonly rowIndex: number;
|
|
377
|
+
readonly continuesFromPreviousPage: boolean;
|
|
378
|
+
readonly continuesToNextPage: boolean;
|
|
379
|
+
}[];
|
|
380
|
+
readonly verticalMergeCarry?: readonly {
|
|
381
|
+
readonly columnIndex: number;
|
|
382
|
+
readonly restartRowIndex: number;
|
|
383
|
+
}[];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface ScopeLayoutEvidence {
|
|
387
|
+
readonly status: ScopeEvidenceAvailability;
|
|
388
|
+
readonly completeness:
|
|
389
|
+
| "complete"
|
|
390
|
+
| "partial"
|
|
391
|
+
| "degraded"
|
|
392
|
+
| "requires-rehydration"
|
|
393
|
+
| "unavailable";
|
|
394
|
+
readonly reason?: string;
|
|
395
|
+
readonly pageSliceIds?: readonly string[];
|
|
396
|
+
readonly layoutObjectIds?: readonly string[];
|
|
397
|
+
readonly continuationState?: ScopeLayoutContinuationEvidence;
|
|
398
|
+
readonly divergenceIds?: readonly string[];
|
|
399
|
+
readonly tableFrame?: ScopeTableFrameEvidence;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export type ScopeGeometryEvidenceStatus =
|
|
403
|
+
| "available"
|
|
404
|
+
| "requires-rehydration"
|
|
405
|
+
| "unavailable";
|
|
406
|
+
|
|
407
|
+
export type ScopeGeometryEvidencePrecision =
|
|
408
|
+
| "exact"
|
|
409
|
+
| "within-tolerance"
|
|
410
|
+
| "heuristic";
|
|
411
|
+
|
|
412
|
+
export interface ScopeGeometryEvidenceRect {
|
|
413
|
+
readonly x: number;
|
|
414
|
+
readonly y: number;
|
|
415
|
+
readonly width: number;
|
|
416
|
+
readonly height: number;
|
|
417
|
+
readonly space: "twips" | "frame" | "overlay";
|
|
418
|
+
readonly precision?: ScopeGeometryEvidencePrecision;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export interface ScopeGeometryEvidence {
|
|
422
|
+
readonly status: ScopeGeometryEvidenceStatus;
|
|
423
|
+
readonly requiresRehydration: boolean;
|
|
424
|
+
readonly reason?: string;
|
|
425
|
+
readonly pageIds?: readonly string[];
|
|
426
|
+
readonly confidence?: "exact" | "medium" | "detached";
|
|
427
|
+
readonly precision?: ScopeGeometryEvidencePrecision;
|
|
428
|
+
readonly rects?: readonly ScopeGeometryEvidenceRect[];
|
|
429
|
+
readonly attachPoint?: {
|
|
430
|
+
readonly x: number;
|
|
431
|
+
readonly y: number;
|
|
432
|
+
readonly space: "twips" | "frame" | "overlay";
|
|
433
|
+
};
|
|
434
|
+
readonly linesCrossed?: number;
|
|
435
|
+
readonly continuationState?: ScopeLayoutContinuationEvidence;
|
|
436
|
+
readonly sourceIdentity?: {
|
|
437
|
+
readonly storyKey?: string;
|
|
438
|
+
readonly blockPath?: string;
|
|
439
|
+
readonly tableKey?: string;
|
|
440
|
+
readonly rowKey?: string;
|
|
441
|
+
readonly cellKey?: string;
|
|
442
|
+
readonly scopeKey?: string;
|
|
443
|
+
readonly scopeId?: string;
|
|
444
|
+
readonly objectKey?: string;
|
|
445
|
+
readonly inlinePath?: string;
|
|
446
|
+
readonly objectKind?: string;
|
|
447
|
+
readonly editPosture?: string;
|
|
448
|
+
readonly joinKind?: string;
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export type ScopeVisualizationClass = "field" | "broad";
|
|
453
|
+
|
|
454
|
+
export interface ScopeVisualizationHint {
|
|
455
|
+
readonly class: ScopeVisualizationClass;
|
|
456
|
+
readonly reason: string;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export type ScopeCapabilityStatus =
|
|
460
|
+
| "supported"
|
|
461
|
+
| "blocked"
|
|
462
|
+
| "unsupported"
|
|
463
|
+
| "degraded";
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Action posture for compiler consumers that need to distinguish "warn and
|
|
467
|
+
* proceed" from a real refusal. L08 must prefer `warn-and-proceed` whenever
|
|
468
|
+
* the mutation can be lowered without losing preserved truth or fabricating
|
|
469
|
+
* layout/geometry. `hard-refusal` means the action must not dispatch.
|
|
470
|
+
*/
|
|
471
|
+
export type ScopeActionPosture =
|
|
472
|
+
| "supported"
|
|
473
|
+
| "warn-and-proceed"
|
|
474
|
+
| "hard-refusal";
|
|
475
|
+
|
|
476
|
+
export interface ScopeCapabilityVerdict {
|
|
477
|
+
readonly supported: boolean;
|
|
478
|
+
readonly status: ScopeCapabilityStatus;
|
|
479
|
+
readonly posture?: ScopeActionPosture;
|
|
480
|
+
readonly reason?: string;
|
|
481
|
+
readonly blockers?: readonly string[];
|
|
482
|
+
readonly warnings?: readonly string[];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export interface ScopeCapabilities {
|
|
486
|
+
readonly canReplaceText: ScopeCapabilityVerdict;
|
|
487
|
+
readonly canReplaceFragment: ScopeCapabilityVerdict;
|
|
488
|
+
readonly canInsertBefore: ScopeCapabilityVerdict;
|
|
489
|
+
readonly canInsertAfter: ScopeCapabilityVerdict;
|
|
490
|
+
readonly canApplyFormatting: ScopeCapabilityVerdict;
|
|
491
|
+
readonly canClearFormattingLayer: ScopeCapabilityVerdict;
|
|
492
|
+
readonly canAttachMetadata: ScopeCapabilityVerdict;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export type ScopeContentControlEvidenceStatus = "none" | "present";
|
|
496
|
+
|
|
497
|
+
export interface ScopeContentControlEvidenceEntry {
|
|
498
|
+
readonly evidenceId: string;
|
|
499
|
+
readonly blockIndex: number;
|
|
500
|
+
readonly sdtType?: string;
|
|
501
|
+
readonly alias?: string;
|
|
502
|
+
readonly tag?: string;
|
|
503
|
+
readonly lock?: string;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export interface ScopeContentControlEvidence {
|
|
507
|
+
readonly status: ScopeContentControlEvidenceStatus;
|
|
508
|
+
readonly count: number;
|
|
509
|
+
readonly entries: readonly ScopeContentControlEvidenceEntry[];
|
|
510
|
+
}
|
|
511
|
+
|
|
282
512
|
export interface ScopeBundle {
|
|
283
513
|
readonly scope: SemanticScope;
|
|
284
514
|
readonly neighborhood: ScopeBundleNeighborhood;
|
|
@@ -286,11 +516,19 @@ export interface ScopeBundle {
|
|
|
286
516
|
readonly generatedAtUtc: string;
|
|
287
517
|
}
|
|
288
518
|
|
|
289
|
-
export type
|
|
519
|
+
export type ScopeReplacementOperationKind =
|
|
290
520
|
| "replace"
|
|
291
521
|
| "insert-before"
|
|
292
|
-
| "insert-after"
|
|
293
|
-
|
|
522
|
+
| "insert-after";
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Compiler-internal proposal operation. Public replacement surfaces accept
|
|
526
|
+
* `ScopeReplacementOperationKind`; `annotate` is retained only for metadata
|
|
527
|
+
* write audit records (`ai.attachExplanation` / `ai.createIssue`) that reuse
|
|
528
|
+
* the `ScopeActionAudit` shape without going through text replacement.
|
|
529
|
+
*/
|
|
530
|
+
export type ReplacementOperationKind =
|
|
531
|
+
| ScopeReplacementOperationKind
|
|
294
532
|
| "annotate";
|
|
295
533
|
|
|
296
534
|
export interface ReplacementPreservePolicy {
|
|
@@ -426,6 +664,7 @@ export interface ValidationApproval {
|
|
|
426
664
|
*/
|
|
427
665
|
export interface ValidationResult {
|
|
428
666
|
readonly safe: boolean;
|
|
667
|
+
readonly posture?: ScopeActionPosture;
|
|
429
668
|
readonly blockedReasons: readonly string[];
|
|
430
669
|
readonly warnings: readonly ValidationIssue[];
|
|
431
670
|
readonly approval?: ValidationApproval;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope visualization hint projection.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately evidence, not geometry. Layer 08 can tell consumers
|
|
5
|
+
* whether a scope is structurally field-like or broad; Layer 05 remains the
|
|
6
|
+
* authority for rects, line spans, and overlay envelopes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
ScopeVisualizationHint,
|
|
11
|
+
SemanticScope,
|
|
12
|
+
} from "./semantic-scope-types.ts";
|
|
13
|
+
|
|
14
|
+
export function deriveScopeVisualization(
|
|
15
|
+
scope: SemanticScope,
|
|
16
|
+
): ScopeVisualizationHint {
|
|
17
|
+
if (scope.kind === "field") {
|
|
18
|
+
return {
|
|
19
|
+
class: "field",
|
|
20
|
+
reason: "kind:field",
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
class: "broad",
|
|
26
|
+
reason: `kind:${scope.kind}`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -149,7 +149,7 @@ export interface SurfaceProjectionOptions {
|
|
|
149
149
|
* if you call `createEditorSurfaceSnapshot` directly, normalize first.
|
|
150
150
|
*/
|
|
151
151
|
viewportBlockRanges?: readonly { start: number; end: number }[] | null;
|
|
152
|
-
/** @deprecated use `viewportBlockRanges`. Kept for back-compat;
|
|
152
|
+
/** @deprecated use `viewportBlockRanges`. Kept for back-compat; remove after the next published API pin refresh. */
|
|
153
153
|
viewportBlockRange?: { start: number; end: number } | null;
|
|
154
154
|
/**
|
|
155
155
|
* Active markup mode. When set together with the document's
|
|
@@ -444,7 +444,9 @@ function createSurfaceBlock(
|
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
if (block.type === "sdt") {
|
|
447
|
-
const descriptor = describeStructuredWrapperBlock(block
|
|
447
|
+
const descriptor = describeStructuredWrapperBlock(block, {
|
|
448
|
+
projectVisibleTocContentControls: true,
|
|
449
|
+
});
|
|
448
450
|
if (descriptor) {
|
|
449
451
|
const blockId = `sdt-wrapper-${counters.sdt}`;
|
|
450
452
|
counters.sdt += 1;
|
|
@@ -1086,6 +1088,12 @@ function createSdtBlock(
|
|
|
1086
1088
|
const children: SurfaceBlockSnapshot[] = [];
|
|
1087
1089
|
const lockedFragmentIds: string[] = [];
|
|
1088
1090
|
let innerCursor = cursor;
|
|
1091
|
+
const descriptor = describeStructuredWrapperBlock(block);
|
|
1092
|
+
const recursableBlockedReasonCode = descriptor
|
|
1093
|
+
? isBlockedImportFeatureKey(descriptor.featureKey)
|
|
1094
|
+
? "workflow_blocked_import"
|
|
1095
|
+
: "workflow_preserve_only"
|
|
1096
|
+
: getRecursableSdtBlockedReasonCode(block);
|
|
1089
1097
|
|
|
1090
1098
|
for (const child of block.children) {
|
|
1091
1099
|
const result = createSurfaceBlock(
|
|
@@ -1112,11 +1120,21 @@ function createSdtBlock(
|
|
|
1112
1120
|
...(block.properties.alias ? { alias: block.properties.alias } : {}),
|
|
1113
1121
|
...(block.properties.tag ? { tag: block.properties.tag } : {}),
|
|
1114
1122
|
...(block.properties.lock ? { lock: block.properties.lock } : {}),
|
|
1123
|
+
...(recursableBlockedReasonCode
|
|
1124
|
+
? { blockedReasonCode: recursableBlockedReasonCode }
|
|
1125
|
+
: {}),
|
|
1115
1126
|
...(block.properties.checkbox ? { checkboxChecked: block.properties.checkbox.checked } : {}),
|
|
1116
1127
|
...(block.properties.datePicker?.fullDate ? { dateValue: block.properties.datePicker.fullDate } : {}),
|
|
1117
1128
|
...(block.properties.dropdownList ? { dropdownItems: block.properties.dropdownList } : {}),
|
|
1118
1129
|
...(block.properties.comboBox ? { comboBoxItems: block.properties.comboBox } : {}),
|
|
1119
1130
|
...(block.properties.showingPlcHdr ? { showingPlcHdr: true } : {}),
|
|
1131
|
+
...(descriptor
|
|
1132
|
+
? {
|
|
1133
|
+
label: descriptor.label,
|
|
1134
|
+
detail: descriptor.detail,
|
|
1135
|
+
featureKey: descriptor.featureKey,
|
|
1136
|
+
}
|
|
1137
|
+
: {}),
|
|
1120
1138
|
children,
|
|
1121
1139
|
},
|
|
1122
1140
|
lockedFragmentIds,
|
|
@@ -1124,6 +1142,24 @@ function createSdtBlock(
|
|
|
1124
1142
|
};
|
|
1125
1143
|
}
|
|
1126
1144
|
|
|
1145
|
+
function getRecursableSdtBlockedReasonCode(
|
|
1146
|
+
block: Mutable<SdtNode>,
|
|
1147
|
+
): "workflow_preserve_only" | null {
|
|
1148
|
+
const searchText = [
|
|
1149
|
+
block.properties.alias,
|
|
1150
|
+
block.properties.tag,
|
|
1151
|
+
block.properties.sdtType,
|
|
1152
|
+
block.properties.propertiesXml,
|
|
1153
|
+
]
|
|
1154
|
+
.filter(Boolean)
|
|
1155
|
+
.join(" ")
|
|
1156
|
+
.toLowerCase();
|
|
1157
|
+
|
|
1158
|
+
return searchText.includes("table of contents") || /\btoc\b/u.test(searchText)
|
|
1159
|
+
? "workflow_preserve_only"
|
|
1160
|
+
: null;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1127
1163
|
function createParagraphBlock(
|
|
1128
1164
|
paragraphIndex: number,
|
|
1129
1165
|
paragraph: Mutable<ParagraphNode>,
|
|
@@ -1562,6 +1598,7 @@ function appendInlineSegments(
|
|
|
1562
1598
|
});
|
|
1563
1599
|
return { nextCursor: start + 1, lockedFragmentIds: [] };
|
|
1564
1600
|
}
|
|
1601
|
+
const objectAnchor = surfaceAnchorFromGeometry(node.anchor);
|
|
1565
1602
|
if (c.type === "chart_preview") {
|
|
1566
1603
|
const parsedChartId = registerParsedChartPreview(c, document);
|
|
1567
1604
|
return appendComplexPreviewSegment(
|
|
@@ -1573,7 +1610,7 @@ function appendInlineSegments(
|
|
|
1573
1610
|
{
|
|
1574
1611
|
previewMediaId: c.previewMediaId,
|
|
1575
1612
|
parsedChartId,
|
|
1576
|
-
anchor:
|
|
1613
|
+
...(objectAnchor ? { anchor: objectAnchor } : {}),
|
|
1577
1614
|
},
|
|
1578
1615
|
);
|
|
1579
1616
|
}
|
|
@@ -1586,7 +1623,7 @@ function appendInlineSegments(
|
|
|
1586
1623
|
`DrawingFrame smartart_preview (${node.anchor.wrapMode}).`,
|
|
1587
1624
|
{
|
|
1588
1625
|
previewMediaId: c.previewMediaId,
|
|
1589
|
-
anchor:
|
|
1626
|
+
...(objectAnchor ? { anchor: objectAnchor } : {}),
|
|
1590
1627
|
},
|
|
1591
1628
|
);
|
|
1592
1629
|
}
|
|
@@ -1596,7 +1633,9 @@ function appendInlineSegments(
|
|
|
1596
1633
|
start,
|
|
1597
1634
|
"Drawing frame",
|
|
1598
1635
|
`DrawingFrame ${c.type} (${node.anchor.wrapMode}).`,
|
|
1599
|
-
{
|
|
1636
|
+
{
|
|
1637
|
+
...(objectAnchor ? { anchor: objectAnchor } : {}),
|
|
1638
|
+
},
|
|
1600
1639
|
);
|
|
1601
1640
|
}
|
|
1602
1641
|
case "symbol":
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export type PerfProbeKind =
|
|
2
|
+
| "typing"
|
|
3
|
+
| "typing.predicted"
|
|
4
|
+
| "typing.reconcile"
|
|
5
|
+
| "typing.divergence"
|
|
6
|
+
| "selection"
|
|
7
|
+
| "runtime.create"
|
|
8
|
+
| "loadSession.laycacheProbe"
|
|
9
|
+
| "loadSession.compatReportCached"
|
|
10
|
+
| "snapshot.surface"
|
|
11
|
+
| "snapshot.compatibility"
|
|
12
|
+
| "snapshot.navigation"
|
|
13
|
+
| "pm.rebuild"
|
|
14
|
+
| "pm.decorations"
|
|
15
|
+
| "pm.decorations.apply"
|
|
16
|
+
| "pm.decorations.comments"
|
|
17
|
+
| "pm.decorations.revisions"
|
|
18
|
+
| "pm.decorations.workflow"
|
|
19
|
+
| "pm.mount"
|
|
20
|
+
| "shell.render"
|
|
21
|
+
| "workspace.chrome"
|
|
22
|
+
| "selection.sync"
|
|
23
|
+
| "layout.incremental"
|
|
24
|
+
| "layout.full"
|
|
25
|
+
| "render.frame_build"
|
|
26
|
+
| "render.frame_diff"
|
|
27
|
+
| "render.decoration_resolve"
|
|
28
|
+
| "chrome.overlay_reposition"
|
|
29
|
+
| "chrome.hit_test"
|
|
30
|
+
| "rail.segment_project";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Counter names the FastTextEditLane emits via `incrementInvalidationCounter`.
|
|
34
|
+
* Expose them as a const so integrators can read the shape without duplicating
|
|
35
|
+
* strings.
|
|
36
|
+
*/
|
|
37
|
+
export const PREDICTED_LANE_COUNTERS = {
|
|
38
|
+
applied: "predictions.applied",
|
|
39
|
+
equivalent: "predictions.equivalent",
|
|
40
|
+
adjusted: "predictions.adjusted",
|
|
41
|
+
rejected: "predictions.rejected",
|
|
42
|
+
rollback: "predictions.rollback",
|
|
43
|
+
structuralDivergence: "predictions.structuralDivergence",
|
|
44
|
+
bailBeforePredict: "predictions.bailBeforePredict",
|
|
45
|
+
refreshSelectionOnly: "predictions.refresh.selectionOnly",
|
|
46
|
+
refreshLocalTextEquivalent: "predictions.refresh.localTextEquivalent",
|
|
47
|
+
refreshSurfaceOnly: "predictions.refresh.surfaceOnly",
|
|
48
|
+
refreshFullProjection: "predictions.refresh.fullProjection",
|
|
49
|
+
refreshBlocked: "predictions.refresh.blocked",
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
export interface PerfProbeSample {
|
|
53
|
+
token: string;
|
|
54
|
+
kind: PerfProbeKind;
|
|
55
|
+
durationMs: number;
|
|
56
|
+
recordedAt: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface PendingProbe {
|
|
60
|
+
kind: PerfProbeKind;
|
|
61
|
+
startedAt: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface PerfProbeState {
|
|
65
|
+
enabled?: boolean;
|
|
66
|
+
nextToken?: number;
|
|
67
|
+
pending?: Record<string, PendingProbe>;
|
|
68
|
+
samples?: PerfProbeSample[];
|
|
69
|
+
maxSamples?: number;
|
|
70
|
+
invalidationCounts?: Record<string, number>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface PerfProbeSummary {
|
|
74
|
+
samples: PerfProbeSample[];
|
|
75
|
+
latest: Partial<Record<PerfProbeKind, PerfProbeSample | null>>;
|
|
76
|
+
invalidationCounts: Record<string, number>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
declare global {
|
|
80
|
+
interface Window {
|
|
81
|
+
__DOCX_REACT_PERF_PROBE__?: PerfProbeState;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function startPerfProbe(kind: PerfProbeKind): string | null {
|
|
86
|
+
const state = getEnabledState();
|
|
87
|
+
if (!state) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const token = `${kind}-${state.nextToken ?? 0}`;
|
|
92
|
+
state.nextToken = (state.nextToken ?? 0) + 1;
|
|
93
|
+
state.pending ??= {};
|
|
94
|
+
state.pending[token] = {
|
|
95
|
+
kind,
|
|
96
|
+
startedAt: performance.now(),
|
|
97
|
+
};
|
|
98
|
+
return token;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function finishPerfProbe(token: string | null | undefined): PerfProbeSample | null {
|
|
102
|
+
if (!token) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const state = getEnabledState();
|
|
106
|
+
if (!state?.pending?.[token]) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const pending = state.pending[token];
|
|
111
|
+
delete state.pending[token];
|
|
112
|
+
|
|
113
|
+
const sample: PerfProbeSample = {
|
|
114
|
+
token,
|
|
115
|
+
kind: pending.kind,
|
|
116
|
+
durationMs: performance.now() - pending.startedAt,
|
|
117
|
+
recordedAt: Date.now(),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
pushSample(state, sample);
|
|
121
|
+
|
|
122
|
+
return sample;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function recordPerfSample(
|
|
126
|
+
kind: PerfProbeKind,
|
|
127
|
+
durationMs = 0,
|
|
128
|
+
): PerfProbeSample | null {
|
|
129
|
+
const state = getEnabledState();
|
|
130
|
+
if (!state) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const token = `${kind}-${state.nextToken ?? 0}`;
|
|
135
|
+
state.nextToken = (state.nextToken ?? 0) + 1;
|
|
136
|
+
const sample: PerfProbeSample = {
|
|
137
|
+
token,
|
|
138
|
+
kind,
|
|
139
|
+
durationMs,
|
|
140
|
+
recordedAt: Date.now(),
|
|
141
|
+
};
|
|
142
|
+
pushSample(state, sample);
|
|
143
|
+
return sample;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function incrementInvalidationCounter(
|
|
147
|
+
counter: string,
|
|
148
|
+
amount = 1,
|
|
149
|
+
): number {
|
|
150
|
+
const state = getEnabledState();
|
|
151
|
+
if (!state) {
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
state.invalidationCounts ??= {};
|
|
156
|
+
state.invalidationCounts[counter] =
|
|
157
|
+
(state.invalidationCounts[counter] ?? 0) + amount;
|
|
158
|
+
return state.invalidationCounts[counter]!;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function getLatestPerfSummary(): PerfProbeSummary | null {
|
|
162
|
+
const state = getEnabledState();
|
|
163
|
+
const samples = state?.samples ?? [];
|
|
164
|
+
if (!state || samples.length === 0) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
samples: [...samples],
|
|
170
|
+
latest: buildLatestSampleMap(samples),
|
|
171
|
+
invalidationCounts: { ...(state.invalidationCounts ?? {}) },
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function resetPerfProbeState(): void {
|
|
176
|
+
const state = getEnabledState();
|
|
177
|
+
if (!state) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
state.nextToken = 0;
|
|
181
|
+
state.pending = {};
|
|
182
|
+
state.samples = [];
|
|
183
|
+
state.invalidationCounts = {};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getEnabledState(): PerfProbeState | null {
|
|
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
|
+
|
|
197
|
+
function pushSample(state: PerfProbeState, sample: PerfProbeSample): void {
|
|
198
|
+
state.samples ??= [];
|
|
199
|
+
state.samples.push(sample);
|
|
200
|
+
const maxSamples = state.maxSamples ?? 20;
|
|
201
|
+
if (state.samples.length > maxSamples) {
|
|
202
|
+
state.samples.splice(0, state.samples.length - maxSamples);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function buildLatestSampleMap(
|
|
207
|
+
samples: PerfProbeSample[],
|
|
208
|
+
): Partial<Record<PerfProbeKind, PerfProbeSample | null>> {
|
|
209
|
+
const latest: Partial<Record<PerfProbeKind, PerfProbeSample | null>> = {};
|
|
210
|
+
for (const sample of [...samples].reverse()) {
|
|
211
|
+
if (latest[sample.kind] === undefined) {
|
|
212
|
+
latest[sample.kind] = sample;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return latest;
|
|
216
|
+
}
|
|
@@ -35,6 +35,21 @@ export interface VirtualizedViewport {
|
|
|
35
35
|
overscanHeight?: number;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export interface VirtualizedBlockGeometryProvider {
|
|
39
|
+
getBlock(blockId: string): { rects: readonly { topPx: number }[] } | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ComputeVirtualizedWindowOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Optional L05 geometry source. When present, `offsetTop` is derived from
|
|
45
|
+
* `GeometryFacet.getBlock(firstVisibleBlockId).rects[0].topPx` instead of
|
|
46
|
+
* the synthetic virtualization estimate. This keeps DOM-free consumers on
|
|
47
|
+
* geometry truth while preserving the legacy estimate for unwired tests and
|
|
48
|
+
* pre-paint sessions.
|
|
49
|
+
*/
|
|
50
|
+
geometryFacet?: VirtualizedBlockGeometryProvider;
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
export interface VirtualizedWindow {
|
|
39
54
|
startIndex: number;
|
|
40
55
|
endIndex: number;
|
|
@@ -121,6 +136,7 @@ export function createVirtualizedRenderingSession(
|
|
|
121
136
|
export function computeVirtualizedWindow(
|
|
122
137
|
session: VirtualizedRenderingSession,
|
|
123
138
|
viewport: VirtualizedViewport,
|
|
139
|
+
options: ComputeVirtualizedWindowOptions = {},
|
|
124
140
|
): VirtualizedWindow {
|
|
125
141
|
if (session.metrics.length === 0) {
|
|
126
142
|
return {
|
|
@@ -145,19 +161,38 @@ export function computeVirtualizedWindow(
|
|
|
145
161
|
const safeEndIndex = Math.max(startIndex + 1, endIndexExclusive);
|
|
146
162
|
const visibleMetrics = session.metrics.slice(startIndex, safeEndIndex);
|
|
147
163
|
const visibleBlocks = session.blocks.slice(startIndex, safeEndIndex);
|
|
164
|
+
const firstMetric = visibleMetrics[0];
|
|
165
|
+
const firstBlock = visibleBlocks[0];
|
|
148
166
|
const lastMetric = visibleMetrics[visibleMetrics.length - 1]!;
|
|
149
167
|
|
|
150
168
|
return {
|
|
151
169
|
startIndex,
|
|
152
170
|
endIndex: safeEndIndex,
|
|
153
171
|
totalHeight: session.totalHeight,
|
|
154
|
-
offsetTop:
|
|
172
|
+
offsetTop: resolveWindowOffsetTop(
|
|
173
|
+
firstBlock?.blockId ?? firstMetric?.blockId,
|
|
174
|
+
firstMetric,
|
|
175
|
+
options,
|
|
176
|
+
),
|
|
155
177
|
offsetBottom: Math.max(0, session.totalHeight - lastMetric.bottom),
|
|
156
178
|
visibleBlocks,
|
|
157
179
|
visibleMetrics,
|
|
158
180
|
};
|
|
159
181
|
}
|
|
160
182
|
|
|
183
|
+
function resolveWindowOffsetTop(
|
|
184
|
+
blockId: string | undefined,
|
|
185
|
+
fallbackMetric: VirtualizedRenderingMetrics | undefined,
|
|
186
|
+
options: ComputeVirtualizedWindowOptions,
|
|
187
|
+
): number {
|
|
188
|
+
const geometryTop = blockId
|
|
189
|
+
? options.geometryFacet?.getBlock(blockId)?.rects[0]?.topPx
|
|
190
|
+
: undefined;
|
|
191
|
+
return typeof geometryTop === "number" && Number.isFinite(geometryTop)
|
|
192
|
+
? geometryTop
|
|
193
|
+
: (fallbackMetric?.top ?? 0);
|
|
194
|
+
}
|
|
195
|
+
|
|
161
196
|
function findFirstVisibleIndex(
|
|
162
197
|
metrics: VirtualizedRenderingMetrics[],
|
|
163
198
|
top: number,
|