@beyondwork/docx-react-component 1.0.19 → 1.0.21
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 +44 -25
- package/src/api/public-types.ts +336 -0
- package/src/api/session-state.ts +2 -0
- package/src/core/commands/formatting-commands.ts +1 -1
- package/src/core/commands/index.ts +14 -2
- package/src/core/search/search-text.ts +28 -0
- package/src/core/state/editor-state.ts +3 -0
- package/src/index.ts +21 -0
- package/src/io/docx-session.ts +363 -17
- package/src/io/export/serialize-comments.ts +104 -34
- package/src/io/export/serialize-footnotes.ts +198 -1
- package/src/io/export/serialize-headers-footers.ts +203 -10
- package/src/io/export/serialize-main-document.ts +83 -3
- package/src/io/export/split-review-boundaries.ts +181 -19
- package/src/io/normalize/normalize-text.ts +82 -8
- package/src/io/ooxml/highlight-colors.ts +39 -0
- package/src/io/ooxml/parse-comments.ts +85 -19
- package/src/io/ooxml/parse-fields.ts +396 -0
- package/src/io/ooxml/parse-footnotes.ts +240 -2
- package/src/io/ooxml/parse-headers-footers.ts +431 -7
- package/src/io/ooxml/parse-inline-media.ts +15 -1
- package/src/io/ooxml/parse-main-document.ts +396 -14
- package/src/io/ooxml/parse-revisions.ts +317 -38
- package/src/legal/bookmarks.ts +44 -0
- package/src/legal/cross-references.ts +59 -1
- package/src/model/canonical-document.ts +117 -1
- package/src/model/snapshot.ts +85 -1
- package/src/review/store/revision-store.ts +6 -0
- package/src/review/store/revision-types.ts +1 -0
- package/src/runtime/document-navigation.ts +52 -13
- package/src/runtime/document-runtime.ts +1521 -75
- package/src/runtime/read-only-diagnostics-runtime.ts +8 -0
- package/src/runtime/session-capabilities.ts +33 -3
- package/src/runtime/surface-projection.ts +86 -25
- package/src/runtime/table-schema.ts +2 -2
- package/src/runtime/view-state.ts +24 -6
- package/src/runtime/workflow-markup.ts +349 -0
- package/src/ui/WordReviewEditor.tsx +915 -1314
- package/src/ui/editor-command-bag.ts +120 -0
- package/src/ui/editor-runtime-boundary.ts +1448 -0
- package/src/ui/editor-shell-view.tsx +134 -0
- package/src/ui/editor-surface-controller.tsx +55 -0
- package/src/ui/headless/revision-decoration-model.ts +4 -4
- package/src/ui/runtime-snapshot-selectors.ts +197 -0
- package/src/ui/workflow-surface-blocked-rails.ts +94 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
- package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +27 -2
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +86 -14
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +2 -2
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +237 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +1 -1
- package/src/ui-tailwind/editor-surface/pm-schema.ts +139 -8
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +98 -48
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +55 -0
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +190 -48
- package/src/ui-tailwind/page-chrome-model.ts +27 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +7 -7
- package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
- package/src/ui-tailwind/review/tw-review-rail.tsx +3 -3
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
- package/src/ui-tailwind/theme/editor-theme.css +130 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +543 -5
- package/src/ui-tailwind/tw-review-workspace.tsx +316 -19
- package/src/validation/compatibility-engine.ts +27 -4
- package/src/validation/compatibility-report.ts +1 -0
- package/src/validation/docx-comment-proof.ts +220 -0
|
@@ -193,6 +193,14 @@ function createDiagnosticsRenderSnapshot(
|
|
|
193
193
|
canRedo: false,
|
|
194
194
|
readOnly: true,
|
|
195
195
|
},
|
|
196
|
+
documentMode: "viewing",
|
|
197
|
+
protectionSnapshot: {
|
|
198
|
+
hasDocumentProtection: false,
|
|
199
|
+
enforcementActive: false,
|
|
200
|
+
ranges: [],
|
|
201
|
+
enforcedRangeCount: 0,
|
|
202
|
+
preservedRangeCount: 0,
|
|
203
|
+
},
|
|
196
204
|
};
|
|
197
205
|
}
|
|
198
206
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimeRenderSnapshot } from "../api/public-types";
|
|
1
|
+
import type { RuntimeRenderSnapshot, WorkflowScopeSnapshot } from "../api/public-types";
|
|
2
2
|
import {
|
|
3
3
|
createDetachedAnchor,
|
|
4
4
|
createNodeAnchor,
|
|
@@ -21,6 +21,10 @@ export interface SessionCapabilities {
|
|
|
21
21
|
/** Effective editor mode after accounting for runtime state. */
|
|
22
22
|
mode: "editing" | "review" | "read-only-diagnostics";
|
|
23
23
|
|
|
24
|
+
// ── Document mode ──
|
|
25
|
+
/** Runtime document mode — editing authority, distinct from view/workspace mode. */
|
|
26
|
+
documentMode: "editing" | "suggesting" | "viewing";
|
|
27
|
+
|
|
24
28
|
// ── Command capabilities ──
|
|
25
29
|
canUndo: boolean;
|
|
26
30
|
canRedo: boolean;
|
|
@@ -44,10 +48,22 @@ export interface SessionCapabilities {
|
|
|
44
48
|
preserveOnlyCount: number;
|
|
45
49
|
unsupportedFatalCount: number;
|
|
46
50
|
|
|
51
|
+
// ── Protection posture ──
|
|
52
|
+
/** Whether the source package declares document-level protection. */
|
|
53
|
+
hasDocumentProtection: boolean;
|
|
54
|
+
/** Count of permission ranges from the source package. */
|
|
55
|
+
protectedRangeCount: number;
|
|
56
|
+
|
|
47
57
|
// ── Health ──
|
|
48
58
|
/** Total count of health issues (preserve-only + unsupported-fatal + warnings). */
|
|
49
59
|
healthIssueCount: number;
|
|
50
60
|
|
|
61
|
+
// ── Workflow ──
|
|
62
|
+
/** Whether a workflow overlay is currently applied. */
|
|
63
|
+
workflowOverlayPresent: boolean;
|
|
64
|
+
/** Whether the current selection is blocked by workflow scope enforcement. */
|
|
65
|
+
workflowBlocked: boolean;
|
|
66
|
+
|
|
51
67
|
// ── Status ──
|
|
52
68
|
isDirty: boolean;
|
|
53
69
|
isReady: boolean;
|
|
@@ -63,12 +79,14 @@ export interface SessionCapabilities {
|
|
|
63
79
|
export function deriveCapabilities(
|
|
64
80
|
snapshot: RuntimeRenderSnapshot,
|
|
65
81
|
reviewMode: "editing" | "review",
|
|
82
|
+
workflowScope?: WorkflowScopeSnapshot | null,
|
|
66
83
|
): SessionCapabilities {
|
|
67
84
|
const hasFatalError = Boolean(snapshot.fatalError);
|
|
68
85
|
const isReady = snapshot.isReady;
|
|
69
86
|
const isReadOnly = snapshot.readOnly;
|
|
70
87
|
const exportBlocked = snapshot.compatibility.blockExport;
|
|
71
88
|
const activeStory = snapshot.activeStory ?? { kind: "main" as const };
|
|
89
|
+
const documentMode = snapshot.documentMode ?? "editing";
|
|
72
90
|
|
|
73
91
|
// Phase derivation
|
|
74
92
|
const phase: SessionCapabilities["phase"] = !isReady
|
|
@@ -83,8 +101,8 @@ export function deriveCapabilities(
|
|
|
83
101
|
? "read-only-diagnostics"
|
|
84
102
|
: reviewMode;
|
|
85
103
|
|
|
86
|
-
// Command capabilities
|
|
87
|
-
const canEdit = isReady && !isReadOnly && !hasFatalError;
|
|
104
|
+
// Command capabilities — document mode "viewing" disables editing
|
|
105
|
+
const canEdit = isReady && !isReadOnly && !hasFatalError && documentMode !== "viewing";
|
|
88
106
|
const canUndo = snapshot.commandState.canUndo && canEdit;
|
|
89
107
|
const canRedo = snapshot.commandState.canRedo && canEdit;
|
|
90
108
|
const canAddComment =
|
|
@@ -125,9 +143,17 @@ export function deriveCapabilities(
|
|
|
125
143
|
|
|
126
144
|
const healthIssueCount = preserveOnlyCount + unsupportedFatalCount + snapshot.warnings.length;
|
|
127
145
|
|
|
146
|
+
const protection = snapshot.protectionSnapshot;
|
|
147
|
+
const hasDocumentProtection = protection?.hasDocumentProtection ?? false;
|
|
148
|
+
const protectedRangeCount = protection?.ranges?.length ?? 0;
|
|
149
|
+
|
|
150
|
+
const workflowOverlayPresent = workflowScope?.overlayPresent ?? false;
|
|
151
|
+
const workflowBlocked = (workflowScope?.blockedReasons?.length ?? 0) > 0;
|
|
152
|
+
|
|
128
153
|
return {
|
|
129
154
|
phase,
|
|
130
155
|
mode,
|
|
156
|
+
documentMode,
|
|
131
157
|
canUndo,
|
|
132
158
|
canRedo,
|
|
133
159
|
canEdit,
|
|
@@ -142,7 +168,11 @@ export function deriveCapabilities(
|
|
|
142
168
|
exportBlocked,
|
|
143
169
|
preserveOnlyCount,
|
|
144
170
|
unsupportedFatalCount,
|
|
171
|
+
hasDocumentProtection,
|
|
172
|
+
protectedRangeCount,
|
|
145
173
|
healthIssueCount,
|
|
174
|
+
workflowOverlayPresent,
|
|
175
|
+
workflowBlocked,
|
|
146
176
|
isDirty: snapshot.isDirty,
|
|
147
177
|
isReady,
|
|
148
178
|
hasFatalError,
|
|
@@ -52,6 +52,7 @@ interface ParagraphAccumulator {
|
|
|
52
52
|
numbering?: ParagraphNode["numbering"];
|
|
53
53
|
numberingPrefix?: string;
|
|
54
54
|
numberingSuffix?: "tab" | "space" | "nothing";
|
|
55
|
+
contextualSpacing?: boolean;
|
|
55
56
|
segments: SurfaceInlineSegment[];
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -469,6 +470,9 @@ function createParagraphBlock(
|
|
|
469
470
|
: {}),
|
|
470
471
|
...(paragraph.alignment ? { alignment: paragraph.alignment } : {}),
|
|
471
472
|
...(paragraph.spacing ? { spacing: paragraph.spacing } : {}),
|
|
473
|
+
...(paragraph.contextualSpacing !== undefined
|
|
474
|
+
? { contextualSpacing: paragraph.contextualSpacing }
|
|
475
|
+
: {}),
|
|
472
476
|
...(paragraph.indentation ? { indentation: paragraph.indentation } : {}),
|
|
473
477
|
...(paragraph.borders ? { borders: paragraph.borders } : {}),
|
|
474
478
|
...(paragraph.shading ? { shading: paragraph.shading } : {}),
|
|
@@ -592,15 +596,16 @@ function appendInlineSegments(
|
|
|
592
596
|
return { nextCursor: start + 1, lockedFragmentIds: [node.fragmentId] };
|
|
593
597
|
}
|
|
594
598
|
case "chart_preview":
|
|
595
|
-
return appendComplexPreviewSegment(paragraph, node, start, "
|
|
599
|
+
return appendComplexPreviewSegment(paragraph, node, start, "Embedded chart", createChartDetail(node));
|
|
596
600
|
case "smartart_preview":
|
|
597
|
-
return appendComplexPreviewSegment(paragraph, node, start, "SmartArt", createSmartArtDetail(node));
|
|
601
|
+
return appendComplexPreviewSegment(paragraph, node, start, "SmartArt diagram", createSmartArtDetail(node));
|
|
598
602
|
case "shape":
|
|
599
|
-
return appendComplexPreviewSegment(paragraph, node, start,
|
|
603
|
+
return appendComplexPreviewSegment(paragraph, node, start,
|
|
604
|
+
node.isTextBox ? "Text box" : "Drawing shape", createShapeDetail(node));
|
|
600
605
|
case "wordart":
|
|
601
606
|
return appendComplexPreviewSegment(paragraph, node, start, "WordArt", createWordArtDetail(node));
|
|
602
607
|
case "vml_shape":
|
|
603
|
-
return appendComplexPreviewSegment(paragraph, node, start, "VML
|
|
608
|
+
return appendComplexPreviewSegment(paragraph, node, start, "Legacy VML drawing", createVmlDetail(node));
|
|
604
609
|
case "symbol":
|
|
605
610
|
paragraph.segments.push({
|
|
606
611
|
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
@@ -635,6 +640,11 @@ function appendInlineSegments(
|
|
|
635
640
|
});
|
|
636
641
|
return { nextCursor: start + 1, lockedFragmentIds: [] };
|
|
637
642
|
case "field": {
|
|
643
|
+
const isSupportedField =
|
|
644
|
+
node.fieldFamily === "REF" ||
|
|
645
|
+
node.fieldFamily === "PAGEREF" ||
|
|
646
|
+
node.fieldFamily === "NOTEREF" ||
|
|
647
|
+
node.fieldFamily === "TOC";
|
|
638
648
|
if (node.children && node.children.length > 0) {
|
|
639
649
|
let cursor = start;
|
|
640
650
|
const lockedIds: string[] = [];
|
|
@@ -645,6 +655,25 @@ function appendInlineSegments(
|
|
|
645
655
|
}
|
|
646
656
|
return { nextCursor: cursor, lockedFragmentIds: lockedIds };
|
|
647
657
|
}
|
|
658
|
+
if (isSupportedField) {
|
|
659
|
+
// Supported field with no resolved content — show as field chip
|
|
660
|
+
const fieldLabel =
|
|
661
|
+
node.fieldFamily === "TOC"
|
|
662
|
+
? "Table of Contents"
|
|
663
|
+
: `${node.fieldFamily ?? "Field"}: ${node.fieldTarget ?? node.instruction.trim()}`;
|
|
664
|
+
paragraph.segments.push({
|
|
665
|
+
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
666
|
+
kind: "field_ref",
|
|
667
|
+
from: start,
|
|
668
|
+
to: start + 1,
|
|
669
|
+
fieldFamily: node.fieldFamily!,
|
|
670
|
+
fieldTarget: node.fieldTarget,
|
|
671
|
+
instruction: node.instruction,
|
|
672
|
+
refreshStatus: node.refreshStatus ?? "stale",
|
|
673
|
+
label: fieldLabel,
|
|
674
|
+
} as SurfaceInlineSegment);
|
|
675
|
+
return { nextCursor: start + 1, lockedFragmentIds: [] };
|
|
676
|
+
}
|
|
648
677
|
paragraph.segments.push({
|
|
649
678
|
segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
|
|
650
679
|
kind: "opaque_inline",
|
|
@@ -653,7 +682,7 @@ function appendInlineSegments(
|
|
|
653
682
|
fragmentId: "",
|
|
654
683
|
warningId: "",
|
|
655
684
|
label: "Field",
|
|
656
|
-
detail:
|
|
685
|
+
detail: `Preserve-only field: ${node.instruction.trim()}`,
|
|
657
686
|
state: "locked-preserve-only",
|
|
658
687
|
});
|
|
659
688
|
return { nextCursor: start + 1, lockedFragmentIds: [] };
|
|
@@ -689,38 +718,54 @@ function appendComplexPreviewSegment(
|
|
|
689
718
|
}
|
|
690
719
|
|
|
691
720
|
function createChartDetail(node: ChartPreviewNode): string {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
721
|
+
const parts = ["Embedded chart."];
|
|
722
|
+
if (node.previewMediaId) {
|
|
723
|
+
parts.push(`Preview available via fallback image (${node.previewMediaId}).`);
|
|
724
|
+
} else {
|
|
725
|
+
parts.push("No fallback preview image found; chart data preserved in package.");
|
|
726
|
+
}
|
|
727
|
+
parts.push("Edit in Word to modify chart data. Original DrawingML preserved for lossless export.");
|
|
728
|
+
return parts.join(" ");
|
|
695
729
|
}
|
|
696
730
|
|
|
697
731
|
function createSmartArtDetail(node: SmartArtPreviewNode): string {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
732
|
+
const parts = ["SmartArt diagram."];
|
|
733
|
+
if (node.previewMediaId) {
|
|
734
|
+
parts.push(`Preview available via fallback image (${node.previewMediaId}).`);
|
|
735
|
+
} else {
|
|
736
|
+
parts.push("No fallback preview image found; diagram structure preserved in package.");
|
|
737
|
+
}
|
|
738
|
+
parts.push("Edit in Word to modify diagram layout and content. Original DrawingML preserved for lossless export.");
|
|
739
|
+
return parts.join(" ");
|
|
701
740
|
}
|
|
702
741
|
|
|
703
742
|
function createShapeDetail(node: ShapeNode): string {
|
|
704
|
-
|
|
743
|
+
if (node.isTextBox) {
|
|
744
|
+
const parts = ["Text box."];
|
|
745
|
+
if (node.text) parts.push(`Content: "${node.text}".`);
|
|
746
|
+
parts.push("Text content is visible; formatting and geometry preserved for export.");
|
|
747
|
+
return parts.join(" ");
|
|
748
|
+
}
|
|
749
|
+
const parts = ["Drawing shape."];
|
|
705
750
|
if (node.geometry) parts.push(`Geometry: ${node.geometry}.`);
|
|
706
|
-
if (node.text) parts.push(`Text: "${node.text}".`);
|
|
707
|
-
parts.push("Original
|
|
751
|
+
if (node.text) parts.push(`Text content: "${node.text}".`);
|
|
752
|
+
parts.push("Visual geometry is preview-only. Original DrawingML preserved for export.");
|
|
708
753
|
return parts.join(" ");
|
|
709
754
|
}
|
|
710
755
|
|
|
711
756
|
function createWordArtDetail(node: WordArtNode): string {
|
|
712
|
-
const parts = ["WordArt
|
|
757
|
+
const parts = ["WordArt decorative text."];
|
|
713
758
|
if (node.text) parts.push(`Text: "${node.text}".`);
|
|
714
759
|
if (node.geometry) parts.push(`Effect: ${node.geometry}.`);
|
|
715
|
-
parts.push("Original
|
|
760
|
+
parts.push("Text effect is preview-only. Edit in Word for full formatting. Original DrawingML preserved for export.");
|
|
716
761
|
return parts.join(" ");
|
|
717
762
|
}
|
|
718
763
|
|
|
719
764
|
function createVmlDetail(node: VmlShapeNode): string {
|
|
720
|
-
const parts = ["VML
|
|
765
|
+
const parts = ["Legacy VML drawing."];
|
|
721
766
|
if (node.shapeType) parts.push(`Type: ${node.shapeType}.`);
|
|
722
|
-
if (node.text) parts.push(`Text: "${node.text}".`);
|
|
723
|
-
parts.push("
|
|
767
|
+
if (node.text) parts.push(`Text content: "${node.text}".`);
|
|
768
|
+
parts.push("VML content is preview-only. Edit in Word for full control. Original VML XML preserved for export.");
|
|
724
769
|
return parts.join(" ");
|
|
725
770
|
}
|
|
726
771
|
|
|
@@ -1039,15 +1084,16 @@ function summarizePreviewInline(node: InlineNode): string {
|
|
|
1039
1084
|
case "column_break":
|
|
1040
1085
|
return "[Column break]";
|
|
1041
1086
|
case "chart_preview":
|
|
1042
|
-
return "[
|
|
1087
|
+
return "[Embedded chart]";
|
|
1043
1088
|
case "smartart_preview":
|
|
1044
|
-
return "[SmartArt]";
|
|
1089
|
+
return "[SmartArt diagram]";
|
|
1045
1090
|
case "shape":
|
|
1046
|
-
|
|
1091
|
+
if (node.isTextBox && node.text) return `[Text box: ${node.text}]`;
|
|
1092
|
+
return node.text ? `[Shape: ${node.text}]` : "[Drawing shape]";
|
|
1047
1093
|
case "wordart":
|
|
1048
1094
|
return node.text ? `[WordArt: ${node.text}]` : "[WordArt]";
|
|
1049
1095
|
case "vml_shape":
|
|
1050
|
-
return node.text ? `[VML: ${node.text}]` : "[VML
|
|
1096
|
+
return node.text ? `[VML: ${node.text}]` : "[Legacy VML drawing]";
|
|
1051
1097
|
}
|
|
1052
1098
|
}
|
|
1053
1099
|
|
|
@@ -1139,8 +1185,15 @@ function describePreservedInlinePreview(
|
|
|
1139
1185
|
.join("")
|
|
1140
1186
|
.trim();
|
|
1141
1187
|
const instruction = (simpleInstruction ?? complexInstruction ?? "").trim();
|
|
1188
|
+
const family = /^([A-Z]+)/i.exec(instruction)?.[1]?.toUpperCase();
|
|
1189
|
+
const label =
|
|
1190
|
+
family === "TOC"
|
|
1191
|
+
? "Table of Contents field"
|
|
1192
|
+
: family
|
|
1193
|
+
? `${family} field`
|
|
1194
|
+
: "Field";
|
|
1142
1195
|
return {
|
|
1143
|
-
label
|
|
1196
|
+
label,
|
|
1144
1197
|
detail:
|
|
1145
1198
|
instruction.length > 0
|
|
1146
1199
|
? `Read-only field preserved for export safety. Instruction: ${instruction}.`
|
|
@@ -1182,6 +1235,8 @@ function cloneMarks(marks: TextMark[]): {
|
|
|
1182
1235
|
fontSize?: number;
|
|
1183
1236
|
textColor?: string;
|
|
1184
1237
|
} = {};
|
|
1238
|
+
let shadingColor: string | undefined;
|
|
1239
|
+
let highlightColor: string | undefined;
|
|
1185
1240
|
for (const mark of marks) {
|
|
1186
1241
|
switch (mark.type) {
|
|
1187
1242
|
case "bold":
|
|
@@ -1200,7 +1255,10 @@ function cloneMarks(marks: TextMark[]): {
|
|
|
1200
1255
|
else if (mark.val < 0) supported.push("subscript");
|
|
1201
1256
|
break;
|
|
1202
1257
|
case "backgroundColor":
|
|
1203
|
-
|
|
1258
|
+
shadingColor = mark.color;
|
|
1259
|
+
break;
|
|
1260
|
+
case "highlight":
|
|
1261
|
+
highlightColor = mark.color;
|
|
1204
1262
|
break;
|
|
1205
1263
|
case "charSpacing":
|
|
1206
1264
|
attrs.charSpacing = mark.val;
|
|
@@ -1231,6 +1289,9 @@ function cloneMarks(marks: TextMark[]): {
|
|
|
1231
1289
|
break;
|
|
1232
1290
|
}
|
|
1233
1291
|
}
|
|
1292
|
+
if (highlightColor || shadingColor) {
|
|
1293
|
+
attrs.backgroundColor = highlightColor ?? shadingColor;
|
|
1294
|
+
}
|
|
1234
1295
|
const hasAttrs = Object.keys(attrs).length > 0;
|
|
1235
1296
|
return hasAttrs ? { marks: supported, markAttrs: attrs } : { marks: supported };
|
|
1236
1297
|
}
|
|
@@ -239,7 +239,7 @@ export const tableRowNodeSpec: NodeSpec = {
|
|
|
239
239
|
};
|
|
240
240
|
|
|
241
241
|
export const tableCellNodeSpec: NodeSpec = {
|
|
242
|
-
content: "
|
|
242
|
+
content: "block+",
|
|
243
243
|
tableRole: "cell",
|
|
244
244
|
isolating: true,
|
|
245
245
|
attrs: tableCellSpecAttrs,
|
|
@@ -264,7 +264,7 @@ export const tableCellNodeSpec: NodeSpec = {
|
|
|
264
264
|
};
|
|
265
265
|
|
|
266
266
|
export const tableHeaderCellNodeSpec: NodeSpec = {
|
|
267
|
-
content: "
|
|
267
|
+
content: "block+",
|
|
268
268
|
tableRole: "header_cell",
|
|
269
269
|
isolating: true,
|
|
270
270
|
attrs: tableCellSpecAttrs,
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
ActiveListContext,
|
|
20
20
|
ActiveNoteContext,
|
|
21
21
|
CaretAffinity,
|
|
22
|
+
DocumentMode,
|
|
22
23
|
EditorStoryTarget,
|
|
23
24
|
EditorSurfaceSnapshot,
|
|
24
25
|
EditorViewStateSnapshot,
|
|
@@ -36,6 +37,7 @@ import type { NumberingCatalog } from "../model/canonical-document.ts";
|
|
|
36
37
|
|
|
37
38
|
export interface ViewState {
|
|
38
39
|
viewMode: ViewMode;
|
|
40
|
+
documentMode: DocumentMode;
|
|
39
41
|
workspaceMode: WorkspaceMode;
|
|
40
42
|
zoomLevel: ZoomLevel;
|
|
41
43
|
isFocused: boolean;
|
|
@@ -49,6 +51,7 @@ const MAX_ZOOM_PERCENT = 200;
|
|
|
49
51
|
|
|
50
52
|
const DEFAULT_VIEW_STATE: ViewState = {
|
|
51
53
|
viewMode: "editing",
|
|
54
|
+
documentMode: "editing",
|
|
52
55
|
workspaceMode: "canvas",
|
|
53
56
|
zoomLevel: 100,
|
|
54
57
|
isFocused: false,
|
|
@@ -66,6 +69,11 @@ export function setViewMode(state: ViewState, mode: ViewMode): ViewState {
|
|
|
66
69
|
return { ...state, viewMode: mode };
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
export function setDocumentMode(state: ViewState, mode: DocumentMode): ViewState {
|
|
73
|
+
if (state.documentMode === mode) return state;
|
|
74
|
+
return { ...state, documentMode: mode };
|
|
75
|
+
}
|
|
76
|
+
|
|
69
77
|
export function setWorkspaceMode(
|
|
70
78
|
state: ViewState,
|
|
71
79
|
workspaceMode: WorkspaceMode,
|
|
@@ -199,6 +207,7 @@ export function createEditorViewStateSnapshot(
|
|
|
199
207
|
|
|
200
208
|
return {
|
|
201
209
|
viewMode: derivedViewState.viewMode,
|
|
210
|
+
documentMode: derivedViewState.documentMode,
|
|
202
211
|
workspaceMode: derivedViewState.workspaceMode,
|
|
203
212
|
zoomLevel: derivedViewState.zoomLevel,
|
|
204
213
|
activeStory,
|
|
@@ -227,6 +236,14 @@ function findBlockAtPosition(
|
|
|
227
236
|
const inner = findBlockAtPosition(block.children, position);
|
|
228
237
|
if (inner) return inner;
|
|
229
238
|
}
|
|
239
|
+
if (block.kind === "table") {
|
|
240
|
+
for (const row of block.rows) {
|
|
241
|
+
for (const cell of row.cells) {
|
|
242
|
+
const inner = findBlockAtPosition(cell.content, position);
|
|
243
|
+
if (inner) return inner;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
230
247
|
return block;
|
|
231
248
|
}
|
|
232
249
|
}
|
|
@@ -262,7 +279,7 @@ function deriveInteractionViewState(
|
|
|
262
279
|
pageLayout: PageLayoutSnapshot | null | undefined,
|
|
263
280
|
): ViewState {
|
|
264
281
|
const activePageRegion = deriveActivePageRegion(activeStory, pageLayout) ?? viewState.activePageRegion;
|
|
265
|
-
const activeObjectFrame = deriveActiveObjectFrame(surface, selection)
|
|
282
|
+
const activeObjectFrame = deriveActiveObjectFrame(surface, selection);
|
|
266
283
|
const caretAffinity = deriveCaretAffinity(surface, selection);
|
|
267
284
|
|
|
268
285
|
return {
|
|
@@ -434,12 +451,13 @@ function isObjectLikeSegment(
|
|
|
434
451
|
function inferOpaqueObjectKind(
|
|
435
452
|
segment: Extract<SurfaceInlineSegment, { kind: "opaque_inline" }>,
|
|
436
453
|
): "textbox" | "shape" | null {
|
|
437
|
-
if (segment.label === "
|
|
438
|
-
return
|
|
439
|
-
|
|
440
|
-
|
|
454
|
+
if (segment.label === "Text box") {
|
|
455
|
+
return "textbox";
|
|
456
|
+
}
|
|
457
|
+
if (segment.label === "Drawing shape") {
|
|
458
|
+
return "shape";
|
|
441
459
|
}
|
|
442
|
-
if (segment.label === "VML
|
|
460
|
+
if (segment.label === "Legacy VML drawing") {
|
|
443
461
|
return segment.detail.includes("#_x0000_t202")
|
|
444
462
|
? "textbox"
|
|
445
463
|
: "shape";
|