@beyondwork/docx-react-component 1.0.104 → 1.0.105
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 +1 -1
- package/src/api/public-types.ts +3 -0
- package/src/api/v3/ai/_pe2-evidence.ts +153 -0
- package/src/api/v3/ai/bundle.ts +13 -5
- package/src/api/v3/ai/inspect.ts +7 -1
- package/src/api/v3/ai/replacement.ts +113 -0
- package/src/api/v3/ui/_types.ts +86 -0
- package/src/api/v3/ui/index.ts +5 -0
- package/src/api/v3/ui/overlays.ts +104 -0
- package/src/model/layout/index.ts +3 -0
- package/src/model/layout/page-graph-types.ts +89 -0
- package/src/model/layout/runtime-page-graph-types.ts +13 -0
- package/src/runtime/event-refresh-hints.ts +33 -6
- package/src/runtime/geometry/geometry-facet.ts +9 -1
- package/src/runtime/geometry/geometry-index.ts +388 -11
- package/src/runtime/geometry/geometry-types.ts +6 -0
- package/src/runtime/geometry/object-handles.ts +7 -4
- package/src/runtime/layout/layout-engine-instance.ts +2 -0
- package/src/runtime/layout/layout-engine-version.ts +29 -1
- package/src/runtime/layout/page-graph.ts +695 -10
- package/src/runtime/layout/project-block-fragments.ts +101 -1
- package/src/runtime/layout/public-facet.ts +152 -0
- package/src/runtime/prerender/graph-canonicalize.ts +14 -0
- package/src/ui/ui-controller-factory.ts +11 -0
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
MAIN_STORY_KEY,
|
|
23
23
|
type CanonicalAnchorLayoutInput,
|
|
24
24
|
type CanonicalLayoutInputs,
|
|
25
|
+
type CanonicalScopeLayoutInput,
|
|
25
26
|
type CanonicalTableLayoutInput,
|
|
26
27
|
type CanonicalTableCellLayoutInput,
|
|
27
28
|
type CanonicalTableRowLayoutInput,
|
|
@@ -43,12 +44,15 @@ import type {
|
|
|
43
44
|
GeometryIndexRegion,
|
|
44
45
|
GeometryIndexSlice,
|
|
45
46
|
GeometryObjectHandleEntry,
|
|
47
|
+
GeometryPrecision,
|
|
46
48
|
GeometryPrecisionCounts,
|
|
47
49
|
GeometryRect,
|
|
50
|
+
GeometryRehydrationStatus,
|
|
48
51
|
GeometryReplacementEnvelopeEntry,
|
|
49
52
|
GeometrySourceIdentity,
|
|
50
53
|
SemanticDisplayEntry,
|
|
51
54
|
} from "./geometry-types.ts";
|
|
55
|
+
import { buildObjectHandleRectsFromRect } from "./object-handles.ts";
|
|
52
56
|
|
|
53
57
|
export interface GeometryIndexProjectionOptions {
|
|
54
58
|
readonly canonicalDocument?: CanonicalDocument | null;
|
|
@@ -86,7 +90,8 @@ export function projectGeometryIndexFromFrame(
|
|
|
86
90
|
const hitTargets: GeometryHitTarget[] = [];
|
|
87
91
|
const semanticEntries: SemanticDisplayEntry[] = [];
|
|
88
92
|
const replacementEnvelopes: GeometryReplacementEnvelopeEntry[] = [];
|
|
89
|
-
const
|
|
93
|
+
const objectHandleEntries = new Map<string, MutableObjectHandleEntry>();
|
|
94
|
+
const projectedBlocksByStory = new Map<string, ProjectedScopeBlock[]>();
|
|
90
95
|
const precision = createPrecisionCounts();
|
|
91
96
|
|
|
92
97
|
for (const page of frame.pages) {
|
|
@@ -95,7 +100,9 @@ export function projectGeometryIndexFromFrame(
|
|
|
95
100
|
|
|
96
101
|
for (const [region, ordinal] of regionEntries) {
|
|
97
102
|
const regionId = makeRegionId(page, region, ordinal);
|
|
98
|
-
const storyKey =
|
|
103
|
+
const storyKey =
|
|
104
|
+
identities?.storyKeyForTarget(region.storyTarget) ??
|
|
105
|
+
canonicalStoryKeyFromTarget(region.storyTarget);
|
|
99
106
|
regionIds.push(regionId);
|
|
100
107
|
const sliceIds: string[] = [];
|
|
101
108
|
|
|
@@ -200,6 +207,25 @@ export function projectGeometryIndexFromFrame(
|
|
|
200
207
|
...(sliceIdentity ? { sourceIdentity: sliceIdentity } : {}),
|
|
201
208
|
});
|
|
202
209
|
recordPrecision(precision, "exact");
|
|
210
|
+
recordProjectedScopeBlock(projectedBlocksByStory, {
|
|
211
|
+
storyKey,
|
|
212
|
+
blockId: block.fragment.blockId,
|
|
213
|
+
blockPath: identities?.blockPathForBlockId(
|
|
214
|
+
storyKey,
|
|
215
|
+
block.fragment.blockId,
|
|
216
|
+
),
|
|
217
|
+
pageId: page.page.pageId,
|
|
218
|
+
rect: toGeometryRect(block.frame),
|
|
219
|
+
});
|
|
220
|
+
appendCanonicalObjectHandleEntries({
|
|
221
|
+
frame,
|
|
222
|
+
page,
|
|
223
|
+
block,
|
|
224
|
+
identities,
|
|
225
|
+
storyKey,
|
|
226
|
+
entries: objectHandleEntries,
|
|
227
|
+
precision,
|
|
228
|
+
});
|
|
203
229
|
appendBlockSemanticEntries({
|
|
204
230
|
page,
|
|
205
231
|
region,
|
|
@@ -209,6 +235,7 @@ export function projectGeometryIndexFromFrame(
|
|
|
209
235
|
identities,
|
|
210
236
|
storyKey,
|
|
211
237
|
entries: semanticEntries,
|
|
238
|
+
projectedBlocksByStory,
|
|
212
239
|
precision,
|
|
213
240
|
});
|
|
214
241
|
}
|
|
@@ -234,6 +261,14 @@ export function projectGeometryIndexFromFrame(
|
|
|
234
261
|
recordPrecision(precision, "exact");
|
|
235
262
|
}
|
|
236
263
|
|
|
264
|
+
appendScopeReplacementEnvelopeEntries({
|
|
265
|
+
identities,
|
|
266
|
+
projectedBlocksByStory,
|
|
267
|
+
entries: replacementEnvelopes,
|
|
268
|
+
precision,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const objectHandles = finalizeObjectHandleEntries(objectHandleEntries);
|
|
237
272
|
const coverage: GeometryIndexCoverage = {
|
|
238
273
|
status: "realized",
|
|
239
274
|
pageCount: pages.length,
|
|
@@ -336,6 +371,7 @@ function appendBlockSemanticEntries(input: {
|
|
|
336
371
|
identities: GeometryIdentityLookup | null;
|
|
337
372
|
storyKey: string;
|
|
338
373
|
entries: SemanticDisplayEntry[];
|
|
374
|
+
projectedBlocksByStory: Map<string, ProjectedScopeBlock[]>;
|
|
339
375
|
precision: GeometryPrecisionCounts;
|
|
340
376
|
}): void {
|
|
341
377
|
const {
|
|
@@ -347,6 +383,7 @@ function appendBlockSemanticEntries(input: {
|
|
|
347
383
|
identities,
|
|
348
384
|
storyKey,
|
|
349
385
|
entries,
|
|
386
|
+
projectedBlocksByStory,
|
|
350
387
|
precision,
|
|
351
388
|
} = input;
|
|
352
389
|
const tableIdentity = identities?.tableIdentity(
|
|
@@ -379,6 +416,16 @@ function appendBlockSemanticEntries(input: {
|
|
|
379
416
|
|
|
380
417
|
const plan = block.tablePlan;
|
|
381
418
|
if (!plan) return;
|
|
419
|
+
if (tableIdentity) {
|
|
420
|
+
recordTableCellScopeBlocks({
|
|
421
|
+
table: tableIdentity,
|
|
422
|
+
block,
|
|
423
|
+
pageId: page.page.pageId,
|
|
424
|
+
storyKey,
|
|
425
|
+
identities,
|
|
426
|
+
projectedBlocksByStory,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
382
429
|
const rowCount = resolveTableRowCount(block);
|
|
383
430
|
const rowHeightPx =
|
|
384
431
|
rowCount > 0 ? block.frame.heightPx / rowCount : block.frame.heightPx;
|
|
@@ -551,9 +598,14 @@ function pageFrameCellRect(
|
|
|
551
598
|
}
|
|
552
599
|
|
|
553
600
|
interface GeometryIdentityLookup {
|
|
601
|
+
storyKeyForTarget(target: EditorStoryTarget): string;
|
|
554
602
|
sliceIdentity(storyKey: string, blockId: string): GeometrySourceIdentity | undefined;
|
|
555
603
|
tableIdentity(storyKey: string, blockId: string): CanonicalTableLayoutInput | undefined;
|
|
556
604
|
anchorIdentity(storyKey: string, blockId: string): GeometrySourceIdentity | undefined;
|
|
605
|
+
objectAnchors(storyKey: string, blockId: string): readonly CanonicalAnchorLayoutInput[];
|
|
606
|
+
blockIdForPath(storyKey: string, blockPath: string): string | undefined;
|
|
607
|
+
blockPathForBlockId(storyKey: string, blockId: string): string | undefined;
|
|
608
|
+
scopeInputs(): readonly CanonicalScopeLayoutInput[];
|
|
557
609
|
}
|
|
558
610
|
|
|
559
611
|
function createIdentityLookup(
|
|
@@ -566,6 +618,7 @@ function createIdentityLookup(
|
|
|
566
618
|
: null);
|
|
567
619
|
if (!layoutInputs) return null;
|
|
568
620
|
|
|
621
|
+
const storyKeys = new Set(layoutInputs.stories.map((story) => story.storyKey));
|
|
569
622
|
const blockIdByPath = options?.canonicalDocument
|
|
570
623
|
? collectProjectedBlockIdsByPath(options.canonicalDocument)
|
|
571
624
|
: new Map<string, string>();
|
|
@@ -603,6 +656,9 @@ function createIdentityLookup(
|
|
|
603
656
|
}
|
|
604
657
|
|
|
605
658
|
return {
|
|
659
|
+
storyKeyForTarget(target) {
|
|
660
|
+
return resolveCanonicalStoryKeyForTarget(target, storyKeys);
|
|
661
|
+
},
|
|
606
662
|
sliceIdentity(storyKey, blockId) {
|
|
607
663
|
const table = tableByStoryBlockId.get(storyBlockKey(storyKey, blockId));
|
|
608
664
|
if (table) return tableSourceIdentity(table);
|
|
@@ -627,16 +683,337 @@ function createIdentityLookup(
|
|
|
627
683
|
};
|
|
628
684
|
}
|
|
629
685
|
const anchor = anchors[0]!;
|
|
630
|
-
return
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
686
|
+
return anchorSourceIdentity(anchor, "block-scoped");
|
|
687
|
+
},
|
|
688
|
+
objectAnchors(storyKey, blockId) {
|
|
689
|
+
return anchorsByStoryBlockId.get(storyBlockKey(storyKey, blockId)) ?? [];
|
|
690
|
+
},
|
|
691
|
+
blockIdForPath(storyKey, blockPath) {
|
|
692
|
+
return blockIdByPath.get(storyPathKey(storyKey, blockPath));
|
|
693
|
+
},
|
|
694
|
+
blockPathForBlockId(storyKey, blockId) {
|
|
695
|
+
return blockPathByStoryBlockId.get(storyBlockKey(storyKey, blockId));
|
|
639
696
|
},
|
|
697
|
+
scopeInputs() {
|
|
698
|
+
return layoutInputs.scopes;
|
|
699
|
+
},
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
interface MutableObjectHandleEntry {
|
|
704
|
+
objectId: string;
|
|
705
|
+
pageIds: string[];
|
|
706
|
+
rects: GeometryRect[];
|
|
707
|
+
status: GeometryRehydrationStatus;
|
|
708
|
+
precision: GeometryPrecision;
|
|
709
|
+
sourceIdentity?: GeometrySourceIdentity;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function appendCanonicalObjectHandleEntries(input: {
|
|
713
|
+
frame: RenderFrame;
|
|
714
|
+
page: RenderPage;
|
|
715
|
+
block: RenderBlock;
|
|
716
|
+
identities: GeometryIdentityLookup | null;
|
|
717
|
+
storyKey: string;
|
|
718
|
+
entries: Map<string, MutableObjectHandleEntry>;
|
|
719
|
+
precision: GeometryPrecisionCounts;
|
|
720
|
+
}): void {
|
|
721
|
+
const { frame, page, block, identities, storyKey, entries, precision } = input;
|
|
722
|
+
if (!identities) return;
|
|
723
|
+
|
|
724
|
+
for (const anchor of identities.objectAnchors(
|
|
725
|
+
storyKey,
|
|
726
|
+
block.fragment.blockId,
|
|
727
|
+
)) {
|
|
728
|
+
if (anchor.hidden === true) continue;
|
|
729
|
+
const exactObjectRect = frame.anchorIndex.byObjectId(anchor.objectKey);
|
|
730
|
+
const rect = exactObjectRect ?? block.frame;
|
|
731
|
+
const entryPrecision: GeometryPrecision = exactObjectRect
|
|
732
|
+
? "within-tolerance"
|
|
733
|
+
: "heuristic";
|
|
734
|
+
const status: GeometryRehydrationStatus = exactObjectRect
|
|
735
|
+
? "realized"
|
|
736
|
+
: "requires-rehydration";
|
|
737
|
+
const handleRects = buildObjectHandleRectsFromRect(rect, entryPrecision);
|
|
738
|
+
const existing = entries.get(anchor.objectKey);
|
|
739
|
+
if (existing) {
|
|
740
|
+
appendUnique(existing.pageIds, page.page.pageId);
|
|
741
|
+
existing.rects.push(...handleRects);
|
|
742
|
+
if (existing.precision !== "heuristic" && entryPrecision === "heuristic") {
|
|
743
|
+
existing.precision = "heuristic";
|
|
744
|
+
existing.status = "requires-rehydration";
|
|
745
|
+
existing.sourceIdentity = anchorSourceIdentity(anchor, "block-scoped");
|
|
746
|
+
}
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
entries.set(anchor.objectKey, {
|
|
751
|
+
objectId: anchor.objectKey,
|
|
752
|
+
pageIds: [page.page.pageId],
|
|
753
|
+
rects: [...handleRects],
|
|
754
|
+
status,
|
|
755
|
+
precision: entryPrecision,
|
|
756
|
+
sourceIdentity: anchorSourceIdentity(
|
|
757
|
+
anchor,
|
|
758
|
+
exactObjectRect ? "direct" : "block-scoped",
|
|
759
|
+
),
|
|
760
|
+
});
|
|
761
|
+
recordPrecision(precision, entryPrecision);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function finalizeObjectHandleEntries(
|
|
766
|
+
entries: ReadonlyMap<string, MutableObjectHandleEntry>,
|
|
767
|
+
): GeometryObjectHandleEntry[] {
|
|
768
|
+
return Array.from(entries.values()).map((entry) => ({
|
|
769
|
+
objectId: entry.objectId,
|
|
770
|
+
pageIds: entry.pageIds,
|
|
771
|
+
rects: entry.rects,
|
|
772
|
+
status: entry.status,
|
|
773
|
+
precision: entry.precision,
|
|
774
|
+
...(entry.sourceIdentity ? { sourceIdentity: entry.sourceIdentity } : {}),
|
|
775
|
+
}));
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function appendUnique(target: string[], value: string): void {
|
|
779
|
+
if (!target.includes(value)) target.push(value);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function resolveCanonicalStoryKeyForTarget(
|
|
783
|
+
target: EditorStoryTarget,
|
|
784
|
+
storyKeys: ReadonlySet<string>,
|
|
785
|
+
): string {
|
|
786
|
+
const exact = canonicalStoryKeyFromTarget(target);
|
|
787
|
+
if (storyKeys.has(exact)) return exact;
|
|
788
|
+
if (
|
|
789
|
+
(target.kind === "header" || target.kind === "footer") &&
|
|
790
|
+
target.sectionIndex !== undefined
|
|
791
|
+
) {
|
|
792
|
+
const unscoped = createHeaderFooterStoryKey({
|
|
793
|
+
kind: target.kind,
|
|
794
|
+
relationshipId: target.relationshipId,
|
|
795
|
+
variant: target.variant,
|
|
796
|
+
});
|
|
797
|
+
if (storyKeys.has(unscoped)) return unscoped;
|
|
798
|
+
}
|
|
799
|
+
return exact;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
interface ProjectedScopeBlock {
|
|
803
|
+
readonly storyKey: string;
|
|
804
|
+
readonly blockId: string;
|
|
805
|
+
readonly blockPath?: string;
|
|
806
|
+
readonly pageId: string;
|
|
807
|
+
readonly rect: GeometryRect;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function recordProjectedScopeBlock(
|
|
811
|
+
blocksByStory: Map<string, ProjectedScopeBlock[]>,
|
|
812
|
+
block: ProjectedScopeBlock,
|
|
813
|
+
): void {
|
|
814
|
+
const list = blocksByStory.get(block.storyKey);
|
|
815
|
+
if (list) {
|
|
816
|
+
list.push(block);
|
|
817
|
+
} else {
|
|
818
|
+
blocksByStory.set(block.storyKey, [block]);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function recordTableCellScopeBlocks(input: {
|
|
823
|
+
table: CanonicalTableLayoutInput;
|
|
824
|
+
block: RenderBlock;
|
|
825
|
+
pageId: string;
|
|
826
|
+
storyKey: string;
|
|
827
|
+
identities: GeometryIdentityLookup | null;
|
|
828
|
+
projectedBlocksByStory: Map<string, ProjectedScopeBlock[]>;
|
|
829
|
+
}): void {
|
|
830
|
+
const {
|
|
831
|
+
table,
|
|
832
|
+
block,
|
|
833
|
+
pageId,
|
|
834
|
+
storyKey,
|
|
835
|
+
identities,
|
|
836
|
+
projectedBlocksByStory,
|
|
837
|
+
} = input;
|
|
838
|
+
if (!identities) return;
|
|
839
|
+
|
|
840
|
+
for (const row of table.rows) {
|
|
841
|
+
for (const cell of row.cells) {
|
|
842
|
+
const rect = pageFrameCellRect(block, row.rowIndex, cell.gridColumnStart);
|
|
843
|
+
if (!rect) continue;
|
|
844
|
+
for (let blockIndex = 0; blockIndex < cell.blockCount; blockIndex += 1) {
|
|
845
|
+
const blockPath =
|
|
846
|
+
`${table.blockPath}/row[${row.rowIndex}]/cell[${cell.cellIndex}]` +
|
|
847
|
+
`/block[${blockIndex}]`;
|
|
848
|
+
const blockId = identities.blockIdForPath(storyKey, blockPath);
|
|
849
|
+
if (!blockId) continue;
|
|
850
|
+
recordProjectedScopeBlock(projectedBlocksByStory, {
|
|
851
|
+
storyKey,
|
|
852
|
+
blockId,
|
|
853
|
+
blockPath,
|
|
854
|
+
pageId,
|
|
855
|
+
rect: {
|
|
856
|
+
...toGeometryRect(rect),
|
|
857
|
+
precision: "within-tolerance",
|
|
858
|
+
},
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function appendScopeReplacementEnvelopeEntries(input: {
|
|
866
|
+
identities: GeometryIdentityLookup | null;
|
|
867
|
+
projectedBlocksByStory: ReadonlyMap<string, readonly ProjectedScopeBlock[]>;
|
|
868
|
+
entries: GeometryReplacementEnvelopeEntry[];
|
|
869
|
+
precision: GeometryPrecisionCounts;
|
|
870
|
+
}): void {
|
|
871
|
+
const { identities, projectedBlocksByStory, entries, precision } = input;
|
|
872
|
+
if (!identities) return;
|
|
873
|
+
|
|
874
|
+
for (const scope of identities.scopeInputs()) {
|
|
875
|
+
const entry = projectScopeReplacementEnvelopeEntry(
|
|
876
|
+
scope,
|
|
877
|
+
identities,
|
|
878
|
+
projectedBlocksByStory.get(scope.storyKey) ?? [],
|
|
879
|
+
);
|
|
880
|
+
entries.push(entry);
|
|
881
|
+
recordPrecision(precision, entry.precision);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function projectScopeReplacementEnvelopeEntry(
|
|
886
|
+
scope: CanonicalScopeLayoutInput,
|
|
887
|
+
identities: GeometryIdentityLookup,
|
|
888
|
+
storyBlocks: readonly ProjectedScopeBlock[],
|
|
889
|
+
): GeometryReplacementEnvelopeEntry {
|
|
890
|
+
const startBlockId = scope.start
|
|
891
|
+
? identities.blockIdForPath(scope.storyKey, scope.start.blockPath)
|
|
892
|
+
: undefined;
|
|
893
|
+
const endBlockId = scope.end
|
|
894
|
+
? identities.blockIdForPath(scope.storyKey, scope.end.blockPath)
|
|
895
|
+
: undefined;
|
|
896
|
+
|
|
897
|
+
let blocks: readonly ProjectedScopeBlock[] = [];
|
|
898
|
+
let status: GeometryReplacementEnvelopeEntry["status"] = "unavailable";
|
|
899
|
+
let precision: GeometryReplacementEnvelopeEntry["precision"] = "heuristic";
|
|
900
|
+
|
|
901
|
+
if (scope.status === "paired" && startBlockId && endBlockId) {
|
|
902
|
+
blocks = blocksInCanonicalPathRange(scope, storyBlocks);
|
|
903
|
+
if (blocks.length === 0) {
|
|
904
|
+
const startIndex = firstBlockIndex(storyBlocks, startBlockId);
|
|
905
|
+
const endIndex = lastBlockIndex(storyBlocks, endBlockId);
|
|
906
|
+
if (startIndex >= 0 && endIndex >= 0) {
|
|
907
|
+
const from = Math.min(startIndex, endIndex);
|
|
908
|
+
const to = Math.max(startIndex, endIndex);
|
|
909
|
+
blocks = storyBlocks.slice(from, to + 1);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (blocks.length > 0) {
|
|
913
|
+
status = "realized";
|
|
914
|
+
precision = "within-tolerance";
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (blocks.length === 0) {
|
|
919
|
+
const fallbackBlockId = startBlockId ?? endBlockId;
|
|
920
|
+
if (fallbackBlockId) {
|
|
921
|
+
blocks = storyBlocks.filter((block) => block.blockId === fallbackBlockId);
|
|
922
|
+
if (blocks.length > 0) status = "requires-rehydration";
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return {
|
|
927
|
+
scopeId: scope.scopeId,
|
|
928
|
+
pageIds: uniquePageIds(blocks),
|
|
929
|
+
rects: blocks.map((block) => ({
|
|
930
|
+
...block.rect,
|
|
931
|
+
precision,
|
|
932
|
+
})),
|
|
933
|
+
status,
|
|
934
|
+
precision,
|
|
935
|
+
sourceIdentity: scopeSourceIdentity(scope),
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function blocksInCanonicalPathRange(
|
|
940
|
+
scope: CanonicalScopeLayoutInput,
|
|
941
|
+
storyBlocks: readonly ProjectedScopeBlock[],
|
|
942
|
+
): readonly ProjectedScopeBlock[] {
|
|
943
|
+
if (!scope.start || !scope.end) return [];
|
|
944
|
+
const orderByPath = new Map<string, number>();
|
|
945
|
+
for (const block of storyBlocks) {
|
|
946
|
+
if (!block.blockPath || orderByPath.has(block.blockPath)) continue;
|
|
947
|
+
orderByPath.set(block.blockPath, orderByPath.size);
|
|
948
|
+
}
|
|
949
|
+
const startOrder = orderByPath.get(scope.start.blockPath);
|
|
950
|
+
const endOrder = orderByPath.get(scope.end.blockPath);
|
|
951
|
+
if (startOrder === undefined || endOrder === undefined) return [];
|
|
952
|
+
|
|
953
|
+
const from = Math.min(startOrder, endOrder);
|
|
954
|
+
const to = Math.max(startOrder, endOrder);
|
|
955
|
+
return storyBlocks.filter((block) => {
|
|
956
|
+
if (!block.blockPath) return false;
|
|
957
|
+
const order = orderByPath.get(block.blockPath);
|
|
958
|
+
return order !== undefined && order >= from && order <= to;
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function firstBlockIndex(
|
|
963
|
+
blocks: readonly ProjectedScopeBlock[],
|
|
964
|
+
blockId: string,
|
|
965
|
+
): number {
|
|
966
|
+
return blocks.findIndex((block) => block.blockId === blockId);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function lastBlockIndex(
|
|
970
|
+
blocks: readonly ProjectedScopeBlock[],
|
|
971
|
+
blockId: string,
|
|
972
|
+
): number {
|
|
973
|
+
for (let index = blocks.length - 1; index >= 0; index -= 1) {
|
|
974
|
+
if (blocks[index]?.blockId === blockId) return index;
|
|
975
|
+
}
|
|
976
|
+
return -1;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function uniquePageIds(
|
|
980
|
+
blocks: readonly ProjectedScopeBlock[],
|
|
981
|
+
): readonly string[] {
|
|
982
|
+
const ids: string[] = [];
|
|
983
|
+
const seen = new Set<string>();
|
|
984
|
+
for (const block of blocks) {
|
|
985
|
+
if (seen.has(block.pageId)) continue;
|
|
986
|
+
seen.add(block.pageId);
|
|
987
|
+
ids.push(block.pageId);
|
|
988
|
+
}
|
|
989
|
+
return ids;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function scopeSourceIdentity(
|
|
993
|
+
scope: CanonicalScopeLayoutInput,
|
|
994
|
+
): GeometrySourceIdentity {
|
|
995
|
+
const marker = scope.start ?? scope.end;
|
|
996
|
+
return {
|
|
997
|
+
storyKey: scope.storyKey,
|
|
998
|
+
...(marker ? { blockPath: marker.blockPath } : {}),
|
|
999
|
+
scopeKey: scope.scopeKey,
|
|
1000
|
+
scopeId: scope.scopeId,
|
|
1001
|
+
joinKind: "block-scoped",
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function anchorSourceIdentity(
|
|
1006
|
+
anchor: CanonicalAnchorLayoutInput,
|
|
1007
|
+
joinKind: GeometrySourceIdentity["joinKind"],
|
|
1008
|
+
): GeometrySourceIdentity {
|
|
1009
|
+
return {
|
|
1010
|
+
storyKey: anchor.storyKey,
|
|
1011
|
+
blockPath: anchor.blockPath,
|
|
1012
|
+
objectKey: anchor.objectKey,
|
|
1013
|
+
inlinePath: anchor.inlinePath,
|
|
1014
|
+
objectKind: anchor.objectKind,
|
|
1015
|
+
editPosture: anchor.editPosture,
|
|
1016
|
+
joinKind,
|
|
640
1017
|
};
|
|
641
1018
|
}
|
|
642
1019
|
|
|
@@ -147,6 +147,8 @@ export interface GeometrySourceIdentity {
|
|
|
147
147
|
tableKey?: string;
|
|
148
148
|
rowKey?: string;
|
|
149
149
|
cellKey?: string;
|
|
150
|
+
scopeKey?: string;
|
|
151
|
+
scopeId?: string;
|
|
150
152
|
objectKey?: string;
|
|
151
153
|
inlinePath?: string;
|
|
152
154
|
objectKind?: string;
|
|
@@ -288,16 +290,20 @@ export interface SemanticDisplayEntry {
|
|
|
288
290
|
|
|
289
291
|
export interface GeometryReplacementEnvelopeEntry {
|
|
290
292
|
scopeId: string;
|
|
293
|
+
pageIds?: readonly string[];
|
|
291
294
|
rects: readonly GeometryRect[];
|
|
292
295
|
status: GeometryRehydrationStatus;
|
|
293
296
|
precision: GeometryPrecision;
|
|
297
|
+
sourceIdentity?: GeometrySourceIdentity;
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
export interface GeometryObjectHandleEntry {
|
|
297
301
|
objectId: string;
|
|
302
|
+
pageIds?: readonly string[];
|
|
298
303
|
rects: readonly GeometryRect[];
|
|
299
304
|
status: GeometryRehydrationStatus;
|
|
300
305
|
precision: GeometryPrecision;
|
|
306
|
+
sourceIdentity?: GeometrySourceIdentity;
|
|
301
307
|
}
|
|
302
308
|
|
|
303
309
|
// ---------------------------------------------------------------------------
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
import type { RenderFrame, RenderFrameRect } from "../render/index.ts";
|
|
30
|
-
import type { GeometryRect } from "./geometry-types.ts";
|
|
30
|
+
import type { GeometryPrecision, GeometryRect } from "./geometry-types.ts";
|
|
31
31
|
|
|
32
32
|
const ROTATE_HANDLE_OFFSET_PX = 20;
|
|
33
33
|
|
|
@@ -38,10 +38,13 @@ export function resolveObjectHandles(
|
|
|
38
38
|
if (!frame) return [];
|
|
39
39
|
const bbox = frame.anchorIndex.byObjectId(objectId);
|
|
40
40
|
if (!bbox) return [];
|
|
41
|
-
return
|
|
41
|
+
return buildObjectHandleRectsFromRect(bbox);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
function
|
|
44
|
+
export function buildObjectHandleRectsFromRect(
|
|
45
|
+
bbox: RenderFrameRect,
|
|
46
|
+
precision: GeometryPrecision = "heuristic",
|
|
47
|
+
): readonly GeometryRect[] {
|
|
45
48
|
const { leftPx, topPx, widthPx, heightPx } = bbox;
|
|
46
49
|
const right = leftPx + widthPx;
|
|
47
50
|
const bottom = topPx + heightPx;
|
|
@@ -61,7 +64,7 @@ function buildHandles(bbox: RenderFrameRect): readonly GeometryRect[] {
|
|
|
61
64
|
widthPx: 0,
|
|
62
65
|
heightPx: 0,
|
|
63
66
|
space: "frame",
|
|
64
|
-
precision
|
|
67
|
+
precision,
|
|
65
68
|
});
|
|
66
69
|
return [
|
|
67
70
|
point(leftPx, topPx), // 0 top-left
|
|
@@ -490,6 +490,7 @@ export function createLayoutEngine(
|
|
|
490
490
|
fragmentsByPageIndex,
|
|
491
491
|
lineBoxesByPageIndex,
|
|
492
492
|
noteAllocationsByPageIndex: pageStack.noteAllocationsByPageIndex,
|
|
493
|
+
subParts: document.subParts,
|
|
493
494
|
});
|
|
494
495
|
|
|
495
496
|
// Field dirtiness diff from previous graph
|
|
@@ -669,6 +670,7 @@ export function createLayoutEngine(
|
|
|
669
670
|
fragmentsByPageIndex: freshFragmentsByPageIndex,
|
|
670
671
|
lineBoxesByPageIndex: freshLineBoxesByPageIndex,
|
|
671
672
|
noteAllocationsByPageIndex: freshResult.noteAllocationsByPageIndex,
|
|
673
|
+
subParts: document.subParts,
|
|
672
674
|
});
|
|
673
675
|
const freshNodes = freshGraph.pages;
|
|
674
676
|
|
|
@@ -1046,8 +1046,36 @@
|
|
|
1046
1046
|
* Existing `regions` remain the compatibility surface, but cache envelopes
|
|
1047
1047
|
* from v63 invalidate because the page graph payload shape changed.
|
|
1048
1048
|
* Shipped via pe2 commit `24a316af2`.
|
|
1049
|
+
*
|
|
1050
|
+
* 65 — PE2 Slice 2 page-local story instances (Layer 04). `RuntimePageFrame`
|
|
1051
|
+
* now carries stable header/footer `pageLocalStories` records with story
|
|
1052
|
+
* keys, variants, relationship ids, measured frame heights, and empty
|
|
1053
|
+
* field/object ledgers for later Slice 2/4 population. Cache envelopes
|
|
1054
|
+
* from v64 invalidate because the page-frame payload shape changed again.
|
|
1055
|
+
* Shipped via pe2 commit `06fe6f692`.
|
|
1056
|
+
*
|
|
1057
|
+
* 66 — PE2 Slice 2 page-local field ledgers (Layer 04). Page graph
|
|
1058
|
+
* construction now walks canonical header/footer subparts for each
|
|
1059
|
+
* page-local story instance and records resolved PAGE / NUMPAGES /
|
|
1060
|
+
* SECTIONPAGES display text in the story's `resolvedFields` ledger.
|
|
1061
|
+
* Cache envelopes from v65 invalidate because page-frame story payloads
|
|
1062
|
+
* now carry field-resolution data. Shipped via pe2 commit `a3a42bec9`.
|
|
1063
|
+
*
|
|
1064
|
+
* 67 — PE2 Slice 2 page-local object ledgers (Layer 04). Header/footer
|
|
1065
|
+
* page-local story instances now record canonical image/drawing/preserve-only
|
|
1066
|
+
* objects as twips/plain anchored-object ledger rows and emit typed
|
|
1067
|
+
* unsupported-wrap / preserve-only-placeholder divergences. Cache envelopes
|
|
1068
|
+
* from v66 invalidate because page-frame story payloads and divergence ids
|
|
1069
|
+
* can change. Shipped via pe2 commit `7d8bb94ac`.
|
|
1070
|
+
*
|
|
1071
|
+
* 68 — PE2 Slice 3 typed continuation cursors (Layer 04). Paragraph and
|
|
1072
|
+
* table slice fragments now carry a twips/plain continuation cursor with
|
|
1073
|
+
* sequence position, line/row range, continuation direction, repeated
|
|
1074
|
+
* table header rows, and vertical-merge carry metadata. Cache envelopes
|
|
1075
|
+
* from v67 invalidate because fragment payloads can now include
|
|
1076
|
+
* continuation state. Shipped via pe2 commit `33fbf45ac`.
|
|
1049
1077
|
*/
|
|
1050
|
-
export const LAYOUT_ENGINE_VERSION =
|
|
1078
|
+
export const LAYOUT_ENGINE_VERSION = 68 as const;
|
|
1051
1079
|
|
|
1052
1080
|
/**
|
|
1053
1081
|
* Serialization schema version for the LayCache payload (the cache envelope
|