@beyondwork/docx-react-component 1.0.56 → 1.0.58
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 +1 -1
- package/package.json +1 -1
- package/src/api/public-types.ts +330 -0
- package/src/compare/diff-engine.ts +3 -0
- package/src/core/commands/formatting-commands.ts +1 -0
- package/src/core/commands/index.ts +17 -11
- package/src/core/selection/mapping.ts +18 -1
- package/src/core/selection/review-anchors.ts +29 -18
- package/src/io/chart-preview-resolver.ts +175 -41
- package/src/io/docx-session.ts +57 -2
- package/src/io/export/serialize-main-document.ts +82 -0
- package/src/io/export/serialize-styles.ts +61 -3
- package/src/io/export/table-properties-xml.ts +19 -4
- package/src/io/normalize/normalize-text.ts +33 -0
- package/src/io/ooxml/parse-anchor.ts +182 -0
- package/src/io/ooxml/parse-drawing.ts +319 -0
- package/src/io/ooxml/parse-fields.ts +115 -2
- package/src/io/ooxml/parse-fill.ts +215 -0
- package/src/io/ooxml/parse-font-table.ts +190 -0
- package/src/io/ooxml/parse-footnotes.ts +52 -1
- package/src/io/ooxml/parse-main-document.ts +241 -1
- package/src/io/ooxml/parse-numbering.ts +96 -0
- package/src/io/ooxml/parse-picture.ts +158 -0
- package/src/io/ooxml/parse-settings.ts +34 -0
- package/src/io/ooxml/parse-shapes.ts +87 -0
- package/src/io/ooxml/parse-solid-fill.ts +11 -0
- package/src/io/ooxml/parse-styles.ts +74 -1
- package/src/io/ooxml/parse-theme.ts +60 -0
- package/src/io/paste/html-clipboard.ts +449 -0
- package/src/io/paste/word-clipboard.ts +5 -1
- package/src/legal/_document-root.ts +26 -0
- package/src/legal/bookmarks.ts +4 -3
- package/src/legal/cross-references.ts +3 -2
- package/src/legal/defined-terms.ts +2 -1
- package/src/legal/signature-blocks.ts +2 -1
- package/src/model/canonical-document.ts +421 -3
- package/src/runtime/chart/chart-model-store.ts +73 -10
- package/src/runtime/document-runtime.ts +760 -41
- package/src/runtime/document-search.ts +61 -0
- package/src/runtime/edit-ops/index.ts +129 -0
- package/src/runtime/event-refresh-hints.ts +7 -0
- package/src/runtime/field-resolver.ts +341 -0
- package/src/runtime/footnote-resolver.ts +55 -0
- package/src/runtime/hyperlink-color-resolver.ts +13 -10
- package/src/runtime/object-grab/index.ts +51 -0
- package/src/runtime/paragraph-style-resolver.ts +105 -0
- package/src/runtime/query-scopes.ts +186 -0
- package/src/runtime/resolved-numbering-geometry.ts +12 -0
- package/src/runtime/scope-resolver.ts +60 -0
- package/src/runtime/selection/cursor-ops.ts +186 -15
- package/src/runtime/selection/index.ts +17 -1
- package/src/runtime/structure-ops/index.ts +77 -0
- package/src/runtime/styles-cascade.ts +33 -0
- package/src/runtime/surface-projection.ts +192 -12
- package/src/runtime/theme-color-resolver.ts +189 -44
- package/src/runtime/units.ts +46 -0
- package/src/runtime/view-state.ts +13 -2
- package/src/ui/WordReviewEditor.tsx +239 -11
- package/src/ui/editor-runtime-boundary.ts +97 -1
- package/src/ui/editor-shell-view.tsx +1 -1
- package/src/ui/runtime-shortcut-dispatch.ts +17 -3
- package/src/ui-tailwind/chart/ChartSurface.tsx +36 -10
- package/src/ui-tailwind/chart/layout/plot-area.ts +120 -45
- package/src/ui-tailwind/chart/render/area.tsx +22 -4
- package/src/ui-tailwind/chart/render/bar-column.tsx +37 -11
- package/src/ui-tailwind/chart/render/bubble.tsx +6 -2
- package/src/ui-tailwind/chart/render/combo.tsx +37 -4
- package/src/ui-tailwind/chart/render/line.tsx +28 -5
- package/src/ui-tailwind/chart/render/pie.tsx +36 -16
- package/src/ui-tailwind/chart/render/progressive-render.ts +8 -1
- package/src/ui-tailwind/chart/render/scatter.tsx +9 -4
- package/src/ui-tailwind/chrome/avatar-initials.ts +15 -0
- package/src/ui-tailwind/chrome/tw-comment-preview.tsx +3 -1
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +14 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +3 -2
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +30 -11
- package/src/ui-tailwind/chrome/tw-shortcut-hint.tsx +15 -2
- package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +1 -1
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +24 -7
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +31 -12
- package/src/ui-tailwind/chrome-overlay/page-border-resolver.ts +211 -0
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +24 -0
- package/src/ui-tailwind/chrome-overlay/tw-comment-balloon-layer.tsx +74 -0
- package/src/ui-tailwind/chrome-overlay/tw-locked-block-layer.tsx +65 -0
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-border-overlay.tsx +233 -0
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +135 -13
- package/src/ui-tailwind/chrome-overlay/tw-revision-margin-bar-layer.tsx +51 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +12 -4
- package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +32 -12
- package/src/ui-tailwind/chrome-overlay/tw-toc-outline-sidebar.tsx +133 -0
- package/src/ui-tailwind/editor-surface/chart-node-view.tsx +49 -10
- package/src/ui-tailwind/editor-surface/float-wrap-resolver.ts +119 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +236 -9
- package/src/ui-tailwind/editor-surface/pm-schema.ts +214 -11
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +32 -2
- package/src/ui-tailwind/editor-surface/shape-renderer.ts +206 -0
- package/src/ui-tailwind/editor-surface/surface-layer.ts +66 -0
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +29 -0
- package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +7 -1
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +22 -6
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +10 -16
- package/src/ui-tailwind/review/tw-health-panel.tsx +0 -25
- package/src/ui-tailwind/review/tw-rail-card.tsx +38 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +2 -2
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +5 -12
- package/src/ui-tailwind/review/tw-workflow-tab.tsx +2 -2
- package/src/ui-tailwind/theme/editor-theme.css +1 -0
- package/src/ui-tailwind/theme/tokens.css +6 -0
- package/src/ui-tailwind/theme/tokens.ts +10 -0
- package/src/ui-tailwind/tw-review-workspace.tsx +23 -0
- package/src/validation/compatibility-engine.ts +2 -0
- package/src/validation/docx-comment-proof.ts +12 -3
package/README.md
CHANGED
|
@@ -238,7 +238,7 @@ Engineering work is organized into 9 lanes (5 active now + 2 later polish + 2 fi
|
|
|
238
238
|
| 6b | [**Shell & Workspace Chrome**](docs/plans/lane-6b-shell-workspace-chrome.md) | **~15%** | Gated on 6a.S1+S2 — shell header + toolbar + status + alert banner + unsaved modal + collab chrome restyle; mode-dock decommission; TwCommandPalette |
|
|
239
239
|
| 6c | [**Context & Review Surfaces**](docs/plans/lane-6c-context-review-surfaces.md) | **~15%** | Gated on 6a.S1+S2 — selection toolbar + suggestion card + rail + scope + context toolbars + health panel restyle; TwCommentPreview / TwEmptyState / TwShortcutHint |
|
|
240
240
|
| 6d | [**Visual Fidelity**](docs/plans/lane-6d-visual-fidelity.md) | **~20%** | Gated on 6a.S1+S2 + external-lane deps — L8 A/B/C shipped; L8 Phase D + P11 overlays + P7 + P12 + P14 next |
|
|
241
|
-
| 7a | [**Visual Fidelity Register**](docs/plans/lane-7a-visual-fidelity-register.md) |
|
|
241
|
+
| 7a | [**Visual Fidelity Register**](docs/plans/lane-7a-visual-fidelity-register.md) | **✅ 100%** | CLOSED — V5 covers (`6f8869b7`), V6 REF/PAGEREF baseline via CO3 + contract pin (`99b66a1f`), V7 × CO1 5-combo cascade audit (`df315488`) |
|
|
242
242
|
| 7b | [**Revision & Redline Completion**](docs/plans/lane-7b-revision-redline-completion.md) | **0%** | LATER (gated on 6a–6d) — X4.a/b structural table revisions, X5 ffData, move-pairing |
|
|
243
243
|
| 7c | [**Preservation & Format Coverage**](docs/plans/lane-7c-preservation-format-coverage.md) | **0%** | LATER (gated on 6a–6d) — O6 Strict, OLE preserve-only, picture-SDT, w14/kern/VML defer |
|
|
244
244
|
| 7d | [**Platform Hardening & Hygiene**](docs/plans/lane-7d-platform-hardening-hygiene.md) | **0%** | LATER (gated on 6a–6d) — harness-crash-hardening, fastload activation, worktree consolidation |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.58",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
6
|
"packageManager": "pnpm@10.30.3",
|
|
7
7
|
"type": "module",
|
package/src/api/public-types.ts
CHANGED
|
@@ -482,6 +482,12 @@ export interface SearchOptions {
|
|
|
482
482
|
matchCase?: boolean;
|
|
483
483
|
wholeWord?: boolean;
|
|
484
484
|
limit?: number;
|
|
485
|
+
/** Phase C §C3 — treat `query` as a JS regex pattern (compiled with `u` flag). */
|
|
486
|
+
regex?: boolean;
|
|
487
|
+
/** Phase C §C3 — restrict results to positions inside this scopeId's marker range. */
|
|
488
|
+
inScope?: string;
|
|
489
|
+
/** Phase C §C3 — restrict results to a specific story target (default: main). */
|
|
490
|
+
inStory?: EditorStoryTarget;
|
|
485
491
|
}
|
|
486
492
|
|
|
487
493
|
export interface SearchResultSnapshot {
|
|
@@ -717,11 +723,17 @@ export type SnapshotRefreshChangeKind =
|
|
|
717
723
|
| "structure"
|
|
718
724
|
| "checkpoint";
|
|
719
725
|
|
|
726
|
+
export interface TocRefreshTrigger {
|
|
727
|
+
headingContentChanged: boolean;
|
|
728
|
+
headingStructureChanged: boolean;
|
|
729
|
+
}
|
|
730
|
+
|
|
720
731
|
export interface SnapshotRefreshHints {
|
|
721
732
|
invalidate: SnapshotRefreshInvalidateTarget[];
|
|
722
733
|
staleTargets: SnapshotRefreshStaleTarget[];
|
|
723
734
|
changeKinds: SnapshotRefreshChangeKind[];
|
|
724
735
|
checkpointType?: "session" | "snapshot" | "export";
|
|
736
|
+
tocRefreshTrigger?: TocRefreshTrigger;
|
|
725
737
|
}
|
|
726
738
|
|
|
727
739
|
export interface StyleCatalogEntrySnapshot {
|
|
@@ -798,6 +810,51 @@ export type SurfaceTextMark =
|
|
|
798
810
|
| "smallCaps"
|
|
799
811
|
| "allCaps";
|
|
800
812
|
|
|
813
|
+
/**
|
|
814
|
+
* V2c.4 / V2c.5 — DrawingFrame anchor geometry projected onto image + shape
|
|
815
|
+
* segments. Mirrors the canonical `AnchorGeometry` shape (defined in
|
|
816
|
+
* `src/model/canonical-document.ts`) with the fields chrome consumers need
|
|
817
|
+
* for float-wrap (Lane 6d N9), object-selection chrome (N6), and frame
|
|
818
|
+
* positioning. EMU values are kept verbatim — converters that need px
|
|
819
|
+
* apply 9525 EMU = 1 px at 96 dpi.
|
|
820
|
+
*/
|
|
821
|
+
export interface SurfaceDrawingAnchor {
|
|
822
|
+
display: "inline" | "floating";
|
|
823
|
+
wrapMode: "none" | "square" | "tight" | "through" | "topAndBottom";
|
|
824
|
+
extent: { widthEmu: number; heightEmu: number };
|
|
825
|
+
positionH?: { relativeFrom: string; align?: string; offset?: number };
|
|
826
|
+
positionV?: { relativeFrom: string; align?: string; offset?: number };
|
|
827
|
+
distMargins?: { top?: number; bottom?: number; left?: number; right?: number };
|
|
828
|
+
relativeHeight?: number;
|
|
829
|
+
behindDoc?: boolean;
|
|
830
|
+
layoutInCell?: boolean;
|
|
831
|
+
allowOverlap?: boolean;
|
|
832
|
+
simplePos?: boolean;
|
|
833
|
+
/** docPr.id / .name / .descr — used for accessibility chrome and selection labels. */
|
|
834
|
+
docPr?: { id: string; name?: string; descr?: string };
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* V2c.4 — Picture-effect data (crop, rotation, flip, preset clip geometry).
|
|
839
|
+
* `srcRect` percentages match OOXML's `a:srcRect` semantics: 0 = no crop,
|
|
840
|
+
* 100 000 = fully cropped from that edge. `rotation` is in 60 000ths of a
|
|
841
|
+
* degree (OOXML `a:xfrm a:rot`).
|
|
842
|
+
*/
|
|
843
|
+
export interface SurfacePictureEffects {
|
|
844
|
+
srcRect?: { top: number; bottom: number; left: number; right: number };
|
|
845
|
+
rotation?: number;
|
|
846
|
+
flipH?: boolean;
|
|
847
|
+
flipV?: boolean;
|
|
848
|
+
presetGeom?: string;
|
|
849
|
+
stretch?: boolean;
|
|
850
|
+
/** N11.b — a:softEdge feather radius in EMU. Maps to CSS `filter: blur(R)`. */
|
|
851
|
+
softEdgeRadius?: number;
|
|
852
|
+
/** N11.b — a:outerShdw drop shadow. `blurRad`/`dist` in EMU; `dir` in 60000ths of a degree. */
|
|
853
|
+
outerShadow?: { blurRad: number; dist: number; dir: number; color: string; colorType: "srgbClr" | "schemeClr" };
|
|
854
|
+
/** N11.b — a:glow ambient glow. `radius` in EMU. */
|
|
855
|
+
glow?: { radius: number; color: string; colorType: "srgbClr" | "schemeClr" };
|
|
856
|
+
}
|
|
857
|
+
|
|
801
858
|
export type SurfaceInlineSegment =
|
|
802
859
|
| {
|
|
803
860
|
segmentId: string;
|
|
@@ -836,6 +893,19 @@ export type SurfaceInlineSegment =
|
|
|
836
893
|
state: "editable" | "missing";
|
|
837
894
|
display?: "inline" | "floating";
|
|
838
895
|
detail?: string;
|
|
896
|
+
/**
|
|
897
|
+
* V2c.4 — DrawingFrame anchor geometry surfaced for Lane 6d float-wrap
|
|
898
|
+
* (P7) and chrome consumers. Present only when the canonical model
|
|
899
|
+
* carries non-trivial anchor metadata; inline pictures with default
|
|
900
|
+
* positioning omit it so the simple-image path stays cheap.
|
|
901
|
+
*/
|
|
902
|
+
anchor?: SurfaceDrawingAnchor;
|
|
903
|
+
/**
|
|
904
|
+
* V2c.4 — Picture-effect data (crop, rotation, flip, preset geometry).
|
|
905
|
+
* Absent when none of the underlying `PictureContent` effect fields
|
|
906
|
+
* are set, so consumers can fast-path image rendering when undefined.
|
|
907
|
+
*/
|
|
908
|
+
pictureEffects?: SurfacePictureEffects;
|
|
839
909
|
}
|
|
840
910
|
| {
|
|
841
911
|
segmentId: string;
|
|
@@ -886,6 +956,44 @@ export type SurfaceInlineSegment =
|
|
|
886
956
|
instruction: string;
|
|
887
957
|
refreshStatus: FieldRefreshStatus;
|
|
888
958
|
label: string;
|
|
959
|
+
}
|
|
960
|
+
| {
|
|
961
|
+
/**
|
|
962
|
+
* V2c.5 — DrawingFrame shape segment. Replaces the `complex_preview`
|
|
963
|
+
* fallback that used to swallow `wps:wsp` shapes. Carries the
|
|
964
|
+
* geometry preset, fill/line, optional textbox flag + first-paragraph
|
|
965
|
+
* preview, and (for floating shapes) anchor geometry. Lane 6d's N10
|
|
966
|
+
* shape rendering consumes this segment.
|
|
967
|
+
*/
|
|
968
|
+
segmentId: string;
|
|
969
|
+
kind: "shape";
|
|
970
|
+
from: number;
|
|
971
|
+
to: number;
|
|
972
|
+
label: string;
|
|
973
|
+
detail: string;
|
|
974
|
+
anchor?: SurfaceDrawingAnchor;
|
|
975
|
+
geometry?: string;
|
|
976
|
+
fill?:
|
|
977
|
+
| { kind: "solid"; color: string; colorType: "srgbClr" | "schemeClr" }
|
|
978
|
+
| { kind: "none" }
|
|
979
|
+
| {
|
|
980
|
+
kind: "gradient";
|
|
981
|
+
stops: Array<{ pos: number; color: string; colorType: "srgbClr" | "schemeClr" }>;
|
|
982
|
+
direction:
|
|
983
|
+
| { kind: "linear"; angle: number; scaled?: boolean }
|
|
984
|
+
| { kind: "path"; path: "circle" | "rect" | "shape" };
|
|
985
|
+
rotWithShape?: boolean;
|
|
986
|
+
}
|
|
987
|
+
| {
|
|
988
|
+
kind: "pattern";
|
|
989
|
+
preset: string;
|
|
990
|
+
fg?: { color: string; colorType: "srgbClr" | "schemeClr" };
|
|
991
|
+
bg?: { color: string; colorType: "srgbClr" | "schemeClr" };
|
|
992
|
+
};
|
|
993
|
+
line?: { color?: string; widthEmu?: number; noLine?: boolean };
|
|
994
|
+
isTextBox?: boolean;
|
|
995
|
+
/** First-paragraph plain-text preview when `isTextBox` is true. */
|
|
996
|
+
txbxText?: string;
|
|
889
997
|
};
|
|
890
998
|
|
|
891
999
|
export interface SurfaceTableCellSnapshot {
|
|
@@ -1716,6 +1824,8 @@ export interface RuntimeRenderSnapshot {
|
|
|
1716
1824
|
commandState: CommandStateSnapshot;
|
|
1717
1825
|
surface?: EditorSurfaceSnapshot;
|
|
1718
1826
|
protectionSnapshot: ProtectionSnapshot;
|
|
1827
|
+
/** R.3 — stable id of the currently grabbed image/shape, or null. Populated from grab state so chrome overlays re-render on selectObject/deselectObject. */
|
|
1828
|
+
grabbedObjectId?: string | null;
|
|
1719
1829
|
}
|
|
1720
1830
|
|
|
1721
1831
|
export interface EditorSessionState {
|
|
@@ -1946,6 +2056,67 @@ export interface WorkflowMetadataSnapshot {
|
|
|
1946
2056
|
entries: WorkflowMetadataEntry[];
|
|
1947
2057
|
}
|
|
1948
2058
|
|
|
2059
|
+
// ---------------------------------------------------------------------------
|
|
2060
|
+
// Phase C — host-side scope query surface (§C1, §C2)
|
|
2061
|
+
// ---------------------------------------------------------------------------
|
|
2062
|
+
|
|
2063
|
+
/**
|
|
2064
|
+
* §C1 — Filter passed to `queryScopes` / `findScopesAt` /
|
|
2065
|
+
* `findScopesIntersecting`. All fields are optional and AND'd together.
|
|
2066
|
+
* Snapshot-based; never triggers runtime mutation.
|
|
2067
|
+
*/
|
|
2068
|
+
export interface ScopeQueryFilter {
|
|
2069
|
+
/** Match only scopes attached to any of these work items. */
|
|
2070
|
+
workItemIds?: string[];
|
|
2071
|
+
/** Match only scopes in one of these modes. */
|
|
2072
|
+
modes?: WorkflowScopeMode[];
|
|
2073
|
+
/** Match only scopes whose `domain` is one of these. */
|
|
2074
|
+
domains?: Array<NonNullable<WorkflowScope["domain"]>>;
|
|
2075
|
+
/**
|
|
2076
|
+
* Match scopes that carry at least one entry with this `metadataId`.
|
|
2077
|
+
* Uses the runtime `WorkflowMetadataSnapshot` joined by `entry.scopeId`.
|
|
2078
|
+
*/
|
|
2079
|
+
metadataId?: string;
|
|
2080
|
+
/**
|
|
2081
|
+
* Match scopes that carry at least one entry whose `value` passes the
|
|
2082
|
+
* predicate. Entries with no `value` are skipped.
|
|
2083
|
+
*/
|
|
2084
|
+
hasValue?: (value: Record<string, unknown>, entry: WorkflowMetadataEntry) => boolean;
|
|
2085
|
+
/** Label prefix (case-insensitive). */
|
|
2086
|
+
labelPrefix?: string;
|
|
2087
|
+
/**
|
|
2088
|
+
* Story target filter. Defaults to `{ kind: "main" }` when omitted. Pass
|
|
2089
|
+
* `"*"` for any story.
|
|
2090
|
+
*/
|
|
2091
|
+
storyTarget?: EditorStoryTarget | "*";
|
|
2092
|
+
/** Max result count. Undefined = no cap. */
|
|
2093
|
+
limit?: number;
|
|
2094
|
+
/**
|
|
2095
|
+
* §C8 — include scopes with `visibility: "hidden"`. Default: false.
|
|
2096
|
+
* Accepted but has no effect until §C8 adds the `visibility` field.
|
|
2097
|
+
*/
|
|
2098
|
+
includeHidden?: boolean;
|
|
2099
|
+
/**
|
|
2100
|
+
* §C8 — include scopes with `visibility: "invisible"`. Default: false.
|
|
2101
|
+
* Accepted but has no effect until §C8 adds the `visibility` field.
|
|
2102
|
+
*/
|
|
2103
|
+
includeInvisible?: boolean;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
/**
|
|
2107
|
+
* §C1 — One result from `queryScopes`. Joins the scope record with the
|
|
2108
|
+
* metadata entries that point at it and the resolved work item (when
|
|
2109
|
+
* `scope.workItemId` matches one in the overlay).
|
|
2110
|
+
*/
|
|
2111
|
+
export interface ScopeQueryResult {
|
|
2112
|
+
/** Scope record from the overlay. */
|
|
2113
|
+
scope: WorkflowScope;
|
|
2114
|
+
/** Metadata entries whose `entry.scopeId === scope.scopeId`. */
|
|
2115
|
+
entries: WorkflowMetadataEntry[];
|
|
2116
|
+
/** Resolved work item when `scope.workItemId` is set and present. */
|
|
2117
|
+
workItem: WorkflowWorkItem | null;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
1949
2120
|
// ---------------------------------------------------------------------------
|
|
1950
2121
|
// R2 — issue metadata (scope-card-overlay P1)
|
|
1951
2122
|
// ---------------------------------------------------------------------------
|
|
@@ -2563,6 +2734,12 @@ export type WordReviewEditorEvent =
|
|
|
2563
2734
|
documentId: string;
|
|
2564
2735
|
activeStory: EditorStoryTarget;
|
|
2565
2736
|
}
|
|
2737
|
+
| {
|
|
2738
|
+
type: "toc_auto_refreshed";
|
|
2739
|
+
documentId: string;
|
|
2740
|
+
entryCount: number;
|
|
2741
|
+
trigger: TocRefreshTrigger;
|
|
2742
|
+
}
|
|
2566
2743
|
| {
|
|
2567
2744
|
type: "warning_added";
|
|
2568
2745
|
documentId: string;
|
|
@@ -3060,6 +3237,61 @@ export interface WordReviewEditorRef {
|
|
|
3060
3237
|
* add merge-intent + richer caret placement as paste parsers come online.
|
|
3061
3238
|
*/
|
|
3062
3239
|
insertFragment(fragment: CanonicalDocumentFragment, target?: EditorAnchorProjection): void;
|
|
3240
|
+
/**
|
|
3241
|
+
* I2 Tier B Slice 4b — serialize `target` (or the current selection) to a
|
|
3242
|
+
* `CanonicalDocumentFragment` and store it in the editor's internal
|
|
3243
|
+
* clipboard buffer. No document mutation.
|
|
3244
|
+
*/
|
|
3245
|
+
copy(target?: EditorAnchorProjection): void;
|
|
3246
|
+
/**
|
|
3247
|
+
* I2 Tier B Slice 4b — `copy(target)` + delete the range.
|
|
3248
|
+
*/
|
|
3249
|
+
cut(target?: EditorAnchorProjection): void;
|
|
3250
|
+
/**
|
|
3251
|
+
* I2 Tier B Slice 4b — return the last fragment written by `cut` / `copy`,
|
|
3252
|
+
* or `null` if none has been captured yet. Hosts pair this with
|
|
3253
|
+
* `insertFragment` to implement paste while Slice 4b's async-Clipboard-API
|
|
3254
|
+
* write lands with Slice 5.
|
|
3255
|
+
*/
|
|
3256
|
+
getClipboardBuffer(): CanonicalDocumentFragment | null;
|
|
3257
|
+
/**
|
|
3258
|
+
* v5 close-out — return the current clipboard buffer serialized to the
|
|
3259
|
+
* wire formats browsers/Word accept, or `null` when no clipboard op has
|
|
3260
|
+
* been performed. Hosts use this inside their own DOM `copy`/`cut` event
|
|
3261
|
+
* handler to feed `navigator.clipboard.write` (the editor does not
|
|
3262
|
+
* install the DOM handler itself).
|
|
3263
|
+
*/
|
|
3264
|
+
getClipboardWireFormats(): { wordml: string; html: string; plainText: string } | null;
|
|
3265
|
+
/**
|
|
3266
|
+
* R.3 ObjectGrabLayer — grab an inline / floating object (image, shape)
|
|
3267
|
+
* by its stable id. Single-select; replaces any previously grabbed
|
|
3268
|
+
* object. Lane 6 P11 chrome reads the grab state to paint handles.
|
|
3269
|
+
*/
|
|
3270
|
+
selectObject(objectId: string): void;
|
|
3271
|
+
/**
|
|
3272
|
+
* R.3 — release the grabbed object, if any. Safe to call when nothing
|
|
3273
|
+
* is grabbed.
|
|
3274
|
+
*/
|
|
3275
|
+
deselectObject(): void;
|
|
3276
|
+
/**
|
|
3277
|
+
* R.3 — return the grabbed object's id, or `null` when nothing is
|
|
3278
|
+
* grabbed.
|
|
3279
|
+
*/
|
|
3280
|
+
getGrabbedObject(): string | null;
|
|
3281
|
+
/**
|
|
3282
|
+
* R.5.a — open an action bracket. Hosts wrap compound edits (paste,
|
|
3283
|
+
* cut+paste, agent suggestion-apply) so snapshot emission + collab
|
|
3284
|
+
* broadcast + undo grouping see them as a single action.
|
|
3285
|
+
*
|
|
3286
|
+
* Phase 1: opt-in. Existing commands do not auto-bracket. Hosts that
|
|
3287
|
+
* want single-undo paste call `startAction("paste")` →
|
|
3288
|
+
* `insertFragment(fragment)` → `endAction()`.
|
|
3289
|
+
*/
|
|
3290
|
+
startAction(name: string): void;
|
|
3291
|
+
/** R.5.a — close one level of action bracketing. Unbalanced calls are no-ops. */
|
|
3292
|
+
endAction(): void;
|
|
3293
|
+
/** R.5.a — `true` when the runtime is inside one or more action brackets. */
|
|
3294
|
+
isInAction(): boolean;
|
|
3063
3295
|
toggleBulletedList(): void;
|
|
3064
3296
|
toggleNumberedList(): void;
|
|
3065
3297
|
toggleBold(): void;
|
|
@@ -3169,6 +3401,86 @@ export interface WordReviewEditorRef {
|
|
|
3169
3401
|
setWorkflowMetadataEntries(entries: WorkflowMetadataEntry[]): void;
|
|
3170
3402
|
clearWorkflowMetadataEntries(): void;
|
|
3171
3403
|
getWorkflowMetadataSnapshot(): WorkflowMetadataSnapshot;
|
|
3404
|
+
/**
|
|
3405
|
+
* Phase C §C1 — filter + project the current workflow overlay into a
|
|
3406
|
+
* scope-joined view. Reads a snapshot of the current overlay + metadata
|
|
3407
|
+
* entries + work items; no mutation. Results are sorted by start-marker
|
|
3408
|
+
* document position, tie-broken by `scopeId` ascending.
|
|
3409
|
+
*
|
|
3410
|
+
* Defaults: `storyTarget` = `{ kind: "main" }` (pass `"*"` for any
|
|
3411
|
+
* story); `includeHidden` / `includeInvisible` default to `false`
|
|
3412
|
+
* (§C8 additive — accepted today but no-ops until `visibility` lands).
|
|
3413
|
+
*
|
|
3414
|
+
* Returns `[]` when no workflow overlay has been set.
|
|
3415
|
+
*/
|
|
3416
|
+
queryScopes(filter?: ScopeQueryFilter): ScopeQueryResult[];
|
|
3417
|
+
/**
|
|
3418
|
+
* Phase C §C2 — every scope whose marker range contains `position.from`
|
|
3419
|
+
* (end-inclusive), ordered outermost → innermost. Pass a zero-length
|
|
3420
|
+
* range (`from === to`) for a point query. Returns the full
|
|
3421
|
+
* `ScopeQueryResult` (scope + entries + workItem) so results compose
|
|
3422
|
+
* directly with `setSelection`, `addScope`, `getLocationForAnchor`.
|
|
3423
|
+
*
|
|
3424
|
+
* Companion to `getInteractionGuardSnapshot().matchedScopeId` — that
|
|
3425
|
+
* API returns the innermost-only match; this one returns the whole
|
|
3426
|
+
* enclosing stack.
|
|
3427
|
+
*/
|
|
3428
|
+
findScopesAt(
|
|
3429
|
+
position: EditorAnchorProjection,
|
|
3430
|
+
options?: { includeHidden?: boolean; includeInvisible?: boolean },
|
|
3431
|
+
): ScopeQueryResult[];
|
|
3432
|
+
/**
|
|
3433
|
+
* Phase C §C2 — every scope whose marker range intersects `range`
|
|
3434
|
+
* (accepts a range anchor; non-range anchors yield `[]`). Default
|
|
3435
|
+
* `mode: "overlap"` matches any intersection including touching
|
|
3436
|
+
* endpoints; `mode: "contain"` requires the scope's entire range to
|
|
3437
|
+
* sit within `range`. Deterministic order by start-marker position.
|
|
3438
|
+
*/
|
|
3439
|
+
findScopesIntersecting(
|
|
3440
|
+
range: EditorAnchorProjection,
|
|
3441
|
+
options?: {
|
|
3442
|
+
includeHidden?: boolean;
|
|
3443
|
+
includeInvisible?: boolean;
|
|
3444
|
+
mode?: "overlap" | "contain";
|
|
3445
|
+
},
|
|
3446
|
+
): ScopeQueryResult[];
|
|
3447
|
+
/**
|
|
3448
|
+
* Phase C §C3 — find the first text match in the document.
|
|
3449
|
+
* Supports `regex`, `inScope`, `inStory` options.
|
|
3450
|
+
* Throws `EditorApiError({ code: "search_invalid_regex" })` synchronously
|
|
3451
|
+
* when `options.regex === true` and `query` is not a valid regex pattern.
|
|
3452
|
+
* Returns `null` when no match is found.
|
|
3453
|
+
*/
|
|
3454
|
+
findFirstText(
|
|
3455
|
+
query: string,
|
|
3456
|
+
options?: SearchOptions,
|
|
3457
|
+
): EditorAnchorProjection | null;
|
|
3458
|
+
/**
|
|
3459
|
+
* Phase C §C3 — find all text matches in the document.
|
|
3460
|
+
* Same options and error contract as `findFirstText`.
|
|
3461
|
+
* Returns `[]` when no matches are found.
|
|
3462
|
+
*/
|
|
3463
|
+
findAllText(
|
|
3464
|
+
query: string,
|
|
3465
|
+
options?: SearchOptions,
|
|
3466
|
+
): EditorAnchorProjection[];
|
|
3467
|
+
/**
|
|
3468
|
+
* Phase C §C3 — find first text match and select it. Returns `true` if a
|
|
3469
|
+
* match was found (and selection updated), `false` otherwise.
|
|
3470
|
+
*/
|
|
3471
|
+
selectFirstText(
|
|
3472
|
+
query: string,
|
|
3473
|
+
options?: SearchOptions,
|
|
3474
|
+
): boolean;
|
|
3475
|
+
/**
|
|
3476
|
+
* Phase C §C3 — select first text match and return the total match count.
|
|
3477
|
+
* Returns `0` when no matches are found. Multi-range selection deferred
|
|
3478
|
+
* to Lane 1 shipping `SelectionSnapshot.multi`.
|
|
3479
|
+
*/
|
|
3480
|
+
selectAllText(
|
|
3481
|
+
query: string,
|
|
3482
|
+
options?: SearchOptions,
|
|
3483
|
+
): number;
|
|
3172
3484
|
/**
|
|
3173
3485
|
* Schema 1.1 — set the overlay default for metadata persistence.
|
|
3174
3486
|
* Author-only per collab-master-plan §7 role-gating matrix;
|
|
@@ -3706,6 +4018,24 @@ export interface ResolveMetadataConflictInput {
|
|
|
3706
4018
|
}
|
|
3707
4019
|
|
|
3708
4020
|
// ---------------------------------------------------------------------------
|
|
4021
|
+
// Phase C §C3 — generic editor API error
|
|
4022
|
+
// ---------------------------------------------------------------------------
|
|
4023
|
+
|
|
4024
|
+
/**
|
|
4025
|
+
* Thrown synchronously by query methods when the caller passes an invalid
|
|
4026
|
+
* argument. The `code` field identifies the specific failure:
|
|
4027
|
+
* - `"search_invalid_regex"` — `options.regex === true` and `query` is not
|
|
4028
|
+
* a valid JavaScript regular expression pattern.
|
|
4029
|
+
*/
|
|
4030
|
+
export class EditorApiError extends Error {
|
|
4031
|
+
readonly code: string;
|
|
4032
|
+
constructor(params: { code: string; message?: string }) {
|
|
4033
|
+
super(params.message ?? `EditorApiError: ${params.code}`);
|
|
4034
|
+
this.name = "EditorApiError";
|
|
4035
|
+
this.code = params.code;
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
|
|
3709
4039
|
// Schema 1.1 — metadata-persistence errors (P17)
|
|
3710
4040
|
// ---------------------------------------------------------------------------
|
|
3711
4041
|
|
|
@@ -522,6 +522,7 @@ function getInlineLength(node: InlineNode): number {
|
|
|
522
522
|
case "shape":
|
|
523
523
|
case "wordart":
|
|
524
524
|
case "vml_shape":
|
|
525
|
+
case "drawing_frame":
|
|
525
526
|
return 1;
|
|
526
527
|
}
|
|
527
528
|
}
|
|
@@ -581,6 +582,8 @@ function getInlineDisplayText(node: InlineNode): string {
|
|
|
581
582
|
return node.text;
|
|
582
583
|
case "vml_shape":
|
|
583
584
|
return node.text ?? "[VML Shape]";
|
|
585
|
+
case "drawing_frame":
|
|
586
|
+
return node.content.type === "picture" ? "[Image]" : "[Drawing]";
|
|
584
587
|
}
|
|
585
588
|
}
|
|
586
589
|
|
|
@@ -89,7 +89,8 @@ import {
|
|
|
89
89
|
setHeaderFooterLinkAtSectionIndex,
|
|
90
90
|
} from "./section-layout-commands.ts";
|
|
91
91
|
import { insertPageBreak, insertTable } from "./text-commands.ts";
|
|
92
|
-
import {
|
|
92
|
+
import { editLayer } from "../../runtime/edit-ops/index.ts";
|
|
93
|
+
import { structureLayer } from "../../runtime/structure-ops/index.ts";
|
|
93
94
|
import type { TableSelectionDescriptor } from "../../runtime/table-commands.ts";
|
|
94
95
|
|
|
95
96
|
export type ContentChildrenPatch =
|
|
@@ -575,8 +576,10 @@ export function executeEditorCommand(
|
|
|
575
576
|
? applySuggestingInsert(state, command.text, context)
|
|
576
577
|
: undefined;
|
|
577
578
|
if (suggestingResult) return suggestingResult;
|
|
579
|
+
// R.2 — dispatch via the named EditLayer entry point so the seam is the
|
|
580
|
+
// single site where R.5.a/b hooks will attach.
|
|
578
581
|
return applyTextCommand(state, context.timestamp, (document, selection) =>
|
|
579
|
-
|
|
582
|
+
editLayer.applyTextInsert(document, selection, command.text, context, command.formatting),
|
|
580
583
|
);
|
|
581
584
|
}
|
|
582
585
|
case "text.delete-backward": {
|
|
@@ -585,7 +588,7 @@ export function executeEditorCommand(
|
|
|
585
588
|
: undefined;
|
|
586
589
|
if (suggestingResult) return suggestingResult;
|
|
587
590
|
return applyTextCommand(state, context.timestamp, (document, selection) =>
|
|
588
|
-
|
|
591
|
+
editLayer.applyDeleteBackward(document, selection, context),
|
|
589
592
|
);
|
|
590
593
|
}
|
|
591
594
|
case "text.delete-forward": {
|
|
@@ -594,7 +597,7 @@ export function executeEditorCommand(
|
|
|
594
597
|
: undefined;
|
|
595
598
|
if (suggestingResult) return suggestingResult;
|
|
596
599
|
return applyTextCommand(state, context.timestamp, (document, selection) =>
|
|
597
|
-
|
|
600
|
+
editLayer.applyDeleteForward(document, selection, context),
|
|
598
601
|
);
|
|
599
602
|
}
|
|
600
603
|
case "text.insert-tab": {
|
|
@@ -603,7 +606,7 @@ export function executeEditorCommand(
|
|
|
603
606
|
: undefined;
|
|
604
607
|
if (suggestingResult) return suggestingResult;
|
|
605
608
|
return applyTextCommand(state, context.timestamp, (document, selection) =>
|
|
606
|
-
|
|
609
|
+
editLayer.applyInsertTab(document, selection, context),
|
|
607
610
|
);
|
|
608
611
|
}
|
|
609
612
|
case "text.outdent-tab": {
|
|
@@ -632,7 +635,7 @@ export function executeEditorCommand(
|
|
|
632
635
|
: undefined;
|
|
633
636
|
if (suggestingResult) return suggestingResult;
|
|
634
637
|
return applyTextCommand(state, context.timestamp, (document, selection) =>
|
|
635
|
-
|
|
638
|
+
editLayer.applyInsertHardBreak(document, selection, context),
|
|
636
639
|
);
|
|
637
640
|
}
|
|
638
641
|
case "paragraph.split":
|
|
@@ -641,15 +644,18 @@ export function executeEditorCommand(
|
|
|
641
644
|
if (suggestingResult) return suggestingResult;
|
|
642
645
|
}
|
|
643
646
|
return applyTextCommand(state, context.timestamp, (document, selection) =>
|
|
644
|
-
|
|
647
|
+
editLayer.applySplitParagraph(document, selection, context),
|
|
645
648
|
);
|
|
646
649
|
case "fragment.insert": {
|
|
647
|
-
// I2 Tier B Slice 1 — route through the
|
|
650
|
+
// I2 Tier B Slice 1 + R.3 — route through the StructureLayer seam. No
|
|
648
651
|
// suggesting-mode branch yet; fragment insertion always lands as a direct
|
|
649
652
|
// edit. Future slices will gate behind track-changes when a fixture needs it.
|
|
650
|
-
const result = applyFragmentInsert(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
+
const result = structureLayer.applyFragmentInsert(
|
|
654
|
+
state.document,
|
|
655
|
+
state.selection,
|
|
656
|
+
command.fragment,
|
|
657
|
+
{ timestamp: context.timestamp },
|
|
658
|
+
);
|
|
653
659
|
return buildDocumentReplaceTransaction(state, context, result);
|
|
654
660
|
}
|
|
655
661
|
case "runtime.set-read-only":
|
|
@@ -31,7 +31,24 @@ export interface DetachedAnchor {
|
|
|
31
31
|
reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity";
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Internal representation of an anchor projection — uses `DocRange` so
|
|
36
|
+
* mapping helpers compose ranges independently of assoc semantics. The
|
|
37
|
+
* public-facing shape lives at `src/api/public-types.ts` (same simple name
|
|
38
|
+
* `EditorAnchorProjection`) and is flat (`{ kind: "range", from, to, assoc }`).
|
|
39
|
+
* Conversion between the two goes through `src/core/selection/anchor-conversion.ts`.
|
|
40
|
+
*/
|
|
41
|
+
export type InternalEditorAnchorProjection = RangeAnchor | NodeAnchor | DetachedAnchor;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated X3 Phase 5 — prefer `InternalEditorAnchorProjection` to avoid
|
|
45
|
+
* name-collision with the public `EditorAnchorProjection` from
|
|
46
|
+
* `src/api/public-types.ts`. Internal call-sites have migrated (via
|
|
47
|
+
* `import type { EditorAnchorProjection as InternalEditorAnchorProjection }`
|
|
48
|
+
* aliasing in `document-runtime.ts`). This alias stays for one release cycle
|
|
49
|
+
* so third-party internal consumers don't break. Remove in v2.1+.
|
|
50
|
+
*/
|
|
51
|
+
export type EditorAnchorProjection = InternalEditorAnchorProjection;
|
|
35
52
|
|
|
36
53
|
export interface MappingStep {
|
|
37
54
|
from: Position;
|
|
@@ -104,17 +104,22 @@ export function rangeStaysWithinSingleParagraph(
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
107
|
+
* Width of the paragraph-to-table proximity window used by
|
|
108
|
+
* `snapCommentAnchorAwayFromTable` to decide when to nudge endpoints to
|
|
109
|
+
* paragraph boundaries. Originally the I8 rejection threshold before Lane 3b
|
|
110
|
+
* §O8 shipped; now used only by the (opt-in) snap helper for hosts that
|
|
111
|
+
* prefer clean anchors.
|
|
112
112
|
*/
|
|
113
113
|
export const TABLE_ADJACENT_WINDOW = 1;
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
/**
|
|
116
|
+
* I8 guard removal (post-O8) — the `comment_anchor_table_adjacent` rejection
|
|
117
|
+
* reason has been retired now that Lane 3b §O8 fixed the serializer's
|
|
118
|
+
* per-paragraph offset walker. The type alias remains as a deprecated union
|
|
119
|
+
* stub so existing host code that narrows on it keeps compiling; new code
|
|
120
|
+
* should simply check against `"invalid_comment_anchor"`.
|
|
121
|
+
*/
|
|
122
|
+
export type CommentAnchorRejectionReason = "invalid_comment_anchor";
|
|
118
123
|
|
|
119
124
|
export function canCreateDocxCommentAnchor(
|
|
120
125
|
content: unknown,
|
|
@@ -140,17 +145,18 @@ export function commentAnchorRejectionReason(
|
|
|
140
145
|
return "invalid_comment_anchor";
|
|
141
146
|
}
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
148
|
+
// I8 branch removed — mid-run-near-table anchors now serialize cleanly
|
|
149
|
+
// via Lane 3b §O8's `walkInlineNodeForBoundaries` fix.
|
|
147
150
|
return null;
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
/**
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
+
* Snap a mid-run-near-table anchor to paragraph boundaries when a host opts
|
|
155
|
+
* into `snapToSafeBoundary`. Originally a defensive workaround for the pre-O8
|
|
156
|
+
* serializer bug; now a boundary-preference convenience for hosts that dislike
|
|
157
|
+
* mid-run comment anchors on principle. Returns `null` if the anchor cannot
|
|
158
|
+
* be rescued (e.g. crosses an opaque block); returns the input unchanged if
|
|
159
|
+
* it doesn't need snapping.
|
|
154
160
|
*/
|
|
155
161
|
export function snapCommentAnchorAwayFromTable(
|
|
156
162
|
content: unknown,
|
|
@@ -161,9 +167,14 @@ export function snapCommentAnchorAwayFromTable(
|
|
|
161
167
|
const normalized = normalizeRange(anchor.range);
|
|
162
168
|
if (normalized.from === normalized.to) return null;
|
|
163
169
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
if (
|
|
170
|
+
// If the anchor is already flagged invalid for other reasons (crosses an
|
|
171
|
+
// opaque block, etc.) we can't rescue it by snapping.
|
|
172
|
+
if (commentAnchorRejectionReason(content, anchor) !== null) return null;
|
|
173
|
+
|
|
174
|
+
// Detect mid-run-near-table via structural check directly (independent of
|
|
175
|
+
// rejection reason — the rejection branch is gone post-O8). If the anchor
|
|
176
|
+
// doesn't need snapping, pass it through unchanged.
|
|
177
|
+
if (!rangeLandsMidRunNearTableBoundary(content, normalized)) return anchor;
|
|
167
178
|
|
|
168
179
|
const surfaceBlocks = readSurfaceBlocks(content);
|
|
169
180
|
if (!surfaceBlocks) return null;
|