@beyondwork/docx-react-component 1.0.84 → 1.0.86
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/internal/build-ref-projections.ts +3 -0
- package/src/api/public-types.ts +38 -0
- package/src/api/v3/_runtime-handle.ts +11 -0
- package/src/api/v3/runtime/content.ts +148 -1
- package/src/api/v3/runtime/formatting.ts +41 -0
- package/src/api/v3/runtime/review.ts +98 -0
- package/src/core/commands/index.ts +81 -25
- package/src/core/state/editor-state.ts +15 -0
- package/src/io/ooxml/header-footer-reference.ts +38 -0
- package/src/io/ooxml/parse-headers-footers.ts +11 -23
- package/src/io/ooxml/parse-main-document.ts +7 -10
- package/src/model/canonical-document.ts +9 -0
- package/src/model/review/comment-types.ts +2 -0
- package/src/runtime/document-runtime.ts +677 -54
- package/src/runtime/formatting/field/resolver.ts +73 -8
- package/src/runtime/layout/layout-engine-version.ts +31 -12
- package/src/runtime/layout/paginated-layout-engine.ts +18 -11
- package/src/runtime/layout/public-facet.ts +119 -16
- package/src/runtime/layout/resolve-page-fields.ts +68 -6
- package/src/runtime/layout/resolve-page-previews.ts +1 -1
- package/src/runtime/suggestions-snapshot.ts +24 -0
- package/src/runtime/surface-projection.ts +59 -2
- package/src/shell/ref-commands.ts +3 -354
- package/src/shell/session-bootstrap.ts +8 -0
- package/src/ui/WordReviewEditor.tsx +192 -35
- package/src/ui/editor-command-bag.ts +7 -1
- package/src/ui/editor-shell-view.tsx +1 -0
- package/src/ui/headless/revision-decoration-model.ts +13 -0
- package/src/ui/headless/selection-tool-types.ts +2 -0
- package/src/ui-tailwind/chrome/editor-action-registry.ts +7 -26
- package/src/ui-tailwind/chrome/tw-detach-handle.tsx +6 -2
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +7 -3
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +175 -25
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +1 -1
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +12 -0
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +18 -30
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +1 -1
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +1 -1
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +20 -11
- package/src/ui-tailwind/page-stack/tw-page-footer-band.tsx +9 -4
- package/src/ui-tailwind/page-stack/tw-page-header-band.tsx +12 -7
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +29 -10
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +1 -1
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +8 -3
- package/src/ui-tailwind/review/tw-review-rail.tsx +2 -0
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +46 -31
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +159 -8
- package/src/ui-tailwind/review-workspace/types.ts +7 -2
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +11 -1
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +8 -9
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +21 -16
- package/src/ui-tailwind/tw-review-workspace.tsx +27 -4
|
@@ -1536,7 +1536,34 @@ function appendInlineSegments(
|
|
|
1536
1536
|
node.fieldFamily === "NOTEREF" ||
|
|
1537
1537
|
node.fieldFamily === "TOC" ||
|
|
1538
1538
|
node.fieldFamily === "PAGE" ||
|
|
1539
|
-
node.fieldFamily === "NUMPAGES"
|
|
1539
|
+
node.fieldFamily === "NUMPAGES" ||
|
|
1540
|
+
node.fieldFamily === "SECTIONPAGES";
|
|
1541
|
+
const isPageScopedField =
|
|
1542
|
+
node.fieldFamily === "PAGE" ||
|
|
1543
|
+
node.fieldFamily === "NUMPAGES" ||
|
|
1544
|
+
node.fieldFamily === "SECTIONPAGES";
|
|
1545
|
+
if (isPageScopedField) {
|
|
1546
|
+
const fieldLabel =
|
|
1547
|
+
node.fieldFamily === "PAGE"
|
|
1548
|
+
? "Current page number"
|
|
1549
|
+
: node.fieldFamily === "NUMPAGES"
|
|
1550
|
+
? "Total pages"
|
|
1551
|
+
: "Section pages";
|
|
1552
|
+
const displayText = flattenSurfaceFieldDisplayText(node.children);
|
|
1553
|
+
paragraph.segments.push({
|
|
1554
|
+
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
1555
|
+
kind: "field_ref",
|
|
1556
|
+
from: start,
|
|
1557
|
+
to: start + 1,
|
|
1558
|
+
fieldFamily: node.fieldFamily,
|
|
1559
|
+
fieldTarget: node.fieldTarget,
|
|
1560
|
+
instruction: node.instruction,
|
|
1561
|
+
refreshStatus: node.refreshStatus ?? "stale",
|
|
1562
|
+
label: fieldLabel,
|
|
1563
|
+
...(displayText ? { displayText } : {}),
|
|
1564
|
+
} as SurfaceInlineSegment);
|
|
1565
|
+
return { nextCursor: start + 1, lockedFragmentIds: [] };
|
|
1566
|
+
}
|
|
1540
1567
|
if (node.children && node.children.length > 0) {
|
|
1541
1568
|
// For REF \h, pass the bookmark as a hyperlink href so child text gets hyperlink styling
|
|
1542
1569
|
const refHyperlinkHref =
|
|
@@ -1574,7 +1601,9 @@ function appendInlineSegments(
|
|
|
1574
1601
|
? "Current page number"
|
|
1575
1602
|
: node.fieldFamily === "NUMPAGES"
|
|
1576
1603
|
? "Total pages"
|
|
1577
|
-
:
|
|
1604
|
+
: node.fieldFamily === "SECTIONPAGES"
|
|
1605
|
+
? "Section pages"
|
|
1606
|
+
: `${node.fieldFamily ?? "Field"}: ${node.fieldTarget ?? node.instruction.trim()}`;
|
|
1578
1607
|
paragraph.segments.push({
|
|
1579
1608
|
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
1580
1609
|
kind: "field_ref",
|
|
@@ -1743,6 +1772,34 @@ function normalizeSafeCssHexColor(value: string | undefined): string | undefined
|
|
|
1743
1772
|
return trimmed.replace(/^#/, "").toUpperCase();
|
|
1744
1773
|
}
|
|
1745
1774
|
|
|
1775
|
+
function flattenSurfaceFieldDisplayText(
|
|
1776
|
+
children: readonly InlineNode[] | undefined,
|
|
1777
|
+
): string {
|
|
1778
|
+
if (!children || children.length === 0) return "";
|
|
1779
|
+
const parts: string[] = [];
|
|
1780
|
+
for (const child of children) {
|
|
1781
|
+
switch (child.type) {
|
|
1782
|
+
case "text":
|
|
1783
|
+
parts.push(child.text);
|
|
1784
|
+
break;
|
|
1785
|
+
case "tab":
|
|
1786
|
+
case "hard_break":
|
|
1787
|
+
parts.push(" ");
|
|
1788
|
+
break;
|
|
1789
|
+
case "symbol":
|
|
1790
|
+
parts.push(child.char ? String.fromCodePoint(parseInt(child.char, 16)) : "\uFFFD");
|
|
1791
|
+
break;
|
|
1792
|
+
case "field":
|
|
1793
|
+
case "hyperlink":
|
|
1794
|
+
parts.push(flattenSurfaceFieldDisplayText(child.children));
|
|
1795
|
+
break;
|
|
1796
|
+
default:
|
|
1797
|
+
break;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
return parts.join("");
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1746
1803
|
/**
|
|
1747
1804
|
* V2c.5 — Extract the first paragraph's plain text from a parsed
|
|
1748
1805
|
* `txbxBlocks` tree for the `txbxText` segment preview. The recursion
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
import type {
|
|
31
31
|
EditorSessionState,
|
|
32
32
|
EditorStoryTarget,
|
|
33
|
-
FormattingAlignment,
|
|
34
33
|
HeaderFooterLinkPatch,
|
|
35
34
|
InsertImageOptions,
|
|
36
35
|
InsertTableOptions,
|
|
@@ -41,7 +40,6 @@ import type {
|
|
|
41
40
|
SelectionSnapshot as PublicSelectionSnapshot,
|
|
42
41
|
StyleCatalogSnapshot,
|
|
43
42
|
SurfaceBlockSnapshot,
|
|
44
|
-
SurfaceInlineSegment,
|
|
45
43
|
TableOp,
|
|
46
44
|
} from "../api/public-types";
|
|
47
45
|
import {
|
|
@@ -51,7 +49,7 @@ import {
|
|
|
51
49
|
storyTargetsEqual,
|
|
52
50
|
type TransactionMapping,
|
|
53
51
|
} from "../core/selection/mapping.ts";
|
|
54
|
-
import {
|
|
52
|
+
import type { FormattingOperation } from "../core/commands/formatting-commands.ts";
|
|
55
53
|
import {
|
|
56
54
|
applyParagraphStyleToDocument,
|
|
57
55
|
applyTableStyleToDocument,
|
|
@@ -220,178 +218,15 @@ export function getRuntimeStyleCatalog(
|
|
|
220
218
|
// Formatting (flat + suggesting-mode)
|
|
221
219
|
// ---------------------------------------------------------------------------
|
|
222
220
|
|
|
223
|
-
type FormattingOperation =
|
|
224
|
-
| { type: "toggle"; mark: "bold" | "italic" | "underline" | "strikethrough" | "superscript" | "subscript" }
|
|
225
|
-
| { type: "set-font-family"; fontFamily: string | null }
|
|
226
|
-
| { type: "set-font-size"; size: number | null }
|
|
227
|
-
| { type: "set-text-color"; color: string | null }
|
|
228
|
-
| { type: "set-highlight-color"; color: string | null }
|
|
229
|
-
| { type: "set-alignment"; alignment: FormattingAlignment }
|
|
230
|
-
| { type: "indent" }
|
|
231
|
-
| { type: "outdent" };
|
|
232
|
-
|
|
233
221
|
export function applyRuntimeFormattingOperation(
|
|
234
222
|
runtime: WordReviewEditorRuntime,
|
|
235
223
|
operation: FormattingOperation,
|
|
236
224
|
): void {
|
|
237
|
-
|
|
238
|
-
if (applySuggestingFormattingOperation(runtime, operation)) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (emitSuggestingUnsupportedMutation(runtime, getFormattingOperationCommandName(operation))) {
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const context = getStoryMutationContext(runtime, getFormattingOperationCommandName(operation));
|
|
246
|
-
if (!context) {
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const result = applyFormattingOperationToDocument(
|
|
251
|
-
context.localDocument,
|
|
252
|
-
context.localSnapshot,
|
|
253
|
-
operation,
|
|
254
|
-
);
|
|
255
|
-
dispatchStoryMutationResult(
|
|
256
|
-
runtime,
|
|
257
|
-
context,
|
|
258
|
-
{
|
|
259
|
-
...result,
|
|
260
|
-
selection: toRuntimeSelectionSnapshot(result.selection),
|
|
261
|
-
},
|
|
262
|
-
context.timestamp,
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function applySuggestingFormattingOperation(
|
|
267
|
-
runtime: WordReviewEditorRuntime,
|
|
268
|
-
operation: FormattingOperation,
|
|
269
|
-
): boolean {
|
|
270
|
-
const commandName = getFormattingOperationCommandName(operation);
|
|
271
|
-
const context = getStoryMutationContext(runtime, commandName);
|
|
272
|
-
if (!context) {
|
|
273
|
-
return true;
|
|
274
|
-
}
|
|
275
|
-
if (context.activeStory.kind !== "main") {
|
|
276
|
-
runtime.emitBlockedCommand(commandName, [{
|
|
277
|
-
code: "suggesting_unsupported",
|
|
278
|
-
message: `"${commandName}" is not supported in suggesting mode for this story.`,
|
|
279
|
-
}]);
|
|
280
|
-
return true;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (operation.type === "set-alignment" || operation.type === "indent" || operation.type === "outdent") {
|
|
284
|
-
const paragraphContext = resolveActiveParagraphContext(context.localSnapshot);
|
|
285
|
-
if (!paragraphContext) {
|
|
286
|
-
return true;
|
|
287
|
-
}
|
|
288
|
-
const beforeXml = buildParagraphPropertyBeforeXml(paragraphContext.paragraph);
|
|
289
|
-
const result = applyFormattingOperationToDocument(
|
|
290
|
-
context.localDocument,
|
|
291
|
-
context.localSnapshot,
|
|
292
|
-
operation,
|
|
293
|
-
);
|
|
294
|
-
if (!result.changed) {
|
|
295
|
-
return true;
|
|
296
|
-
}
|
|
297
|
-
const nextDocument = appendPropertyChangeSuggestion(
|
|
298
|
-
result.document,
|
|
299
|
-
{
|
|
300
|
-
from: paragraphContext.paragraph.from,
|
|
301
|
-
to: paragraphContext.paragraph.to,
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
originalRevisionType: "pPrChange",
|
|
305
|
-
xmlTag: "pPrChange",
|
|
306
|
-
beforeXml,
|
|
307
|
-
semanticKind: "paragraph-property-change",
|
|
308
|
-
storyTarget: context.activeStory,
|
|
309
|
-
authorId: runtime.getDefaultAuthorId?.(),
|
|
310
|
-
},
|
|
311
|
-
context.timestamp,
|
|
312
|
-
);
|
|
313
|
-
dispatchStoryMutationResult(
|
|
314
|
-
runtime,
|
|
315
|
-
context,
|
|
316
|
-
{
|
|
317
|
-
changed: true,
|
|
318
|
-
document: nextDocument,
|
|
319
|
-
selection: toRuntimeSelectionSnapshot(result.selection),
|
|
320
|
-
},
|
|
321
|
-
context.timestamp,
|
|
322
|
-
);
|
|
323
|
-
return true;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const segment = findSingleSelectedTextSegment(context.localSnapshot);
|
|
327
|
-
if (!segment) {
|
|
328
|
-
runtime.emitBlockedCommand(commandName, [{
|
|
329
|
-
code: "suggesting_unsupported",
|
|
330
|
-
message: `"${commandName}" requires one bounded text segment in suggesting mode.`,
|
|
331
|
-
}]);
|
|
332
|
-
return true;
|
|
333
|
-
}
|
|
334
|
-
const beforeXml = buildRunPropertyBeforeXml(segment);
|
|
335
|
-
const result = applyFormattingOperationToDocument(
|
|
336
|
-
context.localDocument,
|
|
337
|
-
context.localSnapshot,
|
|
338
|
-
operation,
|
|
339
|
-
);
|
|
340
|
-
if (!result.changed) {
|
|
341
|
-
return true;
|
|
342
|
-
}
|
|
343
|
-
const nextDocument = appendPropertyChangeSuggestion(
|
|
344
|
-
result.document,
|
|
345
|
-
{
|
|
346
|
-
from: segment.from,
|
|
347
|
-
to: segment.to,
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
originalRevisionType: "rPrChange",
|
|
351
|
-
xmlTag: "rPrChange",
|
|
352
|
-
beforeXml,
|
|
353
|
-
semanticKind: "formatting-change",
|
|
354
|
-
storyTarget: context.activeStory,
|
|
355
|
-
authorId: runtime.getDefaultAuthorId?.(),
|
|
356
|
-
},
|
|
357
|
-
context.timestamp,
|
|
358
|
-
);
|
|
359
|
-
dispatchStoryMutationResult(
|
|
360
|
-
runtime,
|
|
361
|
-
context,
|
|
362
|
-
{
|
|
363
|
-
changed: true,
|
|
364
|
-
document: nextDocument,
|
|
365
|
-
selection: toRuntimeSelectionSnapshot(result.selection),
|
|
366
|
-
},
|
|
367
|
-
context.timestamp,
|
|
368
|
-
);
|
|
369
|
-
return true;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
function getFormattingOperationCommandName(operation: FormattingOperation): string {
|
|
373
|
-
switch (operation.type) {
|
|
374
|
-
case "toggle":
|
|
375
|
-
return `toggle${operation.mark.charAt(0).toUpperCase()}${operation.mark.slice(1)}`;
|
|
376
|
-
case "set-font-family":
|
|
377
|
-
return "setFontFamily";
|
|
378
|
-
case "set-font-size":
|
|
379
|
-
return "setFontSize";
|
|
380
|
-
case "set-text-color":
|
|
381
|
-
return "setTextColor";
|
|
382
|
-
case "set-highlight-color":
|
|
383
|
-
return "setHighlightColor";
|
|
384
|
-
case "set-alignment":
|
|
385
|
-
return "setAlignment";
|
|
386
|
-
case "indent":
|
|
387
|
-
return "indent";
|
|
388
|
-
case "outdent":
|
|
389
|
-
return "outdent";
|
|
390
|
-
}
|
|
225
|
+
runtime.applyFormattingOperation(operation);
|
|
391
226
|
}
|
|
392
227
|
|
|
393
228
|
// ---------------------------------------------------------------------------
|
|
394
|
-
// Suggesting-mode
|
|
229
|
+
// Suggesting-mode guard helper for commands without bounded redline support
|
|
395
230
|
// ---------------------------------------------------------------------------
|
|
396
231
|
|
|
397
232
|
function emitSuggestingUnsupportedMutation(
|
|
@@ -409,192 +244,6 @@ function emitSuggestingUnsupportedMutation(
|
|
|
409
244
|
return true;
|
|
410
245
|
}
|
|
411
246
|
|
|
412
|
-
function appendPropertyChangeSuggestion(
|
|
413
|
-
document: EditorSessionState["canonicalDocument"],
|
|
414
|
-
anchor: { from: number; to: number },
|
|
415
|
-
input: {
|
|
416
|
-
originalRevisionType: "rPrChange" | "pPrChange";
|
|
417
|
-
xmlTag: "rPrChange" | "pPrChange";
|
|
418
|
-
beforeXml: string;
|
|
419
|
-
semanticKind: "formatting-change" | "paragraph-property-change";
|
|
420
|
-
storyTarget: EditorStoryTarget;
|
|
421
|
-
authorId?: string;
|
|
422
|
-
},
|
|
423
|
-
timestamp: string,
|
|
424
|
-
): EditorSessionState["canonicalDocument"] {
|
|
425
|
-
const existing = document.review.revisions;
|
|
426
|
-
const changeId = createRuntimeSuggestionChangeId(existing, timestamp);
|
|
427
|
-
const resolvedAuthorId = input.authorId ?? "unknown";
|
|
428
|
-
return {
|
|
429
|
-
...document,
|
|
430
|
-
review: {
|
|
431
|
-
...document.review,
|
|
432
|
-
revisions: {
|
|
433
|
-
...existing,
|
|
434
|
-
[changeId]: {
|
|
435
|
-
changeId,
|
|
436
|
-
kind: "property-change",
|
|
437
|
-
anchor: createRangeAnchor(anchor.from, anchor.to, { start: 1, end: -1 }),
|
|
438
|
-
authorId: resolvedAuthorId,
|
|
439
|
-
createdAt: timestamp,
|
|
440
|
-
warningIds: [],
|
|
441
|
-
metadata: {
|
|
442
|
-
source: "runtime",
|
|
443
|
-
storyTarget: input.storyTarget,
|
|
444
|
-
suggestionId: changeId,
|
|
445
|
-
semanticKind: input.semanticKind,
|
|
446
|
-
originalRevisionType: input.originalRevisionType,
|
|
447
|
-
propertyChangeData: {
|
|
448
|
-
xmlTag: input.xmlTag,
|
|
449
|
-
beforeXml: input.beforeXml,
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
status: "open",
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
function createRuntimeSuggestionChangeId(
|
|
460
|
-
existing: EditorSessionState["canonicalDocument"]["review"]["revisions"],
|
|
461
|
-
timestamp: string,
|
|
462
|
-
): string {
|
|
463
|
-
const base = `change-${timestamp.replace(/[^0-9]/gu, "")}`;
|
|
464
|
-
let counter = Object.keys(existing).length + 1;
|
|
465
|
-
let candidate = `${base}-p${counter}`;
|
|
466
|
-
while (existing[candidate]) {
|
|
467
|
-
counter += 1;
|
|
468
|
-
candidate = `${base}-p${counter}`;
|
|
469
|
-
}
|
|
470
|
-
return candidate;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function findSingleSelectedTextSegment(
|
|
474
|
-
snapshot: Pick<RuntimeRenderSnapshot, "surface" | "selection">,
|
|
475
|
-
): Extract<SurfaceInlineSegment, { kind: "text" }> | null {
|
|
476
|
-
if (!snapshot.surface || snapshot.selection.activeRange.kind !== "range" || snapshot.selection.isCollapsed) {
|
|
477
|
-
return null;
|
|
478
|
-
}
|
|
479
|
-
const selectionFrom = Math.min(snapshot.selection.anchor, snapshot.selection.head);
|
|
480
|
-
const selectionTo = Math.max(snapshot.selection.anchor, snapshot.selection.head);
|
|
481
|
-
const segments = collectSelectedTextSegments(snapshot.surface.blocks, selectionFrom, selectionTo);
|
|
482
|
-
if (segments.length !== 1) {
|
|
483
|
-
return null;
|
|
484
|
-
}
|
|
485
|
-
const [segment] = segments;
|
|
486
|
-
if (!segment || segment.from !== selectionFrom || segment.to !== selectionTo) {
|
|
487
|
-
return null;
|
|
488
|
-
}
|
|
489
|
-
return segment;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function collectSelectedTextSegments(
|
|
493
|
-
blocks: readonly SurfaceBlockSnapshot[],
|
|
494
|
-
selectionFrom: number,
|
|
495
|
-
selectionTo: number,
|
|
496
|
-
output: Array<Extract<SurfaceInlineSegment, { kind: "text" }>> = [],
|
|
497
|
-
): Array<Extract<SurfaceInlineSegment, { kind: "text" }>> {
|
|
498
|
-
for (const block of blocks) {
|
|
499
|
-
if (block.kind === "paragraph") {
|
|
500
|
-
for (const segment of block.segments) {
|
|
501
|
-
if (
|
|
502
|
-
segment.kind === "text" &&
|
|
503
|
-
rangesOverlap(selectionFrom, selectionTo, segment.from, segment.to)
|
|
504
|
-
) {
|
|
505
|
-
output.push(segment);
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
510
|
-
if (block.kind === "table") {
|
|
511
|
-
for (const row of block.rows) {
|
|
512
|
-
for (const cell of row.cells) {
|
|
513
|
-
collectSelectedTextSegments(cell.content, selectionFrom, selectionTo, output);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
continue;
|
|
517
|
-
}
|
|
518
|
-
if (block.kind === "sdt_block") {
|
|
519
|
-
collectSelectedTextSegments(block.children, selectionFrom, selectionTo, output);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
return output;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function rangesOverlap(
|
|
526
|
-
leftFrom: number,
|
|
527
|
-
leftTo: number,
|
|
528
|
-
rightFrom: number,
|
|
529
|
-
rightTo: number,
|
|
530
|
-
): boolean {
|
|
531
|
-
return leftFrom < rightTo && rightFrom < leftTo;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function buildRunPropertyBeforeXml(
|
|
535
|
-
segment: Extract<SurfaceInlineSegment, { kind: "text" }>,
|
|
536
|
-
): string {
|
|
537
|
-
const parts: string[] = [];
|
|
538
|
-
const marks = new Set(segment.marks ?? []);
|
|
539
|
-
if (marks.has("bold")) parts.push("<w:b/>");
|
|
540
|
-
if (marks.has("italic")) parts.push("<w:i/>");
|
|
541
|
-
if (marks.has("underline")) parts.push("<w:u w:val=\"single\"/>");
|
|
542
|
-
if (marks.has("strikethrough")) parts.push("<w:strike/>");
|
|
543
|
-
if (marks.has("superscript")) parts.push("<w:vertAlign w:val=\"superscript\"/>");
|
|
544
|
-
if (marks.has("subscript")) parts.push("<w:vertAlign w:val=\"subscript\"/>");
|
|
545
|
-
if (segment.markAttrs?.fontFamily) {
|
|
546
|
-
parts.push(`<w:rFonts w:ascii="${escapeAttributeXml(segment.markAttrs.fontFamily)}" w:hAnsi="${escapeAttributeXml(segment.markAttrs.fontFamily)}"/>`);
|
|
547
|
-
}
|
|
548
|
-
if (segment.markAttrs?.fontSize !== undefined) {
|
|
549
|
-
parts.push(`<w:sz w:val="${segment.markAttrs.fontSize}"/>`);
|
|
550
|
-
}
|
|
551
|
-
if (segment.markAttrs?.textColor) {
|
|
552
|
-
parts.push(`<w:color w:val="${escapeAttributeXml(segment.markAttrs.textColor)}"/>`);
|
|
553
|
-
}
|
|
554
|
-
if (segment.markAttrs?.backgroundColor) {
|
|
555
|
-
parts.push(`<w:shd w:val="clear" w:color="auto" w:fill="${escapeAttributeXml(segment.markAttrs.backgroundColor)}"/>`);
|
|
556
|
-
}
|
|
557
|
-
return `<w:rPr>${parts.join("")}</w:rPr>`;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
function buildParagraphPropertyBeforeXml(
|
|
561
|
-
paragraph: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>,
|
|
562
|
-
): string {
|
|
563
|
-
const parts: string[] = [];
|
|
564
|
-
if (paragraph.styleId) {
|
|
565
|
-
parts.push(`<w:pStyle w:val="${escapeAttributeXml(paragraph.styleId)}"/>`);
|
|
566
|
-
}
|
|
567
|
-
if (paragraph.numbering) {
|
|
568
|
-
parts.push(
|
|
569
|
-
`<w:numPr><w:ilvl w:val="${paragraph.numbering.level}"/><w:numId w:val="${escapeAttributeXml(
|
|
570
|
-
paragraph.numbering.numberingInstanceId.replace(/^num:/u, ""),
|
|
571
|
-
)}"/></w:numPr>`,
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
if (paragraph.alignment) {
|
|
575
|
-
parts.push(`<w:jc w:val="${escapeAttributeXml(paragraph.alignment)}"/>`);
|
|
576
|
-
}
|
|
577
|
-
if (paragraph.indentation) {
|
|
578
|
-
const attrs: string[] = [];
|
|
579
|
-
if (paragraph.indentation.left !== undefined) attrs.push(`w:left="${paragraph.indentation.left}"`);
|
|
580
|
-
if (paragraph.indentation.right !== undefined) attrs.push(`w:right="${paragraph.indentation.right}"`);
|
|
581
|
-
if (paragraph.indentation.firstLine !== undefined) attrs.push(`w:firstLine="${paragraph.indentation.firstLine}"`);
|
|
582
|
-
if (paragraph.indentation.hanging !== undefined) attrs.push(`w:hanging="${paragraph.indentation.hanging}"`);
|
|
583
|
-
if (attrs.length > 0) {
|
|
584
|
-
parts.push(`<w:ind ${attrs.join(" ")}/>`);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
return `<w:pPr>${parts.join("")}</w:pPr>`;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function escapeAttributeXml(value: string): string {
|
|
591
|
-
return value
|
|
592
|
-
.replace(/&/g, "&")
|
|
593
|
-
.replace(/</g, "<")
|
|
594
|
-
.replace(/>/g, ">")
|
|
595
|
-
.replace(/"/g, """);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
247
|
// ---------------------------------------------------------------------------
|
|
599
248
|
// List toggle / paragraph style / table style
|
|
600
249
|
// ---------------------------------------------------------------------------
|
|
@@ -1098,6 +1098,7 @@ function createLoadingRuntimeBridge(input: {
|
|
|
1098
1098
|
getFontEntry: (name: string) =>
|
|
1099
1099
|
input.sessionState.canonicalDocument.fontTable?.fonts[name],
|
|
1100
1100
|
replaceText: () => undefined,
|
|
1101
|
+
applyFormattingOperation: () => undefined,
|
|
1101
1102
|
applyScopeReplacement: () => undefined,
|
|
1102
1103
|
insertFragment: () => undefined,
|
|
1103
1104
|
copy: () => undefined,
|
|
@@ -1133,6 +1134,13 @@ function createLoadingRuntimeBridge(input: {
|
|
|
1133
1134
|
addCommentReply: () => {
|
|
1134
1135
|
throw createLoadingBoundaryError(input.snapshot.documentId, "comment");
|
|
1135
1136
|
},
|
|
1137
|
+
getCommentThreadForChange: () => null,
|
|
1138
|
+
ensureCommentThreadForChange: () => {
|
|
1139
|
+
throw createLoadingBoundaryError(input.snapshot.documentId, "comment");
|
|
1140
|
+
},
|
|
1141
|
+
addReplyToChange: () => {
|
|
1142
|
+
throw createLoadingBoundaryError(input.snapshot.documentId, "comment");
|
|
1143
|
+
},
|
|
1136
1144
|
editCommentBody: () => undefined,
|
|
1137
1145
|
addScope: () => {
|
|
1138
1146
|
throw createLoadingBoundaryError(input.snapshot.documentId, "scope");
|