@beyondwork/docx-react-component 1.0.109 → 1.0.111
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/model/layout/page-graph-types.ts +33 -0
- package/src/model/layout/runtime-page-graph-types.ts +25 -0
- package/src/runtime/document-runtime.ts +46 -0
- package/src/runtime/geometry/adjacent-geometry-intake.ts +820 -15
- package/src/runtime/geometry/caret-geometry.ts +219 -7
- package/src/runtime/geometry/geometry-index.ts +52 -12
- package/src/runtime/geometry/object-handles.ts +42 -1
- package/src/runtime/layout/index.ts +3 -0
- package/src/runtime/layout/inert-layout-facet.ts +13 -0
- package/src/runtime/layout/layout-engine-instance.ts +233 -4
- package/src/runtime/layout/layout-engine-version.ts +47 -2
- package/src/runtime/layout/layout-facet-types.ts +3 -0
- package/src/runtime/layout/page-graph.ts +88 -7
- package/src/runtime/layout/paginated-layout-engine.ts +34 -0
- package/src/runtime/layout/project-block-fragments.ts +144 -1
- package/src/runtime/layout/public-facet.ts +228 -9
- package/src/runtime/layout/resolve-page-previews.ts +46 -8
- package/src/runtime/scopes/adjacent-geometry-evidence.ts +456 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +8 -0
- package/src/runtime/scopes/evidence.ts +16 -0
- package/src/runtime/scopes/index.ts +13 -0
- package/src/runtime/scopes/semantic-scope-types.ts +67 -0
- package/src/ui-tailwind/chrome-overlay/tw-table-split-row-carry-overlay.tsx +62 -0
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +104 -0
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +50 -5
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +27 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +62 -0
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +1 -0
- package/src/README.md +0 -85
- package/src/api/README.md +0 -26
- package/src/api/v3/README.md +0 -91
- package/src/component-inventory.md +0 -99
- package/src/core/README.md +0 -10
- package/src/core/commands/README.md +0 -3
- package/src/core/schema/README.md +0 -3
- package/src/core/selection/README.md +0 -3
- package/src/core/state/README.md +0 -3
- package/src/io/README.md +0 -10
- package/src/io/export/README.md +0 -3
- package/src/io/normalize/README.md +0 -3
- package/src/io/ooxml/README.md +0 -3
- package/src/io/opc/README.md +0 -3
- package/src/model/README.md +0 -3
- package/src/preservation/README.md +0 -3
- package/src/review/README.md +0 -16
- package/src/review/store/README.md +0 -3
- package/src/runtime/README.md +0 -3
- package/src/ui/README.md +0 -30
- package/src/ui/comments/README.md +0 -3
- package/src/ui/compatibility/README.md +0 -3
- package/src/ui/editor-surface/README.md +0 -3
- package/src/ui/review/README.md +0 -3
- package/src/ui/status/README.md +0 -3
- package/src/ui/theme/README.md +0 -3
- package/src/ui/toolbar/README.md +0 -3
- package/src/ui-tailwind/debug/README.md +0 -22
- package/src/validation/README.md +0 -3
|
@@ -34,9 +34,14 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import type { EditorStoryTarget } from "../../api/public-types";
|
|
37
|
+
import type {
|
|
38
|
+
PublicLineRunAnchor,
|
|
39
|
+
PublicTwipsRect,
|
|
40
|
+
} from "../layout/public-facet.ts";
|
|
37
41
|
import type {
|
|
38
42
|
RenderFrame,
|
|
39
43
|
RenderFrameRect,
|
|
44
|
+
RenderLine,
|
|
40
45
|
RenderStoryRegion,
|
|
41
46
|
} from "../render/index.ts";
|
|
42
47
|
import type {
|
|
@@ -58,6 +63,9 @@ export function resolveCaretGeometry(
|
|
|
58
63
|
if (!frame) return null;
|
|
59
64
|
if (!Number.isFinite(offset)) return null;
|
|
60
65
|
|
|
66
|
+
const projectedCaret = resolveProducerCaretGeometry(frame, offset, story);
|
|
67
|
+
if (projectedCaret) return projectedCaret;
|
|
68
|
+
|
|
61
69
|
const anchorRect = frame.anchorIndex.byRuntimeOffset(offset, story);
|
|
62
70
|
if (!anchorRect) return null;
|
|
63
71
|
|
|
@@ -113,12 +121,7 @@ export function resolveSelectionRects(
|
|
|
113
121
|
if (lo === hi) {
|
|
114
122
|
const caret = resolveCaretGeometry(frame, lo, range.story);
|
|
115
123
|
if (!caret) return [];
|
|
116
|
-
|
|
117
|
-
// read. The placeholder-grade metadata on `CaretGeometry` (direction,
|
|
118
|
-
// baseline) applies to the caret as a whole, not to the rect's
|
|
119
|
-
// position, so make the rect precision explicit for downstream
|
|
120
|
-
// compositor consumers.
|
|
121
|
-
return [{ ...caret.rect, precision: "exact" }];
|
|
124
|
+
return [{ ...caret.rect, precision: caret.rect.precision ?? "exact" }];
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
const lineRects = resolveLineSelectionRects(frame, lo, hi, range.story);
|
|
@@ -178,7 +181,10 @@ function resolveLineSelectionRects(
|
|
|
178
181
|
: blockFrom +
|
|
179
182
|
Math.floor((span * (i + 1)) / Math.max(1, lineCount));
|
|
180
183
|
if (lineTo <= from || lineFrom >= to) continue;
|
|
181
|
-
rects.push(
|
|
184
|
+
rects.push(
|
|
185
|
+
resolveProducerSelectionRect(line, lineFrom, lineTo, from, to) ??
|
|
186
|
+
toGeometryRect(line.frame, "within-tolerance"),
|
|
187
|
+
);
|
|
182
188
|
}
|
|
183
189
|
}
|
|
184
190
|
}
|
|
@@ -186,6 +192,212 @@ function resolveLineSelectionRects(
|
|
|
186
192
|
return rects;
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
function resolveProducerCaretGeometry(
|
|
196
|
+
frame: RenderFrame,
|
|
197
|
+
offset: number,
|
|
198
|
+
story?: EditorStoryTarget,
|
|
199
|
+
): CaretGeometry | null {
|
|
200
|
+
const located = findProducerLineForOffset(frame, offset, story);
|
|
201
|
+
if (!located) return null;
|
|
202
|
+
const { line, lineFrom, lineTo } = located;
|
|
203
|
+
const lineRectTwips = producerLineRectTwips(line);
|
|
204
|
+
if (!lineRectTwips) return null;
|
|
205
|
+
const contentRectTwips = producerLineContentRectTwips(line) ?? lineRectTwips;
|
|
206
|
+
const direction = producerLineDirection(line);
|
|
207
|
+
const span = Math.max(1, lineTo - lineFrom);
|
|
208
|
+
const ratio = clamp((offset - lineFrom) / span, 0, 1);
|
|
209
|
+
const inlineRatio = direction === "rtl" ? 1 - ratio : ratio;
|
|
210
|
+
const caretXTwips =
|
|
211
|
+
contentRectTwips.xTwips + contentRectTwips.widthTwips * inlineRatio;
|
|
212
|
+
const caretFrame = projectLineTwipsRect(line, lineRectTwips, {
|
|
213
|
+
xTwips: caretXTwips,
|
|
214
|
+
yTwips: lineRectTwips.yTwips,
|
|
215
|
+
widthTwips: 0,
|
|
216
|
+
heightTwips: lineRectTwips.heightTwips,
|
|
217
|
+
});
|
|
218
|
+
if (!caretFrame) return null;
|
|
219
|
+
const baseline =
|
|
220
|
+
line.line.baselinePageYTwips !== undefined
|
|
221
|
+
? projectPageYToLineOffsetPx(
|
|
222
|
+
line,
|
|
223
|
+
lineRectTwips,
|
|
224
|
+
line.line.baselinePageYTwips,
|
|
225
|
+
)
|
|
226
|
+
: producerRunBaselinePageYTwips(line) !== undefined
|
|
227
|
+
? projectPageYToLineOffsetPx(
|
|
228
|
+
line,
|
|
229
|
+
lineRectTwips,
|
|
230
|
+
producerRunBaselinePageYTwips(line)!,
|
|
231
|
+
)
|
|
232
|
+
: resolveBaselinePx(caretFrame);
|
|
233
|
+
return {
|
|
234
|
+
rect: {
|
|
235
|
+
...toGeometryRect(caretFrame, "within-tolerance"),
|
|
236
|
+
widthPx: 0,
|
|
237
|
+
},
|
|
238
|
+
baseline,
|
|
239
|
+
height: caretFrame.heightPx,
|
|
240
|
+
direction,
|
|
241
|
+
precision: "within-tolerance",
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function findProducerLineForOffset(
|
|
246
|
+
frame: RenderFrame,
|
|
247
|
+
offset: number,
|
|
248
|
+
story?: EditorStoryTarget,
|
|
249
|
+
):
|
|
250
|
+
| {
|
|
251
|
+
line: RenderLine;
|
|
252
|
+
lineFrom: number;
|
|
253
|
+
lineTo: number;
|
|
254
|
+
}
|
|
255
|
+
| null {
|
|
256
|
+
if (!Array.isArray(frame.pages)) return null;
|
|
257
|
+
for (const page of frame.pages) {
|
|
258
|
+
for (const region of collectRegions(page.regions)) {
|
|
259
|
+
if (!storyMatches(region.storyTarget, story)) continue;
|
|
260
|
+
for (const block of region.blocks) {
|
|
261
|
+
const blockFrom = block.fragment.from;
|
|
262
|
+
const blockTo = block.fragment.to;
|
|
263
|
+
if (offset < blockFrom || offset > blockTo) continue;
|
|
264
|
+
if (block.lines.length === 0) continue;
|
|
265
|
+
const span = Math.max(1, blockTo - blockFrom);
|
|
266
|
+
const lineCount = block.lines.length;
|
|
267
|
+
for (let i = 0; i < lineCount; i += 1) {
|
|
268
|
+
const lineFrom =
|
|
269
|
+
blockFrom + Math.floor((span * i) / Math.max(1, lineCount));
|
|
270
|
+
const lineTo =
|
|
271
|
+
i === lineCount - 1
|
|
272
|
+
? blockTo
|
|
273
|
+
: blockFrom +
|
|
274
|
+
Math.floor((span * (i + 1)) / Math.max(1, lineCount));
|
|
275
|
+
const contains =
|
|
276
|
+
i === lineCount - 1
|
|
277
|
+
? offset >= lineFrom && offset <= lineTo
|
|
278
|
+
: offset >= lineFrom && offset < lineTo;
|
|
279
|
+
if (!contains) continue;
|
|
280
|
+
const line = block.lines[i]!;
|
|
281
|
+
if (!producerLineRectTwips(line)) continue;
|
|
282
|
+
return { line, lineFrom, lineTo };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function resolveProducerSelectionRect(
|
|
291
|
+
line: RenderLine,
|
|
292
|
+
lineFrom: number,
|
|
293
|
+
lineTo: number,
|
|
294
|
+
from: number,
|
|
295
|
+
to: number,
|
|
296
|
+
): GeometryRect | null {
|
|
297
|
+
const lineRectTwips = producerLineRectTwips(line);
|
|
298
|
+
if (!lineRectTwips) return null;
|
|
299
|
+
const contentRectTwips = producerLineContentRectTwips(line) ?? lineRectTwips;
|
|
300
|
+
const span = Math.max(1, lineTo - lineFrom);
|
|
301
|
+
const startRatio = clamp((Math.max(from, lineFrom) - lineFrom) / span, 0, 1);
|
|
302
|
+
const endRatio = clamp((Math.min(to, lineTo) - lineFrom) / span, 0, 1);
|
|
303
|
+
if (endRatio <= startRatio) return null;
|
|
304
|
+
const direction = producerLineDirection(line);
|
|
305
|
+
const leftRatio = direction === "rtl" ? 1 - endRatio : startRatio;
|
|
306
|
+
const rightRatio = direction === "rtl" ? 1 - startRatio : endRatio;
|
|
307
|
+
const leftTwips =
|
|
308
|
+
contentRectTwips.xTwips + contentRectTwips.widthTwips * leftRatio;
|
|
309
|
+
const rightTwips =
|
|
310
|
+
contentRectTwips.xTwips + contentRectTwips.widthTwips * rightRatio;
|
|
311
|
+
const frameRect = projectLineTwipsRect(line, lineRectTwips, {
|
|
312
|
+
xTwips: leftTwips,
|
|
313
|
+
yTwips: contentRectTwips.yTwips,
|
|
314
|
+
widthTwips: Math.max(0, rightTwips - leftTwips),
|
|
315
|
+
heightTwips: contentRectTwips.heightTwips,
|
|
316
|
+
});
|
|
317
|
+
return frameRect ? toGeometryRect(frameRect, "within-tolerance") : null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function producerLineRectTwips(line: RenderLine): PublicTwipsRect | undefined {
|
|
321
|
+
return line.line.rectTwips ?? line.line.runAnchors?.[0]?.lineRectTwips;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function producerLineContentRectTwips(
|
|
325
|
+
line: RenderLine,
|
|
326
|
+
): PublicTwipsRect | undefined {
|
|
327
|
+
const anchors = line.line.runAnchors ?? [];
|
|
328
|
+
if (anchors.length === 0) return undefined;
|
|
329
|
+
return unionRunRectTwips(anchors);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function producerLineDirection(line: RenderLine): "ltr" | "rtl" {
|
|
333
|
+
return line.line.direction ?? line.line.runAnchors?.[0]?.direction ?? "ltr";
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function producerRunBaselinePageYTwips(
|
|
337
|
+
line: RenderLine,
|
|
338
|
+
): number | undefined {
|
|
339
|
+
return line.line.runAnchors?.[0]?.baselinePageYTwips;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function unionRunRectTwips(
|
|
343
|
+
anchors: readonly PublicLineRunAnchor[],
|
|
344
|
+
): PublicTwipsRect | undefined {
|
|
345
|
+
let left = Number.POSITIVE_INFINITY;
|
|
346
|
+
let top = Number.POSITIVE_INFINITY;
|
|
347
|
+
let right = Number.NEGATIVE_INFINITY;
|
|
348
|
+
let bottom = Number.NEGATIVE_INFINITY;
|
|
349
|
+
for (const anchor of anchors) {
|
|
350
|
+
const rect = anchor.runRectTwips;
|
|
351
|
+
left = Math.min(left, rect.xTwips);
|
|
352
|
+
top = Math.min(top, rect.yTwips);
|
|
353
|
+
right = Math.max(right, rect.xTwips + rect.widthTwips);
|
|
354
|
+
bottom = Math.max(bottom, rect.yTwips + rect.heightTwips);
|
|
355
|
+
}
|
|
356
|
+
if (!Number.isFinite(left) || !Number.isFinite(top)) return undefined;
|
|
357
|
+
return {
|
|
358
|
+
xTwips: left,
|
|
359
|
+
yTwips: top,
|
|
360
|
+
widthTwips: Math.max(0, right - left),
|
|
361
|
+
heightTwips: Math.max(0, bottom - top),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function projectLineTwipsRect(
|
|
366
|
+
line: RenderLine,
|
|
367
|
+
lineRectTwips: PublicTwipsRect,
|
|
368
|
+
rect: PublicTwipsRect,
|
|
369
|
+
): RenderFrameRect | null {
|
|
370
|
+
if (lineRectTwips.widthTwips <= 0 || lineRectTwips.heightTwips <= 0) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
const scaleX = line.frame.widthPx / lineRectTwips.widthTwips;
|
|
374
|
+
const scaleY = line.frame.heightPx / lineRectTwips.heightTwips;
|
|
375
|
+
return {
|
|
376
|
+
leftPx: line.frame.leftPx + (rect.xTwips - lineRectTwips.xTwips) * scaleX,
|
|
377
|
+
topPx: line.frame.topPx + (rect.yTwips - lineRectTwips.yTwips) * scaleY,
|
|
378
|
+
widthPx: rect.widthTwips * scaleX,
|
|
379
|
+
heightPx: rect.heightTwips * scaleY,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function projectPageYToLineOffsetPx(
|
|
384
|
+
line: RenderLine,
|
|
385
|
+
lineRectTwips: PublicTwipsRect,
|
|
386
|
+
pageYTwips: number,
|
|
387
|
+
): number {
|
|
388
|
+
if (lineRectTwips.heightTwips <= 0) return resolveBaselinePx(line.frame);
|
|
389
|
+
const scaleY = line.frame.heightPx / lineRectTwips.heightTwips;
|
|
390
|
+
return clamp(
|
|
391
|
+
(pageYTwips - lineRectTwips.yTwips) * scaleY,
|
|
392
|
+
0,
|
|
393
|
+
line.frame.heightPx,
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function clamp(value: number, min: number, max: number): number {
|
|
398
|
+
return Math.min(Math.max(value, min), max);
|
|
399
|
+
}
|
|
400
|
+
|
|
189
401
|
function collectRegions(
|
|
190
402
|
regions: RenderFrame["pages"][number]["regions"],
|
|
191
403
|
): readonly RenderStoryRegion[] {
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type CanonicalTableCellLayoutInput,
|
|
28
28
|
type CanonicalTableRowLayoutInput,
|
|
29
29
|
} from "../../model/canonical-layout-inputs.ts";
|
|
30
|
+
import type { PublicTwipsRect } from "../layout/public-facet.ts";
|
|
30
31
|
import type {
|
|
31
32
|
RenderFrame,
|
|
32
33
|
RenderFrameRect,
|
|
@@ -106,6 +107,7 @@ export function projectGeometryIndexFromFrame(
|
|
|
106
107
|
let splitRowCarryCount = 0;
|
|
107
108
|
|
|
108
109
|
for (const page of frame.pages) {
|
|
110
|
+
if (!isPageGeometryMaterialized(page)) continue;
|
|
109
111
|
const pageMetadata = pageFrameMetadata(page);
|
|
110
112
|
pageFrameCompleteness[pageMetadata.frameCompleteness] += 1;
|
|
111
113
|
layoutDivergenceObjectCount += pageMetadata.layoutDivergenceObjectIds.length;
|
|
@@ -313,7 +315,7 @@ export function projectGeometryIndexFromFrame(
|
|
|
313
315
|
|
|
314
316
|
const objectHandles = finalizeObjectHandleEntries(objectHandleEntries);
|
|
315
317
|
const coverage: GeometryIndexCoverage = {
|
|
316
|
-
status:
|
|
318
|
+
status: coverageStatusForProjectedPages(frame, pages.length),
|
|
317
319
|
pageCount: pages.length,
|
|
318
320
|
pageFrameCompleteness,
|
|
319
321
|
regionCount: regions.length,
|
|
@@ -367,6 +369,7 @@ export function summarizeGeometryCoverageFromFrame(
|
|
|
367
369
|
let splitRowCarryCount = 0;
|
|
368
370
|
|
|
369
371
|
for (const page of frame.pages) {
|
|
372
|
+
if (!isPageGeometryMaterialized(page)) continue;
|
|
370
373
|
const pageMetadata = pageFrameMetadata(page);
|
|
371
374
|
pageCount += 1;
|
|
372
375
|
pageFrameCompleteness[pageMetadata.frameCompleteness] += 1;
|
|
@@ -407,7 +410,7 @@ export function summarizeGeometryCoverageFromFrame(
|
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
return {
|
|
410
|
-
status:
|
|
413
|
+
status: coverageStatusForProjectedPages(frame, pageCount),
|
|
411
414
|
pageCount,
|
|
412
415
|
pageFrameCompleteness,
|
|
413
416
|
regionCount,
|
|
@@ -433,6 +436,19 @@ interface PageFrameMetadata {
|
|
|
433
436
|
divergenceIdsByObjectId: ReadonlyMap<string, readonly string[]>;
|
|
434
437
|
}
|
|
435
438
|
|
|
439
|
+
function isPageGeometryMaterialized(page: RenderPage): boolean {
|
|
440
|
+
return page.page.materialization !== "unpaginated";
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function coverageStatusForProjectedPages(
|
|
444
|
+
frame: RenderFrame,
|
|
445
|
+
projectedPageCount: number,
|
|
446
|
+
): GeometryRehydrationStatus {
|
|
447
|
+
return frame.pages.length > 0 && projectedPageCount === 0
|
|
448
|
+
? "unavailable"
|
|
449
|
+
: "realized";
|
|
450
|
+
}
|
|
451
|
+
|
|
436
452
|
function pageFrameMetadata(page: RenderPage): PageFrameMetadata {
|
|
437
453
|
const frame = page.page.frame;
|
|
438
454
|
const divergenceIds = frame?.divergenceIds ? [...frame.divergenceIds] : [];
|
|
@@ -1294,25 +1310,32 @@ function appendPageLocalObjectHandleEntries(input: {
|
|
|
1294
1310
|
story.kind === "header" ? page.regions.header?.frame : page.regions.footer?.frame;
|
|
1295
1311
|
if (!regionFrame) continue;
|
|
1296
1312
|
for (const object of story.anchoredObjects) {
|
|
1297
|
-
const objectFrame =
|
|
1298
|
-
|
|
1299
|
-
object.extentTwips,
|
|
1300
|
-
|
|
1313
|
+
const objectFrame = object.anchorRectTwips
|
|
1314
|
+
? pageLocalObjectAnchorFrame(page, object.anchorRectTwips)
|
|
1315
|
+
: pageLocalObjectFrame(regionFrame, object.extentTwips, pxPerTwip);
|
|
1316
|
+
const entryPrecision: GeometryPrecision = object.anchorRectTwips
|
|
1317
|
+
? "within-tolerance"
|
|
1318
|
+
: "heuristic";
|
|
1319
|
+
const entryStatus: GeometryRehydrationStatus = object.anchorRectTwips
|
|
1320
|
+
? "realized"
|
|
1321
|
+
: "requires-rehydration";
|
|
1322
|
+
const handleRects = buildObjectHandleRectsFromRect(
|
|
1323
|
+
objectFrame,
|
|
1324
|
+
entryPrecision,
|
|
1301
1325
|
);
|
|
1302
|
-
const handleRects = buildObjectHandleRectsFromRect(objectFrame, "heuristic");
|
|
1303
1326
|
const sourceIdentity: GeometrySourceIdentity = {
|
|
1304
1327
|
storyKey: story.storyKey,
|
|
1305
1328
|
objectKey: object.objectId,
|
|
1306
1329
|
objectKind: object.sourceType,
|
|
1307
1330
|
editPosture: object.preserveOnly ? "preserve-only" : "editable",
|
|
1308
|
-
joinKind: "block-scoped",
|
|
1331
|
+
joinKind: object.anchorRectTwips ? "direct" : "block-scoped",
|
|
1309
1332
|
};
|
|
1310
1333
|
const existing = entries.get(object.objectId);
|
|
1311
1334
|
if (existing) {
|
|
1312
1335
|
appendUnique(existing.pageIds, page.page.pageId);
|
|
1313
1336
|
existing.rects.push(...handleRects);
|
|
1314
1337
|
appendDivergenceIds(existing, divergenceIdsByObjectId.get(object.objectId));
|
|
1315
|
-
if (existing.precision !== "heuristic") {
|
|
1338
|
+
if (existing.precision !== "heuristic" && entryPrecision === "heuristic") {
|
|
1316
1339
|
existing.precision = "heuristic";
|
|
1317
1340
|
existing.status = "requires-rehydration";
|
|
1318
1341
|
existing.sourceIdentity = sourceIdentity;
|
|
@@ -1323,16 +1346,33 @@ function appendPageLocalObjectHandleEntries(input: {
|
|
|
1323
1346
|
objectId: object.objectId,
|
|
1324
1347
|
pageIds: [page.page.pageId],
|
|
1325
1348
|
rects: [...handleRects],
|
|
1326
|
-
status:
|
|
1327
|
-
precision:
|
|
1349
|
+
status: entryStatus,
|
|
1350
|
+
precision: entryPrecision,
|
|
1328
1351
|
...mutableLayoutDivergenceIds(divergenceIdsByObjectId.get(object.objectId)),
|
|
1329
1352
|
sourceIdentity,
|
|
1330
1353
|
});
|
|
1331
|
-
recordPrecision(precision,
|
|
1354
|
+
recordPrecision(precision, entryPrecision);
|
|
1332
1355
|
}
|
|
1333
1356
|
}
|
|
1334
1357
|
}
|
|
1335
1358
|
|
|
1359
|
+
function pageLocalObjectAnchorFrame(
|
|
1360
|
+
page: RenderPage,
|
|
1361
|
+
rect: PublicTwipsRect,
|
|
1362
|
+
): RenderFrameRect {
|
|
1363
|
+
const pageWidthTwips = page.page.layout.pageWidth;
|
|
1364
|
+
const pageHeightTwips = page.page.layout.pageHeight;
|
|
1365
|
+
const scaleX = pageWidthTwips > 0 ? page.frame.widthPx / pageWidthTwips : 1;
|
|
1366
|
+
const scaleY =
|
|
1367
|
+
pageHeightTwips > 0 ? page.frame.heightPx / pageHeightTwips : scaleX;
|
|
1368
|
+
return {
|
|
1369
|
+
leftPx: page.frame.leftPx + rect.xTwips * scaleX,
|
|
1370
|
+
topPx: page.frame.topPx + rect.yTwips * scaleY,
|
|
1371
|
+
widthPx: rect.widthTwips * scaleX,
|
|
1372
|
+
heightPx: rect.heightTwips * scaleY,
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1336
1376
|
function pageLocalObjectFrame(
|
|
1337
1377
|
regionFrame: RenderFrameRect,
|
|
1338
1378
|
extentTwips:
|
|
@@ -26,7 +26,12 @@
|
|
|
26
26
|
* index 8 — rotate anchor (20 px above the top-center handle)
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
import type {
|
|
29
|
+
import type { PublicTwipsRect } from "../layout/public-facet.ts";
|
|
30
|
+
import type {
|
|
31
|
+
RenderFrame,
|
|
32
|
+
RenderFrameRect,
|
|
33
|
+
RenderPage,
|
|
34
|
+
} from "../render/index.ts";
|
|
30
35
|
import type { GeometryPrecision, GeometryRect } from "./geometry-types.ts";
|
|
31
36
|
|
|
32
37
|
const ROTATE_HANDLE_OFFSET_PX = 20;
|
|
@@ -36,6 +41,10 @@ export function resolveObjectHandles(
|
|
|
36
41
|
objectId: string,
|
|
37
42
|
): readonly GeometryRect[] {
|
|
38
43
|
if (!frame) return [];
|
|
44
|
+
const pageLocalBBox = resolvePageLocalObjectFrame(frame, objectId);
|
|
45
|
+
if (pageLocalBBox) {
|
|
46
|
+
return buildObjectHandleRectsFromRect(pageLocalBBox, "within-tolerance");
|
|
47
|
+
}
|
|
39
48
|
const bbox = frame.anchorIndex.byObjectId(objectId);
|
|
40
49
|
if (!bbox) return [];
|
|
41
50
|
return buildObjectHandleRectsFromRect(bbox);
|
|
@@ -78,3 +87,35 @@ export function buildObjectHandleRectsFromRect(
|
|
|
78
87
|
point(midX, topPx - ROTATE_HANDLE_OFFSET_PX), // 8 rotate
|
|
79
88
|
];
|
|
80
89
|
}
|
|
90
|
+
|
|
91
|
+
function resolvePageLocalObjectFrame(
|
|
92
|
+
frame: RenderFrame,
|
|
93
|
+
objectId: string,
|
|
94
|
+
): RenderFrameRect | null {
|
|
95
|
+
if (!Array.isArray(frame.pages)) return null;
|
|
96
|
+
for (const page of frame.pages) {
|
|
97
|
+
for (const story of page.page.frame?.pageLocalStories ?? []) {
|
|
98
|
+
for (const object of story.anchoredObjects) {
|
|
99
|
+
if (object.objectId !== objectId || !object.anchorRectTwips) continue;
|
|
100
|
+
return projectPageTwipsRect(page, object.anchorRectTwips);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function projectPageTwipsRect(
|
|
108
|
+
page: RenderPage,
|
|
109
|
+
rect: PublicTwipsRect,
|
|
110
|
+
): RenderFrameRect {
|
|
111
|
+
const pageWidthTwips = page.page.layout.pageWidth;
|
|
112
|
+
const pageHeightTwips = page.page.layout.pageHeight;
|
|
113
|
+
const scaleX = pageWidthTwips > 0 ? page.frame.widthPx / pageWidthTwips : 1;
|
|
114
|
+
const scaleY = pageHeightTwips > 0 ? page.frame.heightPx / pageHeightTwips : scaleX;
|
|
115
|
+
return {
|
|
116
|
+
leftPx: page.frame.leftPx + rect.xTwips * scaleX,
|
|
117
|
+
topPx: page.frame.topPx + rect.yTwips * scaleY,
|
|
118
|
+
widthPx: rect.widthTwips * scaleX,
|
|
119
|
+
heightPx: rect.heightTwips * scaleY,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -181,11 +181,14 @@ export {
|
|
|
181
181
|
type PublicRegionKind,
|
|
182
182
|
type PublicBlockFragment,
|
|
183
183
|
type PublicLineBox,
|
|
184
|
+
type PublicLineRunAnchor,
|
|
184
185
|
type PublicNoteAllocation,
|
|
185
186
|
type PublicPageAnchor,
|
|
186
187
|
type PublicPageFrame,
|
|
187
188
|
type PublicPageLocalStoryInstance,
|
|
189
|
+
type PublicPagePaginationTelemetry,
|
|
188
190
|
type PublicPageSpan,
|
|
191
|
+
type PublicPaginationTelemetry,
|
|
189
192
|
type PublicSectionNode,
|
|
190
193
|
type PublicResolvedPageStories,
|
|
191
194
|
type PublicResolvedStoryField,
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
LayoutFacetEvent,
|
|
11
11
|
PublicFieldDirtinessReport,
|
|
12
12
|
PublicMeasurementFidelity,
|
|
13
|
+
PublicPaginationTelemetry,
|
|
13
14
|
WordReviewEditorLayoutFacet,
|
|
14
15
|
} from "./public-facet.ts";
|
|
15
16
|
import { MARGIN_PRESET_CATALOG } from "./margin-preset-catalog.ts";
|
|
@@ -20,6 +21,17 @@ export function createInertLayoutFacet(): WordReviewEditorLayoutFacet {
|
|
|
20
21
|
families: [],
|
|
21
22
|
revision: 0,
|
|
22
23
|
};
|
|
24
|
+
const emptyPaginationTelemetry: PublicPaginationTelemetry = {
|
|
25
|
+
revision: 0,
|
|
26
|
+
pageCount: 0,
|
|
27
|
+
materializedPageCount: 0,
|
|
28
|
+
unpaginatedPageCount: 0,
|
|
29
|
+
bodyFragmentCount: 0,
|
|
30
|
+
bodyBlockReferenceCount: 0,
|
|
31
|
+
averageBodyFragmentsPerMaterializedPage: 0,
|
|
32
|
+
averageBodyBlockReferencesPerMaterializedPage: 0,
|
|
33
|
+
pages: [],
|
|
34
|
+
};
|
|
23
35
|
const fidelity: PublicMeasurementFidelity = "empirical";
|
|
24
36
|
return {
|
|
25
37
|
getPageCount: () => 0,
|
|
@@ -39,6 +51,7 @@ export function createInertLayoutFacet(): WordReviewEditorLayoutFacet {
|
|
|
39
51
|
getStoryBlocksForRegion: () => [],
|
|
40
52
|
getDocumentEndnoteBlocks: () => [],
|
|
41
53
|
getFragmentsForPage: () => [],
|
|
54
|
+
getPaginationTelemetry: () => emptyPaginationTelemetry,
|
|
42
55
|
getPageFormatCatalog: () => PAGE_FORMAT_CATALOG,
|
|
43
56
|
getActivePageFormat: () => null,
|
|
44
57
|
getMarginPresetCatalog: () => MARGIN_PRESET_CATALOG,
|