@beyondwork/docx-react-component 1.0.43 → 1.0.46
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/README.md +35 -1
- package/package.json +44 -32
- package/src/api/public-types.ts +156 -3
- package/src/core/commands/formatting-commands.ts +7 -1
- package/src/core/commands/index.ts +27 -2
- package/src/core/commands/text-commands.ts +59 -0
- package/src/core/selection/review-anchors.ts +131 -21
- package/src/index.ts +16 -1
- package/src/io/chart-preview-resolver.ts +281 -0
- package/src/io/docx-session.ts +21 -1
- package/src/io/export/build-app-properties-xml.ts +1 -1
- package/src/io/export/serialize-comments.ts +38 -9
- package/src/io/export/twip.ts +1 -1
- package/src/io/normalize/normalize-text.ts +33 -0
- package/src/io/ooxml/parse-comments.ts +0 -33
- package/src/io/ooxml/parse-complex-content.ts +14 -0
- package/src/io/ooxml/parse-main-document.ts +4 -0
- package/src/preservation/opaque-region.ts +5 -0
- package/src/review/store/comment-remapping.ts +2 -2
- package/src/runtime/document-runtime.ts +351 -25
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +98 -0
- package/src/runtime/edit-dispatch/index.ts +2 -0
- package/src/runtime/edit-dispatch/list-aware-dispatch.ts +125 -0
- package/src/runtime/editor-surface/capabilities.ts +411 -0
- package/src/runtime/event-refresh-hints.ts +1 -0
- package/src/runtime/layout/docx-font-loader.ts +30 -11
- package/src/runtime/layout/inert-layout-facet.ts +2 -0
- package/src/runtime/layout/layout-engine-instance.ts +46 -0
- package/src/runtime/layout/layout-engine-version.ts +41 -0
- package/src/runtime/layout/public-facet.ts +30 -0
- package/src/runtime/prerender/cache-envelope.ts +29 -0
- package/src/runtime/prerender/cache-key.ts +66 -0
- package/src/runtime/prerender/font-fingerprint.ts +17 -0
- package/src/runtime/prerender/graph-canonicalize.ts +121 -0
- package/src/runtime/prerender/indexeddb-cache.ts +184 -0
- package/src/runtime/prerender/prerender-document.ts +145 -0
- package/src/runtime/render/block-fragment-projection.ts +2 -0
- package/src/runtime/selection/post-edit-validator.ts +77 -0
- package/src/runtime/surface-projection.ts +35 -2
- package/src/ui/WordReviewEditor.tsx +75 -192
- package/src/ui/editor-runtime-boundary.ts +5 -1
- package/src/ui/runtime-shortcut-dispatch.ts +28 -68
- package/src/ui-tailwind/editor-surface/pm-page-break-decorations.ts +76 -165
- package/src/ui-tailwind/editor-surface/pm-schema.ts +18 -0
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +23 -0
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +38 -0
- package/src/ui-tailwind/page-stack/use-visible-block-range.ts +157 -0
- package/src/ui-tailwind/theme/editor-theme.css +47 -14
- package/src/ui-tailwind/tw-review-workspace.tsx +131 -29
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
import type {
|
|
24
24
|
ParsedAltChunkNode,
|
|
25
25
|
ParsedBlockNode,
|
|
26
|
+
ParsedChartPreviewNode,
|
|
26
27
|
ParsedCustomXmlNode,
|
|
27
28
|
ParsedHyperlinkNode,
|
|
28
29
|
ParsedInlineNode,
|
|
@@ -31,6 +32,7 @@ import type {
|
|
|
31
32
|
ParsedParagraphNode,
|
|
32
33
|
ParsedSectionBreakNode,
|
|
33
34
|
ParsedSdtNode,
|
|
35
|
+
ParsedSmartArtPreviewNode,
|
|
34
36
|
ParsedTableBlockNode,
|
|
35
37
|
ParsedTableCellNode,
|
|
36
38
|
ParsedTableRowNode,
|
|
@@ -476,6 +478,7 @@ function normalizeInlineChildren(
|
|
|
476
478
|
state.cursor += 1;
|
|
477
479
|
break;
|
|
478
480
|
case "chart_preview":
|
|
481
|
+
registerComplexPreviewMedia(state, node);
|
|
479
482
|
normalized.push({
|
|
480
483
|
type: "chart_preview",
|
|
481
484
|
...(node.previewMediaId ? { previewMediaId: node.previewMediaId } : {}),
|
|
@@ -484,6 +487,7 @@ function normalizeInlineChildren(
|
|
|
484
487
|
state.cursor += 1;
|
|
485
488
|
break;
|
|
486
489
|
case "smartart_preview":
|
|
490
|
+
registerComplexPreviewMedia(state, node);
|
|
487
491
|
normalized.push({
|
|
488
492
|
type: "smartart_preview",
|
|
489
493
|
...(node.previewMediaId ? { previewMediaId: node.previewMediaId } : {}),
|
|
@@ -611,6 +615,35 @@ function normalizeImageNode(
|
|
|
611
615
|
};
|
|
612
616
|
}
|
|
613
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Register a chart/SmartArt preview bitmap in the media catalog so the
|
|
620
|
+
* surface renderer can resolve `previewMediaId` → `previewSrc` the same
|
|
621
|
+
* way it does for inline images. Chart/SmartArt nodes weren't previously
|
|
622
|
+
* registered because only image nodes walked through `normalizeImageNode`.
|
|
623
|
+
* Needed by docs/plans/lane-5-charts.md Stage 0 (real mc:Fallback bitmaps) and
|
|
624
|
+
* Stage 0B (synthesized previews from the demo harness decorator) —
|
|
625
|
+
* without this, previewMediaId sits in the canonical model but
|
|
626
|
+
* `canonicalDocument.media.items` has no corresponding MediaItem and
|
|
627
|
+
* the editor's mediaPreview resolver skips the bitmap.
|
|
628
|
+
*/
|
|
629
|
+
function registerComplexPreviewMedia(
|
|
630
|
+
state: NormalizationState,
|
|
631
|
+
node: ParsedChartPreviewNode | ParsedSmartArtPreviewNode,
|
|
632
|
+
): void {
|
|
633
|
+
if (!node.previewMediaId) return;
|
|
634
|
+
if (state.media.items[node.previewMediaId]) return; // already registered (e.g. via another reference)
|
|
635
|
+
const packagePartName =
|
|
636
|
+
node.previewPackagePartName ?? `/${node.previewMediaId.slice("media:".length)}`;
|
|
637
|
+
const filename =
|
|
638
|
+
packagePartName.slice(packagePartName.lastIndexOf("/") + 1) || "chart-preview.bin";
|
|
639
|
+
state.media.items[node.previewMediaId] = {
|
|
640
|
+
mediaId: node.previewMediaId,
|
|
641
|
+
contentType: node.previewContentType ?? "application/octet-stream",
|
|
642
|
+
filename,
|
|
643
|
+
packagePartName,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
614
647
|
function normalizeHyperlink(node: ParsedHyperlinkNode): {
|
|
615
648
|
type: "hyperlink";
|
|
616
649
|
href: string;
|
|
@@ -173,39 +173,6 @@ export function parseCommentsFromOoxml(
|
|
|
173
173
|
continue;
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
if (startParagraphIndex !== endParagraphIndex) {
|
|
177
|
-
diagnostics.push({
|
|
178
|
-
commentId: rootCommentId,
|
|
179
|
-
code: "multi_paragraph_anchor_preserve_only",
|
|
180
|
-
message:
|
|
181
|
-
"Comment anchor spans multiple paragraphs. Thread is visible but detached; cross-paragraph anchoring is not yet supported for live editing.",
|
|
182
|
-
featureClass: "preserve-only",
|
|
183
|
-
detachedReason: "multi-paragraph",
|
|
184
|
-
actionabilityNote: "The comment thread and body are preserved. Operators see the thread in the sidebar but cannot navigate to an inline highlight.",
|
|
185
|
-
});
|
|
186
|
-
threads.push(
|
|
187
|
-
createImportedCommentThread({
|
|
188
|
-
commentId: rootCommentId,
|
|
189
|
-
body: rootDefinition.body,
|
|
190
|
-
createdBy,
|
|
191
|
-
createdAt,
|
|
192
|
-
range: detachedRange,
|
|
193
|
-
entries,
|
|
194
|
-
status: "detached",
|
|
195
|
-
resolution,
|
|
196
|
-
metadata: {
|
|
197
|
-
source: "import",
|
|
198
|
-
rootOoxmlCommentId: rootDefinition.commentId,
|
|
199
|
-
rootParaId: rootDefinition.paraId,
|
|
200
|
-
detachedReason: "multi-paragraph",
|
|
201
|
-
actionabilityNote:
|
|
202
|
-
"The comment thread and body are preserved. Operators see the thread in the sidebar but cannot navigate to an inline highlight.",
|
|
203
|
-
},
|
|
204
|
-
}),
|
|
205
|
-
);
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
176
|
threads.push(
|
|
210
177
|
createImportedCommentThread({
|
|
211
178
|
commentId: rootCommentId,
|
|
@@ -21,6 +21,10 @@ export interface ParsedChartContent {
|
|
|
21
21
|
type: "chart_preview";
|
|
22
22
|
/** Media ID of the fallback preview image, if one is present in mc:Fallback. */
|
|
23
23
|
previewMediaId?: string;
|
|
24
|
+
/** Absolute package path of the preview media (e.g. `/word/media/chartN.png`); required by the media catalog when the preview media needs an entry. */
|
|
25
|
+
previewPackagePartName?: string;
|
|
26
|
+
/** MIME type of the preview media (e.g. `image/png`, `image/svg+xml`). */
|
|
27
|
+
previewContentType?: string;
|
|
24
28
|
/** Original drawing XML slice for lossless round-trip export. */
|
|
25
29
|
rawXml: string;
|
|
26
30
|
}
|
|
@@ -29,6 +33,10 @@ export interface ParsedSmartArtContent {
|
|
|
29
33
|
type: "smartart_preview";
|
|
30
34
|
/** Media ID of the fallback preview image, if one is present in mc:Fallback. */
|
|
31
35
|
previewMediaId?: string;
|
|
36
|
+
/** Absolute package path of the preview media. */
|
|
37
|
+
previewPackagePartName?: string;
|
|
38
|
+
/** MIME type of the preview media. */
|
|
39
|
+
previewContentType?: string;
|
|
32
40
|
/** Original drawing XML slice for lossless round-trip export. */
|
|
33
41
|
rawXml: string;
|
|
34
42
|
}
|
|
@@ -116,6 +124,8 @@ function parseAlternateContent(
|
|
|
116
124
|
|
|
117
125
|
// Extract fallback preview image if present
|
|
118
126
|
let previewMediaId: string | undefined;
|
|
127
|
+
let previewPackagePartName: string | undefined;
|
|
128
|
+
let previewContentType: string | undefined;
|
|
119
129
|
if (fallback) {
|
|
120
130
|
const fallbackBlip = findFirstDescendant(fallback, "blip");
|
|
121
131
|
if (fallbackBlip) {
|
|
@@ -132,6 +142,8 @@ function parseAlternateContent(
|
|
|
132
142
|
const mediaPart = mediaParts.get(packagePartName);
|
|
133
143
|
if (mediaPart) {
|
|
134
144
|
previewMediaId = `media:${packagePartName.slice(1)}`;
|
|
145
|
+
previewPackagePartName = packagePartName;
|
|
146
|
+
previewContentType = mediaPart.contentType;
|
|
135
147
|
}
|
|
136
148
|
}
|
|
137
149
|
}
|
|
@@ -141,6 +153,8 @@ function parseAlternateContent(
|
|
|
141
153
|
return {
|
|
142
154
|
type: contentType,
|
|
143
155
|
...(previewMediaId ? { previewMediaId } : {}),
|
|
156
|
+
...(previewPackagePartName ? { previewPackagePartName } : {}),
|
|
157
|
+
...(previewContentType ? { previewContentType } : {}),
|
|
144
158
|
rawXml: fullDrawingXml,
|
|
145
159
|
};
|
|
146
160
|
}
|
|
@@ -211,12 +211,16 @@ export interface ParsedOpaqueInlineNode {
|
|
|
211
211
|
export interface ParsedChartPreviewNode {
|
|
212
212
|
type: "chart_preview";
|
|
213
213
|
previewMediaId?: string;
|
|
214
|
+
previewPackagePartName?: string;
|
|
215
|
+
previewContentType?: string;
|
|
214
216
|
rawXml: string;
|
|
215
217
|
}
|
|
216
218
|
|
|
217
219
|
export interface ParsedSmartArtPreviewNode {
|
|
218
220
|
type: "smartart_preview";
|
|
219
221
|
previewMediaId?: string;
|
|
222
|
+
previewPackagePartName?: string;
|
|
223
|
+
previewContentType?: string;
|
|
220
224
|
rawXml: string;
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -121,6 +121,11 @@ export function createOpaqueRegionSurfaceModel(
|
|
|
121
121
|
export function createOpaqueRegionSurfaceModelFromSurfaceBlock(
|
|
122
122
|
block: Extract<SurfaceBlockSnapshot, { kind: "opaque_block" }>,
|
|
123
123
|
): OpaqueRegionSurfaceModel {
|
|
124
|
+
if (block.state === "placeholder-culled") {
|
|
125
|
+
throw new Error(
|
|
126
|
+
"createOpaqueRegionSurfaceModelFromSurfaceBlock: placeholder-culled block has no real fragment",
|
|
127
|
+
);
|
|
128
|
+
}
|
|
124
129
|
return {
|
|
125
130
|
fragmentId: block.fragmentId,
|
|
126
131
|
warningId: block.warningId,
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getAnchorRange,
|
|
5
5
|
mapReviewAnchor,
|
|
6
6
|
mappingTouchesAnchorContent,
|
|
7
|
-
|
|
7
|
+
rangeStaysWithinCommentableStory,
|
|
8
8
|
type ReviewAnchor,
|
|
9
9
|
} from "../../core/selection/review-anchors.ts";
|
|
10
10
|
import type { TransactionMapping } from "../../core/selection/mapping.ts";
|
|
@@ -85,7 +85,7 @@ function normalizeCommentAnchor(
|
|
|
85
85
|
|
|
86
86
|
if (
|
|
87
87
|
mappedAnchor.kind === "range" &&
|
|
88
|
-
!
|
|
88
|
+
!rangeStaysWithinCommentableStory(nextContent, mappedAnchor.range)
|
|
89
89
|
) {
|
|
90
90
|
return detachReviewAnchor(previousRange, "invalidatedByStructureChange");
|
|
91
91
|
}
|