@beyondwork/docx-react-component 1.0.37 → 1.0.39
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 +41 -31
- package/src/api/public-types.ts +496 -1
- package/src/core/commands/section-layout-commands.ts +58 -0
- package/src/core/commands/table-grid.ts +431 -0
- package/src/core/commands/table-structure-commands.ts +845 -56
- package/src/core/commands/text-commands.ts +122 -2
- package/src/io/docx-session.ts +1 -0
- package/src/io/export/serialize-main-document.ts +2 -11
- package/src/io/export/serialize-numbering.ts +43 -10
- package/src/io/export/serialize-paragraph-formatting.ts +152 -0
- package/src/io/export/serialize-run-formatting.ts +90 -0
- package/src/io/export/serialize-styles.ts +212 -0
- package/src/io/export/serialize-tables.ts +74 -0
- package/src/io/export/table-properties-xml.ts +139 -4
- package/src/io/normalize/normalize-text.ts +15 -0
- package/src/io/ooxml/parse-fields.ts +10 -3
- package/src/io/ooxml/parse-footnotes.ts +60 -0
- package/src/io/ooxml/parse-headers-footers.ts +60 -0
- package/src/io/ooxml/parse-main-document.ts +137 -0
- package/src/io/ooxml/parse-numbering.ts +41 -1
- package/src/io/ooxml/parse-paragraph-formatting.ts +188 -0
- package/src/io/ooxml/parse-run-formatting.ts +129 -0
- package/src/io/ooxml/parse-styles.ts +31 -0
- package/src/io/ooxml/parse-tables.ts +249 -0
- package/src/io/ooxml/xml-attr-helpers.ts +60 -0
- package/src/io/ooxml/xml-element.ts +19 -0
- package/src/model/canonical-document.ts +117 -3
- package/src/runtime/collab/event-types.ts +165 -0
- package/src/runtime/collab/index.ts +22 -0
- package/src/runtime/collab/remote-cursor-awareness.ts +93 -0
- package/src/runtime/collab/runtime-collab-sync.ts +273 -0
- package/src/runtime/document-layout.ts +4 -2
- package/src/runtime/document-navigation.ts +1 -1
- package/src/runtime/document-runtime.ts +248 -18
- package/src/runtime/layout/default-page-format.ts +96 -0
- package/src/runtime/layout/index.ts +47 -0
- package/src/runtime/layout/inert-layout-facet.ts +16 -0
- package/src/runtime/layout/layout-engine-instance.ts +100 -23
- package/src/runtime/layout/layout-invalidation.ts +14 -5
- package/src/runtime/layout/margin-preset-catalog.ts +178 -0
- package/src/runtime/layout/page-format-catalog.ts +233 -0
- package/src/runtime/layout/page-graph.ts +55 -0
- package/src/runtime/layout/paginate-paragraph-lines.ts +128 -0
- package/src/runtime/layout/paginated-layout-engine.ts +484 -37
- package/src/runtime/layout/project-block-fragments.ts +225 -0
- package/src/runtime/layout/public-facet.ts +748 -16
- package/src/runtime/layout/resolve-page-fields.ts +70 -0
- package/src/runtime/layout/resolve-page-previews.ts +185 -0
- package/src/runtime/layout/resolved-formatting-state.ts +30 -26
- package/src/runtime/layout/table-render-plan.ts +249 -0
- package/src/runtime/numbering-prefix.ts +5 -0
- package/src/runtime/paragraph-style-resolver.ts +194 -0
- package/src/runtime/render/block-fragment-projection.ts +35 -0
- package/src/runtime/render/decoration-resolver.ts +189 -0
- package/src/runtime/render/index.ts +57 -0
- package/src/runtime/render/pending-op-delta-reader.ts +129 -0
- package/src/runtime/render/render-frame-types.ts +317 -0
- package/src/runtime/render/render-kernel.ts +759 -0
- package/src/runtime/resolved-numbering-geometry.ts +9 -1
- package/src/runtime/surface-projection.ts +129 -9
- package/src/runtime/table-schema.ts +11 -0
- package/src/runtime/view-state.ts +67 -0
- package/src/runtime/workflow-markup.ts +1 -5
- package/src/runtime/workflow-rail-segments.ts +280 -0
- package/src/ui/WordReviewEditor.tsx +368 -19
- package/src/ui/editor-command-bag.ts +4 -0
- package/src/ui/editor-runtime-boundary.ts +16 -0
- package/src/ui/editor-shell-view.tsx +10 -0
- package/src/ui/editor-surface-controller.tsx +9 -1
- package/src/ui/headless/chrome-registry.ts +310 -15
- package/src/ui/headless/scoped-chrome-policy.ts +49 -1
- package/src/ui/headless/selection-tool-types.ts +10 -0
- package/src/ui-tailwind/chrome/chrome-preset-model.ts +23 -2
- package/src/ui-tailwind/chrome/review-queue-bar.tsx +2 -14
- package/src/ui-tailwind/chrome/role-action-sets.ts +80 -0
- package/src/ui-tailwind/chrome/tw-detach-handle.tsx +147 -0
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +160 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +68 -92
- package/src/ui-tailwind/chrome/tw-selection-tool-placement.ts +149 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +11 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +15 -4
- package/src/ui-tailwind/chrome/tw-table-border-picker.tsx +245 -0
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +356 -140
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +284 -0
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +94 -0
- package/src/ui-tailwind/chrome-overlay/index.ts +16 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +96 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +178 -0
- package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +4 -0
- package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +11 -0
- package/src/ui-tailwind/editor-surface/page-slice-util.ts +15 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +7 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +40 -4
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +22 -12
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +389 -0
- package/src/ui-tailwind/editor-surface/pm-schema.ts +40 -2
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +144 -62
- package/src/ui-tailwind/editor-surface/remote-cursor-plugin.ts +179 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +559 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +224 -75
- package/src/ui-tailwind/editor-surface/tw-table-bands.css +61 -0
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +19 -0
- package/src/ui-tailwind/index.ts +29 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +2 -2
- package/src/ui-tailwind/review/tw-rail-card.tsx +150 -0
- package/src/ui-tailwind/review/tw-review-rail-footer.tsx +52 -0
- package/src/ui-tailwind/review/tw-review-rail.tsx +166 -11
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +108 -0
- package/src/ui-tailwind/theme/editor-theme.css +498 -163
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +680 -0
- package/src/ui-tailwind/toolbar/tw-scope-posture-menu.tsx +182 -0
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +162 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +104 -2
- package/src/ui-tailwind/tw-review-workspace.tsx +234 -21
- package/src/runtime/collab-review-sync.ts +0 -254
- package/src/ui-tailwind/editor-surface/pm-collab-plugins.ts +0 -40
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
CanonicalRunFormatting,
|
|
2
3
|
NumberingCatalog,
|
|
3
4
|
NumberingLevelDefinition,
|
|
4
5
|
NumberingLevelOverrideDefinition,
|
|
@@ -27,6 +28,7 @@ export interface ResolvedNumberingGeometry {
|
|
|
27
28
|
firstLine?: number;
|
|
28
29
|
hanging?: number;
|
|
29
30
|
};
|
|
31
|
+
markerRunProperties?: CanonicalRunFormatting;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export interface ResolvedNumberingDefinitionSet {
|
|
@@ -67,7 +69,7 @@ export function resolveNumberingDefinitionSet(
|
|
|
67
69
|
abstractDefinition,
|
|
68
70
|
effectiveLevels,
|
|
69
71
|
effectiveLevel,
|
|
70
|
-
geometry: resolveNumberingGeometry(effectiveLevel.paragraphGeometry, paragraph),
|
|
72
|
+
geometry: resolveNumberingGeometry(effectiveLevel.paragraphGeometry, paragraph, effectiveLevel.runProperties),
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -110,6 +112,8 @@ function mergeLevelDefinition(
|
|
|
110
112
|
base?.paragraphGeometry,
|
|
111
113
|
override?.paragraphGeometry,
|
|
112
114
|
);
|
|
115
|
+
const runProperties = override?.runProperties ?? base?.runProperties;
|
|
116
|
+
const restartAfterLevel = override?.restartAfterLevel ?? base?.restartAfterLevel;
|
|
113
117
|
|
|
114
118
|
return {
|
|
115
119
|
level,
|
|
@@ -124,6 +128,8 @@ function mergeLevelDefinition(
|
|
|
124
128
|
: {}),
|
|
125
129
|
...(override?.suffix ?? base?.suffix ? { suffix: override?.suffix ?? base?.suffix } : {}),
|
|
126
130
|
...(paragraphGeometry ? { paragraphGeometry } : {}),
|
|
131
|
+
...(runProperties ? { runProperties } : {}),
|
|
132
|
+
...(restartAfterLevel !== undefined ? { restartAfterLevel } : {}),
|
|
127
133
|
};
|
|
128
134
|
}
|
|
129
135
|
|
|
@@ -179,6 +185,7 @@ function mergeLevelParagraphGeometry(
|
|
|
179
185
|
function resolveNumberingGeometry(
|
|
180
186
|
levelGeometry: NumberingLevelParagraphGeometry | undefined,
|
|
181
187
|
paragraph: Pick<ParagraphNode, "spacing" | "indentation" | "tabStops"> | undefined,
|
|
188
|
+
levelRunProperties: CanonicalRunFormatting | undefined,
|
|
182
189
|
): ResolvedNumberingGeometry {
|
|
183
190
|
const spacing = mergeParagraphSpacing(levelGeometry?.spacing, paragraph?.spacing);
|
|
184
191
|
const indentation = mergeParagraphIndentation(levelGeometry?.indentation, paragraph?.indentation);
|
|
@@ -197,6 +204,7 @@ function resolveNumberingGeometry(
|
|
|
197
204
|
...(tabStops && tabStops.length > 0 ? { tabStops } : {}),
|
|
198
205
|
...(markerLane ? { markerLane } : {}),
|
|
199
206
|
...(textColumn ? { textColumn } : {}),
|
|
207
|
+
...(levelRunProperties ? { markerRunProperties: levelRunProperties } : {}),
|
|
200
208
|
};
|
|
201
209
|
}
|
|
202
210
|
|
|
@@ -46,6 +46,12 @@ import {
|
|
|
46
46
|
resolveSectionVariants,
|
|
47
47
|
} from "./story-context.ts";
|
|
48
48
|
import { resolveTableStyleResolution } from "./table-style-resolver.ts";
|
|
49
|
+
import {
|
|
50
|
+
resolveEffectiveParagraphFormatting,
|
|
51
|
+
resolveEffectiveRunFormatting,
|
|
52
|
+
resolveNumberingMarkerRunFormatting,
|
|
53
|
+
} from "./paragraph-style-resolver.ts";
|
|
54
|
+
import type { CanonicalParagraphFormatting, CanonicalRunFormatting } from "../model/canonical-document.ts";
|
|
49
55
|
|
|
50
56
|
interface ParagraphAccumulator {
|
|
51
57
|
blockId: string;
|
|
@@ -57,6 +63,7 @@ interface ParagraphAccumulator {
|
|
|
57
63
|
numberingPrefix?: string;
|
|
58
64
|
numberingSuffix?: "tab" | "space" | "nothing";
|
|
59
65
|
resolvedNumbering?: Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>["resolvedNumbering"];
|
|
66
|
+
resolvedParagraphFormatting?: CanonicalParagraphFormatting;
|
|
60
67
|
contextualSpacing?: boolean;
|
|
61
68
|
segments: SurfaceInlineSegment[];
|
|
62
69
|
}
|
|
@@ -341,6 +348,16 @@ function createTableBlock(
|
|
|
341
348
|
}
|
|
342
349
|
const resolvedCell = resolvedRow?.cells[cellIndex];
|
|
343
350
|
const cellBorders = resolveCellBorderStyles(resolvedCell?.borders ?? cell.borders);
|
|
351
|
+
// R2a: project the resolved conditional-format regions into CSS class
|
|
352
|
+
// names so the NodeView can paint band colors via theme vars instead of
|
|
353
|
+
// inline styles. Direct shading overrides still win at render time.
|
|
354
|
+
const bandClasses =
|
|
355
|
+
resolvedCell?.activeConditionalRegions &&
|
|
356
|
+
resolvedCell.activeConditionalRegions.length > 0
|
|
357
|
+
? resolvedCell.activeConditionalRegions
|
|
358
|
+
.map((region) => `band-${region}`)
|
|
359
|
+
.join(" ")
|
|
360
|
+
: null;
|
|
344
361
|
cells.push({
|
|
345
362
|
gridSpan: cell.gridSpan ?? 1,
|
|
346
363
|
verticalMerge: cell.verticalMerge ?? null,
|
|
@@ -352,6 +369,7 @@ function createTableBlock(
|
|
|
352
369
|
...(cellBorders.borderRight ? { borderRight: cellBorders.borderRight } : {}),
|
|
353
370
|
...(cellBorders.borderBottom ? { borderBottom: cellBorders.borderBottom } : {}),
|
|
354
371
|
...(cellBorders.borderLeft ? { borderLeft: cellBorders.borderLeft } : {}),
|
|
372
|
+
...(bandClasses ? { bandClasses } : {}),
|
|
355
373
|
content: cellContent,
|
|
356
374
|
});
|
|
357
375
|
}
|
|
@@ -365,6 +383,7 @@ function createTableBlock(
|
|
|
365
383
|
...(resolvedRow?.style.height !== undefined ? { height: resolvedRow.style.height } : {}),
|
|
366
384
|
...(resolvedRow?.style.heightRule ? { heightRule: resolvedRow.style.heightRule } : {}),
|
|
367
385
|
...(headerLike ? { isHeader: true } : {}),
|
|
386
|
+
...(row.cantSplit ? { cantSplit: true } : {}),
|
|
368
387
|
});
|
|
369
388
|
}
|
|
370
389
|
|
|
@@ -523,6 +542,26 @@ function createParagraphBlock(
|
|
|
523
542
|
const resolvedNumbering = effectiveNumbering
|
|
524
543
|
? numberingPrefixResolver.resolveDetailed(effectiveNumbering, paragraph)
|
|
525
544
|
: null;
|
|
545
|
+
|
|
546
|
+
// Task 11: compute cascaded paragraph formatting
|
|
547
|
+
const stylesCatalog = document.styles;
|
|
548
|
+
const directParagraphFormatting = buildDirectParagraphFormattingFromNode(paragraph);
|
|
549
|
+
const resolvedParagraphFormatting = resolveEffectiveParagraphFormatting(
|
|
550
|
+
{ styleId: paragraph.styleId, direct: directParagraphFormatting },
|
|
551
|
+
stylesCatalog,
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
// Task 11: compute cascaded marker run formatting
|
|
555
|
+
const markerRunProperties = effectiveNumbering
|
|
556
|
+
? resolveNumberingMarkerRunFormatting(
|
|
557
|
+
{
|
|
558
|
+
paragraphStyleId: paragraph.styleId,
|
|
559
|
+
levelRunProperties: resolvedNumbering?.markerRunProperties,
|
|
560
|
+
},
|
|
561
|
+
stylesCatalog,
|
|
562
|
+
)
|
|
563
|
+
: undefined;
|
|
564
|
+
|
|
526
565
|
const accumulator: ParagraphAccumulator = {
|
|
527
566
|
blockId: `paragraph-${paragraphIndex}`,
|
|
528
567
|
kind: "paragraph",
|
|
@@ -536,9 +575,17 @@ function createParagraphBlock(
|
|
|
536
575
|
...(resolvedNumbering.text !== null && resolvedNumbering.suffix
|
|
537
576
|
? { numberingSuffix: resolvedNumbering.suffix }
|
|
538
577
|
: {}),
|
|
539
|
-
resolvedNumbering:
|
|
578
|
+
resolvedNumbering: {
|
|
579
|
+
...toSurfaceResolvedNumbering(resolvedNumbering),
|
|
580
|
+
...(markerRunProperties && Object.keys(markerRunProperties).length > 0
|
|
581
|
+
? { markerRunProperties }
|
|
582
|
+
: {}),
|
|
583
|
+
},
|
|
540
584
|
}
|
|
541
585
|
: {}),
|
|
586
|
+
...(resolvedParagraphFormatting && Object.keys(resolvedParagraphFormatting).length > 0
|
|
587
|
+
? { resolvedParagraphFormatting }
|
|
588
|
+
: {}),
|
|
542
589
|
...(paragraph.alignment ? { alignment: paragraph.alignment } : {}),
|
|
543
590
|
...(paragraph.spacing ? { spacing: paragraph.spacing } : {}),
|
|
544
591
|
...(paragraph.contextualSpacing !== undefined
|
|
@@ -683,6 +730,48 @@ function resolveStyleLinkedNumberingLevel(
|
|
|
683
730
|
return undefined;
|
|
684
731
|
}
|
|
685
732
|
|
|
733
|
+
function buildDirectRunFormattingFromMarks(
|
|
734
|
+
marks: SurfaceTextMark[] | undefined,
|
|
735
|
+
markAttrs: {
|
|
736
|
+
backgroundColor?: string;
|
|
737
|
+
charSpacing?: number;
|
|
738
|
+
kerning?: number;
|
|
739
|
+
textFill?: string;
|
|
740
|
+
fontFamily?: string;
|
|
741
|
+
fontSize?: number;
|
|
742
|
+
textColor?: string;
|
|
743
|
+
} | undefined,
|
|
744
|
+
): CanonicalRunFormatting | undefined {
|
|
745
|
+
const direct: CanonicalRunFormatting = {};
|
|
746
|
+
if (marks) {
|
|
747
|
+
if (marks.includes("bold")) direct.bold = true;
|
|
748
|
+
if (marks.includes("italic")) direct.italic = true;
|
|
749
|
+
if (marks.includes("underline")) direct.underline = "single";
|
|
750
|
+
if (marks.includes("strikethrough")) direct.strikethrough = true;
|
|
751
|
+
if (marks.includes("doubleStrikethrough")) direct.doubleStrikethrough = true;
|
|
752
|
+
if (marks.includes("vanish")) direct.vanish = true;
|
|
753
|
+
if (marks.includes("allCaps")) direct.allCaps = true;
|
|
754
|
+
if (marks.includes("smallCaps")) direct.smallCaps = true;
|
|
755
|
+
}
|
|
756
|
+
if (markAttrs) {
|
|
757
|
+
if (markAttrs.fontFamily) {
|
|
758
|
+
direct.fontFamily = markAttrs.fontFamily;
|
|
759
|
+
direct.fontFamilyAscii = markAttrs.fontFamily;
|
|
760
|
+
}
|
|
761
|
+
if (typeof markAttrs.fontSize === "number") {
|
|
762
|
+
// markAttrs.fontSize is already in half-points from cloneMarks
|
|
763
|
+
direct.fontSizeHalfPoints = markAttrs.fontSize;
|
|
764
|
+
}
|
|
765
|
+
if (markAttrs.textColor) {
|
|
766
|
+
direct.colorHex = markAttrs.textColor.replace(/^#/, "");
|
|
767
|
+
}
|
|
768
|
+
if (markAttrs.backgroundColor) {
|
|
769
|
+
direct.highlight = markAttrs.backgroundColor;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return Object.keys(direct).length > 0 ? direct : undefined;
|
|
773
|
+
}
|
|
774
|
+
|
|
686
775
|
function appendInlineSegments(
|
|
687
776
|
paragraph: ParagraphAccumulator,
|
|
688
777
|
node: InlineNode,
|
|
@@ -692,23 +781,33 @@ function appendInlineSegments(
|
|
|
692
781
|
hyperlinkHref?: string,
|
|
693
782
|
): { nextCursor: number; lockedFragmentIds: string[] } {
|
|
694
783
|
switch (node.type) {
|
|
695
|
-
case "text":
|
|
784
|
+
case "text": {
|
|
785
|
+
const cloned = node.marks ? cloneMarks(node.marks) : { marks: [] as SurfaceTextMark[] };
|
|
786
|
+
const directRunFormatting = buildDirectRunFormattingFromMarks(
|
|
787
|
+
cloned.marks.length > 0 ? cloned.marks : undefined,
|
|
788
|
+
cloned.markAttrs,
|
|
789
|
+
);
|
|
790
|
+
const resolvedRunFormatting = resolveEffectiveRunFormatting(
|
|
791
|
+
{
|
|
792
|
+
paragraphStyleId: paragraph.styleId,
|
|
793
|
+
characterStyleId: undefined,
|
|
794
|
+
direct: directRunFormatting,
|
|
795
|
+
},
|
|
796
|
+
document.styles,
|
|
797
|
+
);
|
|
696
798
|
paragraph.segments.push({
|
|
697
799
|
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
698
800
|
kind: "text",
|
|
699
801
|
from: start,
|
|
700
802
|
to: start + Array.from(node.text).length,
|
|
701
803
|
text: node.text,
|
|
702
|
-
...(
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
...(result.marks.length > 0 ? { marks: result.marks } : {}),
|
|
706
|
-
...(result.markAttrs ? { markAttrs: result.markAttrs } : {}),
|
|
707
|
-
};
|
|
708
|
-
})() : {}),
|
|
804
|
+
...(cloned.marks.length > 0 ? { marks: cloned.marks } : {}),
|
|
805
|
+
...(cloned.markAttrs ? { markAttrs: cloned.markAttrs } : {}),
|
|
806
|
+
...(Object.keys(resolvedRunFormatting).length > 0 ? { resolvedRunFormatting } : {}),
|
|
709
807
|
...(hyperlinkHref ? { hyperlinkHref } : {}),
|
|
710
808
|
});
|
|
711
809
|
return { nextCursor: start + Array.from(node.text).length, lockedFragmentIds: [] };
|
|
810
|
+
}
|
|
712
811
|
case "tab":
|
|
713
812
|
paragraph.segments.push({
|
|
714
813
|
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
@@ -1368,6 +1467,27 @@ function toSurfaceTabStop(
|
|
|
1368
1467
|
};
|
|
1369
1468
|
}
|
|
1370
1469
|
|
|
1470
|
+
function buildDirectParagraphFormattingFromNode(
|
|
1471
|
+
paragraph: ParagraphNode,
|
|
1472
|
+
): CanonicalParagraphFormatting | undefined {
|
|
1473
|
+
const direct: CanonicalParagraphFormatting = {};
|
|
1474
|
+
if (paragraph.spacing) direct.spacing = paragraph.spacing;
|
|
1475
|
+
if (paragraph.indentation) direct.indentation = paragraph.indentation;
|
|
1476
|
+
if (paragraph.alignment) direct.alignment = paragraph.alignment;
|
|
1477
|
+
if (paragraph.borders) direct.borders = paragraph.borders;
|
|
1478
|
+
if (paragraph.shading) direct.shading = paragraph.shading;
|
|
1479
|
+
if (paragraph.tabStops && paragraph.tabStops.length > 0) direct.tabStops = [...paragraph.tabStops];
|
|
1480
|
+
if (paragraph.contextualSpacing !== undefined) direct.contextualSpacing = paragraph.contextualSpacing;
|
|
1481
|
+
if (paragraph.keepNext !== undefined) direct.keepNext = paragraph.keepNext;
|
|
1482
|
+
if (paragraph.keepLines !== undefined) direct.keepLines = paragraph.keepLines;
|
|
1483
|
+
if (paragraph.widowControl !== undefined) direct.widowControl = paragraph.widowControl;
|
|
1484
|
+
if (paragraph.pageBreakBefore !== undefined) direct.pageBreakBefore = paragraph.pageBreakBefore;
|
|
1485
|
+
if (paragraph.outlineLevel !== undefined) direct.outlineLevel = paragraph.outlineLevel;
|
|
1486
|
+
if (paragraph.bidi !== undefined) direct.bidi = paragraph.bidi;
|
|
1487
|
+
if (paragraph.suppressLineNumbers !== undefined) direct.suppressLineNumbers = paragraph.suppressLineNumbers;
|
|
1488
|
+
return Object.keys(direct).length > 0 ? direct : undefined;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1371
1491
|
function toSurfaceResolvedNumbering(
|
|
1372
1492
|
numbering: NumberingPrefixResult,
|
|
1373
1493
|
): NonNullable<Extract<SurfaceBlockSnapshot, { kind: "paragraph" }>["resolvedNumbering"]> {
|
|
@@ -42,6 +42,7 @@ type TableCellAttrs = {
|
|
|
42
42
|
borderRight?: string | null;
|
|
43
43
|
borderBottom?: string | null;
|
|
44
44
|
borderLeft?: string | null;
|
|
45
|
+
bandClasses?: string | null;
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
function resolveRenderedColspan(attrs: {
|
|
@@ -96,6 +97,7 @@ function getCellAttrs(dom: HTMLElement): TableCellAttrs {
|
|
|
96
97
|
borderRight: dom.getAttribute("data-border-right"),
|
|
97
98
|
borderBottom: dom.getAttribute("data-border-bottom"),
|
|
98
99
|
borderLeft: dom.getAttribute("data-border-left"),
|
|
100
|
+
bandClasses: dom.getAttribute("data-band-classes"),
|
|
99
101
|
};
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -129,6 +131,11 @@ function setCellDomAttrs(nodeAttrs: TableCellAttrs, className: string): Record<s
|
|
|
129
131
|
if (nodeAttrs.borderRight) attrs["data-border-right"] = nodeAttrs.borderRight;
|
|
130
132
|
if (nodeAttrs.borderBottom) attrs["data-border-bottom"] = nodeAttrs.borderBottom;
|
|
131
133
|
if (nodeAttrs.borderLeft) attrs["data-border-left"] = nodeAttrs.borderLeft;
|
|
134
|
+
if (nodeAttrs.bandClasses) {
|
|
135
|
+
attrs["data-band-classes"] = nodeAttrs.bandClasses;
|
|
136
|
+
// Concatenate band classes onto the base `class` so Tailwind's @apply resolves at parse time.
|
|
137
|
+
attrs.class = `${attrs.class} ${nodeAttrs.bandClasses}`;
|
|
138
|
+
}
|
|
132
139
|
|
|
133
140
|
const styles: string[] = [];
|
|
134
141
|
const bgColor = safeCssColor(nodeAttrs.backgroundColor);
|
|
@@ -181,6 +188,8 @@ const tableCellSpecAttrs = {
|
|
|
181
188
|
borderRight: { default: null },
|
|
182
189
|
borderBottom: { default: null },
|
|
183
190
|
borderLeft: { default: null },
|
|
191
|
+
/** R2b: space-joined band classes ("band-firstRow band-band1Horz") from the resolved style. */
|
|
192
|
+
bandClasses: { default: null },
|
|
184
193
|
} as const;
|
|
185
194
|
|
|
186
195
|
export const tableNodeSpec: NodeSpec = {
|
|
@@ -199,6 +208,8 @@ export const tableNodeSpec: NodeSpec = {
|
|
|
199
208
|
tblLookLastColumn: { default: false },
|
|
200
209
|
tblLookNoHBand: { default: false },
|
|
201
210
|
tblLookNoVBand: { default: false },
|
|
211
|
+
/** R2d: raw `w:tblLook/@w:val` hex preserved verbatim so vendor-extended bits survive round-trip. */
|
|
212
|
+
tblLookVal: { default: null },
|
|
202
213
|
},
|
|
203
214
|
parseDOM: [{ tag: "table" }],
|
|
204
215
|
toDOM(node) {
|
|
@@ -19,13 +19,17 @@ import type {
|
|
|
19
19
|
ActiveListContext,
|
|
20
20
|
ActiveNoteContext,
|
|
21
21
|
CaretAffinity,
|
|
22
|
+
ChromePinsState,
|
|
23
|
+
ChromePinSurface,
|
|
22
24
|
DocumentMode,
|
|
25
|
+
EditorRole,
|
|
23
26
|
EditorStoryTarget,
|
|
24
27
|
EditorSurfaceSnapshot,
|
|
25
28
|
EditorViewStateSnapshot,
|
|
26
29
|
LayoutMeasurement,
|
|
27
30
|
PageLayoutSnapshot,
|
|
28
31
|
PageRegionHitTest,
|
|
32
|
+
PinState,
|
|
29
33
|
SelectionSnapshot,
|
|
30
34
|
SurfaceBlockSnapshot,
|
|
31
35
|
SurfaceInlineSegment,
|
|
@@ -44,6 +48,20 @@ export interface ViewState {
|
|
|
44
48
|
caretAffinity: CaretAffinity;
|
|
45
49
|
activePageRegion: PageRegionHitTest | null;
|
|
46
50
|
activeObjectFrame: LayoutMeasurement["objectFrame"] | null;
|
|
51
|
+
/**
|
|
52
|
+
* Role-scoped chrome dimension (spec §6.4). Host apps drive the role via
|
|
53
|
+
* `setEditorRole`; the mounted shell reads this to pick a per-role
|
|
54
|
+
* toolbar action set. Independent of `viewMode` — one user session may
|
|
55
|
+
* start in "review" role and switch to "editor" without changing
|
|
56
|
+
* workspace or document mode.
|
|
57
|
+
*/
|
|
58
|
+
editorRole: EditorRole;
|
|
59
|
+
/**
|
|
60
|
+
* Pin state for detachable chrome surfaces (topnav, selection tier).
|
|
61
|
+
* Lives here so it survives snapshot rebuilds within one session.
|
|
62
|
+
* Absent key ⇒ docked default.
|
|
63
|
+
*/
|
|
64
|
+
chromePins: ChromePinsState;
|
|
47
65
|
}
|
|
48
66
|
|
|
49
67
|
const MIN_ZOOM_PERCENT = 50;
|
|
@@ -58,6 +76,8 @@ const DEFAULT_VIEW_STATE: ViewState = {
|
|
|
58
76
|
caretAffinity: "none",
|
|
59
77
|
activePageRegion: null,
|
|
60
78
|
activeObjectFrame: null,
|
|
79
|
+
editorRole: "editor",
|
|
80
|
+
chromePins: {},
|
|
61
81
|
};
|
|
62
82
|
|
|
63
83
|
export function createViewState(initial?: Partial<ViewState>): ViewState {
|
|
@@ -113,6 +133,37 @@ export function setActiveObjectFrame(
|
|
|
113
133
|
return { ...state, activeObjectFrame: frame };
|
|
114
134
|
}
|
|
115
135
|
|
|
136
|
+
export function setEditorRole(state: ViewState, role: EditorRole): ViewState {
|
|
137
|
+
if (state.editorRole === role) return state;
|
|
138
|
+
return { ...state, editorRole: role };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function setChromePin(
|
|
142
|
+
state: ViewState,
|
|
143
|
+
surface: ChromePinSurface,
|
|
144
|
+
pin: PinState | null,
|
|
145
|
+
): ViewState {
|
|
146
|
+
const next: ChromePinsState = { ...state.chromePins };
|
|
147
|
+
if (pin === null) {
|
|
148
|
+
if (next[surface] === undefined) {
|
|
149
|
+
return state;
|
|
150
|
+
}
|
|
151
|
+
delete next[surface];
|
|
152
|
+
} else {
|
|
153
|
+
const current = next[surface];
|
|
154
|
+
if (
|
|
155
|
+
current &&
|
|
156
|
+
current.detached === pin.detached &&
|
|
157
|
+
current.offset.x === pin.offset.x &&
|
|
158
|
+
current.offset.y === pin.offset.y
|
|
159
|
+
) {
|
|
160
|
+
return state;
|
|
161
|
+
}
|
|
162
|
+
next[surface] = pin;
|
|
163
|
+
}
|
|
164
|
+
return { ...state, chromePins: next };
|
|
165
|
+
}
|
|
166
|
+
|
|
116
167
|
/**
|
|
117
168
|
* Derive list context from the surface block at the current selection head.
|
|
118
169
|
*/
|
|
@@ -219,9 +270,25 @@ export function createEditorViewStateSnapshot(
|
|
|
219
270
|
activeObjectFrame: derivedViewState.activeObjectFrame,
|
|
220
271
|
measurement,
|
|
221
272
|
isFocused: derivedViewState.isFocused,
|
|
273
|
+
editorRole: derivedViewState.editorRole,
|
|
274
|
+
chromePins: cloneChromePins(derivedViewState.chromePins),
|
|
222
275
|
};
|
|
223
276
|
}
|
|
224
277
|
|
|
278
|
+
function cloneChromePins(pins: ChromePinsState): ChromePinsState {
|
|
279
|
+
const out: ChromePinsState = {};
|
|
280
|
+
for (const key of Object.keys(pins) as ChromePinSurface[]) {
|
|
281
|
+
const pin = pins[key];
|
|
282
|
+
if (pin) {
|
|
283
|
+
out[key] = {
|
|
284
|
+
detached: pin.detached,
|
|
285
|
+
offset: { x: pin.offset.x, y: pin.offset.y },
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
|
|
225
292
|
// ---------------------------------------------------------------------------
|
|
226
293
|
// Internal helpers
|
|
227
294
|
// ---------------------------------------------------------------------------
|
|
@@ -341,11 +341,7 @@ function collectOpaqueFragmentMarkup(
|
|
|
341
341
|
const seen = new Set(existing.map((item) => item.fragmentId));
|
|
342
342
|
|
|
343
343
|
return Object.values(preservation.opaqueFragments)
|
|
344
|
-
.filter(
|
|
345
|
-
(fragment) =>
|
|
346
|
-
!seen.has(fragment.fragmentId)
|
|
347
|
-
&& fragment.packagePartName === "/word/document.xml",
|
|
348
|
-
)
|
|
344
|
+
.filter((fragment) => !seen.has(fragment.fragmentId))
|
|
349
345
|
.map((fragment) => {
|
|
350
346
|
const descriptor = describeOpaqueFragment(fragment);
|
|
351
347
|
const blockedReasonCode = isBlockedImportFeatureKey(descriptor.featureKey)
|