@beyondwork/docx-react-component 1.0.106 → 1.0.109
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 +19 -5
- package/src/api/geometry-overlay-rects.ts +5 -0
- package/src/api/package-version.ts +1 -1
- package/src/api/page-anchor-id.ts +5 -0
- package/src/api/public-types.ts +16 -9
- package/src/api/table-node-specs.ts +6 -0
- package/src/api/v3/_create.ts +2 -1
- package/src/api/v3/_page-anchor-id.ts +52 -0
- package/src/api/v3/_runtime-handle.ts +92 -1
- package/src/api/v3/ai/_audit-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +38 -0
- package/src/api/v3/ai/attach.ts +7 -2
- package/src/api/v3/ai/replacement.ts +101 -18
- package/src/api/v3/ai/resolve.ts +2 -2
- package/src/api/v3/ai/review.ts +177 -3
- package/src/api/v3/index.ts +1 -0
- package/src/api/v3/runtime/collab.ts +462 -0
- package/src/api/v3/runtime/document.ts +503 -20
- package/src/api/v3/runtime/geometry.ts +97 -0
- package/src/api/v3/runtime/layout.ts +744 -0
- package/src/api/v3/runtime/perf-probe.ts +14 -0
- package/src/api/v3/runtime/viewport.ts +9 -8
- package/src/api/v3/ui/_types.ts +149 -55
- package/src/api/v3/ui/chrome-preset-model.ts +5 -5
- package/src/api/v3/ui/debug.ts +115 -2
- package/src/api/v3/ui/index.ts +13 -0
- package/src/api/v3/ui/overlays.ts +0 -8
- package/src/api/v3/ui/surface.ts +56 -0
- package/src/api/v3/ui/viewport.ts +22 -9
- package/src/core/commands/image-commands.ts +1 -0
- package/src/core/commands/index.ts +6 -0
- package/src/core/schema/text-schema.ts +43 -5
- package/src/core/selection/mapping.ts +8 -1
- package/src/core/selection/review-anchors.ts +5 -1
- package/src/core/state/text-transaction.ts +8 -2
- package/src/io/export/serialize-revisions.ts +149 -1
- package/src/io/normalize/normalize-text.ts +6 -0
- package/src/io/ooxml/parse-bookmark-references.ts +55 -0
- package/src/io/ooxml/parse-fields.ts +24 -2
- package/src/io/ooxml/parse-headers-footers.ts +38 -5
- package/src/io/ooxml/parse-main-document.ts +153 -9
- package/src/io/ooxml/parse-numbering.ts +20 -0
- package/src/io/ooxml/parse-revisions.ts +19 -8
- package/src/io/opc/package-reader.ts +98 -8
- package/src/model/anchor.ts +4 -3
- package/src/model/canonical-document.ts +220 -2
- package/src/model/canonical-hash.ts +221 -0
- package/src/model/canonical-layout-inputs.ts +245 -6
- package/src/model/layout/index.ts +1 -0
- package/src/model/layout/page-graph-types.ts +118 -1
- package/src/model/review/revision-types.ts +14 -3
- package/src/preservation/store.ts +20 -4
- package/src/review/README.md +1 -1
- package/src/review/store/revision-actions.ts +14 -2
- package/src/runtime/collab/event-types.ts +67 -1
- package/src/runtime/collab/runtime-collab-sync.ts +177 -5
- package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
- package/src/runtime/document-heading-outline.ts +147 -0
- package/src/runtime/document-navigation.ts +8 -243
- package/src/runtime/document-runtime.ts +240 -97
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
- package/src/runtime/formatting/layout-inputs.ts +38 -5
- package/src/runtime/formatting/numbering/geometry.ts +28 -2
- package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
- package/src/runtime/geometry/caret-geometry.ts +5 -6
- package/src/runtime/geometry/geometry-facet.ts +60 -10
- package/src/runtime/geometry/geometry-index.ts +591 -20
- package/src/runtime/geometry/geometry-types.ts +59 -0
- package/src/runtime/geometry/hit-test.ts +11 -1
- package/src/runtime/geometry/overlay-rects.ts +5 -3
- package/src/runtime/geometry/project-anchors.ts +1 -1
- package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
- package/src/runtime/layout/index.ts +6 -0
- package/src/runtime/layout/layout-engine-instance.ts +6 -1
- package/src/runtime/layout/layout-engine-version.ts +181 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +21 -4
- package/src/runtime/layout/paginated-layout-engine.ts +139 -15
- package/src/runtime/layout/project-block-fragments.ts +265 -7
- package/src/runtime/layout/public-facet.ts +78 -24
- package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
- package/src/runtime/layout/table-row-split.ts +92 -35
- package/src/runtime/prerender/cache-envelope.ts +2 -2
- package/src/runtime/prerender/cache-key.ts +5 -4
- package/src/runtime/prerender/customxml-cache.ts +0 -1
- package/src/runtime/render/render-kernel.ts +1 -1
- package/src/runtime/revision-runtime.ts +112 -10
- package/src/runtime/scopes/_scope-dependencies.ts +1 -0
- package/src/runtime/scopes/action-validation.ts +22 -2
- package/src/runtime/scopes/capabilities.ts +316 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
- package/src/runtime/scopes/compiler-service.ts +108 -4
- package/src/runtime/scopes/content-control-evidence.ts +79 -0
- package/src/runtime/scopes/create-issue.ts +5 -5
- package/src/runtime/scopes/evidence.ts +91 -0
- package/src/runtime/scopes/formatting/apply.ts +2 -0
- package/src/runtime/scopes/geometry-evidence.ts +130 -0
- package/src/runtime/scopes/index.ts +54 -0
- package/src/runtime/scopes/issue-lifecycle.ts +224 -0
- package/src/runtime/scopes/layout-evidence.ts +374 -0
- package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
- package/src/runtime/scopes/preservation-boundary.ts +7 -1
- package/src/runtime/scopes/replacement/apply.ts +97 -34
- package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
- package/src/runtime/scopes/semantic-scope-types.ts +242 -3
- package/src/runtime/scopes/visualization.ts +28 -0
- package/src/runtime/surface-projection.ts +44 -5
- package/src/runtime/telemetry/perf-probe.ts +216 -0
- package/src/runtime/virtualized-rendering.ts +36 -1
- package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
- package/src/runtime/workflow/coordinator.ts +39 -11
- package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
- package/src/runtime/workflow/index.ts +3 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +168 -10
- package/src/runtime/workflow/overlay-store.ts +2 -2
- package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
- package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
- package/src/session/_sync-legacy.ts +17 -27
- package/src/session/import/loader.ts +6 -4
- package/src/session/import/source-package-evidence.ts +186 -2
- package/src/session/index.ts +5 -6
- package/src/session/session.ts +30 -56
- package/src/session/types.ts +8 -13
- package/src/shell/session-bootstrap.ts +155 -81
- package/src/ui/WordReviewEditor.tsx +520 -12
- package/src/ui/editor-shell-view.tsx +14 -4
- package/src/ui/editor-surface-controller.tsx +5 -3
- package/src/ui/headless/selection-tool-resolver.ts +1 -2
- package/src/ui/presence-overlay-lane.ts +0 -1
- package/src/ui/ui-controller-factory.ts +7 -0
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
- package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
- package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
- package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
- package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
- package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
- package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
- package/src/ui-tailwind/debug/README.md +4 -1
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
- package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
- package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
- package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
- package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
- package/src/ui-tailwind/review-workspace/types.ts +26 -12
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
- package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
|
@@ -88,10 +88,23 @@ export interface RuntimeResolvedRegions {
|
|
|
88
88
|
export interface RuntimeExclusionZone {
|
|
89
89
|
rectTwips: RuntimeTwipsRect;
|
|
90
90
|
objectId: string;
|
|
91
|
-
wrapMode:
|
|
91
|
+
wrapMode: RuntimeAnchorWrapMode;
|
|
92
92
|
layoutInCell?: boolean;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Project-canonical wrap mode union. Mirrors `AnchorGeometry["wrapMode"]` and
|
|
97
|
+
* `FloatingImageProperties["wrap"]` in `src/model/canonical-document.ts`.
|
|
98
|
+
* `behindText` / `inFrontOfText` are *not* wrap modes here — they live as the
|
|
99
|
+
* `behindDoc?: boolean` flag on the canonical floating shape.
|
|
100
|
+
*/
|
|
101
|
+
export type RuntimeAnchorWrapMode =
|
|
102
|
+
| "none"
|
|
103
|
+
| "square"
|
|
104
|
+
| "tight"
|
|
105
|
+
| "through"
|
|
106
|
+
| "topAndBottom";
|
|
107
|
+
|
|
95
108
|
export type RuntimeLayoutDivergenceKind =
|
|
96
109
|
| "frame-collision"
|
|
97
110
|
| "intentional-overflow"
|
|
@@ -116,6 +129,14 @@ export interface RuntimePageFrame {
|
|
|
116
129
|
pageIndex: number;
|
|
117
130
|
sectionIndex: number;
|
|
118
131
|
displayPageNumber: number;
|
|
132
|
+
/**
|
|
133
|
+
* PE2 v73 — explicit page-frame realization state for downstream runtime
|
|
134
|
+
* readers. L04-built frames are "complete" when every emitted region has a
|
|
135
|
+
* twips rect, "partial" when the graph exists but any region rect is absent,
|
|
136
|
+
* and "absent" is reserved for adapters describing a page without an L04
|
|
137
|
+
* frame.
|
|
138
|
+
*/
|
|
139
|
+
completeness: RuntimePageFrameCompleteness;
|
|
119
140
|
physicalBoundsTwips: RuntimeTwipsRect;
|
|
120
141
|
regions: RuntimeResolvedRegions;
|
|
121
142
|
pageLocalStories: RuntimePageLocalStoryInstance[];
|
|
@@ -123,6 +144,8 @@ export interface RuntimePageFrame {
|
|
|
123
144
|
signature: string;
|
|
124
145
|
}
|
|
125
146
|
|
|
147
|
+
export type RuntimePageFrameCompleteness = "complete" | "partial" | "absent";
|
|
148
|
+
|
|
126
149
|
export interface RuntimePageLocalStoryInstance {
|
|
127
150
|
instanceId: string;
|
|
128
151
|
storyKey: string;
|
|
@@ -156,11 +179,18 @@ export interface RuntimeStoryAnchoredObject {
|
|
|
156
179
|
| "ole-embed"
|
|
157
180
|
| "opaque-inline";
|
|
158
181
|
display: "inline" | "floating" | "unknown";
|
|
182
|
+
/**
|
|
183
|
+
* Source wrap mode for floating objects when L04 can read it. This stays as
|
|
184
|
+
* source/layout truth in twips-space; exact projected object bboxes remain
|
|
185
|
+
* a Layer 05 geometry concern.
|
|
186
|
+
*/
|
|
187
|
+
wrapMode?: RuntimeAnchorWrapMode;
|
|
159
188
|
extentTwips?: {
|
|
160
189
|
widthTwips: number;
|
|
161
190
|
heightTwips: number;
|
|
162
191
|
};
|
|
163
192
|
relationshipIds?: readonly string[];
|
|
193
|
+
mediaIds?: readonly string[];
|
|
164
194
|
preserveOnly: boolean;
|
|
165
195
|
divergenceIds: string[];
|
|
166
196
|
}
|
|
@@ -194,9 +224,16 @@ export interface RuntimeTableContinuationCursor {
|
|
|
194
224
|
continuesFromPreviousPage: boolean;
|
|
195
225
|
continuesToNextPage: boolean;
|
|
196
226
|
repeatedHeaderRowIndexes: readonly number[];
|
|
227
|
+
splitRowCarry?: readonly RuntimeTableSplitRowCarry[];
|
|
197
228
|
verticalMergeCarry: readonly RuntimeTableVerticalMergeCarry[];
|
|
198
229
|
}
|
|
199
230
|
|
|
231
|
+
export interface RuntimeTableSplitRowCarry {
|
|
232
|
+
rowIndex: number;
|
|
233
|
+
continuesFromPreviousPage: boolean;
|
|
234
|
+
continuesToNextPage: boolean;
|
|
235
|
+
}
|
|
236
|
+
|
|
200
237
|
export interface RuntimeTableVerticalMergeCarry {
|
|
201
238
|
columnIndex: number;
|
|
202
239
|
restartRowIndex: number;
|
|
@@ -221,6 +258,86 @@ export interface RuntimeFragmentLayoutObject {
|
|
|
221
258
|
widthTwips?: number;
|
|
222
259
|
};
|
|
223
260
|
fieldFamilies?: readonly string[];
|
|
261
|
+
/**
|
|
262
|
+
* Field-region identities whose visible result participates in this
|
|
263
|
+
* page-local fragment. L04 owns the fragment/page membership; L01/L02 own the
|
|
264
|
+
* source and canonical ids copied here for Debug/L05 joins.
|
|
265
|
+
*/
|
|
266
|
+
fieldRegions?: readonly RuntimeFieldRegionLayoutFacts[];
|
|
267
|
+
/**
|
|
268
|
+
* KI-005 — source numbering facts for geometry/debug consumers.
|
|
269
|
+
* L04 owns the page-local fragment and measured extent; this payload carries
|
|
270
|
+
* the resolved marker/text-column facts that L05 can project without
|
|
271
|
+
* re-reading the surface snapshot, DOM, or numbering resolver.
|
|
272
|
+
*/
|
|
273
|
+
numbering?: RuntimeNumberingLayoutFacts;
|
|
274
|
+
/**
|
|
275
|
+
* Joinable numbering facts for every numbered paragraph represented by this
|
|
276
|
+
* fragment, including paragraphs nested inside tables and block content
|
|
277
|
+
* controls. `numbering` remains the direct-paragraph compatibility field;
|
|
278
|
+
* consumers that need runtime fragment/page joins should prefer this list.
|
|
279
|
+
*/
|
|
280
|
+
numberingRows?: readonly RuntimeNumberingLayoutFacts[];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export interface RuntimeFieldRegionLayoutFacts {
|
|
284
|
+
fieldRegionId: string;
|
|
285
|
+
regionId: string;
|
|
286
|
+
canonicalFieldId: string;
|
|
287
|
+
regionKind: "field" | "toc-region" | "hyperlink-field";
|
|
288
|
+
storyKey: string;
|
|
289
|
+
fieldIndex: number;
|
|
290
|
+
paragraphIndex: number;
|
|
291
|
+
fieldFamily: string;
|
|
292
|
+
refreshStatus: string;
|
|
293
|
+
sourceRef?: {
|
|
294
|
+
sourceId: string;
|
|
295
|
+
partPath?: string;
|
|
296
|
+
storyKind?: string;
|
|
297
|
+
element?: string;
|
|
298
|
+
ordinal?: number;
|
|
299
|
+
startOffset?: number;
|
|
300
|
+
endOffset?: number;
|
|
301
|
+
};
|
|
302
|
+
fieldEvidence?: {
|
|
303
|
+
sourceRef?: RuntimeFieldRegionLayoutFacts["sourceRef"];
|
|
304
|
+
instructionHash?: string;
|
|
305
|
+
resultHash?: string;
|
|
306
|
+
locked?: boolean;
|
|
307
|
+
dirty?: boolean;
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface RuntimeNumberingLayoutFacts {
|
|
312
|
+
numberingLayoutId?: string;
|
|
313
|
+
numberingKey?: string;
|
|
314
|
+
sourceBlockPath?: string;
|
|
315
|
+
sourceBlockId?: string;
|
|
316
|
+
paragraphIndex?: number;
|
|
317
|
+
numberingInstanceId?: string;
|
|
318
|
+
level?: number;
|
|
319
|
+
format?: string;
|
|
320
|
+
markerText?: string;
|
|
321
|
+
markerSuffix?: "tab" | "space" | "nothing";
|
|
322
|
+
markerJustification?: "left" | "center" | "right" | "both" | "distribute";
|
|
323
|
+
markerLane?: {
|
|
324
|
+
startTwips: number;
|
|
325
|
+
widthTwips: number;
|
|
326
|
+
textStartTwips: number;
|
|
327
|
+
};
|
|
328
|
+
textColumn?: {
|
|
329
|
+
startTwips: number;
|
|
330
|
+
rightTwips?: number;
|
|
331
|
+
firstLineTwips?: number;
|
|
332
|
+
hangingTwips?: number;
|
|
333
|
+
};
|
|
334
|
+
tabStops?: readonly RuntimeNumberingTabStopFacts[];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export interface RuntimeNumberingTabStopFacts {
|
|
338
|
+
positionTwips: number;
|
|
339
|
+
align?: string;
|
|
340
|
+
leader?: string;
|
|
224
341
|
}
|
|
225
342
|
|
|
226
343
|
// ---------------------------------------------------------------------------
|
|
@@ -29,7 +29,13 @@ export type RevisionActionability = "actionable" | "preserve-only";
|
|
|
29
29
|
export type RevisionAnchorState = "active" | "detached";
|
|
30
30
|
|
|
31
31
|
export interface PropertyChangeData {
|
|
32
|
-
xmlTag:
|
|
32
|
+
xmlTag:
|
|
33
|
+
| "pPrChange"
|
|
34
|
+
| "sectPrChange"
|
|
35
|
+
| "tblPrChange"
|
|
36
|
+
| "trPrChange"
|
|
37
|
+
| "tcPrChange"
|
|
38
|
+
| "rPrChange";
|
|
33
39
|
beforeXml: string;
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -152,8 +158,9 @@ export function getRevisionActionability(
|
|
|
152
158
|
// preserve-only (see revision-store.ts), which covers both `kind: "move"`
|
|
153
159
|
// and `kind: "formatting"`. The overrides below flip the default back to
|
|
154
160
|
// actionable for the specific shapes that Lane 7b promoted: cellIns
|
|
155
|
-
// structural-table revisions,
|
|
156
|
-
//
|
|
161
|
+
// structural-table revisions, linked container-form move revisions, and
|
|
162
|
+
// linked range-marker move revisions. Do not add a
|
|
163
|
+
// `&& !preserveOnlyReason` guard here — that would re-lock promoted shapes
|
|
157
164
|
// against the injected default and regress 7b.
|
|
158
165
|
if (
|
|
159
166
|
typeof revision !== "string" &&
|
|
@@ -167,6 +174,10 @@ export function getRevisionActionability(
|
|
|
167
174
|
if (
|
|
168
175
|
typeof revision !== "string" &&
|
|
169
176
|
revision.kind === "move" &&
|
|
177
|
+
(revision.metadata.originalRevisionType === "moveFrom" ||
|
|
178
|
+
revision.metadata.originalRevisionType === "moveTo" ||
|
|
179
|
+
revision.metadata.originalRevisionType === "moveFromRangeStart" ||
|
|
180
|
+
revision.metadata.originalRevisionType === "moveToRangeStart") &&
|
|
170
181
|
typeof revision.metadata.moveData?.linkedRevisionId === "string" &&
|
|
171
182
|
revision.metadata.moveData.linkedRevisionId.length > 0
|
|
172
183
|
) {
|
|
@@ -22,6 +22,14 @@ export interface OpaqueFragmentDescriptor {
|
|
|
22
22
|
detail: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface StructuredWrapperDescriptorOptions {
|
|
26
|
+
/**
|
|
27
|
+
* TOC doc-part SDTs are preserve-only workflow objects, but the surface
|
|
28
|
+
* projection must keep their cached result paragraphs visible.
|
|
29
|
+
*/
|
|
30
|
+
projectVisibleTocContentControls?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
25
33
|
const BLOCKED_IMPORT_FEATURE_KEYS = new Set<OpaqueFragmentDescriptor["featureKey"]>([
|
|
26
34
|
"alt-chunk",
|
|
27
35
|
"custom-xml",
|
|
@@ -160,13 +168,21 @@ export function describeOpaqueFragment(
|
|
|
160
168
|
|
|
161
169
|
export function describeStructuredWrapperBlock(
|
|
162
170
|
block: BlockNode,
|
|
171
|
+
options: StructuredWrapperDescriptorOptions = {},
|
|
163
172
|
): OpaqueFragmentDescriptor | null {
|
|
164
173
|
if (block.type === "sdt") {
|
|
165
174
|
if (isTocContentControl(block)) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
if (options.projectVisibleTocContentControls) {
|
|
176
|
+
// CCEP SOW templates wrap the visible cached TOC result paragraphs in
|
|
177
|
+
// a docPartObj SDT. Collapsing that wrapper hides visible content, so
|
|
178
|
+
// callers that project the editing surface keep the wrapper recursive.
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
featureKey: "content-controls",
|
|
183
|
+
label: "TOC content control",
|
|
184
|
+
detail: "Table of Contents doc-part content control remains package-backed and read-only.",
|
|
185
|
+
};
|
|
170
186
|
}
|
|
171
187
|
if (block.properties.sdtType === "docPartObj") {
|
|
172
188
|
// coord-02 §11 P0 — a Template content control that wraps a
|
package/src/review/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Frozen Wave 1 contract:
|
|
|
9
9
|
- all review mutations flow through runtime commands and transactions
|
|
10
10
|
- detached comments or revisions remain addressable with reason metadata; they are not silently dropped
|
|
11
11
|
- v1 authoring is limited to single-paragraph comments, thread replies, tracked insertions, tracked deletions, and accept/reject flows
|
|
12
|
-
- preserve-only review structures such as multi-paragraph editable comment ranges,
|
|
12
|
+
- preserve-only review structures such as multi-paragraph editable comment ranges, unlinked move revisions, and unsupported structural list/table revisions stay locked and warning-backed
|
|
13
13
|
|
|
14
14
|
Key subdirectories:
|
|
15
15
|
|
|
@@ -111,6 +111,7 @@ export function applyRevisionAction(
|
|
|
111
111
|
|
|
112
112
|
if (
|
|
113
113
|
revision.kind === "move" &&
|
|
114
|
+
isLinkedMoveActionRevision(revision) &&
|
|
114
115
|
typeof revision.metadata.moveData?.linkedRevisionId === "string" &&
|
|
115
116
|
revision.metadata.moveData.linkedRevisionId.length > 0
|
|
116
117
|
) {
|
|
@@ -212,7 +213,9 @@ export function applyRevisionAction(
|
|
|
212
213
|
if (
|
|
213
214
|
slice.some(
|
|
214
215
|
(unit) =>
|
|
215
|
-
unit.kind === "paragraph_break" ||
|
|
216
|
+
unit.kind === "paragraph_break" ||
|
|
217
|
+
unit.kind === "opaque_block" ||
|
|
218
|
+
unit.kind === "structural_block",
|
|
216
219
|
)
|
|
217
220
|
) {
|
|
218
221
|
return skippedResult(
|
|
@@ -312,6 +315,15 @@ function applyPairedMoveAction(
|
|
|
312
315
|
};
|
|
313
316
|
}
|
|
314
317
|
|
|
318
|
+
function isLinkedMoveActionRevision(revision: RevisionRecord): boolean {
|
|
319
|
+
return (
|
|
320
|
+
revision.metadata.originalRevisionType === "moveFrom" ||
|
|
321
|
+
revision.metadata.originalRevisionType === "moveTo" ||
|
|
322
|
+
revision.metadata.originalRevisionType === "moveFromRangeStart" ||
|
|
323
|
+
revision.metadata.originalRevisionType === "moveToRangeStart"
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
315
327
|
function applyRowStructuralAction(
|
|
316
328
|
options: ApplyRevisionActionOptions,
|
|
317
329
|
revision: RevisionRecord,
|
|
@@ -862,7 +874,7 @@ function mapParagraphRanges(
|
|
|
862
874
|
continue;
|
|
863
875
|
}
|
|
864
876
|
|
|
865
|
-
if (unit.kind === "opaque_block") {
|
|
877
|
+
if (unit.kind === "opaque_block" || unit.kind === "structural_block") {
|
|
866
878
|
if (hasPendingParagraph) {
|
|
867
879
|
paragraphs.push({ start, end: index });
|
|
868
880
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EditorCommand } from "../../core/commands/index.ts";
|
|
1
|
+
import type { CommandOrigin, EditorCommand } from "../../core/commands/index.ts";
|
|
2
2
|
import type { SelectionSnapshot } from "../../core/state/editor-state.ts";
|
|
3
3
|
import type { EditorStoryTarget } from "../../api/public-types.ts";
|
|
4
4
|
|
|
@@ -63,6 +63,8 @@ export interface CommandEvent {
|
|
|
63
63
|
authorId: string;
|
|
64
64
|
/** ISO-8601 UTC timestamp at origin. */
|
|
65
65
|
timestamp: string;
|
|
66
|
+
/** Normalized origin classification for remote replay/audit consumers. */
|
|
67
|
+
origin: CommandEventOriginMetadata;
|
|
66
68
|
/** The exact `EditorCommand` that produced this delta. */
|
|
67
69
|
command: EditorCommand;
|
|
68
70
|
/** Context required for deterministic replay on remote clients. */
|
|
@@ -86,6 +88,23 @@ export interface CommandEvent {
|
|
|
86
88
|
};
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
export type CommandEventOriginKind =
|
|
92
|
+
| "local-typing"
|
|
93
|
+
| "local-command"
|
|
94
|
+
| "runtime"
|
|
95
|
+
| "agent"
|
|
96
|
+
| "import"
|
|
97
|
+
| "field-refresh"
|
|
98
|
+
| "unknown";
|
|
99
|
+
|
|
100
|
+
export type CommandEventOriginSource = CommandOrigin["source"] | "agent" | "import" | "field_refresh" | "unspecified";
|
|
101
|
+
|
|
102
|
+
export interface CommandEventOriginMetadata {
|
|
103
|
+
readonly channel: "runtime-command-event";
|
|
104
|
+
readonly source: CommandEventOriginSource;
|
|
105
|
+
readonly kind: CommandEventOriginKind;
|
|
106
|
+
}
|
|
107
|
+
|
|
89
108
|
/**
|
|
90
109
|
* Command types that are broadcast to all collaborators.
|
|
91
110
|
*
|
|
@@ -184,6 +203,52 @@ export function isBroadcastCommand(command: EditorCommand): boolean {
|
|
|
184
203
|
return BROADCAST_COMMAND_TYPES.has(command.type);
|
|
185
204
|
}
|
|
186
205
|
|
|
206
|
+
const TYPING_COMMAND_TYPES: ReadonlySet<EditorCommand["type"]> = new Set<EditorCommand["type"]>([
|
|
207
|
+
"text.insert",
|
|
208
|
+
"text.delete-backward",
|
|
209
|
+
"text.delete-forward",
|
|
210
|
+
"text.insert-tab",
|
|
211
|
+
"text.insert-hard-break",
|
|
212
|
+
"text.outdent-tab",
|
|
213
|
+
"paragraph.split",
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
export function classifyCommandEventOriginSource(
|
|
217
|
+
source: CommandEventOriginSource,
|
|
218
|
+
commandType: EditorCommand["type"],
|
|
219
|
+
): CommandEventOriginKind {
|
|
220
|
+
if (source === "agent") return "agent";
|
|
221
|
+
if (source === "import") return "import";
|
|
222
|
+
if (source === "field_refresh") return "field-refresh";
|
|
223
|
+
if (source === "runtime") return "runtime";
|
|
224
|
+
if (source === "keyboard" && TYPING_COMMAND_TYPES.has(commandType)) return "local-typing";
|
|
225
|
+
if (
|
|
226
|
+
source === "keyboard" ||
|
|
227
|
+
source === "toolbar" ||
|
|
228
|
+
source === "context_menu" ||
|
|
229
|
+
source === "comment_panel" ||
|
|
230
|
+
source === "review_panel" ||
|
|
231
|
+
source === "api"
|
|
232
|
+
) {
|
|
233
|
+
return "local-command";
|
|
234
|
+
}
|
|
235
|
+
if (TYPING_COMMAND_TYPES.has(commandType)) return "local-typing";
|
|
236
|
+
return "unknown";
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function classifyCommandEventOrigin(command: EditorCommand): CommandEventOriginKind {
|
|
240
|
+
return classifyCommandEventOriginSource(command.origin?.source ?? "unspecified", command.type);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function commandEventOriginMetadata(command: EditorCommand): CommandEventOriginMetadata {
|
|
244
|
+
const source = command.origin?.source ?? "unspecified";
|
|
245
|
+
return {
|
|
246
|
+
channel: "runtime-command-event",
|
|
247
|
+
source,
|
|
248
|
+
kind: classifyCommandEventOriginSource(source, command.type),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
187
252
|
export interface CreateCommandEventInput {
|
|
188
253
|
command: EditorCommand;
|
|
189
254
|
originClientId: number;
|
|
@@ -203,6 +268,7 @@ export function createCommandEvent(input: CreateCommandEventInput): CommandEvent
|
|
|
203
268
|
originClientId: input.originClientId,
|
|
204
269
|
authorId: input.authorId,
|
|
205
270
|
timestamp: input.timestamp,
|
|
271
|
+
origin: commandEventOriginMetadata(input.command),
|
|
206
272
|
command: input.command,
|
|
207
273
|
context: {
|
|
208
274
|
documentMode: input.context?.documentMode,
|
|
@@ -11,6 +11,11 @@ import type {
|
|
|
11
11
|
BlockNode,
|
|
12
12
|
MutableCanonicalDocument,
|
|
13
13
|
} from "../../model/canonical-document.ts";
|
|
14
|
+
import type {
|
|
15
|
+
Assoc,
|
|
16
|
+
BoundaryAssoc,
|
|
17
|
+
InternalEditorAnchorProjection,
|
|
18
|
+
} from "../../model/anchor.ts";
|
|
14
19
|
import type {
|
|
15
20
|
CommandAppliedMeta,
|
|
16
21
|
DocumentRuntime,
|
|
@@ -19,6 +24,7 @@ import type {
|
|
|
19
24
|
} from "../document-runtime.ts";
|
|
20
25
|
import {
|
|
21
26
|
createCommandEvent,
|
|
27
|
+
commandEventOriginMetadata,
|
|
22
28
|
isBroadcastCommand,
|
|
23
29
|
isLocalOnlyCommand,
|
|
24
30
|
COMMAND_EVENT_SCHEMA_VERSION,
|
|
@@ -784,11 +790,58 @@ function asCommandEvent(value: unknown): CommandEvent | null {
|
|
|
784
790
|
originClientId: value.originClientId,
|
|
785
791
|
authorId: value.authorId,
|
|
786
792
|
timestamp,
|
|
793
|
+
origin: asCommandEventOrigin(value.origin, command),
|
|
787
794
|
command,
|
|
788
795
|
context,
|
|
789
796
|
};
|
|
790
797
|
}
|
|
791
798
|
|
|
799
|
+
function asCommandEventOrigin(value: unknown, command: EditorCommand): CommandEvent["origin"] {
|
|
800
|
+
if (!isRecord(value)) {
|
|
801
|
+
return commandEventOriginMetadata(command);
|
|
802
|
+
}
|
|
803
|
+
if (
|
|
804
|
+
value.channel === "runtime-command-event" &&
|
|
805
|
+
isCommandEventOriginSource(value.source) &&
|
|
806
|
+
isCommandEventOriginKind(value.kind)
|
|
807
|
+
) {
|
|
808
|
+
return {
|
|
809
|
+
channel: "runtime-command-event",
|
|
810
|
+
source: value.source,
|
|
811
|
+
kind: value.kind,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
return commandEventOriginMetadata(command);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function isCommandEventOriginSource(value: unknown): value is CommandEvent["origin"]["source"] {
|
|
818
|
+
return (
|
|
819
|
+
value === "keyboard" ||
|
|
820
|
+
value === "toolbar" ||
|
|
821
|
+
value === "context_menu" ||
|
|
822
|
+
value === "comment_panel" ||
|
|
823
|
+
value === "review_panel" ||
|
|
824
|
+
value === "api" ||
|
|
825
|
+
value === "runtime" ||
|
|
826
|
+
value === "agent" ||
|
|
827
|
+
value === "import" ||
|
|
828
|
+
value === "field_refresh" ||
|
|
829
|
+
value === "unspecified"
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function isCommandEventOriginKind(value: unknown): value is CommandEvent["origin"]["kind"] {
|
|
834
|
+
return (
|
|
835
|
+
value === "local-typing" ||
|
|
836
|
+
value === "local-command" ||
|
|
837
|
+
value === "runtime" ||
|
|
838
|
+
value === "agent" ||
|
|
839
|
+
value === "import" ||
|
|
840
|
+
value === "field-refresh" ||
|
|
841
|
+
value === "unknown"
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
|
|
792
845
|
function normalizeTimestamp(value: unknown): string | null {
|
|
793
846
|
if (typeof value === "string" && value.length > 0) {
|
|
794
847
|
return value;
|
|
@@ -824,16 +877,131 @@ function asCommandEventContext(value: unknown): CommandEvent["context"] | null {
|
|
|
824
877
|
|
|
825
878
|
function asSelectionSnapshot(value: unknown): CommandEvent["context"]["preSelection"] {
|
|
826
879
|
if (!isRecord(value)) return undefined;
|
|
827
|
-
if (
|
|
880
|
+
if (!isFiniteNumber(value.anchor) || !isFiniteNumber(value.head)) return undefined;
|
|
828
881
|
if (typeof value.isCollapsed !== "boolean") return undefined;
|
|
829
|
-
|
|
830
|
-
|
|
882
|
+
const activeRange = asEditorAnchorProjection(value.activeRange);
|
|
883
|
+
if (!activeRange) return undefined;
|
|
884
|
+
return {
|
|
885
|
+
anchor: value.anchor,
|
|
886
|
+
head: value.head,
|
|
887
|
+
isCollapsed: value.isCollapsed,
|
|
888
|
+
activeRange,
|
|
889
|
+
};
|
|
831
890
|
}
|
|
832
891
|
|
|
833
892
|
function asStoryTarget(value: unknown): CommandEvent["context"]["activeStory"] {
|
|
834
893
|
if (!isRecord(value)) return undefined;
|
|
835
|
-
|
|
836
|
-
|
|
894
|
+
switch (value.kind) {
|
|
895
|
+
case "main":
|
|
896
|
+
return { kind: "main" };
|
|
897
|
+
case "header":
|
|
898
|
+
case "footer": {
|
|
899
|
+
if (typeof value.relationshipId !== "string" || value.relationshipId.length === 0) {
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
if (!isHeaderFooterVariant(value.variant)) {
|
|
903
|
+
return undefined;
|
|
904
|
+
}
|
|
905
|
+
if (value.sectionIndex != null && !isFiniteNumber(value.sectionIndex)) {
|
|
906
|
+
return undefined;
|
|
907
|
+
}
|
|
908
|
+
return value.sectionIndex == null
|
|
909
|
+
? {
|
|
910
|
+
kind: value.kind,
|
|
911
|
+
relationshipId: value.relationshipId,
|
|
912
|
+
variant: value.variant,
|
|
913
|
+
}
|
|
914
|
+
: {
|
|
915
|
+
kind: value.kind,
|
|
916
|
+
relationshipId: value.relationshipId,
|
|
917
|
+
variant: value.variant,
|
|
918
|
+
sectionIndex: value.sectionIndex,
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
case "footnote":
|
|
922
|
+
case "endnote":
|
|
923
|
+
if (typeof value.noteId !== "string" || value.noteId.length === 0) {
|
|
924
|
+
return undefined;
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
kind: value.kind,
|
|
928
|
+
noteId: value.noteId,
|
|
929
|
+
};
|
|
930
|
+
default:
|
|
931
|
+
return undefined;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function asEditorAnchorProjection(value: unknown): InternalEditorAnchorProjection | undefined {
|
|
936
|
+
if (!isRecord(value)) return undefined;
|
|
937
|
+
if (value.kind == null && isFiniteNumber(value.from) && isFiniteNumber(value.to)) {
|
|
938
|
+
return {
|
|
939
|
+
kind: "range",
|
|
940
|
+
from: value.from,
|
|
941
|
+
to: value.to,
|
|
942
|
+
assoc: { start: -1, end: 1 },
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
switch (value.kind) {
|
|
947
|
+
case "range": {
|
|
948
|
+
if (!isFiniteNumber(value.from) || !isFiniteNumber(value.to)) return undefined;
|
|
949
|
+
const assoc = asBoundaryAssoc(value.assoc);
|
|
950
|
+
if (!assoc) return undefined;
|
|
951
|
+
const range = value.range == null ? undefined : asDocRange(value.range);
|
|
952
|
+
if (value.range != null && !range) return undefined;
|
|
953
|
+
return range == null
|
|
954
|
+
? { kind: "range", from: value.from, to: value.to, assoc }
|
|
955
|
+
: { kind: "range", from: value.from, to: value.to, range, assoc };
|
|
956
|
+
}
|
|
957
|
+
case "node": {
|
|
958
|
+
if (!isFiniteNumber(value.at)) return undefined;
|
|
959
|
+
const assoc = asAssoc(value.assoc);
|
|
960
|
+
if (assoc == null) return undefined;
|
|
961
|
+
return { kind: "node", at: value.at, assoc };
|
|
962
|
+
}
|
|
963
|
+
case "detached": {
|
|
964
|
+
const lastKnownRange = asDocRange(value.lastKnownRange);
|
|
965
|
+
if (!lastKnownRange) return undefined;
|
|
966
|
+
if (!isDetachedReason(value.reason)) return undefined;
|
|
967
|
+
return { kind: "detached", lastKnownRange, reason: value.reason };
|
|
968
|
+
}
|
|
969
|
+
default:
|
|
970
|
+
return undefined;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
function asDocRange(value: unknown): { from: number; to: number } | undefined {
|
|
975
|
+
if (!isRecord(value)) return undefined;
|
|
976
|
+
if (!isFiniteNumber(value.from) || !isFiniteNumber(value.to)) return undefined;
|
|
977
|
+
return { from: value.from, to: value.to };
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
function asBoundaryAssoc(value: unknown): BoundaryAssoc | undefined {
|
|
981
|
+
if (isAssoc(value)) {
|
|
982
|
+
return { start: value, end: value };
|
|
983
|
+
}
|
|
984
|
+
if (!isRecord(value)) return undefined;
|
|
985
|
+
if (!isAssoc(value.start) || !isAssoc(value.end)) return undefined;
|
|
986
|
+
return { start: value.start, end: value.end };
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function asAssoc(value: unknown): Assoc | undefined {
|
|
990
|
+
return isAssoc(value) ? value : undefined;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
function isAssoc(value: unknown): value is Assoc {
|
|
994
|
+
return value === -1 || value === 1;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
function isDetachedReason(
|
|
998
|
+
value: unknown,
|
|
999
|
+
): value is Extract<InternalEditorAnchorProjection, { kind: "detached" }>["reason"] {
|
|
1000
|
+
return value === "deleted" || value === "invalidatedByStructureChange" || value === "importAmbiguity";
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function isHeaderFooterVariant(value: unknown): value is "default" | "first" | "even" {
|
|
1004
|
+
return value === "default" || value === "first" || value === "even";
|
|
837
1005
|
}
|
|
838
1006
|
|
|
839
1007
|
function isCommandDocumentMode(
|
|
@@ -848,3 +1016,7 @@ function isCommandDocumentMode(
|
|
|
848
1016
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
849
1017
|
return typeof value === "object" && value !== null;
|
|
850
1018
|
}
|
|
1019
|
+
|
|
1020
|
+
function isFiniteNumber(value: unknown): value is number {
|
|
1021
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
1022
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type LayoutWarningEmitter = (
|
|
2
|
+
type: string,
|
|
3
|
+
guard: string,
|
|
4
|
+
inputs: Record<string, unknown>,
|
|
5
|
+
) => void;
|
|
6
|
+
|
|
7
|
+
let activeLayoutWarningEmitter: LayoutWarningEmitter | undefined;
|
|
8
|
+
|
|
9
|
+
export function setActiveLayoutWarningEmitter(fn: LayoutWarningEmitter | undefined): void {
|
|
10
|
+
activeLayoutWarningEmitter = fn;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function emitLayoutGuardWarning(
|
|
14
|
+
guard: string,
|
|
15
|
+
inputs: Record<string, unknown>,
|
|
16
|
+
): void {
|
|
17
|
+
activeLayoutWarningEmitter?.("layout.guard.return-empty", guard, inputs);
|
|
18
|
+
}
|