@beyondwork/docx-react-component 1.0.38 → 1.0.40
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 +305 -6
- package/src/core/commands/table-structure-commands.ts +31 -2
- package/src/core/commands/text-commands.ts +122 -2
- package/src/index.ts +9 -0
- package/src/io/docx-session.ts +1 -0
- package/src/io/export/serialize-numbering.ts +42 -8
- 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/ooxml/parse-fields.ts +10 -3
- 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/xml-attr-helpers.ts +60 -0
- package/src/io/ooxml/xml-element.ts +19 -0
- package/src/model/canonical-document.ts +83 -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-runtime.ts +141 -18
- package/src/runtime/layout/docx-font-loader.ts +30 -11
- package/src/runtime/layout/index.ts +2 -0
- package/src/runtime/layout/inert-layout-facet.ts +3 -0
- package/src/runtime/layout/layout-engine-instance.ts +69 -2
- package/src/runtime/layout/layout-invalidation.ts +14 -5
- package/src/runtime/layout/page-graph.ts +36 -0
- package/src/runtime/layout/paginate-paragraph-lines.ts +128 -0
- package/src/runtime/layout/paginated-layout-engine.ts +342 -28
- package/src/runtime/layout/project-block-fragments.ts +154 -20
- package/src/runtime/layout/public-facet.ts +81 -1
- 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 +21 -1
- package/src/runtime/numbering-prefix.ts +5 -0
- package/src/runtime/paragraph-style-resolver.ts +194 -0
- package/src/runtime/render/render-kernel.ts +5 -1
- 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/workflow-rail-segments.ts +149 -1
- package/src/ui/WordReviewEditor.tsx +302 -5
- 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 +22 -0
- package/src/ui/editor-surface-controller.tsx +9 -1
- package/src/ui/headless/chrome-registry.ts +34 -5
- package/src/ui/headless/scoped-chrome-policy.ts +29 -0
- package/src/ui-tailwind/chrome/review-queue-bar.tsx +2 -14
- package/src/ui-tailwind/chrome/role-action-sets.ts +14 -8
- package/src/ui-tailwind/chrome/tw-mode-dock.tsx +80 -0
- package/src/ui-tailwind/chrome/tw-selection-anchor-resolver.ts +7 -10
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +11 -0
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +11 -0
- package/src/ui-tailwind/chrome/tw-table-border-picker.tsx +245 -0
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +101 -21
- package/src/ui-tailwind/chrome/tw-table-grip-layer.tsx +353 -0
- package/src/ui-tailwind/chrome-overlay/chrome-overlay-projector.ts +5 -1
- package/src/ui-tailwind/chrome-overlay/index.ts +2 -6
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +82 -18
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +133 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card.tsx +386 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-rail-layer.tsx +140 -69
- package/src/ui-tailwind/editor-surface/page-slice-util.ts +15 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +28 -3
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +7 -2
- 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 +170 -63
- 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 -78
- 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 +6 -5
- package/src/ui-tailwind/theme/editor-theme.css +108 -15
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +122 -1
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +36 -3
- package/src/ui-tailwind/tw-review-workspace.tsx +207 -54
- package/src/runtime/collab-review-sync.ts +0 -254
- package/src/ui-tailwind/chrome-overlay/tw-workspace-view-switcher.tsx +0 -95
- package/src/ui-tailwind/editor-surface/pm-collab-plugins.ts +0 -40
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@beyondwork/docx-react-component",
|
|
3
3
|
"publisher": "beyondwork",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.40",
|
|
5
5
|
"description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
|
|
6
|
+
"packageManager": "pnpm@10.30.3",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"sideEffects": [
|
|
8
9
|
"**/*.css"
|
|
@@ -49,6 +50,10 @@
|
|
|
49
50
|
"types": "./src/runtime/document-runtime.ts",
|
|
50
51
|
"import": "./src/runtime/document-runtime.ts"
|
|
51
52
|
},
|
|
53
|
+
"./runtime/collab": {
|
|
54
|
+
"types": "./src/runtime/collab/index.ts",
|
|
55
|
+
"import": "./src/runtime/collab/index.ts"
|
|
56
|
+
},
|
|
52
57
|
"./core/commands/formatting-commands": {
|
|
53
58
|
"types": "./src/core/commands/formatting-commands.ts",
|
|
54
59
|
"import": "./src/core/commands/formatting-commands.ts"
|
|
@@ -88,6 +93,31 @@
|
|
|
88
93
|
"./ui-tailwind/theme/editor-theme.css": "./src/ui-tailwind/theme/editor-theme.css"
|
|
89
94
|
},
|
|
90
95
|
"types": "./src/index.ts",
|
|
96
|
+
"scripts": {
|
|
97
|
+
"build": "tsup",
|
|
98
|
+
"test": "bash scripts/run-workspace-tests.sh",
|
|
99
|
+
"test:repo": "node scripts/run-repo-tests.mjs core",
|
|
100
|
+
"test:repo:all": "node scripts/run-repo-tests.mjs all",
|
|
101
|
+
"test:repo:optional": "node scripts/run-repo-tests.mjs optional",
|
|
102
|
+
"test:repo:browser-ui": "node scripts/run-repo-tests.mjs browser-ui",
|
|
103
|
+
"test:wcag-audit": "node scripts/run-repo-tests.mjs wcag-audit",
|
|
104
|
+
"test:harness": "pnpm --filter @docx-react-component/react-word-editor-harness test",
|
|
105
|
+
"lint": "pnpm run lint:no-authored-js && pnpm run lint:docs-contracts && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
|
|
106
|
+
"lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
|
|
107
|
+
"lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
|
|
108
|
+
"lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
|
|
109
|
+
"lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
|
|
110
|
+
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
111
|
+
"wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
|
|
112
|
+
"wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
|
|
113
|
+
"wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
|
|
114
|
+
"wave:launch:managed": "bash scripts/wave-launch.sh",
|
|
115
|
+
"wave:status": "bash scripts/wave-status.sh",
|
|
116
|
+
"wave:watch": "bash scripts/wave-watch.sh --follow",
|
|
117
|
+
"wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
|
|
118
|
+
"wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
|
|
119
|
+
"harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
|
|
120
|
+
},
|
|
91
121
|
"keywords": [
|
|
92
122
|
"docx",
|
|
93
123
|
"word",
|
|
@@ -132,16 +162,12 @@
|
|
|
132
162
|
"react-dom": "^19.2.0",
|
|
133
163
|
"tailwindcss": "^4.2.2",
|
|
134
164
|
"yjs": "^13.6.0",
|
|
135
|
-
"y-prosemirror": "^1.2.0",
|
|
136
165
|
"y-protocols": "^1.0.0"
|
|
137
166
|
},
|
|
138
167
|
"peerDependenciesMeta": {
|
|
139
168
|
"yjs": {
|
|
140
169
|
"optional": true
|
|
141
170
|
},
|
|
142
|
-
"y-prosemirror": {
|
|
143
|
-
"optional": true
|
|
144
|
-
},
|
|
145
171
|
"y-protocols": {
|
|
146
172
|
"optional": true
|
|
147
173
|
}
|
|
@@ -163,33 +189,17 @@
|
|
|
163
189
|
"react-dom": "19.2.4",
|
|
164
190
|
"tsup": "^8.3.0",
|
|
165
191
|
"tsx": "^4.21.0",
|
|
166
|
-
"y-prosemirror": "^1.3.7",
|
|
167
192
|
"y-protocols": "^1.0.7",
|
|
168
193
|
"yjs": "^13.6.30"
|
|
169
194
|
},
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
"
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"lint": "pnpm run lint:no-authored-js && pnpm run lint:docs-contracts && pnpm run lint:tsgo && pnpm run lint:tsgo:harness",
|
|
180
|
-
"lint:docs-contracts": "bash scripts/check-reference-load-contract.sh",
|
|
181
|
-
"lint:no-authored-js": "bash scripts/check-no-authored-js.sh",
|
|
182
|
-
"lint:tsgo": "tsgo --noEmit -p tsconfig.build.json",
|
|
183
|
-
"lint:tsgo:harness": "pnpm --filter @docx-react-component/react-word-editor-harness lint:tsgo",
|
|
184
|
-
"context7:api-check": "bash scripts/context7-export-env.sh run bash scripts/context7-api-check.sh",
|
|
185
|
-
"wave:doctor": "bash scripts/context7-export-env.sh run pnpm exec wave doctor --json",
|
|
186
|
-
"wave:dry-run": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main --dry-run --no-dashboard",
|
|
187
|
-
"wave:launch": "bash scripts/context7-export-env.sh run pnpm exec wave launch --lane main",
|
|
188
|
-
"wave:launch:managed": "bash scripts/wave-launch.sh",
|
|
189
|
-
"wave:status": "bash scripts/wave-status.sh",
|
|
190
|
-
"wave:watch": "bash scripts/wave-watch.sh --follow",
|
|
191
|
-
"wave:dashboard:current": "bash scripts/wave-dashboard-attach.sh current",
|
|
192
|
-
"wave:dashboard:global": "bash scripts/wave-dashboard-attach.sh global",
|
|
193
|
-
"harness:dev": "pnpm --filter @docx-react-component/react-word-editor-harness dev"
|
|
195
|
+
"pnpm": {
|
|
196
|
+
"onlyBuiltDependencies": [
|
|
197
|
+
"esbuild",
|
|
198
|
+
"sharp"
|
|
199
|
+
],
|
|
200
|
+
"overrides": {
|
|
201
|
+
"react": "19.2.4",
|
|
202
|
+
"react-dom": "19.2.4"
|
|
203
|
+
}
|
|
194
204
|
}
|
|
195
|
-
}
|
|
205
|
+
}
|
package/src/api/public-types.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { PersistedEditorSnapshot as RuntimePersistedEditorSnapshot } from "../core/state/editor-state.ts";
|
|
2
|
+
import type { CanonicalParagraphFormatting, CanonicalRunFormatting } from "../model/canonical-document.ts";
|
|
2
3
|
import type { WordReviewEditorLayoutFacet } from "../runtime/layout/public-facet.ts";
|
|
4
|
+
import type { RenderFrameRect } from "../runtime/render/index.ts";
|
|
5
|
+
import type { ScopeRailPosture } from "../runtime/workflow-rail-segments.ts";
|
|
6
|
+
|
|
7
|
+
export type { CanonicalParagraphFormatting, CanonicalRunFormatting };
|
|
3
8
|
|
|
4
9
|
export type {
|
|
5
10
|
WordReviewEditorLayoutFacet,
|
|
@@ -701,6 +706,8 @@ export type SurfaceInlineSegment =
|
|
|
701
706
|
textColor?: string;
|
|
702
707
|
};
|
|
703
708
|
hyperlinkHref?: string;
|
|
709
|
+
/** Cascaded run formatting for this text segment (docDefaults → paragraph style chain → character style → direct). Added in Task 11. TODO Task 15: wire up from resolveEffectiveRunFormatting in surface-projection. */
|
|
710
|
+
resolvedRunFormatting?: CanonicalRunFormatting;
|
|
704
711
|
}
|
|
705
712
|
| {
|
|
706
713
|
segmentId: string;
|
|
@@ -767,6 +774,13 @@ export interface SurfaceTableCellSnapshot {
|
|
|
767
774
|
borderRight?: string | null;
|
|
768
775
|
borderBottom?: string | null;
|
|
769
776
|
borderLeft?: string | null;
|
|
777
|
+
/**
|
|
778
|
+
* R2a: space-joined CSS class names from the resolved table-style conditional
|
|
779
|
+
* regions (e.g. "band-firstRow band-band1Horz"). Consumers apply these to the
|
|
780
|
+
* cell so theme toggles repaint via CSS vars instead of re-projecting the
|
|
781
|
+
* surface. Direct shading overrides still win over band defaults at render.
|
|
782
|
+
*/
|
|
783
|
+
bandClasses?: string | null;
|
|
770
784
|
content: SurfaceBlockSnapshot[];
|
|
771
785
|
}
|
|
772
786
|
|
|
@@ -777,6 +791,8 @@ export interface SurfaceTableRowSnapshot {
|
|
|
777
791
|
height?: number;
|
|
778
792
|
heightRule?: "auto" | "atLeast" | "exact";
|
|
779
793
|
isHeader?: boolean;
|
|
794
|
+
/** R1b: row carries `w:cantSplit` — pagination keeps the whole table on one page. */
|
|
795
|
+
cantSplit?: boolean;
|
|
780
796
|
}
|
|
781
797
|
|
|
782
798
|
export interface ResolvedNumberingGeometrySnapshot {
|
|
@@ -797,6 +813,8 @@ export interface ResolvedNumberingSnapshot {
|
|
|
797
813
|
isLegalNumbering?: boolean;
|
|
798
814
|
suffix?: "tab" | "space" | "nothing";
|
|
799
815
|
geometry: ResolvedNumberingGeometrySnapshot;
|
|
816
|
+
/** CASCADED marker run formatting: docDefaults → paragraph style chain rPr → paragraph-mark rPr → level rPr. Added in Task 11. The raw level rPr is still available at geometry.markerRunProperties. */
|
|
817
|
+
markerRunProperties?: CanonicalRunFormatting;
|
|
800
818
|
}
|
|
801
819
|
|
|
802
820
|
export type SurfaceBlockSnapshot =
|
|
@@ -813,6 +831,8 @@ export type SurfaceBlockSnapshot =
|
|
|
813
831
|
numberingPrefix?: string;
|
|
814
832
|
numberingSuffix?: "tab" | "space" | "nothing";
|
|
815
833
|
resolvedNumbering?: ResolvedNumberingSnapshot;
|
|
834
|
+
/** Cascaded paragraph formatting: docDefaults → style chain → direct paragraph properties. Added in Task 11. */
|
|
835
|
+
resolvedParagraphFormatting?: CanonicalParagraphFormatting;
|
|
816
836
|
alignment?: "left" | "center" | "right" | "both" | "distribute";
|
|
817
837
|
spacing?: { before?: number; after?: number; line?: number; lineRule?: string };
|
|
818
838
|
contextualSpacing?: boolean;
|
|
@@ -838,6 +858,8 @@ export type SurfaceBlockSnapshot =
|
|
|
838
858
|
gridColumns: number[];
|
|
839
859
|
alignment?: "left" | "center" | "right";
|
|
840
860
|
tblLook?: {
|
|
861
|
+
/** R2d: raw `w:tblLook/@w:val` hex so vendor-extended bits survive round-trip. */
|
|
862
|
+
val?: string;
|
|
841
863
|
firstRow?: boolean;
|
|
842
864
|
lastRow?: boolean;
|
|
843
865
|
firstColumn?: boolean;
|
|
@@ -1288,11 +1310,129 @@ export interface TableOpResult {
|
|
|
1288
1310
|
* read. Read-only geometry queries (columns, rows, render plan, ...)
|
|
1289
1311
|
* land with the layout engine + render kernel integration.
|
|
1290
1312
|
*/
|
|
1313
|
+
/**
|
|
1314
|
+
* Compact summary for one canonical table in the document.
|
|
1315
|
+
*/
|
|
1316
|
+
export interface PublicTableSummary {
|
|
1317
|
+
/** 0-based index across top-level canonical table blocks (walk order). */
|
|
1318
|
+
tableBlockIndex: number;
|
|
1319
|
+
/** The projected surface block id ("table-{index}"). */
|
|
1320
|
+
blockId: string;
|
|
1321
|
+
/** Style chain head, if declared. */
|
|
1322
|
+
styleId: string | null;
|
|
1323
|
+
/** Rows and logical columns (including gridBefore/after padding). */
|
|
1324
|
+
rowCount: number;
|
|
1325
|
+
columnCount: number;
|
|
1326
|
+
/** Raw canonical gridColumns in twips. */
|
|
1327
|
+
gridColumnsTwips: readonly number[];
|
|
1328
|
+
/** Table-level alignment if declared. */
|
|
1329
|
+
alignment: "left" | "center" | "right" | null;
|
|
1330
|
+
/** Whether any row has a vMerge chain crossing it. */
|
|
1331
|
+
hasVerticalMerges: boolean;
|
|
1332
|
+
/** Whether any cell in the table has colspan > 1. */
|
|
1333
|
+
hasHorizontalSpans: boolean;
|
|
1334
|
+
/** 0-based page index where the table's start offset lands, if known. */
|
|
1335
|
+
pageIndex: number | null;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Public shape of the per-page render plan. Sanitized copy of the
|
|
1340
|
+
* internal `TableRenderPlan`: chrome consumers read here, not directly
|
|
1341
|
+
* from the runtime layout module.
|
|
1342
|
+
*/
|
|
1343
|
+
export interface PublicTableRenderPlan {
|
|
1344
|
+
blockId: string;
|
|
1345
|
+
pageIndex: number;
|
|
1346
|
+
columnsTwips: readonly number[];
|
|
1347
|
+
bandClasses: {
|
|
1348
|
+
rows: readonly { rowIndex: number; regions: readonly string[] }[];
|
|
1349
|
+
cells: readonly {
|
|
1350
|
+
rowIndex: number;
|
|
1351
|
+
columnIndex: number;
|
|
1352
|
+
regions: readonly string[];
|
|
1353
|
+
}[];
|
|
1354
|
+
};
|
|
1355
|
+
verticalMerges: readonly {
|
|
1356
|
+
columnIndex: number;
|
|
1357
|
+
startRowIndex: number;
|
|
1358
|
+
endRowIndex: number;
|
|
1359
|
+
columnSpan: number;
|
|
1360
|
+
}[];
|
|
1361
|
+
repeatedHeaderRows: readonly {
|
|
1362
|
+
sourceRowIndex: number;
|
|
1363
|
+
virtualFragmentId: string;
|
|
1364
|
+
}[];
|
|
1365
|
+
columnResizeHandles: readonly {
|
|
1366
|
+
columnIndex: number;
|
|
1367
|
+
originTwips: number;
|
|
1368
|
+
heightTwips: number;
|
|
1369
|
+
}[];
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* Per-row height read derived from pagination measurement + explicit
|
|
1374
|
+
* row height declarations.
|
|
1375
|
+
*/
|
|
1376
|
+
export interface PublicTableRowHeight {
|
|
1377
|
+
/** Measured height in twips from the pagination engine (fallback estimate
|
|
1378
|
+
* when no fragments exist for the row). */
|
|
1379
|
+
measured: number;
|
|
1380
|
+
/** Explicit w:trHeight value when declared. */
|
|
1381
|
+
explicit?: number;
|
|
1382
|
+
/** w:trHeight rule when declared. */
|
|
1383
|
+
rule?: "auto" | "atLeast" | "exact";
|
|
1384
|
+
/** Whether the row is marked as an OOXML header row (tblHeader). */
|
|
1385
|
+
isHeader: boolean;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* Minimal public representation of a table style definition.
|
|
1390
|
+
*/
|
|
1391
|
+
export interface PublicTableStyle {
|
|
1392
|
+
styleId: string;
|
|
1393
|
+
displayName: string;
|
|
1394
|
+
basedOn?: string;
|
|
1395
|
+
isDefault: boolean;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* Events emitted by `ref.tables.subscribe(listener)`. Sourced from the
|
|
1400
|
+
* runtime change stream + layout facet events so chrome surfaces can
|
|
1401
|
+
* refresh their derived reads without polling.
|
|
1402
|
+
*/
|
|
1403
|
+
export type PublicTableEvent =
|
|
1404
|
+
| { kind: "table_structure_changed"; revisionToken: string }
|
|
1405
|
+
| { kind: "table_style_changed"; revisionToken: string }
|
|
1406
|
+
| { kind: "table_render_plan_ready"; revision: number }
|
|
1407
|
+
| { kind: "table_capabilities_changed" };
|
|
1408
|
+
|
|
1291
1409
|
export interface WordReviewEditorTablesFacet {
|
|
1292
1410
|
/** Dispatch a typed table op through the runtime. */
|
|
1293
1411
|
apply(op: TableOp): TableOpResult;
|
|
1294
1412
|
/** Current capability snapshot for the active table selection. */
|
|
1295
1413
|
getCapabilities(): TableStructureContextSnapshot | null;
|
|
1414
|
+
/** List every top-level table in the main document. */
|
|
1415
|
+
getTables(options?: { sectionIndex?: number }): PublicTableSummary[];
|
|
1416
|
+
/** Summary for a single table by tableBlockIndex, or `null` when out of range. */
|
|
1417
|
+
getTable(tableBlockIndex: number): PublicTableSummary | null;
|
|
1418
|
+
/** The table the active selection currently sits in, or `null`. */
|
|
1419
|
+
getTableForSelection(): PublicTableSummary | null;
|
|
1420
|
+
/** Per-page render plan for a table, or `null` when no table / no kernel. */
|
|
1421
|
+
getRenderPlan(
|
|
1422
|
+
tableBlockIndex: number,
|
|
1423
|
+
pageIndex?: number,
|
|
1424
|
+
): PublicTableRenderPlan | null;
|
|
1425
|
+
/** Current gridColumns in twips for the table. */
|
|
1426
|
+
getColumnWidths(tableBlockIndex: number): readonly number[];
|
|
1427
|
+
/** Per-row height reads, combining pagination measurement + explicit declarations. */
|
|
1428
|
+
getRowHeights(tableBlockIndex: number): readonly PublicTableRowHeight[];
|
|
1429
|
+
/** Table-style subset of the document style catalog. */
|
|
1430
|
+
getStyleCatalog(): readonly PublicTableStyle[];
|
|
1431
|
+
/**
|
|
1432
|
+
* Subscribe to table-facet events. Returns an unsubscribe function.
|
|
1433
|
+
* Chrome consumers use this instead of polling `getTables()`.
|
|
1434
|
+
*/
|
|
1435
|
+
subscribe(listener: (event: PublicTableEvent) => void): () => void;
|
|
1296
1436
|
}
|
|
1297
1437
|
|
|
1298
1438
|
export interface PageRegionHitTest {
|
|
@@ -1343,12 +1483,19 @@ export interface EditorViewStateSnapshot {
|
|
|
1343
1483
|
/**
|
|
1344
1484
|
* Role-scoped chrome dimension (spec §6.4 of runtime-rendering-and-chrome-phase.md).
|
|
1345
1485
|
*
|
|
1346
|
-
* - `"editor"` — authoring posture.
|
|
1347
|
-
*
|
|
1348
|
-
*
|
|
1349
|
-
*
|
|
1350
|
-
* - `"
|
|
1351
|
-
*
|
|
1486
|
+
* - `"editor"` — authoring posture. Role action region surfaces the two
|
|
1487
|
+
* review-layer icons: add comment + inline tracked-changes toggle. Left
|
|
1488
|
+
* cluster carries formatting + insert/update actions as usual. Scope
|
|
1489
|
+
* posture is owned by the `"workflow"` role, not editor.
|
|
1490
|
+
* - `"review"` — reviewing posture. Role action region surfaces optional
|
|
1491
|
+
* sidebar-panel shortcuts (`onReviewSidebarTrackedChanges` /
|
|
1492
|
+
* `onReviewSidebarComments`, hidden unless the host provides callbacks),
|
|
1493
|
+
* add comment + inline tracked-changes toggle, review-queue prev/next +
|
|
1494
|
+
* counts + active label, per-item accept/reject, batch accept-all /
|
|
1495
|
+
* reject-all, and markup-mode selector.
|
|
1496
|
+
* - `"workflow"` — workflow-actor posture. Role action region surfaces
|
|
1497
|
+
* the scope posture menu + work-item prev/next, claim, skip, mark
|
|
1498
|
+
* complete, mark blocked, jump to scope.
|
|
1352
1499
|
*/
|
|
1353
1500
|
export type EditorRole = "editor" | "review" | "workflow";
|
|
1354
1501
|
|
|
@@ -1530,6 +1677,102 @@ export interface WorkflowMetadataSnapshot {
|
|
|
1530
1677
|
entries: WorkflowMetadataEntry[];
|
|
1531
1678
|
}
|
|
1532
1679
|
|
|
1680
|
+
// ---------------------------------------------------------------------------
|
|
1681
|
+
// R2 — issue metadata (scope-card-overlay P1)
|
|
1682
|
+
// ---------------------------------------------------------------------------
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* Canonical metadata id for playbook-raised issues. Hosts register a
|
|
1686
|
+
* `WorkflowMetadataDefinition` with this id and push one
|
|
1687
|
+
* `WorkflowMetadataEntry` per issue; the scope card reads the entries
|
|
1688
|
+
* whose `workItemId` or `scopeId` matches a rendered scope.
|
|
1689
|
+
*
|
|
1690
|
+
* Field names line up 1:1 with the CCEP Playbook Engine rule shape so
|
|
1691
|
+
* hosts can push rule output without remapping.
|
|
1692
|
+
*/
|
|
1693
|
+
export const ISSUE_METADATA_ID = "workflow.metadata.issue" as const;
|
|
1694
|
+
|
|
1695
|
+
export type IssueSeverity = "low" | "medium" | "high" | "blocker";
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Playbook rule modes. `guidance` is an advisory preference; `fallback`
|
|
1699
|
+
* is a tiered alternative; `mandatory` cannot be silently removed;
|
|
1700
|
+
* `escalate` requires named-owner sign-off; `block` refuses the edit
|
|
1701
|
+
* outright.
|
|
1702
|
+
*/
|
|
1703
|
+
export type IssueMode =
|
|
1704
|
+
| "guidance"
|
|
1705
|
+
| "fallback"
|
|
1706
|
+
| "mandatory"
|
|
1707
|
+
| "escalate"
|
|
1708
|
+
| "block";
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Named escalation / ownership targets for playbook issues. Hosts can
|
|
1712
|
+
* extend the literal via a widened `string` alias if a tenant needs a
|
|
1713
|
+
* custom owner, but the canonical set matches CCEP's playbook rule
|
|
1714
|
+
* shape.
|
|
1715
|
+
*/
|
|
1716
|
+
export type IssueOwner =
|
|
1717
|
+
| "procurement"
|
|
1718
|
+
| "legal"
|
|
1719
|
+
| "risk"
|
|
1720
|
+
| "finance"
|
|
1721
|
+
| "sustainability";
|
|
1722
|
+
|
|
1723
|
+
export interface IssueMetadataValue {
|
|
1724
|
+
issueId: string;
|
|
1725
|
+
ruleId?: string;
|
|
1726
|
+
topic: string;
|
|
1727
|
+
severity: IssueSeverity;
|
|
1728
|
+
mode: IssueMode;
|
|
1729
|
+
owner?: IssueOwner;
|
|
1730
|
+
escalateTo?: IssueOwner;
|
|
1731
|
+
checklistState: "open" | "acknowledged" | "resolved" | "waived";
|
|
1732
|
+
escalationState?: "none" | "requested" | "approved" | "rejected";
|
|
1733
|
+
suggestionIds?: string[];
|
|
1734
|
+
rationale?: string;
|
|
1735
|
+
title: string;
|
|
1736
|
+
summary?: string;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
/**
|
|
1740
|
+
* Per-scope action the scope card's issue row exposes. Dispatched as a
|
|
1741
|
+
* `scope-issue-action-requested` event — host decides what the action
|
|
1742
|
+
* means and updates the `IssueMetadataValue.checklistState` /
|
|
1743
|
+
* `escalationState` accordingly. The card never mutates runtime state.
|
|
1744
|
+
*/
|
|
1745
|
+
export type ScopeIssueAction =
|
|
1746
|
+
| "resolve"
|
|
1747
|
+
| "waive"
|
|
1748
|
+
| "escalate"
|
|
1749
|
+
| "acknowledge";
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Scope card projection consumed by the chrome overlay's card layer.
|
|
1753
|
+
* Joins a `WorkflowScope` with its attached issue metadata (R2),
|
|
1754
|
+
* suggestion groups (R3 — P2 populates), review-action count (K1 —
|
|
1755
|
+
* P2 populates), and agent-pending flag (K2 — P2 populates).
|
|
1756
|
+
*
|
|
1757
|
+
* `primaryAnchorRect` is the position the card should hover next to —
|
|
1758
|
+
* resolved via `RenderAnchorIndex.bySelection(fromOffset, toOffset)`
|
|
1759
|
+
* on the active page, or `null` when the scope is off-screen.
|
|
1760
|
+
*/
|
|
1761
|
+
export interface ScopeCardModel {
|
|
1762
|
+
scopeId: string;
|
|
1763
|
+
workItemId?: string;
|
|
1764
|
+
posture: ScopeRailPosture;
|
|
1765
|
+
label: string;
|
|
1766
|
+
primaryAnchorRect: RenderFrameRect | null;
|
|
1767
|
+
issue?: IssueMetadataValue;
|
|
1768
|
+
/** R3 suggestion groups attached to the scope. P2 populates; P1 = []. */
|
|
1769
|
+
suggestionGroupIds: readonly string[];
|
|
1770
|
+
/** K1 review-action count for the scope's issue. P2 populates; P1 = 0. */
|
|
1771
|
+
reviewActionCount: number;
|
|
1772
|
+
/** K2 agent-pending flag (overlapping WorkflowCandidateRange source:"ai"). P2 populates; P1 = false. */
|
|
1773
|
+
agentPending: boolean;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1533
1776
|
export interface WorkflowBlockedCommandReason {
|
|
1534
1777
|
code:
|
|
1535
1778
|
| "outside_workflow_scope"
|
|
@@ -2047,6 +2290,30 @@ export type WordReviewEditorEvent =
|
|
|
2047
2290
|
documentId: string;
|
|
2048
2291
|
command: string;
|
|
2049
2292
|
reasons: WorkflowBlockedCommandReason[];
|
|
2293
|
+
}
|
|
2294
|
+
| {
|
|
2295
|
+
/**
|
|
2296
|
+
* Scope card mode selector fired a mode change. Host relays to the
|
|
2297
|
+
* existing `setWorkflowOverlay` path (or the CCEP workflow
|
|
2298
|
+
* endpoint) — the card never mutates runtime state directly.
|
|
2299
|
+
*/
|
|
2300
|
+
type: "scope-mode-change-requested";
|
|
2301
|
+
documentId: string;
|
|
2302
|
+
scopeId: string;
|
|
2303
|
+
mode: WorkflowScopeMode;
|
|
2304
|
+
}
|
|
2305
|
+
| {
|
|
2306
|
+
/**
|
|
2307
|
+
* Scope card issue row fired an action (resolve / waive /
|
|
2308
|
+
* escalate / acknowledge). Host updates the attached
|
|
2309
|
+
* `IssueMetadataValue.checklistState` / `escalationState` and
|
|
2310
|
+
* re-pushes via `setWorkflowMetadataEntries`.
|
|
2311
|
+
*/
|
|
2312
|
+
type: "scope-issue-action-requested";
|
|
2313
|
+
documentId: string;
|
|
2314
|
+
scopeId: string;
|
|
2315
|
+
issueId: string;
|
|
2316
|
+
action: ScopeIssueAction;
|
|
2050
2317
|
};
|
|
2051
2318
|
|
|
2052
2319
|
export interface LoadResult {
|
|
@@ -2204,6 +2471,10 @@ export interface WordReviewEditorRef {
|
|
|
2204
2471
|
getProtectionSnapshot(): ProtectionSnapshot;
|
|
2205
2472
|
setWorkspaceMode(mode: WorkspaceMode): void;
|
|
2206
2473
|
setZoom(level: ZoomLevel): void;
|
|
2474
|
+
/** Switch the per-role top-chrome action set. Drives toolbar + review rail. */
|
|
2475
|
+
setEditorRole(role: EditorRole): void;
|
|
2476
|
+
/** Persist a detach/attach pin for a chrome surface across snapshot rebuilds. */
|
|
2477
|
+
setChromePin(surface: ChromePinSurface, pin: PinState | null): void;
|
|
2207
2478
|
insertSectionBreak(type: SectionBreakType, options?: { afterSectionIndex?: number }): void;
|
|
2208
2479
|
deleteSectionBreak(sectionIndex: number): void;
|
|
2209
2480
|
updateSectionLayout(sectionIndex: number, patch: SectionLayoutPatch): void;
|
|
@@ -2279,7 +2550,20 @@ export type WordReviewEditorChromePreset =
|
|
|
2279
2550
|
| "workflow";
|
|
2280
2551
|
|
|
2281
2552
|
export interface WordReviewEditorChromeOptions {
|
|
2553
|
+
/**
|
|
2554
|
+
* @deprecated The legacy second-strip review queue bar has been collapsed
|
|
2555
|
+
* into the role action region (spec §6.4). Set `role="review"` on the
|
|
2556
|
+
* editor to surface queue Prev/Next + accept/reject inline in the top
|
|
2557
|
+
* toolbar. This flag is retained for type back-compat and has no runtime
|
|
2558
|
+
* effect; it will be removed in a future release.
|
|
2559
|
+
*/
|
|
2282
2560
|
showReviewQueueBar: boolean;
|
|
2561
|
+
/**
|
|
2562
|
+
* @deprecated The "Mark section" button lived on the legacy
|
|
2563
|
+
* `TwReviewQueueBar`. Scope tagging is now the workflow role's posture
|
|
2564
|
+
* menu (`editor-scope-posture-menu`, surfaced via `role="workflow"`).
|
|
2565
|
+
* Retained for type back-compat; will be removed in a future release.
|
|
2566
|
+
*/
|
|
2283
2567
|
showSectionTagAction: boolean;
|
|
2284
2568
|
showReviewRail: boolean;
|
|
2285
2569
|
}
|
|
@@ -2312,6 +2596,21 @@ export interface WordReviewEditorProps {
|
|
|
2312
2596
|
onEvent?: (event: WordReviewEditorEvent) => void;
|
|
2313
2597
|
onWarning?: (warning: EditorWarning) => void;
|
|
2314
2598
|
onError?: (error: EditorError) => void;
|
|
2599
|
+
/**
|
|
2600
|
+
* Optional: opens the host's sidebar to the tracked-changes panel. When
|
|
2601
|
+
* supplied, the review role surfaces an inline "Tracked changes panel"
|
|
2602
|
+
* icon in the top toolbar's role action region (spec §6.4). The button
|
|
2603
|
+
* is hidden when this callback is not provided, so the base runtime
|
|
2604
|
+
* chrome is sidebar-agnostic by default and only the harness / host that
|
|
2605
|
+
* owns a sidebar opts in.
|
|
2606
|
+
*/
|
|
2607
|
+
onReviewSidebarTrackedChanges?: () => void;
|
|
2608
|
+
/**
|
|
2609
|
+
* Optional: opens the host's sidebar to the comments panel. Same
|
|
2610
|
+
* hidden-by-default gating as `onReviewSidebarTrackedChanges` — the
|
|
2611
|
+
* inline "Comments panel" icon appears only when a callback is wired.
|
|
2612
|
+
*/
|
|
2613
|
+
onReviewSidebarComments?: () => void;
|
|
2315
2614
|
}
|
|
2316
2615
|
|
|
2317
2616
|
export interface WordReviewEditorChromeVisibility {
|
|
@@ -358,7 +358,14 @@ export function getTableStructureContext(
|
|
|
358
358
|
? disabledCapability(
|
|
359
359
|
"Selection cuts through a merged cell. Extend the selection to fully enclose the span.",
|
|
360
360
|
)
|
|
361
|
-
:
|
|
361
|
+
: mergeRectCoverage &&
|
|
362
|
+
mergeRectCoverage.fullyCoveredOrigins.some(
|
|
363
|
+
(origin) => origin.columnSpan > 1 || origin.rowSpan > 1,
|
|
364
|
+
)
|
|
365
|
+
? disabledCapability(
|
|
366
|
+
"Selection encloses an already-merged cell. Split it first, then merge.",
|
|
367
|
+
)
|
|
368
|
+
: enabledCapability(),
|
|
362
369
|
splitCell:
|
|
363
370
|
splitWidth === 1 && splitHeight === 1
|
|
364
371
|
? disabledCapability("Select a merged or spanning cell to split it.")
|
|
@@ -803,14 +810,36 @@ function mergeSelectedCells(
|
|
|
803
810
|
selection: TableSelectionDescriptor,
|
|
804
811
|
fallbackSelection: SelectionSnapshot,
|
|
805
812
|
): StructuralMutationResult {
|
|
813
|
+
// R1a: the merge command was previously gated on `!isSimpleTable(table)`,
|
|
814
|
+
// which blocked merging on any table that already carried a single merged
|
|
815
|
+
// span anywhere in the table, even when the user's current selection was
|
|
816
|
+
// over a simple region. The correct gate is whether the rect itself is:
|
|
817
|
+
// (a) clean — every origin it touches is fully enclosed (analyzeRect);
|
|
818
|
+
// (b) internally simple — no pre-existing merged origin lives inside
|
|
819
|
+
// the rect. findCellAtColumn in the splice body below assumes cells
|
|
820
|
+
// at logical column C correspond 1:1 with row.cells entries; that
|
|
821
|
+
// assumption holds iff the rect's internal cells are all gridSpan=1
|
|
822
|
+
// and verticalMerge=undefined.
|
|
823
|
+
// A rect that satisfies both is safe to merge even on a table that has
|
|
824
|
+
// merged spans elsewhere (the common imported-agreement case).
|
|
806
825
|
if (
|
|
807
|
-
!isSimpleTable(table) ||
|
|
808
826
|
selection.selectionKind !== "cell" ||
|
|
809
827
|
selection.rect.bottom - selection.rect.top < 1 ||
|
|
810
828
|
selection.rect.right - selection.rect.left < 1
|
|
811
829
|
) {
|
|
812
830
|
return createNoopStructuralMutation(document, fallbackSelection);
|
|
813
831
|
}
|
|
832
|
+
const grid = buildLogicalGrid(table);
|
|
833
|
+
const coverage = analyzeRect(grid, selection.rect);
|
|
834
|
+
if (!coverage.clean) {
|
|
835
|
+
return createNoopStructuralMutation(document, fallbackSelection);
|
|
836
|
+
}
|
|
837
|
+
const rectInternallySimple = coverage.fullyCoveredOrigins.every(
|
|
838
|
+
(origin) => origin.columnSpan === 1 && origin.rowSpan === 1,
|
|
839
|
+
);
|
|
840
|
+
if (!rectInternallySimple) {
|
|
841
|
+
return createNoopStructuralMutation(document, fallbackSelection);
|
|
842
|
+
}
|
|
814
843
|
|
|
815
844
|
const height = selection.rect.bottom - selection.rect.top;
|
|
816
845
|
const width = selection.rect.right - selection.rect.left;
|